From 9942caffea7c52dfd94b703ecda7856598cd3c2b Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Tue, 5 Mar 2024 16:36:04 +0100 Subject: [PATCH] Add 5.18 compatibility layer --- .../5.18/neo4j-kernel-adapter/build.gradle | 65 ++ .../compat/_518/Neo4jProxyFactoryImpl.java | 44 ++ .../_518/BoltTransactionRunnerImpl.java | 103 ++++ .../compat/_518/Neo4jProxyFactoryImpl.java | 44 ++ .../neo4j/gds/compat/_518/Neo4jProxyImpl.java | 245 ++++++++ .../5.18/storage-engine-adapter/build.gradle | 67 ++ .../_518/InMemoryStorageEngineFactory.java | 268 ++++++++ .../_518/StorageEngineProxyFactoryImpl.java | 44 ++ .../InMemoryCommandCreationContextImpl.java | 112 ++++ .../compat/_518/InMemoryCountsStoreImpl.java | 133 ++++ .../_518/InMemoryMetaDataProviderImpl.java | 201 ++++++ .../gds/compat/_518/InMemoryNodeCursor.java | 87 +++ .../_518/InMemoryNodePropertyCursor.java | 45 ++ .../compat/_518/InMemoryPropertyCursor.java | 71 +++ .../_518/InMemoryPropertySelectionImpl.java | 55 ++ .../InMemoryRelationshipPropertyCursor.java | 60 ++ .../_518/InMemoryRelationshipScanCursor.java | 61 ++ .../InMemoryRelationshipTraversalCursor.java | 47 ++ .../_518/InMemoryStorageEngineFactory.java | 574 ++++++++++++++++++ .../_518/InMemoryStorageEngineImpl.java | 355 +++++++++++ .../compat/_518/InMemoryStorageLocksImpl.java | 86 +++ .../gds/compat/_518/InMemoryStoreVersion.java | 69 +++ .../_518/InMemoryTransactionIdStoreImpl.java | 129 ++++ .../gds/compat/_518/InMemoryVersionCheck.java | 72 +++ .../_518/StorageEngineProxyFactoryImpl.java | 44 ++ .../compat/_518/StorageEngineProxyImpl.java | 152 +++++ .../InMemoryLogVersionRepository518.java | 71 +++ ...nMemoryStorageCommandReaderFactory518.java | 43 ++ .../InMemoryStorageReader518.java | 364 +++++++++++ gradle/dependencies.gradle | 1 + 30 files changed, 3712 insertions(+) create mode 100644 compatibility/5.18/neo4j-kernel-adapter/build.gradle create mode 100644 compatibility/5.18/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_518/Neo4jProxyFactoryImpl.java create mode 100644 compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/BoltTransactionRunnerImpl.java create mode 100644 compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/Neo4jProxyFactoryImpl.java create mode 100644 compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/Neo4jProxyImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/build.gradle create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_518/InMemoryStorageEngineFactory.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_518/StorageEngineProxyFactoryImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryCommandCreationContextImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryCountsStoreImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryMetaDataProviderImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryNodeCursor.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryNodePropertyCursor.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryPropertyCursor.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryPropertySelectionImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipPropertyCursor.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipScanCursor.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipTraversalCursor.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageEngineFactory.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageEngineImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageLocksImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStoreVersion.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryTransactionIdStoreImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryVersionCheck.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/StorageEngineProxyFactoryImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/StorageEngineProxyImpl.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository518.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory518.java create mode 100644 compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader518.java diff --git a/compatibility/5.18/neo4j-kernel-adapter/build.gradle b/compatibility/5.18/neo4j-kernel-adapter/build.gradle new file mode 100644 index 0000000000..736331a2f3 --- /dev/null +++ b/compatibility/5.18/neo4j-kernel-adapter/build.gradle @@ -0,0 +1,65 @@ +apply plugin: 'java-library' +apply plugin: 'me.champeau.mrjar' + +description = 'Neo4j Graph Data Science :: Neo4j Kernel Adapter 5.18' + +group = 'org.neo4j.gds' + +// for all 5.x versions +if (ver.'neo4j'.startsWith('5.')) { + sourceSets { + main { + java { + srcDirs = ['src/main/java17'] + } + } + } + + dependencies { + annotationProcessor project(':annotations') + annotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables' + annotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.18' + + compileOnly project(':annotations') + compileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables' + compileOnly group: 'org.neo4j', name: 'annotations', version: neos.'5.18' + compileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.18' + compileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.18' + compileOnly group: 'org.neo4j.community', name: 'it-test-support', version: neos.'5.18' + + implementation project(':neo4j-kernel-adapter-api') + implementation project(':neo4j-kernel-adapter-5-common') + } +} else { + multiRelease { + targetVersions 11, 17 + } + + if (!project.hasProperty('no-forbidden-apis')) { + forbiddenApisJava17 { + exclude('**') + } + } + + dependencies { + annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j' + + compileOnly project(':annotations') + compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j' + + implementation project(':neo4j-kernel-adapter-api') + + java17AnnotationProcessor project(':annotations') + java17AnnotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables' + java17AnnotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.18' + + java17CompileOnly project(':annotations') + java17CompileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables' + java17CompileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.18' + java17CompileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.18' + java17CompileOnly group: 'org.neo4j.community', name: 'it-test-support', version: neos.'5.18' + + java17Implementation project(':neo4j-kernel-adapter-api') + java17Implementation project(':neo4j-kernel-adapter-5-common') + } +} diff --git a/compatibility/5.18/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_518/Neo4jProxyFactoryImpl.java b/compatibility/5.18/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_518/Neo4jProxyFactoryImpl.java new file mode 100644 index 0000000000..d96429f8dd --- /dev/null +++ b/compatibility/5.18/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/_518/Neo4jProxyFactoryImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.annotations.service.ServiceProvider; +import org.neo4j.gds.compat.Neo4jProxyApi; +import org.neo4j.gds.compat.Neo4jProxyFactory; +import org.neo4j.gds.compat.Neo4jVersion; + +@ServiceProvider +public final class Neo4jProxyFactoryImpl implements Neo4jProxyFactory { + + @Override + public boolean canLoad(Neo4jVersion version) { + return false; + } + + @Override + public Neo4jProxyApi load() { + throw new UnsupportedOperationException("RC compatibility requires JDK17 or later"); + } + + @Override + public String description() { + return "Neo4j RC (placeholder)"; + } +} diff --git a/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/BoltTransactionRunnerImpl.java b/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/BoltTransactionRunnerImpl.java new file mode 100644 index 0000000000..b96bbd2a16 --- /dev/null +++ b/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/BoltTransactionRunnerImpl.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.bolt.dbapi.BoltGraphDatabaseServiceSPI; +import org.neo4j.bolt.dbapi.BoltTransaction; +import org.neo4j.bolt.protocol.common.message.AccessMode; +import org.neo4j.bolt.protocol.common.message.request.connection.RoutingContext; +import org.neo4j.bolt.tx.statement.StatementQuerySubscriber; +import org.neo4j.exceptions.KernelException; +import org.neo4j.gds.compat.BoltQuerySubscriber; +import org.neo4j.gds.compat.BoltTransactionRunner; +import org.neo4j.graphdb.QueryStatistics; +import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo; +import org.neo4j.internal.kernel.api.security.LoginContext; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.impl.query.QueryExecutionConfiguration; +import org.neo4j.kernel.impl.query.QueryExecutionKernelException; +import org.neo4j.values.virtual.MapValue; + +import java.time.Duration; +import java.util.List; +import java.util.Map; + +public class BoltTransactionRunnerImpl extends BoltTransactionRunner { + + @Override + protected BoltQuerySubscriber boltQuerySubscriber() { + var subscriber = new StatementQuerySubscriber(); + return new BoltQuerySubscriber<>() { + @Override + public void assertSucceeded() throws KernelException { + subscriber.assertSuccess(); + } + + @Override + public QueryStatistics queryStatistics() { + return subscriber.getStatistics(); + } + + @Override + public StatementQuerySubscriber innerSubscriber() { + return subscriber; + } + }; + } + + @Override + protected void executeQuery( + BoltTransaction boltTransaction, + String query, + MapValue parameters, + StatementQuerySubscriber querySubscriber + ) throws QueryExecutionKernelException { + try { + boltTransaction.executeQuery(query, parameters, true, querySubscriber) + .queryExecution() // This fella here is quite lazy + .consumeAll(); // We need to do this in order to trigger the query execution. + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + protected BoltTransaction beginBoltWriteTransaction( + BoltGraphDatabaseServiceSPI fabricDb, + LoginContext loginContext, + KernelTransaction.Type kernelTransactionType, + ClientConnectionInfo clientConnectionInfo, + List bookmarks, + Duration txTimeout, + Map txMetadata + ) { + return fabricDb.beginTransaction( + kernelTransactionType, + loginContext, + clientConnectionInfo, + bookmarks, + txTimeout, + AccessMode.WRITE, + txMetadata, + new RoutingContext(true, Map.of()), + QueryExecutionConfiguration.DEFAULT_CONFIG + ); + } +} diff --git a/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/Neo4jProxyFactoryImpl.java b/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/Neo4jProxyFactoryImpl.java new file mode 100644 index 0000000000..da5c2ef054 --- /dev/null +++ b/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/Neo4jProxyFactoryImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.annotations.service.ServiceProvider; +import org.neo4j.gds.compat.Neo4jProxyApi; +import org.neo4j.gds.compat.Neo4jProxyFactory; +import org.neo4j.gds.compat.Neo4jVersion; + +@ServiceProvider +public final class Neo4jProxyFactoryImpl implements Neo4jProxyFactory { + + @Override + public boolean canLoad(Neo4jVersion version) { + return version == Neo4jVersion.V_RC; + } + + @Override + public Neo4jProxyApi load() { + return new Neo4jProxyImpl(); + } + + @Override + public String description() { + return "Neo4j RC"; + } +} diff --git a/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/Neo4jProxyImpl.java b/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/Neo4jProxyImpl.java new file mode 100644 index 0000000000..42bfb29ca1 --- /dev/null +++ b/compatibility/5.18/neo4j-kernel-adapter/src/main/java17/org/neo4j/gds/compat/_518/Neo4jProxyImpl.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.common.DependencyResolver; +import org.neo4j.gds.compat.BoltTransactionRunner; +import org.neo4j.gds.compat.CompatExecutionContext; +import org.neo4j.gds.compat.CustomAccessMode; +import org.neo4j.gds.compat.GlobalProcedureRegistry; +import org.neo4j.gds.compat.GraphDatabaseApiProxy; +import org.neo4j.gds.compat._5x.CommonNeo4jProxyImpl; +import org.neo4j.gds.compat._5x.CompatAccessModeImpl; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.internal.kernel.api.Cursor; +import org.neo4j.internal.kernel.api.NodeCursor; +import org.neo4j.internal.kernel.api.PartitionedScan; +import org.neo4j.internal.kernel.api.Read; +import org.neo4j.internal.kernel.api.TokenSet; +import org.neo4j.internal.kernel.api.exceptions.ProcedureException; +import org.neo4j.internal.kernel.api.procs.FieldSignature; +import org.neo4j.internal.kernel.api.procs.ProcedureSignature; +import org.neo4j.internal.kernel.api.procs.QualifiedName; +import org.neo4j.internal.kernel.api.procs.UserFunctionSignature; +import org.neo4j.internal.kernel.api.security.AccessMode; +import org.neo4j.internal.kernel.api.security.ReadSecurityPropertyProvider; +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.io.pagecache.context.CursorContextFactory; +import org.neo4j.io.pagecache.context.FixedVersionContextSupplier; +import org.neo4j.io.pagecache.tracing.PageCacheTracer; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.api.procedure.Context; +import org.neo4j.kernel.api.procedure.GlobalProcedures; +import org.neo4j.kernel.database.DatabaseReferenceImpl; +import org.neo4j.kernel.database.DatabaseReferenceRepository; +import org.neo4j.kernel.impl.store.RecordStore; +import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; +import org.neo4j.procedure.Mode; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public final class Neo4jProxyImpl extends CommonNeo4jProxyImpl { + + @Override + public long getHighId(RecordStore recordStore) { + return recordStore.getIdGenerator().getHighId(); + } + + @Override + public T lookupComponentProvider(Context ctx, Class component, boolean safe) throws ProcedureException { + var globalProcedures = GraphDatabaseApiProxy.resolveDependency( + ctx.dependencyResolver(), + GlobalProcedures.class + ); + return globalProcedures.getCurrentView().lookupComponentProvider(component, safe).apply(ctx); + } + + @Override + public BoltTransactionRunner boltTransactionRunner() { + return new BoltTransactionRunnerImpl(); + } + + @Override + public ProcedureSignature procedureSignature( + QualifiedName name, + List inputSignature, + List outputSignature, + Mode mode, + boolean admin, + String deprecated, + String description, + String warning, + boolean eager, + boolean caseInsensitive, + boolean systemProcedure, + boolean internal, + boolean allowExpiredCredentials, + boolean threadSafe + ) { + return new ProcedureSignature( + name, + inputSignature, + outputSignature, + mode, + admin, + deprecated, + description, + warning, + eager, + caseInsensitive, + systemProcedure, + internal, + allowExpiredCredentials, + threadSafe + ); + } + + @Override + public GlobalProcedureRegistry globalProcedureRegistry(GlobalProcedures globalProcedures) { + return new GlobalProcedureRegistry() { + @Override + public Set getAllProcedures() { + return globalProcedures.getCurrentView().getAllProcedures(); + } + + @Override + public Stream getAllNonAggregatingFunctions() { + return globalProcedures.getCurrentView().getAllNonAggregatingFunctions(); + } + + @Override + public Stream getAllAggregatingFunctions() { + return globalProcedures.getCurrentView().getAllAggregatingFunctions(); + } + }; + } + + private static final DependencyResolver EMPTY_DEPENDENCY_RESOLVER = new DependencyResolver() { + @Override + public T resolveDependency(Class type, SelectionStrategy selector) { + return null; + } + + @Override + public boolean containsDependency(Class type) { + return false; + } + }; + + @Override + public DependencyResolver emptyDependencyResolver() { + return EMPTY_DEPENDENCY_RESOLVER; + } + + @Override + public CursorContextFactory cursorContextFactory(Optional pageCacheTracer) { + return pageCacheTracer.map(cacheTracer -> new CursorContextFactory( + cacheTracer, + FixedVersionContextSupplier.EMPTY_CONTEXT_SUPPLIER + )).orElse(CursorContextFactory.NULL_CONTEXT_FACTORY); + } + + @Override + public CompatExecutionContext executionContext(KernelTransaction ktx) { + var stmt = ktx.acquireStatement(); + var ctx = ktx.createExecutionContext(); + return new CompatExecutionContext() { + @Override + public CursorContext cursorContext() { + return ctx.cursorContext(); + } + + @Override + public AccessMode accessMode() { + return ctx.securityContext().mode(); + } + + @Override + public boolean reservePartition(PartitionedScan scan, C cursor) { + return scan.reservePartition(cursor, ctx); + } + + @Override + public void close() { + ctx.complete(); + ctx.close(); + stmt.close(); + } + }; + } + + @Override + public AccessMode accessMode(CustomAccessMode customAccessMode) { + return new CompatAccessModeImpl(customAccessMode) { + @Override + public boolean allowsReadNodeProperty( + Supplier labels, + int propertyKey, + ReadSecurityPropertyProvider propertyProvider + ) { + return custom.allowsReadNodeProperty(propertyKey); + } + }; + } + + @Override + public String neo4jArrowServerAddressHeader() { + // TODO: replace this with a dependency to neo4j once we moved the corresponding piece to a public module + return "ArrowPluginAddress"; + } + + @Override + public String metricsManagerClass() { + return "com.neo4j.metrics.MetricsManager"; + } + + @Override + public boolean isCompositeDatabase(GraphDatabaseService databaseService) { + var databaseId = GraphDatabaseApiProxy.databaseId(databaseService); + var repo = GraphDatabaseApiProxy.resolveDependency(databaseService, DatabaseReferenceRepository.class); + return repo.getCompositeDatabaseReferences().stream() + .map(DatabaseReferenceImpl.Internal::databaseId) + .anyMatch(databaseId::equals); + } + + @Override + public long estimateNodeCount(Read read, int label) { + return read.estimateCountsForNode(label); + } + + @Override + public long estimateRelationshipCount(Read read, int sourceLabel, int targetLabel, int type) { + return read.estimateCountsForRelationships(sourceLabel, type, targetLabel); + } + + @Override + public T nodeLabelTokenSet( + NodeCursor nodeCursor, + Function intsConstructor, + Function longsConstructor + ) { + return intsConstructor.apply(nodeCursor.labels().all()); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/build.gradle b/compatibility/5.18/storage-engine-adapter/build.gradle new file mode 100644 index 0000000000..4c82ed1f4a --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/build.gradle @@ -0,0 +1,67 @@ +apply plugin: 'java-library' +apply plugin: 'me.champeau.mrjar' + +description = 'Neo4j Graph Data Science :: Storage Engine Adapter 5.18' + +group = 'org.neo4j.gds' + +// for all 5.x versions +if (ver.'neo4j'.startsWith('5.')) { + sourceSets { + main { + java { + srcDirs = ['src/main/java17'] + } + } + } + + dependencies { + annotationProcessor project(':annotations') + annotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables' + annotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.18' + + compileOnly project(':annotations') + compileOnly project(':progress-tracking') + compileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables' + compileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.18' + compileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.18' + + implementation project(':core') + implementation project(':storage-engine-adapter-api') + implementation project(':config-api') + implementation project(':string-formatting') + } +} else { + multiRelease { + targetVersions 11, 17 + } + + if (!project.hasProperty('no-forbidden-apis')) { + forbiddenApisJava17 { + exclude('**') + } + } + + dependencies { + annotationProcessor group: 'org.neo4j', name: 'annotations', version: ver.'neo4j' + compileOnly group: 'org.neo4j', name: 'annotations', version: ver.'neo4j' + compileOnly group: 'org.neo4j', name: 'neo4j-kernel-api', version: ver.'neo4j' + + implementation project(':storage-engine-adapter-api') + + java17AnnotationProcessor project(':annotations') + java17AnnotationProcessor group: 'org.immutables', name: 'value', version: ver.'immutables' + java17AnnotationProcessor group: 'org.neo4j', name: 'annotations', version: neos.'5.18' + + java17CompileOnly project(':annotations') + java17CompileOnly project(':progress-tracking') + java17CompileOnly group: 'org.immutables', name: 'value-annotations', version: ver.'immutables' + java17CompileOnly group: 'org.neo4j', name: 'neo4j', version: neos.'5.18' + java17CompileOnly group: 'org.neo4j', name: 'neo4j-record-storage-engine', version: neos.'5.18' + + java17Implementation project(':core') + java17Implementation project(':storage-engine-adapter-api') + java17Implementation project(':config-api') + java17Implementation project(':string-formatting') + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_518/InMemoryStorageEngineFactory.java b/compatibility/5.18/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_518/InMemoryStorageEngineFactory.java new file mode 100644 index 0000000000..bf56a02f4a --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_518/InMemoryStorageEngineFactory.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.annotations.service.ServiceProvider; +import org.neo4j.configuration.Config; +import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker; +import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector; +import org.neo4j.internal.id.IdController; +import org.neo4j.internal.id.IdGeneratorFactory; +import org.neo4j.internal.schema.IndexConfigCompleter; +import org.neo4j.internal.schema.SchemaRule; +import org.neo4j.internal.schema.SchemaState; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; +import org.neo4j.io.layout.Neo4jLayout; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.io.pagecache.tracing.PageCacheTracer; +import org.neo4j.lock.LockService; +import org.neo4j.logging.LogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.memory.MemoryTracker; +import org.neo4j.monitoring.DatabaseHealth; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.storageengine.api.CommandReaderFactory; +import org.neo4j.storageengine.api.ConstraintRuleAccessor; +import org.neo4j.storageengine.api.LogVersionRepository; +import org.neo4j.storageengine.api.MetadataProvider; +import org.neo4j.storageengine.api.StorageEngine; +import org.neo4j.storageengine.api.StorageEngineFactory; +import org.neo4j.storageengine.api.StorageFilesState; +import org.neo4j.storageengine.api.StoreId; +import org.neo4j.storageengine.api.StoreVersion; +import org.neo4j.storageengine.api.StoreVersionCheck; +import org.neo4j.storageengine.api.TransactionIdStore; +import org.neo4j.storageengine.migration.RollingUpgradeCompatibility; +import org.neo4j.storageengine.migration.SchemaRuleMigrationAccess; +import org.neo4j.storageengine.migration.StoreMigrationParticipant; +import org.neo4j.token.TokenHolders; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@ServiceProvider +public class InMemoryStorageEngineFactory implements StorageEngineFactory { + + @Override + public String name() { + return "unsupported518"; + } + + @Override + public StoreVersionCheck versionCheck( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache, + LogService logService, + PageCacheTracer pageCacheTracer + ) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public StoreVersion versionInformation(String storeVersion) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public StoreVersion versionInformation(StoreId storeId) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public RollingUpgradeCompatibility rollingUpgradeCompatibility() { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public List migrationParticipants( + FileSystemAbstraction fs, + Config config, + PageCache pageCache, + JobScheduler jobScheduler, + LogService logService, + PageCacheTracer cacheTracer, + MemoryTracker memoryTracker + ) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public StorageEngine instantiate( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache, + TokenHolders tokenHolders, + SchemaState schemaState, + ConstraintRuleAccessor constraintSemantics, + IndexConfigCompleter indexConfigCompleter, + LockService lockService, + IdGeneratorFactory idGeneratorFactory, + IdController idController, + DatabaseHealth databaseHealth, + LogProvider internalLogProvider, + LogProvider userLogProvider, + RecoveryCleanupWorkCollector recoveryCleanupWorkCollector, + PageCacheTracer cacheTracer, + boolean createStoreIfNotExists, + DatabaseReadOnlyChecker readOnlyChecker, + MemoryTracker memoryTracker + ) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public List listStorageFiles(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) throws + IOException { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout, PageCache pageCache) { + return false; + } + + @Override + public TransactionIdStore readOnlyTransactionIdStore( + FileSystemAbstraction filySystem, + DatabaseLayout databaseLayout, + PageCache pageCache, + CursorContext cursorContext + ) throws IOException { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public LogVersionRepository readOnlyLogVersionRepository( + DatabaseLayout databaseLayout, + PageCache pageCache, + CursorContext cursorContext + ) throws IOException { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public MetadataProvider transactionMetaDataStore( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache, + PageCacheTracer cacheTracer, + DatabaseReadOnlyChecker readOnlyChecker + ) throws IOException { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public StoreId storeId( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + PageCache pageCache, + CursorContext cursorContext + ) throws IOException { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public void setStoreId( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + PageCache pageCache, + CursorContext cursorContext, + StoreId storeId, + long upgradeTxChecksum, + long upgradeTxCommitTimestamp + ) throws IOException { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public void setExternalStoreUUID( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + PageCache pageCache, + CursorContext cursorContext, + UUID externalStoreId + ) throws IOException { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public Optional databaseIdUuid( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + PageCache pageCache, + CursorContext cursorContext + ) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public SchemaRuleMigrationAccess schemaRuleMigrationAccess( + FileSystemAbstraction fs, + PageCache pageCache, + Config config, + DatabaseLayout databaseLayout, + LogService logService, + String recordFormats, + PageCacheTracer cacheTracer, + CursorContext cursorContext, + MemoryTracker memoryTracker + ) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public List loadSchemaRules( + FileSystemAbstraction fs, + PageCache pageCache, + Config config, + DatabaseLayout databaseLayout, + CursorContext cursorContext + ) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public StorageFilesState checkStoreFileState( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + PageCache pageCache + ) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public CommandReaderFactory commandReaderFactory() { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } + + @Override + public DatabaseLayout databaseLayout(Neo4jLayout neo4jLayout, String databaseName) { + throw new UnsupportedOperationException("5.18 storage engine requires JDK17 or later"); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_518/StorageEngineProxyFactoryImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_518/StorageEngineProxyFactoryImpl.java new file mode 100644 index 0000000000..9e72f6ab60 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java/org/neo4j/gds/compat/_518/StorageEngineProxyFactoryImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.annotations.service.ServiceProvider; +import org.neo4j.gds.compat.Neo4jVersion; +import org.neo4j.gds.compat.StorageEngineProxyApi; +import org.neo4j.gds.compat.StorageEngineProxyFactory; + +@ServiceProvider +public class StorageEngineProxyFactoryImpl implements StorageEngineProxyFactory { + + @Override + public boolean canLoad(Neo4jVersion version) { + return false; + } + + @Override + public StorageEngineProxyApi load() { + throw new UnsupportedOperationException("RC storage engine requires JDK17 or later"); + } + + @Override + public String description() { + return "Storage Engine RC"; + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryCommandCreationContextImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryCommandCreationContextImpl.java new file mode 100644 index 0000000000..900e9a70d8 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryCommandCreationContextImpl.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.configuration.Config; +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.kernel.KernelVersion; +import org.neo4j.kernel.KernelVersionProvider; +import org.neo4j.lock.LockTracer; +import org.neo4j.lock.ResourceLocker; +import org.neo4j.storageengine.api.CommandCreationContext; +import org.neo4j.storageengine.api.cursor.StoreCursors; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + +public class InMemoryCommandCreationContextImpl implements CommandCreationContext { + + private final AtomicLong schemaTokens; + private final AtomicInteger propertyTokens; + private final AtomicInteger labelTokens; + private final AtomicInteger typeTokens; + + InMemoryCommandCreationContextImpl() { + this.schemaTokens = new AtomicLong(0); + this.propertyTokens = new AtomicInteger(0); + this.labelTokens = new AtomicInteger(0); + this.typeTokens = new AtomicInteger(0); + } + + @Override + public long reserveNode() { + throw new UnsupportedOperationException("Creating nodes is not supported"); + } + + @Override + public long reserveRelationship( + long sourceNode, + long targetNode, + int relationshipType, + boolean sourceNodeAddedInTx, + boolean targetNodeAddedInTx + ) { + throw new UnsupportedOperationException("Creating relationships is not supported"); + } + + @Override + public long reserveSchema() { + return schemaTokens.getAndIncrement(); + } + + @Override + public int reserveLabelTokenId() { + return labelTokens.getAndIncrement(); + } + + @Override + public int reservePropertyKeyTokenId() { + return propertyTokens.getAndIncrement(); + } + + @Override + public int reserveRelationshipTypeTokenId() { + return typeTokens.getAndIncrement(); + } + + @Override + public void close() { + + } + + @Override + public void initialize( + KernelVersionProvider kernelVersionProvider, + CursorContext cursorContext, + StoreCursors storeCursors, + Supplier oldestActiveTransactionSequenceNumber, + ResourceLocker locks, + Supplier lockTracer + ) { + + } + + @Override + public boolean resetIds() { + return false; + } + + @Override + public KernelVersion kernelVersion() { + // NOTE: Double-check if this is still correct when you copy this into a new compat layer + return KernelVersion.getLatestVersion(Config.newBuilder().build()); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryCountsStoreImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryCountsStoreImpl.java new file mode 100644 index 0000000000..4bef8b822b --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryCountsStoreImpl.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.annotations.documented.ReporterFactory; +import org.neo4j.counts.CountsStore; +import org.neo4j.counts.CountsUpdater; +import org.neo4j.counts.CountsVisitor; +import org.neo4j.gds.NodeLabel; +import org.neo4j.gds.RelationshipType; +import org.neo4j.gds.api.GraphStore; +import org.neo4j.internal.helpers.progress.ProgressMonitorFactory; +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.io.pagecache.context.CursorContextFactory; +import org.neo4j.io.pagecache.tracing.FileFlushEvent; +import org.neo4j.memory.MemoryTracker; +import org.neo4j.token.TokenHolders; +import org.neo4j.token.api.TokenNotFoundException; + +import java.io.IOException; + +public class InMemoryCountsStoreImpl implements CountsStore { + + private final GraphStore graphStore; + private final TokenHolders tokenHolders; + + public InMemoryCountsStoreImpl( + GraphStore graphStore, + TokenHolders tokenHolders + ) { + + this.graphStore = graphStore; + this.tokenHolders = tokenHolders; + } + + @Override + public void start(CursorContext cursorContext, MemoryTracker memoryTracker) throws IOException { + + } + + @Override + public CountsUpdater updater(long txId, boolean isLast, CursorContext cursorContext) { + throw new UnsupportedOperationException("Updates are not supported"); + } + + @Override + public void checkpoint(FileFlushEvent fileFlushEvent, CursorContext cursorContext) { + + } + + @Override + public long nodeCount(int labelId, CursorContext cursorContext) { + if (labelId == -1) { + return graphStore.nodeCount(); + } + + String nodeLabel; + try { + nodeLabel = tokenHolders.labelTokens().getTokenById(labelId).name(); + } catch (TokenNotFoundException e) { + throw new RuntimeException(e); + } + return graphStore.nodes().nodeCount(NodeLabel.of(nodeLabel)); + } + + @Override + public long estimateNodeCount(int labelId, CursorContext cursorContext) { + var label = tokenHolders.labelGetName(labelId); + return graphStore.nodes().nodeCount(NodeLabel.of(label)); + } + + @Override + public long relationshipCount(int startLabelId, int typeId, int endLabelId, CursorContext cursorContext) { + // TODO: this is not using start/endLabel + if (typeId == -1) { + return graphStore.relationshipCount(); + } else { + var type = tokenHolders.relationshipTypeGetName(typeId); + return graphStore.relationshipCount(RelationshipType.of(type)); + } + } + + @Override + public long estimateRelationshipCount(int startLabelId, int typeId, int endLabelId, CursorContext cursorContext) { + if (typeId == -1) { + return graphStore.relationshipCount(); + } else { + var type = tokenHolders.relationshipTypeGetName(typeId); + return graphStore.relationshipCount(RelationshipType.of(type)); + } + } + + @Override + public boolean consistencyCheck( + ReporterFactory reporterFactory, + CursorContextFactory cursorContextFactory, + int i, + ProgressMonitorFactory progressMonitorFactory + ) { + return true; + } + + @Override + public void close() { + + } + + @Override + public void accept(CountsVisitor visitor, CursorContext cursorContext) { + tokenHolders.labelTokens().getAllTokens().forEach(labelToken -> { + visitor.visitNodeCount(labelToken.id(), nodeCount(labelToken.id(), cursorContext)); + }); + + visitor.visitRelationshipCount(-1, -1, -1, graphStore.relationshipCount()); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryMetaDataProviderImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryMetaDataProviderImpl.java new file mode 100644 index 0000000000..5e2f973e3b --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryMetaDataProviderImpl.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.internal.recordstorage.InMemoryLogVersionRepository518; +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.io.pagecache.context.TransactionIdSnapshot; +import org.neo4j.storageengine.api.ClosedTransactionMetadata; +import org.neo4j.storageengine.api.ExternalStoreId; +import org.neo4j.storageengine.api.MetadataProvider; +import org.neo4j.storageengine.api.StoreId; +import org.neo4j.storageengine.api.TransactionId; + +import java.io.IOException; +import java.util.Optional; +import java.util.UUID; + +public class InMemoryMetaDataProviderImpl implements MetadataProvider { + + private final ExternalStoreId externalStoreId; + private final InMemoryLogVersionRepository518 logVersionRepository; + private final InMemoryTransactionIdStoreImpl transactionIdStore; + + InMemoryMetaDataProviderImpl() { + this.logVersionRepository = new InMemoryLogVersionRepository518(); + this.externalStoreId = new ExternalStoreId(UUID.randomUUID()); + this.transactionIdStore = new InMemoryTransactionIdStoreImpl(); + } + + @Override + public ExternalStoreId getExternalStoreId() { + return this.externalStoreId; + } + + @Override + public ClosedTransactionMetadata getLastClosedTransaction() { + return this.transactionIdStore.getLastClosedTransaction(); + } + + @Override + public void setCurrentLogVersion(long version) { + logVersionRepository.setCurrentLogVersion(version); + } + + @Override + public long incrementAndGetVersion() { + return logVersionRepository.incrementAndGetVersion(); + } + + @Override + public void setCheckpointLogVersion(long version) { + logVersionRepository.setCheckpointLogVersion(version); + } + + @Override + public long incrementAndGetCheckpointLogVersion() { + return logVersionRepository.incrementAndGetCheckpointLogVersion(); + } + + @Override + public void transactionCommitted(long transactionId, int checksum, long commitTimestamp, long consensusIndex) { + transactionIdStore.transactionCommitted(transactionId, checksum, commitTimestamp, consensusIndex); + } + + @Override + public void setLastCommittedAndClosedTransactionId( + long transactionId, + int checksum, + long commitTimestamp, + long consensusIndex, + long byteOffset, + long logVersion + ) { + transactionIdStore.setLastCommittedAndClosedTransactionId( + transactionId, + checksum, + commitTimestamp, + consensusIndex, + byteOffset, + logVersion + ); + } + + @Override + public void transactionClosed( + long transactionId, + long logVersion, + long byteOffset, + int checksum, + long commitTimestamp, + long consensusIndex + ) { + this.transactionIdStore.transactionClosed( + transactionId, + logVersion, + byteOffset, + checksum, + commitTimestamp, + consensusIndex + ); + } + + @Override + public void resetLastClosedTransaction( + long transactionId, + long logVersion, + long byteOffset, + int checksum, + long commitTimestamp, + long consensusIndex + ) { + this.transactionIdStore.resetLastClosedTransaction( + transactionId, + logVersion, + byteOffset, + checksum, + commitTimestamp, + consensusIndex + ); + } + + @Override + public TransactionIdSnapshot getClosedTransactionSnapshot() { + return new TransactionIdSnapshot(this.getLastClosedTransactionId()); + } + + @Override + public void regenerateMetadata(StoreId storeId, UUID externalStoreUUID, CursorContext cursorContext) { + } + + @Override + public StoreId getStoreId() { + return StoreId.UNKNOWN; + } + + @Override + public void close() throws IOException { + } + + @Override + public long getCurrentLogVersion() { + return this.logVersionRepository.getCurrentLogVersion(); + } + + @Override + public long getCheckpointLogVersion() { + return this.logVersionRepository.getCheckpointLogVersion(); + } + + @Override + public long nextCommittingTransactionId() { + return this.transactionIdStore.nextCommittingTransactionId(); + } + + @Override + public long committingTransactionId() { + return this.transactionIdStore.committingTransactionId(); + } + + @Override + public long getLastCommittedTransactionId() { + return this.transactionIdStore.getLastCommittedTransactionId(); + } + + @Override + public TransactionId getLastCommittedTransaction() { + return this.transactionIdStore.getLastCommittedTransaction(); + } + + @Override + public long getLastClosedTransactionId() { + return this.transactionIdStore.getLastClosedTransactionId(); + } + + @Override + public Optional getDatabaseIdUuid(CursorContext cursorTracer) { + throw new IllegalStateException("Not supported"); + } + + @Override + public void setDatabaseIdUuid(UUID uuid, CursorContext cursorContext) { + throw new IllegalStateException("Not supported"); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryNodeCursor.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryNodeCursor.java new file mode 100644 index 0000000000..da19d6d799 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryNodeCursor.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.gds.api.GraphStore; +import org.neo4j.gds.compat.AbstractInMemoryNodeCursor; +import org.neo4j.storageengine.api.AllNodeScan; +import org.neo4j.storageengine.api.Degrees; +import org.neo4j.storageengine.api.LongReference; +import org.neo4j.storageengine.api.PropertySelection; +import org.neo4j.storageengine.api.Reference; +import org.neo4j.storageengine.api.RelationshipSelection; +import org.neo4j.storageengine.api.StoragePropertyCursor; +import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor; +import org.neo4j.token.TokenHolders; + +public class InMemoryNodeCursor extends AbstractInMemoryNodeCursor { + + public InMemoryNodeCursor(GraphStore graphStore, TokenHolders tokenHolders) { + super(graphStore, tokenHolders); + } + + @Override + public boolean hasLabel() { + return hasAtLeastOneLabelForCurrentNode(); + } + + @Override + public Reference propertiesReference() { + return LongReference.longReference(getId()); + } + + @Override + public void properties(StoragePropertyCursor propertyCursor, PropertySelection selection) { + propertyCursor.initNodeProperties(propertiesReference(), selection); + } + + @Override + public void properties(StoragePropertyCursor propertyCursor) { + properties(propertyCursor, PropertySelection.ALL_PROPERTIES); + } + + @Override + public boolean supportsFastRelationshipsTo() { + return false; + } + + @Override + public void relationshipsTo( + StorageRelationshipTraversalCursor storageRelationshipTraversalCursor, + RelationshipSelection relationshipSelection, + long neighbourNodeReference + ) { + throw new UnsupportedOperationException(); + } + + @Override + public void degrees(RelationshipSelection selection, Degrees.Mutator mutator) { + } + + @Override + public boolean scanBatch(AllNodeScan allNodeScan, long sizeHint) { + return super.scanBatch(allNodeScan, (int) sizeHint); + } + + @Override + public int[] labels() { + return intLabels(); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryNodePropertyCursor.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryNodePropertyCursor.java new file mode 100644 index 0000000000..49ed618d73 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryNodePropertyCursor.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.gds.compat.AbstractInMemoryNodePropertyCursor; +import org.neo4j.gds.core.cypher.CypherGraphStore; +import org.neo4j.storageengine.api.LongReference; +import org.neo4j.storageengine.api.PropertySelection; +import org.neo4j.storageengine.api.Reference; +import org.neo4j.token.TokenHolders; + +public class InMemoryNodePropertyCursor extends AbstractInMemoryNodePropertyCursor { + + public InMemoryNodePropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) { + super(graphStore, tokenHolders); + } + + @Override + public void initNodeProperties(Reference reference, PropertySelection selection, long ownerReference) { + reset(); + setId(((LongReference) reference).id); + setPropertySelection(new InMemoryPropertySelectionImpl(selection)); + } + + @Override + public void initRelationshipProperties(Reference reference, PropertySelection selection, long ownerReference) { + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryPropertyCursor.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryPropertyCursor.java new file mode 100644 index 0000000000..5b57d0a040 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryPropertyCursor.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.gds.compat.AbstractInMemoryPropertyCursor; +import org.neo4j.gds.core.cypher.CypherGraphStore; +import org.neo4j.storageengine.api.PropertySelection; +import org.neo4j.storageengine.api.Reference; +import org.neo4j.storageengine.api.StorageNodeCursor; +import org.neo4j.storageengine.api.StorageRelationshipCursor; +import org.neo4j.token.TokenHolders; + +public class InMemoryPropertyCursor extends AbstractInMemoryPropertyCursor { + + public InMemoryPropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) { + super(graphStore, tokenHolders); + } + + @Override + public void initNodeProperties(Reference reference, PropertySelection selection, long ownerReference) { + if (this.delegate == null || !(this.delegate instanceof InMemoryNodePropertyCursor)) { + this.delegate = new InMemoryNodePropertyCursor(graphStore, tokenHolders); + } + + ((InMemoryNodePropertyCursor) delegate).initNodeProperties(reference, selection); + } + + @Override + public void initNodeProperties(StorageNodeCursor nodeCursor, PropertySelection selection) { + if (this.delegate == null || !(this.delegate instanceof InMemoryNodePropertyCursor)) { + this.delegate = new InMemoryNodePropertyCursor(graphStore, tokenHolders); + } + + ((InMemoryNodePropertyCursor) delegate).initNodeProperties(nodeCursor, selection); + } + + @Override + public void initRelationshipProperties(StorageRelationshipCursor relationshipCursor, PropertySelection selection) { + if (this.delegate == null || !(this.delegate instanceof InMemoryRelationshipPropertyCursor)) { + this.delegate = new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders); + } + + ((InMemoryRelationshipPropertyCursor) delegate).initRelationshipProperties(relationshipCursor, selection); + } + + @Override + public void initRelationshipProperties(Reference reference, PropertySelection selection, long ownerReference) { + if (this.delegate == null || !(this.delegate instanceof InMemoryRelationshipPropertyCursor)) { + this.delegate = new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders); + } + + ((InMemoryRelationshipPropertyCursor) delegate).initRelationshipProperties(reference, selection); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryPropertySelectionImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryPropertySelectionImpl.java new file mode 100644 index 0000000000..652a40b6bd --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryPropertySelectionImpl.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.gds.compat.InMemoryPropertySelection; +import org.neo4j.storageengine.api.PropertySelection; + +public class InMemoryPropertySelectionImpl implements InMemoryPropertySelection { + + private final PropertySelection propertySelection; + + public InMemoryPropertySelectionImpl(PropertySelection propertySelection) {this.propertySelection = propertySelection;} + + @Override + public boolean isLimited() { + return propertySelection.isLimited(); + } + + @Override + public int numberOfKeys() { + return propertySelection.numberOfKeys(); + } + + @Override + public int key(int index) { + return propertySelection.key(index); + } + + @Override + public boolean test(int key) { + return propertySelection.test(key); + } + + @Override + public boolean isKeysOnly() { + return propertySelection.isKeysOnly(); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipPropertyCursor.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipPropertyCursor.java new file mode 100644 index 0000000000..eca1d15797 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipPropertyCursor.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.gds.compat.AbstractInMemoryRelationshipPropertyCursor; +import org.neo4j.gds.core.cypher.CypherGraphStore; +import org.neo4j.gds.storageengine.InMemoryRelationshipCursor; +import org.neo4j.storageengine.api.LongReference; +import org.neo4j.storageengine.api.PropertySelection; +import org.neo4j.storageengine.api.Reference; +import org.neo4j.storageengine.api.StorageRelationshipCursor; +import org.neo4j.token.TokenHolders; + +public class InMemoryRelationshipPropertyCursor extends AbstractInMemoryRelationshipPropertyCursor { + + InMemoryRelationshipPropertyCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) { + super(graphStore, tokenHolders); + } + + @Override + public void initNodeProperties( + Reference reference, PropertySelection propertySelection, long ownerReference + ) { + + } + + @Override + public void initRelationshipProperties( + Reference reference, PropertySelection propertySelection, long ownerReference + ) { + var relationshipId = ((LongReference) reference).id; + var relationshipCursor = new InMemoryRelationshipScanCursor(graphStore, tokenHolders); + relationshipCursor.single(relationshipId); + relationshipCursor.next(); + relationshipCursor.properties(this, new InMemoryPropertySelectionImpl(propertySelection)); + } + + @Override + public void initRelationshipProperties(StorageRelationshipCursor relationshipCursor, PropertySelection selection) { + var inMemoryRelationshipCursor = (InMemoryRelationshipCursor) relationshipCursor; + inMemoryRelationshipCursor.properties(this, selection); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipScanCursor.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipScanCursor.java new file mode 100644 index 0000000000..899e538eaf --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipScanCursor.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.gds.core.cypher.CypherGraphStore; +import org.neo4j.internal.recordstorage.AbstractInMemoryRelationshipScanCursor; +import org.neo4j.storageengine.api.AllRelationshipsScan; +import org.neo4j.storageengine.api.LongReference; +import org.neo4j.storageengine.api.PropertySelection; +import org.neo4j.storageengine.api.Reference; +import org.neo4j.storageengine.api.StoragePropertyCursor; +import org.neo4j.token.TokenHolders; + +public class InMemoryRelationshipScanCursor extends AbstractInMemoryRelationshipScanCursor { + + public InMemoryRelationshipScanCursor( + CypherGraphStore graphStore, + TokenHolders tokenHolders + ) { + super(graphStore, tokenHolders); + } + + @Override + public void single(long reference, long sourceNodeReference, int type, long targetNodeReference) { + single(reference); + } + + @Override + public Reference propertiesReference() { + return LongReference.longReference(getId()); + } + + @Override + public void properties( + StoragePropertyCursor storagePropertyCursor, PropertySelection propertySelection + ) { + properties(storagePropertyCursor, new InMemoryPropertySelectionImpl(propertySelection)); + } + + @Override + public boolean scanBatch(AllRelationshipsScan allRelationshipsScan, long sizeHint) { + return super.scanBatch(allRelationshipsScan, (int) sizeHint); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipTraversalCursor.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipTraversalCursor.java new file mode 100644 index 0000000000..e2b3c63cca --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryRelationshipTraversalCursor.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.gds.compat.AbstractInMemoryRelationshipTraversalCursor; +import org.neo4j.gds.core.cypher.CypherGraphStore; +import org.neo4j.storageengine.api.LongReference; +import org.neo4j.storageengine.api.PropertySelection; +import org.neo4j.storageengine.api.Reference; +import org.neo4j.storageengine.api.StoragePropertyCursor; +import org.neo4j.token.TokenHolders; + +public class InMemoryRelationshipTraversalCursor extends AbstractInMemoryRelationshipTraversalCursor { + + public InMemoryRelationshipTraversalCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) { + super(graphStore, tokenHolders); + } + + @Override + public Reference propertiesReference() { + return LongReference.longReference(getId()); + } + + @Override + public void properties( + StoragePropertyCursor propertyCursor, PropertySelection selection + ) { + properties(propertyCursor, new InMemoryPropertySelectionImpl(selection)); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageEngineFactory.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageEngineFactory.java new file mode 100644 index 0000000000..4a11c5ab82 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageEngineFactory.java @@ -0,0 +1,574 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.eclipse.collections.api.factory.Sets; +import org.eclipse.collections.api.set.ImmutableSet; +import org.neo4j.annotations.service.ServiceProvider; +import org.neo4j.configuration.Config; +import org.neo4j.consistency.checking.ConsistencyFlags; +import org.neo4j.consistency.report.ConsistencySummaryStatistics; +import org.neo4j.dbms.database.readonly.DatabaseReadOnlyChecker; +import org.neo4j.function.ThrowingSupplier; +import org.neo4j.gds.annotation.SuppressForbidden; +import org.neo4j.gds.compat.Neo4jVersion; +import org.neo4j.gds.compat.StorageEngineFactoryIdProvider; +import org.neo4j.gds.compat.StorageEngineProxyApi; +import org.neo4j.index.internal.gbptree.RecoveryCleanupWorkCollector; +import org.neo4j.internal.batchimport.AdditionalInitialIds; +import org.neo4j.internal.batchimport.BatchImporter; +import org.neo4j.internal.batchimport.Configuration; +import org.neo4j.internal.batchimport.IncrementalBatchImporter; +import org.neo4j.internal.batchimport.IndexImporterFactory; +import org.neo4j.internal.batchimport.Monitor; +import org.neo4j.internal.batchimport.ReadBehaviour; +import org.neo4j.internal.batchimport.input.Collector; +import org.neo4j.internal.batchimport.input.Input; +import org.neo4j.internal.batchimport.input.LenientStoreInput; +import org.neo4j.internal.id.IdGeneratorFactory; +import org.neo4j.internal.id.ScanOnOpenReadOnlyIdGeneratorFactory; +import org.neo4j.internal.recordstorage.InMemoryStorageCommandReaderFactory518; +import org.neo4j.internal.recordstorage.StoreTokens; +import org.neo4j.internal.schema.IndexConfigCompleter; +import org.neo4j.internal.schema.SchemaRule; +import org.neo4j.internal.schema.SchemaState; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.layout.DatabaseLayout; +import org.neo4j.io.layout.Neo4jLayout; +import org.neo4j.io.layout.recordstorage.RecordDatabaseLayout; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.io.pagecache.context.CursorContextFactory; +import org.neo4j.io.pagecache.impl.muninn.VersionStorage; +import org.neo4j.io.pagecache.tracing.PageCacheTracer; +import org.neo4j.kernel.KernelVersionRepository; +import org.neo4j.kernel.api.index.IndexProvidersAccess; +import org.neo4j.kernel.impl.api.index.IndexProviderMap; +import org.neo4j.kernel.impl.locking.LockManager; +import org.neo4j.kernel.impl.store.MetaDataStore; +import org.neo4j.kernel.impl.store.NeoStores; +import org.neo4j.kernel.impl.store.StoreFactory; +import org.neo4j.kernel.impl.store.StoreType; +import org.neo4j.kernel.impl.store.cursor.CachedStoreCursors; +import org.neo4j.kernel.impl.transaction.log.LogTailLogVersionsMetadata; +import org.neo4j.kernel.impl.transaction.log.LogTailMetadata; +import org.neo4j.lock.LockService; +import org.neo4j.logging.InternalLog; +import org.neo4j.logging.InternalLogProvider; +import org.neo4j.logging.NullLogProvider; +import org.neo4j.logging.internal.LogService; +import org.neo4j.memory.MemoryTracker; +import org.neo4j.monitoring.DatabaseHealth; +import org.neo4j.scheduler.JobScheduler; +import org.neo4j.storageengine.api.CommandReaderFactory; +import org.neo4j.storageengine.api.ConstraintRuleAccessor; +import org.neo4j.storageengine.api.LogFilesInitializer; +import org.neo4j.storageengine.api.MetadataProvider; +import org.neo4j.storageengine.api.SchemaRule44; +import org.neo4j.storageengine.api.StorageEngine; +import org.neo4j.storageengine.api.StorageEngineFactory; +import org.neo4j.storageengine.api.StorageFilesState; +import org.neo4j.storageengine.api.StoreId; +import org.neo4j.storageengine.api.StoreVersion; +import org.neo4j.storageengine.api.StoreVersionCheck; +import org.neo4j.storageengine.api.StoreVersionIdentifier; +import org.neo4j.storageengine.migration.SchemaRuleMigrationAccessExtended; +import org.neo4j.storageengine.migration.StoreMigrationParticipant; +import org.neo4j.time.SystemNanoClock; +import org.neo4j.token.CreatingTokenHolder; +import org.neo4j.token.ReadOnlyTokenCreator; +import org.neo4j.token.TokenHolders; +import org.neo4j.token.api.NamedToken; +import org.neo4j.token.api.TokenHolder; +import org.neo4j.token.api.TokensLoader; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.time.Clock; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.function.Function; + +@ServiceProvider +public class InMemoryStorageEngineFactory implements StorageEngineFactory { + + static final String IN_MEMORY_STORAGE_ENGINE_NAME = "in-memory-518"; + + public InMemoryStorageEngineFactory() { + StorageEngineProxyApi.requireNeo4jVersion(Neo4jVersion.V_RC, StorageEngineFactory.class); + } + + @Override + public byte id() { + return StorageEngineFactoryIdProvider.ID; + } + + @Override + public boolean storageExists(FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout) { + return false; + } + + @Override + public StorageEngine instantiate( + FileSystemAbstraction fs, + Clock clock, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache, + TokenHolders tokenHolders, + SchemaState schemaState, + ConstraintRuleAccessor constraintSemantics, + IndexConfigCompleter indexConfigCompleter, + LockService lockService, + IdGeneratorFactory idGeneratorFactory, + DatabaseHealth databaseHealth, + InternalLogProvider internalLogProvider, + InternalLogProvider userLogProvider, + RecoveryCleanupWorkCollector recoveryCleanupWorkCollector, + LogTailMetadata logTailMetadata, + KernelVersionRepository kernelVersionRepository, + MemoryTracker memoryTracker, + CursorContextFactory contextFactory, + PageCacheTracer pageCacheTracer, + VersionStorage versionStorage + ) { + StoreFactory factory = new StoreFactory( + databaseLayout, + config, + idGeneratorFactory, + pageCache, + pageCacheTracer, + fs, + internalLogProvider, + contextFactory, + false, + logTailMetadata + ); + + factory.openNeoStores(StoreType.LABEL_TOKEN).close(); + + return new InMemoryStorageEngineImpl( + databaseLayout, + tokenHolders + ); + } + + @Override + public Optional databaseIdUuid( + FileSystemAbstraction fs, DatabaseLayout databaseLayout, PageCache pageCache, CursorContext cursorContext + ) { + var fieldAccess = MetaDataStore.getFieldAccess( + pageCache, + RecordDatabaseLayout.convert(databaseLayout).metadataStore(), + databaseLayout.getDatabaseName(), + cursorContext + ); + + try { + return fieldAccess.readDatabaseUUID(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public List migrationParticipants( + FileSystemAbstraction fileSystemAbstraction, + Config config, + PageCache pageCache, + JobScheduler jobScheduler, + LogService logService, + MemoryTracker memoryTracker, + PageCacheTracer pageCacheTracer, + CursorContextFactory cursorContextFactory, + boolean b + ) { + return List.of(); + } + + @Override + public DatabaseLayout databaseLayout( + Neo4jLayout neo4jLayout, String databaseName + ) { + return RecordDatabaseLayout.of(neo4jLayout, databaseName); + } + + @Override + public DatabaseLayout formatSpecificDatabaseLayout(DatabaseLayout plainLayout) { + return databaseLayout(plainLayout.getNeo4jLayout(), plainLayout.getDatabaseName()); + } + + @SuppressForbidden(reason = "This is the compat layer and we don't really need to go through the proxy") + @Override + public BatchImporter batchImporter( + DatabaseLayout databaseLayout, + FileSystemAbstraction fileSystemAbstraction, + PageCacheTracer pageCacheTracer, + Configuration configuration, + LogService logService, + PrintStream printStream, + boolean b, + AdditionalInitialIds additionalInitialIds, + Config config, + Monitor monitor, + JobScheduler jobScheduler, + Collector collector, + LogFilesInitializer logFilesInitializer, + IndexImporterFactory indexImporterFactory, + MemoryTracker memoryTracker, + CursorContextFactory cursorContextFactory + ) { + throw new UnsupportedOperationException("Batch Import into GDS is not supported"); + } + + @Override + public Input asBatchImporterInput( + DatabaseLayout databaseLayout, + FileSystemAbstraction fileSystemAbstraction, + PageCache pageCache, + PageCacheTracer pageCacheTracer, + Config config, + MemoryTracker memoryTracker, + ReadBehaviour readBehaviour, + boolean b, + CursorContextFactory cursorContextFactory, + LogTailMetadata logTailMetadata + ) { + NeoStores neoStores = (new StoreFactory( + databaseLayout, + config, + new ScanOnOpenReadOnlyIdGeneratorFactory(), + pageCache, + pageCacheTracer, + fileSystemAbstraction, + NullLogProvider.getInstance(), + cursorContextFactory, + false, + logTailMetadata + )).openAllNeoStores(); + return new LenientStoreInput( + neoStores, + readBehaviour.decorateTokenHolders(this.loadReadOnlyTokens(neoStores, true, cursorContextFactory)), + true, + cursorContextFactory, + readBehaviour + ); + } + + @Override + public long optimalAvailableConsistencyCheckerMemory( + FileSystemAbstraction fileSystemAbstraction, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache + ) { + return 0; + } + + @Override + public String name() { + return IN_MEMORY_STORAGE_ENGINE_NAME; + } + + @Override + public Set supportedFormats(boolean includeFormatsUnderDevelopment) { + return Set.of(IN_MEMORY_STORAGE_ENGINE_NAME); + } + + @Override + public boolean supportedFormat(String format, boolean includeFormatsUnderDevelopment) { + return format.equals(IN_MEMORY_STORAGE_ENGINE_NAME); + } + + @Override + public MetadataProvider transactionMetaDataStore( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache, + DatabaseReadOnlyChecker readOnlyChecker, + CursorContextFactory contextFactory, + LogTailLogVersionsMetadata logTailMetadata, + PageCacheTracer pageCacheTracer + ) throws IOException { + return new InMemoryMetaDataProviderImpl(); + } + + @Override + public StoreVersionCheck versionCheck( + FileSystemAbstraction fileSystemAbstraction, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache, + LogService logService, + CursorContextFactory cursorContextFactory + ) { + return new InMemoryVersionCheck(); + } + + @Override + public List loadSchemaRules( + FileSystemAbstraction fileSystemAbstraction, + PageCache pageCache, + PageCacheTracer pageCacheTracer, + Config config, + DatabaseLayout databaseLayout, + boolean b, + Function function, + CursorContextFactory cursorContextFactory + ) { + return List.of(); + } + + @Override + public List load44SchemaRules( + FileSystemAbstraction fs, + PageCache pageCache, + PageCacheTracer pageCacheTracer, + Config config, + DatabaseLayout databaseLayout, + CursorContextFactory contextFactory, + LogTailLogVersionsMetadata logTailMetadata + ) { + return List.of(); + } + + @Override + public TokenHolders loadReadOnlyTokens( + FileSystemAbstraction fileSystemAbstraction, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache, + PageCacheTracer pageCacheTracer, + boolean lenient, + CursorContextFactory cursorContextFactory + ) { + StoreFactory factory = new StoreFactory( + databaseLayout, + config, + new ScanOnOpenReadOnlyIdGeneratorFactory(), + pageCache, + pageCacheTracer, + fileSystemAbstraction, + NullLogProvider.getInstance(), + cursorContextFactory, + false, + LogTailMetadata.EMPTY_LOG_TAIL + ); + try ( + NeoStores stores = factory.openNeoStores( + StoreType.PROPERTY_KEY_TOKEN, StoreType.PROPERTY_KEY_TOKEN_NAME, + StoreType.LABEL_TOKEN, StoreType.LABEL_TOKEN_NAME, + StoreType.RELATIONSHIP_TYPE_TOKEN, StoreType.RELATIONSHIP_TYPE_TOKEN_NAME + ) + ) { + return loadReadOnlyTokens(stores, lenient, cursorContextFactory); + } + } + + @Override + public SchemaRuleMigrationAccessExtended schemaRuleMigrationAccess( + FileSystemAbstraction fs, + PageCache pageCache, + PageCacheTracer pageCacheTracer, + Config config, + DatabaseLayout databaseLayout, + CursorContextFactory contextFactory, + MemoryTracker memoryTracker, + LogTailMetadata logTail + ) { + // this is used by store copy, which is not supported for GDS storage engine + return null; + } + + private TokenHolders loadReadOnlyTokens( + NeoStores stores, + boolean lenient, + CursorContextFactory cursorContextFactory + ) { + try ( + var cursorContext = cursorContextFactory.create("loadReadOnlyTokens"); + var storeCursors = new CachedStoreCursors(stores, cursorContext) + ) { + stores.start( cursorContext ); + TokensLoader loader = lenient ? StoreTokens.allReadableTokens( stores ) : StoreTokens.allTokens( stores ); + TokenHolder propertyKeys = new CreatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_PROPERTY_KEY ); + TokenHolder labels = new CreatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_LABEL ); + TokenHolder relationshipTypes = new CreatingTokenHolder( ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_RELATIONSHIP_TYPE ); + + propertyKeys.setInitialTokens( lenient ? unique( loader.getPropertyKeyTokens( storeCursors ) ) : loader.getPropertyKeyTokens( storeCursors ) ); + labels.setInitialTokens( lenient ? unique( loader.getLabelTokens( storeCursors ) ) : loader.getLabelTokens( storeCursors ) ); + relationshipTypes.setInitialTokens( + lenient ? unique( loader.getRelationshipTypeTokens( storeCursors ) ) : loader.getRelationshipTypeTokens( storeCursors ) ); + return new TokenHolders( propertyKeys, labels, relationshipTypes ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + private static List unique( List tokens ) + { + if ( !tokens.isEmpty() ) + { + Set names = new HashSet<>( tokens.size() ); + int i = 0; + while ( i < tokens.size() ) + { + if ( names.add( tokens.get( i ).name() ) ) + { + i++; + } + else + { + // Remove the token at the given index, by replacing it with the last token in the list. + // This changes the order of elements, but can be done in constant time instead of linear time. + int lastIndex = tokens.size() - 1; + NamedToken endToken = tokens.remove( lastIndex ); + if ( i < lastIndex ) + { + tokens.set( i, endToken ); + } + } + } + } + return tokens; + } + + @Override + public CommandReaderFactory commandReaderFactory() { + return InMemoryStorageCommandReaderFactory518.INSTANCE; + } + @Override + public void consistencyCheck( + FileSystemAbstraction fileSystem, + DatabaseLayout layout, + Config config, + PageCache pageCache, + IndexProviderMap indexProviders, + InternalLog reportLog, + InternalLog verboseLog, + ConsistencySummaryStatistics summary, + int numberOfThreads, + long maxOffHeapCachingMemory, + OutputStream progressOutput, + boolean verbose, + ConsistencyFlags flags, + CursorContextFactory contextFactory, + PageCacheTracer pageCacheTracer, + LogTailMetadata logTailMetadata + ) { + // we can do no-op, since our "database" is _always_ consistent + } + + @Override + public ImmutableSet getStoreOpenOptions( + FileSystemAbstraction fs, + PageCache pageCache, + DatabaseLayout layout, + CursorContextFactory contextFactory + ) { + // Not sure about this, empty set is returned when the store files are in `little-endian` format + // See: `org.neo4j.kernel.impl.store.format.PageCacheOptionsSelector.select` + return Sets.immutable.empty(); + } + + @Override + public StoreId retrieveStoreId( + FileSystemAbstraction fs, + DatabaseLayout databaseLayout, + PageCache pageCache, + CursorContext cursorContext + ) throws IOException { + return StoreId.retrieveFromStore(fs, databaseLayout, pageCache, cursorContext); + } + + + @Override + public Optional versionInformation(StoreVersionIdentifier storeVersionIdentifier) { + return Optional.of(new InMemoryStoreVersion()); + } + + @Override + public void resetMetadata( + FileSystemAbstraction fileSystemAbstraction, + DatabaseLayout databaseLayout, + Config config, + PageCache pageCache, + CursorContextFactory cursorContextFactory, + PageCacheTracer pageCacheTracer, + StoreId storeId, + UUID externalStoreId + ) { + throw new UnsupportedOperationException(); + } + + @Override + public IncrementalBatchImporter incrementalBatchImporter( + DatabaseLayout databaseLayout, + FileSystemAbstraction fileSystemAbstraction, + PageCacheTracer pageCacheTracer, + Configuration configuration, + LogService logService, + PrintStream printStream, + boolean b, + AdditionalInitialIds additionalInitialIds, + ThrowingSupplier throwingSupplier, + Config config, + Monitor monitor, + JobScheduler jobScheduler, + Collector collector, + LogFilesInitializer logFilesInitializer, + IndexImporterFactory indexImporterFactory, + MemoryTracker memoryTracker, + CursorContextFactory cursorContextFactory, + IndexProvidersAccess indexProvidersAccess + ) { + throw new UnsupportedOperationException(); + } + + @Override + public LockManager createLockManager(Config config, SystemNanoClock systemNanoClock) { + return LockManager.NO_LOCKS_LOCK_MANAGER; + } + + @Override + public List listStorageFiles( + FileSystemAbstraction fileSystem, DatabaseLayout databaseLayout + ) { + return Collections.emptyList(); + } + + @Override + public StorageFilesState checkStoreFileState( + FileSystemAbstraction fs, DatabaseLayout databaseLayout, PageCache pageCache + ) { + return StorageFilesState.recoveredState(); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageEngineImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageEngineImpl.java new file mode 100644 index 0000000000..674aac3673 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageEngineImpl.java @@ -0,0 +1,355 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.configuration.Config; +import org.neo4j.counts.CountsStore; +import org.neo4j.exceptions.KernelException; +import org.neo4j.gds.compat.TokenManager; +import org.neo4j.gds.config.GraphProjectConfig; +import org.neo4j.gds.core.cypher.CypherGraphStore; +import org.neo4j.gds.core.loading.GraphStoreCatalog; +import org.neo4j.gds.storageengine.InMemoryDatabaseCreationCatalog; +import org.neo4j.gds.storageengine.InMemoryTransactionStateVisitor; +import org.neo4j.internal.diagnostics.DiagnosticsLogger; +import org.neo4j.internal.recordstorage.InMemoryStorageReader518; +import org.neo4j.internal.schema.StorageEngineIndexingBehaviour; +import org.neo4j.io.fs.WritableChannel; +import org.neo4j.io.layout.DatabaseLayout; +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.io.pagecache.tracing.DatabaseFlushEvent; +import org.neo4j.kernel.KernelVersion; +import org.neo4j.kernel.impl.store.stats.StoreEntityCounters; +import org.neo4j.kernel.lifecycle.Lifecycle; +import org.neo4j.kernel.lifecycle.LifecycleAdapter; +import org.neo4j.lock.LockGroup; +import org.neo4j.lock.LockService; +import org.neo4j.lock.LockTracer; +import org.neo4j.lock.ResourceLocker; +import org.neo4j.logging.InternalLog; +import org.neo4j.memory.MemoryTracker; +import org.neo4j.storageengine.api.CommandBatchToApply; +import org.neo4j.storageengine.api.CommandCreationContext; +import org.neo4j.storageengine.api.CommandStream; +import org.neo4j.storageengine.api.IndexUpdateListener; +import org.neo4j.storageengine.api.InternalErrorTracer; +import org.neo4j.storageengine.api.MetadataProvider; +import org.neo4j.storageengine.api.StorageCommand; +import org.neo4j.storageengine.api.StorageEngine; +import org.neo4j.storageengine.api.StorageLocks; +import org.neo4j.storageengine.api.StorageReader; +import org.neo4j.storageengine.api.StoreFileMetadata; +import org.neo4j.storageengine.api.StoreId; +import org.neo4j.storageengine.api.TransactionApplicationMode; +import org.neo4j.storageengine.api.cursor.StoreCursors; +import org.neo4j.storageengine.api.enrichment.Enrichment; +import org.neo4j.storageengine.api.enrichment.EnrichmentCommand; +import org.neo4j.storageengine.api.txstate.ReadableTransactionState; +import org.neo4j.storageengine.api.txstate.TxStateVisitor; +import org.neo4j.storageengine.api.txstate.validation.TransactionValidatorFactory; +import org.neo4j.token.TokenHolders; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import static org.neo4j.gds.utils.StringFormatting.formatWithLocale; + +public final class InMemoryStorageEngineImpl implements StorageEngine { + + public static final byte ID = 42; + private final MetadataProvider metadataProvider; + private final CypherGraphStore graphStore; + private final DatabaseLayout databaseLayout; + private final InMemoryTransactionStateVisitor txStateVisitor; + + private final CommandCreationContext commandCreationContext; + + private final TokenManager tokenManager; + private final InMemoryCountsStoreImpl countsStore; + + private static final StorageEngineIndexingBehaviour INDEXING_BEHAVIOUR = new StorageEngineIndexingBehaviour() { + @Override + public boolean useNodeIdsInRelationshipTokenIndex() { + return false; + } + + @Override + public boolean requireCoordinationLocks() { + return false; + } + + @Override + public int nodesPerPage() { + return 0; + } + + @Override + public int relationshipsPerPage() { + return 0; + } + }; + + InMemoryStorageEngineImpl( + DatabaseLayout databaseLayout, + TokenHolders tokenHolders + ) { + this.databaseLayout = databaseLayout; + this.graphStore = getGraphStoreFromCatalog(databaseLayout.getDatabaseName()); + this.txStateVisitor = new InMemoryTransactionStateVisitor(graphStore, tokenHolders); + this.commandCreationContext = new InMemoryCommandCreationContextImpl(); + this.tokenManager = new TokenManager( + tokenHolders, + InMemoryStorageEngineImpl.this.txStateVisitor, + InMemoryStorageEngineImpl.this.graphStore, + commandCreationContext + ); + InMemoryStorageEngineImpl.this.graphStore.initialize(tokenHolders); + this.countsStore = new InMemoryCountsStoreImpl(graphStore, tokenHolders); + this.metadataProvider = new InMemoryMetaDataProviderImpl(); + } + + private static CypherGraphStore getGraphStoreFromCatalog(String databaseName) { + var graphName = InMemoryDatabaseCreationCatalog.getRegisteredDbCreationGraphName(databaseName); + return (CypherGraphStore) GraphStoreCatalog.getAllGraphStores() + .filter(graphStoreWithUserNameAndConfig -> graphStoreWithUserNameAndConfig + .config() + .graphName() + .equals(graphName)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(formatWithLocale( + "No graph with name `%s` was found in GraphStoreCatalog. Available graph names are %s", + graphName, + GraphStoreCatalog.getAllGraphStores() + .map(GraphStoreCatalog.GraphStoreWithUserNameAndConfig::config) + .map(GraphProjectConfig::graphName) + .collect(Collectors.toList()) + ))) + .graphStore(); + } + + @Override + public StoreEntityCounters storeEntityCounters() { + return new StoreEntityCounters() { + @Override + public long nodes(CursorContext cursorContext) { + return graphStore.nodeCount(); + } + + @Override + public long relationships(CursorContext cursorContext) { + return graphStore.relationshipCount(); + } + + @Override + public long properties(CursorContext cursorContext) { + return graphStore.nodePropertyKeys().size() + graphStore.relationshipPropertyKeys().size(); + } + + @Override + public long relationshipTypes(CursorContext cursorContext) { + return graphStore.relationshipTypes().size(); + } + + @Override + public long allNodesCountStore(CursorContext cursorContext) { + return graphStore.nodeCount(); + } + + @Override + public long allRelationshipsCountStore(CursorContext cursorContext) { + return graphStore.relationshipCount(); + } + }; + } + + @Override + public StoreCursors createStorageCursors(CursorContext initialContext) { + return StoreCursors.NULL; + } + + @Override + public StorageLocks createStorageLocks(ResourceLocker locker) { + return new InMemoryStorageLocksImpl(locker); + } + + @Override + public List createCommands( + ReadableTransactionState state, + StorageReader storageReader, + CommandCreationContext creationContext, + LockTracer lockTracer, + TxStateVisitor.Decorator additionalTxStateVisitor, + CursorContext cursorContext, + StoreCursors storeCursors, + MemoryTracker memoryTracker + ) throws KernelException { + state.accept(txStateVisitor); + return List.of(); + } + + @Override + public void dumpDiagnostics(InternalLog internalLog, DiagnosticsLogger diagnosticsLogger) { + } + + @Override + public StoreId retrieveStoreId() { + return metadataProvider.getStoreId(); + } + + @Override + public StorageEngineIndexingBehaviour indexingBehaviour() { + return INDEXING_BEHAVIOUR; + } + + @Override + public StorageReader newReader() { + return new InMemoryStorageReader518(graphStore, tokenManager.tokenHolders(), countsStore); + } + + @Override + public void addIndexUpdateListener(IndexUpdateListener listener) { + + } + + @Override + public void apply(CommandBatchToApply batch, TransactionApplicationMode mode) { + } + + @Override + public void release( + ReadableTransactionState txState, + CursorContext cursorContext, + CommandCreationContext commandCreationContext, + boolean rolledBack + ) { + + } + + @Override + public void init() { + } + + @Override + public void start() { + + } + + @Override + public void stop() { + shutdown(); + } + + @Override + public void shutdown() { + InMemoryDatabaseCreationCatalog.removeDatabaseEntry(databaseLayout.getDatabaseName()); + } + + @Override + public void listStorageFiles( + Collection atomic, Collection replayable + ) { + + } + + @Override + public Lifecycle schemaAndTokensLifecycle() { + return new LifecycleAdapter() { + @Override + public void init() { + + } + }; + } + + @Override + public CountsStore countsAccessor() { + return countsStore; + } + + @Override + public MetadataProvider metadataProvider() { + return metadataProvider; + } + + @Override + public String name() { + return "gds in-memory storage engine"; + } + + @Override + public byte id() { + return ID; + } + + @Override + public CommandCreationContext newCommandCreationContext(boolean multiVersioned) { + return commandCreationContext; + } + + @Override + public TransactionValidatorFactory createTransactionValidatorFactory(Config config) { + return TransactionValidatorFactory.EMPTY_VALIDATOR_FACTORY; + } + + @Override + public void lockRecoveryCommands( + CommandStream commands, LockService lockService, LockGroup lockGroup, TransactionApplicationMode mode + ) { + + } + + @Override + public void checkpoint(DatabaseFlushEvent flushEvent, CursorContext cursorContext) { + // checkpoint is not supported but it is also called when we fail for something else + // that we do not support, such as removing node properties + } + + @Override + public void preAllocateStoreFilesForCommands(CommandBatchToApply batch, TransactionApplicationMode mode) { + // GDS has its own mechanism of memory allocation, so we don't need this + } + + @Override + public EnrichmentCommand createEnrichmentCommand(KernelVersion kernelVersion, Enrichment enrichment) { + return new EnrichmentCommand() { + + @Override + public Enrichment enrichment() { + return null; + } + + @Override + public void serialize(WritableChannel channel) { + + } + + @Override + public KernelVersion kernelVersion() { + return kernelVersion; + } + }; + } + + @Override + public InternalErrorTracer internalErrorTracer() { + return InternalErrorTracer.NO_TRACER; + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageLocksImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageLocksImpl.java new file mode 100644 index 0000000000..c67d1ebe9f --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStorageLocksImpl.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.lock.LockTracer; +import org.neo4j.lock.ResourceLocker; +import org.neo4j.storageengine.api.StorageLocks; +import org.neo4j.storageengine.api.txstate.ReadableTransactionState; + +public class InMemoryStorageLocksImpl implements StorageLocks { + + InMemoryStorageLocksImpl(ResourceLocker locker) {} + + @Override + public void acquireExclusiveNodeLock(LockTracer lockTracer, long... ids) {} + + @Override + public void releaseExclusiveNodeLock(long... ids) {} + + @Override + public void acquireSharedNodeLock(LockTracer lockTracer, long... ids) {} + + @Override + public void releaseSharedNodeLock(long... ids) {} + + @Override + public void acquireExclusiveRelationshipLock(LockTracer lockTracer, long... ids) {} + + @Override + public void releaseExclusiveRelationshipLock(long... ids) {} + + @Override + public void acquireSharedRelationshipLock(LockTracer lockTracer, long... ids) {} + + @Override + public void releaseSharedRelationshipLock(long... ids) {} + + @Override + public void acquireRelationshipCreationLock( + LockTracer lockTracer, + long sourceNode, + long targetNode, + boolean sourceNodeAddedInTx, + boolean targetNodeAddedInTx + ) { + } + + @Override + public void acquireRelationshipDeletionLock( + LockTracer lockTracer, + long sourceNode, + long targetNode, + long relationship, + boolean relationshipAddedInTx, + boolean sourceNodeAddedInTx, + boolean targetNodeAddedInTx + ) { + } + + @Override + public void acquireNodeDeletionLock( + ReadableTransactionState readableTransactionState, + LockTracer lockTracer, + long node + ) {} + + @Override + public void acquireNodeLabelChangeLock(LockTracer lockTracer, long node, int labelId) {} +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStoreVersion.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStoreVersion.java new file mode 100644 index 0000000000..c69bd102e0 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryStoreVersion.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.configuration.Config; +import org.neo4j.storageengine.api.StoreVersion; +import org.neo4j.storageengine.api.format.Capability; +import org.neo4j.storageengine.api.format.CapabilityType; + +import java.util.Optional; + +public class InMemoryStoreVersion implements StoreVersion { + + public static final String STORE_VERSION = "gds-experimental"; + + @Override + public String getStoreVersionUserString() { + return "Unknown"; + } + + @Override + public Optional successorStoreVersion(Config config) { + return Optional.empty(); + } + + @Override + public String formatName() { + return getClass().getSimpleName(); + } + + @Override + public boolean onlyForMigration() { + return false; + } + + @Override + public boolean hasCapability(Capability capability) { + return false; + } + + @Override + public boolean hasCompatibleCapabilities( + StoreVersion otherVersion, CapabilityType type + ) { + return false; + } + + @Override + public String introductionNeo4jVersion() { + return "foo"; + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryTransactionIdStoreImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryTransactionIdStoreImpl.java new file mode 100644 index 0000000000..73e36f2a95 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryTransactionIdStoreImpl.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.internal.recordstorage.AbstractTransactionIdStore; +import org.neo4j.io.pagecache.context.TransactionIdSnapshot; +import org.neo4j.kernel.impl.transaction.log.LogPosition; +import org.neo4j.storageengine.api.ClosedTransactionMetadata; +import org.neo4j.storageengine.api.TransactionId; +import org.neo4j.storageengine.api.TransactionIdStore; +import org.neo4j.util.concurrent.ArrayQueueOutOfOrderSequence; +import org.neo4j.util.concurrent.OutOfOrderSequence; + +public class InMemoryTransactionIdStoreImpl extends AbstractTransactionIdStore { + + @Override + protected OutOfOrderSequence createClosedTransactionId() { + return new ArrayQueueOutOfOrderSequence( + -1L, + 100, + new OutOfOrderSequence.Meta(0, 0, 0, 0, 0) + ); + } + + @Override + protected void initLastCommittedAndClosedTransactionId( + long previouslyCommittedTxId, + int checksum, + long previouslyCommittedTxCommitTimestamp, + long previouslyCommittedTxLogByteOffset, + long previouslyCommittedTxLogVersion + ) { + this.setLastCommittedAndClosedTransactionId( + previouslyCommittedTxId, + checksum, + previouslyCommittedTxCommitTimestamp, + TransactionIdStore.UNKNOWN_CONSENSUS_INDEX, + previouslyCommittedTxLogByteOffset, + previouslyCommittedTxLogVersion + ); + } + + @Override + public ClosedTransactionMetadata getLastClosedTransaction() { + var numberWithMeta = this.closedTransactionId.get(); + OutOfOrderSequence.Meta meta = numberWithMeta.meta(); + return new ClosedTransactionMetadata( + numberWithMeta.number(), + new LogPosition(meta.logVersion(), meta.byteOffset()), + meta.checksum(), + meta.commitTimestamp(), + meta.consensusIndex() + ); + } + + @Override + public TransactionIdSnapshot getClosedTransactionSnapshot() { + return new TransactionIdSnapshot(this.getLastClosedTransactionId()); + } + + @Override + protected TransactionId transactionId(long transactionId, int checksum, long commitTimestamp) { + return new TransactionId(transactionId, checksum, commitTimestamp, TransactionIdStore.UNKNOWN_CONSENSUS_INDEX); + } + + @Override + public void transactionCommitted(long transactionId, int checksum, long commitTimestamp, long consensusIndex) { + + } + + @Override + public void setLastCommittedAndClosedTransactionId( + long transactionId, + int checksum, + long commitTimestamp, + long consensusIndex, + long byteOffset, + long logVersion + ) { + + } + + @Override + public void transactionClosed( + long transactionId, + long logVersion, + long byteOffset, + int checksum, + long commitTimestamp, + long consensusIndex + ) { + this.closedTransactionId.offer( + transactionId, + new OutOfOrderSequence.Meta(logVersion, byteOffset, checksum, commitTimestamp, consensusIndex) + ); + } + + @Override + public void resetLastClosedTransaction( + long transactionId, + long logVersion, + long byteOffset, + int checksum, + long commitTimestamp, + long consensusIndex + ) { + this.closedTransactionId.set( + transactionId, + new OutOfOrderSequence.Meta(logVersion, byteOffset, checksum, commitTimestamp, consensusIndex) + ); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryVersionCheck.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryVersionCheck.java new file mode 100644 index 0000000000..7c7efed573 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/InMemoryVersionCheck.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.kernel.impl.store.format.FormatFamily; +import org.neo4j.storageengine.api.StoreVersionCheck; +import org.neo4j.storageengine.api.StoreVersionIdentifier; + +import static org.neo4j.gds.compat._518.InMemoryStoreVersion.STORE_VERSION; + +public class InMemoryVersionCheck implements StoreVersionCheck { + + private static final StoreVersionIdentifier STORE_IDENTIFIER = new StoreVersionIdentifier( + STORE_VERSION, + FormatFamily.STANDARD.name(), + 0, + 0 + ); + + @Override + public StoreVersionIdentifier getCurrentVersion(CursorContext cursorContext) throws + IllegalArgumentException, IllegalStateException { + return STORE_IDENTIFIER; + } + + @Override + public boolean isCurrentStoreVersionFullySupported(CursorContext cursorContext) { + return true; + } + + @Override + public boolean isStoreVersionFullySupported(StoreVersionIdentifier storeVersion, CursorContext cursorContext) { + return storeVersion.equals(STORE_IDENTIFIER); + } + + @Override + public MigrationCheckResult getAndCheckMigrationTargetVersion(String formatFamily, CursorContext cursorContext) { + return new StoreVersionCheck.MigrationCheckResult(MigrationOutcome.NO_OP, STORE_IDENTIFIER, null, null); + } + + @Override + public UpgradeCheckResult getAndCheckUpgradeTargetVersion(CursorContext cursorContext) { + return new StoreVersionCheck.UpgradeCheckResult(UpgradeOutcome.NO_OP, STORE_IDENTIFIER, null, null); + } + + @Override + public String getIntroductionVersionFromVersion(StoreVersionIdentifier storeVersionIdentifier) { + return STORE_VERSION; + } + + public StoreVersionIdentifier findLatestVersion(String s) { + return STORE_IDENTIFIER; + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/StorageEngineProxyFactoryImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/StorageEngineProxyFactoryImpl.java new file mode 100644 index 0000000000..566eb8c11b --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/StorageEngineProxyFactoryImpl.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.annotations.service.ServiceProvider; +import org.neo4j.gds.compat.Neo4jVersion; +import org.neo4j.gds.compat.StorageEngineProxyApi; +import org.neo4j.gds.compat.StorageEngineProxyFactory; + +@ServiceProvider +public class StorageEngineProxyFactoryImpl implements StorageEngineProxyFactory { + + @Override + public boolean canLoad(Neo4jVersion version) { + return version == Neo4jVersion.V_RC; + } + + @Override + public StorageEngineProxyApi load() { + return new StorageEngineProxyImpl(); + } + + @Override + public String description() { + return "Storage Engine RC"; + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/StorageEngineProxyImpl.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/StorageEngineProxyImpl.java new file mode 100644 index 0000000000..96d318fa1f --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/gds/compat/_518/StorageEngineProxyImpl.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.gds.compat._518; + +import org.neo4j.common.Edition; +import org.neo4j.configuration.Config; +import org.neo4j.configuration.GraphDatabaseInternalSettings; +import org.neo4j.dbms.api.DatabaseManagementService; +import org.neo4j.gds.compat.AbstractInMemoryNodeCursor; +import org.neo4j.gds.compat.AbstractInMemoryNodePropertyCursor; +import org.neo4j.gds.compat.AbstractInMemoryRelationshipPropertyCursor; +import org.neo4j.gds.compat.AbstractInMemoryRelationshipTraversalCursor; +import org.neo4j.gds.compat.GdsDatabaseManagementServiceBuilder; +import org.neo4j.gds.compat.GraphDatabaseApiProxy; +import org.neo4j.gds.compat.StorageEngineProxyApi; +import org.neo4j.gds.core.cypher.CypherGraphStore; +import org.neo4j.graphdb.Direction; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.internal.recordstorage.AbstractInMemoryRelationshipScanCursor; +import org.neo4j.io.layout.DatabaseLayout; +import org.neo4j.storageengine.api.PropertySelection; +import org.neo4j.storageengine.api.RelationshipSelection; +import org.neo4j.storageengine.api.StorageEngine; +import org.neo4j.storageengine.api.StorageEntityCursor; +import org.neo4j.storageengine.api.StoragePropertyCursor; +import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor; +import org.neo4j.token.CreatingTokenHolder; +import org.neo4j.token.ReadOnlyTokenCreator; +import org.neo4j.token.TokenHolders; +import org.neo4j.token.api.TokenHolder; + +import static org.neo4j.configuration.GraphDatabaseSettings.db_format; + +public class StorageEngineProxyImpl implements StorageEngineProxyApi { + + @Override + public void initRelationshipTraversalCursorForRelType( + StorageRelationshipTraversalCursor cursor, + long sourceNodeId, + int relTypeToken + ) { + var relationshipSelection = RelationshipSelection.selection( + relTypeToken, + Direction.OUTGOING + ); + cursor.init(sourceNodeId, -1, relationshipSelection); + } + + @Override + public StorageEngine createInMemoryStorageEngine(DatabaseLayout databaseLayout, TokenHolders tokenHolders) { + return new InMemoryStorageEngineImpl(databaseLayout, tokenHolders); + } + + @Override + public void createInMemoryDatabase( + DatabaseManagementService dbms, + String dbName, + Config config + ) { + config.set(db_format, InMemoryStorageEngineFactory.IN_MEMORY_STORAGE_ENGINE_NAME); + dbms.createDatabase(dbName, config); + } + + @Override + public GraphDatabaseService startAndGetInMemoryDatabase(DatabaseManagementService dbms, String dbName) { + dbms.startDatabase(dbName); + return dbms.database(dbName); + } + + @Override + public GdsDatabaseManagementServiceBuilder setSkipDefaultIndexesOnCreationSetting(GdsDatabaseManagementServiceBuilder dbmsBuilder) { + return dbmsBuilder.setConfig(GraphDatabaseInternalSettings.skip_default_indexes_on_creation, true); + } + + @Override + public AbstractInMemoryNodeCursor inMemoryNodeCursor(CypherGraphStore graphStore, TokenHolders tokenHolders) { + return new InMemoryNodeCursor(graphStore, tokenHolders); + } + + @Override + public AbstractInMemoryNodePropertyCursor inMemoryNodePropertyCursor( + CypherGraphStore graphStore, + TokenHolders tokenHolders + ) { + return new InMemoryNodePropertyCursor(graphStore, tokenHolders); + } + + @Override + public AbstractInMemoryRelationshipTraversalCursor inMemoryRelationshipTraversalCursor( + CypherGraphStore graphStore, TokenHolders tokenHolders + ) { + return new InMemoryRelationshipTraversalCursor(graphStore, tokenHolders); + } + + @Override + public AbstractInMemoryRelationshipScanCursor inMemoryRelationshipScanCursor( + CypherGraphStore graphStore, TokenHolders tokenHolders + ) { + return new InMemoryRelationshipScanCursor(graphStore, tokenHolders); + } + + @Override + public AbstractInMemoryRelationshipPropertyCursor inMemoryRelationshipPropertyCursor( + CypherGraphStore graphStore, TokenHolders tokenHolders + ) { + return new InMemoryRelationshipPropertyCursor(graphStore, tokenHolders); + } + + @Override + public void properties( + StorageEntityCursor storageCursor, StoragePropertyCursor propertyCursor, int[] propertySelection + ) { + PropertySelection selection; + if (propertySelection.length == 0) { + selection = PropertySelection.ALL_PROPERTIES; + } else { + selection = PropertySelection.selection(propertySelection); + } + storageCursor.properties(propertyCursor, selection); + } + + @Override + public Edition dbmsEdition(GraphDatabaseService databaseService) { + return GraphDatabaseApiProxy.dbmsInfo(databaseService).edition; + } + + @Override + public TokenHolders newTokenHolders() { + return new TokenHolders( + new CreatingTokenHolder(ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_LABEL), + new CreatingTokenHolder(ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_PROPERTY_KEY), + new CreatingTokenHolder(ReadOnlyTokenCreator.READ_ONLY, TokenHolder.TYPE_RELATIONSHIP_TYPE) + ); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository518.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository518.java new file mode 100644 index 0000000000..def16f012a --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryLogVersionRepository518.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.internal.recordstorage; + +import org.neo4j.storageengine.api.LogVersionRepository; + +import java.util.concurrent.atomic.AtomicLong; + +public class InMemoryLogVersionRepository518 implements LogVersionRepository { + + private final AtomicLong logVersion; + private final AtomicLong checkpointLogVersion; + + public InMemoryLogVersionRepository518() { + this(0, 0); + } + + private InMemoryLogVersionRepository518(long initialLogVersion, long initialCheckpointLogVersion) { + this.logVersion = new AtomicLong(); + this.checkpointLogVersion = new AtomicLong(); + this.logVersion.set(initialLogVersion); + this.checkpointLogVersion.set(initialCheckpointLogVersion); + } + + @Override + public void setCurrentLogVersion(long version) { + this.logVersion.set(version); + } + + @Override + public long incrementAndGetVersion() { + return this.logVersion.incrementAndGet(); + } + + @Override + public void setCheckpointLogVersion(long version) { + this.checkpointLogVersion.set(version); + } + + @Override + public long incrementAndGetCheckpointLogVersion() { + return this.checkpointLogVersion.incrementAndGet(); + } + + @Override + public long getCurrentLogVersion() { + return this.logVersion.get(); + } + + @Override + public long getCheckpointLogVersion() { + return this.checkpointLogVersion.get(); + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory518.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory518.java new file mode 100644 index 0000000000..c014a1cc93 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageCommandReaderFactory518.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.internal.recordstorage; + +import org.neo4j.kernel.KernelVersion; +import org.neo4j.storageengine.api.CommandReader; +import org.neo4j.storageengine.api.CommandReaderFactory; + +public class InMemoryStorageCommandReaderFactory518 implements CommandReaderFactory { + + public static final CommandReaderFactory INSTANCE = new InMemoryStorageCommandReaderFactory518(); + + @Override + public CommandReader get(KernelVersion kernelVersion) { + switch (kernelVersion) { + case V4_2: + return LogCommandSerializationV4_2.INSTANCE; + case V4_3_D4: + return LogCommandSerializationV4_3_D3.INSTANCE; + case V5_0: + return LogCommandSerializationV5_0.INSTANCE; + default: + throw new IllegalArgumentException("Unsupported kernel version " + kernelVersion); + } + } +} diff --git a/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader518.java b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader518.java new file mode 100644 index 0000000000..5d666f56c3 --- /dev/null +++ b/compatibility/5.18/storage-engine-adapter/src/main/java17/org/neo4j/internal/recordstorage/InMemoryStorageReader518.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://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.internal.recordstorage; + +import org.eclipse.collections.api.set.primitive.IntSet; +import org.neo4j.common.EntityType; +import org.neo4j.common.TokenNameLookup; +import org.neo4j.counts.CountsStore; +import org.neo4j.counts.CountsVisitor; +import org.neo4j.gds.NodeLabel; +import org.neo4j.gds.RelationshipType; +import org.neo4j.gds.compat._518.InMemoryNodeCursor; +import org.neo4j.gds.compat._518.InMemoryPropertyCursor; +import org.neo4j.gds.compat._518.InMemoryRelationshipScanCursor; +import org.neo4j.gds.compat._518.InMemoryRelationshipTraversalCursor; +import org.neo4j.gds.core.cypher.CypherGraphStore; +import org.neo4j.internal.schema.ConstraintDescriptor; +import org.neo4j.internal.schema.IndexDescriptor; +import org.neo4j.internal.schema.IndexType; +import org.neo4j.internal.schema.SchemaDescriptor; +import org.neo4j.internal.schema.constraints.IndexBackedConstraintDescriptor; +import org.neo4j.io.pagecache.context.CursorContext; +import org.neo4j.memory.MemoryTracker; +import org.neo4j.storageengine.api.AllNodeScan; +import org.neo4j.storageengine.api.AllRelationshipsScan; +import org.neo4j.storageengine.api.StorageNodeCursor; +import org.neo4j.storageengine.api.StoragePropertyCursor; +import org.neo4j.storageengine.api.StorageReader; +import org.neo4j.storageengine.api.StorageRelationshipScanCursor; +import org.neo4j.storageengine.api.StorageRelationshipTraversalCursor; +import org.neo4j.storageengine.api.StorageSchemaReader; +import org.neo4j.storageengine.api.cursor.StoreCursors; +import org.neo4j.token.TokenHolders; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class InMemoryStorageReader518 implements StorageReader { + + protected final CypherGraphStore graphStore; + protected final TokenHolders tokenHolders; + protected final CountsStore counts; + private final Map, Object> dependantState; + private boolean closed; + + public InMemoryStorageReader518( + CypherGraphStore graphStore, + TokenHolders tokenHolders, + CountsStore counts + ) { + this.graphStore = graphStore; + + this.tokenHolders = tokenHolders; + this.counts = counts; + this.dependantState = new ConcurrentHashMap<>(); + } + + @Override + public Collection uniquenessConstraintsGetRelated( + int[] changedLabels, + int[] unchangedLabels, + int[] propertyKeyIds, + boolean propertyKeyListIsComplete, + EntityType entityType + ) { + return Collections.emptyList(); + } + + @Override + public long relationshipsGetCount(CursorContext cursorTracer) { + return graphStore.relationshipCount(); + } + + @Override + public boolean nodeExists(long id, StoreCursors storeCursors) { + var originalId = graphStore.nodes().toOriginalNodeId(id); + return graphStore.nodes().containsOriginalId(originalId); + } + + @Override + public boolean relationshipExists(long id, StoreCursors storeCursors) { + return true; + } + + @Override + public StorageNodeCursor allocateNodeCursor( + CursorContext cursorContext, StoreCursors storeCursors + ) { + return new InMemoryNodeCursor(graphStore, tokenHolders); + } + + @Override + public StoragePropertyCursor allocatePropertyCursor( + CursorContext cursorContext, StoreCursors storeCursors, MemoryTracker memoryTracker + ) { + return new InMemoryPropertyCursor(graphStore, tokenHolders); + } + + @Override + public StorageRelationshipTraversalCursor allocateRelationshipTraversalCursor( + CursorContext cursorContext, StoreCursors storeCursors + ) { + return new InMemoryRelationshipTraversalCursor(graphStore, tokenHolders); + } + + @Override + public StorageRelationshipScanCursor allocateRelationshipScanCursor( + CursorContext cursorContext, StoreCursors storeCursors + ) { + return new InMemoryRelationshipScanCursor(graphStore, tokenHolders); + } + + @Override + public IndexDescriptor indexGetForSchemaAndType( + SchemaDescriptor descriptor, IndexType type + ) { + return null; + } + + @Override + public AllRelationshipsScan allRelationshipScan() { + return new AbstractInMemoryAllRelationshipScan() { + @Override + boolean scanRange(AbstractInMemoryRelationshipScanCursor cursor, long start, long stopInclusive) { + return cursor.scanRange(start, stopInclusive); + } + + @Override + public boolean scanBatch(long sizeHint, AbstractInMemoryRelationshipScanCursor cursor) { + return super.scanBatch(sizeHint, cursor); + } + }; + } + + @Override + public Iterator indexGetForSchema(SchemaDescriptor descriptor) { + return Collections.emptyIterator(); + } + + @Override + public Iterator indexesGetForLabel(int labelId) { + return Collections.emptyIterator(); + } + + @Override + public Iterator indexesGetForRelationshipType(int relationshipType) { + return Collections.emptyIterator(); + } + + @Override + public IndexDescriptor indexGetForName(String name) { + return null; + } + + @Override + public ConstraintDescriptor constraintGetForName(String name) { + return null; + } + + @Override + public boolean indexExists(IndexDescriptor index) { + return false; + } + + @Override + public Iterator indexesGetAll() { + return Collections.emptyIterator(); + } + + @Override + public Collection valueIndexesGetRelated( + int[] tokens, int propertyKeyId, EntityType entityType + ) { + return valueIndexesGetRelated(tokens, new int[]{propertyKeyId}, entityType); + } + + @Override + public Collection valueIndexesGetRelated( + int[] tokens, int[] propertyKeyIds, EntityType entityType + ) { + return Collections.emptyList(); + } + + @Override + public Collection uniquenessConstraintsGetRelated( + int[] labels, + int propertyKeyId, + EntityType entityType + ) { + return Collections.emptyList(); + } + + @Override + public Collection uniquenessConstraintsGetRelated( + int[] tokens, + int[] propertyKeyIds, + EntityType entityType + ) { + return Collections.emptyList(); + } + + @Override + public boolean hasRelatedSchema(int[] labels, int propertyKey, EntityType entityType) { + return false; + } + + @Override + public boolean hasRelatedSchema(int label, EntityType entityType) { + return false; + } + + @Override + public Iterator constraintsGetForSchema(SchemaDescriptor descriptor) { + return Collections.emptyIterator(); + } + + @Override + public boolean constraintExists(ConstraintDescriptor descriptor) { + return false; + } + + @Override + public Iterator constraintsGetForLabel(int labelId) { + return Collections.emptyIterator(); + } + + @Override + public Iterator constraintsGetForRelationshipType(int typeId) { + return Collections.emptyIterator(); + } + + @Override + public Iterator constraintsGetAll() { + return Collections.emptyIterator(); + } + + @Override + public IntSet[] constraintsGetPropertyTokensForLogicalKey(int token, EntityType entityType) { + return new IntSet[0]; + } + + @Override + public Long indexGetOwningUniquenessConstraintId(IndexDescriptor index) { + return null; + } + + @Override + public long countsForNode(int labelId, CursorContext cursorContext) { + return counts.nodeCount(labelId, cursorContext); + } + + @Override + public long estimateCountsForNode(int labelId, CursorContext cursorContext) { + return counts.estimateNodeCount(labelId, cursorContext); + } + + @Override + public long countsForRelationship(int startLabelId, int typeId, int endLabelId, CursorContext cursorContext) { + return counts.relationshipCount(startLabelId, typeId, endLabelId, cursorContext); + } + + @Override + public void visitAllCounts(CountsVisitor countsVisitor, CursorContext cursorContext) { + for (NodeLabel label : graphStore.nodeLabels()) { + var tokenHolder = tokenHolders.labelTokens(); + countsVisitor.visitNodeCount(tokenHolder.getIdByName(label.name()), graphStore.nodes().nodeCount(label)); + } + + for (RelationshipType relationshipType : graphStore.relationshipTypes()) { + var tokenHolder = tokenHolders.relationshipTypeTokens(); + // we dont know start / end label for a reltype + countsVisitor.visitRelationshipCount( + -1, + tokenHolder.getIdByName(relationshipType.name()), + -1, + graphStore.relationshipCount(relationshipType) + ); + } + } + + @Override + public long estimateCountsForRelationship(int startLabelId, int typeId, int endLabelId, CursorContext cursorContext) { + return counts.estimateRelationshipCount(startLabelId, typeId, endLabelId, cursorContext); + } + + @Override + public long nodesGetCount(CursorContext cursorContext) { + return graphStore.nodeCount(); + } + + @Override + public int labelCount() { + return graphStore.nodes().availableNodeLabels().size(); + } + + @Override + public int propertyKeyCount() { + int nodePropertyCount = graphStore + .schema() + .nodeSchema() + .unionProperties() + .size(); + int relPropertyCount = graphStore + .schema() + .relationshipSchema() + .unionProperties() + .size(); + + return nodePropertyCount + relPropertyCount; + } + + @Override + public int relationshipTypeCount() { + return graphStore.schema().relationshipSchema().entries().size(); + } + + @Override + public T getOrCreateSchemaDependantState(Class type, Function factory) { + return type.cast(dependantState.computeIfAbsent(type, key -> factory.apply(this))); + } + + @Override + public AllNodeScan allNodeScan() { + return new InMemoryNodeScan(); + } + + @Override + public void close() { + assert !closed; + closed = true; + } + + @Override + public StorageSchemaReader schemaSnapshot() { + return this; + } + + @Override + public TokenNameLookup tokenNameLookup() { + return tokenHolders; + } + +} diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 90961d8c7c..709fc3a1b5 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -8,6 +8,7 @@ ext { '5.15': properties.getOrDefault('neo4jVersion515', '5.15.0'), '5.16': properties.getOrDefault('neo4jVersion516', '5.16.0'), '5.17': properties.getOrDefault('neo4jVersion517', '5.17.0'), + '5.18': properties.getOrDefault('neo4jVersion518', '5.18.0'), ] neo4jDefault = neos.'4.4'