perf: stop scanning every skill on each listing and chat turn (#28798)
Listing skills ran one database query per skill in the instance. A non-admin opening the list on a workspace with 500 skills issued over 500 queries, the paginated list re-resolved the caller's group membership once per row, and every chat message carrying a skill loaded every skill the user can read, full body and owner included, to use the two or three it actually referenced. Skills now arrive already filtered: the owner-or-grant check runs in the query as an EXISTS subquery, the same way prompts and the search endpoints already do it, the per-item write flag uses the existing batch grant lookup, and the chat path asks only for the skill ids the request names. Measured with 500 skills of which 3 are visible to the caller: 504 queries and ~300 ms before, 4 queries and ~2.6 ms after. The resulting set is unchanged for owner, public, direct-user, group and multi-grant entries, for both read and write.
This commit is contained in:
1 parent
9bdb072690
commit
dbf715cb63
3 files changed
+40
-58
No files matched your search
@@ -46,21 +46,7 @@ async def get_skills(
|
||||
if user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL:
|
||||
skills = await Skills.get_skills(db=db)
|
||||
else:
|
||||
user_group_ids = {group.id for group in await Groups.get_groups_by_member_id(user.id, db=db)}
|
||||
all_skills = await Skills.get_skills(db=db)
|
||||
skills = [
|
||||
skill
|
||||
for skill in all_skills
|
||||
if skill.user_id == user.id
|
||||
or await AccessGrants.has_access(
|
||||
user_id=user.id,
|
||||
resource_type='skill',
|
||||
resource_id=skill.id,
|
||||
permission='read',
|
||||
user_group_ids=user_group_ids,
|
||||
db=db,
|
||||
)
|
||||
]
|
||||
skills = await Skills.get_skills(db=db, user_id=user.id)
|
||||
|
||||
if query:
|
||||
q = query.casefold()
|
||||
@@ -99,30 +85,29 @@ async def get_skill_list(
|
||||
if direction:
|
||||
filter['direction'] = direction
|
||||
|
||||
if not (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL):
|
||||
groups = await Groups.get_groups_by_member_id(user.id, db=db)
|
||||
if groups:
|
||||
filter['group_ids'] = [group.id for group in groups]
|
||||
is_bypass_admin = user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL
|
||||
user_group_ids = {group.id for group in await Groups.get_groups_by_member_id(user.id, db=db)}
|
||||
|
||||
if not is_bypass_admin:
|
||||
filter['group_ids'] = user_group_ids
|
||||
filter['user_id'] = user.id
|
||||
|
||||
result = await Skills.search_skills(user.id, filter=filter, skip=skip, limit=limit, db=db)
|
||||
|
||||
writable_skill_ids = await AccessGrants.get_accessible_resource_ids(
|
||||
user_id=user.id,
|
||||
resource_type='skill',
|
||||
resource_ids=[skill.id for skill in result.items],
|
||||
permission='write',
|
||||
user_group_ids=user_group_ids,
|
||||
db=db,
|
||||
)
|
||||
|
||||
return SkillAccessListResponse(
|
||||
items=[
|
||||
SkillAccessResponse(
|
||||
**skill.model_dump(),
|
||||
write_access=(
|
||||
(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||
or user.id == skill.user_id
|
||||
or await AccessGrants.has_access(
|
||||
user_id=user.id,
|
||||
resource_type='skill',
|
||||
resource_id=skill.id,
|
||||
permission='write',
|
||||
db=db,
|
||||
)
|
||||
),
|
||||
write_access=(is_bypass_admin or user.id == skill.user_id or skill.id in writable_skill_ids),
|
||||
)
|
||||
for skill in result.items
|
||||
],
|
||||
@@ -155,7 +140,7 @@ async def export_skills(
|
||||
if user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL:
|
||||
return await Skills.get_skills(db=db)
|
||||
else:
|
||||
return await Skills.get_skills_by_user_id(user.id, 'read', db=db)
|
||||
return await Skills.get_skills(db=db, user_id=user.id)
|
||||
|
||||
|
||||
############################
|
||||
|
||||
Reference in new issue
Block a user