filesRepository.calculateNextfileID(orderedFiles);
int lastDataPos = JournalImpl.SIZE_HEADER;
final AtomicLong maxID = new AtomicLong(-1);
for (final JournalFile file : orderedFiles)
{
JournalImpl.trace("Loading file " + file.getFile().getFileName());
final AtomicBoolean hasData = new AtomicBoolean(false);
int resultLastPost = JournalImpl.readJournalFile(fileFactory, file, new JournalReaderCallback()
{
private void checkID(final long id)
{
if (id > maxID.longValue())
{
maxID.set(id);
}
}
public void onReadAddRecord(final RecordInfo info) throws Exception
{
checkID(info.id);
hasData.set(true);
loadManager.addRecord(info);
records.put(info.id, new JournalRecord(file, info.data.length + JournalImpl.SIZE_ADD_RECORD + 1));
}
public void onReadUpdateRecord(final RecordInfo info) throws Exception
{
checkID(info.id);
hasData.set(true);
loadManager.updateRecord(info);
JournalRecord posFiles = records.get(info.id);
if (posFiles != null)
{
// It's legal for this to be null. The file(s) with the may
// have been deleted
// just leaving some updates in this file
posFiles.addUpdateFile(file, info.data.length + JournalImpl.SIZE_ADD_RECORD + 1); // +1 = compact
// count
}
}
public void onReadDeleteRecord(final long recordID) throws Exception
{
hasData.set(true);
loadManager.deleteRecord(recordID);
JournalRecord posFiles = records.remove(recordID);
if (posFiles != null)
{
posFiles.delete(file);
}
}
public void onReadUpdateRecordTX(final long transactionID, final RecordInfo info) throws Exception
{
onReadAddRecordTX(transactionID, info);
}
public void onReadAddRecordTX(final long transactionID, final RecordInfo info) throws Exception
{
checkID(info.id);
hasData.set(true);
TransactionHolder tx = loadTransactions.get(transactionID);
if (tx == null)
{
tx = new TransactionHolder(transactionID);
loadTransactions.put(transactionID, tx);
}
tx.recordInfos.add(info);
JournalTransaction tnp = transactions.get(transactionID);
if (tnp == null)
{
tnp = new JournalTransaction(transactionID, JournalImpl.this);
transactions.put(transactionID, tnp);
}
tnp.addPositive(file, info.id, info.data.length + JournalImpl.SIZE_ADD_RECORD_TX + 1); // +1 = compact
// count
}
public void onReadDeleteRecordTX(final long transactionID, final RecordInfo info) throws Exception
{
hasData.set(true);
TransactionHolder tx = loadTransactions.get(transactionID);
if (tx == null)
{
tx = new TransactionHolder(transactionID);
loadTransactions.put(transactionID, tx);
}
tx.recordsToDelete.add(info);
JournalTransaction tnp = transactions.get(transactionID);
if (tnp == null)
{
tnp = new JournalTransaction(transactionID, JournalImpl.this);
transactions.put(transactionID, tnp);
}
tnp.addNegative(file, info.id);
}
public void onReadPrepareRecord(final long transactionID, final byte[] extraData, final int numberOfRecords) throws Exception
{
hasData.set(true);
TransactionHolder tx = loadTransactions.get(transactionID);
if (tx == null)
{
// The user could choose to prepare empty transactions
tx = new TransactionHolder(transactionID);
loadTransactions.put(transactionID, tx);
}
tx.prepared = true;
tx.extraData = extraData;
JournalTransaction journalTransaction = transactions.get(transactionID);
if (journalTransaction == null)
{
journalTransaction = new JournalTransaction(transactionID, JournalImpl.this);
transactions.put(transactionID, journalTransaction);
}
boolean healthy = checkTransactionHealth(file, journalTransaction, orderedFiles, numberOfRecords);
if (healthy)
{
journalTransaction.prepare(file);
}
else
{
JournalImpl.log.warn("Prepared transaction " + transactionID +
" wasn't considered completed, it will be ignored");
tx.invalid = true;
}
}
public void onReadCommitRecord(final long transactionID, final int numberOfRecords) throws Exception
{
TransactionHolder tx = loadTransactions.remove(transactionID);
// The commit could be alone on its own journal-file and the
// whole transaction body was reclaimed but not the
// commit-record
// So it is completely legal to not find a transaction at this
// point
// If we can't find it, we assume the TX was reclaimed and we
// ignore this
if (tx != null)
{
JournalTransaction journalTransaction = transactions.remove(transactionID);
if (journalTransaction == null)
{
throw new IllegalStateException("Cannot find tx " + transactionID);
}
boolean healthy = checkTransactionHealth(file, journalTransaction, orderedFiles, numberOfRecords);
if (healthy)
{
for (RecordInfo txRecord : tx.recordInfos)
{
if (txRecord.isUpdate)
{
loadManager.updateRecord(txRecord);
}
else
{
loadManager.addRecord(txRecord);
}
}
for (RecordInfo deleteValue : tx.recordsToDelete)
{
loadManager.deleteRecord(deleteValue.id);
}
journalTransaction.commit(file);
}
else
{
JournalImpl.log.warn("Transaction " + transactionID +
" is missing elements so the transaction is being ignored");
journalTransaction.forget();
}
hasData.set(true);
}
}
public void onReadRollbackRecord(final long transactionID) throws Exception
{
TransactionHolder tx = loadTransactions.remove(transactionID);
// The rollback could be alone on its own journal-file and the
// whole transaction body was reclaimed but the commit-record
// So it is completely legal to not find a transaction at this
// point
if (tx != null)
{
JournalTransaction tnp = transactions.remove(transactionID);
if (tnp == null)
{
throw new IllegalStateException("Cannot find tx " + transactionID);
}
// There is no need to validate summaries/holes on
// Rollbacks.. We will ignore the data anyway.
tnp.rollback(file);
hasData.set(true);
}
}
public void markAsDataFile(final JournalFile file)
{
hasData.set(true);
}
});
if (hasData.get())
{
lastDataPos = resultLastPost;
filesRepository.addDataFileOnBottom(file);
}
else
{
// Empty dataFiles with no data
filesRepository.addFreeFileNoInit(file);
}
}
// Create any more files we need
filesRepository.ensureMinFiles();
// The current file is the last one that has data
currentFile = filesRepository.pollLastDataFile();
if (currentFile != null)
{
currentFile.getFile().open();
currentFile.getFile().position(currentFile.getFile().calculateBlockStart(lastDataPos));
}
else
{
currentFile = filesRepository.getFreeFile();
filesRepository.openFile(currentFile, true);
}
fileFactory.activateBuffer(currentFile.getFile());
filesRepository.pushOpenedFile();
state = JournalImpl.STATE_LOADED;
for (TransactionHolder transaction : loadTransactions.values())
{
if (!transaction.prepared || transaction.invalid)
{
JournalImpl.log.warn("Uncommitted transaction with id " + transaction.transactionID +
" found and discarded");
if (fixFailingTransactions)
{
// I append a rollback record here, because otherwise compacting will be throwing messages because of unknown transactions
this.appendRollbackRecord(transaction.transactionID, false);
}
loadManager.failedTransaction(transaction.transactionID,
transaction.recordInfos,
transaction.recordsToDelete);
}
else
{
for (RecordInfo info : transaction.recordInfos)
{
if (info.id > maxID.get())
{
maxID.set(info.id);
}
}
PreparedTransactionInfo info = new PreparedTransactionInfo(transaction.transactionID, transaction.extraData);
info.records.addAll(transaction.recordInfos);
info.recordsToDelete.addAll(transaction.recordsToDelete);
loadManager.addPreparedTransaction(info);
}
}
checkReclaimStatus();
return new JournalLoadInformation(records.size(), maxID.longValue());
}