From 6f7582a1f3b36a125e57e95a5171d61f5cbaabd0 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 28 Mar 2026 18:59:13 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20JSON=20file=20re?= =?UTF-8?q?pository=20save=5Fitems=20batching?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ivangegovdve-sudo <225339531+ivangegovdve-sudo@users.noreply.github.com> --- .jules/bolt.md | 4 +++ .../adapters/json_file_practice_repository.py | 27 ++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 699e7a3..30ed01e 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -13,3 +13,7 @@ ## 2024-03-16 - Batch JSON File I/O Operations **Learning:** When using JSON file-backed repositories, iterating over items sequentially and calling `save_item` or `record_attempt` inside a loop leads to N+1 file read/write operations. This creates a significant performance bottleneck, especially when importing progress snapshots with numerous items and attempts. **Action:** Prefer batch processing methods (e.g., `save_items`, `record_attempts`) so file-backed adapters can load storage once, update it in memory, and write it back in a single pass. + +## 2025-03-28 - In-place JSON Dictionary Updates +**Learning:** Fully deserializing and reserializing all stored records in JSON file-backed adapters (like `JsonFilePracticeRepository`) during batch operations (`save_items`) creates a significant O(N) CPU/memory bottleneck. +**Action:** When saving multiple records, avoid full domain conversions (`_item_from_dict` / `_item_to_dict` loops) for unchanged items. Instead, update the raw dictionary entries in-place before re-saving the file. diff --git a/src/python_learning_orchestrated/adapters/json_file_practice_repository.py b/src/python_learning_orchestrated/adapters/json_file_practice_repository.py index 15da61b..efc1277 100644 --- a/src/python_learning_orchestrated/adapters/json_file_practice_repository.py +++ b/src/python_learning_orchestrated/adapters/json_file_practice_repository.py @@ -46,15 +46,24 @@ def save_items(self, items: list[LearningItem]) -> None: storage = self._load_storage() raw_items = storage.get("items", []) - existing_items = [] - if isinstance(raw_items, list): - existing_items = [ - _item_from_dict(entry) for entry in raw_items if isinstance(entry, dict) - ] - by_id = {existing.id: existing for existing in existing_items} - for item in items: - by_id[item.id] = item - storage["items"] = [_item_to_dict(entry) for entry in by_id.values()] + if not isinstance(raw_items, list): + raw_items = [] + + # Avoid O(N) CPU/memory penalties by doing in-place updates of raw entries + items_by_id = {str(item.id): _item_to_dict(item) for item in items} + + updated_raw_items = [] + for entry in raw_items: + if isinstance(entry, dict): + entry_id = str(entry.get("id")) + if entry_id in items_by_id: + updated_raw_items.append(items_by_id.pop(entry_id)) + else: + updated_raw_items.append(entry) + + updated_raw_items.extend(items_by_id.values()) + + storage["items"] = updated_raw_items self._save_storage(storage) def list_attempts(self) -> list[Attempt]: