Skip to content

known2_64.met: orphan-prune AICH hashsets at sync startup - #605

Merged
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:feat/known2-aich-orphan-prune
May 14, 2026
Merged

known2_64.met: orphan-prune AICH hashsets at sync startup#605
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:feat/known2-aich-orphan-prune

Conversation

@got3nks

@got3nks got3nks commented May 14, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #598 — closes the AICH side of @ngosang's #597 numbers.

known2_64.met is the AICH hashset cache (Merkle tree of SHA-1 hashes per shared file, used by peers for sub-part corruption recovery). It's content-addressed by AICH root hash, so it never had the mtime-touch bloat that hit known.met. But it never shrinks either: once a hashset is cached, the entry stays even after the underlying file leaves the user's library. On ngosang's profile this was 1.1 GB, and after #598's TTL dropped ~14 k orphaned live entries from known.met, the corresponding AICH entries in known2_64.met are now dead weight.

The fix is a single-pass orphan prune in CAICHSyncTask::Entry() (already walks known2_64.met at startup to build the hashlist for CheckAICHHashes). Snapshot the set of AICH master hashes still referenced by a live or duplicate-list CKnownFile (CKnownFileList::CollectLiveAICHRoots, added), then while walking source entries, stream-copy each one into a <name>.new temp file only if its root hash is in the live set. On clean walk completion, CFile::write_safe's Close() atomic-renames .new over the original; on corruption or IO failure the temp is removed without finalising, preserving the existing truncation-recovery path.

Hashset bytes are streamed through a 64 KB buffer rather than slurped — a single large-file entry can have megabytes of SHA-1s and we don't want one to dominate the working set.

Effective TTL is inherited from known.met. When PruneDuplicates evicts a known.met record for being unseen for 30 d, its AICH master hash leaves the live set, and the next AICH sync drops the orphaned hashset. Decoupled lifecycles would require bumping KNOWN2_MET_VERSION to add a per-entry timestamp (the file format is positional, no tag system), which is deliberately out of scope here.

Defensive: if theApp->knownfiles isn't populated yet (empty liveRoots set), the prune is skipped — we don't wipe everything on a misconfigured start.

Both lists (m_knownFileMap AND m_duplicateFileList) are scanned in CollectLiveAICHRoots: Append's demote branch parks a record (with its hashset) on the duplicate list while the new record takes the live slot, and an mtime-restore can re-promote the duplicate. Dropping a duplicate's hashset would silently lose it on re-promote.

The dedup root-hash cache (s_rootHashCache, #581) mirrored the old file; invalidated after a non-zero drop so the next SaveHashSet rebuilds against the rewritten known2_64.met.

Kept as draft while @ngosang validates the eviction behaviour against his 1.1 GB profile.

known2_64.met is the AICH (Advanced Intelligent Corruption Handling)
hashset cache: a Merkle tree of SHA-1 hashes per shared file, used
by peers to recover from sub-part-granularity corruption. Each entry
is keyed by its AICH root hash; ngosang's amule-project#597 report had it at
1.1 GB. The file has no mtime-touch bloat (entries are
content-addressed, dedup is already in place since amule-project#581), but it
never shrinks: once a hashset is cached, the entry stays even after
the underlying file leaves the user's library.

After amule-project#598's known.met TTL prune drops ~14 k orphaned live entries
on a long-lived profile, those hashes' AICH entries in
known2_64.met are dead weight and should follow them out.

Add CKnownFileList::CollectLiveAICHRoots() -- walks
m_knownFileMap and m_duplicateFileList under list_mut, returns the
set of AICH master hashes still referenced by either. Both lists
need to be scanned: Append's demote branch parks a record (with its
hashset) on the duplicate list while the new record takes the live
slot, and an mtime-restore can re-promote the duplicate later.
Dropping a duplicate's hashset would silently lose it on re-promote.

Extend CAICHSyncTask::Entry()'s existing known2_64.met walk: open a
"<name>.new" temp file via CFile::write_safe, and for each entry
read from the source, either copy it through to the temp (if its
root hash is in liveRoots) or skip it. On clean walk completion the
Close() atomic-renames .new over the original; on corruption catch
or IO error the temp is removed without finalising, leaving the
source's existing truncation-recovery path intact. Hashset bytes
are streamed through a 64 KB buffer rather than slurped, so a
single large-file entry can't dominate the working set.

The dedup root-hash cache (s_rootHashCache, amule-project#581) mirrored the old
file; invalidate it after a non-zero drop so the next SaveHashSet
rebuilds against the rewritten known2_64.met.

Effective TTL is inherited from known.met: a record evicted there
by PruneDuplicates ages out of liveRoots and its hashset gets
dropped on the next AICH sync. Decoupled lifecycles would require
bumping KNOWN2_MET_VERSION to add a per-entry timestamp (the file
format is positional, not tag-based), which is out of scope here.

Defensive: if knownfiles isn't yet populated (empty liveRoots), the
prune is skipped -- we don't wipe everything on a misconfigured
start.
@got3nks
got3nks marked this pull request as ready for review May 14, 2026 16:37
@Stoatwblr

Copy link
Copy Markdown

My known2.met is slightly over 3GB. It'll be interesting to see how much this shrinks

@mrjimenez
mrjimenez merged commit 2971761 into amule-project:master May 14, 2026
12 checks passed
@got3nks
got3nks deleted the feat/known2-aich-orphan-prune branch May 14, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants