41.2 LazyWriter

The lazy writer employs a low priority worker thread to write filled segments to disk. This allows other threads (that write to the log in memory) to avoid blocking on I/O, unless the segment cache becomes full.

The lazy writer has a LazyWriterQueue (LWQ) to manage a FIFO queue of filled segments that need to be written to disk. The lazy writer thread pops segments from the front of this queue and writes them to disk. When there are no filled segments, the thread blocks in an efficient wait state on an event. This event is signalled each time a segment is pushed onto the back of the queue.

After a filled segment is written to disk, it is released (see Segment::Release()). This decrements the access count. Typically the access count will fall to zero, allowing the segment to be pushed onto the back of the Segment Eviction Queue (SEQ), in turn allowing it to be evicted from memory.

The worker thread is only interested in writing full segments to disk. It will never itself flush the log (and write a partially filled segment). However the LazyWriter class does provide a threadsafe Flush() method that a client can use to flush the log. This uses a mutex to avoid contention with other threads trying to flush the log, and the lazy writer worker thread that writes filled segments to disk.

Note that the lazy writer must be explicitly started and stopped. It is an error to start it while it has already been started, and to stop it while it has already been stopped. It is an error to allow the LazyWriter to destruct before it has been stopped.

Error handling

All writing to the store and the delta file is performed by WriteSegmentSectionToRAS(). This uses a try-catch block to catch FileExceptions. A copy of the exception is saved in the LSSFileExceptionStatus member in the LSS. A boolean flag to indicate that an error has occurred is set to true.

Subsequent calls to WriteSegmentSectionToRAS() make no further attempts to write the segments (by testing the boolean flag). Therefore as a "consumer" the lazy writer will eat segments from the LWQ very quickly ensuring that the SegmentWriter (the "producer") is not blocked because the lazy writer is unable to continue writing to disk. This ensures that the SegmentWriter doesn't get stuck waiting for a free segment, and not actually get around to propagating the error condition to clients of the LSS.

Code


class LazyWriter
{
	cxNotCloneable(LazyWriter)

public:
	LazyWriter(LSS& lss);
    void Start(LogRecordPosition endOfLog);
    void Stop();
    void SetFlushPosition(Segment* s, int size);
    void OnFilledSegment(Segment* s);
    void Flush(bool flushForCheckPoint);

private:
    void WriteSegmentSectionToRAS(Segment* s, int i1, int i2);
    void WriteFullSegment(Segment* s);
    void SignalDontNeedToFlush() { lazyFlusher_.SignalDontNeedToFlush(); }

private:
    LSS& lss_;
    LazyFlusher lazyFlusher_;
    LazyWriterQueue lwq_;
    SignaledTask signaledTask_;
    mutable std::mutex writeSegmentsToDiskMutex_;
    LogRecordPosition prevFlushPosition_;
};