74 Log
Each Partition has its own log. That log uses its own
forward-chained sequence of segments, independently of the logs belonging to other partitions.
Use of PartitionSeid
A 32-bit Seid is meaningful only within a Space. There is no
concept of a selected Space in the log, so records which identify serial elements or packets use a
PartitionSeid:
struct PartitionSeid
{
SpaceId spaceId;
Seid seid;
};
The SpaceId selects a Space within a
Partition and the
32-bit Seid selects a serial element or packet within that space.
A PartitionSeid is therefore a 64-bit identity which is unambiguous within
one partition.
Log records corresponding to new versions of serial elements include a
PartitionSeid in the packet header. Records for overflow packets and
other packets having their own RPM entries likewise use the PartitionSeid
of the packet they identify. A DeleteSerialElement record identifies the head packet of the serial
element being deleted. Other records which identify an existing serial element or packet also carry
enough information to reconstruct its PartitionSeid.
When a packet contains a PartitionSeid, the LSS can verify that
its SpaceId belongs to the Partition which owns the log. A
mismatch is an integrity error and puts the LSS into its zombie state.
LogRecordPosition
A LogRecordPosition is a 64-bit byte offset measured from the
beginning of Segment 0. Segment 0 begins immediately after the root block, so converting a
LogRecordPosition to a physical RAS position adds the size of the
root block.
Position zero is not a possible position for a real log record. The beginning of a segment containing log records is occupied by an LFU header, and the records in that LFU follow the header. Zero is therefore available as a sentinel without conflicting with a real record.
The value zero represents an absent or null log-record position. In-memory structures can store a
LogRecordPosition directly and test it for zero instead of using
std::optional<LogRecordPosition>. This convention applies to RPM
entries, persistent RPM-root positions and other optional references to log records.
The segment size must be a power of two. The SegId and offset within
the segment can therefore be obtained from a LogRecordPosition using
shifting and masking.
using LogRecordPosition = uint64;
SegId GetSegId(LogRecordPosition position, uint64 segmentSize)
{
cxAssert(segmentSize != 0 && (segmentSize & (segmentSize - 1)) == 0);
return static_cast<SegId>(position >> std::countr_zero(segmentSize));
}
uint64 GetOffsetWithinSegment(LogRecordPosition position, uint64 segmentSize)
{
cxAssert(segmentSize != 0 && (segmentSize & (segmentSize - 1)) == 0);
return position & (segmentSize - 1);
}
Log Flush Units
Segments do not have persistent headers or footers. They contain Log Flush Units (LFUs),
which are the independently identifiable and validated units appended to the log. Every LFU
redundantly records the PartitionId of the
Partition which owns the log:
using PartitionId = uint32;
struct LogFlushUnitHeader
{
Checksum64 checksum64;
Guid checkpointId;
PartitionId partitionId;
FlushSeqNumber flushSeqNumber;
int32 numBytesInPayload;
SegId nextSegId;
};
LogFlushUnitHeader is 40 bytes.
The partition identity is stored with the LFU metadata and covered by the LFU's integrity checks.
Partition-local log sequence
Each partition has its own log and writer, so LFU sequence numbers can be local to the partition. An LFU position in logical log order is qualified by both values:
(PartitionId, LFU sequence number)
Each partition assigns its own LFU sequence numbers.
Partition identity within segments
A segment is assigned to one partition while that partition uses it, but there is no segment header which persistently records that assignment. The partition ID in each LFU identifies its partition log.
All valid LFUs in a segment must agree on the same
PartitionId.
LFUs record the next SegId in the log. Each
Partition therefore has an independent forward-chained sequence
of segments from its last valid checkpoint. An independent recovery scan of that sequence can be
performed when the partition is opened.
Self-identifying records
Using PartitionSeid makes the relevant log records self-identifying. Log
scanning, cleaning, checkpoint processing, validation and diagnostic tools can select the correct
Space and RPM without relying on external context.
This remains necessary when a Partition contains many Seid
spaces. The partition identifies the physical log and SUT, while the
SpaceId in the packet header identifies the variable-height RPM
which maps that packet's local Seid.
Using a 64-bit identity in self-describing log records does not remove the principal benefit of 32-bit Seids. High-volume references in B+Trees, RPMs and other structures whose address space is already known continue to store only the local 32-bit value.