use of org.neo4j.storageengine.api.StorageCommand in project neo4j by neo4j.
the class RaftContentByteBufferMarshalTest method shouldSerializeTransactionRepresentation.
@Test
public void shouldSerializeTransactionRepresentation() throws Exception {
// given
CoreReplicatedContentMarshal serializer = new CoreReplicatedContentMarshal();
Collection<StorageCommand> commands = new ArrayList<>();
IndexCommand.AddNodeCommand addNodeCommand = new IndexCommand.AddNodeCommand();
addNodeCommand.init(0, 0, 0, 0);
commands.add(addNodeCommand);
byte[] extraHeader = new byte[0];
PhysicalTransactionRepresentation txIn = new PhysicalTransactionRepresentation(commands);
txIn.setHeader(extraHeader, -1, -1, 0, 0, 0, 0);
ReplicatedTransaction in = ReplicatedTransactionFactory.createImmutableReplicatedTransaction(txIn);
// when
ByteBuf buf = Unpooled.buffer();
serializer.marshal(in, new NetworkFlushableByteBuf(buf));
ReplicatedTransaction out = (ReplicatedTransaction) serializer.unmarshal(new NetworkReadableClosableChannelNetty4(buf));
TransactionRepresentation txOut = ReplicatedTransactionFactory.extractTransactionRepresentation(out, extraHeader);
// then
assertEquals(in, out);
assertEquals(txIn, txOut);
}
use of org.neo4j.storageengine.api.StorageCommand 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;
}
use of org.neo4j.storageengine.api.StorageCommand in project neo4j by neo4j.
the class KernelTransactionImplementation method commit.
private long commit() throws TransactionFailureException {
boolean success = false;
long txId = READ_ONLY;
try (CommitEvent commitEvent = transactionEvent.beginCommitEvent()) {
// Trigger transaction "before" hooks.
if (hasDataChanges()) {
try {
hooksState = hooks.beforeCommit(txState, this, storageEngine.storeReadLayer(), storageStatement);
if (hooksState != null && hooksState.failed()) {
TransactionHookException cause = hooksState.failure();
throw new TransactionFailureException(Status.Transaction.TransactionHookFailed, cause, "");
}
} finally {
beforeHookInvoked = true;
}
}
// Convert changes into commands and commit
if (hasChanges()) {
// grab all optimistic locks now, locks can't be deferred any further
statementLocks.prepareForCommit();
// use pessimistic locks for the rest of the commit process, locks can't be deferred any further
Locks.Client commitLocks = statementLocks.pessimistic();
// Gather up commands from the various sources
Collection<StorageCommand> extractedCommands = new ArrayList<>();
storageEngine.createCommands(extractedCommands, txState, storageStatement, commitLocks, lastTransactionIdWhenStarted);
if (hasLegacyIndexChanges()) {
legacyIndexTransactionState.extractCommands(extractedCommands);
}
/* Here's the deal: we track a quick-to-access hasChanges in transaction state which is true
* if there are any changes imposed by this transaction. Some changes made inside a transaction undo
* previously made changes in that same transaction, and so at some point a transaction may have
* changes and at another point, after more changes seemingly,
* the transaction may not have any changes.
* However, to track that "undoing" of the changes is a bit tedious, intrusive and hard to maintain
* and get right.... So to really make sure the transaction has changes we re-check by looking if we
* have produced any commands to add to the logical log.
*/
if (!extractedCommands.isEmpty()) {
// Finish up the whole transaction representation
PhysicalTransactionRepresentation transactionRepresentation = new PhysicalTransactionRepresentation(extractedCommands);
TransactionHeaderInformation headerInformation = headerInformationFactory.create();
long timeCommitted = clock.millis();
transactionRepresentation.setHeader(headerInformation.getAdditionalHeader(), headerInformation.getMasterId(), headerInformation.getAuthorId(), startTimeMillis, lastTransactionIdWhenStarted, timeCommitted, commitLocks.getLockSessionId());
// Commit the transaction
success = true;
TransactionToApply batch = new TransactionToApply(transactionRepresentation);
txId = transactionId = commitProcess.commit(batch, commitEvent, INTERNAL);
commitTime = timeCommitted;
}
}
success = true;
return txId;
} catch (ConstraintValidationException | CreateConstraintFailureException e) {
throw new ConstraintViolationTransactionFailureException(e.getUserMessage(new KeyReadTokenNameLookup(currentTransactionOperations.keyReadOperations())), e);
} finally {
if (!success) {
rollback();
} else {
afterCommit(txId);
}
}
}
use of org.neo4j.storageengine.api.StorageCommand in project neo4j by neo4j.
the class BatchingTransactionAppenderTest method singleCreateNodeCommand.
private Collection<StorageCommand> singleCreateNodeCommand(long id) {
Collection<StorageCommand> commands = new ArrayList<>();
NodeRecord before = new NodeRecord(id);
NodeRecord after = new NodeRecord(id);
after.setInUse(true);
commands.add(new NodeCommand(before, after));
return commands;
}
use of org.neo4j.storageengine.api.StorageCommand in project neo4j by neo4j.
the class PhysicalLogicalTransactionStoreTest method singleCreateNodeCommand.
private Collection<StorageCommand> singleCreateNodeCommand() {
Collection<StorageCommand> commands = new ArrayList<>();
long id = 0;
NodeRecord before = new NodeRecord(id);
NodeRecord after = new NodeRecord(id);
after.setInUse(true);
commands.add(new Command.NodeCommand(before, after));
return commands;
}
Aggregations