Skip to content

⚡ Bolt: [performance improvement] Memoize blueprint variant lookups to prevent N+1 API bottlenecks#47

Open
merg357 wants to merge 1 commit into
mainfrom
bolt-memoize-get-variants-3220928889986122159
Open

⚡ Bolt: [performance improvement] Memoize blueprint variant lookups to prevent N+1 API bottlenecks#47
merg357 wants to merge 1 commit into
mainfrom
bolt-memoize-get-variants-3220928889986122159

Conversation

@merg357

@merg357 merg357 commented May 25, 2026

Copy link
Copy Markdown
Owner

💡 What: Implemented @functools.lru_cache on get_variants to 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.json endpoint. Execution time during bulk creation loops using identical blueprints will decrease significantly.
🔬 Measurement: Verify by executing python3 create_merch.py or running the create command in printify_manager.py for 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

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>
Copilot AI review requested due to automatic review settings May 25, 2026 09:17
@google-labs-jules

Copy link
Copy Markdown

👋 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 @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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) to get_variants to prevent repeated /variants.json requests in iterative workflows.
  • Switched get_variants return values from list to immutable tuple (and updated typing in create_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_variants now returns a tuple, but create_product(..., variant_ids: list, ...) still advertises variant_ids as a list. Either update create_product's type to accept an immutable sequence (e.g., Sequence[int] / Iterable[int]) or explicitly convert variant_ids to 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.

Comment thread printify_manager.py
Comment on lines +78 to +82
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]])
Comment thread printify_manager.py
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]])
Comment thread printify_manager.py
# 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)
Comment thread merch_factory.py
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]])
Comment thread create_merch.py
Comment on lines +61 to 70
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]:
Comment thread create_merch.py
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()
Comment thread create_merch.py
Comment on lines +60 to 62
@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"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants