Skip to content

fix: expose max_concurrent_logins on Graphene UserResourcePolicy - #11075

Closed
adrysn wants to merge 3 commits into
mainfrom
fix/expose-max-concurrent-logins-on-graphene-user-resource-policy
Closed

adrysn wants to merge 3 commits into
mainfrom
fix/expose-max-concurrent-logins-on-graphene-user-resource-policy

Conversation

@adrysn

@adrysn adrysn commented Apr 14, 2026 •

Copy link
Copy Markdown
Member

Mistake PR in testing...

Summary

  • Adds the max_concurrent_logins field to the legacy Graphene UserResourcePolicy type, along with create/modify input types and their spec mappings
  • Closes the gap left by feat(BA-5619): Support max concurrent login sessions per user #10838, which introduced max_concurrent_logins in the DB model, repository specs, and Strawberry v2 (UserResourcePolicyV2GQL) but did not update the Graphene schema still used by the Control Panel

Problem

Control Panel's User Resource Policy page (feature/1581) queries max_concurrent_logins on UserResourcePolicy. 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:

Cannot query field 'max_concurrent_logins' on type 'UserResourcePolicy'

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:

  1. UserResourcePolicy ObjectType — declare max_concurrent_logins = graphene.Int(...) and pass row.max_concurrent_logins through from_row().
  2. CreateUserResourcePolicyInput — declare the field and pass it to UserResourcePolicyCreatorSpec via an optional_int helper so Undefined maps to None (the spec's default, meaning unlimited). The existing value_or_default coerces to int and would drop the None semantics, so it could not be reused here.
  3. ModifyUserResourcePolicyInput — declare the field and pass it to UserResourcePolicyUpdaterSpec via TriState[int].from_graphql(...), matching the spec's TriState[int] field type (distinct from the other fields which use OptionalState) so explicit null-clearing is supported.

All mappings downstream (UserResourcePolicyCreatorSpec, UserResourcePolicyUpdaterSpec, UserResourcePolicyRow, Alembic 689f66507280) already support the field.

Test plan

  • uv run ruff check src/ai/backend/manager/api/gql_legacy/resource_policy.py passes
  • Start a 26.4.x manager with this patch; confirm query { user_resource_policies { max_concurrent_logins } } returns the column value
  • Create a user resource policy via Graphene mutation with max_concurrent_logins: 5 and verify it persists
  • Modify a user resource policy to set max_concurrent_logins: null and verify the column is cleared (TriState semantics)
  • Against this manager, open Control Panel (refactor,fix: Improve manager CLI logging initialization #1581 branch) → User Resource Policy → verify no "Cannot query field" error and the column/form field display correctly

Related

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.
Copilot AI review requested due to automatic review settings April 14, 2026 13:48
@github-actions github-actions Bot added size:S 10~30 LoC comp:manager Related to Manager component labels Apr 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_logins to the UserResourcePolicy Graphene ObjectType and map it from DB rows.
  • Add max_concurrent_logins to 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."

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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."

Copilot uses AI. Check for mistakes.
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."

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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."

Copilot uses AI. Check for mistakes.
Comment on lines +647 to +649
max_concurrent_logins = graphene.Int(
description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited."
)

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +518 to +520
max_concurrent_logins = graphene.Int(
description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited."
)

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +608 to +610
max_concurrent_logins = graphene.Int(
description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited."
)

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +647 to +649
max_concurrent_logins = graphene.Int(
description="Added in 26.4.0. Maximum number of concurrent authenticated login sessions per user. Null means unlimited."
)

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Co-authored-by: octodog <mu001@lablup.com>
@github-actions github-actions Bot added size:M 30~100 LoC area:docs Documentations and removed size:S 10~30 LoC labels Apr 14, 2026
@adrysn adrysn closed this Apr 14, 2026
@adrysn
adrysn deleted the fix/expose-max-concurrent-logins-on-graphene-user-resource-policy branch April 14, 2026 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:docs Documentations comp:manager Related to Manager component size:M 30~100 LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants