/** * @param directory * @param journalSizeThresholdInBytes Size of the current journal file beyond which it is closed * and a new one started. Zero indicates no size threshold. This is useful journal backup * purposes. * @param journalAgeThresholdInMillis Age of the current journal file beyond which it is closed * and a new one started. Zero indicates no age threshold. This is useful journal backup * purposes. */ public PersistentJournal( PrevaylerDirectory directory, long journalSizeThresholdInBytes, long journalAgeThresholdInMillis, String journalSuffix, Monitor monitor) throws IOException { PrevaylerDirectory.checkValidJournalSuffix(journalSuffix); _monitor = monitor; _directory = directory; _directory.produceDirectory(); _journalSizeThresholdInBytes = journalSizeThresholdInBytes; _journalAgeThresholdInMillis = journalAgeThresholdInMillis; _journalSuffix = journalSuffix; }
private long recoverPendingTransactions( TransactionSubscriber subscriber, long initialTransaction, File initialJournal) throws IOException { long recoveringTransaction = PrevaylerDirectory.journalVersion(initialJournal); File journal = initialJournal; DurableInputStream input = new DurableInputStream(journal, _monitor); while (true) { try { Chunk chunk = input.readChunk(); if (recoveringTransaction >= initialTransaction) { if (!journal.getName().endsWith(_journalSuffix)) { throw new IOException( "There are transactions needing to be recovered from " + journal + ", but only " + _journalSuffix + " files are supported"); } TransactionTimestamp entry = TransactionTimestamp.fromChunk(chunk); if (entry.systemVersion() != recoveringTransaction) { throw new IOException( "Expected " + recoveringTransaction + " but was " + entry.systemVersion()); } subscriber.receive(entry); } recoveringTransaction++; } catch (EOFException eof) { File nextFile = _directory.journalFile(recoveringTransaction, _journalSuffix); if (journal.equals(nextFile)) PrevaylerDirectory.renameUnusedFile( journal); // The first transaction in this log file is incomplete. We need to reuse // this file name. journal = nextFile; if (!journal.exists()) break; input = new DurableInputStream(journal, _monitor); } } return recoveringTransaction; }
private DurableOutputStream createOutputJournal(long transactionNumber) { File file = _directory.journalFile(transactionNumber, _journalSuffix); try { return new DurableOutputStream(file); } catch (IOException iox) { handle(iox, file, "creating"); return null; } }
/** * IMPORTANT: This method cannot be called while the log() method is being called in another * thread. If there are no journal files in the directory (when a snapshot is taken and all * journal files are manually deleted, for example), the initialTransaction parameter in the first * call to this method will define what the next transaction number will be. We have to find * clearer/simpler semantics. */ public void update(TransactionSubscriber subscriber, long initialTransactionWanted) throws IOException, ClassNotFoundException { File initialJournal = _directory.findInitialJournalFile(initialTransactionWanted); if (initialJournal == null) { initializeNextTransaction(initialTransactionWanted, 1); return; } long nextTransaction = recoverPendingTransactions(subscriber, initialTransactionWanted, initialJournal); initializeNextTransaction(initialTransactionWanted, nextTransaction); }