Skip to content

fix: AAD deserialization bug + dead code removal#83

Open
27Bslash6 wants to merge 7 commits intomainfrom
refactor/dead-code-removal
Open

fix: AAD deserialization bug + dead code removal#83
27Bslash6 wants to merge 7 commits intomainfrom
refactor/dead-code-removal

Conversation

@27Bslash6
Copy link
Copy Markdown
Contributor

Summary

  • Bug fix: 5 deserialize_data() calls missing cache_key for AAD v0x03 verification — encrypted L1+L2 and L2 cache hits would fail AAD verification silently
  • Dead code removal: 8 dead functions + 2 dead variables removed (~114 LOC)
  • Found by expert panel review (bug-hunter, security-specialist, code-craftsman)

AAD Bug (fix: commit)

All deserialize_data() calls in the L1+L2 and L2 paths were missing cache_key=cache_key, causing AAD verification failure for encrypted caches. L1-only paths already passed it correctly. The bug meant encrypted cache hits would fail silently and fall through to cache miss on every request.

Dead Code Removed

Function File Reason
setup_distributed_tracing logging.py Stub, never called
setup_correlation_tracking logging.py Stub, never called
reset_session_id decorators/session.py Zero internal callers
get_async_http_client backends/cachekitio/client.py Async singleton never wired
record_cache_operation (module) reliability/metrics_collection.py Superseded by class methods
record_circuit_breaker_state (module) reliability/metrics_collection.py Superseded by class methods
record_timeout_adjustment (module) reliability/metrics_collection.py Superseded by class methods
_use_pipelined (variable) decorators/wrapper.py Assigned, never read
use_pipelined (variable) decorators/wrapper.py Cascading dead code
lock_key (sync only) decorators/wrapper.py Computed but never used in sync path

Test plan

  • Spec compliance review: PASS
  • Code quality review: APPROVED
  • Ruff lint + format: clean
  • No orphaned imports or stale __all__ exports
  • close_async_client thread-local cleanup preserved (SEC-001)
  • get_session_id doctests updated

Five deserialize_data() calls were missing cache_key=cache_key:
- L1+L2 sync path (line 678)
- L1+L2 async path (line 973)
- L2 async get after lock acquire (line 1035)
- L2 async get after lock wait, path A (line 1109)
- L2 async get after lock wait, path B (line 1132)

Without cache_key, encrypted cache entries using AAD v0x03 would fail
verification on every hit, silently falling through to cache miss.
The L1-only paths (lines 591, 935) already passed cache_key correctly.

Found by code-craftsman expert panel review of remediation spec.
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 28, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
257 1 256 7
View the full list of 1 ❄️ flaky test(s)
tests/critical/test_intelligent_cache_regression.py::TestIntelligentCacheRegression::test_concurrent_access_regression

Flake rate in main: 52.78% (Passed 17 times, Failed 19 times)

Stack Traces | 0.051s run time
self = <tests.critical.test_intelligent_cache_regression.TestIntelligentCacheRegression object at 0x7d317307c7a0>

    def test_concurrent_access_regression(self):
        """CRITICAL: Concurrent access must work identically for both interfaces.
    
        Note: Sync decorators don't support distributed locking (async-only feature).
        With max_workers=5 and 10 concurrent requests, some cache stampede is expected.
        The test verifies that both interfaces behave identically, not that there's
        perfect deduplication (which requires async functions with distributed locks).
        """
        legacy_calls = intelligent_calls = 0
    
        @cache(ttl=300, namespace="legacy_concurrent")
        def legacy_concurrent(value):
            nonlocal legacy_calls
            legacy_calls += 1
            time.sleep(0.01)  # Simulate work
            return f"legacy_{value}"
    
        @cache(ttl=300, namespace="intelligent_concurrent")
        def intelligent_concurrent(value):
            nonlocal intelligent_calls
            intelligent_calls += 1
            time.sleep(0.01)  # Simulate work
            return f"intelligent_{value}"
    
        # Test concurrent access with ThreadPoolExecutor
        with ThreadPoolExecutor(max_workers=5) as executor:
            # Submit multiple concurrent requests for same value
            legacy_futures = [executor.submit(legacy_concurrent, "test") for _ in range(10)]
            intelligent_futures = [executor.submit(intelligent_concurrent, "test") for _ in range(10)]
    
            # Collect results
            legacy_results = [f.result() for f in legacy_futures]
            intelligent_results = [f.result() for f in intelligent_futures]
    
        # Both should have some cache stampede (multiple calls due to no distributed lock in sync mode)
        # But both should behave identically (same number of calls)
        assert legacy_calls > 0 and legacy_calls <= 10, "Should have some calls but not all 10"
        assert intelligent_calls > 0 and intelligent_calls <= 10, "Should have some calls but not all 10"
        # Both interfaces should have similar behavior (within tolerance)
>       assert abs(legacy_calls - intelligent_calls) <= 2, "Both interfaces should have similar stampede behavior"
E       AssertionError: Both interfaces should have similar stampede behavior
E       assert 4 <= 2
E        +  where 4 = abs((1 - 5))

tests/critical/test_intelligent_cache_regression.py:223: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Two tests exercising the thundering herd protection paths where
deserialize_data(cache_key=cache_key) was added:

1. Lock acquired, double-check finds cache already populated
   (another request filled it during lock wait)
2. Lock timeout, final cache check finds it populated
   (another request completed while waiting for lock)

Both use FakeLockableBackend with acquire_lock protocol to
trigger the lock-then-deserialize code paths.
@27Bslash6 27Bslash6 force-pushed the refactor/dead-code-removal branch from a5cda04 to 6f582eb Compare March 29, 2026 07:32
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.

1 participant