diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DatabaseUnavailableException.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DatabaseUnavailableException.java
new file mode 100644
index 0000000000000..1dcb531c33897
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/DatabaseUnavailableException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2002-2017 "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.api;
+
+class DatabaseUnavailableException extends RuntimeException
+{
+ DatabaseUnavailableException()
+ {
+ super( "This database is unavailable." );
+ }
+
+}
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 425f2da9674ec..7ec1ab61c0305 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
@@ -103,6 +103,13 @@ public class KernelTransactions extends LifecycleAdapter implements Supplier localTxPool = new MarshlandPool<>( globalTxPool );
+ /**
+ * Kernel transactions component status. True when stopped, false when started.
+ * Will not allow to start new transaction by stopped instance of kernel transactions.
+ * Should simplify tracking of stopped component usage by up the stack components.
+ */
+ private volatile boolean stopped = true;
+
public KernelTransactions( StatementLocksFactory statementLocksFactory,
ConstraintIndexCreator constraintIndexCreator,
StatementOperationContainer statementOperationContainer,
@@ -148,11 +155,11 @@ public KernelTransaction newInstance( KernelTransaction.Type type, SecurityConte
{
while ( !newTransactionsLock.readLock().tryLock( 1, TimeUnit.SECONDS ) )
{
- assertDatabaseIsRunning();
+ assertRunning();
}
try
{
- assertDatabaseIsRunning();
+ assertRunning();
TransactionId lastCommittedTransaction = transactionIdStore.getLastCommittedTransaction();
KernelTransactionImplementation tx = localTxPool.acquire();
StatementLocks statementLocks = statementLocksFactory.newInstance();
@@ -219,6 +226,7 @@ public boolean haveClosingTransaction()
@Override
public void start() throws Throwable
{
+ stopped = false;
unblockNewTransactions();
}
@@ -226,6 +234,7 @@ public void start() throws Throwable
public void stop() throws Throwable
{
blockNewTransactions();
+ stopped = true;
}
@Override
@@ -290,12 +299,20 @@ Set getAllTransactions()
return allTransactions;
}
- private void assertDatabaseIsRunning()
+ private void assertRunning()
{
if ( availabilityGuard.isShutdown() )
{
throw new DatabaseShutdownException();
}
+ if ( !availabilityGuard.isAvailable() )
+ {
+ throw new DatabaseUnavailableException();
+ }
+ if ( stopped )
+ {
+ throw new IllegalStateException( "Can't start new transaction with stopped " + getClass() );
+ }
}
private void assertCurrentThreadIsNotBlockingNewTransactions()
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 72f33c0332baa..308ab8c82486d 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
@@ -69,6 +69,7 @@
import org.neo4j.storageengine.api.TransactionApplicationMode;
import org.neo4j.storageengine.api.lock.ResourceLocker;
import org.neo4j.storageengine.api.txstate.ReadableTransactionState;
+import org.neo4j.test.OtherThreadExecutor;
import org.neo4j.test.Race;
import org.neo4j.test.rule.concurrent.OtherThreadRule;
@@ -413,12 +414,62 @@ public void exceptionWhenStartingNewTransactionOnShutdownInstance() throws Throw
SecurityContext securityContext = mock( SecurityContext.class );
availabilityGuard.shutdown();
- Executors.newSingleThreadExecutor().submit( () -> stopKernelTransactions( kernelTransactions ) ).get();
+ t2.execute( (OtherThreadExecutor.WorkerCommand) state ->
+ {
+ stopKernelTransactions( kernelTransactions );
+ return null;
+ } );
expectedException.expect( DatabaseShutdownException.class );
kernelTransactions.newInstance( KernelTransaction.Type.explicit, securityContext, 0L );
}
+ @Test
+ public void exceptionWhenStartingNewTransactionOnNonAvailableInstance() throws Throwable
+ {
+ KernelTransactions kernelTransactions = newKernelTransactions();
+ SecurityContext securityContext = mock( SecurityContext.class );
+
+ availabilityGuard.require( AvailabilityGuard.availabilityRequirement( "Perform store copy." ) );
+
+ t2.execute( (OtherThreadExecutor.WorkerCommand) state ->
+ {
+ stopKernelTransactions( kernelTransactions );
+ return null;
+ } );
+
+ expectedException.expect( DatabaseUnavailableException.class );
+ kernelTransactions.newInstance( KernelTransaction.Type.explicit, securityContext, 0L );
+ }
+
+ @Test
+ public void exceptionWhenStartingNewTransactionOnStoppedKernelTransactions() throws Throwable
+ {
+ KernelTransactions kernelTransactions = newKernelTransactions();
+ SecurityContext securityContext = mock( SecurityContext.class );
+
+ t2.execute( (OtherThreadExecutor.WorkerCommand) state ->
+ {
+ stopKernelTransactions( kernelTransactions );
+ return null;
+ } );
+
+ expectedException.expect( IllegalStateException.class );
+ kernelTransactions.newInstance( KernelTransaction.Type.explicit, securityContext, 0L );
+ }
+
+ @Test
+ public void startNewTransactionOnRestartedKErnelTransactions() throws Throwable
+ {
+ KernelTransactions kernelTransactions = newKernelTransactions();
+ SecurityContext securityContext = mock( SecurityContext.class );
+
+ kernelTransactions.stop();
+ kernelTransactions.start();
+ assertNotNull( "New transaction created by restarted kernel transactions component.",
+ kernelTransactions.newInstance( KernelTransaction.Type.explicit, securityContext, 0L ) );
+ }
+
private void stopKernelTransactions( KernelTransactions kernelTransactions )
{
try
diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/NeoStoreDataSourceRule.java b/community/kernel/src/test/java/org/neo4j/test/rule/NeoStoreDataSourceRule.java
index 3d4e6cd09197c..fe4a6829d7b40 100644
--- a/community/kernel/src/test/java/org/neo4j/test/rule/NeoStoreDataSourceRule.java
+++ b/community/kernel/src/test/java/org/neo4j/test/rule/NeoStoreDataSourceRule.java
@@ -73,11 +73,11 @@
import org.neo4j.logging.NullLog;
import org.neo4j.logging.NullLogProvider;
import org.neo4j.time.Clocks;
+import org.neo4j.time.SystemNanoClock;
import static org.mockito.Mockito.RETURNS_MOCKS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-
import static org.neo4j.helpers.Exceptions.launderedException;
public class NeoStoreDataSourceRule extends ExternalResource
@@ -126,6 +126,7 @@ public NeoStoreDataSource getDataSource( File storeDir, FileSystemAbstraction fs
Monitors monitors = new Monitors();
LabelScanStoreProvider labelScanStoreProvider =
nativeLabelScanStoreProvider( storeDir, fs, pageCache, config, logService );
+ SystemNanoClock clock = Clocks.nanoClock();
dataSource = new NeoStoreDataSource( storeDir, config, idGeneratorFactory, IdReuseEligibility.ALWAYS,
idConfigurationProvider,
logService, mock( JobScheduler.class, RETURNS_MOCKS ), mock( TokenNameLookup.class ),
@@ -140,7 +141,7 @@ fs, mock( TransactionMonitor.class ), databaseHealth,
new Tracers( "null", NullLog.getInstance(), monitors, jobScheduler ),
mock( Procedures.class ),
IOLimiter.unlimited(),
- mock( AvailabilityGuard.class ), Clocks.nanoClock(),
+ new AvailabilityGuard( clock, NullLog.getInstance() ), clock,
new CanWrite(), new StoreCopyCheckPointMutex() );
return dataSource;