checkSync method
Verifies the manifest, every referenced segment and the file set.
This is a read-only diagnostic: it does not repair anything. With
verifyBlocks (default) every block's CRC is checked and every document
is parsed, which costs time proportional to the data size.
Implementation
CheckReport checkSync({bool verifyBlocks = true}) {
_checkOpen();
final issues = <CheckIssue>[];
final stats = <String, int>{};
if (_recoveredFromWal) {
issues.add(
const CheckIssue(
CheckSeverity.warning,
'wal-only-recovery',
'no manifest slot decoded on open; state was rebuilt from the WAL. '
'Orphan segment files, if any, were kept for inspection',
),
);
}
final segmentNames = <String>[
for (final name in _directory.listSync())
if (name.startsWith(segmentPrefix) && name.endsWith(segmentSuffix))
name,
];
stats['segmentFiles'] = segmentNames.length;
Manifest? manifest;
try {
manifest = ManifestStore(_directory).loadSync();
} on Object catch (error) {
issues.add(
CheckIssue(CheckSeverity.error, 'manifest', 'read failed: $error'),
);
}
if (manifest == null) {
// A recovered database has no manifest until its first flush; that is
// reported by the wal-only-recovery warning above, not as an error.
if (segmentNames.isNotEmpty && !_recoveredFromWal) {
issues.add(
const CheckIssue(
CheckSeverity.error,
'no-manifest',
'segment files exist but no manifest decodes',
),
);
}
} else {
stats['manifestGeneration'] = manifest.generation;
stats['segments'] = manifest.segments.length;
final referenced = <String>{};
var entries = 0;
var bytes = 0;
var documents = 0;
for (final info in manifest.segments) {
referenced.add(info.name);
entries += info.entryCount;
bytes += info.byteLength;
if (!_directory.existsSync(info.name)) {
issues.add(
CheckIssue(CheckSeverity.error, 'missing-segment', info.name),
);
continue;
}
SyncStorageFile? file;
try {
file = _directory.openSync(info.name, create: false);
final data = file.readAllSync();
if (data.length != info.byteLength) {
issues.add(
CheckIssue(
CheckSeverity.warning,
'segment-size',
'${info.name}: expected ${info.byteLength}, found ${data.length}',
),
);
}
final reader = SegmentReader(data);
if (verifyBlocks) {
reader.forEach((_) => documents++);
}
} on Object catch (error) {
issues.add(
CheckIssue(
CheckSeverity.error,
'segment-read',
'${info.name}: $error',
),
);
} finally {
file?.closeSync();
}
}
stats['entries'] = entries;
stats['bytes'] = bytes;
if (verifyBlocks) stats['documents'] = documents;
for (final name in segmentNames) {
if (!referenced.contains(name)) {
issues.add(CheckIssue(CheckSeverity.warning, 'orphan-segment', name));
}
}
}
// Use the engine's own WAL handle: opening a second handle and closing it
// would close the shared handle in in-memory directories.
stats['walBytes'] = _walFile.lengthSync;
return CheckReport(issues, stats);
}