Repository navigation
Conversation
The `max_concurrent_logins` field was added to the DB model, repository specs, and Strawberry v2 (UserResourcePolicyV2GQL) in #10838, but the legacy Graphene UserResourcePolicy type was not updated to expose the field. This caused control-panel to fail with "Cannot query field 'max_concurrent_logins' on type 'UserResourcePolicy'" when a manager >= 26.4.0 is connected. Add the field and the corresponding mappings to three places in gql_legacy/resource_policy.py: - UserResourcePolicy ObjectType + from_row() row-to-GQL mapping - CreateUserResourcePolicyInput + to_creator() passing through Undefined as None so the creator spec default (None = unlimited) is honored - ModifyUserResourcePolicyInput + to_updater() using TriState[int].from_graphql() to match the updater spec field type, which allows explicit null-clearing semantics Fixes control-panel issue lablup/control-panel#1581.
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates the legacy Graphene GraphQL schema to expose max_concurrent_logins on UserResourcePolicy, closing a compatibility gap for clients (e.g., Control Panel) that still query the Graphene endpoint.
Changes:
- Add
max_concurrent_loginsto theUserResourcePolicyGrapheneObjectTypeand map it from DB rows. - Add
max_concurrent_loginsto create/modify input types and map to the corresponding creator/updater specs. - Add a changelog entry documenting the Graphene exposure.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| src/ai/backend/manager/api/gql_legacy/resource_policy.py | Exposes max_concurrent_logins on legacy Graphene type and mutation inputs with correct spec mappings. |
| changes/11075.fix.md | Adds a release note about exposing the field on the legacy Graphene schema. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| description="Added in 24.03.0. Maximum available number of customized images one can publish to." | ||
| ) | ||
| max_concurrent_logins = graphene.Int( | ||
| description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." |
There was a problem hiding this comment.
The version formatting in the added descriptions (26.4.0) is inconsistent with nearby schema descriptions that use zero-padded minor versions (e.g., 24.03.0). Consider aligning the version string format in these descriptions to match the established convention in this file for consistency in the public GraphQL schema docs.
| description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." | |
| description="Added in 26.04.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." |
| description="Added in 24.03.0. Maximum available number of customized images one can publish to." | ||
| ) | ||
| max_concurrent_logins = graphene.Int( | ||
| description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." |
There was a problem hiding this comment.
The version formatting in the added descriptions (26.4.0) is inconsistent with nearby schema descriptions that use zero-padded minor versions (e.g., 24.03.0). Consider aligning the version string format in these descriptions to match the established convention in this file for consistency in the public GraphQL schema docs.
| description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." | |
| description="Added in 26.04.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." |
| max_concurrent_logins = graphene.Int( | ||
| description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." | ||
| ) |
There was a problem hiding this comment.
The version formatting in the added descriptions (26.4.0) is inconsistent with nearby schema descriptions that use zero-padded minor versions (e.g., 24.03.0). Consider aligning the version string format in these descriptions to match the established convention in this file for consistency in the public GraphQL schema docs.
| max_concurrent_logins = graphene.Int( | ||
| description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." | ||
| ) |
There was a problem hiding this comment.
The exact same long description string is duplicated across the ObjectType and both input types. To reduce future drift (e.g., if wording changes), consider extracting it into a single module-level constant (or a small helper) and reusing it in all three field declarations.
| max_concurrent_logins = graphene.Int( | ||
| description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." | ||
| ) |
There was a problem hiding this comment.
The exact same long description string is duplicated across the ObjectType and both input types. To reduce future drift (e.g., if wording changes), consider extracting it into a single module-level constant (or a small helper) and reusing it in all three field declarations.
| max_concurrent_logins = graphene.Int( | ||
| description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited." | ||
| ) |
There was a problem hiding this comment.
The exact same long description string is duplicated across the ObjectType and both input types. To reduce future drift (e.g., if wording changes), consider extracting it into a single module-level constant (or a small helper) and reusing it in all three field declarations.
Co-authored-by: octodog <mu001@lablup.com>
Mistake PR in testing...
Summary
max_concurrent_loginsfield to the legacy GrapheneUserResourcePolicytype, along with create/modify input types and their spec mappingsmax_concurrent_loginsin the DB model, repository specs, and Strawberry v2 (UserResourcePolicyV2GQL) but did not update the Graphene schema still used by the Control PanelProblem
Control Panel's User Resource Policy page (feature/1581) queries
max_concurrent_loginsonUserResourcePolicy. When a manager >= 26.4.0 is connected, the client's@since(version: "26.4.0")gating no longer strips the field, so the query is forwarded to Graphene and fails with:See lablup/control-panel#1581 for the downstream impact and #1580 for the hotfix-revert the field was blocked on.
Changes
src/ai/backend/manager/api/gql_legacy/resource_policy.py:UserResourcePolicyObjectType — declaremax_concurrent_logins = graphene.Int(...)and passrow.max_concurrent_loginsthroughfrom_row().CreateUserResourcePolicyInput— declare the field and pass it toUserResourcePolicyCreatorSpecvia anoptional_inthelper soUndefinedmaps toNone(the spec's default, meaning unlimited). The existingvalue_or_defaultcoerces tointand would drop theNonesemantics, so it could not be reused here.ModifyUserResourcePolicyInput— declare the field and pass it toUserResourcePolicyUpdaterSpecviaTriState[int].from_graphql(...), matching the spec'sTriState[int]field type (distinct from the other fields which useOptionalState) so explicit null-clearing is supported.All mappings downstream (
UserResourcePolicyCreatorSpec,UserResourcePolicyUpdaterSpec,UserResourcePolicyRow, Alembic689f66507280) already support the field.Test plan
uv run ruff check src/ai/backend/manager/api/gql_legacy/resource_policy.pypassesquery { user_resource_policies { max_concurrent_logins } }returns the column valuemax_concurrent_logins: 5and verify it persistsmax_concurrent_logins: nulland verify the column is cleared (TriState semantics)Related