Skip to content

perf(redis): fetch get_cache_data in chunked, non-transactional pipelines - #212

Merged
allen0099 merged 1 commit into
masterfrom
perf/redis-get-cache-data-chunks
Sep 26, 2026
Merged

allen0099 merged 1 commit into
masterfrom
perf/redis-get-cache-data-chunks

Conversation

@allen0099

Copy link
Copy Markdown
Owner

Closes #171

Problem

get_cache_data queued a GET and a PTTL for every key in one self.client.pipeline(), which redis-py makes transactional by default. The server therefore ran all 2N commands inside a single MULTI/EXEC and blocked other clients for its duration on a large keyspace. With 250 keys, the recorded pipelines (transactional, commands) were [(True, 500)].

Change

  • Commands are sent in pipelines of _BATCH_SIZE (100) keys with transaction=False. The same 250 keys now produce [(False, 200), (False, 200), (False, 100)].
  • Each chunk's now is taken right after its own execute(), so the reported absolute expiry stays accurate to that round trip.
  • A consistent snapshot across chunks is not needed. A key that disappears between SCAN and its chunk is still skipped through PTTL == -2, as before.
  • The empty-keyspace early return is gone, because the loop handles it.

Tests

  • test_redis_get_cache_data_uses_chunked_non_transactional_pipelines: over 210 keys it checks three non-transactional pipelines, at most 2 * _BATCH_SIZE commands each, 2N commands in total, and every entry returned with an expiry.
  • Mutation checks:
    • With redis.py reverted to master, only this test fails.
    • With chunking kept but transaction=False dropped, only this test fails.
  • ruff, mypy --strict, full suite against live Redis and Memcached (CACHEX_REQUIRE_LIVE_SERVERS=1): 862 passed. backends/redis.py coverage is 100%.

CHANGELOG entry

Section: Changed (tracked in #206)

- **Redis `get_cache_data()` no longer blocks the server.** It fetched every
  value and TTL inside one `MULTI`/`EXEC`, because redis-py pipelines are
  transactional by default. It now sends non-transactional pipelines of 100
  keys each.
  ([#171](https://github.com/allen0099/FastAPI-CacheX/issues/171))

…ines

get_cache_data sent GET and PTTL for the whole keyspace in one pipeline,
which redis-py makes transactional by default. The server therefore ran
every command inside a single MULTI/EXEC and blocked other clients for
its duration on a large keyspace.

Send the commands in pipelines of _BATCH_SIZE keys with
transaction=False. Monitoring does not need a consistent snapshot, and
each chunk's expiry is still measured right after its own round trip.

Closes #171
@allen0099 allen0099 added this to the 0.3.8 milestone Sep 26, 2026
@allen0099 allen0099 added enhancement New feature or request backends Cache backends and their atomic primitives labels Sep 26, 2026
@allen0099
allen0099 merged commit 9d1fe00 into master Sep 26, 2026
11 checks passed
@allen0099
allen0099 deleted the perf/redis-get-cache-data-chunks branch September 26, 2026 14:35
allen0099 added a commit that referenced this pull request Sep 27, 2026
Copies the CHANGELOG sections of #207, #208, #209, #211, #212, #272,
#273, #274, #275, #276, #279 and #284 into Unreleased. The #273 entry
drops expire_if_equals from its list of methods that changed, since that
primitive is new in 0.3.8.
allen0099 added a commit that referenced this pull request Sep 27, 2026
Copies the CHANGELOG sections of #207, #208, #209, #211, #212, #272,
#273, #274, #275, #276, #279 and #284 into Unreleased. The #273 entry
drops expire_if_equals from its list of methods that changed, since that
primitive is new in 0.3.8.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backends Cache backends and their atomic primitives enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redis get_cache_data: use a non-transactional, chunked pipeline

1 participant