From f3fcb989b35ceab05d3a12b78594eef008c7c0e0 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:40:11 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Parallelize=20folder=20dele?= =?UTF-8?q?tion=20in=20sync=5Fprofile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/bolt.md | 4 ++++ main.py | 17 ++++++++++++++--- uv.lock | 12 +++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index c717282b..61118b80 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -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. diff --git a/main.py b/main.py index e6aabc57..fe1dbe95 100644 --- a/main.py +++ b/main.py @@ -646,12 +646,23 @@ def sync_profile( 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 + 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 + ] + for future in concurrent.futures.as_completed(futures): + if future.result(): + deletion_occurred = True + # CRITICAL FIX: Increased wait time for massive folders to clear if deletion_occurred: if not USE_COLORS: diff --git a/uv.lock b/uv.lock index e337fe39..342c5f25 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.13" +requires-python = ">=3.12" [[package]] name = "anyio" @@ -9,6 +9,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f1/b4/636b3b65173d3ce9a38ef5f0522789614e590dab6a8d505340a4efe4c567/anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6", size = 213252, upload-time = "2025-08-04T08:54:26.451Z" } wheels = [ @@ -102,3 +103,12 @@ sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e0 wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +]