-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1353 from b2ihealthcare/issue/SO-6268-index-neste…
…d-lock-contexts Add nested context descriptions to lock documents
- Loading branch information
Showing
12 changed files
with
359 additions
and
193 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,10 @@ | |
*/ | ||
package com.b2international.snowowl.core.locks; | ||
|
||
import static org.junit.Assert.assertTrue; | ||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertThrows; | ||
|
||
import java.util.List; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
@@ -35,84 +38,103 @@ | |
*/ | ||
public class DatastoreLockTests { | ||
|
||
private static final long TIMEOUT = 10_000L; | ||
private static final String USER1 = "[email protected]"; | ||
private static final String USER2 = "[email protected]"; | ||
|
||
private static final DatastoreLockContext CONTEXT_GRANTED = new DatastoreLockContext( | ||
USER1, | ||
DatastoreLockContextDescriptions.MAINTENANCE | ||
); | ||
|
||
private static final DatastoreLockContext CONTEXT_DIFFERENT_USER = new DatastoreLockContext( | ||
USER2, | ||
DatastoreLockContextDescriptions.MAINTENANCE | ||
); | ||
|
||
private static final DatastoreLockContext CONTEXT_NESTED = new DatastoreLockContext( | ||
USER1, | ||
DatastoreLockContextDescriptions.COMMIT, | ||
DatastoreLockContextDescriptions.MAINTENANCE | ||
); | ||
|
||
private static final Lockable TARGET_ALL = Lockable.ALL; | ||
private static final Lockable TARGET_REPOSITORY = new Lockable("snomed", null); | ||
private static final Lockable TARGET_REPOSITORY_BRANCH = new Lockable("snomed", "MAIN/a/b"); | ||
|
||
private static final String USER = "snowowl"; | ||
private static final long TIMEOUT = 100L; | ||
|
||
private DefaultOperationLockManager manager; | ||
|
||
@Before | ||
public void setup() { | ||
final ObjectMapper mapper = JsonSupport.getDefaultObjectMapper(); | ||
final Index index = Indexes.createIndex("locks", mapper, new Mappings(DatastoreLockIndexEntry.class)); | ||
|
||
manager = new DefaultOperationLockManager(index); | ||
manager.addLockTargetListener(new Slf4jOperationLockTargetListener()); | ||
manager.unlockAll(); | ||
} | ||
|
||
@Test | ||
public void testLockAll() { | ||
final DatastoreLockContext context = createContext(USER, DatastoreLockContextDescriptions.MAINTENANCE); | ||
final Lockable allLockTarget = Lockable.ALL; | ||
private void testLock(Lockable target) { | ||
// Take the lock | ||
manager.lock(CONTEXT_GRANTED, TIMEOUT, target); | ||
checkIfLockExists(CONTEXT_GRANTED, true, target); | ||
|
||
manager.lock(context, TIMEOUT, allLockTarget); | ||
checkIfLockExists(context, true, allLockTarget); | ||
manager.unlockAll(); | ||
} | ||
|
||
@Test | ||
public void testUnlock() { | ||
final DatastoreLockContext context = createContext(USER, DatastoreLockContextDescriptions.MAINTENANCE); | ||
final Lockable target = new Lockable("snomedStore", "MAIN"); | ||
// Different user, same target is rejected | ||
assertThrows(LockedException.class, () -> manager.lock(CONTEXT_DIFFERENT_USER, TIMEOUT, target)); | ||
checkIfLockExists(CONTEXT_DIFFERENT_USER, false, target); | ||
|
||
manager.lock(context, TIMEOUT, target); | ||
checkIfLockExists(context, true, target); | ||
|
||
manager.unlock(context, target); | ||
checkIfLockExists(context, false, target); | ||
} | ||
|
||
@Test | ||
public void testUnlockAll() { | ||
final DatastoreLockContext context = createContext(USER, DatastoreLockContextDescriptions.MAINTENANCE); | ||
final Lockable target1 = new Lockable("snomedStore", "MAIN"); | ||
final Lockable target2 = new Lockable("loincStore", "MAIN"); | ||
// Same user, improper nesting (same parent description) is also rejected | ||
assertThrows(LockedException.class, () -> manager.lock(CONTEXT_GRANTED, TIMEOUT, target)); | ||
|
||
manager.lock(context, TIMEOUT, target1, target2); | ||
checkIfLockExists(context, true, target1, target2); | ||
// Same user, proper nesting, same target is allowed | ||
manager.lock(CONTEXT_NESTED, TIMEOUT, TARGET_ALL); | ||
checkIfLockExists(CONTEXT_NESTED, true, TARGET_ALL); | ||
manager.unlock(CONTEXT_NESTED, TARGET_ALL); | ||
checkIfLockExists(CONTEXT_NESTED, false, TARGET_ALL); | ||
|
||
manager.unlockAll(); | ||
checkIfLockExists(context, false, target1, target2); | ||
// Same user, proper nesting, repository target is also allowed | ||
manager.lock(CONTEXT_NESTED, TIMEOUT, TARGET_REPOSITORY); | ||
checkIfLockExists(CONTEXT_NESTED, true, TARGET_REPOSITORY); | ||
manager.unlock(CONTEXT_NESTED, TARGET_REPOSITORY); | ||
checkIfLockExists(CONTEXT_NESTED, false, TARGET_REPOSITORY); | ||
|
||
// Same user, proper nesting, repository + branch target is, yet again, allowed | ||
manager.lock(CONTEXT_NESTED, TIMEOUT, TARGET_REPOSITORY_BRANCH); | ||
checkIfLockExists(CONTEXT_NESTED, true, TARGET_REPOSITORY_BRANCH); | ||
manager.unlock(CONTEXT_NESTED, TARGET_REPOSITORY_BRANCH); | ||
checkIfLockExists(CONTEXT_NESTED, false, TARGET_REPOSITORY_BRANCH); | ||
|
||
// Release the first lock | ||
manager.unlock(CONTEXT_GRANTED, target); | ||
checkIfLockExists(CONTEXT_GRANTED, false, target); | ||
} | ||
|
||
@Test(expected = LockedException.class) | ||
public void testLockAllNotAbleToLockAnother() { | ||
final DatastoreLockContext context = createContext(USER, DatastoreLockContextDescriptions.MAINTENANCE); | ||
final Lockable allLockTarget = Lockable.ALL; | ||
|
||
manager.lock(context, 1_000L, allLockTarget); | ||
manager.lock(context, 1_000L, allLockTarget); | ||
@Test | ||
public void testLockAll() { | ||
testLock(TARGET_ALL); | ||
} | ||
|
||
@Test | ||
public void testLockBranchAndRepository() { | ||
final DatastoreLockContext context = createContext(USER, DatastoreLockContextDescriptions.CREATE_VERSION); | ||
final Lockable target = new Lockable("snomedStore", "MAIN"); | ||
manager.lock(context, 10_000L, target); | ||
checkIfLockExists(context, true, target); | ||
public void testLockRepository() { | ||
testLock(TARGET_REPOSITORY); | ||
} | ||
|
||
private DatastoreLockContext createContext(final String user, final String description) { | ||
return new DatastoreLockContext(user, description); | ||
@Test | ||
public void testLockRepositoryBranch() { | ||
testLock(TARGET_REPOSITORY_BRANCH); | ||
} | ||
|
||
private void checkIfLockExists(DatastoreLockContext context, boolean expected, Lockable...targets) { | ||
for (int i = 0; i < targets.length; i++) { | ||
final Lockable target = targets[i]; | ||
final OperationLock operationLock = new OperationLock(i, target); | ||
operationLock.acquire(context); | ||
final OperationLockInfo info = new OperationLockInfo(i, operationLock.getLevel(), operationLock.getCreationDate(), target, context); | ||
assertTrue(expected == manager.getLocks().contains(info)); | ||
private void checkIfLockExists(DatastoreLockContext context, boolean expected, Lockable...targets) { | ||
final List<OperationLockInfo> locks = manager.getLocks(); | ||
|
||
for (final Lockable target : targets) { | ||
final boolean lockExists = locks.stream() | ||
.filter(info -> info.getTarget().equals(target) && info.getContext().equals(context)) | ||
.findFirst() | ||
.isPresent(); | ||
|
||
assertEquals(expected, lockExists); | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2019-2023 B2i Healthcare, https://b2ihealthcare.com | ||
* Copyright 2019-2024 B2i Healthcare, https://b2ihealthcare.com | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
|
@@ -17,6 +17,7 @@ | |
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assert.assertNull; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
|
@@ -39,6 +40,7 @@ | |
public class LockIndexTests { | ||
|
||
private static final String USER = "[email protected]"; | ||
|
||
private Index index; | ||
private ObjectMapper mapper; | ||
|
||
|
@@ -48,48 +50,101 @@ public void setup() { | |
index = Indexes.createIndex("locks", mapper, new Mappings(DatastoreLockIndexEntry.class)); | ||
index.admin().create(); | ||
} | ||
|
||
@Test | ||
public void indexLockEntry() { | ||
final String lockId = "1"; | ||
|
||
final DatastoreLockIndexEntry lock = DatastoreLockIndexEntry.builder() | ||
.id(lockId) | ||
.userId(USER) | ||
.description(DatastoreLockContextDescriptions.CLASSIFY) | ||
.addContext(DatastoreLockContextDescriptions.CLASSIFY) | ||
.repositoryId("repositoryUuid") | ||
.branchPath("branchPath") | ||
.build(); | ||
|
||
indexDocument(lock); | ||
final DatastoreLockIndexEntry actual = get(lockId); | ||
|
||
final DatastoreLockIndexEntry actual = getDocument(lockId); | ||
assertDocEquals(lock, actual); | ||
} | ||
|
||
private void indexDocument(DatastoreLockIndexEntry doc) { | ||
|
||
@Test | ||
public void updateLockEntry() { | ||
final String lockId = "2"; | ||
|
||
final DatastoreLockIndexEntry lock = DatastoreLockIndexEntry.builder() | ||
.id(lockId) | ||
.userId(USER) | ||
.addContext(DatastoreLockContextDescriptions.CREATE_VERSION) | ||
.repositoryId("repositoryUuid") | ||
.branchPath("branchPath") | ||
.build(); | ||
|
||
indexDocument(lock); | ||
|
||
final DatastoreLockIndexEntry updatedLock = DatastoreLockIndexEntry.from(lock) | ||
.addContext(DatastoreLockContextDescriptions.COMMIT) | ||
.build(); | ||
|
||
indexDocument(updatedLock); | ||
|
||
final DatastoreLockIndexEntry actual = getDocument(lockId); | ||
assertDocEquals(updatedLock, actual); | ||
} | ||
|
||
@Test | ||
public void deleteLockEntry() { | ||
final String lockId = "3"; | ||
|
||
final DatastoreLockIndexEntry lock = DatastoreLockIndexEntry.builder() | ||
.id(lockId) | ||
.userId(USER) | ||
.addContext(DatastoreLockContextDescriptions.CREATE_VERSION) | ||
.repositoryId("repositoryUuid") | ||
.branchPath("branchPath") | ||
.build(); | ||
|
||
indexDocument(lock); | ||
deleteDocument(lockId); | ||
|
||
assertNull(getDocument(lockId)); | ||
} | ||
|
||
private void indexDocument(final DatastoreLockIndexEntry doc) { | ||
index.write(writer -> { | ||
writer.put(doc); | ||
writer.commit(); | ||
|
||
return null; | ||
}); | ||
} | ||
private DatastoreLockIndexEntry get(final String lockId) { | ||
|
||
private DatastoreLockIndexEntry getDocument(final String lockId) { | ||
return index.read(searcher -> searcher.get(DatastoreLockIndexEntry.class, lockId)); | ||
} | ||
|
||
private void assertDocEquals(DatastoreLockIndexEntry expected, DatastoreLockIndexEntry actual) { | ||
|
||
private void deleteDocument(final String id) { | ||
index.write(writer -> { | ||
writer.remove(DatastoreLockIndexEntry.class, id); | ||
writer.commit(); | ||
return null; | ||
}); | ||
} | ||
|
||
private void assertDocEquals(final DatastoreLockIndexEntry expected, final DatastoreLockIndexEntry actual) { | ||
assertNotNull("Actual document is missing from index", actual); | ||
for (Field f : index.admin().getIndexMapping().getMapping(expected.getClass()).getFields()) { | ||
|
||
for (final Field f : index.admin().getIndexMapping().getMapping(expected.getClass()).getFields()) { | ||
if (Revision.Fields.CREATED.equals(f.getName()) | ||
|| Revision.Fields.REVISED.equals(f.getName()) | ||
|| WithScore.SCORE.equals(f.getName()) | ||
) { | ||
|| Revision.Fields.REVISED.equals(f.getName()) | ||
|| WithScore.SCORE.equals(f.getName()) | ||
) { | ||
// skip revision fields from equality check | ||
continue; | ||
} | ||
|
||
assertEquals(String.format("Field '%s' should be equal", f.getName()), Reflections.getValue(expected, f), Reflections.getValue(actual, f)); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.