Skip to content

Replaying

ReplaySession (REPLAY state)

Continuing from the previous page, once the ReplaySession is in the REPLAY state, whenever the Replayer calls doWork() on it, if the Replay Publication can accept more data, more Fragments are published to it from the Segment file.

The diagram below is the opposite of the Recording diagram. The Log Buffer at the top is for the Exclusive Publication that the ReplaySession will be publishing Fragments to. When it was created during StartReplay, the metadata would have been set to the correct values, based on the RecordingDescriptor in the Catalog and information in the replay request. For example, the Tail Counter for the Term where the next message will be written to. The two Recording Segments below it are as they were at the end of the Recording diagram. Let's replay this Recording from the start.

TermLength: 2 MB Metadata: InitialTermId: 1005 ActiveTermCount: 0 TermId: 1004 TermId: 1005 TermId: 1003 Term 2 Term 1 Term 0 4 MB 6 MB 8 MB 0-4194304.rec TermId: 1007 TermId: 1008 0-0.rec 2 MB 4 MB 0 MB TermId: 1006 TermId: 1005 rec-pos cnc.dat Aeron Archive ReplaySession limitPosition stopPosition replayPosition ReplayBuffer (1 MB)

Use the tabs above to step through the animation.


ReplaySession has a replayPosition, which is where the replay will read from next. It may also have a limitPosition Counter that can be read to find out where to replay up to. Here, it's set to the rec-pos Counter, which implies the Recording is still active. The stopPosition field (not the stopPosition in the RecordingDescriptor) is set to the current position the replay will stop at (it will be reevaluated when reached, if there is a limitPosition Counter). All the fields are described in more detail below.

The ReplaySession reads from the Segment file into a ReplayBuffer, which is 1 MB long by default. It tries to read up to the end of the current Term, but no more than the ReplayBuffer length and no further than stopPosition. Here, this means reading the first 1 MB from the start of 0-0.rec, filling the ReplayBuffer.

The ReplaySession walks through all the messages in the ReplayBuffer, rewriting all the streamId and sessionId fields in the Frame headers to the streamId and sessionId of the Replay Publication (if checksums are enabled, they are checked first, because the checksum is actually stored in the otherwise unused sessionId field). It also keeps track of where the last whole message ends, as ReplayBuffer may end with a message that was only partially read from the file. More details on the rewriting below.

The ReplaySession offers all the messages in the ReplayBuffer, up to the end of the last whole message, to the ExclusivePublication as a block. This will never span a Term boundary. Flow control also applies to this method, so it will return BACK_PRESSURED if the Client cannot keep up with the replayed messages. If successful, all the messages are copied into the Log Buffer, then replayPosition is updated. If it failed, the whole process restarts on the next duty cycle. The partial message at the end of the ReplayBuffer is discarded.

Assuming success, on the next duty cycle, the ReplaySession will read the next batch of messages from the Segment file, from replayPosition. This will start from the start of the message that was only partially read at the end of the first batch. This means the ReplayBuffer will always start at the beginning of a whole message and only whole messages are offered to the Publication.

This process repeats, so the streamIds and sessionIds in the ReplayBuffer are rewritten, then the whole messages are offered to the Publication. The next read would read the final message fragments and padding at the end of the first Term, then they would be offered to the Publication. Then the ReplaySession would start with the next Term in exactly the same way. And when it gets to the end of the Segment file, it would open the next one, etc.

When the ReplaySession gets to the stopPosition, if there's a limitCounter, it is read and stopPosition is updated. For an active Recording, the rec-pos Counter will be advancing, so stopPosition will keep advancing. This is how the ReplaySession tails the end of an active Recording. If there is no limitCounter, i.e. the Recording has stopped, when the ReplaySession reaches the stopPosition, it goes into a 'done' state, stops, and is removed from the Replayer.


ReplaySession fields

Some of the ReplaySession fields are initialised differently, depending on whether it's a bounded replay, replay of an active Recording, or replay of a stopped Recording. Replay of an active Recording is like a bounded replay, where the bounding Counter is the rec-pos Counter of the Recording.

The ReplaySession fields are initialised as follows:

  • limitPosition is a Counter whose value is used to limit how far to replay up to. For a bounded replay, it's from the bounded replay request. If not bounded, then if the Recording is still active, it's the rec-pos counter, otherwise the Recording has stopped and it's null.
  • replayPosition is set to RecordingDescriptor.startPosition, or the position in the replay request, if it was set
  • stopPosition is set to the limitPosition counter value if there is one, otherwise the Recording stopPosition
  • replayLimit is the position to replay to. If there's a limitPosition Counter, this is Long.MAX_VALUE (because the limit is controlled by the Counter instead). If there's no Counter (this is only when the Recording has stopped), it's set to the Recording stopPosition. In both cases, replayLimit can be reduced (but not increased) by the length in the replay request, if it's set.

ReplayBuffer

ReplayBuffer is a single buffer in Archive.Context that is shared by all ReplaySessions. Each ReplaySession reads data into the buffer from a Segment file, before publishing it to its Publication. The ReplaySessions are all invoked one after the other by the Replayer on the same thread, so this is safe. The buffer's length is aeron.archive.file.io.max.length=1MB, but it's wrapped in a smaller UnsafeBuffer for a specific ReplaySession if the session was configured with a smaller fileIoMaxLength (a larger fileIoMaxLength is ignored).

StreamIds, SessionIds and Checksums

After part of the Segment file has been read into the ReplayBuffer, some processing is done on it. Every Frame in the buffer will have a StreamId and a SessionId, which will be from the original Publication that was recorded.

If checksums are enabled, the SessionIds will have been overwritten with the checksum that was calculated when the recording was made. At this point, all the checksums will be checked.

Whether checksums are enabled or not, all the StreamIds and SessionIds need rewriting to be those of the Replay Publication before the ReplayBuffer can be offered to the Log Buffer.

Counters

After each read from a Segment file, the number of bytes read and the time taken is reported back to the Replayer (even if the offer to the Publication fails). These are captured in the following Counters in the cnc.dat file (example from AeronStat):

 47:                    0 - archive-replayer max read time in ns - archiveId=1
 48:                    0 - archive-replayer total read bytes - archiveId=1
 49:                    0 - archive-replayer total read time in ns - archiveId=1