use of org.neo4j.adversaries.ClassGuardedAdversary in project neo4j by neo4j.
the class CountsRotationTest method possibleToShutdownDbWhenItIsNotHealthyAndNotAllTransactionsAreApplied.
@Test(timeout = 60_000)
public void possibleToShutdownDbWhenItIsNotHealthyAndNotAllTransactionsAreApplied() throws Exception {
// adversary that makes page cache throw exception when node store is used
ClassGuardedAdversary adversary = new ClassGuardedAdversary(new CountingAdversary(1, true), NodeStore.class);
adversary.disable();
GraphDatabaseService db = AdversarialPageCacheGraphDatabaseFactory.create(fs, adversary).newEmbeddedDatabaseBuilder(dir).newGraphDatabase();
CountDownLatch txStartLatch = new CountDownLatch(1);
CountDownLatch txCommitLatch = new CountDownLatch(1);
Future<?> result = ForkJoinPool.commonPool().submit(() -> {
try (Transaction tx = db.beginTx()) {
txStartLatch.countDown();
db.createNode();
await(txCommitLatch);
tx.success();
}
});
await(txStartLatch);
adversary.enable();
txCommitLatch.countDown();
try {
result.get();
fail("Exception expected");
} catch (ExecutionException ee) {
// transaction is expected to fail because write through the page cache fails
assertThat(ee.getCause(), instanceOf(TransactionFailureException.class));
}
adversary.disable();
// shutdown should complete without any problems
db.shutdown();
}
use of org.neo4j.adversaries.ClassGuardedAdversary in project neo4j by neo4j.
the class RecoveryIT method recoveryShouldFixPartiallyAppliedSchemaIndexUpdates.
@Test(timeout = 60_000)
public void recoveryShouldFixPartiallyAppliedSchemaIndexUpdates() {
Label label = Label.label("Foo");
String property = "Bar";
// cause failure during 'relationship.delete()' command application
ClassGuardedAdversary adversary = new ClassGuardedAdversary(new CountingAdversary(1, true), Command.RelationshipCommand.class);
adversary.disable();
File storeDir = directory.graphDbDir();
GraphDatabaseService db = AdversarialPageCacheGraphDatabaseFactory.create(fileSystemRule.get(), adversary).newEmbeddedDatabaseBuilder(storeDir).newGraphDatabase();
try {
try (Transaction tx = db.beginTx()) {
db.schema().constraintFor(label).assertPropertyIsUnique(property).create();
tx.success();
}
long relationshipId = createRelationship(db);
TransactionFailureException txFailure = null;
try (Transaction tx = db.beginTx()) {
Node node = db.createNode(label);
node.setProperty(property, "B");
// this should fail because of the adversary
db.getRelationshipById(relationshipId).delete();
tx.success();
adversary.enable();
} catch (TransactionFailureException e) {
txFailure = e;
}
assertNotNull(txFailure);
adversary.disable();
// heal the db so it is possible to inspect the data
healthOf(db).healed();
// now we can observe partially committed state: node is in the index and relationship still present
try (Transaction tx = db.beginTx()) {
assertNotNull(findNode(db, label, property, "B"));
assertNotNull(db.getRelationshipById(relationshipId));
tx.success();
}
// panic the db again to force recovery on the next startup
healthOf(db).panic(txFailure.getCause());
// restart the database, now with regular page cache
db.shutdown();
db = startDatabase(storeDir);
// now we observe correct state: node is in the index and relationship is removed
try (Transaction tx = db.beginTx()) {
assertNotNull(findNode(db, label, property, "B"));
assertRelationshipNotExist(db, relationshipId);
tx.success();
}
} finally {
db.shutdown();
}
}
use of org.neo4j.adversaries.ClassGuardedAdversary in project neo4j by neo4j.
the class PartialTransactionFailureIT method concurrentlyCommittingTransactionsMustNotRotateOutLoggedCommandsOfFailingTransaction.
@Test
void concurrentlyCommittingTransactionsMustNotRotateOutLoggedCommandsOfFailingTransaction() throws Exception {
final ClassGuardedAdversary adversary = new ClassGuardedAdversary(new CountingAdversary(1, false), Command.RelationshipCommand.class);
adversary.disable();
Path storeDir = testDirectory.homePath();
final Map<Setting<?>, Object> params = Map.of(GraphDatabaseSettings.pagecache_memory, "8m");
managementService = new TestDatabaseManagementServiceBuilder(storeDir).setFileSystem(new AdversarialFileSystemAbstraction(adversary)).setConfig(params).build();
GraphDatabaseAPI db = (GraphDatabaseAPI) managementService.database(DEFAULT_DATABASE_NAME);
Node a;
Node b;
Node c;
Node d;
try (Transaction tx = db.beginTx()) {
a = tx.createNode();
b = tx.createNode();
c = tx.createNode();
d = tx.createNode();
tx.commit();
}
adversary.enable();
CountDownLatch latch = new CountDownLatch(1);
Thread t1 = new Thread(createRelationship(db, a, b, latch), "T1");
Thread t2 = new Thread(createRelationship(db, c, d, latch), "T2");
t1.start();
t2.start();
// Wait for both threads to get going
t1.join(10);
t2.join(10);
latch.countDown();
// Wait for the transactions to finish
t1.join(25000);
t2.join(25000);
managementService.shutdown();
// We should observe the store in a consistent state
managementService = new TestDatabaseManagementServiceBuilder(storeDir).setConfig(params).build();
GraphDatabaseService database = managementService.database(DEFAULT_DATABASE_NAME);
try (Transaction tx = database.beginTx()) {
Node x = tx.getNodeById(a.getId());
Node y = tx.getNodeById(b.getId());
Node z = tx.getNodeById(c.getId());
Node w = tx.getNodeById(d.getId());
Iterator<Relationship> itrRelX = x.getRelationships().iterator();
Iterator<Relationship> itrRelY = y.getRelationships().iterator();
Iterator<Relationship> itrRelZ = z.getRelationships().iterator();
Iterator<Relationship> itrRelW = w.getRelationships().iterator();
if (itrRelX.hasNext() != itrRelY.hasNext()) {
fail("Node x and y have inconsistent relationship counts");
} else if (itrRelX.hasNext()) {
Relationship rel = itrRelX.next();
assertEquals(rel, itrRelY.next());
assertFalse(itrRelX.hasNext());
assertFalse(itrRelY.hasNext());
}
if (itrRelZ.hasNext() != itrRelW.hasNext()) {
fail("Node z and w have inconsistent relationship counts");
} else if (itrRelZ.hasNext()) {
Relationship rel = itrRelZ.next();
assertEquals(rel, itrRelW.next());
assertFalse(itrRelZ.hasNext());
assertFalse(itrRelW.hasNext());
}
}
}
use of org.neo4j.adversaries.ClassGuardedAdversary in project neo4j by neo4j.
the class BatchingTransactionAppenderConcurrencyTest method shouldHaveAllConcurrentAppendersSeePanic.
/*
* There was an issue where if multiple concurrent appending threads did append and they moved on
* to await a force, where the force would fail and the one doing the force would raise a panic...
* the other threads may not notice the panic and move on to mark those transactions as committed
* and notice the panic later (which would be too late).
*/
@Test
void shouldHaveAllConcurrentAppendersSeePanic() throws Throwable {
// GIVEN
Adversary adversary = new ClassGuardedAdversary(new CountingAdversary(1, true), failMethod(TransactionLogFile.class, "force"));
EphemeralFileSystemAbstraction efs = new EphemeralFileSystemAbstraction();
FileSystemAbstraction fs = new AdversarialFileSystemAbstraction(adversary, efs);
life.add(new FileSystemLifecycleAdapter(fs));
DatabaseHealth databaseHealth = new DatabaseHealth(mock(DatabasePanicEventGenerator.class), NullLog.getInstance());
LogFiles logFiles = LogFilesBuilder.builder(databaseLayout, fs).withLogVersionRepository(logVersionRepository).withTransactionIdStore(transactionIdStore).withDatabaseHealth(databaseHealth).withLogEntryReader(new VersionAwareLogEntryReader(new TestCommandReaderFactory())).withStoreId(StoreId.UNKNOWN).build();
life.add(logFiles);
final BatchingTransactionAppender appender = life.add(new BatchingTransactionAppender(logFiles, logRotation, transactionMetadataCache, transactionIdStore, databaseHealth));
life.start();
// WHEN
int numberOfAppenders = 10;
final CountDownLatch trap = new CountDownLatch(numberOfAppenders);
final LogAppendEvent beforeForceTrappingEvent = new LogAppendEvent.Empty() {
@Override
public LogForceWaitEvent beginLogForceWait() {
trap.countDown();
awaitLatch(trap);
return super.beginLogForceWait();
}
};
Race race = new Race();
for (int i = 0; i < numberOfAppenders; i++) {
race.addContestant(() -> {
// Good, we know that this test uses an adversarial file system which will throw
// an exception in LogFile#force, and since all these transactions
// will append and be forced in the same batch, where the force will fail then
// all these transactions should fail. If there's any transaction not failing then
// it just didn't notice the panic, which would be potentially hazardous.
assertThrows(IOException.class, () -> appender.append(tx(), beforeForceTrappingEvent));
});
}
// THEN perform the race. The relevant assertions are made inside the contestants.
race.go();
}
use of org.neo4j.adversaries.ClassGuardedAdversary in project neo4j by neo4j.
the class DatabaseRecoveryIT method recoveryShouldFixPartiallyAppliedSchemaIndexUpdates.
@Test
void recoveryShouldFixPartiallyAppliedSchemaIndexUpdates() {
Label label = Label.label("Foo");
String property = "Bar";
// cause failure during 'relationship.delete()' command application
ClassGuardedAdversary adversary = new ClassGuardedAdversary(new CountingAdversary(1, true), Command.RelationshipCommand.class);
adversary.disable();
Path storeDir = directory.homePath();
DatabaseManagementService managementService = AdversarialPageCacheGraphDatabaseFactory.create(storeDir, fileSystem, adversary).build();
GraphDatabaseService db = managementService.database(DEFAULT_DATABASE_NAME);
try {
try (Transaction tx = db.beginTx()) {
tx.schema().constraintFor(label).assertPropertyIsUnique(property).create();
tx.commit();
}
long relationshipId = createRelationship(db);
TransactionFailureException txFailure = null;
try (Transaction tx = db.beginTx()) {
Node node = tx.createNode(label);
node.setProperty(property, "B");
// this should fail because of the adversary
tx.getRelationshipById(relationshipId).delete();
adversary.enable();
tx.commit();
} catch (TransactionFailureException e) {
txFailure = e;
}
assertNotNull(txFailure);
adversary.disable();
// heal the db so it is possible to inspect the data
healthOf(db).healed();
// now we can observe partially committed state: node is in the index and relationship still present
try (Transaction tx = db.beginTx()) {
assertNotNull(findNode(label, property, "B", tx));
assertNotNull(tx.getRelationshipById(relationshipId));
tx.commit();
}
// panic the db again to force recovery on the next startup
healthOf(db).panic(txFailure.getCause());
// restart the database, now with regular page cache
managementService.shutdown();
db = startDatabase(storeDir);
// now we observe correct state: node is in the index and relationship is removed
try (Transaction tx = db.beginTx()) {
assertNotNull(findNode(label, property, "B", tx));
assertRelationshipNotExist(tx, relationshipId);
tx.commit();
}
} finally {
managementService.shutdown();
}
}
Aggregations