Add fresh read variants that await a server round-trip (closes #75) #76

Merged
clawbot merged 1 commits from issue-75-fresh-reads into next 2026-09-22 23:29:15 +02:00
Collaborator

Owner amendment to design #36 (issue #75): the default reads still answer from RAM and refresh in the background, and now Library.fresh() gives a caller that needs server-current data an awaited path. It forces a refresh, waits for it to complete and persist, then returns the same albums/photos/timeline namespaces, so their values reflect a completed server round-trip. The CLI adopts this separately in #52.

Both paths now share one in-flight-cycle slot instead of the old refreshing boolean:

  • The background loop and the default reads are unchanged: the loop still skips when a cycle is already running and still swallows a failed refresh into status()/onProgress.
  • A fresh read coalesces onto the in-flight cycle (the background loop's included) or starts one, so concurrent fresh reads collapse to a single server round-trip rather than fanning out.
  • A fresh read propagates a refresh failure and rejects, where a default read would silently keep serving the last good copy.

What a reader might trip over: the shared cycle promise carries the refresh's real outcome (it can reject), so startCycle() attaches its own settle handler and each caller attaches the handling its path needs — the background loop catches, a fresh read awaits. fresh() hands back the live namespaces, which only ever get more current after it resolves, never staler.

Tests drive a mock client: a fresh read catches a change a same-instant default read misses, concurrent fresh reads make one round-trip, and a failed refresh rejects while defaults stay intact.

Model: opus-4-8

Owner amendment to design #36 (issue https://git.eeqj.de/sneak/quak/issues/75): the default reads still answer from RAM and refresh in the background, and now `Library.fresh()` gives a caller that needs server-current data an awaited path. It forces a refresh, waits for it to complete and persist, then returns the same `albums`/`photos`/`timeline` namespaces, so their values reflect a completed server round-trip. The CLI adopts this separately in https://git.eeqj.de/sneak/quak/issues/52. Both paths now share one in-flight-cycle slot instead of the old `refreshing` boolean: - The background loop and the default reads are unchanged: the loop still skips when a cycle is already running and still swallows a failed refresh into `status()`/`onProgress`. - A fresh read coalesces onto the in-flight cycle (the background loop's included) or starts one, so concurrent fresh reads collapse to a single server round-trip rather than fanning out. - A fresh read propagates a refresh failure and rejects, where a default read would silently keep serving the last good copy. What a reader might trip over: the shared cycle promise carries the refresh's real outcome (it can reject), so `startCycle()` attaches its own settle handler and each caller attaches the handling its path needs — the background loop catches, a fresh read awaits. `fresh()` hands back the live namespaces, which only ever get more current after it resolves, never staler. Tests drive a mock client: a fresh read catches a change a same-instant default read misses, concurrent fresh reads make one round-trip, and a failed refresh rejects while defaults stay intact. Model: opus-4-8
clawbot added the needs-review label 2026-09-22 23:06:40 +02:00
clawbot self-assigned this 2026-09-22 23:06:40 +02:00
clawbot added 1 commit 2026-09-22 23:06:40 +02:00
Owner amendment to design #36: alongside the default reads, which answer
from RAM and refresh in the background, `Library.fresh()` forces a refresh,
awaits it, and only then hands back the albums/photos/timeline namespaces, so
a caller (the CLI, in #52) gets server-current data.

The background loop and the default reads are unchanged. Both paths now share
one in-flight-cycle slot: the loop skips when a cycle runs and swallows its
errors as before; a fresh read coalesces onto that cycle or starts one, and
propagates a failure so a refresh that could not complete rejects the caller
instead of silently serving stale data. Concurrent fresh reads therefore
collapse to a single server round-trip.

Model: opus-4-8
Author
Collaborator

PASS — Library.fresh() forces a refresh, awaits its completion and persist, then hands back the same albums/photos/timeline namespaces now reflecting a completed server round-trip; concurrent fresh reads coalesce onto one in-flight cycle, a failed refresh rejects the caller while the default reads and the background loop are unchanged and keep serving the last good copy, the three tests are meaningful (each fails when the corresponding behaviour is mutated) and non-flaky (they wait on observable results), and both make check and make build are green on the head rebased onto current next (#76, #75).

Model: opus-4-8

PASS — `Library.fresh()` forces a refresh, awaits its completion and persist, then hands back the same albums/photos/timeline namespaces now reflecting a completed server round-trip; concurrent fresh reads coalesce onto one in-flight cycle, a failed refresh rejects the caller while the default reads and the background loop are unchanged and keep serving the last good copy, the three tests are meaningful (each fails when the corresponding behaviour is mutated) and non-flaky (they wait on observable results), and both `make check` and `make build` are green on the head rebased onto current `next` (https://git.eeqj.de/sneak/quak/pulls/76, https://git.eeqj.de/sneak/quak/issues/75). Model: opus-4-8
clawbot merged commit aeccb489b5 into next 2026-09-22 23:29:15 +02:00
clawbot deleted branch issue-75-fresh-reads 2026-09-22 23:29:15 +02:00
Sign in to join this conversation.