Skip to content

fix(image): rewrite image cache with broadcast single-flight#13

Merged
lxl66566 merged 1 commit into
masterfrom
fix-img
Jul 19, 2026
Merged

fix(image): rewrite image cache with broadcast single-flight#13
lxl66566 merged 1 commit into
masterfrom
fix-img

Conversation

@lxl66566

Copy link
Copy Markdown
Owner

close #11

Bug: when a Game had image_url but no image_sha256, the card rendered blank and the hash never propagated back to the config.

Root cause was an interaction between two earlier commits:

  • deb6bcb turned CachedImage's createResource source key from a tuple into a concatenated string, converting props.hash=null into '' (empty string).
  • Tauri serializes '' as Some("") on the Rust side, not None.
  • The pre-existing fast-path at http/mod.rs did IMAGE_CACHE_DIR.join(hash); join("") is a no-op, so cache_path resolved to IMAGE_CACHE_DIR itself, which always .exists(). prepare_image returned Ok("").
  • Back in TS, hash !== currentHash was false, so onHashUpdate never fired, and imageHash() was falsy so the never rendered.

Fix / rewrite:

  • http::prepare_image now validates the hash (is_valid_hash: 32 hex chars) before using it as a cache key. Empty / malformed values are treated as None. The galimg protocol handler is tightened the same way.
  • The single-flight dedup is rewritten using DashMap<String, broadcast::Sender> instead of DashMap<String, Arc<Mutex<Option<String>>>>. The leader does the HTTP GET, hashes the bytes, writes the cache file, broadcasts the result to all subscribers, then removes itself from the pool. Late subscribers that miss the broadcast (RecvError::Closed) fall back to the disk fast-path when sha256 is known, or loop once to become the new leader otherwise.
  • Dedup key is sha256 when known, falling back to the URL itself for the first load of a URL-only config (the bug case).
  • Renamed the IPC and internal parameter hash -> sha256 for clarity, since the value is always the content sha256 of the cached bytes.
  • CachedImage now passes sha256: currentHash || null so a missing hash serializes to None on the Rust side, never an empty string.

Bug: when a Game had image_url but no image_sha256, the card rendered
blank and the hash never propagated back to the config.

Root cause was an interaction between two earlier commits:

- deb6bcb turned CachedImage's createResource source key from a tuple into
  a concatenated string, converting props.hash=null into '' (empty string).
- Tauri serializes '' as Some("") on the Rust side, not None.
- The pre-existing fast-path at http/mod.rs did IMAGE_CACHE_DIR.join(hash);
  join("") is a no-op, so cache_path resolved to IMAGE_CACHE_DIR itself,
  which always .exists(). prepare_image returned Ok("").
- Back in TS, hash !== currentHash was '' !== '' -> false, so onHashUpdate
  never fired, and imageHash() was falsy so the <img> never rendered.

Fix / rewrite:

- http::prepare_image now validates the hash (is_valid_hash: 32 hex chars)
  before using it as a cache key. Empty / malformed values are treated as
  None. The galimg protocol handler is tightened the same way.
- The single-flight dedup is rewritten using
  DashMap<String, broadcast::Sender> instead of
  DashMap<String, Arc<Mutex<Option<String>>>>. The leader does the HTTP
  GET, hashes the bytes, writes the cache file, broadcasts the result to
  all subscribers, then removes itself from the pool. Late subscribers
  that miss the broadcast (RecvError::Closed) fall back to the disk
  fast-path when sha256 is known, or loop once to become the new leader
  otherwise.
- Dedup key is sha256 when known, falling back to the URL itself for the
  first load of a URL-only config (the bug case).
- Renamed the IPC and internal parameter hash -> sha256 for clarity,
  since the value is always the content sha256 of the cached bytes.
- CachedImage now passes sha256: currentHash || null so a missing hash
  serializes to None on the Rust side, never an empty string.

The existing Cache-Control: public, max-age=31536000, immutable on the
galimg protocol response already lets the webview cache content-addressed
images indefinitely; no JS-side cache is needed.
@lxl66566
lxl66566 merged commit 4017a18 into master Jul 19, 2026
2 checks passed
@lxl66566
lxl66566 deleted the fix-img branch July 19, 2026 05:25
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.

[bug] 无法正常获取封面图片

1 participant