Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReader.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReader.java (revision 1c4b5a0c7902be04dc1962f0bd05a81f81fbceb2) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReader.java (revision ) @@ -19,36 +19,56 @@ package org.apache.jackrabbit.oak.plugins.segment.file; -import java.io.DataInput; +import java.io.Closeable; +import java.io.File; import java.io.IOException; -import java.util.LinkedList; +import java.util.Iterator; -import com.google.common.collect.Lists; +import com.google.common.collect.AbstractIterator; +import org.apache.commons.io.input.ReversedLinesFileReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Reader for journal files of the SegmentMK. */ -public final class JournalReader { +public final class JournalReader implements Closeable, Iterable { + private static final Logger LOG = LoggerFactory.getLogger(JournalReader.class); - private JournalReader() { } + private final ReversedLinesFileReader journal; + public JournalReader(File journalFile) throws IOException { + journal = new ReversedLinesFileReader(journalFile); + } + /** - * Read a journal file - * @param journal name of the journal file - * @return list of revisions listed in the journal. Oldest revision first. - * @throws IOException + * @return Iterator over the revisions in the journal in reverse order + * (end of the file to beginning). */ - public static LinkedList heads(DataInput journal) throws IOException { - LinkedList heads = Lists.newLinkedList(); + @Override + public Iterator iterator() { + return new AbstractIterator() { + @Override + protected String computeNext() { + try { - String line = journal.readLine(); - while (line != null) { - int space = line.indexOf(' '); - if (space != -1) { + String line = journal.readLine(); + while (line != null) { + int space = line.indexOf(' '); + if (space != -1) { - heads.add(line.substring(0, space)); + return line.substring(0, space); - } - line = journal.readLine(); - } + } + line = journal.readLine(); + } - return heads; + } catch (IOException e) { + LOG.error("Error reading journal file", e); - } + } + return endOfData(); + } + }; + } + @Override + public void close() throws IOException { + journal.close(); + } } Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java (revision 1c4b5a0c7902be04dc1962f0bd05a81f81fbceb2) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/FileStore.java (revision ) @@ -186,8 +186,8 @@ this.maxFileSize = maxFileSizeMB * MB; this.memoryMapping = memoryMapping; - journalFile = new RandomAccessFile( - new File(directory, JOURNAL_FILE_NAME), "rw"); + journalFile = new RandomAccessFile(new File(directory, JOURNAL_FILE_NAME), "rw"); + journalFile.seek(journalFile.length()); journalLock = journalFile.getChannel().lock(); Map> map = collectFiles(directory); @@ -208,18 +208,23 @@ String.format(FILE_NAME_FORMAT, writeNumber, "a")); this.writer = new TarWriter(writeFile); - LinkedList heads = JournalReader.heads(journalFile); RecordId id = null; - while (id == null && !heads.isEmpty()) { - RecordId last = RecordId.fromString(tracker, heads.removeLast()); + JournalReader journalReader = new JournalReader(new File(directory, JOURNAL_FILE_NAME)); + try { + Iterator heads = journalReader.iterator(); + while (id == null && heads.hasNext()) { + RecordId last = RecordId.fromString(tracker, heads.next()); - SegmentId segmentId = last.getSegmentId(); - if (containsSegment( - segmentId.getMostSignificantBits(), - segmentId.getLeastSignificantBits())) { - id = last; - } else { - log.warn("Unable to access revision {}, rewinding...", last); - } + SegmentId segmentId = last.getSegmentId(); + if (containsSegment( + segmentId.getMostSignificantBits(), + segmentId.getLeastSignificantBits())) { + id = last; + } else { + log.warn("Unable to access revision {}, rewinding...", last); + } + } + } finally { + journalReader.close(); } if (id != null) { Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/ConsistencyChecker.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/ConsistencyChecker.java (revision 1c4b5a0c7902be04dc1962f0bd05a81f81fbceb2) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/file/tooling/ConsistencyChecker.java (revision ) @@ -19,7 +19,6 @@ package org.apache.jackrabbit.oak.plugins.segment.file.tooling; -import static com.google.common.collect.Lists.reverse; import static com.google.common.collect.Sets.newHashSet; import static org.apache.jackrabbit.oak.commons.PathUtils.concat; import static org.apache.jackrabbit.oak.commons.PathUtils.denotesRoot; @@ -29,8 +28,6 @@ import java.io.File; import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.List; import java.util.Set; import org.apache.jackrabbit.oak.api.PropertyState; @@ -67,22 +64,13 @@ */ public static String checkConsistency(File directory, String journalFileName, boolean fullTraversal, long debugInterval) throws IOException { - RandomAccessFile journalFile = new RandomAccessFile( - new File(directory, journalFileName), "r"); - - List revisions; - try { - revisions = reverse(JournalReader.heads(journalFile)); - } finally { - journalFile.close(); - } - print("Searching for last good revision in {}", journalFileName); + JournalReader journal = new JournalReader(new File(directory, journalFileName)); Set badPaths = newHashSet(); ConsistencyChecker checker = new ConsistencyChecker(directory, debugInterval); try { int revisionCount = 0; - for (String revision : revisions) { + for (String revision : journal) { print("Checking revision {}", revision); revisionCount++; String badPath = checker.check(revision, badPaths); @@ -91,7 +79,7 @@ } if (badPath == null) { print("Found latest good revision {}", revision); - print("Searched through {} of {} revisions", revisionCount, revisions.size()); + print("Searched through {} revisions", revisionCount); return revision; } else { badPaths.add(badPath); @@ -100,6 +88,7 @@ } } finally { checker.close(); + journal.close(); } print("No good revision found"); Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReaderTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReaderTest.java (revision 1c4b5a0c7902be04dc1962f0bd05a81f81fbceb2) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/JournalReaderTest.java (revision ) @@ -19,13 +19,15 @@ package org.apache.jackrabbit.oak.plugins.segment.file; -import static com.google.common.io.ByteStreams.newDataInput; +import static java.io.File.createTempFile; +import static org.apache.commons.io.FileUtils.write; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import java.io.DataInput; +import java.io.File; import java.io.IOException; -import java.util.LinkedList; +import java.util.Iterator; import org.junit.Test; @@ -33,49 +35,82 @@ @Test public void testEmpty() throws IOException { - assertTrue(JournalReader.heads(getJournal("")).isEmpty()); + JournalReader journalReader = createJournalReader(""); + try { + assertFalse(journalReader.iterator().hasNext()); + } finally { + journalReader.close(); - } + } + } @Test public void testSingleton() throws IOException { - String journal = "one 1"; - LinkedList heads = JournalReader.heads(getJournal(journal)); - assertEquals(1, heads.size()); - assertEquals("one", heads.get(0)); + JournalReader journalReader = createJournalReader("one 1"); + try { + Iterator journal = journalReader.iterator(); + assertTrue(journal.hasNext()); + assertEquals("one", journal.next()); + assertFalse(journal.hasNext()); + } finally { + journalReader.close(); - } + } + } @Test public void testMultiple() throws IOException { - String journal = "one 1\ntwo 2\nthree 3"; - LinkedList heads = JournalReader.heads(getJournal(journal)); - assertEquals(3, heads.size()); - assertEquals("one", heads.get(0)); - assertEquals("two", heads.get(1)); - assertEquals("three", heads.get(2)); + JournalReader journalReader = createJournalReader("one 1\ntwo 2\nthree 3"); + try { + Iterator journal = journalReader.iterator(); + assertTrue(journal.hasNext()); + assertEquals("three", journal.next()); + assertTrue(journal.hasNext()); + assertEquals("two", journal.next()); + assertTrue(journal.hasNext()); + assertEquals("one", journal.next()); + assertFalse(journal.hasNext()); + } finally { + journalReader.close(); - } + } + } @Test public void testSpaces() throws IOException { - String journal = "\n \n \n "; - LinkedList heads = JournalReader.heads(getJournal(journal)); - assertEquals(3, heads.size()); - assertEquals("", heads.get(0)); - assertEquals("", heads.get(1)); - assertEquals("", heads.get(2)); + JournalReader journalReader = createJournalReader("\n \n \n "); + try { + Iterator journal = journalReader.iterator(); + assertTrue(journal.hasNext()); + assertEquals("", journal.next()); + assertTrue(journal.hasNext()); + assertEquals("", journal.next()); + assertTrue(journal.hasNext()); + assertEquals("", journal.next()); + assertFalse(journal.hasNext()); + } finally { + journalReader.close(); - } + } + } @Test public void testIgnoreInvalid() throws IOException { - String journal = "one 1\ntwo 2\ninvalid\nthree 3"; - LinkedList heads = JournalReader.heads(getJournal(journal)); - assertEquals(3, heads.size()); - assertEquals("one", heads.get(0)); - assertEquals("two", heads.get(1)); - assertEquals("three", heads.get(2)); + JournalReader journalReader = createJournalReader("one 1\ntwo 2\ninvalid\nthree 3"); + try { + Iterator journal = journalReader.iterator(); + assertTrue(journal.hasNext()); + assertEquals("three", journal.next()); + assertTrue(journal.hasNext()); + assertEquals("two", journal.next()); + assertTrue(journal.hasNext()); + assertEquals("one", journal.next()); + assertFalse(journal.hasNext()); + } finally { + journalReader.close(); - } + } + } - private static DataInput getJournal(String s) { - return newDataInput(s.getBytes()); + private static JournalReader createJournalReader(String s) throws IOException { + File journalFile = createTempFile("jrt", null); + write(journalFile, s); + return new JournalReader(journalFile); } }