Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Purge user after deleting records that reference the user #2374

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
36b2cf9
fix: Delete vfolders in background when purge user
fregataa Jul 4, 2024
f320d36
minor log update
fregataa Jul 4, 2024
b7eb52c
Merge branch 'main' into fix/purge-user-after-deleting-all-user-refer…
fregataa Jul 4, 2024
feee137
add version notation to new gql schema field
fregataa Jul 4, 2024
1e4c934
naming update
fregataa Jul 4, 2024
29a17b9
chore: update GraphQL schema dump
fregataa Jul 4, 2024
b96a72e
add news fragment
fregataa Jul 4, 2024
7c41bb6
rename methods and variables
fregataa Jul 4, 2024
edc7bb6
update news fragment
fregataa Jul 4, 2024
88790f3
add TODO comment
fregataa Jul 4, 2024
195095a
Merge branch 'main' into fix/purge-user-after-deleting-all-user-refer…
fregataa Jul 4, 2024
bd1b18a
minor nameing
fregataa Jul 4, 2024
afdddef
synchronously delete vfolder data and db record
fregataa Jul 4, 2024
74e26fe
apply txn retry
fregataa Jul 4, 2024
d0d56bf
CLI purge user cmd gets bgtask_id
fregataa Jul 4, 2024
a9f7314
Merge branch 'main' into fix/purge-user-after-deleting-all-user-refer…
fregataa Jul 5, 2024
0bd6594
align usage of db session and cxn
fregataa Jul 5, 2024
1be1688
group vfolders by storage proxy
fregataa Jul 5, 2024
473d27b
update message
fregataa Jul 6, 2024
e5650ba
Merge branch 'main' into fix/purge-user-after-deleting-all-user-refer…
fregataa Jul 6, 2024
a17adb7
add func docstring
fregataa Jul 6, 2024
6525963
Merge branch 'main' into fix/purge-user-after-deleting-all-user-refer…
fregataa Jul 6, 2024
dc53f65
minor naming update
fregataa Jul 7, 2024
5b152a2
update type hints
fregataa Jul 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/2374.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix `purge_user` mutation by preceding the mutation with deletion of records that reference the user.
1 change: 1 addition & 0 deletions src/ai/backend/client/cli/admin/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,5 +548,6 @@ def purge(ctx: CLIContext, email, purge_shared_vfolders):
data,
extra_info={
"email": email,
"bgtask_id": data.get("bgtask_id"),
},
)
2 changes: 1 addition & 1 deletion src/ai/backend/client/func/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ async def purge(cls, email: str, purge_shared_vfolders=False):
"""\
mutation($email: String!, $input: PurgeUserInput!) {
purge_user(email: $email, props: $input) {
ok msg
ok msg bgtask_id
}
}
"""
Expand Down
3 changes: 3 additions & 0 deletions src/ai/backend/manager/api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,9 @@
type PurgeUser {
ok: Boolean
msg: String

"""Added in 24.03.7. ID of background task deleting user owned vfolders."""
bgtask_id: UUID

Check notice on line 1372 in src/ai/backend/manager/api/schema.graphql

View workflow job for this annotation

GitHub Actions / GraphQL Inspector

Field 'bgtask_id' was added to object type 'PurgeUser'

Field 'bgtask_id' was added to object type 'PurgeUser'
}

input PurgeUserInput {
Expand Down
73 changes: 73 additions & 0 deletions src/ai/backend/manager/models/keypair.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import base64
import logging
import secrets
import uuid
from datetime import datetime
Expand All @@ -15,10 +16,12 @@
from graphene.types.datetime import DateTime as GQLDateTime
from sqlalchemy.engine.row import Row
from sqlalchemy.ext.asyncio import AsyncConnection as SAConnection
from sqlalchemy.ext.asyncio import AsyncSession as SASession
from sqlalchemy.orm import relationship
from sqlalchemy.sql.expression import false

from ai.backend.common import msgpack, redis_helper
from ai.backend.common.logging import BraceStyleAdapter
from ai.backend.common.types import AccessKey, SecretKey

if TYPE_CHECKING:
Expand All @@ -43,6 +46,9 @@
from .user import ModifyUserInput, UserRole
from .utils import agg_to_array

log = BraceStyleAdapter(logging.getLogger(__spec__.name)) # type: ignore[name-defined]


__all__: Sequence[str] = (
"keypairs",
"KeyPairRow",
Expand Down Expand Up @@ -718,3 +724,70 @@ def verify_dotfile_name(dotfile: str) -> bool:
if dotfile in RESERVED_DOTFILES:
return False
return True


async def delete_kernels_by_access_key(
db_session: SASession,
access_key: AccessKey,
) -> int:
"""
Delete keypair's all kernels.

:param conn: DB connection
:param access_key: access key to delete kernels
:return: number of deleted rows
"""
from . import KernelRow

result = await db_session.execute(
sa.delete(KernelRow).where(KernelRow.access_key == access_key),
)
if result.rowcount > 0:
log.info("deleted {0} keypair's kernels (ak:{1})", result.rowcount, access_key)
return result.rowcount


async def delete_sessions_by_access_key(
db_session: SASession,
access_key: AccessKey,
) -> int:
"""
Delete keypair's all sessions.

:param db_session: SQLAlchemy session
:param access_key: access key to delete sessions
:return: number of deleted rows
"""
from .session import SessionRow

result = await db_session.execute(
sa.delete(SessionRow).where(SessionRow.access_key == access_key)
)
if result.rowcount > 0:
log.info("deleted {0} user's sessions (ak:{1})", result.rowcount, access_key)
return result.rowcount


async def access_key_has_active_sessions(
db_session: SASession,
access_key: AccessKey,
) -> bool:
"""
Check if the keypair does not have active sessions.

:param db_session: SQLAlchemy session
:param access_key: access key

:return: True if the access key has some active sessions.
"""
from . import AGENT_RESOURCE_OCCUPYING_KERNEL_STATUSES, KernelRow

active_kernel_count = await db_session.scalar(
sa.select(sa.func.count())
.select_from(KernelRow)
.where(
(KernelRow.access_key == access_key)
& (KernelRow.status.in_(AGENT_RESOURCE_OCCUPYING_KERNEL_STATUSES)),
),
)
return active_kernel_count > 0
Loading
Loading