Skip to content

feat: add Memcached backend with pymemcache HashClient#67

Merged
27Bslash6 merged 6 commits intomainfrom
feat/memcached-backend
Mar 27, 2026
Merged

feat: add Memcached backend with pymemcache HashClient#67
27Bslash6 merged 6 commits intomainfrom
feat/memcached-backend

Conversation

@27Bslash6
Copy link
Copy Markdown
Contributor

Summary

  • MemcachedBackend implementing BaseBackend protocol (get/set/delete/exists/health_check)
  • pymemcache HashClient for consistent hashing across multiple servers
  • TTL clamped to 30-day Memcached maximum
  • Bug fix: health_check() was calling HashClient.stats() which doesn't exist — replaced with benign get() probe
  • 78 unit/config/critical tests + 28 integration tests against real Memcached
  • MemcachedBackendConfig added to architecture tests

Test plan

  • 78 unit/config/critical tests pass (mocked)
  • 28 integration tests pass against real Memcached (Docker)
  • Lint and format clean
  • Architecture test includes MemcachedBackendConfig

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

- MemcachedBackend implementing BaseBackend protocol (get/set/delete/exists/health_check)
- pymemcache HashClient for consistent hashing across multiple servers
- TTL clamped to 30-day Memcached maximum (2,592,000 seconds)
- MemcachedBackendConfig with pydantic-settings (CACHEKIT_MEMCACHED_ env prefix)
- Error classification mapping pymemcache exceptions to BackendErrorType
- Lazy import via __getattr__ (pymemcache is optional dependency)
- 79 unit/config/critical tests (all mocked — integration tests to follow)
Bug fix: health_check() called self._client.stats() but HashClient
has no stats() method — only Client does. Replaced with a benign
get() probe. The unit tests never caught this because they mocked
everything. This is exactly why integration tests matter.

Integration tests (28 total, all against real Memcached):
- CRUD round-trip: set/get/delete/exists, overwrite, empty values
- TTL expiry: real server-side eviction, negative TTL, 30-day clamp
- Key prefix isolation: namespace separation verified
- Binary data integrity: null bytes, all-byte-values, 1MB payloads
- Concurrent thread safety: 10 threads x 50 ops, mixed read/write/delete
- Health check: live server + unreachable server
- Decorator integration: @cache.minimal with set_default_backend
- Edge cases: 500-key bulk ops, rapid set/delete cycles

Threading note: per-thread MemcachedBackend instances used for
concurrency tests — pymemcache HashClient pool has known contention
issues when shared across many threads simultaneously.
Was missing from BACKEND_CONFIGS parametrization — never added when
the memcached backend was created.
- Bump requests>=2.33.0 (CVE-2026-25645) in dev deps with py>=3.10 marker
- Ignore pygments GHSA-5239-wwwm-4pmq in pip-audit (no upstream fix)
- Validate server port is numeric and in range 1-65535
- Route health_check probe through _prefixed_key() for consistency
- Add error-raising tests for get/set/delete/exists except branches
- Add __getattr__ lazy import tests for MemcachedBackend and unknown attr
CI PRs only run tests/critical/ for coverage. Added error paths,
health_check failure, TTL clamping, key prefix, error classification,
lazy import, and config validation to critical tests.
@27Bslash6 27Bslash6 force-pushed the feat/memcached-backend branch from 81507f5 to 03315da Compare March 26, 2026 22:00
@27Bslash6 27Bslash6 merged commit a06e88a into main Mar 27, 2026
30 checks passed
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