lablup / lablup/backend.ai

resolve_rolling_count builds a Valkey client per field resolution

Open
#14,019 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
670
Forks
183
Avg merge
17h 7m
Merged PRs (30d)
358

Description

## Motivation

`KeyPair.resolve_rolling_count` builds and tears down a whole Valkey client on every field
resolution:

```python
# src/ai/backend/manager/api/gql_legacy/keypair.py:199
async def resolve_rolling_count(self, info: graphene.ResolveInfo) -> int:
ctx: GraphQueryContext = info.context
valkey_profile_target = ctx.config_provider.config.redis.to_valkey_profile_target()
valkey_target = valkey_profile_target.profile_target(RedisRole.RATE_LIMIT)
valkey_client = await ValkeyRateLimitClient.create(
valkey_target=valkey_target,
db_id=REDIS_RATE_LIMIT_DB,
human_readable_name="ratelimit",
)
try:
rlim_window = await valkey_client.get_user_rlim_window(UserID(self.user))
return rlim_window.count if rlim_window is not None else 0
finally:
await valkey_client.close()
```

`create()` goes through `create_valkey_client()`, which builds **two** GLIDE clients — an
operation client and a monitor client — and `MonitoringValkeyClient.connect()` then starts a
background monitor task. All of that serves a single read, and it is repeated for every keypair
in the result, on every query that selects `rolling_count`.

This is the only request-path caller of `ValkeyRateLimitClient.create()`. The sibling resolvers on
the same type read a long-lived client off the context (`ctx.valkey_stat` at lines 197 and 243),
and the manager already holds a long-lived rate-limit client
(`manager/dependencies/infrastructure/redis.py:142`) that is passed to the REST layer at
`api/rest/setup.py:75`.

## Required Features

Resolve `rolling_count` from the long-lived client instead of a per-request one:

1. Add a `valkey_rate_limit: ValkeyRateLimitClient` field to `GraphQueryContext`
(`api/gql_legacy/schema.py:318`), alongside `valkey_stat` / `valkey_live` / `valkey_image` /
`valkey_schedule`.
2. Carry `r.infrastructure.valkey.rate_limit` through `GQLContextDeps` (`api/rest/types.py:59`,
populated in `api/rest/setup.py`) into the `GraphQueryContext(...)` built in
`api/rest/admin/handler.py` — reuse the existing client rather than creating a second one.
3. Reduce `resolve_rolling_count` to a single `get_user_rlim_window()` call on
`ctx.valkey_rate_limit`.

## Impact

| Area | Effect |
|---|---|
| `manager/api/gql_legacy/keypair.py` | `resolve_rolling_count` loses its client lifecycle |
| `manager/api/gql_legacy/schema.py` | One new `GraphQueryContext` field |
| `manager/api/rest/types.py`, `setup.py`, `admin/handler.py` | Pass the existing rate-limit client through |
| GraphQL `keypair`/`keypairs` queries selecting `rolling_count` | Two fewer GLIDE connections and one fewer monitor task per keypair |

No schema, config, or API-surface change. `rolling_count` values are unaffected — same client
type, same DB, same command.

## Testing Scenarios

| # | Scenario | Expected |
|---|---|---|
| 1 | Query `rolling_count` for a keypair with Valkey healthy | Same value as before the change |
| 2 | Query `rolling_count` for N keypairs | No client is created or closed during resolution; the context client is used |
| 3 | Query the sibling fields (`num_queries`, `last_used`) | Unchanged — they already read from the context |

Contributor guide

Open the contributing guide

Research direction

Start with resolve_rolling_count in manager/api/gql_legacy/keypair.py and the GraphQueryContext definition in api/gql_legacy/schema.py, then trace GQLContextDeps through api/rest/types.py, api/rest/setup.py, and api/rest/admin/handler.py. Confirm the existing rate-limit client reaches the GraphQL context, no client is created or closed during resolution, and the listed rolling_count and sibling-field scenarios retain their expected behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, redis
Domain
api, backend
Issue type
Refactor
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.