Skip to content

perf(preview): cache the Labelary blob URL by ZPL string#68

Merged
u8array merged 3 commits into
mainfrom
perf/preview-cache
May 16, 2026
Merged

perf(preview): cache the Labelary blob URL by ZPL string#68
u8array merged 3 commits into
mainfrom
perf/preview-cache

Conversation

@u8array
Copy link
Copy Markdown
Owner

@u8array u8array commented May 16, 2026

No description provided.

Toggling preview off then on for a side-by-side pixel compare with the
editor used to fire a fresh fetch every time, even when nothing
changed. Keep the (zpl, url) pair in a module-level closure with
get/set semantics; the closure owns the URL so callers can't forget to
revoke the stale blob on a cache miss.

The slot lives outside Zustand state because the blob URL is a non-
serialisable side-effect handle: persisting it through `partialize`
would resurrect a stale identifier across reloads, and putting it in
the store would churn every selector that observes previewMode.

Two test cases exercise the cache hit + cache miss + revoke paths; a
test-only `__resetPreviewCacheForTests` keeps them isolated since the
closure outlives `useLabelStore.setState`.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a single-entry cache for Labelary preview blob URLs to optimize performance by avoiding redundant API calls when the ZPL remains unchanged. The cache is managed outside the store state to handle non-serializable blob URLs and ensure proper memory cleanup via URL.revokeObjectURL. Feedback highlights a race condition where a stale fetch might update the store with an incorrect URL if the design is modified while a request is pending; a validation check was suggested to ensure the returned preview matches the current design.

Comment thread src/store/labelStore.ts
Gemini's PR review flagged a race the audit missed: if the user opens
preview, exits while the fetch is still in flight, edits the design,
and re-opens preview, two fetches end up overlapping. The first one
resolves while `status === 'loading'` (which it is again, for the new
request) and the old check let it through — clobbering the new
loading state with the previous design's URL and caching a stale pair.

Add a `zpl !== currentZpl` check next to the status check, gated
through one `isStale()` helper used by both the success and the error
paths. Captured `zpl` in the closure is the ground truth for which
request this continuation belongs to.

Regression test stages the exact scenario with hand-controlled
fetchPreview promises: enter → exit → mutate → enter → resolve the
first (stale) → assert state stays `loading` and the stale blob got
revoked → resolve the second → assert the fresh URL is the one that
lands in state.
@u8array
Copy link
Copy Markdown
Owner Author

u8array commented May 16, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements a single-entry cache for Labelary preview blob URLs to prevent unnecessary API calls when toggling the preview mode on an unchanged design. It also introduces a mechanism to discard stale in-flight preview requests if the user exits or modifies the design before the fetch completes. A review comment suggests optimizing the staleness check by comparing object and label references instead of re-generating the ZPL string, which would enhance performance for complex labels.

Comment thread src/store/labelStore.ts Outdated
Gemini PR feedback: regenerating the ZPL string each time a fetch
resolves is wasted work when the store already mutates `label` and the
objects array immutably — a reference compare is O(1) versus
O(generator pass + coordinate conversions) and detects the exact same
"state diverged while we were fetching" condition.

As a bonus, the reference compare catches a page switch (which yields
a different `objects` array) even when both pages happen to generate
identical ZPL by coincidence; the previous string-based check would
let that through.
@u8array u8array merged commit e3103b6 into main May 16, 2026
2 checks passed
@u8array u8array deleted the perf/preview-cache branch May 17, 2026 16:20
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.

1 participant