ChangeDispatcher dispatcher = new ChangeDispatcher(store.getRoot());
AtomicBoolean running = new AtomicBoolean(true);
final CommitQueue queue = new CommitQueue(store, dispatcher);
final List<Exception> exceptions = Collections.synchronizedList(new ArrayList<Exception>());
Closeable observer = dispatcher.addObserver(new Observer() {
private Revision before = new Revision(0, 0, store.getClusterId());
@Override
public void contentChanged(@Nonnull NodeState root, @Nullable CommitInfo info) {
MongoNodeState after = (MongoNodeState) root;
Revision r = after.getRevision();
// System.out.println("seen: " + r);
if (r.compareRevisionTime(before) < 0) {
exceptions.add(new Exception(
"Inconsistent revision sequence. Before: " +
before + ", after: " + r));
}
before = r;
}
});
// perform commits with multiple threads
List<Thread> writers = new ArrayList<Thread>();
for (int i = 0; i < NUM_WRITERS; i++) {
final Random random = new Random(i);
writers.add(new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < COMMITS_PER_WRITER; i++) {
Revision rev = queue.createRevision();
try {
Thread.sleep(0, random.nextInt(1000));
} catch (InterruptedException e) {
// ignore
}
if (random.nextInt(5) == 0) {
// cancel 20% of the commits
queue.canceled(rev);
} else {
boolean isBranch = random.nextInt(5) == 0;
queue.done(rev, isBranch, null);
}
}
} catch (Exception e) {
exceptions.add(e);
}
}
}));
}
for (Thread t : writers) {
t.start();
}
for (Thread t : writers) {
t.join();
}
running.set(false);
observer.close();
for (Exception e : exceptions) {
throw e;
}
}