/drivers/mongodb-driver-test-template/src/test/java/com/github/cloudyrock/mongock/driver/mongodb/test/template/MongoLockManagerITestBase.java

https://github.com/cloudyrock/mongock · Java · 311 lines · 219 code · 51 blank · 41 comment · 0 complexity · 2b6ebe4cb1f9043bbf0bc700e5e44d63 MD5 · raw file

  1. package com.github.cloudyrock.mongock.driver.mongodb.test.template;
  2. import com.github.cloudyrock.mongock.driver.mongodb.test.template.util.IntegrationTestBase;
  3. import com.mongodb.client.FindIterable;
  4. import com.mongodb.client.model.UpdateOptions;
  5. import io.changock.driver.api.lock.LockCheckException;
  6. import io.changock.driver.api.lock.LockManager;
  7. import io.changock.driver.core.lock.DefaultLockManager;
  8. import io.changock.driver.core.lock.LockEntry;
  9. import io.changock.driver.core.lock.LockRepository;
  10. import io.changock.driver.core.lock.LockStatus;
  11. import io.changock.utils.TimeService;
  12. import org.bson.Document;
  13. import org.junit.After;
  14. import org.junit.Before;
  15. import org.junit.Test;
  16. import java.util.Date;
  17. import static org.junit.Assert.assertNotNull;
  18. import static org.junit.Assert.assertNull;
  19. public abstract class MongoLockManagerITestBase extends IntegrationTestBase {
  20. protected static final String LOCK_COLLECTION_NAME = "changockLock";
  21. protected static final long lockActiveMillis = 5 * 60 * 1000;
  22. protected static final long maxWaitMillis = 5 * 60 * 1000;
  23. protected static final int lockMaxTries = 3;
  24. protected LockManager lockManager;
  25. protected LockRepository<Document> repository;
  26. @Before
  27. public void setUp() {
  28. initializeRepository();
  29. TimeService timeUtils = new TimeService();
  30. lockManager = new DefaultLockManager(repository, timeUtils)
  31. .setLockAcquiredForMillis(lockActiveMillis)
  32. .setLockMaxTries(lockMaxTries)
  33. .setLockMaxWaitMillis(maxWaitMillis);
  34. }
  35. @After
  36. public void tearDown() {
  37. getDataBase().getCollection(LOCK_COLLECTION_NAME).deleteMany(new Document());
  38. }
  39. @Test
  40. public void shouldAcquireLock_WhenHeld_IfSameOwner() throws LockCheckException {
  41. //given
  42. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  43. new Document(),
  44. new Document().append("$set", getLockDbBody(lockManager.getOwner(), currentTimePlusHours(24))),
  45. new UpdateOptions().upsert(true));
  46. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  47. .find(new Document().append("key", lockManager.getDefaultKey()));
  48. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  49. //when
  50. lockManager.acquireLockDefault();
  51. }
  52. @Test
  53. public void shouldAcquireLock_WhenHeldByOtherOwner_IfExpired() throws LockCheckException {
  54. //given
  55. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  56. new Document(),
  57. new Document().append("$set", getLockDbBody("otherOwner", currentTimePlusHours(-1))),
  58. new UpdateOptions().upsert(true));
  59. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  60. .find(new Document().append("key", lockManager.getDefaultKey()));
  61. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  62. //when
  63. lockManager.acquireLockDefault();
  64. }
  65. @Test
  66. public void shouldAcquireLock_WhenHeldByOtherOwner_IfExpiresAtIsLessThanMaxWaitTime() throws LockCheckException {
  67. //given
  68. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  69. new Document(),
  70. new Document().append("$set", getLockDbBody("otherOwner", System.currentTimeMillis() + 100)),
  71. new UpdateOptions().upsert(true));
  72. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  73. .find(new Document().append("key", lockManager.getDefaultKey()));
  74. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  75. //when
  76. lockManager.acquireLockDefault();
  77. }
  78. @Test(expected = LockCheckException.class)
  79. public void shouldNotAcquireLock_WhenHeldByOtherOwner_IfExpiresAtIsGreaterThanMaxWaitTime() throws LockCheckException {
  80. //given
  81. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  82. new Document(),
  83. new Document()
  84. .append("$set", getLockDbBody("otherOwner", currentTimePlusMinutes(millisToMinutes(maxWaitMillis) + 1))),
  85. new UpdateOptions().upsert(true));
  86. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  87. .find(new Document().append("key", lockManager.getDefaultKey()));
  88. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  89. //when
  90. lockManager.acquireLockDefault();
  91. }
  92. @Test(expected = LockCheckException.class)
  93. public void shouldNotEnsure_WhenFirstTime() throws LockCheckException {
  94. //when
  95. lockManager.ensureLockDefault();
  96. }
  97. /**
  98. * If it's not expired, the lock is ensured because still belongs to the owner
  99. */
  100. @Test
  101. public void shouldEnsureLock_WhenHeldBySameOwner_IfNotExpiredInDB() throws LockCheckException {
  102. //given
  103. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  104. new Document(),
  105. new Document().append("$set", getLockDbBody(lockManager.getOwner(), currentTimePlusMinutes(1))),
  106. new UpdateOptions().upsert(true));
  107. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  108. .find(new Document().append("key", lockManager.getDefaultKey()));
  109. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  110. //when
  111. lockManager.ensureLockDefault();
  112. }
  113. /**
  114. * If it's expired, the lock should be ensured because no one has requested, so it should be extended for the same
  115. * owner
  116. */
  117. @Test
  118. public void shouldEnsureLock_WhenHeldBySameOwner_IfExpiredInDB() throws LockCheckException {
  119. //given
  120. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  121. new Document(),
  122. new Document().append("$set", getLockDbBody(lockManager.getOwner(), currentTimePlusMinutes(-10))),
  123. new UpdateOptions().upsert(true));
  124. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  125. .find(new Document().append("key", lockManager.getDefaultKey()));
  126. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  127. //when
  128. lockManager.ensureLockDefault();
  129. }
  130. @Test
  131. public void shouldEnsureLock_WhenAcquiredPreviously_IfSameOwner() throws LockCheckException {
  132. //given
  133. lockManager.acquireLockDefault();
  134. //when
  135. lockManager.ensureLockDefault();
  136. }
  137. @Test(expected = LockCheckException.class)
  138. public void shouldNotEnsureLock_WhenHeldByOtherOwnerAndExpiredInDB_ifHasNotBeenRequestedPreviously() throws LockCheckException {
  139. //given
  140. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  141. new Document(),
  142. new Document().append("$set", getLockDbBody("other", currentTimePlusMinutes(-10))),
  143. new UpdateOptions().upsert(true));
  144. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  145. .find(new Document().append("key", lockManager.getDefaultKey()));
  146. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  147. //when
  148. lockManager.ensureLockDefault();
  149. }
  150. @Test(expected = LockCheckException.class)
  151. public void shouldNotEnsureLock_WhenHeldByOtherOwner_IfNotExpiredInDB() throws LockCheckException {
  152. //given
  153. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  154. new Document(),
  155. new Document().append("$set", getLockDbBody("other", currentTimePlusMinutes(10))),
  156. new UpdateOptions().upsert(true));
  157. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  158. .find(new Document().append("key", lockManager.getDefaultKey()));
  159. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  160. //when
  161. lockManager.ensureLockDefault();
  162. }
  163. @Test
  164. public void shouldReleaseLock_WhenHeldBySameOwner() {
  165. //given
  166. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  167. new Document(),
  168. new Document().append("$set", getLockDbBody(lockManager.getOwner(), currentTimePlusMinutes(10))),
  169. new UpdateOptions().upsert(true));
  170. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  171. .find(new Document().append("key", lockManager.getDefaultKey()));
  172. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  173. //when
  174. lockManager.releaseLockDefault();
  175. //then
  176. FindIterable<Document> resultAfter = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  177. .find(new Document().append("key", lockManager.getDefaultKey()));
  178. assertNull("Lock should be removed from DB", resultAfter.first());
  179. }
  180. @Test
  181. public void shouldNotReleaseLock_IfHeldByOtherOwner() {
  182. //given
  183. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  184. new Document(),
  185. new Document().append("$set", getLockDbBody("otherOwner", currentTimePlusMinutes(10))),
  186. new UpdateOptions().upsert(true));
  187. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  188. .find(new Document().append("key", lockManager.getDefaultKey()));
  189. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  190. //when
  191. lockManager.releaseLockDefault();
  192. //then
  193. FindIterable<Document> resultAfter = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  194. .find(new Document().append("key", lockManager.getDefaultKey()));
  195. assertNotNull("Lock should be removed from DB", resultAfter.first());
  196. }
  197. @Test
  198. public void releaseLockShouldBeIdempotent_WhenHeldBySameOwner() {
  199. //given
  200. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  201. new Document(),
  202. new Document().append("$set", getLockDbBody(lockManager.getOwner(), currentTimePlusMinutes(10))),
  203. new UpdateOptions().upsert(true));
  204. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  205. .find(new Document().append("key", lockManager.getDefaultKey()));
  206. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  207. //when
  208. lockManager.releaseLockDefault();
  209. lockManager.releaseLockDefault();
  210. //then
  211. FindIterable<Document> resultAfter = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  212. .find(new Document().append("key", lockManager.getDefaultKey()));
  213. assertNull("Lock should be removed from DB", resultAfter.first());
  214. }
  215. @Test
  216. public void releaseLockShouldBeIdempotent_WhenHeldByOtherOwner() {
  217. //given
  218. getDataBase().getCollection(LOCK_COLLECTION_NAME).updateMany(
  219. new Document(),
  220. new Document().append("$set", getLockDbBody("otherOwner", currentTimePlusMinutes(10))),
  221. new UpdateOptions().upsert(true));
  222. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  223. .find(new Document().append("key", lockManager.getDefaultKey()));
  224. assertNotNull("Precondition: Lock should be in database", resultBefore.first());
  225. //when
  226. lockManager.releaseLockDefault();
  227. lockManager.releaseLockDefault();
  228. //then
  229. FindIterable<Document> resultAfter = getDataBase().getCollection(LOCK_COLLECTION_NAME)
  230. .find(new Document().append("key", lockManager.getDefaultKey()));
  231. assertNotNull("Lock should be removed from DB", resultAfter.first());
  232. }
  233. @Test
  234. public void releaseLockShouldNotThrowAnyException_WhenNoLockPresent() {
  235. //given
  236. FindIterable<Document> resultBefore = getDataBase().getCollection(LOCK_COLLECTION_NAME).find();
  237. assertNull("Precondition: Lock should not be in database", resultBefore.first());
  238. //when
  239. lockManager.releaseLockDefault();
  240. lockManager.releaseLockDefault();
  241. //then
  242. FindIterable<Document> resultAfter = getDataBase().getCollection(LOCK_COLLECTION_NAME).find();
  243. assertNull("Lock should be removed from DB", resultAfter.first());
  244. }
  245. private Document getLockDbBody(String owner, long expiresAt) {
  246. LockEntry lockEntry = new LockEntry(lockManager.getDefaultKey(), LockStatus.LOCK_HELD.name(), owner, new Date(expiresAt));
  247. return repository.toEntity(lockEntry);
  248. }
  249. private long currentTimePlusHours(int hours) {
  250. return currentTimePlusMinutes(hours * 60);
  251. }
  252. private long currentTimePlusMinutes(int minutes) {
  253. long millis = minutes * 60 * 1000;
  254. return System.currentTimeMillis() + millis;
  255. }
  256. private int millisToMinutes(long millis) {
  257. return (int) (millis / (1000 * 60));
  258. }
  259. protected abstract void initializeRepository();
  260. }