openSync method

void openSync()

Loads the manifest and replays the WAL. Call once before serving reads.

Implementation

void openSync() {
  final store = ManifestStore(_directory);
  final sawManifest = ManifestStore.slotNames.any(_directory.existsSync);
  // A single torn slot with a non-empty WAL is the signature of a crash in
  // the very first manifest write: the WAL was not truncated yet, so every
  // record is still there. Two bad slots are real corruption and stay fatal.
  final manifest = store.loadSync(
    allowTornSingleSlot: !readOnly && _walFile.lengthSync > 0,
  );
  if (manifest == null && sawManifest) {
    // Keep the database writable: the unusable slots cannot be part of the
    // next generation, and the WAL replay below reconstructs the state.
    _recoveredFromWal = true;
    for (final name in ManifestStore.slotNames) {
      if (_directory.existsSync(name)) _directory.deleteSync(name);
    }
  }
  if (manifest != null) {
    _manifest = manifest;
    for (final info in manifest.segments) {
      // The manifest is data; a corrupted or hostile name must never escape
      // the database directory through mapSync.
      if (!_segmentNamePattern.hasMatch(info.name)) {
        throw VelxoCorruptException(
          'manifest references an invalid segment name: ${info.name}',
        );
      }
      // Segments are immutable: map them instead of copying. The view stays
      // valid until close(), which unmaps after the engine stops serving.
      final mapped = _directory.mapSync(info.name);
      try {
        final reader = SegmentReader(mapped.bytes);
        _segmentMaps.add(mapped);
        _segments.add(reader);
        _segmentInfos.add(info);
        _trackSegmentId(info.name);
      } on Object catch (error) {
        mapped.closeSync();
        throw VelxoCorruptException('segment ${info.name} is corrupt', error);
      }
    }
  }
  for (final name in _directory.listSync()) {
    if (name.startsWith(segmentPrefix) && name.endsWith(segmentSuffix)) {
      _trackSegmentId(name);
    }
  }
  _wal.replaySync((payload) {
    WalBatch.decode(payload, (kind, key, value) {
      if (kind == WalOpKind.put) {
        final ref = _arena.append(value!);
        _memtable.put(key, ref.chunk, ref.offset, ref.length);
      } else {
        _memtable.putTombstone(key);
      }
    });
  }, truncateTorn: !readOnly);
}