Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion apps/worker/services/bundle_analysis/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,14 @@ def _attempt_init_from_previous_report(
# if caching is on then update bundle.is_cached property to true
# if caching is off then delete that bundle from the report
update_fields = {}
bundles_to_delete = []
for bundle in bundle_report.bundle_reports():
if bundle.name in bundles_to_be_cached:
update_fields[bundle.name] = True
else:
bundle_report.delete_bundle_by_name(bundle.name)
bundles_to_delete.append(bundle.name)
if bundles_to_delete:
bundle_report.delete_bundles_by_names(bundles_to_delete)
if update_fields:
bundle_report.update_is_cached(update_fields)
return bundle_report
Expand Down
44 changes: 44 additions & 0 deletions libs/shared/shared/bundle_analysis/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,3 +632,47 @@ def delete_bundle_by_name(self, bundle_name: str) -> None:
session.delete(bundle_to_be_deleted)

session.commit()

@sentry_sdk.trace
def delete_bundles_by_names(self, bundle_names: list[str]) -> None:
"""Batch delete multiple bundles by name in a single database session,
avoiding N+1 queries when deleting many bundles at once."""
if not bundle_names:
return
with get_db_session(self.db_path) as session:
bundles_to_delete = (
session.query(Bundle)
.filter(Bundle.name.in_(bundle_names))
.all()
)
if not bundles_to_delete:
return

bundle_ids = [b.id for b in bundles_to_delete]

sessions_to_delete = (
session.query(Session)
.filter(Session.bundle_id.in_(bundle_ids))
.all()
)
session_ids = [s.id for s in sessions_to_delete]

if len(sessions_to_delete) != len(bundles_to_delete):
raise Exception(
"Data integrity error - cannot have Bundles without Sessions"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Aggregate count check weakens data integrity validation

Medium Severity

The integrity check len(sessions_to_delete) != len(bundles_to_delete) only compares aggregate counts, unlike the original one_or_none() which validated each bundle individually. If one bundle has two sessions and another has zero, the counts balance out (e.g., 2 sessions == 2 bundles) and the check passes — silently masking a real data integrity issue where a bundle exists without a session. The original per-bundle one_or_none() would have caught both the missing-session and the multiple-sessions cases independently.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 328e0f0. Configure here.


# Deletes Asset, Chunk, Module for all matching sessions at once
for model in [Asset, Chunk, Module]:
stmt = model.__table__.delete().where(
model.session_id.in_(session_ids)
)
session.execute(stmt)

# Deletes Sessions and Bundles
for session_to_delete in sessions_to_delete:
session.delete(session_to_delete)
for bundle_to_delete in bundles_to_delete:
session.delete(bundle_to_delete)

session.commit()
Loading