use of java.util.concurrent.ConcurrentHashMap.newKeySet in project neo4j by neo4j.
the class DenseNodeConcurrencyIT method shouldCreateAndDeleteRelationshipsConcurrently.
/**
* @param multipleDenseNodes if {@code true} then multiple subject nodes are created and will get relationships created between each other during the test,
* otherwise only one dense node is the test subject and other nodes are just small created nodes.
* @param startAsDense if {@code true} then the subject node(s) will start as dense and has lots of relationships of the initial type,
* otherwise the node(s) will start as sparse with only a few relationships.
* @param multipleOperationsInOneTx if {@code true} then each transaction have a chance to do multiple operations,
* otherwise each transaction only performs one operation.
* @param multipleTypes if {@code true} then relationships will be either of several types, otherwise only the same type.
* @param operationWeights chances of each type of operation happening in the test.
*/
@MethodSource("permutations")
@ParameterizedTest(name = "multipleDenseNodes:{0}, startAsDense:{1}, multipleOpsPerTx:{2}, multipleTypes:{3}, opWeights:{4}")
void shouldCreateAndDeleteRelationshipsConcurrently(boolean multipleDenseNodes, boolean startAsDense, boolean multipleOperationsInOneTx, boolean multipleTypes, Map<WorkType, Integer> operationWeights) {
// given
Map<Long, Set<Relationship>> relationships = new ConcurrentHashMap<>();
Set<Relationship> allRelationships = newKeySet();
Set<Long> initialDenseNodes = new HashSet<>(createInitialNodes(multipleDenseNodes, startAsDense, relationships));
Set<Long> denseNodeIds = ConcurrentHashMap.newKeySet();
denseNodeIds.addAll(initialDenseNodes);
relationships.forEach((nodeId, rels) -> allRelationships.addAll(rels));
// when
Queue<WorkTask> workQueue = new ConcurrentLinkedDeque<>(createWork(operationWeights));
Race race = new Race().withFailureAction(t -> workQueue.clear());
int numWorkers = Runtime.getRuntime().availableProcessors();
List<RelationshipType> typesList = new ArrayList<>();
typesList.add(INITIAL_DENSE_NODE_TYPE);
if (multipleTypes) {
typesList.add(withName("a"));
typesList.add(withName("b"));
}
RelationshipType[] types = typesList.toArray(new RelationshipType[0]);
AtomicInteger numDeadlocks = new AtomicInteger();
race.addContestants(numWorkers, throwing(() -> {
WorkTask work;
while ((work = workQueue.poll()) != null) {
// Construct all the tasks that this transaction will perform
List<WorkTask> txTasks = new ArrayList<>();
txTasks.add(work);
while (multipleOperationsInOneTx && random.nextBoolean() && (work = workQueue.poll()) != null) {
txTasks.add(work);
}
// Try to perform those operations, if we fail on dead-lock then simply retry all those operations until we don't
boolean retry;
int numRetries = 0;
do {
// Intermediary state of created/deleted relationships to update or relationships mirror with upon success of the transaction
Map<Long, TxNodeChanges> txCreated = new HashMap<>();
Map<Long, TxNodeChanges> txDeleted = new HashMap<>();
try (Transaction tx = database.beginTx()) {
for (WorkTask task : txTasks) {
task.perform(tx, denseNodeIds, random.among(types), relationships, allRelationships, random, txCreated, txDeleted);
}
tx.commit();
retry = false;
// Now on success update each node's relationship mirror
txCreated.forEach((nodeId, changes) -> relationships.get(nodeId).addAll(changes.relationships));
txDeleted.forEach((nodeId, changes) -> relationships.get(nodeId).removeAll(changes.relationships));
// Finally update the global relationships map afterwards so that no deleter is able to spot them before we've updated the mirrors
txCreated.forEach((nodeId, changes) -> allRelationships.addAll(changes.relationships));
} catch (TransientTransactionFailureException e) {
retry = true;
numRetries++;
allRelationships.addAll(txDeleted.values().stream().flatMap(change -> change.relationships.stream()).collect(Collectors.toSet()));
denseNodeIds.addAll(txDeleted.values().stream().filter(change -> change.node).map(change -> change.id).collect(Collectors.toList()));
numDeadlocks.incrementAndGet();
// Random back-off after deadlock
Thread.sleep(random.nextInt(1, 10 * numRetries));
}
} while (retry);
}
}), 1);
race.goUnchecked();
// then
Set<Long> deletedDenseNodes = new HashSet<>(initialDenseNodes);
deletedDenseNodes.removeAll(denseNodeIds);
assertDeletedNodes(deletedDenseNodes);
for (long denseNodeId : denseNodeIds) {
assertRelationshipsAndDegrees(denseNodeId, relationships.get(denseNodeId));
}
assertThat(numDeadlocks.get()).isLessThan(NUM_TASKS / 10);
}
Aggregations