Search in sources :

Example 1 with Command

use of org.neo4j.kernel.impl.transaction.command.Command 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();
    }
}
Also used : GraphDatabaseService(org.neo4j.graphdb.GraphDatabaseService) CountingAdversary(org.neo4j.adversaries.CountingAdversary) TransactionFailureException(org.neo4j.graphdb.TransactionFailureException) ClassGuardedAdversary(org.neo4j.adversaries.ClassGuardedAdversary) Transaction(org.neo4j.graphdb.Transaction) Command(org.neo4j.kernel.impl.transaction.command.Command) Node(org.neo4j.graphdb.Node) Label(org.neo4j.graphdb.Label) File(java.io.File) Test(org.junit.Test)

Example 2 with Command

use of org.neo4j.kernel.impl.transaction.command.Command in project neo4j by neo4j.

the class LegacyLogEntryWriterTest method shouldWriteAllTheEntryInACommitToTheFile.

@Test
public void shouldWriteAllTheEntryInACommitToTheFile() throws IOException {
    // given
    final LogVersionedStoreChannel channel = mock(LogVersionedStoreChannel.class);
    final LogEntryWriter logEntryWriter = mock(LogEntryWriter.class);
    final LegacyLogEntryWriter writer = new LegacyLogEntryWriter(fs, liftToFactory(logEntryWriter));
    final LogEntryStart start = new LogEntryStart(0, 1, 2L, 3L, EMPTY_ADDITIONAL_ARRAY, UNSPECIFIED);
    final LogEntryCommand command = new LogEntryCommand(new Command.NodeCommand(nodeRecord, nodeRecord));
    final LogEntryCommit commit = new OnePhaseCommit(42L, 43L);
    // when
    final IOCursor<LogEntry> cursor = mockCursor(start, command, commit);
    writer.writeAllLogEntries(channel, cursor);
    // then
    verify(logEntryWriter, times(1)).writeStartEntry(0, 1, 2L, 3L, EMPTY_ADDITIONAL_ARRAY);
    final TransactionRepresentation expected = new PhysicalTransactionRepresentation(Arrays.asList(command.getXaCommand()));
    verify(logEntryWriter, times(1)).serialize(eq(expected));
    verify(logEntryWriter, times(1)).writeCommitEntry(42L, 43L);
}
Also used : LogEntryStart(org.neo4j.kernel.impl.transaction.log.entry.LogEntryStart) LogVersionedStoreChannel(org.neo4j.kernel.impl.transaction.log.LogVersionedStoreChannel) LogEntryCommand(org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand) TransactionRepresentation(org.neo4j.kernel.impl.transaction.TransactionRepresentation) PhysicalTransactionRepresentation(org.neo4j.kernel.impl.transaction.log.PhysicalTransactionRepresentation) Command(org.neo4j.kernel.impl.transaction.command.Command) LogEntryCommand(org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand) LogEntryCommit(org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommit) LogEntryWriter(org.neo4j.kernel.impl.transaction.log.entry.LogEntryWriter) OnePhaseCommit(org.neo4j.kernel.impl.transaction.log.entry.OnePhaseCommit) LogEntry(org.neo4j.kernel.impl.transaction.log.entry.LogEntry) PhysicalTransactionRepresentation(org.neo4j.kernel.impl.transaction.log.PhysicalTransactionRepresentation) Test(org.junit.Test)

Example 3 with Command

use of org.neo4j.kernel.impl.transaction.command.Command in project neo4j by neo4j.

the class LegacyLogsTest method createNode.

private static LogEntry createNode(int id) {
    NodeRecord before = new NodeRecord(id).initialize(false, NO_NEXT_REL, false, NO_NEXT_REL, NO_LABELS);
    NodeRecord after = new NodeRecord(id).initialize(true, NO_NEXT_REL, false, NO_NEXT_REL, NO_LABELS);
    Command.NodeCommand command = new Command.NodeCommand(before, after);
    return new LogEntryCommand(command);
}
Also used : NodeRecord(org.neo4j.kernel.impl.store.record.NodeRecord) LogEntryCommand(org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand) Command(org.neo4j.kernel.impl.transaction.command.Command) LogEntryCommand(org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand)

Example 4 with Command

use of org.neo4j.kernel.impl.transaction.command.Command in project neo4j by neo4j.

the class LogEntrySortingCursorTest method command.

private LogEntry command() {
    NodeRecord before = new NodeRecord(random.nextInt());
    NodeRecord after = new NodeRecord(random.nextInt());
    return new LogEntryCommand(new Command.NodeCommand(before, after));
}
Also used : NodeRecord(org.neo4j.kernel.impl.store.record.NodeRecord) LogEntryCommand(org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand) Command(org.neo4j.kernel.impl.transaction.command.Command) LogEntryCommand(org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand)

Example 5 with Command

use of org.neo4j.kernel.impl.transaction.command.Command in project neo4j by neo4j.

the class TransactionRecordState method extractCommands.

@Override
public void extractCommands(Collection<StorageCommand> commands) throws TransactionFailureException {
    assert !prepared : "Transaction has already been prepared";
    integrityValidator.validateTransactionStartKnowledge(lastCommittedTxWhenTransactionStarted);
    int noOfCommands = recordChangeSet.changeSize() + (neoStoreRecord != null ? neoStoreRecord.changeSize() : 0);
    for (RecordProxy<Integer, LabelTokenRecord, Void> record : recordChangeSet.getLabelTokenChanges().changes()) {
        commands.add(new Command.LabelTokenCommand(record.getBefore(), record.forReadingLinkage()));
    }
    for (RecordProxy<Integer, RelationshipTypeTokenRecord, Void> record : recordChangeSet.getRelationshipTypeTokenChanges().changes()) {
        commands.add(new Command.RelationshipTypeTokenCommand(record.getBefore(), record.forReadingLinkage()));
    }
    for (RecordProxy<Integer, PropertyKeyTokenRecord, Void> record : recordChangeSet.getPropertyKeyTokenChanges().changes()) {
        commands.add(new Command.PropertyKeyTokenCommand(record.getBefore(), record.forReadingLinkage()));
    }
    // Collect nodes, relationships, properties
    Command[] nodeCommands = EMPTY_COMMANDS;
    int skippedCommands = 0;
    if (recordChangeSet.getNodeRecords().changeSize() > 0) {
        nodeCommands = new Command[recordChangeSet.getNodeRecords().changeSize()];
        int i = 0;
        for (RecordProxy<Long, NodeRecord, Void> change : recordChangeSet.getNodeRecords().changes()) {
            NodeRecord record = prepared(change, nodeStore);
            integrityValidator.validateNodeRecord(record);
            nodeCommands[i++] = new Command.NodeCommand(change.getBefore(), record);
        }
        Arrays.sort(nodeCommands, COMMAND_SORTER);
    }
    Command[] relCommands = EMPTY_COMMANDS;
    if (recordChangeSet.getRelRecords().changeSize() > 0) {
        relCommands = new Command[recordChangeSet.getRelRecords().changeSize()];
        int i = 0;
        for (RecordProxy<Long, RelationshipRecord, Void> change : recordChangeSet.getRelRecords().changes()) {
            relCommands[i++] = new Command.RelationshipCommand(change.getBefore(), prepared(change, relationshipStore));
        }
        Arrays.sort(relCommands, COMMAND_SORTER);
    }
    Command[] propCommands = EMPTY_COMMANDS;
    if (recordChangeSet.getPropertyRecords().changeSize() > 0) {
        propCommands = new Command[recordChangeSet.getPropertyRecords().changeSize()];
        int i = 0;
        for (RecordProxy<Long, PropertyRecord, PrimitiveRecord> change : recordChangeSet.getPropertyRecords().changes()) {
            propCommands[i++] = new Command.PropertyCommand(change.getBefore(), prepared(change, propertyStore));
        }
        Arrays.sort(propCommands, COMMAND_SORTER);
    }
    Command[] relGroupCommands = EMPTY_COMMANDS;
    if (recordChangeSet.getRelGroupRecords().changeSize() > 0) {
        relGroupCommands = new Command[recordChangeSet.getRelGroupRecords().changeSize()];
        int i = 0;
        for (RecordProxy<Long, RelationshipGroupRecord, Integer> change : recordChangeSet.getRelGroupRecords().changes()) {
            if (change.isCreated() && !change.forReadingLinkage().inUse()) {
                /*
                     * This is an edge case that may come up and which we must handle properly. Relationship groups are
                     * not managed by the tx state, since they are created as side effects rather than through
                     * direct calls. However, they differ from say, dynamic records, in that their management can happen
                     * through separate code paths. What we are interested in here is the following scenario.
                     * 0. A node has one less relationship that is required to transition to dense node. The relationships
                     *    it has belong to at least two different types
                     * 1. In the same tx, a relationship is added making the node dense and all the relationships of a type
                     *    are removed from that node. Regardless of the order these operations happen, the creation of the
                     *    relationship (and the transition of the node to dense) will happen first.
                     * 2. A relationship group will be created because of the transition to dense and then deleted because
                     *    all the relationships it would hold are no longer there. This results in a relationship group
                     *    command that appears in the tx as not in use. Depending on the final order of operations, this
                     *    can end up using an id that is higher than the highest id seen so far. This may not be a problem
                     *    for a single instance, but it can result in errors in cases where transactions are applied
                     *    externally, such as backup or HA.
                     *
                     * The way we deal with this issue here is by not issuing a command for that offending record. This is
                     * safe, since the record is not in use and never was, so the high id is not necessary to change and
                     * the store remains consistent.
                     */
                skippedCommands++;
                continue;
            }
            relGroupCommands[i++] = new Command.RelationshipGroupCommand(change.getBefore(), prepared(change, relationshipGroupStore));
        }
        relGroupCommands = i < relGroupCommands.length ? Arrays.copyOf(relGroupCommands, i) : relGroupCommands;
        Arrays.sort(relGroupCommands, COMMAND_SORTER);
    }
    addFiltered(commands, Mode.CREATE, propCommands, relCommands, relGroupCommands, nodeCommands);
    addFiltered(commands, Mode.UPDATE, propCommands, relCommands, relGroupCommands, nodeCommands);
    addFiltered(commands, Mode.DELETE, propCommands, relCommands, relGroupCommands, nodeCommands);
    if (neoStoreRecord != null) {
        for (RecordProxy<Long, NeoStoreRecord, Void> change : neoStoreRecord.changes()) {
            commands.add(new Command.NeoStoreCommand(change.getBefore(), change.forReadingData()));
        }
    }
    for (RecordProxy<Long, SchemaRecord, SchemaRule> change : recordChangeSet.getSchemaRuleChanges().changes()) {
        integrityValidator.validateSchemaRule(change.getAdditionalData());
        commands.add(new Command.SchemaRuleCommand(change.getBefore(), change.forChangingData(), change.getAdditionalData()));
    }
    assert commands.size() == noOfCommands - skippedCommands : format("Expected %d final commands, got %d " + "instead, with %d skipped", noOfCommands, commands.size(), skippedCommands);
    prepared = true;
}
Also used : RelationshipTypeTokenRecord(org.neo4j.kernel.impl.store.record.RelationshipTypeTokenRecord) RelationshipRecord(org.neo4j.kernel.impl.store.record.RelationshipRecord) SchemaRule(org.neo4j.storageengine.api.schema.SchemaRule) PropertyKeyTokenRecord(org.neo4j.kernel.impl.store.record.PropertyKeyTokenRecord) NodeRecord(org.neo4j.kernel.impl.store.record.NodeRecord) NeoStoreRecord(org.neo4j.kernel.impl.store.record.NeoStoreRecord) PropertyRecord(org.neo4j.kernel.impl.store.record.PropertyRecord) SchemaRecord(org.neo4j.kernel.impl.store.record.SchemaRecord) LabelTokenRecord(org.neo4j.kernel.impl.store.record.LabelTokenRecord) PrimitiveRecord(org.neo4j.kernel.impl.store.record.PrimitiveRecord) RelationshipGroupRecord(org.neo4j.kernel.impl.store.record.RelationshipGroupRecord) StorageCommand(org.neo4j.storageengine.api.StorageCommand) Command(org.neo4j.kernel.impl.transaction.command.Command)

Aggregations

Command (org.neo4j.kernel.impl.transaction.command.Command)21 Test (org.junit.Test)16 NodeRecord (org.neo4j.kernel.impl.store.record.NodeRecord)10 IndexCommand (org.neo4j.kernel.impl.index.IndexCommand)7 IndexDefineCommand (org.neo4j.kernel.impl.index.IndexDefineCommand)7 StorageCommand (org.neo4j.storageengine.api.StorageCommand)7 HashSet (java.util.HashSet)6 InMemoryClosableChannel (org.neo4j.kernel.impl.transaction.log.InMemoryClosableChannel)5 LogEntryCommand (org.neo4j.kernel.impl.transaction.log.entry.LogEntryCommand)5 SchemaRecord (org.neo4j.kernel.impl.store.record.SchemaRecord)3 LogEntryStart (org.neo4j.kernel.impl.transaction.log.entry.LogEntryStart)3 OnePhaseCommit (org.neo4j.kernel.impl.transaction.log.entry.OnePhaseCommit)3 SchemaRuleCommand (org.neo4j.kernel.impl.transaction.command.Command.SchemaRuleCommand)2 PhysicalTransactionRepresentation (org.neo4j.kernel.impl.transaction.log.PhysicalTransactionRepresentation)2 LogEntry (org.neo4j.kernel.impl.transaction.log.entry.LogEntry)2 File (java.io.File)1 InOrder (org.mockito.InOrder)1 ClassGuardedAdversary (org.neo4j.adversaries.ClassGuardedAdversary)1 CountingAdversary (org.neo4j.adversaries.CountingAdversary)1 GraphDatabaseService (org.neo4j.graphdb.GraphDatabaseService)1