synology_apm_repo.sdk.storage.dircache module

DirCache — mandatory directory-listing cache.

resolve_seq_file needs “logical name -> [physical names]” lookups for every per-generation file (.buk, .inf, .fgp, .ref, …). Re-listdir()-ing the same directory on every such lookup turns “export one image that touches a few hundred buckets” into O(n^2) directory scans — on an object-storage backend, hundreds of paginated list-object API calls. This is a correctness-adjacent performance requirement, not an optional optimization: a Session holds exactly one DirCache per open repository for its whole lifetime; only an explicit invalidate() (e.g. the TUI’s “refresh” action) forces a re-scan.

generations.py’s S3/Azure db/<name> generation selection is a separate mechanism with its own transaction-log-based rule — it calls store.listdir() directly and does not go through this cache.

synology_apm_repo.sdk.storage.dircache.split_seq_suffix(name)

Split a trailing .<digits> sequence-id suffix off name (FORMAT-SPEC.md: sequence-id-suffix). Returns (name, None) unchanged if there is no such suffix.

Note the suffix is only ever the last dot-segment: "132.buk.1" -> ("132.buk", 1) — the base name "132.buk" itself legitimately contains a dot. "132.buk" (no suffix) -> ("132.buk", None).

class synology_apm_repo.sdk.storage.dircache.DirCache(store)

Bases: object

Per-repository cache of directory listings, keyed by store-relative directory path.

Both _raw and _grouped are AsyncKeyedCache instances — unbounded, session-wide shared. Two Tasks racing on the same cold directory only pay for one listing: the second awaits the first’s still-in-flight store.listdir() future instead of issuing its own, and both end up with the same result (or exception).

async listdir(dir_path)

Cached passthrough to store.listdir(dir_path).

async grouped(dir_path)

Return {logical_name: [physical_name, ...]} for dir_path, built from exactly one listdir call and cached thereafter. logical_name is each entry with any trailing sequence-id suffix stripped (split_seq_suffix) — a directory entry with no suffix maps to itself.

async invalidate(dir_path=None)

Drop cached entries for dir_path (or everything, if omitted).

Stays async def (nothing here actually awaits) purely to preserve the existing await dir_cache.invalidate(...) call shape at every call site.