Skip to content

Commit aad5e45

Browse files
committed
Do not load all projects attrs for projects filtering
1 parent 165b1c3 commit aad5e45

5 files changed

Lines changed: 57 additions & 40 deletions

File tree

src/dstack/_internal/server/services/fleets.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
from dstack._internal.server.services.projects import (
6868
get_member,
6969
get_member_permissions,
70-
list_project_models,
7170
list_user_project_models,
7271
)
7372
from dstack._internal.server.services.resources import set_resources_defaults
@@ -88,10 +87,11 @@ async def list_fleets(
8887
limit: int,
8988
ascending: bool,
9089
) -> List[Fleet]:
91-
if user.global_role == GlobalRole.ADMIN:
92-
projects = await list_project_models(session=session)
93-
else:
94-
projects = await list_user_project_models(session=session, user=user)
90+
projects = await list_user_project_models(
91+
session=session,
92+
user=user,
93+
only_names=True,
94+
)
9595
if project_name is not None:
9696
projects = [p for p in projects if p.name == project_name]
9797
fleet_models = await list_projects_fleet_models(

src/dstack/_internal/server/services/instances.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
TerminationPolicy,
3535
)
3636
from dstack._internal.core.models.runs import JobProvisioningData, Requirements
37-
from dstack._internal.core.models.users import GlobalRole
3837
from dstack._internal.core.models.volumes import Volume
3938
from dstack._internal.core.services.profiles import get_termination
4039
from dstack._internal.server.models import (
@@ -44,7 +43,7 @@
4443
UserModel,
4544
)
4645
from dstack._internal.server.services.offers import generate_shared_offer
47-
from dstack._internal.server.services.projects import list_project_models, list_user_project_models
46+
from dstack._internal.server.services.projects import list_user_project_models
4847
from dstack._internal.utils import common as common_utils
4948
from dstack._internal.utils.logging import get_logger
5049

@@ -372,18 +371,15 @@ async def list_user_instances(
372371
limit: int,
373372
ascending: bool,
374373
) -> List[Instance]:
375-
if user.global_role == GlobalRole.ADMIN:
376-
projects = await list_project_models(session=session)
377-
else:
378-
projects = await list_user_project_models(session=session, user=user)
379-
if not projects:
380-
return []
381-
374+
projects = await list_user_project_models(
375+
session=session,
376+
user=user,
377+
only_names=True,
378+
)
382379
if project_names is not None:
383-
projects = [proj for proj in projects if proj.name in project_names]
380+
projects = [p for p in projects if p.name in project_names]
384381
if len(projects) == 0:
385382
return []
386-
387383
instance_models = await list_projects_instance_models(
388384
session=session,
389385
projects=projects,

src/dstack/_internal/server/services/projects.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from sqlalchemy import delete, select, update
55
from sqlalchemy import func as safunc
66
from sqlalchemy.ext.asyncio import AsyncSession
7-
from sqlalchemy.orm import joinedload
7+
from sqlalchemy.orm import QueryableAttribute, joinedload, load_only
88

99
from dstack._internal.core.backends.configurators import get_configurator
1010
from dstack._internal.core.backends.dstack.models import (
@@ -53,13 +53,12 @@ async def list_user_projects(
5353
user: UserModel,
5454
) -> List[Project]:
5555
"""
56-
Returns projects where the user is a member.
56+
Returns projects where the user is a member or all projects for global admins.
5757
"""
58-
if user.global_role == GlobalRole.ADMIN:
59-
projects = await list_project_models(session=session)
60-
else:
61-
projects = await list_user_project_models(session=session, user=user)
62-
58+
projects = await list_user_project_models(
59+
session=session,
60+
user=user,
61+
)
6362
projects = sorted(projects, key=lambda p: p.created_at)
6463
return [
6564
project_model_to_project(p, include_backends=False, include_members=False)
@@ -79,7 +78,7 @@ async def list_user_accessible_projects(
7978
if user.global_role == GlobalRole.ADMIN:
8079
projects = await list_project_models(session=session)
8180
else:
82-
member_projects = await list_user_project_models(session=session, user=user)
81+
member_projects = await list_member_project_models(session=session, user=user)
8382
public_projects = await list_public_non_member_project_models(session=session, user=user)
8483
projects = member_projects + public_projects
8584

@@ -166,7 +165,7 @@ async def delete_projects(
166165
projects_names: List[str],
167166
):
168167
if user.global_role != GlobalRole.ADMIN:
169-
user_projects = await list_user_project_models(
168+
user_projects = await list_member_project_models(
170169
session=session, user=user, include_members=True
171170
)
172171
user_project_names = [p.name for p in user_projects]
@@ -338,16 +337,34 @@ async def clear_project_members(
338337

339338

340339
async def list_user_project_models(
340+
session: AsyncSession,
341+
user: UserModel,
342+
only_names: bool = False,
343+
) -> List[ProjectModel]:
344+
load_only_attrs = []
345+
if only_names:
346+
load_only_attrs += [ProjectModel.id, ProjectModel.name]
347+
if user.global_role == GlobalRole.ADMIN:
348+
return await list_project_models(session=session, load_only_attrs=load_only_attrs)
349+
return await list_member_project_models(
350+
session=session, user=user, load_only_attrs=load_only_attrs
351+
)
352+
353+
354+
async def list_member_project_models(
341355
session: AsyncSession,
342356
user: UserModel,
343357
include_members: bool = False,
358+
load_only_attrs: Optional[List[QueryableAttribute]] = None,
344359
) -> List[ProjectModel]:
345360
"""
346361
List project models for a user where they are a member.
347362
"""
348363
options = []
349364
if include_members:
350365
options.append(joinedload(ProjectModel.members))
366+
if load_only_attrs:
367+
options.append(load_only(*load_only_attrs))
351368
res = await session.execute(
352369
select(ProjectModel)
353370
.where(
@@ -394,9 +411,13 @@ async def list_user_owned_project_models(
394411

395412
async def list_project_models(
396413
session: AsyncSession,
414+
load_only_attrs: Optional[List[QueryableAttribute]] = None,
397415
) -> List[ProjectModel]:
416+
options = []
417+
if load_only_attrs:
418+
options.append(load_only(*load_only_attrs))
398419
res = await session.execute(
399-
select(ProjectModel).where(ProjectModel.deleted == False),
420+
select(ProjectModel).where(ProjectModel.deleted == False).options(*options)
400421
)
401422
return list(res.scalars().all())
402423

src/dstack/_internal/server/services/runs.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from apscheduler.triggers.cron import CronTrigger
99
from sqlalchemy import and_, func, or_, select, update
1010
from sqlalchemy.ext.asyncio import AsyncSession
11-
from sqlalchemy.orm import joinedload, selectinload
11+
from sqlalchemy.orm import joinedload
1212

1313
import dstack._internal.utils.common as common_utils
1414
from dstack._internal.core.errors import (
@@ -43,7 +43,6 @@
4343
RunTerminationReason,
4444
ServiceSpec,
4545
)
46-
from dstack._internal.core.models.users import GlobalRole
4746
from dstack._internal.core.models.volumes import (
4847
InstanceMountPoint,
4948
Volume,
@@ -82,7 +81,7 @@
8281
from dstack._internal.server.services.logging import fmt
8382
from dstack._internal.server.services.offers import get_offers_by_requirements
8483
from dstack._internal.server.services.plugins import apply_plugin_policies
85-
from dstack._internal.server.services.projects import list_project_models, list_user_project_models
84+
from dstack._internal.server.services.projects import list_user_project_models
8685
from dstack._internal.server.services.resources import set_resources_defaults
8786
from dstack._internal.server.services.secrets import get_project_secrets_mapping
8887
from dstack._internal.server.services.users import get_user_model_by_name
@@ -116,10 +115,11 @@ async def list_user_runs(
116115
) -> List[Run]:
117116
if project_name is None and repo_id is not None:
118117
return []
119-
if user.global_role == GlobalRole.ADMIN:
120-
projects = await list_project_models(session=session)
121-
else:
122-
projects = await list_user_project_models(session=session, user=user)
118+
projects = await list_user_project_models(
119+
session=session,
120+
user=user,
121+
only_names=True,
122+
)
123123
runs_user = None
124124
if username is not None:
125125
runs_user = await get_user_model_by_name(session=session, username=username)
@@ -218,9 +218,9 @@ async def list_projects_run_models(
218218
res = await session.execute(
219219
select(RunModel)
220220
.where(*filters)
221+
.options(joinedload(RunModel.user).load_only(UserModel.name))
221222
.order_by(*order_by)
222223
.limit(limit)
223-
.options(selectinload(RunModel.user))
224224
)
225225
run_models = list(res.scalars().all())
226226
return run_models

src/dstack/_internal/server/services/volumes.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
ResourceExistsError,
1414
ServerClientError,
1515
)
16-
from dstack._internal.core.models.users import GlobalRole
1716
from dstack._internal.core.models.volumes import (
1817
Volume,
1918
VolumeAttachment,
@@ -40,7 +39,7 @@
4039
string_to_lock_id,
4140
)
4241
from dstack._internal.server.services.plugins import apply_plugin_policies
43-
from dstack._internal.server.services.projects import list_project_models, list_user_project_models
42+
from dstack._internal.server.services.projects import list_user_project_models
4443
from dstack._internal.utils import common, random_names
4544
from dstack._internal.utils.logging import get_logger
4645

@@ -57,10 +56,11 @@ async def list_volumes(
5756
limit: int,
5857
ascending: bool,
5958
) -> List[Volume]:
60-
if user.global_role == GlobalRole.ADMIN:
61-
projects = await list_project_models(session=session)
62-
else:
63-
projects = await list_user_project_models(session=session, user=user)
59+
projects = await list_user_project_models(
60+
session=session,
61+
user=user,
62+
only_names=True,
63+
)
6464
if project_name is not None:
6565
projects = [p for p in projects if p.name == project_name]
6666
volume_models = await list_projects_volume_models(

0 commit comments

Comments
 (0)