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.
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 therec-pos
counter, otherwise the Recording has stopped and it's null.replayPosition
is set to RecordingDescriptor.startPosition, or theposition
in the replay request, if it was setstopPosition
is set to thelimitPosition
counter value if there is one, otherwise the RecordingstopPosition
replayLimit
is the position to replay to. If there's alimitPosition
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 RecordingstopPosition
. In both cases,replayLimit
can be reduced (but not increased) by thelength
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