From c64f30952c42e7339964481143fe02c210fc4a93 Mon Sep 17 00:00:00 2001 From: Paul Horn Date: Fri, 20 Sep 2024 16:18:30 +0200 Subject: [PATCH 1/5] Inline usages of Neo4jProxy This is part 1. Having all those old compat methods is a magnet for all kinds of Neo4j dependencies. Having to go through Neo4jProxy, even if it is no longer requires, would also try to initialize ALL other dependencies of that class (which is a lot) --- .../gds/compat/CompatExecutionContext.java | 6 - .../java/org/neo4j/gds/compat/Neo4jProxy.java | 280 ------------------ .../gds/compat/PartitionedStoreScan.java | 4 +- .../org/neo4j/gds/compat/Neo4jProxyTest.java | 2 +- .../java/org/neo4j/gds/TerminationTest.java | 5 +- .../org/neo4j/gds/core/GdsProxyExtension.java | 5 +- .../org/neo4j/gds/core/UsernameExtension.java | 5 +- .../java/org/neo4j/gds/core/UsernameTest.java | 3 +- .../projection/AlphaCypherAggregation.java | 9 +- .../gds/projection/CypherAggregation.java | 9 +- .../CypherAggregationExtension.java | 5 +- .../projection/ExecutingQueryProvider.java | 7 +- .../gds/executor/ProcedureExecutorTest.java | 14 +- .../neo4j/gds/executor/ExecutionContext.java | 13 +- .../core/io/db/GdsParallelBatchImporter.java | 16 +- .../GraphStoreToDatabaseExporterConfig.java | 9 +- .../CypherRecordLoader.java | 3 +- .../projection/NodeLabelIndexScanTest.java | 49 ++- .../AbstractCursorBasedScanner.java | 22 +- .../gds/projection/BufferedNodeConsumer.java | 12 +- .../gds/projection/GraphDimensionsReader.java | 8 +- .../MultipleNodeLabelIndexBasedScanner.java | 4 +- .../MultipleNodeLabelIndexReference.java | 5 +- .../NativeNodePropertyImporter.java | 12 +- .../projection/NodeCursorBasedScanner.java | 2 +- .../gds/projection/NodeCursorReference.java | 3 +- .../NodeLabelIndexBasedScanner.java | 13 +- .../projection/NodeLabelIndexReference.java | 5 +- .../gds/projection/PartitionedStoreScan.java | 5 +- .../RelationshipScanCursorBasedScanner.java | 2 +- .../RelationshipScanCursorReference.java | 3 +- .../projection/RelationshipsScannerTask.java | 7 +- .../projection/BufferedNodeConsumerTest.java | 4 +- .../BufferedRelationshipConsumerTest.java | 6 +- .../org/neo4j/gds/settings/Neo4jSettings.java | 9 +- .../LinkPredictionTrainingPipelineTest.java | 38 ++- ...opertiesComputationResultConsumerTest.java | 14 +- .../GraphCatalogProcedureFacadeFactory.java | 3 +- .../neo4j/gds/procedures/UserAccessor.java | 5 +- .../procedures/integration/LogAccessor.java | 3 +- .../progress/TaskRegistryFactoryProvider.java | 5 +- .../UserLogRegistryFactoryProvider.java | 5 +- .../main/java/org/neo4j/gds/QueryRunner.java | 52 +++- .../DatabaseTransactionContext.java | 3 +- 44 files changed, 292 insertions(+), 402 deletions(-) diff --git a/compatibility/api/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompatExecutionContext.java b/compatibility/api/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompatExecutionContext.java index 899030b961a..1e4b81f9218 100644 --- a/compatibility/api/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompatExecutionContext.java +++ b/compatibility/api/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompatExecutionContext.java @@ -21,15 +21,9 @@ import org.neo4j.internal.kernel.api.Cursor; import org.neo4j.internal.kernel.api.PartitionedScan; -import org.neo4j.internal.kernel.api.security.AccessMode; -import org.neo4j.io.pagecache.context.CursorContext; public interface CompatExecutionContext extends AutoCloseable { - CursorContext cursorContext(); - - AccessMode accessMode(); - boolean reservePartition(PartitionedScan scan, C cursor); @Override diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/Neo4jProxy.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/Neo4jProxy.java index 5619fa7fbf8..6116f42d10d 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/Neo4jProxy.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/Neo4jProxy.java @@ -20,15 +20,8 @@ package org.neo4j.gds.compat; import org.intellij.lang.annotations.PrintFormat; -import org.jetbrains.annotations.TestOnly; -import org.neo4j.common.DependencyResolver; -import org.neo4j.configuration.BootloaderSettings; import org.neo4j.configuration.Config; import org.neo4j.configuration.GraphDatabaseSettings; -import org.neo4j.configuration.SettingValueParsers; -import org.neo4j.configuration.connectors.ConnectorPortRegister; -import org.neo4j.configuration.connectors.ConnectorType; -import org.neo4j.configuration.helpers.DatabaseNameValidator; import org.neo4j.dbms.api.DatabaseNotFoundException; import org.neo4j.exceptions.KernelException; import org.neo4j.gds.annotation.SuppressForbidden; @@ -38,59 +31,27 @@ import org.neo4j.gds.compat.batchimport.input.Collector; import org.neo4j.gds.compat.batchimport.input.Estimates; import org.neo4j.gds.compat.batchimport.input.ReadableGroups; -import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; -import org.neo4j.graphdb.config.Setting; -import org.neo4j.internal.helpers.HostnamePort; import org.neo4j.internal.id.IdGenerator; import org.neo4j.internal.id.IdGeneratorFactory; -import org.neo4j.internal.kernel.api.Cursor; -import org.neo4j.internal.kernel.api.EntityCursor; import org.neo4j.internal.kernel.api.NodeCursor; import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; -import org.neo4j.internal.kernel.api.PartitionedScan; -import org.neo4j.internal.kernel.api.PropertyCursor; -import org.neo4j.internal.kernel.api.Read; import org.neo4j.internal.kernel.api.RelationshipScanCursor; -import org.neo4j.internal.kernel.api.connectioninfo.ClientConnectionInfo; import org.neo4j.internal.kernel.api.exceptions.ProcedureException; -import org.neo4j.internal.kernel.api.security.AccessMode; -import org.neo4j.internal.kernel.api.security.AuthSubject; -import org.neo4j.internal.kernel.api.security.AuthenticationResult; -import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.internal.recordstorage.RecordIdType; import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.io.layout.DatabaseLayout; -import org.neo4j.io.layout.Neo4jLayout; import org.neo4j.io.pagecache.context.CursorContext; import org.neo4j.kernel.api.KernelTransaction; -import org.neo4j.kernel.api.KernelTransactionHandle; import org.neo4j.kernel.api.procedure.CallableProcedure; 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.database.NormalizedDatabaseName; -import org.neo4j.kernel.impl.coreapi.InternalTransaction; -import org.neo4j.kernel.impl.query.QueryExecutionConfiguration; -import org.neo4j.kernel.impl.query.TransactionalContext; -import org.neo4j.kernel.impl.query.TransactionalContextFactory; import org.neo4j.logging.InternalLog; -import org.neo4j.logging.Log; import org.neo4j.logging.internal.LogService; import org.neo4j.scheduler.JobScheduler; -import org.neo4j.ssl.config.SslPolicyLoader; -import org.neo4j.storageengine.api.LongReference; -import org.neo4j.storageengine.api.PropertySelection; -import org.neo4j.storageengine.api.Reference; -import org.neo4j.storageengine.api.StorageEngineFactory; import org.neo4j.values.SequenceValue; -import org.neo4j.values.storable.TextArray; -import org.neo4j.values.virtual.MapValue; -import org.neo4j.values.virtual.NodeValue; -import org.neo4j.values.virtual.VirtualValues; import java.io.OutputStream; import java.nio.ByteBuffer; @@ -102,7 +63,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.stream.Stream; import static java.lang.String.format; @@ -113,69 +73,6 @@ public final class Neo4jProxy { ProxyUtil.MayLogToStdout.YES ); - public static long estimateNodeCount(Read read, int label) { - return read.estimateCountsForNode(label); - } - - public static long estimateRelationshipCount(Read read, int sourceLabel, int targetLabel, int type) { - return read.estimateCountsForRelationships(sourceLabel, type, targetLabel); - } - - public static DependencyResolver emptyDependencyResolver() { - return new DependencyResolver() { - @Override - public T resolveDependency(Class type, SelectionStrategy selector) { - return null; - } - - @Override - public boolean containsDependency(Class type) { - return false; - } - }; - } - - public static 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(); - } - }; - } - - public static BoltTransactionRunner boltTransactionRunner() { - return new BoltTransactionRunner(); - } - - public static 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); - } - public static T lookupComponentProvider(Context ctx, Class component, boolean safe) throws ProcedureException { var globalProcedures = GraphDatabaseApiProxy.resolveDependency( @@ -185,98 +82,10 @@ public static T lookupComponentProvider(Context ctx, Class component, boo return globalProcedures.getCurrentView().lookupComponentProvider(component, safe).apply(ctx); } - public static String validateExternalDatabaseName(String databaseName) { - var normalizedName = new NormalizedDatabaseName(databaseName); - DatabaseNameValidator.validateExternalDatabaseName(normalizedName); - return normalizedName.name(); - } - - public static String username(AuthSubject subject) { - return subject.executingUser(); - } - - // Maybe we should move this to a test-only proxy? - @TestOnly - public static SecurityContext securityContext( - String username, - AuthSubject authSubject, - AccessMode mode, - String databaseName - ) { - return new SecurityContext( - new AuthSubject() { - @Override - public AuthenticationResult getAuthenticationResult() { - return authSubject.getAuthenticationResult(); - } - - @Override - public boolean hasUsername(String s) { - return s.equals(username); - } - - @Override - public String executingUser() { - return username; - } - }, - mode, - // GDS is always operating from an embedded context - ClientConnectionInfo.EMBEDDED_CONNECTION, - databaseName - ); - } - - - public static PropertyCursor allocatePropertyCursor(KernelTransaction kernelTransaction) { - return kernelTransaction - .cursors() - .allocatePropertyCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker()); - } - - public static Reference propertyReference(EntityCursor nodeCursor) { - return nodeCursor.propertiesReference(); - } - - public static Reference noPropertyReference() { - return LongReference.NULL_REFERENCE; - } - - public static void nodeProperties( - KernelTransaction kernelTransaction, - long nodeReference, - Reference reference, - PropertyCursor cursor - ) { - kernelTransaction - .dataRead() - .nodeProperties(nodeReference, reference, PropertySelection.ALL_PROPERTIES, cursor); - } - - public static NodeCursor allocateNodeCursor(KernelTransaction kernelTransaction) { - return kernelTransaction.cursors().allocateNodeCursor(kernelTransaction.cursorContext()); - } - - public static RelationshipScanCursor allocateRelationshipScanCursor(KernelTransaction kernelTransaction) { - return kernelTransaction.cursors().allocateRelationshipScanCursor(kernelTransaction.cursorContext()); - } - - public static NodeLabelIndexCursor allocateNodeLabelIndexCursor(KernelTransaction kernelTransaction) { - return kernelTransaction.cursors().allocateNodeLabelIndexCursor(kernelTransaction.cursorContext()); - } - public static boolean hasNodeLabelIndex(KernelTransaction kernelTransaction) { return NodeLabelIndexLookupImpl.hasNodeLabelIndex(kernelTransaction); } - public static StoreScan nodeLabelIndexScan( - KernelTransaction transaction, - int labelId, - int batchSize - ) { - return PartitionedStoreScan.createScans(transaction, batchSize, labelId).get(0); - } - public static StoreScan nodesScan(KernelTransaction ktx, long nodeCount, int batchSize) { int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(nodeCount, batchSize); return new PartitionedStoreScan<>(ktx.dataRead().allNodesScan(numberOfPartitions, ktx.cursorContext())); @@ -317,18 +126,6 @@ public static BatchImporter instantiateBatchImporter( ); } - public static Setting additionalJvm() { - return BootloaderSettings.additional_jvm; - } - - public static Setting pageCacheMemory() { - return GraphDatabaseSettings.pagecache_memory; - } - - public static Long pageCacheMemoryValue(String value) { - return SettingValueParsers.BYTES.parse(value); - } - public static long getHighestPossibleNodeCount(IdGeneratorFactory idGeneratorFactory) { return InternalReadOps.findValidIdGeneratorsStream( idGeneratorFactory, @@ -341,10 +138,6 @@ public static long getHighestPossibleNodeCount(IdGeneratorFactory idGeneratorFac .orElseThrow(InternalReadOps::unsupportedStoreFormatException); } - public static long getHighestPossibleRelationshipCount(Read read) { - return read.relationshipsGetCount(); - } - public static ReadableGroups newGroups() { return IMPL.newGroups(); } @@ -430,20 +223,6 @@ public static TestLog testLog() { return new TestLogImpl(); } - @SuppressForbidden(reason = "This is the compat specific use") - public static Log getUserLog(LogService logService, Class loggingClass) { - return logService.getUserLog(loggingClass); - } - - @SuppressForbidden(reason = "This is the compat specific use") - public static Log getInternalLog(LogService logService, Class loggingClass) { - return logService.getInternalLog(loggingClass); - } - - public static NodeValue nodeValue(long id, TextArray labels, MapValue properties) { - return VirtualValues.nodeValue(id, String.valueOf(id), labels, properties); - } - public static Relationship virtualRelationship(long id, Node startNode, Node endNode, RelationshipType type) { return new VirtualRelationshipImpl(id, startNode, endNode, type); } @@ -469,41 +248,6 @@ private static Optional migratedDefaultDatabaseFormatSetting() { } } - public static void configureDatabaseFormat(Config.Builder configBuilder, String recordFormat) { - var databaseRecordFormat = recordFormat.toLowerCase(Locale.ENGLISH); - configBuilder.set(GraphDatabaseSettings.db_format, databaseRecordFormat); - } - - public static DatabaseLayout databaseLayout(Config config, String databaseName) { - var neo4jLayout = neo4jLayout(config); - var storageEngineFactory = StorageEngineFactory.selectStorageEngine(config); - return storageEngineFactory.databaseLayout(neo4jLayout, databaseName); - } - - public static Stream allAvailableDatabaseLayouts(Config config, String databaseName) { - var neo4jLayout = neo4jLayout(config); - return StorageEngineFactory.allAvailableStorageEngines().stream() - .map(engine -> engine.databaseLayout(neo4jLayout, databaseName)); - } - - @SuppressForbidden(reason = "This is the compat specific use") - public static Neo4jLayout neo4jLayout(Config config) { - return Neo4jLayout.of(config); - } - - public static HostnamePort getLocalBoltAddress(ConnectorPortRegister connectorPortRegister) { - return connectorPortRegister.getLocalAddress(ConnectorType.BOLT); - } - - @SuppressForbidden(reason = "This is the compat specific use") - public static SslPolicyLoader createSllPolicyLoader( - FileSystemAbstraction fileSystem, - Config config, - LogService logService - ) { - return SslPolicyLoader.create(fileSystem, config, logService.getInternalLogProvider()); - } - @SuppressForbidden(reason = "This is the compat API") public static CallableProcedure callableProcedure(CompatCallableProcedure procedure) { return IMPL.callableProcedure(procedure); @@ -567,14 +311,6 @@ public static String exceptionMessage(Throwable e) { return IMPL.exceptionMessage(e); } - public static long transactionId(KernelTransactionHandle kernelTransactionHandle) { - return kernelTransactionHandle.getTransactionSequenceNumber(); - } - - public static long transactionId(KernelTransaction kernelTransaction) { - return kernelTransaction.getTransactionSequenceNumber(); - } - public static void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size, CursorContext cursorContext) { var idGenerator = InternalReadOps.findValidIdGeneratorsStream( generatorFactory, @@ -587,22 +323,6 @@ public static void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size idGenerator.nextConsecutiveIdRange(size, false, cursorContext); } - public static TransactionalContext newQueryContext( - TransactionalContextFactory contextFactory, - InternalTransaction tx, - String queryText, - MapValue queryParameters - ) { - return contextFactory.newContext(tx, queryText, queryParameters, QueryExecutionConfiguration.DEFAULT_CONFIG); - } - - - public static void registerCloseableResource( - org.neo4j.kernel.api.KernelTransaction transaction, - AutoCloseable autoCloseable - ) { - transaction.resourceMonitor().registerCloseableResource(autoCloseable); - } /** * The implementations of this method should look identical and are source-compatible. diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java index 993efffed48..656fe0a6fff 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java @@ -64,11 +64,11 @@ public static List> createScans( // and use that one as the driving partitioned index scan. The partitions // of all other partitioned index scans will be aligned to that one. int maxToken = labelIds[0]; - long maxCount = Neo4jProxy.estimateNodeCount(read, labelIds[0]); + long maxCount = read.estimateCountsForNode(labelIds[0]); int maxIndex = 0; for (int i = 1; i < labelIds.length; i++) { - long count = Neo4jProxy.estimateNodeCount(read, labelIds[i]); + long count = read.estimateCountsForNode(labelIds[i]); if (count > maxCount) { maxCount = count; maxToken = labelIds[i]; diff --git a/compatibility/common/neo4j-kernel-adapter/src/test/java/org/neo4j/gds/compat/Neo4jProxyTest.java b/compatibility/common/neo4j-kernel-adapter/src/test/java/org/neo4j/gds/compat/Neo4jProxyTest.java index 47800a8e1b9..af8a00679c3 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/test/java/org/neo4j/gds/compat/Neo4jProxyTest.java +++ b/compatibility/common/neo4j-kernel-adapter/src/test/java/org/neo4j/gds/compat/Neo4jProxyTest.java @@ -35,7 +35,7 @@ class Neo4jProxyTest { @Test void shouldLoadProxySuccessfully() { // Any access to the proxy will trigger loading an implementation - assertThatCode(Neo4jProxy::additionalJvm).doesNotThrowAnyException(); + assertThatCode(Neo4jProxy::emptyCollector).doesNotThrowAnyException(); } @ParameterizedTest diff --git a/core/src/integrationTest/java/org/neo4j/gds/TerminationTest.java b/core/src/integrationTest/java/org/neo4j/gds/TerminationTest.java index 26ac0b67165..c4a2403055c 100644 --- a/core/src/integrationTest/java/org/neo4j/gds/TerminationTest.java +++ b/core/src/integrationTest/java/org/neo4j/gds/TerminationTest.java @@ -21,7 +21,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.concurrency.DefaultPool; import org.neo4j.gds.core.concurrency.ParallelUtil; import org.neo4j.graphdb.TransactionFailureException; @@ -59,7 +58,7 @@ void setup() throws Exception { private void terminateTransaction(long txId) { kernelTransactions.activeTransactions() .stream() - .filter(thx -> Neo4jProxy.transactionId(thx) == txId) + .filter(thx -> thx.getTransactionSequenceNumber() == txId) .forEach(ktx -> ktx.markForTermination(Status.Transaction.TransactionMarkedAsFailed)); } @@ -70,7 +69,7 @@ private Map getQueryTransactionIds() { String query = kth.executingQuery() .map(ExecutingQuery::rawQueryText) .orElse(""); - map.put(query, Neo4jProxy.transactionId(kth)); + map.put(query, kth.getTransactionSequenceNumber()); }); return map; } diff --git a/core/src/main/java/org/neo4j/gds/core/GdsProxyExtension.java b/core/src/main/java/org/neo4j/gds/core/GdsProxyExtension.java index 26d9fa1ae89..6556f104fd0 100644 --- a/core/src/main/java/org/neo4j/gds/core/GdsProxyExtension.java +++ b/core/src/main/java/org/neo4j/gds/core/GdsProxyExtension.java @@ -20,13 +20,13 @@ package org.neo4j.gds.core; import org.neo4j.annotations.service.ServiceProvider; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.ProxyUtil; import org.neo4j.kernel.extension.ExtensionFactory; import org.neo4j.kernel.extension.ExtensionType; import org.neo4j.kernel.extension.context.ExtensionContext; import org.neo4j.kernel.lifecycle.Lifecycle; import org.neo4j.kernel.lifecycle.LifecycleAdapter; +import org.neo4j.logging.Log; import org.neo4j.logging.internal.LogService; @ServiceProvider @@ -39,7 +39,8 @@ public GdsProxyExtension() { @Override public Lifecycle newInstance(ExtensionContext context, Dependencies dependencies) { return LifecycleAdapter.onInit(() -> { - var log = Neo4jProxy.getUserLog(dependencies.logService(), GdsProxyExtension.class); + LogService logService = dependencies.logService(); + var log = (Log) logService.getUserLog(GdsProxyExtension.class); ProxyUtil.dumpLogMessages(log); }); } diff --git a/core/src/main/java/org/neo4j/gds/core/UsernameExtension.java b/core/src/main/java/org/neo4j/gds/core/UsernameExtension.java index d96756e3b17..90c88e09a2e 100644 --- a/core/src/main/java/org/neo4j/gds/core/UsernameExtension.java +++ b/core/src/main/java/org/neo4j/gds/core/UsernameExtension.java @@ -20,7 +20,7 @@ package org.neo4j.gds.core; import org.neo4j.annotations.service.ServiceProvider; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.internal.kernel.api.security.AuthSubject; import org.neo4j.kernel.api.procedure.Context; import org.neo4j.kernel.api.procedure.GlobalProcedures; import org.neo4j.kernel.extension.ExtensionFactory; @@ -45,7 +45,8 @@ public Lifecycle newInstance(ExtensionContext context, Dependencies dependencies } static Username createUsername(Context context) { - var username = Neo4jProxy.username(context.securityContext().subject()); + AuthSubject subject = context.securityContext().subject(); + var username = subject.executingUser(); return new Username(username); } diff --git a/core/src/test/java/org/neo4j/gds/core/UsernameTest.java b/core/src/test/java/org/neo4j/gds/core/UsernameTest.java index fe7768d23cc..b171b244329 100644 --- a/core/src/test/java/org/neo4j/gds/core/UsernameTest.java +++ b/core/src/test/java/org/neo4j/gds/core/UsernameTest.java @@ -20,7 +20,6 @@ package org.neo4j.gds.core; import org.junit.jupiter.api.Test; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.internal.kernel.api.security.AuthSubject; import static org.assertj.core.api.Assertions.assertThat; @@ -29,6 +28,6 @@ class UsernameTest { @Test void emptyUsernameShouldAgreeWithAnonymousAuthSubject() { - assertThat(Username.EMPTY_USERNAME.username()).isEqualTo(Neo4jProxy.username(AuthSubject.ANONYMOUS)); + assertThat(Username.EMPTY_USERNAME.username()).isEqualTo(AuthSubject.ANONYMOUS.executingUser()); } } diff --git a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/AlphaCypherAggregation.java b/cypher-aggregation/src/main/java/org/neo4j/gds/projection/AlphaCypherAggregation.java index 247c69d97e4..5cb352d6ac3 100644 --- a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/AlphaCypherAggregation.java +++ b/cypher-aggregation/src/main/java/org/neo4j/gds/projection/AlphaCypherAggregation.java @@ -33,6 +33,8 @@ import org.neo4j.internal.kernel.api.procs.UserFunctionSignature; import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction; import org.neo4j.kernel.api.procedure.Context; +import org.neo4j.kernel.database.DatabaseReferenceImpl; +import org.neo4j.kernel.database.DatabaseReferenceRepository; import org.neo4j.kernel.impl.api.KernelTransactions; import org.neo4j.procedure.Name; import org.neo4j.values.AnyValue; @@ -91,7 +93,12 @@ public UserAggregationReducer createReducer(Context ctx) throws ProcedureExcepti var ktxs = GraphDatabaseApiProxy.resolveDependency(databaseService, KernelTransactions.class); var queryProvider = ExecutingQueryProvider.fromTransaction(ktxs, transaction); - var runsOnCompositeDatabase = Neo4jProxy.isCompositeDatabase(databaseService); + var databaseId = GraphDatabaseApiProxy.databaseId(databaseService); + var repo = GraphDatabaseApiProxy.resolveDependency(databaseService, DatabaseReferenceRepository.class); + var runsOnCompositeDatabase = repo.getCompositeDatabaseReferences() + .stream() + .map(DatabaseReferenceImpl.Internal::databaseId) + .anyMatch(databaseId::equals); var writeMode = runsOnCompositeDatabase ? WriteMode.NONE : WriteMode.LOCAL; diff --git a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/CypherAggregation.java b/cypher-aggregation/src/main/java/org/neo4j/gds/projection/CypherAggregation.java index 21dd7a5df70..18e6926d6e1 100644 --- a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/CypherAggregation.java +++ b/cypher-aggregation/src/main/java/org/neo4j/gds/projection/CypherAggregation.java @@ -35,6 +35,8 @@ import org.neo4j.internal.kernel.api.procs.UserFunctionSignature; import org.neo4j.kernel.api.procedure.CallableUserAggregationFunction; import org.neo4j.kernel.api.procedure.Context; +import org.neo4j.kernel.database.DatabaseReferenceImpl; +import org.neo4j.kernel.database.DatabaseReferenceRepository; import org.neo4j.kernel.impl.api.KernelTransactions; import org.neo4j.logging.Log; import org.neo4j.procedure.Name; @@ -90,7 +92,12 @@ public UserAggregationReducer createReducer(Context ctx) throws ProcedureExcepti var username = ctx.kernelTransaction().securityContext().subject().executingUser(); var transaction = ctx.transaction(); - var runsOnCompositeDatabase = Neo4jProxy.isCompositeDatabase(databaseService); + var databaseId = GraphDatabaseApiProxy.databaseId(databaseService); + var repo = GraphDatabaseApiProxy.resolveDependency(databaseService, DatabaseReferenceRepository.class); + var runsOnCompositeDatabase = repo.getCompositeDatabaseReferences() + .stream() + .map(DatabaseReferenceImpl.Internal::databaseId) + .anyMatch(databaseId::equals); ExecutingQueryProvider queryProvider; if (runsOnCompositeDatabase) { diff --git a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/CypherAggregationExtension.java b/cypher-aggregation/src/main/java/org/neo4j/gds/projection/CypherAggregationExtension.java index a1ddcd6597d..63ac26c3493 100644 --- a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/CypherAggregationExtension.java +++ b/cypher-aggregation/src/main/java/org/neo4j/gds/projection/CypherAggregationExtension.java @@ -20,7 +20,6 @@ package org.neo4j.gds.projection; import org.neo4j.annotations.service.ServiceProvider; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.internal.kernel.api.exceptions.ProcedureException; import org.neo4j.kernel.api.procedure.GlobalProcedures; import org.neo4j.kernel.extension.ExtensionFactory; @@ -28,6 +27,7 @@ import org.neo4j.kernel.extension.context.ExtensionContext; import org.neo4j.kernel.lifecycle.Lifecycle; import org.neo4j.kernel.lifecycle.LifecycleAdapter; +import org.neo4j.logging.Log; import org.neo4j.logging.internal.LogService; import static org.neo4j.gds.utils.StringFormatting.formatWithLocale; @@ -48,7 +48,8 @@ public Lifecycle newInstance(ExtensionContext context, CypherAggregationExtensio registry.register(new CypherAggregation()); registry.register(new AlphaCypherAggregation()); } catch (ProcedureException e) { - var log = Neo4jProxy.getInternalLog(dependencies.logService(), getClass()); + LogService logService = dependencies.logService(); + var log = (Log) logService.getInternalLog(getClass()); log.warn(formatWithLocale("`%s` is not available", CypherAggregation.FUNCTION_NAME), e); } }); diff --git a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/ExecutingQueryProvider.java b/cypher-aggregation/src/main/java/org/neo4j/gds/projection/ExecutingQueryProvider.java index cc980dd3445..f0a547631c6 100644 --- a/cypher-aggregation/src/main/java/org/neo4j/gds/projection/ExecutingQueryProvider.java +++ b/cypher-aggregation/src/main/java/org/neo4j/gds/projection/ExecutingQueryProvider.java @@ -19,8 +19,8 @@ */ package org.neo4j.gds.projection; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.graphdb.Transaction; +import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.impl.api.KernelTransactions; import org.neo4j.kernel.impl.coreapi.InternalTransaction; @@ -54,9 +54,10 @@ public Optional executingQuery() { return Optional.empty(); } - var txId = Neo4jProxy.transactionId(((InternalTransaction) this.transaction).kernelTransaction()); + KernelTransaction kernelTransaction = ((InternalTransaction) this.transaction).kernelTransaction(); + var txId = kernelTransaction.getTransactionSequenceNumber(); return this.ktxs.activeTransactions().stream() - .filter(handle -> Neo4jProxy.transactionId(handle) == txId) + .filter(handle -> handle.getTransactionSequenceNumber() == txId) .flatMap(handle -> handle.executingQuery().stream()) .flatMap(eq -> eq.snapshot() .obfuscatedQueryText() diff --git a/executor/src/integrationTest/java/org/neo4j/gds/executor/ProcedureExecutorTest.java b/executor/src/integrationTest/java/org/neo4j/gds/executor/ProcedureExecutorTest.java index 18f0c7a0e8e..16a716ab92a 100644 --- a/executor/src/integrationTest/java/org/neo4j/gds/executor/ProcedureExecutorTest.java +++ b/executor/src/integrationTest/java/org/neo4j/gds/executor/ProcedureExecutorTest.java @@ -22,11 +22,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.neo4j.common.DependencyResolver; import org.neo4j.gds.api.CloseableResourceRegistry; import org.neo4j.gds.api.GraphStore; import org.neo4j.gds.api.NodeLookup; import org.neo4j.gds.api.ProcedureReturnColumns; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.config.GraphProjectConfig; import org.neo4j.gds.core.loading.GraphStoreCatalog; import org.neo4j.gds.core.model.ModelCatalog; @@ -138,7 +138,17 @@ private ExecutionContext executionContext(TaskStore taskStore) { .username("") .terminationMonitor(TerminationMonitor.EMPTY) .isGdsAdmin(true) - .dependencyResolver(Neo4jProxy.emptyDependencyResolver()) + .dependencyResolver(new DependencyResolver() { + @Override + public T resolveDependency(Class type, SelectionStrategy selector) { + return null; + } + + @Override + public boolean containsDependency(Class type) { + return false; + } + }) .modelCatalog(ModelCatalog.EMPTY) .closeableResourceRegistry(CloseableResourceRegistry.EMPTY) .nodeLookup(NodeLookup.EMPTY) diff --git a/executor/src/main/java/org/neo4j/gds/executor/ExecutionContext.java b/executor/src/main/java/org/neo4j/gds/executor/ExecutionContext.java index 07d9759d062..be8b75b000b 100644 --- a/executor/src/main/java/org/neo4j/gds/executor/ExecutionContext.java +++ b/executor/src/main/java/org/neo4j/gds/executor/ExecutionContext.java @@ -26,7 +26,6 @@ import org.neo4j.gds.api.DatabaseId; import org.neo4j.gds.api.NodeLookup; import org.neo4j.gds.api.ProcedureReturnColumns; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.model.ModelCatalog; import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory; import org.neo4j.gds.core.utils.progress.TaskRegistryFactory; @@ -111,7 +110,17 @@ public DatabaseId databaseId() { @Override public DependencyResolver dependencyResolver() { - return Neo4jProxy.emptyDependencyResolver(); + return new DependencyResolver() { + @Override + public T resolveDependency(Class type, SelectionStrategy selector) { + return null; + } + + @Override + public boolean containsDependency(Class type) { + return false; + } + }; } @Override diff --git a/io/core/src/main/java/org/neo4j/gds/core/io/db/GdsParallelBatchImporter.java b/io/core/src/main/java/org/neo4j/gds/core/io/db/GdsParallelBatchImporter.java index 3432899f985..4f2a63f4ef8 100644 --- a/io/core/src/main/java/org/neo4j/gds/core/io/db/GdsParallelBatchImporter.java +++ b/io/core/src/main/java/org/neo4j/gds/core/io/db/GdsParallelBatchImporter.java @@ -36,15 +36,18 @@ import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.io.layout.DatabaseLayout; +import org.neo4j.io.layout.Neo4jLayout; import org.neo4j.kernel.impl.scheduler.JobSchedulerFactory; import org.neo4j.kernel.lifecycle.LifeSupport; import org.neo4j.logging.internal.LogService; import org.neo4j.logging.internal.NullLogService; import org.neo4j.scheduler.JobScheduler; +import org.neo4j.storageengine.api.StorageEngineFactory; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; +import java.util.Locale; import static org.neo4j.configuration.GraphDatabaseSettings.SYSTEM_DATABASE_NAME; import static org.neo4j.gds.core.io.GraphStoreExporter.DIRECTORY_IS_WRITABLE; @@ -114,7 +117,8 @@ private GdsParallelBatchImporter( .set(Neo4jSettings.neo4jHome(), databaseConfig.get(Neo4jSettings.neo4jHome())) .set(GraphDatabaseSettings.data_directory, databaseConfig.get(GraphDatabaseSettings.data_directory)); - Neo4jProxy.configureDatabaseFormat(configBuilder, config.databaseFormat()); + var databaseRecordFormat = config.databaseFormat().toLowerCase(Locale.ENGLISH); + configBuilder.set(GraphDatabaseSettings.db_format, databaseRecordFormat); this.databaseConfig = configBuilder.build(); } @@ -124,13 +128,19 @@ public void writeDatabase(Input input, boolean startDatabase) { var importTimer = ProgressTimer.start(); - Neo4jProxy.allAvailableDatabaseLayouts(databaseConfig, config.databaseName()) + String databaseName1 = config.databaseName(); + var neo4jLayout1 = Neo4jLayout.of(databaseConfig); + StorageEngineFactory.allAvailableStorageEngines().stream() + .map(engine -> engine.databaseLayout(neo4jLayout1, databaseName1)) .forEach(databaseLayout -> { validateWritableDirectories(databaseLayout); validateDatabaseDoesNotExist(databaseLayout); }); - var databaseLayout = Neo4jProxy.databaseLayout(databaseConfig, config.databaseName()); + String databaseName = config.databaseName(); + var neo4jLayout = Neo4jLayout.of(databaseConfig); + var storageEngineFactory = StorageEngineFactory.selectStorageEngine(databaseConfig); + var databaseLayout = storageEngineFactory.databaseLayout(neo4jLayout, databaseName); var lifeSupport = new LifeSupport(); diff --git a/io/core/src/main/java/org/neo4j/gds/core/io/db/GraphStoreToDatabaseExporterConfig.java b/io/core/src/main/java/org/neo4j/gds/core/io/db/GraphStoreToDatabaseExporterConfig.java index 41b249243be..917cc7a9c8e 100644 --- a/io/core/src/main/java/org/neo4j/gds/core/io/db/GraphStoreToDatabaseExporterConfig.java +++ b/io/core/src/main/java/org/neo4j/gds/core/io/db/GraphStoreToDatabaseExporterConfig.java @@ -19,11 +19,13 @@ */ package org.neo4j.gds.core.io.db; +import org.neo4j.configuration.helpers.DatabaseNameValidator; import org.neo4j.gds.annotation.Configuration; import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.config.JobIdConfig; import org.neo4j.gds.core.CypherMapWrapper; import org.neo4j.gds.core.io.GraphStoreExporterBaseConfig; +import org.neo4j.kernel.database.NormalizedDatabaseName; @Configuration public interface GraphStoreToDatabaseExporterConfig extends GraphStoreExporterBaseConfig, JobIdConfig { @@ -46,12 +48,15 @@ default String databaseFormat() { @Configuration.Check default void validate() { - Neo4jProxy.validateExternalDatabaseName(databaseName()); + var normalizedName = new NormalizedDatabaseName(databaseName()); + DatabaseNameValidator.validateExternalDatabaseName(normalizedName); } static GraphStoreToDatabaseExporterConfig of(CypherMapWrapper config) { var normalizedConfig = config.getString(DB_NAME_KEY).map(dbName -> { - var databaseName = Neo4jProxy.validateExternalDatabaseName(dbName); + var normalizedName = new NormalizedDatabaseName(dbName); + DatabaseNameValidator.validateExternalDatabaseName(normalizedName); + var databaseName = normalizedName.name(); return config.withString(DB_NAME_KEY, databaseName); }).orElse(config); return new GraphStoreToDatabaseExporterConfigImpl(normalizedConfig); diff --git a/legacy-cypher-projection/src/main/java/org/neo4j/gds/legacycypherprojection/CypherRecordLoader.java b/legacy-cypher-projection/src/main/java/org/neo4j/gds/legacycypherprojection/CypherRecordLoader.java index d207c9dd43c..a1d3a92049b 100644 --- a/legacy-cypher-projection/src/main/java/org/neo4j/gds/legacycypherprojection/CypherRecordLoader.java +++ b/legacy-cypher-projection/src/main/java/org/neo4j/gds/legacycypherprojection/CypherRecordLoader.java @@ -28,6 +28,7 @@ import org.neo4j.kernel.impl.coreapi.InternalTransaction; import org.neo4j.kernel.impl.query.Neo4jTransactionalContextFactory; import org.neo4j.kernel.impl.query.QueryExecution; +import org.neo4j.kernel.impl.query.QueryExecutionConfiguration; import org.neo4j.kernel.impl.query.QueryExecutionEngine; import org.neo4j.kernel.impl.query.QuerySubscriber; import org.neo4j.kernel.impl.query.TransactionalContextFactory; @@ -158,7 +159,7 @@ private static QueryExecution runQueryWithoutClosingTheResult( QuerySubscriber subscriber ) { var convertedParams = ValueUtils.asMapValue(params); - var context = Neo4jProxy.newQueryContext(contextFactory, tx, query, convertedParams); + var context = contextFactory.newContext(tx, query, convertedParams, QueryExecutionConfiguration.DEFAULT_CONFIG); try { return executionEngine.executeQuery(query, convertedParams, context, false, subscriber); } catch (Exception e) { diff --git a/native-projection/src/integrationTest/java/org/neo4j/gds/projection/NodeLabelIndexScanTest.java b/native-projection/src/integrationTest/java/org/neo4j/gds/projection/NodeLabelIndexScanTest.java index 2584f88aa15..aed9839d59d 100644 --- a/native-projection/src/integrationTest/java/org/neo4j/gds/projection/NodeLabelIndexScanTest.java +++ b/native-projection/src/integrationTest/java/org/neo4j/gds/projection/NodeLabelIndexScanTest.java @@ -24,9 +24,14 @@ import org.junit.jupiter.params.provider.ValueSource; import org.neo4j.gds.BaseTest; import org.neo4j.gds.TestSupport; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.CompatExecutionContext; +import org.neo4j.gds.compat.PartitionedStoreScan; import org.neo4j.gds.core.loading.RecordsBatchBuffer; import org.neo4j.graphdb.Label; +import org.neo4j.internal.kernel.api.Cursor; +import org.neo4j.internal.kernel.api.PartitionedScan; +import org.neo4j.kernel.api.ExecutionContext; +import org.neo4j.kernel.api.Statement; import java.util.HashSet; import java.util.Random; @@ -70,24 +75,40 @@ void nodeLabelIndexScanTest(String label) { tx.accept((tx1, ktx) -> { var aToken = ktx.tokenRead().nodeLabel(label); - var storeScan = Neo4jProxy.nodeLabelIndexScan( - ktx, - aToken, - RecordsBatchBuffer.DEFAULT_BUFFER_SIZE - ); + var storeScan = PartitionedStoreScan.createScans(ktx, RecordsBatchBuffer.DEFAULT_BUFFER_SIZE, aToken) + .get(0); try ( - var cursor = Neo4jProxy.allocateNodeLabelIndexCursor(ktx); - var ctx = Neo4jProxy.executionContext(ktx) + var cursor = ktx.cursors().allocateNodeLabelIndexCursor(ktx.cursorContext()) ) { - var aNodesCount = 0; - while (storeScan.reserveBatch(cursor, ctx)) { - while (cursor.next()) { - assertThat(expectedSet.contains(cursor.nodeReference())).isTrue(); - aNodesCount += 1; + try ( + var ctx = new CompatExecutionContext() { + + private final Statement stmt = ktx.acquireStatement(); + private final ExecutionContext ctx1 = ktx.createExecutionContext(); + + @Override + public boolean reservePartition(PartitionedScan scan, C cursor1) { + return scan.reservePartition(cursor1, ctx1); + } + + @Override + public void close() { + ctx1.complete(); + ctx1.close(); + stmt.close(); + } + } + ) { + var aNodesCount = 0; + while (storeScan.reserveBatch(cursor, ctx)) { + while (cursor.next()) { + assertThat(expectedSet.contains(cursor.nodeReference())).isTrue(); + aNodesCount += 1; + } } + assertThat(aNodesCount).isEqualTo(expectedSet.size()); } - assertThat(aNodesCount).isEqualTo(expectedSet.size()); } }); } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/AbstractCursorBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/AbstractCursorBasedScanner.java index 1f409145a30..144dfcf49f4 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/AbstractCursorBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/AbstractCursorBasedScanner.java @@ -20,12 +20,14 @@ package org.neo4j.gds.projection; import org.neo4j.gds.compat.CompatExecutionContext; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.StoreScan; import org.neo4j.gds.mem.BitUtil; import org.neo4j.gds.transaction.TransactionContext; import org.neo4j.internal.kernel.api.Cursor; +import org.neo4j.internal.kernel.api.PartitionedScan; +import org.neo4j.kernel.api.ExecutionContext; import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.api.Statement; abstract class AbstractCursorBasedScanner implements StoreScanner { @@ -46,7 +48,23 @@ private final class ScanCursor implements StoreScanner.ScanCursor { this.cursor = cursor; this.cursorReference = reference; this.scan = entityCursorScan; - this.executionContext = Neo4jProxy.executionContext(ktx); + this.executionContext = new CompatExecutionContext() { + + private final Statement stmt = ktx.acquireStatement(); + private final ExecutionContext ctx = ktx.createExecutionContext(); + + @Override + public boolean reservePartition(PartitionedScan scan1, C cursor1) { + return scan1.reservePartition(cursor1, ctx); + } + + @Override + public void close() { + ctx.complete(); + ctx.close(); + stmt.close(); + } + }; } @Override diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/BufferedNodeConsumer.java b/native-projection/src/main/java/org/neo4j/gds/projection/BufferedNodeConsumer.java index 45c73d67182..9a092e03a44 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/BufferedNodeConsumer.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/BufferedNodeConsumer.java @@ -22,10 +22,10 @@ import com.carrotsearch.hppc.LongHashSet; import com.carrotsearch.hppc.LongSet; import org.immutables.builder.Builder; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.loading.NodeLabelTokenSet; import org.neo4j.gds.core.loading.NodesBatchBuffer; import org.neo4j.gds.core.loading.NodesBatchBufferBuilder; +import org.neo4j.storageengine.api.LongReference; import org.neo4j.storageengine.api.Reference; import org.neo4j.token.api.TokenConstants; @@ -92,9 +92,8 @@ public boolean offer(NodeReference record) { } if (this.nodeLabelIds.isEmpty()) { - var propertiesReference = this.readProperty - ? record.propertiesReference() - : Neo4jProxy.noPropertyReference(); + Reference propertiesReference; + propertiesReference = this.readProperty ? record.propertiesReference() : LongReference.NULL_REFERENCE; this.buffer.add(record.nodeId(), propertiesReference, NodeLabelTokenSet.ANY_LABEL); } else { boolean atLeastOneLabelFound = false; @@ -108,9 +107,8 @@ public boolean offer(NodeReference record) { } } if (atLeastOneLabelFound) { - var propertiesReference = this.readProperty - ? record.propertiesReference() - : Neo4jProxy.noPropertyReference(); + Reference propertiesReference; + propertiesReference = this.readProperty ? record.propertiesReference() : LongReference.NULL_REFERENCE; this.buffer.add(record.nodeId(), propertiesReference, labels); } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/GraphDimensionsReader.java b/native-projection/src/main/java/org/neo4j/gds/projection/GraphDimensionsReader.java index 2ae65f106bf..5d46d2c40a6 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/GraphDimensionsReader.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/GraphDimensionsReader.java @@ -99,7 +99,7 @@ public GraphDimensions apply(KernelTransaction transaction) throws RuntimeExcept Map relationshipPropertyTokens = loadPropertyTokens(getRelationshipProjections().projections(), tokenRead); long nodeCount = labelTokenNodeLabelMappings.keyStream() - .mapToLong(label -> Neo4jProxy.estimateNodeCount(dataRead, label)) + .mapToLong(label -> dataRead.estimateCountsForNode(label)) .sum(); final long allNodesCount = Neo4jProxy.getHighestPossibleNodeCount(idGeneratorFactory); long finalNodeCount = labelTokenNodeLabelMappings.keys().contains(ANY_LABEL) @@ -113,7 +113,7 @@ public GraphDimensions apply(KernelTransaction transaction) throws RuntimeExcept typeTokenRelTypeMappings ); long relCountUpperBound = relationshipCounts.values().stream().mapToLong(Long::longValue).sum(); - long allRelationshipsCount = Neo4jProxy.getHighestPossibleRelationshipCount(dataRead); + long allRelationshipsCount = dataRead.relationshipsGetCount(); return ImmutableGraphDimensions.builder() .nodeCount(finalNodeCount) @@ -205,8 +205,8 @@ protected Map getRelationshipCountsByType( private static long relCountUpperBoundForLabelAndType(Read dataRead, int labelId, int id) { return Math.max( - Neo4jProxy.estimateRelationshipCount(dataRead, labelId, ANY_LABEL, id), - Neo4jProxy.estimateRelationshipCount(dataRead, ANY_LABEL, labelId, id) + dataRead.estimateCountsForRelationships(labelId, id, ANY_LABEL), + dataRead.estimateCountsForRelationships(ANY_LABEL, id, labelId) ); } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java index 8597800c454..47d983df037 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java @@ -47,7 +47,7 @@ final class MultipleNodeLabelIndexBasedScanner extends AbstractNodeCursorBasedSc CompositeNodeCursor entityCursor(KernelTransaction transaction) { List cursors = Arrays .stream(labelIds) - .mapToObj(i -> Neo4jProxy.allocateNodeLabelIndexCursor(transaction)) + .mapToObj(i -> transaction.cursors().allocateNodeLabelIndexCursor(transaction.cursorContext())) .collect(Collectors.toList()); return Neo4jProxy.compositeNodeCursor(cursors, labelIds); } @@ -66,7 +66,7 @@ NodeReference cursorReference(KernelTransaction transaction, CompositeNodeCursor return new MultipleNodeLabelIndexReference( cursor, transaction.dataRead(), - Neo4jProxy.allocateNodeCursor(transaction) + transaction.cursors().allocateNodeCursor(transaction.cursorContext()) ); } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexReference.java b/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexReference.java index 7ebbbed3b42..e4fdbf92abe 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexReference.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexReference.java @@ -20,7 +20,6 @@ package org.neo4j.gds.projection; import org.neo4j.gds.compat.CompositeNodeCursor; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.loading.NodeLabelTokenSet; import org.neo4j.internal.kernel.api.NodeCursor; import org.neo4j.internal.kernel.api.Read; @@ -67,9 +66,9 @@ public long relationshipReference() { public Reference propertiesReference() { dataRead.singleNode(compositeNodeCursor.nodeReference(), nodeCursor); if (nodeCursor.next()) { - return Neo4jProxy.propertyReference(nodeCursor); + return nodeCursor.propertiesReference(); } else { - return Neo4jProxy.noPropertyReference(); + return LongReference.NULL_REFERENCE; } } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/NativeNodePropertyImporter.java b/native-projection/src/main/java/org/neo4j/gds/projection/NativeNodePropertyImporter.java index dc7f5be7ffd..0b2a65ed095 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/NativeNodePropertyImporter.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/NativeNodePropertyImporter.java @@ -29,7 +29,6 @@ import org.neo4j.gds.PropertyMappings; import org.neo4j.gds.api.IdMap; import org.neo4j.gds.api.properties.nodes.NodePropertyValues; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.config.ConcurrencyConfig; import org.neo4j.gds.core.GraphDimensions; import org.neo4j.gds.core.concurrency.Concurrency; @@ -37,6 +36,7 @@ import org.neo4j.gds.core.loading.nodeproperties.NodePropertiesFromStoreBuilder; import org.neo4j.internal.kernel.api.PropertyCursor; import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.storageengine.api.PropertySelection; import org.neo4j.storageengine.api.Reference; import org.neo4j.values.storable.Value; @@ -76,8 +76,14 @@ public int importProperties( Reference propertiesReference, KernelTransaction kernelTransaction ) { - try (PropertyCursor pc = Neo4jProxy.allocatePropertyCursor(kernelTransaction)) { - Neo4jProxy.nodeProperties(kernelTransaction, neoNodeId, propertiesReference, pc); + try ( + PropertyCursor pc = kernelTransaction + .cursors() + .allocatePropertyCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker()) + ) { + kernelTransaction + .dataRead() + .nodeProperties(neoNodeId, propertiesReference, PropertySelection.ALL_PROPERTIES, pc); int nodePropertiesRead = 0; while (pc.next()) { nodePropertiesRead += importProperty(neoNodeId, labelTokens, pc); diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java index 4c9b747cfa6..c1790843ca3 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java @@ -36,7 +36,7 @@ final class NodeCursorBasedScanner extends AbstractNodeCursorBasedScanner entityCursorScan(KernelTransaction transaction) { - return Neo4jProxy.nodeLabelIndexScan( - transaction, - labelId, - batchSize() - ); + int batchSize = batchSize(); + return PartitionedStoreScan.createScans(transaction, batchSize, labelId).get(0); } @Override @@ -57,7 +54,7 @@ NodeReference cursorReference(KernelTransaction transaction, NodeLabelIndexCurso return new NodeLabelIndexReference( cursor, transaction.dataRead(), - Neo4jProxy.allocateNodeCursor(transaction), + transaction.cursors().allocateNodeCursor(transaction.cursorContext()), labelId ); } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/NodeLabelIndexReference.java b/native-projection/src/main/java/org/neo4j/gds/projection/NodeLabelIndexReference.java index 72ecdb59045..f673645012f 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/NodeLabelIndexReference.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/NodeLabelIndexReference.java @@ -19,7 +19,6 @@ */ package org.neo4j.gds.projection; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.loading.NodeLabelTokenSet; import org.neo4j.internal.kernel.api.NodeCursor; import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; @@ -65,9 +64,9 @@ public long relationshipReference() { public Reference propertiesReference() { dataRead.singleNode(labelIndexCursor.nodeReference(), nodeCursor); if (nodeCursor.next()) { - return Neo4jProxy.propertyReference(nodeCursor); + return nodeCursor.propertiesReference(); } else { - return Neo4jProxy.noPropertyReference(); + return LongReference.NULL_REFERENCE; } } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/PartitionedStoreScan.java b/native-projection/src/main/java/org/neo4j/gds/projection/PartitionedStoreScan.java index e7ea7ec654e..ef5b457db59 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/PartitionedStoreScan.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/PartitionedStoreScan.java @@ -22,7 +22,6 @@ import org.neo4j.common.EntityType; import org.neo4j.exceptions.KernelException; import org.neo4j.gds.compat.CompatExecutionContext; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.StoreScan; import org.neo4j.internal.kernel.api.Cursor; import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; @@ -67,11 +66,11 @@ public static List> createScans( // and use that one as the driving partitioned index scan. The partitions // of all other partitioned index scans will be aligned to that one. int maxToken = labelIds[0]; - long maxCount = Neo4jProxy.estimateNodeCount(read, labelIds[0]); + long maxCount = read.estimateCountsForNode(labelIds[0]); int maxIndex = 0; for (int i = 1; i < labelIds.length; i++) { - long count = Neo4jProxy.estimateNodeCount(read, labelIds[i]); + long count = read.estimateCountsForNode(labelIds[i]); if (count > maxCount) { maxCount = count; maxToken = labelIds[i]; diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java index fd4419552b4..cb5d9018f3d 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java @@ -63,7 +63,7 @@ public long storeSize(GraphDimensions graphDimensions) { @Override RelationshipScanCursor entityCursor(KernelTransaction transaction) { - return Neo4jProxy.allocateRelationshipScanCursor(transaction); + return transaction.cursors().allocateRelationshipScanCursor(transaction.cursorContext()); } @Override diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorReference.java b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorReference.java index 46f2860058c..b3c6a087c91 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorReference.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorReference.java @@ -19,7 +19,6 @@ */ package org.neo4j.gds.projection; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.internal.kernel.api.RelationshipScanCursor; import org.neo4j.storageengine.api.Reference; @@ -53,6 +52,6 @@ public long targetNodeReference() { @Override public Reference propertiesReference() { - return Neo4jProxy.propertyReference(relationshipScanCursor); + return relationshipScanCursor.propertiesReference(); } } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipsScannerTask.java b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipsScannerTask.java index a3808091f63..20981851fe3 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipsScannerTask.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipsScannerTask.java @@ -23,7 +23,6 @@ import org.jetbrains.annotations.NotNull; import org.neo4j.gds.api.GraphLoaderContext; import org.neo4j.gds.api.IdMap; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.loading.AdjacencyBuffer; import org.neo4j.gds.core.loading.PropertyReader; import org.neo4j.gds.core.loading.RecordScannerTask; @@ -214,7 +213,11 @@ private static PropertyReader storeBackedPropertyReader(KernelTransac long[][] properties = new long[propertyKeyIds.length][producer.numberOfElements()]; if (atLeastOnePropertyToLoad) { var read = kernelTransaction.dataRead(); - try (PropertyCursor pc = Neo4jProxy.allocatePropertyCursor(kernelTransaction)) { + try ( + PropertyCursor pc = kernelTransaction + .cursors() + .allocatePropertyCursor(kernelTransaction.cursorContext(), kernelTransaction.memoryTracker()) + ) { double[] relProps = new double[propertyKeyIds.length]; producer.forEach((index, source, target, relationshipReference, propertyReference) -> { read.relationshipProperties( diff --git a/native-projection/src/test/java/org/neo4j/gds/projection/BufferedNodeConsumerTest.java b/native-projection/src/test/java/org/neo4j/gds/projection/BufferedNodeConsumerTest.java index 3632b26bc08..6418a62a513 100644 --- a/native-projection/src/test/java/org/neo4j/gds/projection/BufferedNodeConsumerTest.java +++ b/native-projection/src/test/java/org/neo4j/gds/projection/BufferedNodeConsumerTest.java @@ -21,8 +21,8 @@ import com.carrotsearch.hppc.LongHashSet; import org.junit.jupiter.api.Test; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.loading.NodeLabelTokenSet; +import org.neo4j.storageengine.api.LongReference; import org.neo4j.storageengine.api.Reference; import static org.assertj.core.api.Assertions.assertThat; @@ -116,7 +116,7 @@ public long relationshipReference() { @Override public Reference propertiesReference() { - return Neo4jProxy.noPropertyReference(); + return LongReference.NULL_REFERENCE; } } } diff --git a/native-projection/src/test/java/org/neo4j/gds/projection/BufferedRelationshipConsumerTest.java b/native-projection/src/test/java/org/neo4j/gds/projection/BufferedRelationshipConsumerTest.java index 4f43867fd6a..c622ebd8df2 100644 --- a/native-projection/src/test/java/org/neo4j/gds/projection/BufferedRelationshipConsumerTest.java +++ b/native-projection/src/test/java/org/neo4j/gds/projection/BufferedRelationshipConsumerTest.java @@ -21,8 +21,8 @@ import org.junit.jupiter.api.Test; import org.neo4j.gds.annotation.ValueClass; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.huge.DirectIdMap; +import org.neo4j.storageengine.api.LongReference; import org.neo4j.storageengine.api.Reference; import static org.assertj.core.api.Assertions.assertThat; @@ -38,7 +38,7 @@ void flushBufferWhenFull() { .capacity(1) .build(); - buffer.relationshipsBatchBuffer().add(0, 1, -1, Neo4jProxy.noPropertyReference()); + buffer.relationshipsBatchBuffer().add(0, 1, -1, LongReference.NULL_REFERENCE); assertTrue(buffer.relationshipsBatchBuffer().isFull()); } @@ -68,7 +68,7 @@ public interface TestRelationship extends RelationshipReference { @Override default Reference propertiesReference() { - return Neo4jProxy.noPropertyReference(); + return LongReference.NULL_REFERENCE; } } diff --git a/neo4j-settings/src/main/java/org/neo4j/gds/settings/Neo4jSettings.java b/neo4j-settings/src/main/java/org/neo4j/gds/settings/Neo4jSettings.java index 9306b275ae9..a88f72788d0 100644 --- a/neo4j-settings/src/main/java/org/neo4j/gds/settings/Neo4jSettings.java +++ b/neo4j-settings/src/main/java/org/neo4j/gds/settings/Neo4jSettings.java @@ -19,12 +19,13 @@ */ package org.neo4j.gds.settings; +import org.neo4j.configuration.BootloaderSettings; import org.neo4j.configuration.GraphDatabaseSettings; +import org.neo4j.configuration.SettingValueParsers; import org.neo4j.configuration.connectors.BoltConnector; import org.neo4j.configuration.connectors.HttpConnector; import org.neo4j.configuration.connectors.HttpsConnector; import org.neo4j.configuration.helpers.SocketAddress; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.SettingsUtil; import org.neo4j.graphdb.config.Setting; @@ -71,11 +72,11 @@ public static Setting neo4jHome() { } public static Setting pageCacheMemory() { - return Neo4jProxy.pageCacheMemory(); + return GraphDatabaseSettings.pagecache_memory; } public static Long pageCacheMemoryValue(String value) { - return Neo4jProxy.pageCacheMemoryValue(value); + return SettingValueParsers.BYTES.parse(value); } public static Setting transactionStateAllocation() { @@ -123,7 +124,7 @@ public static Setting failOnMissingFiles() { } public static Setting additionalJvm() { - return Neo4jProxy.additionalJvm(); + return BootloaderSettings.additional_jvm; } private Neo4jSettings() { diff --git a/pipeline/src/test/java/org/neo4j/gds/ml/pipeline/linkPipeline/LinkPredictionTrainingPipelineTest.java b/pipeline/src/test/java/org/neo4j/gds/ml/pipeline/linkPipeline/LinkPredictionTrainingPipelineTest.java index a82db5a344d..786ce09150f 100644 --- a/pipeline/src/test/java/org/neo4j/gds/ml/pipeline/linkPipeline/LinkPredictionTrainingPipelineTest.java +++ b/pipeline/src/test/java/org/neo4j/gds/ml/pipeline/linkPipeline/LinkPredictionTrainingPipelineTest.java @@ -22,6 +22,7 @@ import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.neo4j.common.DependencyResolver; import org.neo4j.gds.NodeLabel; import org.neo4j.gds.RelationshipType; import org.neo4j.gds.api.CloseableResourceRegistry; @@ -29,7 +30,6 @@ import org.neo4j.gds.api.NodeLookup; import org.neo4j.gds.api.ProcedureReturnColumns; import org.neo4j.gds.api.schema.GraphSchema; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.concurrency.Concurrency; import org.neo4j.gds.core.model.Model; import org.neo4j.gds.core.model.ModelCatalog; @@ -185,7 +185,17 @@ void overridesTheSplitConfig() { void deriveRelationshipWeightProperty() { var executionContext = ImmutableExecutionContext.builder() .databaseId(DatabaseId.of("")) - .dependencyResolver(Neo4jProxy.emptyDependencyResolver()) + .dependencyResolver(new DependencyResolver() { + @Override + public T resolveDependency(Class type, SelectionStrategy selector) { + return null; + } + + @Override + public boolean containsDependency(Class type) { + return false; + } + }) .username("") .terminationMonitor(TerminationMonitor.EMPTY) .closeableResourceRegistry(CloseableResourceRegistry.EMPTY) @@ -229,7 +239,17 @@ void deriveRelationshipWeightPropertyFromTrainedModel() { var executionContext = ImmutableExecutionContext.builder() .databaseId(DatabaseId.of("")) - .dependencyResolver(Neo4jProxy.emptyDependencyResolver()) + .dependencyResolver(new DependencyResolver() { + @Override + public T resolveDependency(Class type, SelectionStrategy selector) { + return null; + } + + @Override + public boolean containsDependency(Class type) { + return false; + } + }) .username("") .modelCatalog(modelCatalog) .terminationMonitor(TerminationMonitor.EMPTY) @@ -273,7 +293,17 @@ void notDerivePropertyFromUnweightedTrainedModel() { var executionContext = ImmutableExecutionContext.builder() .databaseId(DatabaseId.of("")) - .dependencyResolver(Neo4jProxy.emptyDependencyResolver()) + .dependencyResolver(new DependencyResolver() { + @Override + public T resolveDependency(Class type, SelectionStrategy selector) { + return null; + } + + @Override + public boolean containsDependency(Class type) { + return false; + } + }) .username("") .modelCatalog(modelCatalog) .terminationMonitor(TerminationMonitor.EMPTY) diff --git a/proc/common/src/test/java/org/neo4j/gds/WriteNodePropertiesComputationResultConsumerTest.java b/proc/common/src/test/java/org/neo4j/gds/WriteNodePropertiesComputationResultConsumerTest.java index ccdfa3bd96a..0ce5dbd84f3 100644 --- a/proc/common/src/test/java/org/neo4j/gds/WriteNodePropertiesComputationResultConsumerTest.java +++ b/proc/common/src/test/java/org/neo4j/gds/WriteNodePropertiesComputationResultConsumerTest.java @@ -20,6 +20,7 @@ package org.neo4j.gds; import org.junit.jupiter.api.Test; +import org.neo4j.common.DependencyResolver; import org.neo4j.gds.api.CloseableResourceRegistry; import org.neo4j.gds.api.DatabaseId; import org.neo4j.gds.api.Graph; @@ -38,7 +39,6 @@ import org.neo4j.gds.api.schema.MutableNodeSchema; import org.neo4j.gds.api.schema.MutableRelationshipSchema; import org.neo4j.gds.api.schema.PropertySchema; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.core.huge.HugeGraphBuilder; import org.neo4j.gds.core.loading.Capabilities; import org.neo4j.gds.core.loading.Capabilities.WriteMode; @@ -84,7 +84,17 @@ class WriteNodePropertiesComputationResultConsumerTest extends BaseTest { private final ExecutionContext executionContext = ImmutableExecutionContext .builder() .databaseId(DatabaseId.of("")) - .dependencyResolver(Neo4jProxy.emptyDependencyResolver()) + .dependencyResolver(new DependencyResolver() { + @Override + public T resolveDependency(Class type, SelectionStrategy selector) { + return null; + } + + @Override + public boolean containsDependency(Class type) { + return false; + } + }) .returnColumns(ProcedureReturnColumns.EMPTY) .log(Log.noOpLog()) .taskRegistryFactory(EmptyTaskRegistryFactory.INSTANCE) diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphCatalogProcedureFacadeFactory.java b/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphCatalogProcedureFacadeFactory.java index e1ca72c1833..422cbdcf332 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphCatalogProcedureFacadeFactory.java +++ b/procedures/facade/src/main/java/org/neo4j/gds/procedures/GraphCatalogProcedureFacadeFactory.java @@ -24,7 +24,6 @@ import org.neo4j.gds.applications.algorithms.machinery.RequestScopedDependencies; import org.neo4j.gds.applications.algorithms.machinery.WriteContext; import org.neo4j.gds.applications.graphstorecatalog.GraphProjectMemoryUsageService; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.logging.Log; import org.neo4j.gds.procedures.catalog.DatabaseModeRestriction; import org.neo4j.gds.procedures.catalog.GraphCatalogProcedureFacade; @@ -75,7 +74,7 @@ GraphCatalogProcedureFacade createGraphCatalogProcedureFacade( var streamCloser = new Consumer() { @Override public void accept(AutoCloseable autoCloseable) { - Neo4jProxy.registerCloseableResource(kernelTransaction, autoCloseable); + kernelTransaction.resourceMonitor().registerCloseableResource(autoCloseable); } }; var transactionContext = transactionContextAccessor.transactionContext( diff --git a/procedures/facade/src/main/java/org/neo4j/gds/procedures/UserAccessor.java b/procedures/facade/src/main/java/org/neo4j/gds/procedures/UserAccessor.java index 70909cb0bff..a7611492e09 100644 --- a/procedures/facade/src/main/java/org/neo4j/gds/procedures/UserAccessor.java +++ b/procedures/facade/src/main/java/org/neo4j/gds/procedures/UserAccessor.java @@ -20,7 +20,7 @@ package org.neo4j.gds.procedures; import org.neo4j.gds.api.User; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.internal.kernel.api.security.AuthSubject; import org.neo4j.internal.kernel.api.security.SecurityContext; /** @@ -28,7 +28,8 @@ */ public class UserAccessor { public User getUser(SecurityContext securityContext) { - String username = Neo4jProxy.username(securityContext.subject()); + AuthSubject subject = securityContext.subject(); + String username = subject.executingUser(); boolean isAdmin = securityContext.roles().contains("admin"); return new User(username, isAdmin); } diff --git a/procedures/integration/src/main/java/org/neo4j/gds/procedures/integration/LogAccessor.java b/procedures/integration/src/main/java/org/neo4j/gds/procedures/integration/LogAccessor.java index 019375b9f26..cca16f351fc 100644 --- a/procedures/integration/src/main/java/org/neo4j/gds/procedures/integration/LogAccessor.java +++ b/procedures/integration/src/main/java/org/neo4j/gds/procedures/integration/LogAccessor.java @@ -19,7 +19,6 @@ */ package org.neo4j.gds.procedures.integration; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.logging.Log; import org.neo4j.gds.logging.LogAdapter; import org.neo4j.logging.internal.LogService; @@ -30,7 +29,7 @@ public class LogAccessor { public Log getLog(LogService logService, Class cls) { // We stack off the Neo4j's log and have our own - var neo4jUserLog = Neo4jProxy.getUserLog(logService, cls); + var neo4jUserLog = (org.neo4j.logging.Log) logService.getUserLog(cls); return new LogAdapter(neo4jUserLog); } } diff --git a/progress-tracking/src/main/java/org/neo4j/gds/core/utils/progress/TaskRegistryFactoryProvider.java b/progress-tracking/src/main/java/org/neo4j/gds/core/utils/progress/TaskRegistryFactoryProvider.java index dbad9866c52..9084c32ebcd 100644 --- a/progress-tracking/src/main/java/org/neo4j/gds/core/utils/progress/TaskRegistryFactoryProvider.java +++ b/progress-tracking/src/main/java/org/neo4j/gds/core/utils/progress/TaskRegistryFactoryProvider.java @@ -20,8 +20,8 @@ package org.neo4j.gds.core.utils.progress; import org.neo4j.function.ThrowingFunction; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.internal.kernel.api.exceptions.ProcedureException; +import org.neo4j.internal.kernel.api.security.AuthSubject; import org.neo4j.kernel.api.procedure.Context; class TaskRegistryFactoryProvider implements ThrowingFunction { @@ -35,7 +35,8 @@ class TaskRegistryFactoryProvider implements ThrowingFunction { @@ -31,7 +31,8 @@ class UserLogRegistryFactoryProvider implements ThrowingFunction block ) { - var securityContext = Neo4jProxy.securityContext(username, AuthSubject.AUTH_DISABLED, READ, db.databaseName()); + String databaseName = db.databaseName(); + var securityContext = new SecurityContext( + new AuthSubject() { + @Override + public AuthenticationResult getAuthenticationResult() { + return AUTH_DISABLED.getAuthenticationResult(); + } + + @Override + public boolean hasUsername(String s) { + return s.equals(username); + } + + @Override + public String executingUser() { + return username; + } + }, + READ, + // GDS is always operating from an embedded context + ClientConnectionInfo.EMBEDDED_CONNECTION, + databaseName + ); GraphDatabaseApiProxy.runInTransaction(db, securityContext, block); } @@ -239,7 +263,29 @@ private static T applyWithUsername( GraphDatabaseService db, Function block ) { - var securityContext = Neo4jProxy.securityContext(username, AuthSubject.AUTH_DISABLED, READ, db.databaseName()); + String databaseName = db.databaseName(); + var securityContext = new SecurityContext( + new AuthSubject() { + @Override + public AuthenticationResult getAuthenticationResult() { + return AUTH_DISABLED.getAuthenticationResult(); + } + + @Override + public boolean hasUsername(String s) { + return s.equals(username); + } + + @Override + public String executingUser() { + return username; + } + }, + READ, + // GDS is always operating from an embedded context + ClientConnectionInfo.EMBEDDED_CONNECTION, + databaseName + ); return GraphDatabaseApiProxy.applyInTransaction(db, securityContext, block); } } diff --git a/transaction/src/main/java/org/neo4j/gds/transaction/DatabaseTransactionContext.java b/transaction/src/main/java/org/neo4j/gds/transaction/DatabaseTransactionContext.java index 159f31ce648..91ec7156c90 100644 --- a/transaction/src/main/java/org/neo4j/gds/transaction/DatabaseTransactionContext.java +++ b/transaction/src/main/java/org/neo4j/gds/transaction/DatabaseTransactionContext.java @@ -20,7 +20,6 @@ package org.neo4j.gds.transaction; import org.neo4j.gds.compat.GraphDatabaseApiProxy; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.AccessMode; @@ -78,7 +77,7 @@ private DatabaseTransactionContext( @Override public String username() { - return Neo4jProxy.username(securityContext.subject()); + return securityContext.subject().executingUser(); } @Override From ea55cc972b46d00ef22374d982ffac7562150248 Mon Sep 17 00:00:00 2001 From: Paul Horn Date: Fri, 20 Sep 2024 16:45:38 +0200 Subject: [PATCH 2/5] Inline more usages of Neo4jProxy --- .../java/org/neo4j/gds/paths/PathFactory.java | 34 ++-- .../UserInputWritePropertiesTest.java | 4 +- .../neo4j/gds/compat/CompositeNodeCursor.java | 4 +- .../gds/compat/CompositeNodeCursorImpl.java | 31 ---- ...sDatabaseManagementServiceBuilderImpl.java | 4 +- .../java/org/neo4j/gds/compat/Neo4jProxy.java | 145 ------------------ .../gds/compat/NodeLabelIndexLookupImpl.java | 33 ++-- .../gds/compat/PartitionedStoreScan.java | 36 ++--- .../org/neo4j/gds/compat/SettingProxy.java | 23 +++ .../org/neo4j/gds/compat/TestLogImpl.java | 4 +- .../gds/compat/VirtualRelationshipImpl.java | 4 +- .../org/neo4j/gds/compat/Neo4jProxyTest.java | 24 --- .../UserInputAsStringOrListOfStringTest.java | 10 +- .../GraphStoreToDatabaseExporterConfig.java | 4 +- .../gds/projection/GraphDimensionsReader.java | 4 +- .../MultipleNodeLabelIndexBasedScanner.java | 3 +- .../projection/NodeCursorBasedScanner.java | 22 ++- .../gds/projection/NodeScannerFactory.java | 34 +++- .../RelationshipScanCursorBasedScanner.java | 22 ++- .../org/neo4j/gds/compat/InternalReadOps.java | 53 +++++++ .../ShortestPathDijkstraStreamProcTest.java | 4 +- .../ShortestPathDijkstraWriteProcTest.java | 4 +- .../org/neo4j/gds/TestProcedureRunner.java | 4 +- .../org/neo4j/gds/ProcedureRunnerTest.java | 5 +- .../src/main/java/org/neo4j/gds/BaseTest.java | 4 +- .../gds/extension/Neo4jSupportExtension.java | 4 +- .../gds/projection/GraphImporterTest.java | 4 +- 27 files changed, 227 insertions(+), 300 deletions(-) delete mode 100644 compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompositeNodeCursorImpl.java diff --git a/algo/src/main/java/org/neo4j/gds/paths/PathFactory.java b/algo/src/main/java/org/neo4j/gds/paths/PathFactory.java index 09ed6fd89a9..e1eddc8687a 100644 --- a/algo/src/main/java/org/neo4j/gds/paths/PathFactory.java +++ b/algo/src/main/java/org/neo4j/gds/paths/PathFactory.java @@ -21,9 +21,11 @@ import org.jetbrains.annotations.TestOnly; import org.neo4j.gds.api.NodeLookup; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.VirtualRelationshipImpl; import org.neo4j.graphalgo.impl.util.PathImpl; +import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; +import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import java.util.List; @@ -48,12 +50,10 @@ public static Path create( long targetNodeId = nodeIds[i + 1]; - var relationship = Neo4jProxy.virtualRelationship( - RelationshipIds.next(), - nodeLookup.getNodeById(sourceNodeId), - nodeLookup.getNodeById(targetNodeId), - relationshipType - ); + long id = RelationshipIds.next(); + Node startNode = nodeLookup.getNodeById(sourceNodeId); + Node endNode = nodeLookup.getNodeById(targetNodeId); + var relationship = (Relationship) new VirtualRelationshipImpl(id, startNode, endNode, relationshipType); var costDifference = costs[i + 1] - costs[i]; relationship.setProperty(costPropertyName, costDifference); pathBuilder = pathBuilder.push(relationship); @@ -77,13 +77,11 @@ public static Path create( long sourceNodeId = nodeIds.get(i); long targetNodeId = nodeIds.get(i + 1); + long id = RelationshipIds.next(); + Node startNode = nodeLookup.getNodeById(sourceNodeId); + Node endNode = nodeLookup.getNodeById(targetNodeId); + var relationship = (Relationship) new VirtualRelationshipImpl(id, startNode, endNode, relationshipType); - var relationship = Neo4jProxy.virtualRelationship( - RelationshipIds.next(), - nodeLookup.getNodeById(sourceNodeId), - nodeLookup.getNodeById(targetNodeId), - relationshipType - ); var costDifference = costs.get(i + 1) - costs.get(i); relationship.setProperty(costPropertyName, costDifference); pathBuilder = pathBuilder.push(relationship); @@ -104,12 +102,10 @@ public static Path create( long sourceNodeId = nodeIds.get(i); long targetNodeId = nodeIds.get(i + 1); - var relationship = Neo4jProxy.virtualRelationship( - RelationshipIds.next(), - nodeLookup.getNodeById(sourceNodeId), - nodeLookup.getNodeById(targetNodeId), - relationshipType - ); + long id = RelationshipIds.next(); + Node startNode = nodeLookup.getNodeById(sourceNodeId); + Node endNode = nodeLookup.getNodeById(targetNodeId); + var relationship = (Relationship) new VirtualRelationshipImpl(id, startNode, endNode, relationshipType); pathBuilder = pathBuilder.push(relationship); } diff --git a/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/UserInputWritePropertiesTest.java b/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/UserInputWritePropertiesTest.java index dd187be1fb6..49e07fdc5c9 100644 --- a/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/UserInputWritePropertiesTest.java +++ b/applications/graph-store-catalog/src/test/java/org/neo4j/gds/applications/graphstorecatalog/UserInputWritePropertiesTest.java @@ -23,7 +23,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.VirtualRelationshipImpl; import org.neo4j.graphalgo.impl.util.PathImpl; import org.neo4j.graphdb.RelationshipType; import org.neo4j.kernel.impl.core.NodeEntity; @@ -92,7 +92,7 @@ static Stream typesInput() { arguments(1, "number"), arguments(Boolean.TRUE, "boolean"), arguments(new NodeEntity(null, 1), "node"), - arguments(Neo4jProxy.virtualRelationship( + arguments(new VirtualRelationshipImpl( 0, new NodeEntity(null, 1), new NodeEntity(null, 2), diff --git a/compatibility/api/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompositeNodeCursor.java b/compatibility/api/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompositeNodeCursor.java index 97f12d4ec62..8154fb82cd5 100644 --- a/compatibility/api/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompositeNodeCursor.java +++ b/compatibility/api/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompositeNodeCursor.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.PriorityQueue; -public abstract class CompositeNodeCursor extends DefaultCloseListenable implements Cursor { +public class CompositeNodeCursor extends DefaultCloseListenable implements Cursor { private final PriorityQueue cursorQueue; private boolean repopulateCursorQueue; @@ -43,7 +43,7 @@ public abstract class CompositeNodeCursor extends DefaultCloseListenable impleme private boolean closed = false; - protected CompositeNodeCursor(List cursors, int[] labelIds) { + public CompositeNodeCursor(List cursors, int[] labelIds) { this.cursors = cursors; this.cursorQueue = new PriorityQueue<>( cursors.size(), diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompositeNodeCursorImpl.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompositeNodeCursorImpl.java deleted file mode 100644 index 9f398782aca..00000000000 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/CompositeNodeCursorImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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; - -import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; - -import java.util.List; - -final class CompositeNodeCursorImpl extends CompositeNodeCursor { - - CompositeNodeCursorImpl(List cursors, int[] labelIds) { - super(cursors, labelIds); - } -} diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/GdsDatabaseManagementServiceBuilderImpl.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/GdsDatabaseManagementServiceBuilderImpl.java index 50edbecc6ce..0e6885a02fb 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/GdsDatabaseManagementServiceBuilderImpl.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/GdsDatabaseManagementServiceBuilderImpl.java @@ -26,11 +26,11 @@ import java.nio.file.Path; import java.util.Map; -public class GdsDatabaseManagementServiceBuilderImpl implements GdsDatabaseManagementServiceBuilder { +public final class GdsDatabaseManagementServiceBuilderImpl implements GdsDatabaseManagementServiceBuilder { private final DatabaseManagementServiceBuilderImplementation dbmsBuilder; - GdsDatabaseManagementServiceBuilderImpl(Path storeDir) { + public GdsDatabaseManagementServiceBuilderImpl(Path storeDir) { this.dbmsBuilder = new DatabaseManagementServiceBuilderImplementation(storeDir); } diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/Neo4jProxy.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/Neo4jProxy.java index 6116f42d10d..3e4ed8e89eb 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/Neo4jProxy.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/Neo4jProxy.java @@ -21,7 +21,6 @@ import org.intellij.lang.annotations.PrintFormat; import org.neo4j.configuration.Config; -import org.neo4j.configuration.GraphDatabaseSettings; import org.neo4j.dbms.api.DatabaseNotFoundException; import org.neo4j.exceptions.KernelException; import org.neo4j.gds.annotation.SuppressForbidden; @@ -31,40 +30,17 @@ import org.neo4j.gds.compat.batchimport.input.Collector; import org.neo4j.gds.compat.batchimport.input.Estimates; import org.neo4j.gds.compat.batchimport.input.ReadableGroups; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Relationship; -import org.neo4j.graphdb.RelationshipType; -import org.neo4j.internal.id.IdGenerator; -import org.neo4j.internal.id.IdGeneratorFactory; -import org.neo4j.internal.kernel.api.NodeCursor; -import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; -import org.neo4j.internal.kernel.api.RelationshipScanCursor; import org.neo4j.internal.kernel.api.exceptions.ProcedureException; -import org.neo4j.internal.recordstorage.RecordIdType; import org.neo4j.io.fs.FileSystemAbstraction; import org.neo4j.io.layout.DatabaseLayout; -import org.neo4j.io.pagecache.context.CursorContext; -import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.procedure.CallableProcedure; import org.neo4j.kernel.api.procedure.Context; import org.neo4j.kernel.api.procedure.GlobalProcedures; -import org.neo4j.logging.InternalLog; import org.neo4j.logging.internal.LogService; import org.neo4j.scheduler.JobScheduler; import org.neo4j.values.SequenceValue; import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; - -import static java.lang.String.format; public final class Neo4jProxy { @@ -82,28 +58,6 @@ public static T lookupComponentProvider(Context ctx, Class component, boo return globalProcedures.getCurrentView().lookupComponentProvider(component, safe).apply(ctx); } - public static boolean hasNodeLabelIndex(KernelTransaction kernelTransaction) { - return NodeLabelIndexLookupImpl.hasNodeLabelIndex(kernelTransaction); - } - - public static StoreScan nodesScan(KernelTransaction ktx, long nodeCount, int batchSize) { - int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(nodeCount, batchSize); - return new PartitionedStoreScan<>(ktx.dataRead().allNodesScan(numberOfPartitions, ktx.cursorContext())); - } - - public static StoreScan relationshipsScan( - KernelTransaction ktx, - long relationshipCount, - int batchSize - ) { - int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(relationshipCount, batchSize); - return new PartitionedStoreScan<>(ktx.dataRead().allRelationshipsScan(numberOfPartitions, ktx.cursorContext())); - } - - public static CompositeNodeCursor compositeNodeCursor(List cursors, int[] labelIds) { - return new CompositeNodeCursorImpl(cursors, labelIds); - } - public static BatchImporter instantiateBatchImporter( DatabaseLayout directoryStructure, FileSystemAbstraction fileSystem, @@ -126,18 +80,6 @@ public static BatchImporter instantiateBatchImporter( ); } - public static long getHighestPossibleNodeCount(IdGeneratorFactory idGeneratorFactory) { - return InternalReadOps.findValidIdGeneratorsStream( - idGeneratorFactory, - RecordIdType.NODE, - BlockFormat.INSTANCE.nodeType, - BlockFormat.INSTANCE.dynamicNodeType - ) - .mapToLong(IdGenerator::getHighId) - .max() - .orElseThrow(InternalReadOps::unsupportedStoreFormatException); - } - public static ReadableGroups newGroups() { return IMPL.newGroups(); } @@ -174,80 +116,6 @@ public static Estimates knownEstimates( ); } - private static final class BlockFormat { - private static final BlockFormat INSTANCE = new BlockFormat(); - - private org.neo4j.internal.id.IdType nodeType = null; - private org.neo4j.internal.id.IdType dynamicNodeType = null; - - BlockFormat() { - try { - var blockIdType = Class.forName("com.neo4j.internal.blockformat.BlockIdType"); - var blockTypes = Objects.requireNonNull(blockIdType.getEnumConstants()); - for (Object blockType : blockTypes) { - var type = (Enum) blockType; - switch (type.name()) { - case "NODE" -> this.nodeType = (org.neo4j.internal.id.IdType) type; - case "DYNAMIC_NODE" -> this.dynamicNodeType = (org.neo4j.internal.id.IdType) type; - } - } - } catch (ClassNotFoundException | NullPointerException | ClassCastException ignored) { - } - } - } - - public static String versionLongToString(long storeVersion) { - // copied from org.neo4j.kernel.impl.store.LegacyMetadataHandler.versionLongToString which is private - if (storeVersion == -1) { - return "Unknown"; - } - var bits = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(storeVersion).rewind(); - int length = bits.get() & 0xFF; - if (length == 0 || length > 7) { - throw new IllegalArgumentException( - format( - Locale.ENGLISH, - "The read version string length %d is not proper.", - length - ) - ); - } - char[] result = new char[length]; - for (int i = 0; i < length; i++) { - result[i] = (char) (bits.get() & 0xFF); - } - return new String(result); - } - - public static TestLog testLog() { - return new TestLogImpl(); - } - - public static Relationship virtualRelationship(long id, Node startNode, Node endNode, RelationshipType type) { - return new VirtualRelationshipImpl(id, startNode, endNode, type); - } - - public static GdsDatabaseManagementServiceBuilder databaseManagementServiceBuilder(Path storeDir) { - return new GdsDatabaseManagementServiceBuilderImpl(storeDir); - } - - public static String defaultDatabaseFormatSetting() { - return migratedDefaultDatabaseFormatSetting().orElseGet(GraphDatabaseSettings.db_format::defaultValue); - } - - private static Optional migratedDefaultDatabaseFormatSetting() { - try { - Class cls = Class.forName("com.neo4j.configuration.DefaultDbFormatSettingMigrator"); - Object migrator = cls.getDeclaredConstructor().newInstance(); - var migrateMethod = cls.getDeclaredMethod("migrate", Map.class, Map.class, InternalLog.class); - var defaultValues = new HashMap(); - migrateMethod.invoke(migrator, Map.of(), defaultValues, null); - return Optional.ofNullable(defaultValues.get(GraphDatabaseSettings.db_format.name())); - } catch (Exception e) { - return Optional.empty(); - } - } - @SuppressForbidden(reason = "This is the compat API") public static CallableProcedure callableProcedure(CompatCallableProcedure procedure) { return IMPL.callableProcedure(procedure); @@ -311,19 +179,6 @@ public static String exceptionMessage(Throwable e) { return IMPL.exceptionMessage(e); } - public static void reserveNeo4jIds(IdGeneratorFactory generatorFactory, int size, CursorContext cursorContext) { - var idGenerator = InternalReadOps.findValidIdGeneratorsStream( - generatorFactory, - RecordIdType.NODE, - BlockFormat.INSTANCE.nodeType, - BlockFormat.INSTANCE.dynamicNodeType - ) - .findFirst().orElseThrow(InternalReadOps::unsupportedStoreFormatException); - - idGenerator.nextConsecutiveIdRange(size, false, cursorContext); - } - - /** * The implementations of this method should look identical and are source-compatible. * However, Since 5.24, Neo4j exceptions implement `HasGqlStatusInfo`, which requires diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/NodeLabelIndexLookupImpl.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/NodeLabelIndexLookupImpl.java index 2abe0033db2..5e27a7461a1 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/NodeLabelIndexLookupImpl.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/NodeLabelIndexLookupImpl.java @@ -19,24 +19,15 @@ */ package org.neo4j.gds.compat; -import org.neo4j.common.EntityType; import org.neo4j.internal.kernel.api.InternalIndexState; -import org.neo4j.internal.kernel.api.SchemaRead; import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; import org.neo4j.internal.schema.IndexDescriptor; import org.neo4j.internal.schema.IndexType; import org.neo4j.internal.schema.SchemaDescriptor; -import org.neo4j.internal.schema.SchemaDescriptors; import org.neo4j.kernel.api.KernelTransaction; final class NodeLabelIndexLookupImpl { - static boolean hasNodeLabelIndex(KernelTransaction transaction) { - return NodeLabelIndexLookupImpl.findUsableMatchingIndex( - transaction, - SchemaDescriptors.forAnyEntityTokens(EntityType.NODE) - ) != IndexDescriptor.NO_INDEX; - } static IndexDescriptor findUsableMatchingIndex( KernelTransaction transaction, @@ -46,23 +37,21 @@ static IndexDescriptor findUsableMatchingIndex( var iterator = schemaRead.index(schemaDescriptor); while (iterator.hasNext()) { var index = iterator.next(); - if (index.getIndexType() == IndexType.LOOKUP && indexIsOnline(schemaRead, index)) { - return index; + if (index.getIndexType() == IndexType.LOOKUP) { + var state = InternalIndexState.FAILED; + try { + state = schemaRead.indexGetState(index); + } catch (IndexNotFoundKernelException e) { + // Well the index should always exist here, but if we didn't find it while checking the state, + // then we obviously don't want to use it. + } + if (state == InternalIndexState.ONLINE) { + return index; + } } } return IndexDescriptor.NO_INDEX; } - private static boolean indexIsOnline(SchemaRead schemaRead, IndexDescriptor index) { - var state = InternalIndexState.FAILED; - try { - state = schemaRead.indexGetState(index); - } catch (IndexNotFoundKernelException e) { - // Well the index should always exist here, but if we didn't find it while checking the state, - // then we obviously don't want to use it. - } - return state == InternalIndexState.ONLINE; - } - private NodeLabelIndexLookupImpl() {} } diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java index 656fe0a6fff..1dcc190c3b3 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java @@ -80,7 +80,22 @@ public static List> createScans( labelIds[maxIndex] = labelIds[0]; labelIds[0] = maxToken; - int numberOfPartitions = getNumberOfPartitions(maxCount, batchSize); + int numberOfPartitions; + if (maxCount > 0) { + // ceil div to try to get enough partitions so a single one does + // not include more nodes than batchSize + long partitions = ((maxCount - 1) / batchSize) + 1; + + // value must be positive + if (partitions < 1) { + partitions = 1; + } + + numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); + } else { + // we have no partitions to scan, but the value must still be positive + numberOfPartitions = 1; + } try { var session = read.tokenReadSession(indexDescriptor); @@ -110,23 +125,4 @@ public static List> createScans( } } - static int getNumberOfPartitions(long nodeCount, int batchSize) { - int numberOfPartitions; - if (nodeCount > 0) { - // ceil div to try to get enough partitions so a single one does - // not include more nodes than batchSize - long partitions = ((nodeCount - 1) / batchSize) + 1; - - // value must be positive - if (partitions < 1) { - partitions = 1; - } - - numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); - } else { - // we have no partitions to scan, but the value must still be positive - numberOfPartitions = 1; - } - return numberOfPartitions; - } } diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/SettingProxy.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/SettingProxy.java index 33c98ffd5ea..e2c159bf7c6 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/SettingProxy.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/SettingProxy.java @@ -21,12 +21,18 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +import org.neo4j.configuration.GraphDatabaseSettings; import org.neo4j.configuration.SettingBuilder; import org.neo4j.configuration.SettingValueParser; import org.neo4j.dbms.systemgraph.TopologyGraphDbmsModel; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.kernel.impl.factory.GraphDatabaseFacade; import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.logging.InternalLog; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; public final class SettingProxy { @@ -77,5 +83,22 @@ public static String secondaryModeName() { return "Secondary"; } + public static String defaultDatabaseFormatSetting() { + return migratedDefaultDatabaseFormatSetting().orElseGet(GraphDatabaseSettings.db_format::defaultValue); + } + + private static Optional migratedDefaultDatabaseFormatSetting() { + try { + Class cls = Class.forName("com.neo4j.configuration.DefaultDbFormatSettingMigrator"); + Object migrator = cls.getDeclaredConstructor().newInstance(); + var migrateMethod = cls.getDeclaredMethod("migrate", Map.class, Map.class, InternalLog.class); + var defaultValues = new HashMap(); + migrateMethod.invoke(migrator, Map.of(), defaultValues, null); + return Optional.ofNullable(defaultValues.get(GraphDatabaseSettings.db_format.name())); + } catch (Exception e) { + return Optional.empty(); + } + } + private SettingProxy() {} } diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/TestLogImpl.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/TestLogImpl.java index da544cc1895..66c2b94884c 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/TestLogImpl.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/TestLogImpl.java @@ -28,11 +28,11 @@ import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; -public class TestLogImpl implements TestLog { +public final class TestLogImpl implements TestLog { private final ConcurrentMap> messages; - TestLogImpl() { + public TestLogImpl() { messages = new ConcurrentHashMap<>(3); } diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/VirtualRelationshipImpl.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/VirtualRelationshipImpl.java index fb94fe01e3c..f0407df6562 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/VirtualRelationshipImpl.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/VirtualRelationshipImpl.java @@ -22,9 +22,9 @@ import org.neo4j.graphdb.Node; import org.neo4j.graphdb.RelationshipType; -public class VirtualRelationshipImpl extends VirtualRelationship { +public final class VirtualRelationshipImpl extends VirtualRelationship { - VirtualRelationshipImpl( + public VirtualRelationshipImpl( long id, Node startNode, Node endNode, diff --git a/compatibility/common/neo4j-kernel-adapter/src/test/java/org/neo4j/gds/compat/Neo4jProxyTest.java b/compatibility/common/neo4j-kernel-adapter/src/test/java/org/neo4j/gds/compat/Neo4jProxyTest.java index af8a00679c3..396929c30a0 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/test/java/org/neo4j/gds/compat/Neo4jProxyTest.java +++ b/compatibility/common/neo4j-kernel-adapter/src/test/java/org/neo4j/gds/compat/Neo4jProxyTest.java @@ -20,15 +20,8 @@ package org.neo4j.gds.compat; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import static org.junit.jupiter.params.provider.Arguments.arguments; class Neo4jProxyTest { @@ -37,21 +30,4 @@ void shouldLoadProxySuccessfully() { // Any access to the proxy will trigger loading an implementation assertThatCode(Neo4jProxy::emptyCollector).doesNotThrowAnyException(); } - - @ParameterizedTest - @MethodSource("versions") - void testVersionLongToString(long version, String expected) { - var proxy = Neo4jProxy.versionLongToString(version); - assertThat(proxy).isEqualTo(expected); - } - - static Stream versions() { - return Stream.of( - arguments(-1L, "Unknown"), - arguments(0x0000007473755204L, "Rust"), - arguments(0x0000000000736902L, "is"), - arguments(0x0000000065687403L, "the"), - arguments(0x2165727574754607L, "Future!") - ); - } } diff --git a/core/src/test/java/org/neo4j/gds/config/UserInputAsStringOrListOfStringTest.java b/core/src/test/java/org/neo4j/gds/config/UserInputAsStringOrListOfStringTest.java index 41981d999ed..e082dba6988 100644 --- a/core/src/test/java/org/neo4j/gds/config/UserInputAsStringOrListOfStringTest.java +++ b/core/src/test/java/org/neo4j/gds/config/UserInputAsStringOrListOfStringTest.java @@ -20,8 +20,9 @@ package org.neo4j.gds.config; import org.junit.jupiter.api.Test; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.VirtualRelationshipImpl; import org.neo4j.graphalgo.impl.util.PathImpl; +import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.kernel.impl.core.NodeEntity; @@ -70,7 +71,12 @@ void shouldNotParseNode() { void shouldNotParseRelationship() { var node1 = new NodeEntity(null, 0); var node2 = new NodeEntity(null, 1); - var relationship = Neo4jProxy.virtualRelationship(0, node1, node2, RelationshipType.withName("FOO")); + var relationship = (Relationship) new VirtualRelationshipImpl( + 0, + node1, + node2, + RelationshipType.withName("FOO") + ); assertThatThrownBy(() -> UserInputAsStringOrListOfString.parse(relationship, "nodeProperties")) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Type mismatch for nodeProperties: expected List or String, but found relationship"); diff --git a/io/core/src/main/java/org/neo4j/gds/core/io/db/GraphStoreToDatabaseExporterConfig.java b/io/core/src/main/java/org/neo4j/gds/core/io/db/GraphStoreToDatabaseExporterConfig.java index 917cc7a9c8e..8696f07e537 100644 --- a/io/core/src/main/java/org/neo4j/gds/core/io/db/GraphStoreToDatabaseExporterConfig.java +++ b/io/core/src/main/java/org/neo4j/gds/core/io/db/GraphStoreToDatabaseExporterConfig.java @@ -21,7 +21,7 @@ import org.neo4j.configuration.helpers.DatabaseNameValidator; import org.neo4j.gds.annotation.Configuration; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.SettingProxy; import org.neo4j.gds.config.JobIdConfig; import org.neo4j.gds.core.CypherMapWrapper; import org.neo4j.gds.core.io.GraphStoreExporterBaseConfig; @@ -43,7 +43,7 @@ default boolean enableDebugLog() { @Configuration.Key(DB_FORMAT_KEY) default String databaseFormat() { - return Neo4jProxy.defaultDatabaseFormatSetting(); + return SettingProxy.defaultDatabaseFormatSetting(); } @Configuration.Check diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/GraphDimensionsReader.java b/native-projection/src/main/java/org/neo4j/gds/projection/GraphDimensionsReader.java index 5d46d2c40a6..afa94840ac9 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/GraphDimensionsReader.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/GraphDimensionsReader.java @@ -33,7 +33,7 @@ import org.neo4j.gds.RelationshipProjections; import org.neo4j.gds.RelationshipType; import org.neo4j.gds.api.GraphLoaderContext; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.InternalReadOps; import org.neo4j.gds.core.GraphDimensions; import org.neo4j.gds.core.ImmutableGraphDimensions; import org.neo4j.gds.core.utils.StatementFunction; @@ -101,7 +101,7 @@ public GraphDimensions apply(KernelTransaction transaction) throws RuntimeExcept long nodeCount = labelTokenNodeLabelMappings.keyStream() .mapToLong(label -> dataRead.estimateCountsForNode(label)) .sum(); - final long allNodesCount = Neo4jProxy.getHighestPossibleNodeCount(idGeneratorFactory); + final long allNodesCount = InternalReadOps.getHighestPossibleNodeCount(idGeneratorFactory); long finalNodeCount = labelTokenNodeLabelMappings.keys().contains(ANY_LABEL) ? allNodesCount : Math.min(nodeCount, allNodesCount); diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java index 47d983df037..4302cd52d9f 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java @@ -20,7 +20,6 @@ package org.neo4j.gds.projection; import org.neo4j.gds.compat.CompositeNodeCursor; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.StoreScan; import org.neo4j.gds.transaction.TransactionContext; import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; @@ -49,7 +48,7 @@ CompositeNodeCursor entityCursor(KernelTransaction transaction) { .stream(labelIds) .mapToObj(i -> transaction.cursors().allocateNodeLabelIndexCursor(transaction.cursorContext())) .collect(Collectors.toList()); - return Neo4jProxy.compositeNodeCursor(cursors, labelIds); + return new CompositeNodeCursor(cursors, labelIds); } @Override diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java index c1790843ca3..6acf03f8ea5 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java @@ -19,7 +19,7 @@ */ package org.neo4j.gds.projection; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.PartitionedStoreScan; import org.neo4j.gds.compat.StoreScan; import org.neo4j.gds.transaction.TransactionContext; import org.neo4j.internal.kernel.api.NodeCursor; @@ -41,7 +41,25 @@ NodeCursor entityCursor(KernelTransaction transaction) { @Override StoreScan entityCursorScan(KernelTransaction transaction) { - return Neo4jProxy.nodesScan(transaction, this.nodeCount, batchSize()); + int batchSize = batchSize(); + int numberOfPartitions; + if (this.nodeCount > 0) { + // ceil div to try to get enough partitions so a single one does + // not include more nodes than batchSize + long partitions = ((this.nodeCount - 1) / batchSize) + 1; + + // value must be positive + if (partitions < 1) { + partitions = 1; + } + + numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); + } else { + // we have no partitions to scan, but the value must still be positive + numberOfPartitions = 1; + } + return new PartitionedStoreScan<>(transaction.dataRead() + .allNodesScan(numberOfPartitions, transaction.cursorContext())); } @Override diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/NodeScannerFactory.java b/native-projection/src/main/java/org/neo4j/gds/projection/NodeScannerFactory.java index 31d810292b9..6625195e502 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/NodeScannerFactory.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/NodeScannerFactory.java @@ -19,9 +19,15 @@ */ package org.neo4j.gds.projection; -import org.neo4j.gds.compat.Neo4jProxy; -import org.neo4j.gds.transaction.TransactionContext; +import org.neo4j.common.EntityType; import org.neo4j.gds.logging.Log; +import org.neo4j.gds.transaction.TransactionContext; +import org.neo4j.internal.kernel.api.InternalIndexState; +import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotFoundKernelException; +import org.neo4j.internal.schema.IndexDescriptor; +import org.neo4j.internal.schema.IndexType; +import org.neo4j.internal.schema.SchemaDescriptor; +import org.neo4j.internal.schema.SchemaDescriptors; import java.util.Arrays; @@ -61,6 +67,28 @@ public static StoreScanner.Factory create( } private static boolean hasNodeLabelIndex(TransactionContext transactionContext) { - return transactionContext.apply((tx, ktx) -> Neo4jProxy.hasNodeLabelIndex(ktx)); + return transactionContext.apply((tx, ktx) -> { + IndexDescriptor usableMatchingIndex = IndexDescriptor.NO_INDEX; + SchemaDescriptor schemaDescriptor = SchemaDescriptors.forAnyEntityTokens(EntityType.NODE); + var schemaRead = ktx.schemaRead(); + var iterator = schemaRead.index(schemaDescriptor); + while (iterator.hasNext()) { + var index = iterator.next(); + if (index.getIndexType() == IndexType.LOOKUP) { + var state = InternalIndexState.FAILED; + try { + state = schemaRead.indexGetState(index); + } catch (IndexNotFoundKernelException e) { + // Well the index should always exist here, but if we didn't find it while checking the state, + // then we obviously don't want to use it. + } + if (state == InternalIndexState.ONLINE) { + usableMatchingIndex = index; + break; + } + } + } + return usableMatchingIndex != IndexDescriptor.NO_INDEX; + }); } } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java index cb5d9018f3d..190706c1fb6 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java @@ -19,7 +19,7 @@ */ package org.neo4j.gds.projection; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.PartitionedStoreScan; import org.neo4j.gds.compat.StoreScan; import org.neo4j.gds.core.GraphDimensions; import org.neo4j.gds.transaction.TransactionContext; @@ -68,7 +68,25 @@ RelationshipScanCursor entityCursor(KernelTransaction transaction) { @Override StoreScan entityCursorScan(KernelTransaction transaction) { - return Neo4jProxy.relationshipsScan(transaction, this.relationshipCount, batchSize()); + int batchSize = batchSize(); + int numberOfPartitions; + if (this.relationshipCount > 0) { + // ceil div to try to get enough partitions so a single one does + // not include more nodes than batchSize + long partitions = ((this.relationshipCount - 1) / batchSize) + 1; + + // value must be positive + if (partitions < 1) { + partitions = 1; + } + + numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); + } else { + // we have no partitions to scan, but the value must still be positive + numberOfPartitions = 1; + } + return new PartitionedStoreScan<>(transaction.dataRead() + .allRelationshipsScan(numberOfPartitions, transaction.cursorContext())); } @Override diff --git a/neo4j-adapter/src/main/java/org/neo4j/gds/compat/InternalReadOps.java b/neo4j-adapter/src/main/java/org/neo4j/gds/compat/InternalReadOps.java index c7903313e74..a9fe8322a9e 100644 --- a/neo4j-adapter/src/main/java/org/neo4j/gds/compat/InternalReadOps.java +++ b/neo4j-adapter/src/main/java/org/neo4j/gds/compat/InternalReadOps.java @@ -23,12 +23,43 @@ import org.neo4j.internal.id.IdGenerator; import org.neo4j.internal.id.IdGeneratorFactory; import org.neo4j.internal.id.IdType; +import org.neo4j.internal.recordstorage.RecordIdType; +import org.neo4j.io.pagecache.context.CursorContext; import java.util.Arrays; +import java.util.Objects; import java.util.stream.Stream; public final class InternalReadOps { + public static long getHighestPossibleNodeCount(@Nullable IdGeneratorFactory idGeneratorFactory) { + return InternalReadOps.findValidIdGeneratorsStream( + idGeneratorFactory, + RecordIdType.NODE, + BlockFormat.INSTANCE.nodeType, + BlockFormat.INSTANCE.dynamicNodeType + ) + .mapToLong(IdGenerator::getHighId) + .max() + .orElseThrow(InternalReadOps::unsupportedStoreFormatException); + } + + public static void reserveNeo4jIds( + @Nullable IdGeneratorFactory generatorFactory, + int size, + CursorContext cursorContext + ) { + var idGenerator = InternalReadOps.findValidIdGeneratorsStream( + generatorFactory, + RecordIdType.NODE, + BlockFormat.INSTANCE.nodeType, + BlockFormat.INSTANCE.dynamicNodeType + ) + .findFirst().orElseThrow(InternalReadOps::unsupportedStoreFormatException); + + idGenerator.nextConsecutiveIdRange(size, false, cursorContext); + } + public static Stream findValidIdGeneratorsStream( @Nullable IdGeneratorFactory idGeneratorFactory, IdType... idTypes @@ -53,6 +84,28 @@ public static IllegalStateException unsupportedStoreFormatException() { "Please try to use Cypher projection instead."); } + private static final class BlockFormat { + private static final BlockFormat INSTANCE = new BlockFormat(); + + private org.neo4j.internal.id.IdType nodeType = null; + private org.neo4j.internal.id.IdType dynamicNodeType = null; + + BlockFormat() { + try { + var blockIdType = Class.forName("com.neo4j.internal.blockformat.BlockIdType"); + var blockTypes = Objects.requireNonNull(blockIdType.getEnumConstants()); + for (Object blockType : blockTypes) { + var type = (Enum) blockType; + switch (type.name()) { + case "NODE" -> this.nodeType = (org.neo4j.internal.id.IdType) type; + case "DYNAMIC_NODE" -> this.dynamicNodeType = (org.neo4j.internal.id.IdType) type; + } + } + } catch (ClassNotFoundException | NullPointerException | ClassCastException ignored) { + } + } + } + private InternalReadOps() { throw new UnsupportedOperationException("No instances"); } diff --git a/proc/path-finding/src/test/java/org/neo4j/gds/paths/sourcetarget/ShortestPathDijkstraStreamProcTest.java b/proc/path-finding/src/test/java/org/neo4j/gds/paths/sourcetarget/ShortestPathDijkstraStreamProcTest.java index f7bcd1c6f35..2f945337bd9 100644 --- a/proc/path-finding/src/test/java/org/neo4j/gds/paths/sourcetarget/ShortestPathDijkstraStreamProcTest.java +++ b/proc/path-finding/src/test/java/org/neo4j/gds/paths/sourcetarget/ShortestPathDijkstraStreamProcTest.java @@ -25,8 +25,8 @@ import org.neo4j.gds.GdsCypher; import org.neo4j.gds.TestLogProvider; import org.neo4j.gds.catalog.GraphProjectProc; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.TestLog; +import org.neo4j.gds.compat.TestLogImpl; import org.neo4j.gds.extension.IdFunction; import org.neo4j.gds.extension.Inject; import org.neo4j.gds.extension.Neo4jGraph; @@ -103,7 +103,7 @@ void setup() throws Exception { @ExtensionCallback protected void configuration(TestDatabaseManagementServiceBuilder builder) { super.configuration(builder); - testLog = Neo4jProxy.testLog(); + testLog = new TestLogImpl(); builder.setUserLogProvider(new TestLogProvider(testLog)); } diff --git a/proc/path-finding/src/test/java/org/neo4j/gds/paths/sourcetarget/ShortestPathDijkstraWriteProcTest.java b/proc/path-finding/src/test/java/org/neo4j/gds/paths/sourcetarget/ShortestPathDijkstraWriteProcTest.java index 18fb4bcc5c3..391879989e4 100644 --- a/proc/path-finding/src/test/java/org/neo4j/gds/paths/sourcetarget/ShortestPathDijkstraWriteProcTest.java +++ b/proc/path-finding/src/test/java/org/neo4j/gds/paths/sourcetarget/ShortestPathDijkstraWriteProcTest.java @@ -27,8 +27,8 @@ import org.neo4j.gds.GdsCypher; import org.neo4j.gds.TestLogProvider; import org.neo4j.gds.catalog.GraphProjectProc; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.TestLog; +import org.neo4j.gds.compat.TestLogImpl; import org.neo4j.gds.extension.IdFunction; import org.neo4j.gds.extension.Inject; import org.neo4j.gds.extension.Neo4jGraph; @@ -104,7 +104,7 @@ void setup() throws Exception { @ExtensionCallback protected void configuration(TestDatabaseManagementServiceBuilder builder) { super.configuration(builder); - testLog = Neo4jProxy.testLog(); + testLog = new TestLogImpl(); builder.setUserLogProvider(new TestLogProvider(testLog)); } diff --git a/proc/test/src/main/java/org/neo4j/gds/TestProcedureRunner.java b/proc/test/src/main/java/org/neo4j/gds/TestProcedureRunner.java index a5be51e0909..72bf6ea7ee7 100644 --- a/proc/test/src/main/java/org/neo4j/gds/TestProcedureRunner.java +++ b/proc/test/src/main/java/org/neo4j/gds/TestProcedureRunner.java @@ -20,7 +20,7 @@ package org.neo4j.gds; import org.neo4j.gds.compat.GraphDatabaseApiProxy; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.TestLogImpl; import org.neo4j.gds.core.Username; import org.neo4j.gds.core.utils.progress.EmptyTaskRegistryFactory; import org.neo4j.graphdb.GraphDatabaseService; @@ -38,7 +38,7 @@ public static

void applyOnProcedure( Class

procClass, Consumer

func ) { - applyOnProcedure(graphDb, procClass, Neo4jProxy.testLog(), func); + applyOnProcedure(graphDb, procClass, new TestLogImpl(), func); } public static

void applyOnProcedure( diff --git a/proc/test/src/test/java/org/neo4j/gds/ProcedureRunnerTest.java b/proc/test/src/test/java/org/neo4j/gds/ProcedureRunnerTest.java index 3d6f8aeed89..6af5dc44a2d 100644 --- a/proc/test/src/test/java/org/neo4j/gds/ProcedureRunnerTest.java +++ b/proc/test/src/test/java/org/neo4j/gds/ProcedureRunnerTest.java @@ -20,7 +20,8 @@ package org.neo4j.gds; import org.junit.jupiter.api.Test; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.TestLog; +import org.neo4j.gds.compat.TestLogImpl; import org.neo4j.gds.core.Username; import org.neo4j.gds.core.model.ModelCatalog; import org.neo4j.gds.core.utils.progress.PerDatabaseTaskStore; @@ -80,7 +81,7 @@ void shouldValidateThatAllContextFieldsAreSet() { void shouldPassCorrectParameters() { try (var tx = db.beginTx()) { var procedureCallContext = ProcedureCallContext.EMPTY; - var log = Neo4jProxy.testLog(); + var log = (TestLog) new TestLogImpl(); var username = Username.of("foo"); TaskRegistryFactory taskRegistryFactory = jobId -> new TaskRegistry( username.username(), diff --git a/test-utils/src/main/java/org/neo4j/gds/BaseTest.java b/test-utils/src/main/java/org/neo4j/gds/BaseTest.java index 32bb469296e..9245e58d26d 100644 --- a/test-utils/src/main/java/org/neo4j/gds/BaseTest.java +++ b/test-utils/src/main/java/org/neo4j/gds/BaseTest.java @@ -22,8 +22,8 @@ import org.assertj.core.api.Assertions; import org.intellij.lang.annotations.Language; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.TestLog; +import org.neo4j.gds.compat.TestLogImpl; import org.neo4j.gds.extension.IdToVariable; import org.neo4j.gds.extension.Inject; import org.neo4j.gds.extension.Neo4jGraphExtension; @@ -83,7 +83,7 @@ protected void configuration(TestDatabaseManagementServiceBuilder builder) { // but those are not enabled by default, test scope or otherwise. builder.setConfigRaw(Map.of("unsupported.dbms.debug.track_cursor_close", "true")); builder.setConfigRaw(Map.of("unsupported.dbms.debug.trace_cursors", "true")); - testLog = Neo4jProxy.testLog(); + testLog = new TestLogImpl(); builder.setUserLogProvider(new TestLogProvider(testLog)); // Hacky as hell but will have to do until we make this BaseTest obsolete diff --git a/test-utils/src/main/java/org/neo4j/gds/extension/Neo4jSupportExtension.java b/test-utils/src/main/java/org/neo4j/gds/extension/Neo4jSupportExtension.java index 244d875fa52..5941739d1ef 100644 --- a/test-utils/src/main/java/org/neo4j/gds/extension/Neo4jSupportExtension.java +++ b/test-utils/src/main/java/org/neo4j/gds/extension/Neo4jSupportExtension.java @@ -27,7 +27,7 @@ import org.neo4j.gds.QueryRunner; import org.neo4j.gds.TestSupport; import org.neo4j.gds.compat.GraphDatabaseApiProxy; -import org.neo4j.gds.compat.Neo4jProxy; +import org.neo4j.gds.compat.InternalReadOps; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Result; @@ -141,7 +141,7 @@ private void offsetNodeIds(GraphDatabaseService db, boolean offsetIds) { // try to convince the db that `idOffset` number of nodes have already been allocated var idGeneratorFactory = GraphDatabaseApiProxy.resolveDependency(db, IdGeneratorFactory.class); TestSupport.fullAccessTransaction(db) - .accept((tx, ktx) -> Neo4jProxy.reserveNeo4jIds(idGeneratorFactory, 42, ktx.cursorContext())); + .accept((tx, ktx) -> InternalReadOps.reserveNeo4jIds(idGeneratorFactory, 42, ktx.cursorContext())); } private void injectFields(ExtensionContext context, GraphDatabaseService db, IdFunctions idFunctions) { diff --git a/triplet-graph-builder/src/test/java/org/neo4j/gds/projection/GraphImporterTest.java b/triplet-graph-builder/src/test/java/org/neo4j/gds/projection/GraphImporterTest.java index 77a245c19d8..cb3b0d99312 100644 --- a/triplet-graph-builder/src/test/java/org/neo4j/gds/projection/GraphImporterTest.java +++ b/triplet-graph-builder/src/test/java/org/neo4j/gds/projection/GraphImporterTest.java @@ -26,8 +26,8 @@ import org.neo4j.gds.api.DatabaseId; import org.neo4j.gds.api.DatabaseInfo; import org.neo4j.gds.api.PropertyState; -import org.neo4j.gds.compat.Neo4jProxy; import org.neo4j.gds.compat.TestLog; +import org.neo4j.gds.compat.TestLogImpl; import org.neo4j.gds.config.GraphProjectConfig; import org.neo4j.gds.core.concurrency.Concurrency; import org.neo4j.gds.core.loading.Capabilities; @@ -390,7 +390,7 @@ void shouldFailImportWithUnusedInverseRelationshipType() { @Test void shouldRegisterTaskAndLogProgress() { - var log = Neo4jProxy.testLog(); + var log = new TestLogImpl(); var jobId = new JobId("test"); var taskStore = new TestTaskStore(); var progressTracker = new TaskProgressTracker( From 4e12a83d6fe4137a5fa085ba64c7ee7412177a62 Mon Sep 17 00:00:00 2001 From: Paul Horn Date: Fri, 20 Sep 2024 17:19:00 +0200 Subject: [PATCH 3/5] Bring back numberOfPartitions() --- .../gds/compat/PartitionedStoreScan.java | 36 ++--- .../MultipleNodeLabelIndexBasedScanner.java | 1 + .../projection/NodeCursorBasedScanner.java | 17 +-- .../gds/projection/PartitionedStoreScan.java | 134 ------------------ .../RelationshipScanCursorBasedScanner.java | 17 +-- 5 files changed, 23 insertions(+), 182 deletions(-) delete mode 100644 native-projection/src/main/java/org/neo4j/gds/projection/PartitionedStoreScan.java diff --git a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java index 1dcc190c3b3..ea749f3b391 100644 --- a/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java +++ b/compatibility/common/neo4j-kernel-adapter/src/main/java/org/neo4j/gds/compat/PartitionedStoreScan.java @@ -80,22 +80,7 @@ public static List> createScans( labelIds[maxIndex] = labelIds[0]; labelIds[0] = maxToken; - int numberOfPartitions; - if (maxCount > 0) { - // ceil div to try to get enough partitions so a single one does - // not include more nodes than batchSize - long partitions = ((maxCount - 1) / batchSize) + 1; - - // value must be positive - if (partitions < 1) { - partitions = 1; - } - - numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); - } else { - // we have no partitions to scan, but the value must still be positive - numberOfPartitions = 1; - } + int numberOfPartitions = getNumberOfPartitions(maxCount, batchSize); try { var session = read.tokenReadSession(indexDescriptor); @@ -125,4 +110,23 @@ public static List> createScans( } } + public static int getNumberOfPartitions(long nodeCount, int batchSize) { + int numberOfPartitions; + if (nodeCount > 0) { + // ceil div to try to get enough partitions so a single one does + // not include more nodes than batchSize + long partitions = ((nodeCount - 1) / batchSize) + 1; + + // value must be positive + if (partitions < 1) { + partitions = 1; + } + + numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); + } else { + // we have no partitions to scan, but the value must still be positive + numberOfPartitions = 1; + } + return numberOfPartitions; + } } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java index 4302cd52d9f..1046de65672 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/MultipleNodeLabelIndexBasedScanner.java @@ -20,6 +20,7 @@ package org.neo4j.gds.projection; import org.neo4j.gds.compat.CompositeNodeCursor; +import org.neo4j.gds.compat.PartitionedStoreScan; import org.neo4j.gds.compat.StoreScan; import org.neo4j.gds.transaction.TransactionContext; import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java index 6acf03f8ea5..85b91f8d9d1 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/NodeCursorBasedScanner.java @@ -42,22 +42,7 @@ NodeCursor entityCursor(KernelTransaction transaction) { @Override StoreScan entityCursorScan(KernelTransaction transaction) { int batchSize = batchSize(); - int numberOfPartitions; - if (this.nodeCount > 0) { - // ceil div to try to get enough partitions so a single one does - // not include more nodes than batchSize - long partitions = ((this.nodeCount - 1) / batchSize) + 1; - - // value must be positive - if (partitions < 1) { - partitions = 1; - } - - numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); - } else { - // we have no partitions to scan, but the value must still be positive - numberOfPartitions = 1; - } + int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(this.nodeCount, batchSize); return new PartitionedStoreScan<>(transaction.dataRead() .allNodesScan(numberOfPartitions, transaction.cursorContext())); } diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/PartitionedStoreScan.java b/native-projection/src/main/java/org/neo4j/gds/projection/PartitionedStoreScan.java deleted file mode 100644 index ef5b457db59..00000000000 --- a/native-projection/src/main/java/org/neo4j/gds/projection/PartitionedStoreScan.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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.projection; - -import org.neo4j.common.EntityType; -import org.neo4j.exceptions.KernelException; -import org.neo4j.gds.compat.CompatExecutionContext; -import org.neo4j.gds.compat.StoreScan; -import org.neo4j.internal.kernel.api.Cursor; -import org.neo4j.internal.kernel.api.NodeLabelIndexCursor; -import org.neo4j.internal.kernel.api.PartitionedScan; -import org.neo4j.internal.kernel.api.TokenPredicate; -import org.neo4j.internal.schema.IndexDescriptor; -import org.neo4j.internal.schema.SchemaDescriptors; -import org.neo4j.kernel.api.KernelTransaction; - -import java.util.ArrayList; -import java.util.List; - -public final class PartitionedStoreScan implements StoreScan { - private final PartitionedScan scan; - - public PartitionedStoreScan(PartitionedScan scan) { - this.scan = scan; - } - - @Override - public boolean reserveBatch(C cursor, CompatExecutionContext ctx) { - return ctx.reservePartition(scan, cursor); - } - - public static List> createScans( - KernelTransaction transaction, - int batchSize, - int... labelIds - ) { - var indexDescriptor = NodeLabelIndexLookupImpl.findUsableMatchingIndex( - transaction, - SchemaDescriptors.forAnyEntityTokens(EntityType.NODE) - ); - - if (indexDescriptor == IndexDescriptor.NO_INDEX) { - throw new IllegalStateException("There is no index that can back a node label scan."); - } - - var read = transaction.dataRead(); - - // Our current strategy is to select the token with the highest count - // and use that one as the driving partitioned index scan. The partitions - // of all other partitioned index scans will be aligned to that one. - int maxToken = labelIds[0]; - long maxCount = read.estimateCountsForNode(labelIds[0]); - int maxIndex = 0; - - for (int i = 1; i < labelIds.length; i++) { - long count = read.estimateCountsForNode(labelIds[i]); - if (count > maxCount) { - maxCount = count; - maxToken = labelIds[i]; - maxIndex = i; - } - } - - // swap the first label with the max count label - labelIds[maxIndex] = labelIds[0]; - labelIds[0] = maxToken; - - int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(maxCount, batchSize); - - try { - var session = read.tokenReadSession(indexDescriptor); - - var partitionedScan = read.nodeLabelScan( - session, - numberOfPartitions, - transaction.cursorContext(), - new TokenPredicate(maxToken) - ); - - var scans = new ArrayList>(labelIds.length); - scans.add(new PartitionedStoreScan<>(partitionedScan)); - - // Initialize the remaining index scans with the partitioning of the first scan. - for (int i = 1; i < labelIds.length; i++) { - int labelToken = labelIds[i]; - var scan = read.nodeLabelScan(session, partitionedScan, new TokenPredicate(labelToken)); - scans.add(new PartitionedStoreScan<>(scan)); - } - - return scans; - } catch (KernelException e) { - // should not happen, we check for the index existence and applicability - // before reading it - throw new RuntimeException("Unexpected error while initialising reading from node label index", e); - } - } - - static int getNumberOfPartitions(long nodeCount, int batchSize) { - int numberOfPartitions; - if (nodeCount > 0) { - // ceil div to try to get enough partitions so a single one does - // not include more nodes than batchSize - long partitions = ((nodeCount - 1) / batchSize) + 1; - - // value must be positive - if (partitions < 1) { - partitions = 1; - } - - numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); - } else { - // we have no partitions to scan, but the value must still be positive - numberOfPartitions = 1; - } - return numberOfPartitions; - } -} diff --git a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java index 190706c1fb6..69f9c2902aa 100644 --- a/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java +++ b/native-projection/src/main/java/org/neo4j/gds/projection/RelationshipScanCursorBasedScanner.java @@ -69,22 +69,7 @@ RelationshipScanCursor entityCursor(KernelTransaction transaction) { @Override StoreScan entityCursorScan(KernelTransaction transaction) { int batchSize = batchSize(); - int numberOfPartitions; - if (this.relationshipCount > 0) { - // ceil div to try to get enough partitions so a single one does - // not include more nodes than batchSize - long partitions = ((this.relationshipCount - 1) / batchSize) + 1; - - // value must be positive - if (partitions < 1) { - partitions = 1; - } - - numberOfPartitions = (int) Long.min(Integer.MAX_VALUE, partitions); - } else { - // we have no partitions to scan, but the value must still be positive - numberOfPartitions = 1; - } + int numberOfPartitions = PartitionedStoreScan.getNumberOfPartitions(this.relationshipCount, batchSize); return new PartitionedStoreScan<>(transaction.dataRead() .allRelationshipsScan(numberOfPartitions, transaction.cursorContext())); } From 7398d796201875d7a7ea09dfd3e562453843aaba Mon Sep 17 00:00:00 2001 From: Paul Horn Date: Fri, 20 Sep 2024 17:20:35 +0200 Subject: [PATCH 4/5] Drop compat rules from forbidden-apis --- etc/forbidden-apis | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/etc/forbidden-apis b/etc/forbidden-apis index a7e19542c69..61f61e65836 100644 --- a/etc/forbidden-apis +++ b/etc/forbidden-apis @@ -1,17 +1,5 @@ org.neo4j.internal.helpers.collection.MapUtil @ Don't do it java.lang.Thread.Thread(java.lang.Runnable) @ Use org.neo4j.gds.core.concurrency.ExecutorServiceUtil.newThread to make sure that the new thread is properly named java.lang.Thread.Thread(java.lang.ThreadGroup, java.lang.Runnable) @ Use org.neo4j.gds.core.concurrency.ExecutorServiceUtil.newThread to make sure that the new thread is properly named -org.neo4j.internal.batchimport.staging.ExecutionMonitor.Adapter @ Implement CompatExecutionMonitor and call Neo4jProxy.executionMonitor - -org.neo4j.logging.internal.LogService#getUserLogProvider() @ Pass the logService through and use Neo4jProxy at the end. -org.neo4j.logging.internal.LogService#getUserLog(java.lang.Class) @ Use Neo4jProxy#getUserLog instead. -org.neo4j.logging.internal.LogService#getInternalLogProvider() @ Pass the logService through and use Neo4jProxy at the end. -org.neo4j.logging.internal.LogService#getInternalLog(java.lang.Class) @ Use Neo4jProxy#getInternalLog instead. - -org.neo4j.configuration.SettingImpl#newBuilder(java.lang.String, org.neo4j.configuration.SettingValueParser, java.lang.Object) @ Use SettingProxy#newBuilder instead. -org.neo4j.configuration.SettingBuilder(java.lang.String, org.neo4j.configuration.SettingValueParser, java.lang.Object) @ Use SettingProxy#newBuilder instead. - -org.neo4j.io.layout.Neo4jLayout.of(org.neo4j.configuration.Config) @ Use Neo4jProxy.neo4jLayout instead. -org.neo4j.io.layout.Neo4jLayout.of(org.neo4j.graphdb.config.Configuration) @ Use Neo4jProxy.neo4jLayout instead. java.util.ServiceLoader#load(java.lang.Class) @ Must pass explicit class loader with service loading, as the context class loader for the thread may be the wrong one From 808824a86dd4879a440e61f8ad4e1c5630227b52 Mon Sep 17 00:00:00 2001 From: Paul Horn Date: Tue, 24 Sep 2024 14:39:07 +0200 Subject: [PATCH 5/5] We don't need this hacky piece of test anymore --- .../neo4j/gds/core/UsernameExtensionTest.java | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/core/src/test/java/org/neo4j/gds/core/UsernameExtensionTest.java b/core/src/test/java/org/neo4j/gds/core/UsernameExtensionTest.java index ceff55575cb..c8100941951 100644 --- a/core/src/test/java/org/neo4j/gds/core/UsernameExtensionTest.java +++ b/core/src/test/java/org/neo4j/gds/core/UsernameExtensionTest.java @@ -19,13 +19,11 @@ */ package org.neo4j.gds.core; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.neo4j.gds.annotation.SuppressForbidden; import org.neo4j.gds.compat.GraphDatabaseApiProxy; import org.neo4j.gds.utils.CheckedRunnable; import org.neo4j.graphdb.Result; -import org.neo4j.internal.unsafe.UnsafeUtil; import org.neo4j.procedure.Context; import org.neo4j.procedure.Procedure; import org.neo4j.test.TestDatabaseManagementServiceBuilder; @@ -76,51 +74,6 @@ void shouldNotInvokeCompatLayerWhenUsernameIsContextInjected() throws Exception assertThat(runCapturingStdOut(runnable)).isEmpty(); } - @Test - void shouldInvokeCompatLayerWhenTheUsernameIsInteractedWith() throws Exception { - // This test checks that we initialize the Neo4jProxy in the procedure invocation - // Since we have no control over test execution, it might be that the class is already initialized. - // There is also no good way of checking if a class is initialized without also initializing it. - // So we will do the not-so-good way for now - - // UnsafeUtil does not expose the actual unsafe (for good reasons) - var theUnsafeField = UnsafeUtil.class.getDeclaredField("unsafe"); - // This might trigger a warning of illegal reflective access - theUnsafeField.setAccessible(true); - var theUnsafe = (sun.misc.Unsafe) theUnsafeField.get(null); - - Assumptions.assumeFalse(theUnsafe == null, "There is no Unsafe available on this platform, skipping test"); - - Class neoProxyClass = Class.forName( - "org.neo4j.gds.compat.Neo4jProxy", - /* initialize class = */ false, - getClass().getClassLoader() - ); - - // The unsafe method is (other than being unsafe) deprecated since JDK15 and scheduled for removal with no replacement. - // The implementation is apparently not thread-safe and one should use `ensureInitialized` in the - // MethodHandles API, but that does not give us what we want. - Assumptions.assumeTrue( - theUnsafe.shouldBeInitialized(neoProxyClass), - "The Neo4jProxy is already initialized, this test cannot succeed" - ); - - CheckedRunnable runnable = () -> { - var dbms = new TestDatabaseManagementServiceBuilder() - .impermanent() - .noOpSystemGraphInitializer() - // make sure that we 1) have our extension under test and 2) have it only once - .removeExtensions(ex -> ex instanceof UsernameExtension) - .addExtension(new UsernameExtension()) - .build(); - var db = dbms.database(DEFAULT_DATABASE_NAME); - GraphDatabaseApiProxy.registerProcedures(db, UsernameProc.class); - var ignored = db.executeTransactionally("CALL gds.test.username()", Map.of(), Result::resultAsString); - dbms.shutdown(); - }; - assertThat(runCapturingStdOut(runnable)).contains(" Loaded compatibility layer:"); - } - @SuppressForbidden(reason = "System.out is used") private String runCapturingStdOut(CheckedRunnable runnable) throws E { var stdout = new ByteArrayOutputStream(8192);