41.1 SegmentBeingWrittenInMemory

SegmentBeingWrittenInMemory is concerned with tracking the write position of the current segment being prepared in memory. It also is responsible for writing the flush unit headers.

There is a concept of opening a flush unit (which means that some members of the header are initialised, and the write position is offset past the header, ready to write log records for the flush unit payload.

A flush unit must be closed before the next one is opened. When a flush unit is closed the size of the payload and the CRC can be written to the flush unit header.

Write position

The following two members are zero based offsets into the current segment being prepared in memory


    int startPositionOfFlushUnit_;
    int writePosition_;

startPositionOfFlushUnit_ will always be a multiple of the disk sector size. It locates the current flush unit.

writePosition_ will never be less than startPositionOfFlushUnit_.

If writePosition_ equals startPositionOfFlushUnit_, then we must have closed the last flush unit, and we haven't opened the next one. In fact, it is allowable for


    writePosition_ = startPositionOfFlushUnit_ = LSS segment size

which means that the current segment is full, and to open a new flush unit we will need to allocate a fresh segment.

Interface to the SegmentWriter

The thread writing to the segment in memory will write to the buffer at the current write position represented by


    int writePosition_;

This of course assumes that the current flush unit has already been opened.

After writing data to a flush unit, the segment writer may want to close the flush unit (in order to make the LRS flushable, or because the segment is full). In that case just after closing the flush unit, it immediately opens a new one, in preparation for continued writing to the log.

This means that the SegmentWriter will always see a currently open flush unit.

Check Point Id and FSN

Each flush unit stores a check point id and a 32 bit Flush Sequence Number (FSN). The check point is a guid that is generated each time a check point is performed.

Consider the following depiction of the log broken up into log flush units (LFUs)

Log flush units showing check point identities and sequence numbers; the units written by check point cpid2 retain cpid1 until the check point completes

Note the following

  • An LFU stores the identity of the last completed check point. The LFUs written while check point cpid2 is being made therefore still contain cpid1.
  • After the last check-point LFU is closed, the LSS records the boundary shown by the dotted line as positionOfLastCheckPoint_. This LogRecordPosition is written into the new root-block division.
  • The open, empty LFU immediately after that position is then changed to cpid2, and its FSN is reset to 1. Later LFUs increment the FSN.
  • If the preceding LFU exactly fills a segment, the stored position is the end of that segment; logically it is still the dotted boundary before the first cpid2 LFU.

When to calculate the CRC

A previous design used the LazyWriter to calculate the CRC on each flush unit before it was written to disk. However, this caused a bug with the cleaner, which needs to be able to iterate through the flush units of segments in memory that haven't yet been written to disk (and had their CRC calculated).

Therefore, the current design calculates the CRC of each flush unit as soon as it is closed by the segment writer.

Code


class SegmentBeingWrittenInMemory
{
public:
    SegmentBeingWrittenInMemory(LSS& lss);
    ~SegmentBeingWrittenInMemory();
    void Clear();
    void Initialise(RecoveryScanInfo& rsi);
    void SetCheckPointId(const Guid& checkPointId);
    void StartFlushUnitOnNewSegment(Segment* s, SegId nextSegid);
    bool StartAnotherFlushUnit(LogRecordPosition& flushPosition, bool& needToStartANewSegment);
    SegId GetNextSegId() const { return nextSegid_; }
    Segment* GetSegment() const { return segment_; }
    LogRecordPosition GetWritePosAsLogRecordPosition() const
    SegId GetSegId() const
    int GetNumFreeBytes() const
    void OffsetWritePos(int offset)
    void WriteData(const void* buffer, int numBytes)
    octet_t* GetBufferPtrAtWritePosition()
    void SetWritePosFromBufferPtr(const octet_t* p)

private:
    void OpenFlushUnit();
    bool CloseCurrentNonEmptyFlushUnit();

private:
    LSS& lss_;
    Guid checkPointId_;
    FlushSeqNumber fsn_;
    SegId nextSegid_;
    Segment* segment_;
    int diskSectorSize_;
    octet_t* buffer_;
    int startPositionOfFlushUnit_;
    int writePosition_;
};