synology_apm_repo.sdk.dedup.verify_checks module¶
Byte-level integrity check primitives for units/verify_reachable.py’s
top-down, reachability-scoped walk — what Repository.verify()/
Catalog.verify() actually use.
Every function here is a pure “given this already-open reader/triple,
check it, return the Finding``(s)" primitive — no scanning of
``db/file_map, no bucket sampling, no notion of which triples/buckets
are worth looking at in the first place. That’s the walker’s own job;
this module only owns how to check one thing once it’s been decided
the thing is worth checking.
Also owns the Finding/Stage/Symptom/VerifyLevel value
types the walker (and its callers, api.catalog/api.repository)
share — defined here, at the bottom of the dependency, so nothing above
this module ever needs to import back into units/verify_reachable.py
just to reference one of these value types.
- class synology_apm_repo.sdk.dedup.verify_checks.Symptom(value)¶
Bases:
EnumThe symptom categories a finding can carry.
- CORRUPTION = 'Corruption'¶
- FILE_MISSING = 'FileMissing'¶
- MISMATCH = 'Mismatch'¶
- DATA_MISSING = 'DataMissing'¶
- KEY_MISSING = 'KeyMissing'¶
- REPAIRED_VIA_PARITY = 'RepairedViaParity'¶
A CRC mismatch that this SDK’s own Redundancy-blob self-repair (
format.redundancy.attempt_repair) was able to reconstruct and byte-for-byte confirm correct. Not a problem left unresolved, but still reported (not silently dropped) since repeated repairs against the same bucket group over time is itself a signal of failing underlying storage, even though the check the caller actually cared about (is this data readable) passed.
- class synology_apm_repo.sdk.dedup.verify_checks.VerifyLevel(value)¶
Bases:
EnumHow thorough a
verifyrun is.FULLreads and checks every live chunk in a touched bucket — both its ciphertext CRC32 and, once a vault key is available, its decrypt+decompress+SHA-256 fingerprint — as costly as a real full export of the whole repository.QUICKreads no chunk content at all, only each touched bucket’s own structural checks; there is deliberately no sampled middle tier.- QUICK = 'quick'¶
- FULL = 'full'¶
- class synology_apm_repo.sdk.dedup.verify_checks.Stage(value)¶
Bases:
StrEnumThe stages a
Findingcan come from. Astrsubclass: every existing site rendering/serializingFinding.stagedirectly as a string keeps working unchanged.- REPO_INFO = 'RepoInfo'¶
- FILE_MAP = 'FileMap'¶
- COMPOSITION = 'Composition'¶
- BUCKET = 'Bucket'¶
- ENCRYPT_KEY = 'EncryptKey'¶
- VERSION = 'Version'¶
A catalog/workload/version that should resolve to real content, per its own metadata, but doesn’t — covers failure at either the version level or the workload/connection-enumeration level above it, one stage rather than one per layer of a top-down walk.
- class synology_apm_repo.sdk.dedup.verify_checks.Finding(stage, symptom, path, detail, ref=None)¶
Bases:
objectOne integrity-check result.
pathis whatever store-relative path (orfile_mappath, or workload/version display info, for aStage.VERSIONfinding) the finding is about.ref(Noneunless set) is a canonicalrepo_path#cat:<id>/wl:<id>/ver:<uid>ref (units.node_ref.NodeRef) naming the exact catalog/workload/version this finding was found while checking — set byunits/verify_reachable.py’s top-down walk, the only place with aVersionin hand to name. When a bucket/chunk is shared by more than one version through dedup,refnames whichever version’s own check actually triggered this finding first — memoization means a later version sharing the same already-checked data produces no finding of its own, not a second one with a differentref.
- async synology_apm_repo.sdk.dedup.verify_checks.check_repo_info(repo)¶
repo_info’s own header/CRC/JSON-parse check — repository-wide, not scoped to any one catalog reference, soverify_reachable()runs this exactly once regardless of what else it covers.
- async synology_apm_repo.sdk.dedup.verify_checks.check_composition_header(reader, *, path)¶
The session’s own
subID=0sub-file header (CompositionReader.verify_header) — the stricter, opt-in checkCompositionReaderitself normally skips. Meant to be called once per distinct(stream_id, session_id), not once per row/extent that happens to share it.
- async synology_apm_repo.sdk.dedup.verify_checks.check_record_head(reader, comp_offset, *, path)¶
Read and parse the 32-byte
RecordHeadatcomp_offset— magic +head_crcare checked unconditionally byparse_record_headitself. Returns(finding, None)on failure,(None, record_head)on success.
- async synology_apm_repo.sdk.dedup.verify_checks.verify_chunk_map_crc_threaded(map_array_bytes, expected_crc)¶
format.composition.verify_chunk_map_crc, with the thread-hop decision folded in —map_array_bytescan be a multi-hundred-MB chunk-map array, so this keeps that rare case’szlib.crc32pass off the event loop the same way every other large-payload synchronous computation in this codebase does (catalog/version.py::open_target_db,units/fs.py’s ownpeel()call).should_thread_chunk_map_crcskips the hop for the common small-array case, where the hop itself would cost more than thecrc32pass it’s saving. Shared by this module’s owncheck_map_and_attr_crcanddiagnostics.py’s_verify_map_crc— same check, two orchestrators.- Raises:
DataCorruptError –
map_array_bytesdoesn’t matchexpected_crc.
- async synology_apm_repo.sdk.dedup.verify_checks.check_map_and_attr_crc(reader, comp_offset, record_head, *, path)¶
Full
mapCrcover the whole chunk-map array plus (when present)attrCrcover the trailing JSON attribute blob — one merged read for both regions, since they’re contiguous.A
map_crcmismatch is not immediately reported as a problem: this first tries the record’s own Redundancy-blob self-repair (_attempt_map_crc_repair), since every current-format record carries one (RecordHead.has_redundancy, checked byparse_record_headitself). A confirmed-correct repair is reported asSymptom.REPAIRED_VIA_PARITY(see that symptom for why a successful repair still gets reported) rather than dropped silently.Nothing is checked when
record_head.map_num == 0— an empty chunk-map array has nothing to CRC.- Returns:
(findings, repaired_array).repaired_arrayis the confirmed-correct chunk-map array bytes exactly when amap_crcmismatch was just repaired via parity,Noneotherwise (no mismatch, or one that couldn’t be repaired). A caller with its own separate re-read of this same array —units/verify_reachable.py’splan_chunks_windowedwalk goes through a differentCompositionReader/CompositionRecordthat never sees this repair on its own — must feed a non-Noneresult intoCompositionRecord.seed_pages_from_arrayitself, or it re-derives entries from the still-corrupted on-disk bytes.- Return type:
- async synology_apm_repo.sdk.dedup.verify_checks.check_bucket_structure(store, reader)¶
Everything about this bucket that doesn’t depend on which chunk(s) a caller actually cares about:
expected_bucket_sizeagainst the real on-disk size, and the ChunkCrcStore trailer’s owncrcOfChunkCrcself-consistency (viaBucketReader.ensure_chunk_crc_store, which also warms the cachecheck_chunk_ciphertext_crcbelow reuses — one trailer read shared by both, not two). Header/SizeStore CRC are already unconditionally checked byBucketReader.openitself; the one further thing owned here is turning a successful SizeStore self-repair (reader.sizestore_repaired) into its ownSymptom.REPAIRED_VIA_PARITYfinding, sinceopen()itself has noFinding-returning contract — the same visibilitycheck_map_and_attr_crcalready gives the composition-layer map-CRC repair.A legacy uncompressed-layout bucket has neither an
expected_bucket_sizeformula nor a ChunkCrcStore trailer that applies to it — both are skipped, not treated as findings.Takes a bare
store, not a fullDedupRepo— the only thing this function ever needed from one — so a caller with just anObjectStorein hand (a multiprocess worker rebuilding its own repository state viadedup.pool_descriptor, which has noDedupRepoat all) can call this directly.
- async synology_apm_repo.sdk.dedup.verify_checks.check_chunk_ciphertext_crc(reader, chunk_idx)¶
One chunk’s stored bytes against its own ChunkCrcStore entry (FORMAT-SPEC.md: ChunkCrcStore) — no decrypt/decompress attempt, so this needs no vault key and runs the same whether or not the bucket turns out to be encrypted.
- async synology_apm_repo.sdk.dedup.verify_checks.check_raw_chunk_ciphertext_crc(reader, chunk_idx, raw)¶
check_chunk_ciphertext_crc’s counterpart for a caller that already haschunk_idx’s stored bytes in hand — e.g. from a batchedread_raw_chunkscall — instead of fetching them itself, the same splitverify_raw_chunk_ciphertext_crcalready has fromverify_chunk_ciphertext_crc. Same three-way exception mapping as the singular form above; kept as its own function rather than folded intocheck_chunk_ciphertext_crcsbelow (its only caller) so that caller’s own batched-fetch-then-check-each shape stays readable.
- async synology_apm_repo.sdk.dedup.verify_checks.check_chunk_ciphertext_crcs(reader, chunk_indices)¶
Batched
check_chunk_ciphertext_crc: every one ofchunk_indices’s stored bytes is fetched viareader.read_raw_chunks— one mergedstore.read()per contiguous run instead of one per chunk — before checking each against its ownChunkCrcStoreentry viacheck_raw_chunk_ciphertext_crc. Reports each chunk that fails on its own, the same granularity loopingcheck_chunk_ciphertext_crcwould.read_raw_chunksreads its own runs one after another with no per-run isolation: a later run’s failure raises past every earlier run’s already-fetched bytes, discarding them along with it. Since those bytes were never actually checked, silently swallowing that failure here would mean a real ciphertext CRC mismatch sitting in an earlier, otherwise-successful run could go unreported — so any failure at all from the batched read falls back to onecheck_chunk_ciphertext_crccall per chunk instead, which has no such cross-run coupling. Reserved for the failure path only; the common case (a single run, or every run succeeding) never pays for it.