private long appendToLog( TransactionToApply batch, CommitEvent commitEvent ) throws TransactionFailureException { try ( LogAppendEvent logAppendEvent = commitEvent.beginLogAppend() ) { return appender.append( batch, logAppendEvent ); } catch ( Throwable cause ) { throw new TransactionFailureException( TransactionLogError, cause, "Could not append transaction representation to log" ); } }
@Override public KernelTransaction beginTransaction( KernelTransaction.Type type, LoginContext loginContext, long timeout ) { try { availability.assertDatabaseAvailable(); KernelTransaction kernelTx = dataSource.kernelAPI.get().beginTransaction( type, loginContext, timeout ); kernelTx.registerCloseListener( txId -> threadToTransactionBridge.unbindTransactionFromCurrentThread() ); threadToTransactionBridge.bindTransactionToCurrentThread( kernelTx ); return kernelTx; } catch ( TransactionFailureException e ) { throw new org.neo4j.graphdb.TransactionFailureException( e.getMessage(), e ); } } }
@Test public void shouldPropagateTimeoutException() throws Exception { // given long version = 5L; TimeoutException timeoutException = new TimeoutException(); doThrow( timeoutException ).when( transactionIdStore ).awaitClosedTransactionId( anyLong(), anyLong() ); try { // when transactionIdTracker.awaitUpToDate( version + 1, ofMillis( 50 ) ); fail( "should have thrown" ); } catch ( TransactionFailureException ex ) { // then assertEquals( Status.Transaction.InstanceStateChanged, ex.status() ); assertEquals( timeoutException, ex.getCause() ); } }
protected void applyToStore( TransactionToApply batch, CommitEvent commitEvent, TransactionApplicationMode mode ) throws TransactionFailureException { try ( StoreApplyEvent storeApplyEvent = commitEvent.beginStoreApply() ) { storageEngine.apply( batch, mode ); } catch ( Throwable cause ) { throw new TransactionFailureException( TransactionCommitFailed, cause, "Could not apply the transaction to the store after written to log" ); } }
@Override public KernelTransaction beginTransaction( KernelTransaction.Type type, LoginContext ignored, long timeout ) { try { availability.assertDatabaseAvailable(); KernelTransaction kernelTx = sourceModule.kernelAPI.get().beginTransaction( type, this.securityContext, timeout ); kernelTx.registerCloseListener( txId -> threadToTransactionBridge.unbindTransactionFromCurrentThread() ); threadToTransactionBridge.bindTransactionToCurrentThread( kernelTx ); return kernelTx; } catch ( TransactionFailureException e ) { throw new org.neo4j.graphdb.TransactionFailureException( e.getMessage(), e ); } } }
public void validateTransactionStartKnowledge( long lastCommittedTxWhenTransactionStarted ) throws TransactionFailureException { long latestConstraintIntroducingTx = neoStores.getMetaDataStore().getLatestConstraintIntroducingTx(); if ( lastCommittedTxWhenTransactionStarted < latestConstraintIntroducingTx ) { // Constraints have changed since the transaction begun // This should be a relatively uncommon case, window for this happening is a few milliseconds when an admin // explicitly creates a constraint, after the index has been populated. We can improve this later on by // replicating the constraint validation logic down here, or rethinking where we validate constraints. // For now, we just kill these transactions. throw new TransactionFailureException( Status.Transaction.ConstraintsChanged, "Database constraints have changed (txId=%d) after this transaction (txId=%d) started, " + "which is not yet supported. Please retry your transaction to ensure all " + "constraints are executed.", latestConstraintIntroducingTx, lastCommittedTxWhenTransactionStarted ); } }
@Test public void shouldFailWithProperMessageOnAppendException() throws Exception { // GIVEN TransactionAppender appender = mock( TransactionAppender.class ); IOException rootCause = new IOException( "Mock exception" ); doThrow( new IOException( rootCause ) ).when( appender ).append( any( TransactionToApply.class ), any( LogAppendEvent.class ) ); StorageEngine storageEngine = mock( StorageEngine.class ); TransactionCommitProcess commitProcess = new TransactionRepresentationCommitProcess( appender, storageEngine ); // WHEN try { commitProcess.commit( mockedTransaction(), commitEvent, INTERNAL ); fail( "Should have failed, something is wrong with the mocking in this test" ); } catch ( TransactionFailureException e ) { assertThat( e.getMessage(), containsString( "Could not append transaction representation to log" ) ); assertTrue( contains( e, rootCause.getMessage(), rootCause.getClass() ) ); } }
/** * Throws exception if this transaction was marked as successful but failure flag has also been set to true. * <p> * This could happen when: * <ul> * <li>caller explicitly calls both {@link #success()} and {@link #failure()}</li> * <li>caller explicitly calls {@link #success()} but transaction execution fails</li> * <li>caller explicitly calls {@link #success()} but transaction is terminated</li> * </ul> * <p> * * @throws TransactionFailureException when execution failed * @throws TransactionTerminatedException when transaction was terminated */ private void failOnNonExplicitRollbackIfNeeded() throws TransactionFailureException { if ( success && isTerminated() ) { throw new TransactionTerminatedException( terminationReason ); } if ( success ) { // Success was called, but also failure which means that the client code using this // transaction passed through a happy path, but the transaction was still marked as // failed for one or more reasons. Tell the user that although it looked happy it // wasn't committed, but was instead rolled back. throw new TransactionFailureException( Status.Transaction.TransactionMarkedAsFailed, "Transaction rolled back even if marked as successful" ); } }
@Test public void shouldCloseTransactionRegardlessOfWhetherOrNotItAppliedCorrectly() throws Exception { // GIVEN TransactionIdStore transactionIdStore = mock( TransactionIdStore.class ); TransactionAppender appender = new TestableTransactionAppender( transactionIdStore ); long txId = 11; when( transactionIdStore.nextCommittingTransactionId() ).thenReturn( txId ); IOException rootCause = new IOException( "Mock exception" ); StorageEngine storageEngine = mock( StorageEngine.class ); doThrow( new IOException( rootCause ) ).when( storageEngine ).apply( any( TransactionToApply.class ), any( TransactionApplicationMode.class ) ); TransactionCommitProcess commitProcess = new TransactionRepresentationCommitProcess( appender, storageEngine ); TransactionToApply transaction = mockedTransaction(); // WHEN try { commitProcess.commit( transaction, commitEvent, INTERNAL ); } catch ( TransactionFailureException e ) { assertThat( e.getMessage(), containsString( "Could not apply the transaction to the store" ) ); assertTrue( contains( e, rootCause.getMessage(), rootCause.getClass() ) ); } // THEN // we can't verify transactionCommitted since that's part of the TransactionAppender, which we have mocked verify( transactionIdStore, times( 1 ) ).transactionClosed( eq( txId ), anyLong(), anyLong() ); }
public void validateSchemaRule( SchemaRule schemaRule ) throws TransactionFailureException { if ( schemaRule instanceof ConstraintRule ) { ConstraintRule constraintRule = (ConstraintRule) schemaRule; if ( constraintRule.getConstraintDescriptor().enforcesUniqueness() ) { try { indexes.validateIndex( constraintRule.getOwnedIndex() ); } catch ( UniquePropertyValueValidationException e ) { throw new TransactionFailureException( Status.Transaction.TransactionValidationFailed, e, "Index validation failed" ); } catch ( IndexNotFoundKernelException | IndexPopulationFailedKernelException e ) { // We don't expect this to occur, and if they do, it is because we are in a very bad state - out of // disk or index corruption, or similar. This will kill the database such that it can be shut down // and have recovery performed. It's the safest bet to avoid loosing data. throw new TransactionFailureException( Status.Transaction.TransactionValidationFailed, e, "Index population failure" ); } } } } }
@Override public KernelTransaction beginTransaction( KernelTransaction.Type type, LoginContext ignored, long timeout ) { try { availability.assertDatabaseAvailable(); KernelTransaction kernelTx = sourceModule.kernelAPI.get().beginTransaction( type, this.securityContext, timeout ); kernelTx.registerCloseListener( txId -> threadToTransactionBridge.unbindTransactionFromCurrentThread() ); threadToTransactionBridge.bindTransactionToCurrentThread( kernelTx ); return kernelTx; } catch ( TransactionFailureException e ) { throw new org.neo4j.graphdb.TransactionFailureException( e.getMessage(), e ); } } }
throw new TransactionFailureException( Status.General.DatabaseUnavailable, "Database unavailable" ); throw new TransactionFailureException( Status.Transaction.InstanceStateChanged, e, "Database not up to the requested version: %d. Latest database version is %d", oldestAcceptableTxId, transactionIdStore().getLastClosedTransactionId() );
@Override public KernelTransaction beginTransaction( KernelTransaction.Type type, LoginContext loginContext, long timeout ) { try { availability.assertDatabaseAvailable(); KernelTransaction kernelTx = dataSource.kernelAPI.get().beginTransaction( type, loginContext, timeout ); kernelTx.registerCloseListener( txId -> threadToTransactionBridge.unbindTransactionFromCurrentThread() ); threadToTransactionBridge.bindTransactionToCurrentThread( kernelTx ); return kernelTx; } catch ( TransactionFailureException e ) { throw new org.neo4j.graphdb.TransactionFailureException( e.getMessage(), e ); } } }
throw new TransactionFailureException( Status.Transaction.TransactionRollbackFailed, e, "Could not drop created constraint indexes" );
@Override protected Continuation exec( AppCommandParser parser, Session session, Output out ) throws ShellException, RemoteException { if ( parser.getLineWithoutApp().trim().length() > 0 ) { out.println( "Error: ROLLBACK should be run without trailing arguments" ); return Continuation.INPUT_COMPLETE; } KernelTransaction tx = Begin.currentTransaction( getServer() ); if ( tx == null ) { throw Commit.fail( session, "Not in a transaction" ); } session.remove( Variables.TX_COUNT ); tx.failure(); try { tx.close(); } catch ( TransactionFailureException e ) { throw new ShellException( e.getMessage() ); } out.println( "Transaction rolled back" ); return Continuation.INPUT_COMPLETE; } }
@Test public void shouldThrowTransientExceptionOnTransientKernelException() throws Exception { // GIVEN KernelTransaction kernelTransaction = mock( KernelTransaction.class ); when( kernelTransaction.isOpen() ).thenReturn( true ); doThrow( new TransactionFailureException( Status.Transaction.ConstraintsChanged, "Proving that TopLevelTransaction does the right thing" ) ).when( kernelTransaction ).close(); TopLevelTransaction transaction = new TopLevelTransaction( kernelTransaction ); // WHEN transaction.success(); try { transaction.close(); fail( "Should have failed" ); } catch ( TransientTransactionFailureException e ) { // THEN Good } }
throw new TransactionFailureException( String.format( "Index (%s) that we just created does not exist.", descriptor ), e );
throw new TransactionFailureException( Status.Transaction.TransactionHookFailed, cause, "" );
private long appendToLog( TransactionToApply batch, CommitEvent commitEvent ) throws TransactionFailureException { try ( LogAppendEvent logAppendEvent = commitEvent.beginLogAppend() ) { return appender.append( batch, logAppendEvent ); } catch ( Throwable cause ) { throw new TransactionFailureException( TransactionLogError, cause, "Could not append transaction representation to log" ); } }