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 transactionsStartedBeforeAConstraintWasCreatedAreDisallowed() { // Given NeoStores store = mock( NeoStores.class ); MetaDataStore metaDataStore = mock( MetaDataStore.class ); when( store.getMetaDataStore() ).thenReturn( metaDataStore ); IndexingService indexes = mock( IndexingService.class ); when( metaDataStore.getLatestConstraintIntroducingTx() ).thenReturn( 10L ); IntegrityValidator validator = new IntegrityValidator( store, indexes ); // When try { validator.validateTransactionStartKnowledge( 1 ); fail( "Should have thrown integrity error." ); } catch ( Exception e ) { // good } } }
@Test public void getLatestConstraintIntroducingTxShouldFailWhenStoreIsClosed() { MetaDataStore metaDataStore = newMetaDataStore(); metaDataStore.close(); try { metaDataStore.getLatestConstraintIntroducingTx(); fail( "Expected exception reading from MetaDataStore after being closed." ); } catch ( Exception e ) { assertThat( e, instanceOf( IllegalStateException.class ) ); } }
@Test public void testSetLatestConstraintTx() { // given Config config = Config.defaults(); StoreFactory sf = new StoreFactory( dir.databaseLayout(), config, new DefaultIdGeneratorFactory( fs.get() ), pageCacheRule.getPageCache( fs.get() ), fs.get(), LOG_PROVIDER, EmptyVersionContextSupplier.EMPTY ); // when NeoStores neoStores = sf.openAllNeoStores( true ); MetaDataStore metaDataStore = neoStores.getMetaDataStore(); // then the default is 0 assertEquals( 0L, metaDataStore.getLatestConstraintIntroducingTx() ); // when metaDataStore.setLatestConstraintIntroducingTx( 10L ); // then assertEquals( 10L, metaDataStore.getLatestConstraintIntroducingTx() ); // when neoStores.flush( IOLimiter.UNLIMITED ); neoStores.close(); neoStores = sf.openAllNeoStores(); // then the value should have been stored assertEquals( 10L, neoStores.getMetaDataStore().getLatestConstraintIntroducingTx() ); neoStores.close(); }
assertEquals( recordVersion, metaDataStore.getStoreVersion() ); assertEquals( 8, metaDataStore.getGraphNextProp() ); assertEquals( 9, metaDataStore.getLatestConstraintIntroducingTx() ); assertArrayEquals( metaDataStore.getLastClosedTransaction(), new long[]{FIELD_NOT_PRESENT,44,43} );
assertEquals( recordVersion, metaDataStore.getStoreVersion() ); assertEquals( 8, metaDataStore.getGraphNextProp() ); assertEquals( 9, metaDataStore.getLatestConstraintIntroducingTx() ); assertEquals( new TransactionId( 10, 11, BASE_TX_COMMIT_TIMESTAMP ), metaDataStore.getUpgradeTransaction() );
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 ); } }