use of org.neo4j.internal.schema.SchemaRule in project neo4j by neo4j.
the class LogCommandSerializationV3_0_10 method readSchemaRule.
private static SchemaRule readSchemaRule(Collection<DynamicRecord> recordsBefore) {
// TODO: Why was this assertion here?
// assert first(recordsBefore).inUse() : "Asked to deserialize schema records that were not in
// use.";
SchemaRule rule;
ByteBuffer deserialized = AbstractDynamicStore.concatData(recordsBefore, new byte[100]);
try {
rule = SchemaRuleSerialization35.deserialize(Iterables.first(recordsBefore).getId(), deserialized);
} catch (MalformedSchemaRuleException e) {
return null;
}
return rule;
}
use of org.neo4j.internal.schema.SchemaRule in project neo4j by neo4j.
the class LogCommandSerializationV4_0 method readSchemaRuleCommand.
@Override
protected Command readSchemaRuleCommand(ReadableChannel channel) throws IOException {
long id = channel.getLong();
byte schemaRulePresence = channel.get();
boolean hasSchemaRule = schemaRulePresence == SchemaRecord.COMMAND_HAS_SCHEMA_RULE;
SchemaRecord before = readSchemaRecord(id, channel);
SchemaRecord after = readSchemaRecord(id, channel);
markAfterRecordAsCreatedIfCommandLooksCreated(before, after);
SchemaRule schemaRule = null;
if (hasSchemaRule) {
schemaRule = readSchemaRule(id, channel);
}
return new Command.SchemaRuleCommand(this, before, after, schemaRule);
}
use of org.neo4j.internal.schema.SchemaRule in project neo4j by neo4j.
the class SchemaStorage35 method loadAllSchemaRules.
/**
* Scans the schema store and loads all {@link SchemaRule rules} in it. This method is written with the assumption
* that there's no id reuse on schema records.
*
* @param returnType type of {@link SchemaRule} to load.
* @return {@link Iterator} of the loaded schema rules, lazily loaded when advancing the iterator.
*/
private <ReturnType extends SchemaRule> Iterator<ReturnType> loadAllSchemaRules(final Class<ReturnType> returnType, CursorContext cursorContext, ThrowingConsumer<Exception, RuntimeException> malformedExceptionHandler) {
return new PrefetchingIterator<>() {
private final long highestId = schemaStore.getHighestPossibleIdInUse(cursorContext);
private long currentId = 1;
/*record 0 contains the block size*/
private final byte[] scratchData = newRecordBuffer();
private final DynamicRecord record = schemaStore.newRecord();
@Override
protected ReturnType fetchNextOrNull() {
while (currentId <= highestId) {
try {
long id = currentId++;
schemaStore.getRecord(id, record, RecordLoad.LENIENT_CHECK, cursorContext);
if (!record.inUse()) {
continue;
}
schemaStore.getRecord(id, record, RecordLoad.NORMAL, cursorContext);
if (record.isStartRecord()) {
// that we're reading and that rule may have spanned multiple dynamic records.
try {
Collection<DynamicRecord> records;
try {
records = schemaStore.getRecords(id, RecordLoad.NORMAL, false, cursorContext);
} catch (InvalidRecordException e) {
// This may have been due to a concurrent drop of this rule.
continue;
}
SchemaRule schemaRule = SchemaStore35.readSchemaRule(id, records, scratchData);
if (returnType.isInstance(schemaRule)) {
return returnType.cast(schemaRule);
}
} catch (MalformedSchemaRuleException e) {
throw new RuntimeException(e);
}
}
} catch (Exception e) {
malformedExceptionHandler.accept(e);
}
}
return null;
}
};
}
use of org.neo4j.internal.schema.SchemaRule in project neo4j by neo4j.
the class SchemaStorage method streamAllSchemaRules.
@VisibleForTesting
Stream<SchemaRule> streamAllSchemaRules(boolean ignoreMalformed, CursorContext cursorContext) {
long startId = schemaStore.getNumberOfReservedLowIds();
long endId = schemaStore.getHighId();
Stream<IndexDescriptor> nli = Stream.empty();
KernelVersion currentVersion;
try {
currentVersion = versionSupplier.kernelVersion();
} catch (IllegalStateException ignored) {
// If KernelVersion is missing we are an older store.
currentVersion = KernelVersion.V4_2;
}
if (currentVersion.isLessThan(KernelVersion.VERSION_IN_WHICH_TOKEN_INDEXES_ARE_INTRODUCED)) {
nli = Stream.of(IndexDescriptor.INJECTED_NLI);
}
return Stream.concat(LongStream.range(startId, endId).mapToObj(id -> schemaStore.getRecord(id, schemaStore.newRecord(), RecordLoad.LENIENT_ALWAYS, cursorContext)).filter(AbstractBaseRecord::inUse).flatMap(record -> readSchemaRuleThrowingRuntimeException(record, ignoreMalformed, cursorContext)), nli);
}
use of org.neo4j.internal.schema.SchemaRule in project neo4j by neo4j.
the class TransactionRecordState method extractCommands.
@Override
public void extractCommands(Collection<StorageCommand> commands, MemoryTracker memoryTracker) throws TransactionFailureException {
assert !prepared : "Transaction has already been prepared";
integrityValidator.validateTransactionStartKnowledge(lastCommittedTxWhenTransactionStarted);
int noOfCommands = recordChangeSet.changeSize();
var labelTokenChanges = recordChangeSet.getLabelTokenChanges().changes();
memoryTracker.allocateHeap(labelTokenChanges.size() * Command.LabelTokenCommand.HEAP_SIZE);
for (RecordProxy<LabelTokenRecord, Void> record : labelTokenChanges) {
commands.add(new Command.LabelTokenCommand(commandSerialization, record.getBefore(), record.forReadingLinkage()));
}
var relationshipTypeTokenChanges = recordChangeSet.getRelationshipTypeTokenChanges().changes();
memoryTracker.allocateHeap(relationshipTypeTokenChanges.size() * Command.RelationshipTypeTokenCommand.HEAP_SIZE);
for (RecordProxy<RelationshipTypeTokenRecord, Void> record : relationshipTypeTokenChanges) {
commands.add(new Command.RelationshipTypeTokenCommand(commandSerialization, record.getBefore(), record.forReadingLinkage()));
}
var propertyKeyTokenChanges = recordChangeSet.getPropertyKeyTokenChanges().changes();
memoryTracker.allocateHeap(propertyKeyTokenChanges.size() * Command.PropertyKeyTokenCommand.HEAP_SIZE);
for (RecordProxy<PropertyKeyTokenRecord, Void> record : propertyKeyTokenChanges) {
commands.add(new Command.PropertyKeyTokenCommand(commandSerialization, record.getBefore(), record.forReadingLinkage()));
}
// Collect nodes, relationships, properties
Command[] nodeCommands = EMPTY_COMMANDS;
int skippedCommands = 0;
var nodeChanges = recordChangeSet.getNodeRecords().changes();
if (!nodeChanges.isEmpty()) {
memoryTracker.allocateHeap(nodeChanges.size() * Command.NodeCommand.HEAP_SIZE);
nodeCommands = new Command[nodeChanges.size()];
int i = 0;
for (RecordProxy<NodeRecord, Void> change : nodeChanges) {
NodeRecord record = prepared(change, nodeStore);
IntegrityValidator.validateNodeRecord(record);
nodeCommands[i++] = new Command.NodeCommand(commandSerialization, change.getBefore(), record);
}
Arrays.sort(nodeCommands, COMMAND_COMPARATOR);
}
Command[] relCommands = EMPTY_COMMANDS;
var relationshipChanges = recordChangeSet.getRelRecords().changes();
if (!relationshipChanges.isEmpty()) {
memoryTracker.allocateHeap(relationshipChanges.size() * Command.RelationshipCommand.HEAP_SIZE);
relCommands = new Command[relationshipChanges.size()];
int i = 0;
for (RecordProxy<RelationshipRecord, Void> change : relationshipChanges) {
relCommands[i++] = new Command.RelationshipCommand(commandSerialization, change.getBefore(), prepared(change, relationshipStore));
}
Arrays.sort(relCommands, COMMAND_COMPARATOR);
}
Command[] propCommands = EMPTY_COMMANDS;
var propertyChanges = recordChangeSet.getPropertyRecords().changes();
if (!propertyChanges.isEmpty()) {
memoryTracker.allocateHeap(propertyChanges.size() * Command.PropertyCommand.HEAP_SIZE);
propCommands = new Command[propertyChanges.size()];
int i = 0;
for (RecordProxy<PropertyRecord, PrimitiveRecord> change : propertyChanges) {
propCommands[i++] = new Command.PropertyCommand(commandSerialization, change.getBefore(), prepared(change, propertyStore));
}
Arrays.sort(propCommands, COMMAND_COMPARATOR);
}
Command[] relGroupCommands = EMPTY_COMMANDS;
var relationshipGroupChanges = recordChangeSet.getRelGroupRecords().changes();
if (!relationshipGroupChanges.isEmpty()) {
memoryTracker.allocateHeap(relationshipGroupChanges.size() * Command.RelationshipGroupCommand.HEAP_SIZE);
relGroupCommands = new Command[relationshipGroupChanges.size()];
int i = 0;
for (RecordProxy<RelationshipGroupRecord, Integer> change : relationshipGroupChanges) {
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.
*
* 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(commandSerialization, change.getBefore(), prepared(change, relationshipGroupStore));
}
relGroupCommands = i < relGroupCommands.length ? Arrays.copyOf(relGroupCommands, i) : relGroupCommands;
Arrays.sort(relGroupCommands, COMMAND_COMPARATOR);
}
addFiltered(commands, Mode.CREATE, propCommands, relCommands, relGroupCommands, nodeCommands);
addFiltered(commands, Mode.UPDATE, propCommands, relCommands, relGroupCommands, nodeCommands);
addFiltered(commands, Mode.DELETE, relCommands, relGroupCommands, nodeCommands);
EnumMap<Mode, List<Command>> schemaChangeByMode = new EnumMap<>(Mode.class);
var schemaRuleChange = recordChangeSet.getSchemaRuleChanges().changes();
memoryTracker.allocateHeap(schemaRuleChange.size() * Command.SchemaRuleCommand.HEAP_SIZE);
for (RecordProxy<SchemaRecord, SchemaRule> change : schemaRuleChange) {
SchemaRecord schemaRecord = change.forReadingLinkage();
SchemaRule rule = change.getAdditionalData();
if (schemaRecord.inUse()) {
integrityValidator.validateSchemaRule(rule);
}
Command.SchemaRuleCommand cmd = new Command.SchemaRuleCommand(commandSerialization, change.getBefore(), change.forChangingData(), rule);
schemaChangeByMode.computeIfAbsent(cmd.getMode(), MODE_TO_ARRAY_LIST).add(cmd);
}
commands.addAll(schemaChangeByMode.getOrDefault(Mode.DELETE, Collections.emptyList()));
commands.addAll(schemaChangeByMode.getOrDefault(Mode.CREATE, Collections.emptyList()));
commands.addAll(schemaChangeByMode.getOrDefault(Mode.UPDATE, Collections.emptyList()));
// Add deleted property commands last, so they happen after the schema record changes.
// This extends the lifetime of property records just past the last moment of use,
// and prevents reading and deleting of schema records from racing, and making the
// schema records look malformed.
addFiltered(commands, Mode.DELETE, propCommands);
assert commands.size() == noOfCommands - skippedCommands : format("Expected %d final commands, got %d " + "instead, with %d skipped", noOfCommands, commands.size(), skippedCommands);
if (groupDegreesUpdater.degrees != null) {
memoryTracker.allocateHeap(groupDegreesUpdater.degrees.size() * Command.GroupDegreeCommand.SHALLOW_SIZE);
groupDegreesUpdater.degrees.forEachKeyValue((key, delta) -> {
if (delta.longValue() != 0) {
long groupId = Command.GroupDegreeCommand.groupIdFromCombinedKey(key);
RelationshipDirection direction = Command.GroupDegreeCommand.directionFromCombinedKey(key);
commands.add(new Command.GroupDegreeCommand(groupId, direction, delta.longValue()));
}
});
}
prepared = true;
}
Aggregations