Skip to content

feat: allow injecting a custom connection pool via pool_factory - #33

Open
wizzense wants to merge 1 commit into
redis:mainfrom
wizzense:feat/pool-factory-injection
Open

wizzense wants to merge 1 commit into
redis:mainfrom
wizzense:feat/pool-factory-injection

Conversation

@wizzense

@wizzense wizzense commented Aug 2, 2026

Copy link
Copy Markdown

Motivation

Deployments whose connection parameters are not static environment variables cannot use the settings-built pool:

  • Sentinel-managed masters — the pool that follows failover is a SentinelConnectionPool built from Sentinel.master_for(...), which no REDIS_URL/KV setting can express.
  • Secret-manager-issued passwords — the credential is fetched at startup (Vault, AWS SM, etc.), not present in the environment.

Today the only way in is reaching into the private _PoolState and assigning app.state._redis from a custom lifespan — which works, but pins consumers to internals (we are running exactly that in production against 0.8.0 and would like to stop).

Design

One public seam, no behavior change for existing users:

FastAPIRedis(app).lifespan(pool_factory=make_my_pool)   # sync or async callable

or, for plain FastAPI(lifespan=redis_lifespan) users:

app.state.redis_pool_factory = make_my_pool
  • The factory is a zero-argument sync or async callable returning the redis.asyncio.ConnectionPool to use (async matters: fetching a credential at startup is I/O).
  • The lifespan owns the returned pool and closes it on shutdown, identical to the settings-built one.
  • Cluster mode raises ValueError rather than silently ignoring the override.
  • Every caching / rate-limiting dependency is untouched — they already resolve through the pool state.

Tests

tests/unit/test_pool_injection.py: sync factory, async factory, state-attribute path, cluster refusal, and a regression guard for the default settings-built path. Full unit run: 108 passed.

🤖 Generated with Claude Code

Deployments whose connection parameters are not static environment
variables cannot use the settings-built pool: Sentinel-managed masters
(SentinelConnectionPool), passwords issued at runtime by a secret
manager, or any pool needing programmatic construction. Today the only
way in is reaching into the private _PoolState, which pins consumers to
internals.

This adds one public seam:

- FastAPIRedis(app).lifespan(pool_factory=...) — a zero-arg sync or
  async callable returning the redis.asyncio.ConnectionPool to use.
- Plain FastAPI(lifespan=redis_lifespan) users can set
  app.state.redis_pool_factory directly.

The lifespan owns the returned pool and closes it on shutdown, same as
the settings-built one. Cluster mode raises ValueError rather than
silently ignoring the override. Every caching / rate-limiting
dependency is unchanged — they already resolve through the pool state.

Includes unit tests: sync factory, async factory, state-attribute path,
cluster refusal, and a regression guard for the default path.
@tishun
tishun self-requested a review August 19, 2026 12:40
@tishun tishun added the needs-triage The team needs to go through the provided information and analyse it label Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-triage The team needs to go through the provided information and analyse it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants