/// <summary> /// Returns event records in the reverse sequence they were committed into TF. /// Positions is specified as post-positions (pointer after the end of record). /// </summary> public ReadAllResult ReadAllEventsBackward(TFPos pos, int maxCount, bool resolveLinks) { var records = new List <ResolvedEventRecord>(); var nextPos = pos; // in case we are at position after which there is no commit at all, in that case we have to force // PreparePosition to 0, so if you decide to read backwards from PrevPos, // you will receive all prepares. var prevPos = new TFPos(pos.CommitPosition, 0); var count = 0; bool firstCommit = true; ITransactionFileSequentialReader seqReader = GetSeqReader(); try { long nextCommitPostPos = pos.CommitPosition; while (count < maxCount) { seqReader.Reposition(nextCommitPostPos); SeqReadResult result; do { result = seqReader.TryReadPrev(); }while (result.Success && result.LogRecord.RecordType != LogRecordType.Commit); // skip until commit if (!result.Success) // no more records in TF { break; } var commitPostPos = result.RecordPostPosition; nextCommitPostPos = result.RecordPrePosition; var commit = (CommitLogRecord)result.LogRecord; if (firstCommit) { firstCommit = false; // for forward pass we allow read the same commit and as we have post-positions here // we can put just prepare post-position as prepare pre-position for forward read // so we put pre-position of commit and post-position of prepare prevPos = new TFPos(commit.LogPosition, pos.PreparePosition); } // as we don't know exact position of the last record of transaction, // we have to sequentially scan backwards, so no need to reposition //seqReader.Reposition(commitLogRecord.TransactionPosition); while (count < maxCount) { result = seqReader.TryReadPrev(); if (!result.Success) // no more records in TF { break; } // prepare with TransactionBegin could be scavenged already // so we could reach beyond the start of transaction. In that case we have to stop. if (result.LogRecord.Position < commit.TransactionPosition) { break; } if (result.LogRecord.RecordType != LogRecordType.Prepare) { continue; } var prepare = (PrepareLogRecord)result.LogRecord; if (prepare.TransactionPosition != commit.TransactionPosition) // wrong prepare { continue; } if ((prepare.Flags & PrepareFlags.Data) != 0) // prepare with useful data { if (new TFPos(commitPostPos, result.RecordPostPosition) <= pos) { var eventRecord = new EventRecord(commit.EventNumber + prepare.TransactionOffset, prepare); EventRecord linkToEvent = null; if (resolveLinks) { var resolved = ResolveLinkToEvent(eventRecord); if (resolved != null) { linkToEvent = eventRecord; eventRecord = resolved; } } records.Add(new ResolvedEventRecord(eventRecord, linkToEvent, commit.Position)); count++; // for backward pass we allow read the same commit, but force to skip last read prepare // so we put post-position of commit and pre-position of prepare nextPos = new TFPos(commitPostPos, prepare.LogPosition); } } if ((prepare.Flags & PrepareFlags.TransactionBegin) != 0) { break; } } } } finally { ReturnSeqReader(seqReader); } return(new ReadAllResult(records, maxCount, pos, nextPos, prevPos)); }
/// <summary> /// Returns event records in the sequence they were committed into TF. /// Positions is specified as pre-positions (pointer at the beginning of the record). /// </summary> public ReadAllResult ReadAllEventsForward(TFPos pos, int maxCount, bool resolveLinks) { var records = new List <ResolvedEventRecord>(); var nextPos = pos; // in case we are at position after which there is no commit at all, in that case we have to force // PreparePosition to int.MaxValue, so if you decide to read backwards from PrevPos, // you will receive all prepares. var prevPos = new TFPos(pos.CommitPosition, int.MaxValue); var count = 0; bool firstCommit = true; ITransactionFileSequentialReader seqReader = GetSeqReader(); try { long nextCommitPos = pos.CommitPosition; while (count < maxCount) { seqReader.Reposition(nextCommitPos); SeqReadResult result; do { result = seqReader.TryReadNext(); }while (result.Success && result.LogRecord.RecordType != LogRecordType.Commit); // skip until commit if (!result.Success) // no more records in TF { break; } nextCommitPos = result.RecordPostPosition; var commit = (CommitLogRecord)result.LogRecord; if (firstCommit) { firstCommit = false; // for backward pass we want to allow read the same commit and skip read prepares, // so we put post-position of commit and post-position of prepare as TFPos for backward pass prevPos = new TFPos(result.RecordPostPosition, pos.PreparePosition); } seqReader.Reposition(commit.TransactionPosition); while (count < maxCount) { result = seqReader.TryReadNext(); if (!result.Success) // no more records in TF { break; } // prepare with TransactionEnd could be scavenged already // so we could reach the same commit record. In that case have to stop if (result.LogRecord.Position >= commit.Position) { break; } if (result.LogRecord.RecordType != LogRecordType.Prepare) { continue; } var prepare = (PrepareLogRecord)result.LogRecord; if (prepare.TransactionPosition != commit.TransactionPosition) // wrong prepare { continue; } if ((prepare.Flags & PrepareFlags.Data) != 0) // prepare with useful data { if (new TFPos(commit.Position, prepare.LogPosition) >= pos) { var eventRecord = new EventRecord(commit.EventNumber + prepare.TransactionOffset, prepare); EventRecord linkToEvent = null; if (resolveLinks) { var resolved = ResolveLinkToEvent(eventRecord); if (resolved != null) { linkToEvent = eventRecord; eventRecord = resolved; } } records.Add(new ResolvedEventRecord(eventRecord, linkToEvent, commit.Position)); count++; // for forward pass position is inclusive, // so we put pre-position of commit and post-position of prepare nextPos = new TFPos(commit.LogPosition, result.RecordPostPosition); } } if ((prepare.Flags & PrepareFlags.TransactionEnd) != 0) { break; } } } } finally { ReturnSeqReader(seqReader); } return(new ReadAllResult(records, maxCount, pos, nextPos, prevPos)); }