Skip to content
Closed
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
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,7 @@
## 2024-05-24 - Avoid Copying Large Sets for Membership Checks
**Learning:** Copying a large set (e.g. 100k items) to create a snapshot for read-only membership checks is expensive O(N) and unnecessary. Python's set membership testing is thread-safe.
**Action:** When filtering data against a shared large set, iterate and check membership directly instead of snapshotting, unless strict transactional consistency across the entire iteration is required.

## 2025-02-24 - Parallelize Batch Deletions
**Learning:** Sequential deletion of resources (folders) via REST API is a major bottleneck when syncing state, as latency accumulates linearly. Since deletions are independent operations, they can be parallelized safely.
**Action:** Use `ThreadPoolExecutor` to parallelize deletion loops, but limit max_workers (e.g., 5) to avoid rate limits.
17 changes: 14 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,12 +646,23 @@
existing_folders = list_existing_folders(client, profile_id)
if not no_delete:
deletion_occurred = False
folders_to_delete = []
for folder_data in folder_data_list:
name = folder_data["group"]["group"].strip()
if name in existing_folders:
delete_folder(client, profile_id, name, existing_folders[name])
deletion_occurred = True

folders_to_delete.append((name, existing_folders[name]))

if folders_to_delete:
# Parallelize deletion: Use 5 workers to speed up deletions without hammering the API too hard

Check warning

Code scanning / Pylintpython3 (reported by Codacy)

Line too long (114/100) Warning

Line too long (114/100)

Check warning

Code scanning / Pylint (reported by Codacy)

Line too long (114/100) Warning

Line too long (114/100)
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = [
executor.submit(delete_folder, client, profile_id, name, folder_id)
for name, folder_id in folders_to_delete
Comment on lines +657 to +660
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The same httpx.Client instance is being shared across multiple threads in the ThreadPoolExecutor. httpx.Client is not thread-safe and should not be shared across threads without synchronization. Each thread should either create its own client instance, or the code should use proper locking mechanisms. Consider creating a new client within delete_folder or passing a client factory function instead of sharing the same client instance.

Copilot uses AI. Check for mistakes.
]
for future in concurrent.futures.as_completed(futures):
if future.result():
deletion_occurred = True
Comment on lines +663 to +664
Copy link

Copilot AI Jan 9, 2026

Choose a reason for hiding this comment

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

The parallel deletion loop lacks exception handling around future.result() calls. If delete_folder raises an exception other than httpx.HTTPError (which is already caught inside delete_folder), the exception will propagate here and potentially crash the sync process. Consider wrapping future.result() in a try-except block to handle unexpected exceptions gracefully, similar to how it's done in the folder processing loop at lines 690-694.

Suggested change
if future.result():
deletion_occurred = True
try:
if future.result():
deletion_occurred = True
except Exception as e:
log.error(f"Failed to delete folder during parallel deletion: {e}")

Copilot uses AI. Check for mistakes.

# CRITICAL FIX: Increased wait time for massive folders to clear
if deletion_occurred:
if not USE_COLORS:
Expand Down
12 changes: 11 additions & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading