Skip to content

L2 corruption/tamper is never evicted (poison persists; L1 self-heals but L2 does not) #159

@27Bslash6

Description

@27Bslash6

Summary

On deserialize/integrity failure, the L1 path invalidates the entry (self-heals), but the L2 path catches SerializationError and returns None (miss → recompute) without deleting the corrupt/tampered backend entry. Every subsequent reader re-pays the full backend read + decompress + xxHash3 (+ decrypt) before recomputing; under thundering-herd every waiter does it.

Evidence

  • L1 heals: src/cachekit/decorators/wrapper.py:695 (sync), :955 (async)
  • L2 does not delete: src/cachekit/cache_handler.py:822-824 (sync), :855-857 (async); async post-lock double-check wrapper.py:1100-1101 only warns
  • Sync L2 also lacks a separate except SerializationError branch (async has one at wrapper.py:1037-1048) — corruption is indistinguishable from a network error on the sync path

Impact

Poisoned L2 entries persist until TTL/overwrite, repeatedly burning full read+verify work and masking tamper as transient miss.

Fix

On L2 integrity/auth failure, delete the offending key and emit a distinct cache_get_deserialize metric on both sync and async paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpythonPython library

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions