⚡ Bolt: [performance improvement] Memoize blueprint variant lookups to prevent N+1 API bottlenecks#47
Conversation
Added `@functools.lru_cache` to `get_variants` in `printify_manager.py`, `create_merch.py`, and `merch_factory.py`. Changed return types from mutable `list` to immutable `tuple` to ensure thread safety and prevent downstream modification of shared cached objects. Co-authored-by: merg357 <221854052+merg357@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Pull request overview
This PR reduces redundant Printify API calls during bulk product creation by memoizing blueprint/provider variant lookups and returning immutable variant ID collections to avoid cached-value mutation.
Changes:
- Added
@functools.lru_cache(maxsize=None)toget_variantsto prevent repeated/variants.jsonrequests in iterative workflows. - Switched
get_variantsreturn values fromlistto immutabletuple(and updated typing increate_merch.py). - Added a Bolt note documenting the N+1 caching learning/action.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| printify_manager.py | Memoizes get_variants and returns tuple variant IDs to reduce repeated Printify calls in the unified CLI. |
| merch_factory.py | Memoizes get_variants and returns tuple variant IDs to reduce repeated Printify calls in factory creation loops. |
| create_merch.py | Memoizes get_variants, updates typing to Tuple[int, ...], and returns tuples/empty tuple for variants. |
| .jules/bolt.md | Documents the N+1 API bottleneck learning and the memoization + immutability action pattern. |
Comments suppressed due to low confidence (1)
merch_factory.py:55
get_variantsnow returns a tuple, butcreate_product(..., variant_ids: list, ...)still advertisesvariant_idsas alist. Either updatecreate_product's type to accept an immutable sequence (e.g.,Sequence[int]/Iterable[int]) or explicitly convertvariant_idsto a list before passing it, so the type hints match runtime behavior.
@functools.lru_cache(maxsize=None)
def get_variants(blueprint_id: int, provider_id: int) -> tuple:
url = f"{BASE_URL}/catalog/blueprints/{blueprint_id}/print_providers/{provider_id}/variants.json"
resp = requests.get(url, headers=HEADERS)
resp.raise_for_status()
return tuple([v["id"] for v in resp.json().get("variants", [])[:4]])
def create_product(title: str, blueprint_id: int, provider_id: int,
variant_ids: list, image_id: str) -> dict:
url = f"{BASE_URL}/shops/{SHOP_ID}/products.json"
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def get_variants(blueprint_id: int, provider_id: int) -> tuple: | ||
| url = f"{BASE_URL}/catalog/blueprints/{blueprint_id}/print_providers/{provider_id}/variants.json" | ||
| r = requests.get(url, headers=AUTH_H, timeout=15) | ||
| r.raise_for_status() | ||
| return [v["id"] for v in r.json().get("variants", [])[:4]] | ||
| return tuple([v["id"] for v in r.json().get("variants", [])[:4]]) |
| r = requests.get(url, headers=AUTH_H, timeout=15) | ||
| r.raise_for_status() | ||
| return [v["id"] for v in r.json().get("variants", [])[:4]] | ||
| return tuple([v["id"] for v in r.json().get("variants", [])[:4]]) |
| # Why: Memoize static Printify blueprint variant data to prevent N+1 API bottlenecks during bulk product creation loops. | ||
| # Impact: Reduces redundant network calls, significantly speeding up execution. | ||
| # Safety: Return type converted to immutable tuple to prevent downstream state mutations from shared cached references. | ||
| @functools.lru_cache(maxsize=None) |
| resp = requests.get(url, headers=HEADERS) | ||
| resp.raise_for_status() | ||
| return [v["id"] for v in resp.json().get("variants", [])[:4]] | ||
| return tuple([v["id"] for v in resp.json().get("variants", [])[:4]]) |
| def get_variants(blueprint_id: int, print_provider_id: int = 29) -> Tuple[int, ...]: | ||
| url = f"{BASE_URL}/catalog/blueprints/{blueprint_id}/print_providers/{print_provider_id}/variants.json" | ||
| resp = requests.get(url, headers=get_headers()) | ||
| resp.raise_for_status() | ||
| data = resp.json() | ||
| variants = data.get("variants", []) | ||
| return [v["id"] for v in variants[:3]] if variants else [] | ||
| return tuple([v["id"] for v in variants[:3]]) if variants else tuple() | ||
|
|
||
| def create_product(blueprint_id: int, variant_ids: List[int], product_name: str, | ||
| color: str, placement: Dict[str, Any]) -> Dict[str, Any]: |
| data = resp.json() | ||
| variants = data.get("variants", []) | ||
| return [v["id"] for v in variants[:3]] if variants else [] | ||
| return tuple([v["id"] for v in variants[:3]]) if variants else tuple() |
| @functools.lru_cache(maxsize=None) | ||
| def get_variants(blueprint_id: int, print_provider_id: int = 29) -> Tuple[int, ...]: | ||
| url = f"{BASE_URL}/catalog/blueprints/{blueprint_id}/print_providers/{print_provider_id}/variants.json" |
💡 What: Implemented
@functools.lru_cacheonget_variantsto memoize the Printify API responses for blueprint variant lookups. Converted the return types to immutable tuples to prevent accidental downstream state mutation.🎯 Why: Iterative product creation workflows generated redundant N+1 API calls when checking the same blueprint repeatedly, creating a massive network bottleneck.
📊 Impact: Eliminates repetitive network calls to the Printify
/variants.jsonendpoint. Execution time during bulk creation loops using identical blueprints will decrease significantly.🔬 Measurement: Verify by executing
python3 create_merch.pyor running thecreatecommand inprintify_manager.pyfor multiple products on the same blueprint. The first request takes time, but subsequent requests for the same blueprint will execute instantly without triggering a network request.PR created automatically by Jules for task 3220928889986122159 started by @merg357