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 6c0d18d449a3f..1df1e7b930032 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/NeoStoreDataSource.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/NeoStoreDataSource.java @@ -604,7 +604,7 @@ public long getTimestampForVersion( long version ) throws IOException } }; final LogFileInformation logFileInformation = - new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore, logInformation ); + new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore::getLastCommittedTransactionId, logInformation ); String pruningConf = config.get( config.get( GraphDatabaseFacadeFactory.Configuration.ephemeral ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/LogFileInformation.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/LogFileInformation.java index 0dd6e852b5d45..e438354b5518d 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/LogFileInformation.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/LogFileInformation.java @@ -24,25 +24,25 @@ public interface LogFileInformation { /** - * @return the reachable transaction that is farthest back of them all, in any existing version. + * @return the reachable entry that is farthest back of them all, in any existing version. */ - long getFirstExistingTxId() throws IOException; + long getFirstExistingEntryId() throws IOException; /** * @param version the log version to get first committed tx for. - * @return the first committed transaction id for the log with {@code version}. + * @return the first committed entry id for the log with {@code version}. * If that log doesn't exist -1 is returned. */ - long getFirstCommittedTxId( long version ) throws IOException; + long getFirstEntryId( long version ) throws IOException; /** - * @return the last committed transaction id for this Log + * @return the last committed entry id for this Log */ - long getLastCommittedTxId(); + long getLastEntryId(); /** - * @param version the log version to get first tx timestamp for. - * @return the timestamp for the start record for the first encountered transaction + * @param version the log version to get first entry timestamp for. + * @return the timestamp for the start record for the first encountered entry * in the log {@code version}. */ long getFirstStartRecordTimestamp( long version ) throws IOException; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/PhysicalLogFileInformation.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/PhysicalLogFileInformation.java index 3460ea5383bac..6d3c1e26a148c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/PhysicalLogFileInformation.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/PhysicalLogFileInformation.java @@ -28,30 +28,36 @@ public interface LogVersionToTimestamp long getTimestampForVersion( long version ) throws IOException; } + + public interface LastEntryInLog + { + long getLastEntryId( ); + } + private final PhysicalLogFiles logFiles; private final LogHeaderCache logHeaderCache; - private final TransactionIdStore transactionIdStore; + private final LastEntryInLog lastEntryInLog; private final LogVersionToTimestamp logVersionToTimestamp; public PhysicalLogFileInformation( PhysicalLogFiles logFiles, LogHeaderCache logHeaderCache, - TransactionIdStore transactionIdStore, + LastEntryInLog lastEntryInLog, LogVersionToTimestamp logVersionToTimestamp ) { this.logFiles = logFiles; this.logHeaderCache = logHeaderCache; - this.transactionIdStore = transactionIdStore; + this.lastEntryInLog = lastEntryInLog; this.logVersionToTimestamp = logVersionToTimestamp; } @Override - public long getFirstExistingTxId() throws IOException + public long getFirstExistingEntryId() throws IOException { long version = logFiles.getHighestLogVersion(); long candidateFirstTx = -1; while ( logFiles.versionExists( version ) ) { - candidateFirstTx = getFirstCommittedTxId( version ); + candidateFirstTx = getFirstEntryId( version ); version--; } version++; // the loop above goes back one version too far. @@ -62,7 +68,7 @@ public long getFirstExistingTxId() throws IOException } @Override - public long getFirstCommittedTxId( long version ) throws IOException + public long getFirstEntryId( long version ) throws IOException { long logHeader = logHeaderCache.getLogHeader( version ); if ( logHeader != -1 ) @@ -81,9 +87,9 @@ public long getFirstCommittedTxId( long version ) throws IOException } @Override - public long getLastCommittedTxId() + public long getLastEntryId() { - return transactionIdStore.getLastCommittedTransactionId(); + return lastEntryInLog.getLastEntryId(); } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionCountThreshold.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryCountThreshold.java similarity index 89% rename from community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionCountThreshold.java rename to community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryCountThreshold.java index 822ca18dcdd30..8e4fac48ca611 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionCountThreshold.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryCountThreshold.java @@ -25,11 +25,11 @@ import org.neo4j.kernel.impl.transaction.log.IllegalLogFormatException; import org.neo4j.kernel.impl.transaction.log.LogFileInformation; -public final class TransactionCountThreshold implements Threshold +public final class EntryCountThreshold implements Threshold { private final long maxTransactionCount; - TransactionCountThreshold( long maxTransactionCount ) + EntryCountThreshold( long maxTransactionCount ) { this.maxTransactionCount = maxTransactionCount; } @@ -46,7 +46,7 @@ public boolean reached( File ignored, long version, LogFileInformation source ) try { // try to ask next version log file which is my last tx - long lastTx = source.getFirstCommittedTxId( version + 1 ); + long lastTx = source.getFirstEntryId( version + 1 ); if ( lastTx == -1 ) { throw new IllegalStateException( @@ -54,7 +54,7 @@ public boolean reached( File ignored, long version, LogFileInformation source ) "PruneStrategy never checks the current active log file" ); } - long highest = source.getLastCommittedTxId(); + long highest = source.getLastEntryId(); return highest - lastTx >= maxTransactionCount; } catch ( IllegalLogFormatException e ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionTimespanThreshold.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryTimespanThreshold.java similarity index 92% rename from community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionTimespanThreshold.java rename to community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryTimespanThreshold.java index 6e825dfa5f544..7814a22aceedd 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionTimespanThreshold.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryTimespanThreshold.java @@ -27,14 +27,14 @@ import org.neo4j.kernel.impl.transaction.log.IllegalLogFormatException; import org.neo4j.kernel.impl.transaction.log.LogFileInformation; -public final class TransactionTimespanThreshold implements Threshold +public final class EntryTimespanThreshold implements Threshold { private final long timeToKeepInMillis; private final Clock clock; private long lowerLimit; - TransactionTimespanThreshold( Clock clock, TimeUnit timeUnit, long timeToKeep ) + EntryTimespanThreshold( Clock clock, TimeUnit timeUnit, long timeToKeep ) { this.clock = clock; this.timeToKeepInMillis = timeUnit.toMillis( timeToKeep ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruneStrategyFactory.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruneStrategyFactory.java index c9586031224e7..e14e22cb5b8f1 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruneStrategyFactory.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruneStrategyFactory.java @@ -92,7 +92,7 @@ public static LogPruneStrategy fromConfigValue( FileSystemAbstraction fileSystem case "true": return NO_PRUNING; case "false": - final TransactionCountThreshold thresholdToUse = new TransactionCountThreshold( 1 ); + final EntryCountThreshold thresholdToUse = new EntryCountThreshold( 1 ); return new ThresholdBasedPruneStrategy( fileSystem, logFileInformation, files, thresholdToUse ); default: throw new IllegalArgumentException( "Invalid log pruning configuration value '" + configValue + @@ -121,17 +121,18 @@ static Threshold getThresholdByType( FileSystemAbstraction fileSystem, String ty thresholdToUse = new FileSizeThreshold( fileSystem, thresholdValue ); break; case "txs": - thresholdToUse = new TransactionCountThreshold( thresholdValue ); + case "entries": // txs and entries are synonyms + thresholdToUse = new EntryCountThreshold( thresholdValue ); break; case "hours": - thresholdToUse = new TransactionTimespanThreshold( Clock.SYSTEM_CLOCK, TimeUnit.HOURS, thresholdValue ); + thresholdToUse = new EntryTimespanThreshold( Clock.SYSTEM_CLOCK, TimeUnit.HOURS, thresholdValue ); break; case "days": - thresholdToUse = new TransactionTimespanThreshold( Clock.SYSTEM_CLOCK, TimeUnit.DAYS, thresholdValue ); + thresholdToUse = new EntryTimespanThreshold( Clock.SYSTEM_CLOCK, TimeUnit.DAYS, thresholdValue ); break; default: throw new IllegalArgumentException( "Invalid log pruning configuration value '" + originalConfigValue + - "'. Invalid type '" + type + "', valid are files, size, txs, hours, days." ); + "'. Invalid type '" + type + "', valid are files, size, txs, entries, hours, days." ); } return thresholdToUse; } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/rotation/LogRotation.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/rotation/LogRotation.java index dd78c27042624..3e20f91cfc318 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/rotation/LogRotation.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/log/rotation/LogRotation.java @@ -53,6 +53,7 @@ public void rotateLogFile() throws IOException }; /** + * Rotates the undelying log if it is required. Returns true if rotation happened, false otherwise * @param logAppendEvent A trace event for the current log append operation. */ boolean rotateLogIfNeeded( LogAppendEvent logAppendEvent ) throws IOException; diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/PhysicalLogFileInformationTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/PhysicalLogFileInformationTest.java index 3bc07c6ce0103..971dd4c992feb 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/PhysicalLogFileInformationTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/PhysicalLogFileInformationTest.java @@ -45,7 +45,7 @@ public class PhysicalLogFileInformationTest @Test public void shouldReadAndCacheFirstCommittedTransactionIdForAGivenVersionWhenNotCached() throws Exception { - PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore, + PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore::getLastCommittedTransactionId, logVersionToTimestamp ); long expected = 5; @@ -56,7 +56,7 @@ public void shouldReadAndCacheFirstCommittedTransactionIdForAGivenVersionWhenNot new LogHeader( (byte) -1/*ignored*/, -1L/*ignored*/, expected - 1L ) ); - long firstCommittedTxId = info.getFirstCommittedTxId( version ); + long firstCommittedTxId = info.getFirstEntryId( version ); assertEquals( expected, firstCommittedTxId ); verify( logHeaderCache, times( 1 ) ).putHeader( version, expected - 1 ); } @@ -64,21 +64,21 @@ public void shouldReadAndCacheFirstCommittedTransactionIdForAGivenVersionWhenNot @Test public void shouldReadFirstCommittedTransactionIdForAGivenVersionWhenCached() throws Exception { - PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore, + PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore::getLastCommittedTransactionId, logVersionToTimestamp ); long expected = 5; long version = 10L; when( logHeaderCache.getLogHeader( version ) ).thenReturn( expected - 1 ); - long firstCommittedTxId = info.getFirstCommittedTxId( version ); + long firstCommittedTxId = info.getFirstEntryId( version ); assertEquals( expected, firstCommittedTxId ); } @Test public void shouldReadAndCacheFirstCommittedTransactionIdWhenNotCached() throws Exception { - PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore, + PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore::getLastCommittedTransactionId, logVersionToTimestamp ); long expected = 5; @@ -91,7 +91,7 @@ public void shouldReadAndCacheFirstCommittedTransactionIdWhenNotCached() throws ); when( logFiles.hasAnyEntries( version ) ).thenReturn( true ); - long firstCommittedTxId = info.getFirstExistingTxId(); + long firstCommittedTxId = info.getFirstExistingEntryId(); assertEquals( expected, firstCommittedTxId ); verify( logHeaderCache, times( 1 ) ).putHeader( version, expected - 1 ); } @@ -99,7 +99,7 @@ public void shouldReadAndCacheFirstCommittedTransactionIdWhenNotCached() throws @Test public void shouldReadFirstCommittedTransactionIdWhenCached() throws Exception { - PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore, + PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore::getLastCommittedTransactionId, logVersionToTimestamp ); long expected = 5; @@ -109,21 +109,21 @@ public void shouldReadFirstCommittedTransactionIdWhenCached() throws Exception when( logHeaderCache.getLogHeader( version ) ).thenReturn( expected -1 ); when( logFiles.hasAnyEntries( version ) ).thenReturn( true ); - long firstCommittedTxId = info.getFirstExistingTxId(); + long firstCommittedTxId = info.getFirstExistingEntryId(); assertEquals( expected, firstCommittedTxId ); } @Test public void shouldReturnNothingWhenThereAreNoTransactions() throws Exception { - PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore, + PhysicalLogFileInformation info = new PhysicalLogFileInformation( logFiles, logHeaderCache, transactionIdStore::getLastCommittedTransactionId, logVersionToTimestamp ); long version = 10L; when( logFiles.getHighestLogVersion() ).thenReturn( version ); when( logFiles.hasAnyEntries( version ) ).thenReturn( false ); - long firstCommittedTxId = info.getFirstExistingTxId(); + long firstCommittedTxId = info.getFirstExistingEntryId(); assertEquals( -1, firstCommittedTxId ); } } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/ReadTransactionLogWritingTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/ReadTransactionLogWritingTest.java index 44a260950815f..c6a6f030e2c27 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/ReadTransactionLogWritingTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/ReadTransactionLogWritingTest.java @@ -102,7 +102,7 @@ private long countLogEntries() filterNeostoreLogicalLog( fs, storeDir.getPath(), logicalLogCounter ); long txLogRecordCount = db.getDependencyResolver() - .resolveDependency( LogFileInformation.class ).getLastCommittedTxId(); + .resolveDependency( LogFileInformation.class ).getLastEntryId(); return logicalLogCounter.getCount() + txLogRecordCount; } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryCountThresholdTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryCountThresholdTest.java new file mode 100644 index 0000000000000..80634664e86d7 --- /dev/null +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryCountThresholdTest.java @@ -0,0 +1,217 @@ +/* + * 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.transaction.log.pruning; + +import org.junit.Test; + +import java.io.File; + +import org.neo4j.kernel.impl.transaction.log.IllegalLogFormatException; +import org.neo4j.kernel.impl.transaction.log.LogFileInformation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class EntryCountThresholdTest +{ + private LogFileInformation info = mock( LogFileInformation.class ); + private File file = mock( File.class ); + + @Test + public void shouldReportThresholdReachedWhenThresholdIsReached() throws Exception + { + long version = 10L; + + when( info.getFirstEntryId( version + 1 ) ).thenReturn( 1L ); + when( info.getLastEntryId() ).thenReturn( 2L ); + + EntryCountThreshold threshold = new EntryCountThreshold( 1 ); + boolean reached = threshold.reached( file, version, info ); + + assertTrue( reached ); + } + + @Test + public void shouldReportThresholdNotReachedWhenThresholdIsNotReached() throws Exception + { + long version = 10L; + + when( info.getFirstEntryId( version ) ).thenReturn( 1L ); + when( info.getFirstEntryId( version + 1 ) ).thenReturn( 1L ); + + when( info.getLastEntryId() ).thenReturn( 1L ); + + EntryCountThreshold threshold = new EntryCountThreshold( 1 ); + + assertFalse( threshold.reached( file, version, info ) ); + } + + @Test + public void shouldProperlyHandleCaseWithOneEntryPerLogFile() throws Exception + { + // Given 2 files with one entry each + when( info.getFirstEntryId( 1L ) ).thenReturn( 1L ); + when( info.getFirstEntryId( 2L ) ).thenReturn( 2L ); + when( info.getFirstEntryId( 3L ) ).thenReturn( 3L ); + + when( info.getLastEntryId() ).thenReturn( 3L ); + + // When the threshold is 1 entries + EntryCountThreshold threshold = new EntryCountThreshold( 1 ); + + // Then the last file should be kept around + assertFalse( threshold.reached( file, 2L, info ) ); + assertTrue( threshold.reached( file, 1L, info ) ); + } + + @Test + public void shouldReturnTrueWhenLogFormatVersionIsOlderThanTheRequiredOne() throws Exception + { + long version = 10L; + + when( info.getFirstEntryId( version + 1 ) ).thenThrow( new IllegalLogFormatException( 9L, 8L ) ); + + EntryCountThreshold threshold = new EntryCountThreshold( 2 ); + boolean reached = threshold.reached( file, version, info ); + + assertTrue( reached ); + } + + @Test + public void shouldThrowExceptionWhenLogFormatVersionIsNewerThanTheRequiredOne() throws Exception + { + long version = 10L; + + when( info.getFirstEntryId( version + 1 ) ).thenThrow( new IllegalLogFormatException( 9L, 11L ) ); + + EntryCountThreshold threshold = new EntryCountThreshold( 2 ); + try + { + threshold.reached( file, version, info ); + fail( "should have thrown IllegalLogFormatException" ); + } + catch ( RuntimeException e ) + { + assertTrue( e.getCause() instanceof IllegalLogFormatException ); + assertTrue( ((IllegalLogFormatException) e.getCause()).wasNewerLogVersion() ); + } + } + + @Test + public void shouldWorkWhenCalledMultipleTimesKeeping2Files() throws Exception + { + when( info.getFirstEntryId( 1L ) ).thenReturn( 1L ); + when( info.getFirstEntryId( 2L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 3L ) ).thenReturn( 15L ); + when( info.getFirstEntryId( 4L ) ).thenReturn( 18L ); + when( info.getLastEntryId() ).thenReturn( 18L ); + + EntryCountThreshold threshold = new EntryCountThreshold( 8 ); + + assertTrue( threshold.reached( file, 1L, info ) ); + + assertFalse( threshold.reached( file, 2L, info ) ); + + assertFalse( threshold.reached( file, 3L, info ) ); + } + + @Test + public void shouldWorkWhenCalledMultipleTimesKeeping3Files() throws Exception + { + when( info.getFirstEntryId( 1L ) ).thenReturn( 1L ); + when( info.getFirstEntryId( 2L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 3L ) ).thenReturn( 15L ); + when( info.getFirstEntryId( 4L ) ).thenReturn( 18L ); + when( info.getLastEntryId() ).thenReturn( 18L ); + + EntryCountThreshold threshold = new EntryCountThreshold( 15 ); + + assertFalse( threshold.reached( file, 1L, info ) ); + + assertFalse( threshold.reached( file, 2L, info ) ); + + assertFalse( threshold.reached( file, 3L, info ) ); + } + + @Test + public void shouldWorkWhenCalledMultipleTimesKeeping1FileOnBoundary() throws Exception + { + when( info.getFirstEntryId( 1L ) ).thenReturn( 1L ); + when( info.getFirstEntryId( 2L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 3L ) ).thenReturn( 15L ); + when( info.getFirstEntryId( 4L ) ).thenReturn( 18L ); + when( info.getLastEntryId() ).thenReturn( 18L ); + + EntryCountThreshold threshold = new EntryCountThreshold( 3 ); + + assertTrue( threshold.reached( file, 1L, info ) ); + assertTrue( threshold.reached( file, 2L, info ) ); + assertFalse( threshold.reached( file, 3L, info ) ); + } + + @Test + public void shouldSkipEmptyLogsBetweenLogsThatWillBeKept() throws Exception + { + // Given + // 1, 3 and 4 are empty. 2 has 5 transactions, 5 has 8, 6 is the current version + when( info.getFirstEntryId( 1L ) ).thenReturn( 1L ); + when( info.getFirstEntryId( 2L ) ).thenReturn( 1L ); + when( info.getFirstEntryId( 3L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 4L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 5L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 6L ) ).thenReturn( 13L ); + when( info.getLastEntryId() ).thenReturn( 13L ); + + // The threshold is 9, which is one more than what version 5 has, which means 2 should be kept + EntryCountThreshold threshold = new EntryCountThreshold( 9 ); + + assertFalse( threshold.reached( file, 5L, info ) ); + assertFalse( threshold.reached( file, 4L, info ) ); + assertFalse( threshold.reached( file, 3L, info ) ); + assertFalse( threshold.reached( file, 2L, info ) ); + assertTrue( threshold.reached( file, 1L, info ) ); + } + + @Test + public void shouldDeleteNonEmptyLogThatIsAfterASeriesOfEmptyLogs() throws Exception + { + // Given + // 1, 3 and 4 are empty. 2 has 5 transactions, 5 has 8, 6 is the current version + when( info.getFirstEntryId( 1L ) ).thenReturn( 1L ); + when( info.getFirstEntryId( 2L ) ).thenReturn( 1L ); + when( info.getFirstEntryId( 3L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 4L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 5L ) ).thenReturn( 5L ); + when( info.getFirstEntryId( 6L ) ).thenReturn( 13L ); + when( info.getLastEntryId() ).thenReturn( 13L ); + + // The threshold is 8, which is exactly what version 5 has, which means 2 should be deleted + EntryCountThreshold threshold = new EntryCountThreshold( 8 ); + + assertFalse( threshold.reached( file, 5L, info ) ); + assertTrue( threshold.reached( file, 4L, info ) ); + assertTrue( threshold.reached( file, 3L, info ) ); + assertTrue( threshold.reached( file, 2L, info ) ); + assertTrue( threshold.reached( file, 1L, info ) ); + } +} diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionTimespanThresholdTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryTimespanThresholdTest.java similarity index 84% rename from community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionTimespanThresholdTest.java rename to community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryTimespanThresholdTest.java index f2e1d5c071788..42c251d595b29 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionTimespanThresholdTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/EntryTimespanThresholdTest.java @@ -36,7 +36,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class TransactionTimespanThresholdTest +public class EntryTimespanThresholdTest { private final File file = mock( File.class ); private final LogFileInformation source = mock( LogFileInformation.class ); @@ -47,8 +47,8 @@ public void shouldReturnFalseWhenTimeIsEqualOrAfterTheLowerLimit() throws IOExce { // given FrozenClock clock = new FrozenClock( 1000l, TimeUnit.MILLISECONDS ); - final TransactionTimespanThreshold threshold = - new TransactionTimespanThreshold( clock, TimeUnit.MILLISECONDS, 200 ); + final EntryTimespanThreshold threshold = + new EntryTimespanThreshold( clock, TimeUnit.MILLISECONDS, 200 ); when( source.getFirstStartRecordTimestamp( version ) ).thenReturn( 800l ); @@ -65,8 +65,8 @@ public void shouldReturnReturnWhenTimeIsBeforeTheLowerLimit() throws IOException { // given FrozenClock clock = new FrozenClock( 1000l, TimeUnit.MILLISECONDS ); - final TransactionTimespanThreshold threshold = - new TransactionTimespanThreshold( clock, TimeUnit.MILLISECONDS, 100 ); + final EntryTimespanThreshold threshold = + new EntryTimespanThreshold( clock, TimeUnit.MILLISECONDS, 100 ); when( source.getFirstStartRecordTimestamp( version ) ).thenReturn( 800l ); @@ -83,8 +83,8 @@ public void shouldReturnTrueIfTheLogHasAnOlderVersion() throws IOException { // given FrozenClock clock = new FrozenClock( 1000l, TimeUnit.MILLISECONDS ); - final TransactionTimespanThreshold threshold = - new TransactionTimespanThreshold( clock, TimeUnit.MILLISECONDS, 100 ); + final EntryTimespanThreshold threshold = + new EntryTimespanThreshold( clock, TimeUnit.MILLISECONDS, 100 ); when( source.getFirstStartRecordTimestamp( version ) ).thenThrow( new IllegalLogFormatException( version, 3 ) ); @@ -101,8 +101,8 @@ public void shouldThrowIfTheLogHasANewerVersion() throws IOException { // given FrozenClock clock = new FrozenClock( 1000l, TimeUnit.MILLISECONDS ); - final TransactionTimespanThreshold threshold = - new TransactionTimespanThreshold( clock, TimeUnit.MILLISECONDS, 100 ); + final EntryTimespanThreshold threshold = + new EntryTimespanThreshold( clock, TimeUnit.MILLISECONDS, 100 ); final IllegalLogFormatException ex = new IllegalLogFormatException( version, 5 ); when( source.getFirstStartRecordTimestamp( version ) ).thenThrow( ex ); @@ -123,8 +123,8 @@ public void shouldThrowIfTheLogCannotBeRead() throws IOException { // given FrozenClock clock = new FrozenClock( 1000l, TimeUnit.MILLISECONDS ); - final TransactionTimespanThreshold threshold = - new TransactionTimespanThreshold( clock, TimeUnit.MILLISECONDS, 100 ); + final EntryTimespanThreshold threshold = + new EntryTimespanThreshold( clock, TimeUnit.MILLISECONDS, 100 ); final IOException ex = new IOException( ); when( source.getFirstStartRecordTimestamp( version ) ).thenThrow( ex ); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruneStrategyFactoryTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruneStrategyFactoryTest.java index 8bb72669d412f..7b77d2af5f33c 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruneStrategyFactoryTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/LogPruneStrategyFactoryTest.java @@ -29,16 +29,16 @@ public class LogPruneStrategyFactoryTest { - @Test public void testLogPruneThresholdsByType() throws Exception { assertThat( getPruneStrategy( "files", "25", "25 files" ), instanceOf( FileCountThreshold.class ) ); assertThat( getPruneStrategy( "size", "16G", "16G size" ), instanceOf( FileSizeThreshold.class ) ); - assertThat( getPruneStrategy( "txs", "4G", "4G txs" ), instanceOf( TransactionCountThreshold.class ) ); - assertThat( getPruneStrategy( "hours", "100", "100 hours" ), instanceOf( TransactionTimespanThreshold.class ) ); + assertThat( getPruneStrategy( "txs", "4G", "4G txs" ), instanceOf( EntryCountThreshold.class ) ); + assertThat( getPruneStrategy( "entries", "4G", "4G entries" ), instanceOf( EntryCountThreshold.class ) ); + assertThat( getPruneStrategy( "hours", "100", "100 hours" ), instanceOf( EntryTimespanThreshold.class ) ); assertThat( getPruneStrategy( "days", "100k", "100k days" ), - instanceOf( TransactionTimespanThreshold.class) ); + instanceOf( EntryTimespanThreshold.class) ); } private Threshold getPruneStrategy(String type, String value, String configValue) diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionCountThresholdTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionCountThresholdTest.java deleted file mode 100644 index fbaccad90635e..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/log/pruning/TransactionCountThresholdTest.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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.transaction.log.pruning; - -import org.junit.Test; - -import java.io.File; - -import org.neo4j.kernel.impl.transaction.log.IllegalLogFormatException; -import org.neo4j.kernel.impl.transaction.log.LogFileInformation; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class TransactionCountThresholdTest -{ - private LogFileInformation info = mock( LogFileInformation.class ); - private File file = mock( File.class ); - - @Test - public void shouldReportThresholdReachedWhenThresholdIsReached() throws Exception - { - long version = 10l; - - when( info.getFirstCommittedTxId( version + 1 ) ).thenReturn( 1l ); - when( info.getLastCommittedTxId() ).thenReturn( 2l ); - - TransactionCountThreshold threshold = new TransactionCountThreshold( 1 ); - boolean reached = threshold.reached( file, version, info ); - - assertTrue( reached ); - } - - @Test - public void shouldReportThresholdNotReachedWhenThresholdIsNotReached() throws Exception - { - long version = 10l; - - when( info.getFirstCommittedTxId( version + 1 ) ).thenReturn( 1l ); - when( info.getLastCommittedTxId() ).thenReturn( 2l ); - - TransactionCountThreshold threshold = new TransactionCountThreshold( 2 ); - boolean reached = threshold.reached( file, version, info ); - - assertFalse( reached ); - } - - @Test - public void shouldReturnTrueWhenLogFormatVersionIsOlderThanTheRequiredOne() throws Exception - { - long version = 10l; - - when( info.getFirstCommittedTxId( version + 1 ) ).thenThrow( new IllegalLogFormatException( 9l, 8l ) ); - - TransactionCountThreshold threshold = new TransactionCountThreshold( 2 ); - boolean reached = threshold.reached( file, version, info ); - - assertTrue( reached ); - } - - @Test - public void shouldThrowExceptionWhenLogFormatVersionIsNewerThanTheRequiredOne() throws Exception - { - long version = 10l; - - when( info.getFirstCommittedTxId( version + 1 ) ).thenThrow( new IllegalLogFormatException( 9l, 11l ) ); - - TransactionCountThreshold threshold = new TransactionCountThreshold( 2 ); - try - { - threshold.reached( file, version, info ); - fail( "should have thrown IllegalLogFormatException" ); - } - catch ( RuntimeException e ) - { - assertTrue( e.getCause() instanceof IllegalLogFormatException ); - assertTrue( ((IllegalLogFormatException) e.getCause()).wasNewerLogVersion() ); - } - } - - @Test - public void shouldWorkWhenCalledMultipleTimesKeeping2Files() throws Exception - { - when( info.getFirstCommittedTxId( 1l ) ).thenReturn( 1l ); - when( info.getFirstCommittedTxId( 2l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 3l ) ).thenReturn( 15l ); - when( info.getFirstCommittedTxId( 4l ) ).thenReturn( 18l ); - when( info.getLastCommittedTxId() ).thenReturn( 18l ); - - TransactionCountThreshold threshold = new TransactionCountThreshold( 8 ); - - assertTrue( threshold.reached( file, 1l, info ) ); - - assertFalse( threshold.reached( file, 2l, info ) ); - - assertFalse( threshold.reached( file, 3l, info ) ); - } - - @Test - public void shouldWorkWhenCalledMultipleTimesKeeping3Files() throws Exception - { - when( info.getFirstCommittedTxId( 1l ) ).thenReturn( 1l ); - when( info.getFirstCommittedTxId( 2l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 3l ) ).thenReturn( 15l ); - when( info.getFirstCommittedTxId( 4l ) ).thenReturn( 18l ); - when( info.getLastCommittedTxId() ).thenReturn( 18l ); - - TransactionCountThreshold threshold = new TransactionCountThreshold( 15 ); - - assertFalse( threshold.reached( file, 1l, info ) ); - - assertFalse( threshold.reached( file, 2l, info ) ); - - assertFalse( threshold.reached( file, 3l, info ) ); - } - - @Test - public void shouldWorkWhenCalledMultipleTimesKeeping1FileOnBoundary() throws Exception - { - when( info.getFirstCommittedTxId( 1l ) ).thenReturn( 1l ); - when( info.getFirstCommittedTxId( 2l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 3l ) ).thenReturn( 15l ); - when( info.getFirstCommittedTxId( 4l ) ).thenReturn( 18l ); - when( info.getLastCommittedTxId() ).thenReturn( 18l ); - - TransactionCountThreshold threshold = new TransactionCountThreshold( 3 ); - - assertTrue( threshold.reached( file, 1l, info ) ); - - assertTrue( threshold.reached( file, 2l, info ) ); - - assertFalse( threshold.reached( file, 3l, info ) ); - } - - @Test - public void shouldSkipEmptyLogsBetweenLogsThatWillBeKept() throws Exception - { - // Given - // 1, 3 and 4 are empty. 2 has 5 transactions, 5 has 8, 6 is the current version - when( info.getFirstCommittedTxId( 1l ) ).thenReturn( 1l ); - when( info.getFirstCommittedTxId( 2l ) ).thenReturn( 1l ); - when( info.getFirstCommittedTxId( 3l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 4l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 5l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 6l ) ).thenReturn( 13l ); - when( info.getLastCommittedTxId() ).thenReturn( 13l ); - - // The threshold is 9, which is one more than what version 5 has, which means 2 should be kept - TransactionCountThreshold threshold = new TransactionCountThreshold( 9 ); - - assertFalse( threshold.reached( file, 5l, info ) ); - assertFalse( threshold.reached( file, 4l, info ) ); - assertFalse( threshold.reached( file, 3l, info ) ); - assertFalse( threshold.reached( file, 2l, info ) ); - assertTrue( threshold.reached( file, 1l, info ) ); - } - - @Test - public void shouldDeleteNonEmptyLogThatIsAfterASeriesOfEmptyLogs() throws Exception - { - // Given - // 1, 3 and 4 are empty. 2 has 5 transactions, 5 has 8, 6 is the current version - when( info.getFirstCommittedTxId( 1l ) ).thenReturn( 1l ); - when( info.getFirstCommittedTxId( 2l ) ).thenReturn( 1l ); - when( info.getFirstCommittedTxId( 3l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 4l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 5l ) ).thenReturn( 5l ); - when( info.getFirstCommittedTxId( 6l ) ).thenReturn( 13l ); - when( info.getLastCommittedTxId() ).thenReturn( 13l ); - - // The threshold is 8, which is exactly what version 5 has, which means 2 should be deleted - TransactionCountThreshold threshold = new TransactionCountThreshold( 8 ); - - assertFalse( threshold.reached( file, 5l, info ) ); - assertTrue( threshold.reached( file, 4l, info ) ); - assertTrue( threshold.reached( file, 3l, info ) ); - assertTrue( threshold.reached( file, 2l, info ) ); - assertTrue( threshold.reached( file, 1l, info ) ); - } -} diff --git a/enterprise/backup/src/main/java/org/neo4j/backup/StoreCopyResponsePacker.java b/enterprise/backup/src/main/java/org/neo4j/backup/StoreCopyResponsePacker.java index 2f1e80301c2ac..fbdf75d8aab1f 100644 --- a/enterprise/backup/src/main/java/org/neo4j/backup/StoreCopyResponsePacker.java +++ b/enterprise/backup/src/main/java/org/neo4j/backup/StoreCopyResponsePacker.java @@ -96,7 +96,7 @@ protected void extractTransactions( long startingAtTransactionId, { // We don't necessarily need to ask that far back. Ask which is the oldest transaction in the log(s) // that we can possibly serve - long oldestExistingTransactionId = logFileInformation.getFirstExistingTxId(); + long oldestExistingTransactionId = logFileInformation.getFirstExistingEntryId(); if ( oldestExistingTransactionId == -1 ) { // Seriously, there are no logs that we can serve?