appendEntry method
Appends record to the file and the in-memory index.
Implementation
@override
Future<void> appendEntry(SessionRecord record) async {
// Serialize with every other in-process writer of this file (a
// full-open JsonlSessionStorage on the same path — issue #135
// round-4 review): concurrent bursts must never interleave bytes
// mid-record. Same contract as [JsonlSessionStorage].
final line = jsonEncode(record.toJson());
await withSessionFileLock(_filePath, () async {
// Issue #427: the app-chat submit path appends here; a transient
// ENOENT rides the capped retry instead of killing the submit.
_fsOrThrow(
await retryTransientSessionFileIo(
() => _fs.appendFile(_filePath, '$line\n'),
op: 'append',
path: _filePath,
config: _ioRetry,
),
'Failed to append session entry ${record.id}',
);
final info = await _reader.stat();
final start = info == null
? _knownFileBytes
: info.size - line.length - 1;
_fileSize = start + line.length + 1;
_fileMtimeMs = info?.mtimeMs ?? _fileMtimeMs;
_offsetById[record.id] = start;
final leaf = leafIdAfterSessionRecord(record);
if (_knownFileBytes == start) {
// Window bottom is the file tail: the record extends it.
final chained =
record.parentId == _branchBottomId ||
record is LeafRecord && record.parentId == _branchBottomId;
_indexEntry(record, start: start, end: _fileSize);
_knownFileBytes = _fileSize;
if (chained) _branchBottomId = leaf;
} else if (_belowCount != null) {
// Deep-paged: the record lands below the window and pages in later.
_belowCount = _belowCount! + 1;
_appendedBelow[record.id] = record;
}
_currentLeafId = leaf;
});
_evictToBound(newestSide: false);
}