From 9a3e987942ac3a56e6163782ba83044159129e37 Mon Sep 17 00:00:00 2001 From: lutovich Date: Thu, 28 Jul 2016 13:15:25 +0200 Subject: [PATCH] Add config to enable/disable deferred locks Also simplified StatementLocks creation in KTI - now it only requires StatementLocksFactory and does not require Locks. --- .../org/neo4j/kernel/NeoStoreDataSource.java | 7 +- .../api/KernelTransactionImplementation.java | 6 +- .../kernel/impl/api/KernelTransactions.java | 7 +- .../kernel/impl/factory/DataSourceModule.java | 31 ++++- .../locking/SimpleStatementLocksFactory.java | 29 ++++- .../impl/locking/StatementLocksFactory.java | 21 ++- .../kernel/api/KernelTransactionFactory.java | 2 +- .../KernelTransactionImplementationTest.java | 2 +- .../api/KernelTransactionTerminationTest.java | 2 +- .../impl/api/KernelTransactionsTest.java | 2 +- .../SimpleStatementLocksFactoryTest.java | 62 +++++++++ .../neo4j/test/NeoStoreDataSourceRule.java | 4 +- .../DeferringStatementLocksFactory.java | 40 +++++- .../kernel/impl/locking/DeferringLocksIT.java | 32 +++-- .../DeferringStatementLocksFactoryTest.java | 120 ++++++++++++++++++ 15 files changed, 326 insertions(+), 41 deletions(-) create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/locking/SimpleStatementLocksFactoryTest.java create mode 100644 enterprise/deferred-locks/src/test/java/org/neo4j/kernel/impl/locking/DeferringStatementLocksFactoryTest.java diff --git a/community/kernel/src/main/java/org/neo4j/kernel/NeoStoreDataSource.java b/community/kernel/src/main/java/org/neo4j/kernel/NeoStoreDataSource.java index 76ba078910b6a..5a5aa3dfc4950 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/NeoStoreDataSource.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/NeoStoreDataSource.java @@ -99,7 +99,6 @@ import org.neo4j.kernel.impl.index.IndexConfigStore; import org.neo4j.kernel.impl.index.LegacyIndexStore; import org.neo4j.kernel.impl.locking.LockService; -import org.neo4j.kernel.impl.locking.Locks; import org.neo4j.kernel.impl.locking.ReentrantLockService; import org.neo4j.kernel.impl.locking.StatementLocksFactory; import org.neo4j.kernel.impl.store.MetaDataStore; @@ -359,7 +358,6 @@ boolean applicable( DiagnosticsPhase phase ) private final PropertyKeyTokenHolder propertyKeyTokenHolder; private final LabelTokenHolder labelTokens; private final RelationshipTypeTokenHolder relationshipTypeTokens; - private final Locks locks; private final StatementLocksFactory statementLocksFactory; private final SchemaWriteGuard schemaWriteGuard; private final TransactionEventHandlers transactionEventHandlers; @@ -425,7 +423,7 @@ public NeoStoreDataSource( File storeDir, Config config, StoreFactory sf, LogPro JobScheduler scheduler, TokenNameLookup tokenNameLookup, DependencyResolver dependencyResolver, PropertyKeyTokenHolder propertyKeyTokens, LabelTokenHolder labelTokens, RelationshipTypeTokenHolder relationshipTypeTokens, - Locks locks, StatementLocksFactory statementLocksFactory, + StatementLocksFactory statementLocksFactory, SchemaWriteGuard schemaWriteGuard, TransactionEventHandlers transactionEventHandlers, IndexingService.Monitor indexingServiceMonitor, FileSystemAbstraction fs, StoreUpgrader storeMigrationProcess, TransactionMonitor transactionMonitor, @@ -451,7 +449,6 @@ public NeoStoreDataSource( File storeDir, Config config, StoreFactory sf, LogPro this.propertyKeyTokenHolder = propertyKeyTokens; this.labelTokens = labelTokens; this.relationshipTypeTokens = relationshipTypeTokens; - this.locks = locks; this.statementLocksFactory = statementLocksFactory; this.schemaWriteGuard = schemaWriteGuard; this.transactionEventHandlers = transactionEventHandlers; @@ -1101,7 +1098,7 @@ public KernelAPI get() final TransactionHooks hooks = new TransactionHooks(); final KernelTransactions kernelTransactions = life.add( new KernelTransactions( neoStoreTxContextFactory, - neoStores, locks, statementLocksFactory, integrityValidator, constraintIndexCreator, + neoStores, statementLocksFactory, integrityValidator, constraintIndexCreator, indexingService, labelScanStore, statementOperations, updateableSchemaState, schemaWriteGuard, schemaIndexProviderMap, transactionHeaderInformationFactory, storeLayer, transactionCommitProcess, indexConfigStore, legacyIndexProviderLookup, hooks, diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionImplementation.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionImplementation.java index eaf63c1f1600a..854039b4e0633 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionImplementation.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactionImplementation.java @@ -70,7 +70,6 @@ import org.neo4j.kernel.impl.constraints.ConstraintSemantics; import org.neo4j.kernel.impl.index.IndexEntityType; import org.neo4j.kernel.impl.locking.LockGroup; -import org.neo4j.kernel.impl.locking.Locks; import org.neo4j.kernel.impl.locking.StatementLocks; import org.neo4j.kernel.impl.locking.StatementLocksFactory; import org.neo4j.kernel.impl.store.NeoStores; @@ -160,7 +159,6 @@ TransactionType upgradeToSchemaTransaction() throws InvalidTransactionTypeKernel private final TransactionMonitor transactionMonitor; private final StoreReadLayer storeLayer; private final ProcedureCache procedureCache; - private final Locks locks; private final StatementLocksFactory statementLocksFactory; private final Clock clock; private final TransactionToRecordStateVisitor txStateToRecordStateVisitor = new TransactionToRecordStateVisitor(); @@ -221,7 +219,6 @@ public KernelTransactionImplementation( StatementOperationParts operations, Clock clock, TransactionTracer tracer, ProcedureCache procedureCache, - Locks locks, StatementLocksFactory statementLocksFactory, NeoStoreTransactionContext context, boolean txTerminationAwareLocks ) @@ -241,7 +238,6 @@ public KernelTransactionImplementation( StatementOperationParts operations, this.transactionMonitor = transactionMonitor; this.storeLayer = storeLayer; this.procedureCache = procedureCache; - this.locks = locks; this.statementLocksFactory = statementLocksFactory; this.context = context; this.legacyIndexTransactionState = new CachingLegacyIndexTransactionState( legacyIndexTransactionState ); @@ -257,7 +253,7 @@ public KernelTransactionImplementation( StatementOperationParts operations, */ public KernelTransactionImplementation initialize( long lastCommittedTx, long lastTimeStamp ) { - this.statementLocks = statementLocksFactory.newInstance( locks.newClient() ); + this.statementLocks = statementLocksFactory.newInstance(); this.terminationReason = null; this.closing = closed = failure = success = false; this.transactionType = TransactionType.ANY; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactions.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactions.java index 813614bfb5eb8..0c7183a21609d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactions.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/KernelTransactions.java @@ -46,7 +46,6 @@ import org.neo4j.kernel.impl.api.store.StoreReadLayer; import org.neo4j.kernel.impl.constraints.ConstraintSemantics; import org.neo4j.kernel.impl.index.IndexConfigStore; -import org.neo4j.kernel.impl.locking.Locks; import org.neo4j.kernel.impl.locking.StatementLocksFactory; import org.neo4j.kernel.impl.store.NeoStores; import org.neo4j.kernel.impl.store.TransactionId; @@ -83,7 +82,6 @@ public class KernelTransactions extends LifecycleAdapter private final NeoStoreTransactionContextFactory neoStoreTransactionContextFactory; private final NeoStores neoStores; private final StatementLocksFactory statementLocksFactory; - private final Locks locks; private final boolean txTerminationAwareLocks; private final IntegrityValidator integrityValidator; private final ConstraintIndexCreator constraintIndexCreator; @@ -125,7 +123,7 @@ public class KernelTransactions extends LifecycleAdapter public KernelTransactions( NeoStoreTransactionContextFactory neoStoreTransactionContextFactory, NeoStores neoStores, - Locks locks, StatementLocksFactory statementLocksFactory, + StatementLocksFactory statementLocksFactory, IntegrityValidator integrityValidator, ConstraintIndexCreator constraintIndexCreator, IndexingService indexingService, LabelScanStore labelScanStore, @@ -146,7 +144,6 @@ public KernelTransactions( NeoStoreTransactionContextFactory neoStoreTransaction { this.neoStoreTransactionContextFactory = neoStoreTransactionContextFactory; this.neoStores = neoStores; - this.locks = locks; this.statementLocksFactory = statementLocksFactory; this.txTerminationAwareLocks = config.get( tx_termination_aware_locks ); this.integrityValidator = integrityValidator; @@ -189,7 +186,7 @@ public KernelTransactionImplementation newInstance() labelScanStore, indexingService, updateableSchemaState, recordState, providerMap, neoStores, hooks, constraintIndexCreator, transactionHeaderInformationFactory, transactionCommitProcess, transactionMonitor, storeLayer, legacyIndexTransactionState, - localTxPool, constraintSemantics, clock, tracers.transactionTracer, procedureCache, locks, + localTxPool, constraintSemantics, clock, tracers.transactionTracer, procedureCache, statementLocksFactory, context, txTerminationAwareLocks ); allTransactions.add( tx ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java index 15d9a7f35c384..5d828fbd30ca4 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java @@ -66,6 +66,7 @@ import org.neo4j.kernel.impl.coreapi.RelationshipAutoIndexerImpl; import org.neo4j.kernel.impl.coreapi.schema.SchemaImpl; import org.neo4j.kernel.impl.index.IndexConfigStore; +import org.neo4j.kernel.impl.locking.Locks; import org.neo4j.kernel.impl.locking.SimpleStatementLocksFactory; import org.neo4j.kernel.impl.locking.StatementLocksFactory; import org.neo4j.kernel.impl.logging.LogService; @@ -81,6 +82,7 @@ import org.neo4j.kernel.info.DiagnosticsManager; import org.neo4j.kernel.lifecycle.LifeSupport; import org.neo4j.kernel.lifecycle.LifecycleAdapter; +import org.neo4j.logging.Log; import static org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory.Configuration.execution_guard_enabled; @@ -113,12 +115,15 @@ public class DataSourceModule public final Supplier storeId; + private final Log log; + public DataSourceModule( final GraphDatabaseFacadeFactory.Dependencies dependencies, final PlatformModule platformModule, EditionModule editionModule ) { final org.neo4j.kernel.impl.util.Dependencies deps = platformModule.dependencies; Config config = platformModule.config; LogService logging = platformModule.logging; + this.log = logging.getInternalLog( DataSourceModule.class ); FileSystemAbstraction fileSystem = platformModule.fileSystem; DataSourceManager dataSourceManager = platformModule.dataSourceManager; LifeSupport life = platformModule.life; @@ -195,14 +200,14 @@ public GraphDatabaseService getGraphDatabaseService() KernelHealth kernelHealth = deps.satisfyDependency( new KernelHealth( kernelPanicEventGenerator, logging.getInternalLog( KernelHealth.class ) ) ); - StatementLocksFactory statementLocksFactory = createStatementLocksFactory(); + StatementLocksFactory locksFactory = createStatementLocksFactory( editionModule.lockManager, config, log ); neoStoreDataSource = deps.satisfyDependency( new NeoStoreDataSource( storeDir, config, storeFactory, logging.getInternalLogProvider(), platformModule.jobScheduler, new NonTransactionalTokenNameLookup( editionModule.labelTokenHolder, editionModule.relationshipTypeTokenHolder, editionModule.propertyKeyTokenHolder ), deps, editionModule.propertyKeyTokenHolder, editionModule.labelTokenHolder, relationshipTypeTokenHolder, - editionModule.lockManager, statementLocksFactory, schemaWriteGuard, transactionEventHandlers, + locksFactory, schemaWriteGuard, transactionEventHandlers, platformModule.monitors.newMonitor( IndexingService.Monitor.class ), fileSystem, storeMigrationProcess, platformModule.transactionMonitor, kernelHealth, platformModule.monitors.newMonitor( PhysicalLogFile.Monitor.class ), @@ -384,21 +389,35 @@ public Relationship newRelationshipProxy( long id, long startNodeId, int typeId, }; } - private static StatementLocksFactory createStatementLocksFactory() + private static StatementLocksFactory createStatementLocksFactory( Locks locks, Config config, Log log ) { + StatementLocksFactory statementLocksFactory; + + String serviceName = StatementLocksFactory.class.getSimpleName(); List factories = Iterables.toList( Service.load( StatementLocksFactory.class ) ); if ( factories.isEmpty() ) { - return new SimpleStatementLocksFactory(); + statementLocksFactory = new SimpleStatementLocksFactory( locks ); + + log.info( "No services implementing " + serviceName + " found. " + + "Using " + SimpleStatementLocksFactory.class.getSimpleName() ); } else if ( factories.size() == 1 ) { - return factories.get( 0 ); + statementLocksFactory = factories.get( 0 ); + + log.info( "Found single implementation of " + serviceName + + ". Namely " + statementLocksFactory.getClass().getSimpleName() ); } else { - throw new IllegalStateException( "Found more than one implementation: " + factories ); + throw new IllegalStateException( + "Found more than one implementation of " + serviceName + ": " + factories ); } + + statementLocksFactory.initialize( locks, config ); + + return statementLocksFactory; } /** diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/SimpleStatementLocksFactory.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/SimpleStatementLocksFactory.java index c50502ba5fca5..f1563bd07bfc2 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/SimpleStatementLocksFactory.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/SimpleStatementLocksFactory.java @@ -19,11 +19,36 @@ */ package org.neo4j.kernel.impl.locking; +import org.neo4j.kernel.configuration.Config; + +import static java.util.Objects.requireNonNull; + +/** + * A {@link StatementLocksFactory} that creates {@link SimpleStatementLocks}. + */ public class SimpleStatementLocksFactory implements StatementLocksFactory { + private final Locks locks; + + public SimpleStatementLocksFactory( Locks locks ) + { + this.locks = requireNonNull( locks ); + } + + /** + * Inherited from the {@link StatementLocksFactory} interface but does nothing for this factory. + * + * @param locks will not be used. + * @param config will not be used. + */ + @Override + public void initialize( Locks locks, Config config ) + { + } + @Override - public StatementLocks newInstance( Locks.Client locksClient ) + public StatementLocks newInstance() { - return new SimpleStatementLocks( locksClient ); + return new SimpleStatementLocks( locks.newClient() ); } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/StatementLocksFactory.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/StatementLocksFactory.java index c816e06c4e9d0..88db2aa0979f2 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/StatementLocksFactory.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/StatementLocksFactory.java @@ -19,7 +19,26 @@ */ package org.neo4j.kernel.impl.locking; +import org.neo4j.kernel.configuration.Config; + +/** + * Factory to create {@link StatementLocks} instances. + */ public interface StatementLocksFactory { - StatementLocks newInstance( Locks.Client locksClient ); + /** + * Initialize this factory with the given {@code locks} and {@code config}. Callers should ensure this method + * is called once during database startup. + * + * @param locks the locks to use. + * @param config the database config that can contain settings interesting for factory implementations. + */ + void initialize( Locks locks, Config config ); + + /** + * Create new {@link StatementLocks} instance. + * + * @return new statement locks. + */ + StatementLocks newInstance(); } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/api/KernelTransactionFactory.java b/community/kernel/src/test/java/org/neo4j/kernel/api/KernelTransactionFactory.java index c5b60491904d2..6bc7be4fc1e8f 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/api/KernelTransactionFactory.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/api/KernelTransactionFactory.java @@ -65,7 +65,7 @@ null, mock( NeoStores.class ), Clock.SYSTEM_CLOCK, TransactionTracer.NULL, new ProcedureCache(), - new NoOpLocks(), new SimpleStatementLocksFactory(), + new SimpleStatementLocksFactory( new NoOpLocks() ), mock( NeoStoreTransactionContext.class ), false ); } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionImplementationTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionImplementationTest.java index 544e5dbd2345c..76b914e6fe637 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionImplementationTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionImplementationTest.java @@ -635,7 +635,7 @@ private KernelTransactionImplementation newTransaction( boolean txTerminationAwa return new KernelTransactionImplementation( null, null, null, null, null, recordState, null, neoStores, hooks, null, headerInformationFactory, commitProcess, transactionMonitor, storeReadLayer, legacyIndexState, pool, new StandardConstraintSemantics(), clock, - TransactionTracer.NULL, new ProcedureCache(), locks, new SimpleStatementLocksFactory(), + TransactionTracer.NULL, new ProcedureCache(), new SimpleStatementLocksFactory( locks ), mock( NeoStoreTransactionContext.class ), txTerminationAware ); } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionTerminationTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionTerminationTest.java index 4aaf6229443dd..ad889b7d78531 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionTerminationTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionTerminationTest.java @@ -397,7 +397,7 @@ private static class TestKernelTransaction extends KernelTransactionImplementati mock( TransactionCommitProcess.class ), monitor, mock( StoreReadLayer.class, RETURNS_MOCKS ), mock( LegacyIndexTransactionState.class ), mock( Pool.class ), new StandardConstraintSemantics(), new FakeClock(), TransactionTracer.NULL, - new ProcedureCache(), new NoOpLocks(), new SimpleStatementLocksFactory(), + new ProcedureCache(), new SimpleStatementLocksFactory( new NoOpLocks() ), mock( NeoStoreTransactionContext.class ), true ); this.monitor = monitor; diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionsTest.java index 133ded4b968a8..d4ca5eb2e8d9b 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionsTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/KernelTransactionsTest.java @@ -339,7 +339,7 @@ private static KernelTransactions newKernelTransactions( TransactionCommitProces MetaDataStore metaDataStore = mock( MetaDataStore.class ); when( metaDataStore.getLastCommittedTransaction() ).thenReturn( new TransactionId( 2, 3, 4 ) ); when( neoStores.getMetaDataStore() ).thenReturn( metaDataStore ); - return new KernelTransactions( contextSupplier, neoStores, locks, new SimpleStatementLocksFactory(), + return new KernelTransactions( contextSupplier, neoStores, new SimpleStatementLocksFactory( locks ), mock( IntegrityValidator.class ), null, null, null, null, null, null, null, TransactionHeaderInformationFactory.DEFAULT, readLayer, commitProcess, null, null, new TransactionHooks(), mock( ConstraintSemantics.class ), mock( TransactionMonitor.class ), diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/locking/SimpleStatementLocksFactoryTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/locking/SimpleStatementLocksFactoryTest.java new file mode 100644 index 0000000000000..91caf722fed5b --- /dev/null +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/locking/SimpleStatementLocksFactoryTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.locking; + +import org.junit.Test; + +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SimpleStatementLocksFactoryTest +{ + @Test + public void throwsForNullLocks() + { + try + { + new SimpleStatementLocksFactory( null ); + fail( "Exception expected" ); + } + catch ( Exception e ) + { + assertThat( e, instanceOf( NullPointerException.class ) ); + } + } + + @Test + public void createSimpleStatementLocks() + { + Locks locks = mock( Locks.class ); + Locks.Client client = mock( Locks.Client.class ); + when( locks.newClient() ).thenReturn( client ); + + SimpleStatementLocksFactory factory = new SimpleStatementLocksFactory( locks ); + + StatementLocks statementLocks = factory.newInstance(); + + assertThat( statementLocks, instanceOf( SimpleStatementLocks.class ) ); + assertSame( client, statementLocks.optimistic() ); + assertSame( client, statementLocks.pessimistic() ); + } +} diff --git a/community/kernel/src/test/java/org/neo4j/test/NeoStoreDataSourceRule.java b/community/kernel/src/test/java/org/neo4j/test/NeoStoreDataSourceRule.java index 23002471bef8e..3eb2c8a3a0f27 100644 --- a/community/kernel/src/test/java/org/neo4j/test/NeoStoreDataSourceRule.java +++ b/community/kernel/src/test/java/org/neo4j/test/NeoStoreDataSourceRule.java @@ -102,8 +102,8 @@ public NeoStoreDataSource getDataSource( File storeDir, FileSystemAbstraction fs dataSource = new NeoStoreDataSource( storeDir, config, storeFactory, logProvider, mock( JobScheduler.class, RETURNS_MOCKS ), mock( TokenNameLookup.class ), dependencyResolverForNoIndexProvider(), mock( PropertyKeyTokenHolder.class ), - mock( LabelTokenHolder.class ), mock( RelationshipTypeTokenHolder.class ), locks, - new SimpleStatementLocksFactory(), + mock( LabelTokenHolder.class ), mock( RelationshipTypeTokenHolder.class ), + new SimpleStatementLocksFactory( locks ), mock( SchemaWriteGuard.class ), mock( TransactionEventHandlers.class ), IndexingService.NO_MONITOR, fs, mock( StoreUpgrader.class ), mock( TransactionMonitor.class ), kernelHealth, mock( PhysicalLogFile.Monitor.class ), TransactionHeaderInformationFactory.DEFAULT, diff --git a/enterprise/deferred-locks/src/main/java/org/neo4j/kernel/impl/locking/DeferringStatementLocksFactory.java b/enterprise/deferred-locks/src/main/java/org/neo4j/kernel/impl/locking/DeferringStatementLocksFactory.java index 8859685ffd094..9d9d59c7299c3 100644 --- a/enterprise/deferred-locks/src/main/java/org/neo4j/kernel/impl/locking/DeferringStatementLocksFactory.java +++ b/enterprise/deferred-locks/src/main/java/org/neo4j/kernel/impl/locking/DeferringStatementLocksFactory.java @@ -19,11 +19,47 @@ */ package org.neo4j.kernel.impl.locking; +import org.neo4j.graphdb.config.Setting; +import org.neo4j.graphdb.factory.Description; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.configuration.Internal; +import org.neo4j.kernel.configuration.Settings; + +import static java.util.Objects.requireNonNull; +import static org.neo4j.kernel.configuration.Settings.setting; + +/** + * A {@link StatementLocksFactory} that created {@link DeferringStatementLocks} based on the given + * {@link Locks} and {@link Config}. + */ public class DeferringStatementLocksFactory implements StatementLocksFactory { + @Internal + @Description( "Enable deferring of locks to commit time. This feature is really dangerous and impacts the " + + "isolation level. It can result in both domain and storage level inconsistencies if used " + + "without an additional transaction management layer on top." ) + public static final Setting deferred_locks_enabled = + setting( "unsupported.dbms.deferred_locks.enabled", Settings.BOOLEAN, Settings.FALSE ); + + private Locks locks; + private boolean deferredLocksEnabled; + @Override - public StatementLocks newInstance( Locks.Client locksClient ) + public void initialize( Locks locks, Config config ) { - return new DeferringStatementLocks( locksClient ); + this.locks = requireNonNull( locks ); + this.deferredLocksEnabled = config.get( deferred_locks_enabled ); + } + + @Override + public StatementLocks newInstance() + { + if ( locks == null ) + { + throw new IllegalStateException( "Factory has not been initialized" ); + } + + Locks.Client client = locks.newClient(); + return deferredLocksEnabled ? new DeferringStatementLocks( client ) : new SimpleStatementLocks( client ); } } diff --git a/enterprise/deferred-locks/src/test/java/org/neo4j/kernel/impl/locking/DeferringLocksIT.java b/enterprise/deferred-locks/src/test/java/org/neo4j/kernel/impl/locking/DeferringLocksIT.java index b54b364af5395..1e65393c4a77f 100644 --- a/enterprise/deferred-locks/src/test/java/org/neo4j/kernel/impl/locking/DeferringLocksIT.java +++ b/enterprise/deferred-locks/src/test/java/org/neo4j/kernel/impl/locking/DeferringLocksIT.java @@ -19,6 +19,7 @@ */ package org.neo4j.kernel.impl.locking; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -29,12 +30,14 @@ import org.neo4j.graphdb.ConstraintViolationException; import org.neo4j.graphdb.DynamicLabel; +import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.NotFoundException; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.TransactionFailureException; +import org.neo4j.kernel.configuration.Settings; import org.neo4j.kernel.impl.store.InvalidRecordException; import org.neo4j.test.Barrier; import org.neo4j.test.DatabaseRule; @@ -52,19 +55,30 @@ public class DeferringLocksIT { + private static final long TEST_TIMEOUT = 30_000; + private static final Label LABEL = DynamicLabel.label( "label" ); private static final String PROPERTY_KEY = "key"; private static final String VALUE_1 = "value1"; private static final String VALUE_2 = "value2"; @Rule - public final DatabaseRule db = new ImpermanentDatabaseRule(); + public final DatabaseRule dbRule = new ImpermanentDatabaseRule().startLazily(); @Rule public final OtherThreadRule t2 = new OtherThreadRule<>(); @Rule public final OtherThreadRule t3 = new OtherThreadRule<>(); - @Test + private GraphDatabaseService db; + + @Before + public void initDb() throws Exception + { + dbRule.setConfig( DeferringStatementLocksFactory.deferred_locks_enabled, Settings.TRUE ); + db = dbRule.getGraphDatabaseAPI(); + } + + @Test( timeout = TEST_TIMEOUT ) public void shouldNotFreakOutIfTwoTransactionsDecideToEachAddTheSameProperty() throws Exception { // GIVEN @@ -106,7 +120,7 @@ public Void doWork( Void state ) throws Exception } } - @Test + @Test( timeout = TEST_TIMEOUT ) public void firstRemoveSecondChangeProperty() throws Exception { // GIVEN @@ -150,7 +164,7 @@ public Void doWork( Void state ) throws Exception } } - @Test + @Test( timeout = TEST_TIMEOUT ) public void removeNodeChangeNodeProperty() throws Exception { // GIVEN @@ -211,7 +225,7 @@ public Void doWork( Void state ) throws Exception } } - @Test + @Test( timeout = TEST_TIMEOUT ) public void readOwnChangesFromRacingIndexNoBlock() throws Throwable { t2.execute( new WorkerCommand() @@ -247,7 +261,7 @@ public Void doWork( Void state ) throws Exception assertInTxNodeWith( LABEL, PROPERTY_KEY, VALUE_1 ); } - @Test + @Test( timeout = TEST_TIMEOUT ) public void readOwnChangesWithoutIndex() throws Exception { // WHEN @@ -264,7 +278,7 @@ public void readOwnChangesWithoutIndex() throws Exception assertInTxNodeWith( LABEL, PROPERTY_KEY, VALUE_1 ); } - @Test + @Test( timeout = TEST_TIMEOUT ) public void racingMultipleUniquenessConstraintCreation() throws Exception { final Future t2ConstraintCreator = t2.execute( createUniquenessConstraintOn( LABEL, PROPERTY_KEY ) ); @@ -273,7 +287,7 @@ public void racingMultipleUniquenessConstraintCreation() throws Exception assertOnlyOneSucceeds( t2ConstraintCreator, t3ConstraintCreator, ConstraintViolationException.class ); } - @Test + @Test( timeout = TEST_TIMEOUT ) public void racingMultipleIndexCreation() throws Exception { final Future t2IndexCreator = t2.execute( createIndexOn( LABEL, PROPERTY_KEY ) ); @@ -282,7 +296,7 @@ public void racingMultipleIndexCreation() throws Exception assertOnlyOneSucceeds( t2IndexCreator, t3IndexCreator, ConstraintViolationException.class ); } - @Test + @Test( timeout = TEST_TIMEOUT ) public void racingCreationOfNodesWithDuplicatedProperties() throws Exception { try ( Transaction tx = db.beginTx() ) diff --git a/enterprise/deferred-locks/src/test/java/org/neo4j/kernel/impl/locking/DeferringStatementLocksFactoryTest.java b/enterprise/deferred-locks/src/test/java/org/neo4j/kernel/impl/locking/DeferringStatementLocksFactoryTest.java new file mode 100644 index 0000000000000..9e48c43ad6cf8 --- /dev/null +++ b/enterprise/deferred-locks/src/test/java/org/neo4j/kernel/impl/locking/DeferringStatementLocksFactoryTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2002-2016 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.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 Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.locking; + +import org.junit.Test; + +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.configuration.Settings; + +import static org.hamcrest.Matchers.instanceOf; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.neo4j.helpers.collection.MapUtil.stringMap; +import static org.neo4j.kernel.impl.locking.DeferringStatementLocksFactory.deferred_locks_enabled; + +public class DeferringStatementLocksFactoryTest +{ + @Test + public void initializeThrowsForNullLocks() + { + DeferringStatementLocksFactory factory = new DeferringStatementLocksFactory(); + try + { + factory.initialize( null, new Config() ); + fail( "Exception expected" ); + } + catch ( Exception e ) + { + assertThat( e, instanceOf( NullPointerException.class ) ); + } + } + + @Test + public void initializeThrowsForNullConfig() + { + DeferringStatementLocksFactory factory = new DeferringStatementLocksFactory(); + try + { + factory.initialize( new NoOpLocks(), null ); + fail( "Exception expected" ); + } + catch ( Exception e ) + { + assertThat( e, instanceOf( NullPointerException.class ) ); + } + } + + @Test + public void newInstanceThrowsWhenNotInitialized() + { + DeferringStatementLocksFactory factory = new DeferringStatementLocksFactory(); + try + { + factory.newInstance(); + fail( "Exception expected" ); + } + catch ( Exception e ) + { + assertThat( e, instanceOf( IllegalStateException.class ) ); + } + } + + @Test + public void newInstanceCreatesSimpleLocksWhenConfigNotSet() + { + Locks locks = mock( Locks.class ); + Locks.Client client = mock( Locks.Client.class ); + when( locks.newClient() ).thenReturn( client ); + + Config config = new Config( stringMap( deferred_locks_enabled.name(), Settings.FALSE ) ); + + DeferringStatementLocksFactory factory = new DeferringStatementLocksFactory(); + factory.initialize( locks, config ); + + StatementLocks statementLocks = factory.newInstance(); + + assertThat( statementLocks, instanceOf( SimpleStatementLocks.class ) ); + assertSame( client, statementLocks.optimistic() ); + assertSame( client, statementLocks.pessimistic() ); + } + + @Test + public void newInstanceCreatesDeferredLocksWhenConfigSet() + { + Locks locks = mock( Locks.class ); + Locks.Client client = mock( Locks.Client.class ); + when( locks.newClient() ).thenReturn( client ); + + Config config = new Config( stringMap( deferred_locks_enabled.name(), Settings.TRUE ) ); + + DeferringStatementLocksFactory factory = new DeferringStatementLocksFactory(); + factory.initialize( locks, config ); + + StatementLocks statementLocks = factory.newInstance(); + + assertThat( statementLocks, instanceOf( DeferringStatementLocks.class ) ); + assertThat( statementLocks.optimistic(), instanceOf( DeferringLockClient.class ) ); + assertSame( client, statementLocks.pessimistic() ); + } +}