All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.kernel.impl.api.state.TxState Maven / Gradle / Ivy

Go to download

Neo4j kernel is a lightweight, embedded Java database designed to store data structured as graphs rather than tables. For more information, see http://neo4j.org.

There is a newer version: 5.26.1
Show newest version
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.kernel.impl.api.state;

import static org.neo4j.collection.diffset.TrackableDiffSets.newMutableDiffSets;
import static org.neo4j.collection.diffset.TrackableDiffSets.newMutableLongDiffSets;
import static org.neo4j.collection.diffset.TrackableDiffSets.newRemovalsCountingDiffSets;
import static org.neo4j.collection.trackable.HeapTrackingCollections.newLongObjectMap;
import static org.neo4j.collection.trackable.HeapTrackingCollections.newMap;
import static org.neo4j.kernel.impl.api.state.TokenState.createTokenState;
import static org.neo4j.storageengine.api.txstate.RelationshipModifications.idsAsBatch;
import static org.neo4j.values.storable.Values.NO_VALUE;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.map.primitive.MutableLongObjectMap;
import org.eclipse.collections.api.set.primitive.MutableIntSet;
import org.eclipse.collections.impl.UnmodifiableMap;
import org.neo4j.collection.diffset.DiffSets;
import org.neo4j.collection.diffset.LongDiffSets;
import org.neo4j.collection.diffset.MutableDiffSets;
import org.neo4j.collection.diffset.MutableLongDiffSets;
import org.neo4j.collection.diffset.RemovalsCountingDiffSets;
import org.neo4j.collection.factory.CollectionsFactory;
import org.neo4j.collection.factory.OnHeapCollectionsFactory;
import org.neo4j.collection.trackable.HeapTrackingArrayList;
import org.neo4j.exceptions.KernelException;
import org.neo4j.internal.helpers.collection.Iterables;
import org.neo4j.internal.kernel.api.Upgrade;
import org.neo4j.internal.kernel.api.exceptions.DeletedNodeStillHasRelationshipsException;
import org.neo4j.internal.schema.ConstraintDescriptor;
import org.neo4j.internal.schema.IndexDescriptor;
import org.neo4j.internal.schema.SchemaDescriptor;
import org.neo4j.internal.schema.SchemaDescriptorPredicates;
import org.neo4j.internal.schema.SchemaDescriptors;
import org.neo4j.internal.schema.constraints.IndexBackedConstraintDescriptor;
import org.neo4j.kernel.api.txstate.TransactionState;
import org.neo4j.kernel.impl.api.chunk.ChunkedTransactionSink;
import org.neo4j.kernel.impl.transaction.tracing.TransactionEvent;
import org.neo4j.memory.DefaultScopedMemoryTracker;
import org.neo4j.memory.EmptyMemoryTracker;
import org.neo4j.memory.HeapEstimator;
import org.neo4j.memory.MemoryTracker;
import org.neo4j.memory.ScopedMemoryTracker;
import org.neo4j.storageengine.api.RelationshipDirection;
import org.neo4j.storageengine.api.RelationshipVisitor;
import org.neo4j.storageengine.api.RelationshipVisitorWithProperties;
import org.neo4j.storageengine.api.enrichment.ApplyEnrichmentStrategy;
import org.neo4j.storageengine.api.enrichment.EnrichmentMode;
import org.neo4j.storageengine.api.txstate.NodeState;
import org.neo4j.storageengine.api.txstate.RelationshipModifications;
import org.neo4j.storageengine.api.txstate.RelationshipModifications.NodeRelationshipIds;
import org.neo4j.storageengine.api.txstate.RelationshipState;
import org.neo4j.storageengine.api.txstate.TransactionStateBehaviour;
import org.neo4j.storageengine.api.txstate.TxStateVisitor;
import org.neo4j.util.VisibleForTesting;
import org.neo4j.values.storable.Value;
import org.neo4j.values.storable.ValueTuple;

/**
 * This class contains transaction-local changes to the graph. These changes can then be used to augment reads from the
 * committed state of the database (to make the local changes appear in local transaction read operations). At commit
 * time a visitor is sent into this class to convert the end result of the tx changes into a physical change-set.
 * 

* See {@link org.neo4j.kernel.impl.api.KernelTransactionImplementation} for how this happens. *

* This class is very large, as it has been used as a gathering point to consolidate all transaction state knowledge * into one component. Now that that work is done, this class should be refactored to increase transparency in how it * works. */ public class TxState implements TransactionState { private static final long SHALLOW_SIZE = HeapEstimator.shallowSizeOfInstance(TxState.class); /** * This factory must be used only for creating collections representing internal state that doesn't leak outside this class. */ private final CollectionsFactory collectionsFactory; private MutableLongObjectMap labelStatesMap; private MutableLongObjectMap nodeStatesMap; private MutableLongObjectMap relationshipTypeStatesMap; private MutableLongObjectMap relationshipStatesMap; private MutableLongObjectMap createdLabelTokens; private MutableLongObjectMap createdPropertyKeyTokens; private MutableLongObjectMap createdRelationshipTypeTokens; private MutableDiffSets indexChanges; private MutableDiffSets constraintsChanges; private RemovalsCountingDiffSets nodes; private RemovalsCountingDiffSets relationships; private MutableMap createdConstraintIndexesByConstraint; private MutableMap> indexUpdates; private Upgrade.KernelUpgrade upgrade; private final ScopedMemoryTracker stateMemoryTracker; private final TransactionStateBehaviour behaviour; private final ApplyEnrichmentStrategy enrichmentStrategy; private final ChunkedTransactionSink chunkWriter; private long revision; private long dataRevision; private final TransactionEvent transactionEvent; private boolean isMultiChunk; @VisibleForTesting public TxState() { this( OnHeapCollectionsFactory.INSTANCE, EmptyMemoryTracker.INSTANCE, TransactionStateBehaviour.DEFAULT_BEHAVIOUR, ApplyEnrichmentStrategy.NO_ENRICHMENT, ChunkedTransactionSink.EMPTY, TransactionEvent.NULL); } public TxState( CollectionsFactory collectionsFactory, MemoryTracker transactionTracker, TransactionStateBehaviour behaviour, ApplyEnrichmentStrategy enrichmentStrategy, ChunkedTransactionSink chunkWriter, TransactionEvent transactionEvent) { transactionTracker.allocateHeap(SHALLOW_SIZE); this.chunkWriter = chunkWriter; this.collectionsFactory = collectionsFactory; this.stateMemoryTracker = new DefaultScopedMemoryTracker(transactionTracker); this.behaviour = behaviour; this.enrichmentStrategy = enrichmentStrategy; this.transactionEvent = transactionEvent; } @Override public void accept(final TxStateVisitor visitor) throws KernelException { if (nodes != null) { nodes.getAdded().each(visitor::visitCreatedNode); } if (relationships != null) { try (HeapTrackingArrayList sortedNodeRelState = HeapTrackingArrayList.newArrayList(nodeStatesMap.size(), stateMemoryTracker)) { for (NodeStateImpl nodeState : nodeStatesMap.values()) { if (nodeState.isDeleted() && nodeState.hasAddedRelationships()) { // The usual case for nodes created in previous tx is handled by // IntegrityValidator/MutableRelationships, this is just for deleted nodes // with added rels in this tx. if (nodeState.isAddedInThisBatch()) { throw new DeletedNodeStillHasRelationshipsException(); } throw new DeletedNodeStillHasRelationshipsException(nodeState.getId()); } // For nodes that were added and removed in this tx there is no need to try to figure out any // relationship ids, there shouldn't be any. if (!(nodeState.isDeleted() && nodeState.isAddedInThisBatch())) { if (nodeState.hasAddedRelationships() || nodeState.hasRemovedRelationships()) { sortedNodeRelState.add(StateNodeRelationshipIds.createStateNodeRelationshipIds( nodeState, this::relationshipVisitWithProperties, stateMemoryTracker)); } } } sortedNodeRelState.sort(Comparator.comparingLong(NodeRelationshipIds::nodeId)); // Visit relationships, this will grab all the locks needed to do the updates visitor.visitRelationshipModifications(new RelationshipModifications() { @Override public void forEachSplit(IdsVisitor visitor) { sortedNodeRelState.forEach(visitor); } @Override public RelationshipBatch creations() { return idsAsBatch(relationships.getAdded(), TxState.this::relationshipVisitWithProperties); } @Override public RelationshipBatch deletions() { if (behaviour.keepMetaDataForDeletedRelationship()) { return idsAsBatch(relationships.getRemoved(), TxState.this::deletedRelationshipVisit); } else { return idsAsBatch(relationships.getRemoved()); } } }); } } if (nodes != null) { nodes.getRemoved().each(visitor::visitDeletedNode); } for (NodeState node : modifiedNodes()) { if (node.hasPropertyChanges()) { visitor.visitNodePropertyChanges( node.getId(), node.addedProperties(), node.changedProperties(), node.removedProperties()); } final LongDiffSets labelDiffSets = node.labelDiffSets(); if (!labelDiffSets.isEmpty()) { visitor.visitNodeLabelChanges(node.getId(), labelDiffSets.getAdded(), labelDiffSets.getRemoved()); } } for (RelationshipState rel : modifiedRelationships()) { if (relationships == null || !relationships.getAdded().contains(rel.getId())) { // Only specifically visit property changes for relationships that aren't created. // Created relationships will get added properties directly into the creation call instead. relationshipStatesMap .get(rel.getId()) .accept((relationshipId, typeId, startNodeId, endNodeId) -> visitor.visitRelPropertyChanges( relationshipId, typeId, startNodeId, endNodeId, rel.addedProperties(), rel.changedProperties(), rel.removedProperties())); } } if (indexChanges != null) { for (IndexDescriptor indexDescriptor : indexChanges.getAdded()) { visitor.visitAddedIndex(indexDescriptor); } indexChanges.getRemoved().forEach(visitor::visitRemovedIndex); } if (constraintsChanges != null) { for (ConstraintDescriptor added : constraintsChanges.getAdded()) { visitor.visitAddedConstraint(added); } constraintsChanges.getRemoved().forEach(visitor::visitRemovedConstraint); } if (createdLabelTokens != null) { createdLabelTokens.forEachKeyValue( (id, token) -> visitor.visitCreatedLabelToken(id, token.name, token.internal)); } if (createdPropertyKeyTokens != null) { createdPropertyKeyTokens.forEachKeyValue( (id, token) -> visitor.visitCreatedPropertyKeyToken(id, token.name, token.internal)); } if (createdRelationshipTypeTokens != null) { createdRelationshipTypeTokens.forEachKeyValue( (id, token) -> visitor.visitCreatedRelationshipTypeToken(id, token.name, token.internal)); } if (upgrade != null) { visitor.visitKernelUpgrade(upgrade); } } @Override public boolean hasChanges() { return revision != 0; } @Override public boolean hasDataChanges() { return getDataRevision() != 0; } public EnrichmentMode enrichmentMode() { return enrichmentStrategy.check(); } @Override public void reset() { labelStatesMap = null; nodeStatesMap = null; relationshipTypeStatesMap = null; relationshipStatesMap = null; createdLabelTokens = null; createdPropertyKeyTokens = null; createdRelationshipTypeTokens = null; indexChanges = null; constraintsChanges = null; nodes = null; relationships = null; createdConstraintIndexesByConstraint = null; indexUpdates = null; collectionsFactory.release(); stateMemoryTracker.reset(); } @Override public Iterable modifiedNodes() { if (nodeStatesMap == null) { return Iterables.empty(); } Collection nodeStates = nodeStatesMap.values(); return Iterables.cast(Iterables.filter(ns -> !ns.isDeleted(), nodeStates)); } @VisibleForTesting MutableLongDiffSets getOrCreateLabelStateNodeDiffSets(long labelId) { if (labelStatesMap == null) { labelStatesMap = newLongObjectMap(stateMemoryTracker); } return labelStatesMap.getIfAbsentPut( labelId, () -> newMutableLongDiffSets(collectionsFactory, stateMemoryTracker)); } private LongDiffSets getLabelStateNodeDiffSets(long labelId) { if (labelStatesMap == null) { return LongDiffSets.EMPTY; } final LongDiffSets nodeDiffSets = labelStatesMap.get(labelId); return nodeDiffSets == null ? LongDiffSets.EMPTY : nodeDiffSets; } @VisibleForTesting MutableLongDiffSets getOrCreateTypeStateRelationshipDiffSets(int relationshipType) { if (relationshipTypeStatesMap == null) { relationshipTypeStatesMap = newLongObjectMap(stateMemoryTracker); } return relationshipTypeStatesMap.getIfAbsentPut( relationshipType, () -> newMutableLongDiffSets(collectionsFactory, stateMemoryTracker)); } @Override public LongDiffSets nodeStateLabelDiffSets(long nodeId) { return getNodeState(nodeId).labelDiffSets(); } private MutableLongDiffSets getOrCreateNodeStateLabelDiffSets(long nodeId) { return getOrCreateNodeState(nodeId).getOrCreateLabelDiffSets(); } @Override public boolean nodeIsAddedInThisBatch(long nodeId) { return nodes != null && nodes.isAdded(nodeId); } @Override public boolean relationshipIsAddedInThisBatch(long relationshipId) { return relationships != null && relationships.isAdded(relationshipId); } private void changed() { revision++; } private void dataChanged() { changed(); dataRevision = revision; checkChunk(); } private void checkChunk() { chunkWriter.write(this, transactionEvent); } @Override public void nodeDoCreate(long id) { nodes().add(id); dataChanged(); } @Override public void nodeDoDelete(long nodeId) { nodes().remove(nodeId); if (nodeStatesMap != null) { // Previously this node state was removed completely and its state cleared. Was that to reduce memory // footprint for large deletions? // We have changed this so that it still keeps the removed relationships for this node so that they can be // handed to command creation // grouped by type and direction. NodeStateImpl nodeState = nodeStatesMap.get(nodeId); if (nodeState != null) { final LongDiffSets diff = nodeState.labelDiffSets(); diff.getAdded() .each(label -> getOrCreateLabelStateNodeDiffSets(label).remove(nodeId)); nodeState.markAsDeleted(); } } dataChanged(); } @Override public void relationshipDoCreate(long id, int relationshipTypeId, long startNodeId, long endNodeId) { relationships().add(id); if (startNodeId == endNodeId) { getOrCreateNodeState(startNodeId).addRelationship(id, relationshipTypeId, RelationshipDirection.LOOP); } else { getOrCreateNodeState(startNodeId).addRelationship(id, relationshipTypeId, RelationshipDirection.OUTGOING); getOrCreateNodeState(endNodeId).addRelationship(id, relationshipTypeId, RelationshipDirection.INCOMING); } getOrCreateRelationshipState(id, relationshipTypeId, startNodeId, endNodeId); getOrCreateTypeStateRelationshipDiffSets(relationshipTypeId).add(id); dataChanged(); } @Override public boolean nodeIsModifiedInThisBatch(long nodeId) { if (nodeStatesMap == null) { return false; } final var nodeState = nodeStatesMap.get(nodeId); return nodeState != null && !nodeState.isAddedInThisBatch() && !nodeState.isDeleted() && (!nodeState.labelDiffSets().isEmpty() || nodeState.hasPropertyChanges()); } @Override public boolean nodeIsDeletedInThisBatch(long nodeId) { return nodes != null && nodes.wasRemoved(nodeId); } @Override public void relationshipDoDelete(long id, int type, long startNodeId, long endNodeId) { RemovalsCountingDiffSets relationships = relationships(); boolean wasAddedInThisBatch = relationships.isAdded(id); relationships.remove(id); if (startNodeId == endNodeId) { getOrCreateNodeState(startNodeId).removeRelationship(id, type, RelationshipDirection.LOOP); } else { getOrCreateNodeState(startNodeId).removeRelationship(id, type, RelationshipDirection.OUTGOING); getOrCreateNodeState(endNodeId).removeRelationship(id, type, RelationshipDirection.INCOMING); } if (wasAddedInThisBatch || !behaviour.keepMetaDataForDeletedRelationship()) { if (relationshipStatesMap != null) { RelationshipStateImpl removed = relationshipStatesMap.remove(id); if (removed != null) { removed.clear(); } } } else { getOrCreateRelationshipState(id, type, startNodeId, endNodeId).setDeleted(); } getOrCreateTypeStateRelationshipDiffSets(type).remove(id); dataChanged(); } @Override public void relationshipDoDeleteAddedInThisBatch(long relationshipId) { getRelationshipState(relationshipId).accept(this::relationshipDoDelete); } @Override public boolean relationshipIsDeletedInThisBatch(long relationshipId) { return relationships != null && relationships.wasRemoved(relationshipId); } @Override public void nodeDoAddProperty(long nodeId, int newPropertyKeyId, Value value) { NodeStateImpl nodeState = getOrCreateNodeState(nodeId); nodeState.addProperty(newPropertyKeyId, value); dataChanged(); } @Override public void nodeDoChangeProperty(long nodeId, int propertyKeyId, Value newValue) { getOrCreateNodeState(nodeId).changeProperty(propertyKeyId, newValue); dataChanged(); } @Override public void relationshipDoReplaceProperty( long relationshipId, int type, long startNode, long endNode, int propertyKeyId, Value replacedValue, Value newValue) { RelationshipStateImpl relationshipState = getOrCreateRelationshipState(relationshipId, type, startNode, endNode); if (replacedValue != NO_VALUE) { relationshipState.changeProperty(propertyKeyId, newValue); } else { relationshipState.addProperty(propertyKeyId, newValue); } dataChanged(); } @Override public void nodeDoRemoveProperty(long nodeId, int propertyKeyId) { getOrCreateNodeState(nodeId).removeProperty(propertyKeyId); dataChanged(); } @Override public void relationshipDoRemoveProperty( long relationshipId, int type, long startNode, long endNode, int propertyKeyId) { getOrCreateRelationshipState(relationshipId, type, startNode, endNode).removeProperty(propertyKeyId); dataChanged(); } @Override public void nodeDoAddLabel(int labelId, long nodeId) { getOrCreateLabelStateNodeDiffSets(labelId).add(nodeId); getOrCreateNodeStateLabelDiffSets(nodeId).add(labelId); dataChanged(); } @Override public void nodeDoRemoveLabel(int labelId, long nodeId) { getOrCreateLabelStateNodeDiffSets(labelId).remove(nodeId); getOrCreateNodeStateLabelDiffSets(nodeId).remove(labelId); dataChanged(); } @Override public void labelDoCreateForName(String labelName, boolean internal, long id) { if (createdLabelTokens == null) { createdLabelTokens = newLongObjectMap(stateMemoryTracker); } createdLabelTokens.put(id, createTokenState(labelName, internal, stateMemoryTracker)); changed(); } @Override public void propertyKeyDoCreateForName(String propertyKeyName, boolean internal, int id) { if (createdPropertyKeyTokens == null) { createdPropertyKeyTokens = newLongObjectMap(stateMemoryTracker); } createdPropertyKeyTokens.put(id, createTokenState(propertyKeyName, internal, stateMemoryTracker)); changed(); } @Override public void relationshipTypeDoCreateForName(String labelName, boolean internal, int id) { if (createdRelationshipTypeTokens == null) { createdRelationshipTypeTokens = newLongObjectMap(stateMemoryTracker); } createdRelationshipTypeTokens.put(id, createTokenState(labelName, internal, stateMemoryTracker)); changed(); } @Override public NodeState getNodeState(long id) { if (nodeStatesMap == null) { return NodeStateImpl.EMPTY; } final NodeStateImpl nodeState = nodeStatesMap.get(id); return nodeState == null || nodeState.isDeleted() ? NodeStateImpl.EMPTY : nodeState; } @Override public RelationshipState getRelationshipState(long id) { if (relationshipStatesMap == null) { return RelationshipStateImpl.EMPTY; } final RelationshipStateImpl relationshipState = relationshipStatesMap.get(id); return relationshipState == null || relationshipState.isDeleted() ? RelationshipStateImpl.EMPTY : relationshipState; } @Override public RelationshipState getRelationshipStateEvenDeleted(long id) { if (relationshipStatesMap == null) { return RelationshipStateImpl.EMPTY; } final RelationshipStateImpl relationshipState = relationshipStatesMap.get(id); return relationshipState == null ? RelationshipStateImpl.EMPTY : relationshipState; } @Override public MutableIntSet augmentLabels(MutableIntSet labels, NodeState nodeState) { final LongDiffSets labelDiffSets = nodeState.labelDiffSets(); if (!labelDiffSets.isEmpty()) { labelDiffSets.getRemoved().forEach(value -> labels.remove((int) value)); labelDiffSets.getAdded().forEach(element -> labels.add((int) element)); } return labels; } @Override public LongDiffSets nodesWithLabelChanged(int label) { return getLabelStateNodeDiffSets(label); } @Override public void indexDoAdd(IndexDescriptor descriptor) { MutableDiffSets diff = indexChangesDiffSets(); if (!diff.unRemove(descriptor)) { diff.add(descriptor); } changed(); } @Override public void indexDoDrop(IndexDescriptor index) { indexChangesDiffSets().remove(index); changed(); } @Override public DiffSets indexDiffSetsByLabel(int labelId) { return indexChangesDiffSets().filterAdded(SchemaDescriptorPredicates.hasLabel(labelId)); } @Override public DiffSets indexDiffSetsByRelationshipType(int relationshipType) { return indexChangesDiffSets().filterAdded(SchemaDescriptorPredicates.hasRelType(relationshipType)); } @Override public DiffSets indexDiffSetsBySchema(SchemaDescriptor schema) { return indexChangesDiffSets() .filterAdded(indexDescriptor -> indexDescriptor.schema().equals(schema)); } @Override public DiffSets indexChanges() { return DiffSets.Empty.ifNull(indexChanges); } private MutableDiffSets indexChangesDiffSets() { if (indexChanges == null) { indexChanges = newMutableDiffSets(stateMemoryTracker); } return indexChanges; } @Override public LongDiffSets addedAndRemovedNodes() { return nodes == null ? LongDiffSets.EMPTY : nodes; } private RemovalsCountingDiffSets nodes() { if (nodes == null) { nodes = newRemovalsCountingDiffSets(collectionsFactory, stateMemoryTracker); } return nodes; } @Override public LongDiffSets relationshipsWithTypeChanged(int type) { if (relationshipTypeStatesMap == null) { return LongDiffSets.EMPTY; } MutableLongDiffSets relationshipDiffSets = relationshipTypeStatesMap.get(type); return relationshipDiffSets == null ? LongDiffSets.EMPTY : relationshipDiffSets; } @Override public LongDiffSets addedAndRemovedRelationships() { return relationships == null ? LongDiffSets.EMPTY : relationships; } private RemovalsCountingDiffSets relationships() { if (relationships == null) { relationships = newRemovalsCountingDiffSets(collectionsFactory, stateMemoryTracker); } return relationships; } @Override public Iterable modifiedRelationships() { return relationshipStatesMap == null ? Iterables.empty() : Iterables.cast(Iterables.filter(rel -> !rel.isDeleted(), relationshipStatesMap.values())); } @VisibleForTesting NodeStateImpl getOrCreateNodeState(long nodeId) { if (nodeStatesMap == null) { nodeStatesMap = newLongObjectMap(stateMemoryTracker); } return nodeStatesMap.getIfAbsentPut(nodeId, () -> newNodeState(nodeId)); } private RelationshipStateImpl getOrCreateRelationshipState( long relationshipId, int type, long startNode, long endNode) { if (relationshipStatesMap == null) { relationshipStatesMap = newLongObjectMap(stateMemoryTracker); } return relationshipStatesMap.getIfAbsentPut( relationshipId, () -> newRelationshipState(relationshipId, type, startNode, endNode)); } @Override public void constraintDoAdd(IndexBackedConstraintDescriptor constraint, IndexDescriptor index) { constraintsChangesDiffSets().add(constraint); if (index != null && index != IndexDescriptor.NO_INDEX) { createdConstraintIndexesByConstraint().put(constraint, index); } changed(); } @Override public void constraintDoAdd(ConstraintDescriptor constraint) { constraintsChangesDiffSets().add(constraint); changed(); } @Override public DiffSets constraintsChangesForLabel(int labelId) { return constraintsChangesDiffSets().filterAdded(SchemaDescriptorPredicates.hasLabel(labelId)); } @Override public DiffSets constraintsChangesForSchema(SchemaDescriptor descriptor) { return constraintsChangesDiffSets().filterAdded(SchemaDescriptors.equalTo(descriptor)); } @Override public DiffSets constraintsChangesForRelationshipType(int relTypeId) { return constraintsChangesDiffSets().filterAdded(SchemaDescriptorPredicates.hasRelType(relTypeId)); } @Override public DiffSets constraintsChanges() { return DiffSets.Empty.ifNull(constraintsChanges); } private MutableDiffSets constraintsChangesDiffSets() { if (constraintsChanges == null) { constraintsChanges = newMutableDiffSets(stateMemoryTracker); } return constraintsChanges; } @Override public void constraintDoDrop(ConstraintDescriptor constraint) { constraintsChangesDiffSets().remove(constraint); changed(); } @Override public Iterator constraintIndexesCreatedInTx() { if (hasConstraintIndexesCreatedInTx()) { return createdConstraintIndexesByConstraint.values().iterator(); } return Collections.emptyIterator(); } @Override public boolean hasConstraintIndexesCreatedInTx() { return createdConstraintIndexesByConstraint != null && !createdConstraintIndexesByConstraint.isEmpty(); } @Override public UnmodifiableMap getIndexUpdates(SchemaDescriptor schema) { if (indexUpdates == null) { return null; } Map updates = indexUpdates.get(schema); if (updates == null) { return null; } return new UnmodifiableMap<>(updates); } @Override public NavigableMap getSortedIndexUpdates(SchemaDescriptor descriptor) { if (indexUpdates == null) { return null; } Map updates = indexUpdates.get(descriptor); if (updates == null) { return null; } TreeMap sortedUpdates; if (updates instanceof TreeMap) { sortedUpdates = (TreeMap) updates; } else { sortedUpdates = new TreeMap<>(ValueTuple.COMPARATOR); sortedUpdates.putAll(updates); indexUpdates.put(descriptor, sortedUpdates); } return Collections.unmodifiableNavigableMap(sortedUpdates); } @Override public void indexDoUpdateEntry( SchemaDescriptor descriptor, long entityIdId, ValueTuple propertiesBefore, ValueTuple propertiesAfter) { Map updates = getOrCreateIndexUpdatesByDescriptor(descriptor); if (propertiesBefore != null) { MutableLongDiffSets before = getOrCreateIndexUpdatesForSeek(updates, propertiesBefore); before.remove(entityIdId); } if (propertiesAfter != null) { MutableLongDiffSets after = getOrCreateIndexUpdatesForSeek(updates, propertiesAfter); after.add(entityIdId); } } @Override public void kernelDoUpgrade(Upgrade.KernelUpgrade kernelUpgrade) { assert upgrade == null; upgrade = kernelUpgrade; changed(); } @Override public MemoryTracker memoryTracker() { return stateMemoryTracker; } @VisibleForTesting MutableLongDiffSets getOrCreateIndexUpdatesForSeek( Map updates, ValueTuple values) { return updates.computeIfAbsent(values, value -> newMutableLongDiffSets(collectionsFactory, stateMemoryTracker)); } private Map getOrCreateIndexUpdatesByDescriptor(SchemaDescriptor schema) { if (indexUpdates == null) { indexUpdates = newMap(stateMemoryTracker); } return indexUpdates.getIfAbsentPut(schema, () -> newMap(stateMemoryTracker)); } private Map createdConstraintIndexesByConstraint() { if (createdConstraintIndexesByConstraint == null) { createdConstraintIndexesByConstraint = newMap(stateMemoryTracker); } return createdConstraintIndexesByConstraint; } @Override public boolean relationshipVisit(long relId, RelationshipVisitor visitor) throws EX { return getRelationshipState(relId).accept(visitor); } public boolean relationshipVisitWithProperties( long relId, RelationshipVisitorWithProperties visitor) throws EX { return getRelationshipState(relId).accept(visitor); } public boolean deletedRelationshipVisit( long relId, RelationshipVisitorWithProperties visitor) throws EX { return getRelationshipStateEvenDeleted(relId).accept(visitor); } public void markAsMultiChunk() { isMultiChunk = true; } @Override public boolean isMultiChunk() { return isMultiChunk; } @Override public long getDataRevision() { return dataRevision; } private NodeStateImpl newNodeState(long nodeId) { return NodeStateImpl.createNodeState( nodeId, nodeIsAddedInThisBatch(nodeId), collectionsFactory, stateMemoryTracker); } private RelationshipStateImpl newRelationshipState(long relationshipId, int type, long startNode, long endNode) { return RelationshipStateImpl.createRelationshipStateImpl( relationshipId, type, startNode, endNode, collectionsFactory, stateMemoryTracker); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy