A follower joining a watched state hears its current values, and a templates archive is fetched once
A read could start a state's watch before its follower joined, and a change landing in that gap woke nothing (review of ADR 0580 §2): a joining follower now runs over the copy's current values before any later change. The watched reader keeps each archive once per URL, and both caches are bounded. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/compose/frombus"
|
||||
@@ -47,9 +48,39 @@ func (w Watched) Get(state, key string) ([]byte, bool, error) {
|
||||
return w.WatchedStates.Get(state, key)
|
||||
}
|
||||
|
||||
// WatchedReader is frombus's Reader over w, fetching archives over HTTP.
|
||||
// WatchedReader is frombus's Reader over w, fetching each templates archive over HTTP once (KeptFetch): a composition
|
||||
// made again on every change reads it again otherwise.
|
||||
func WatchedReader(w *stdio.WatchedStates) frombus.Reader {
|
||||
return frombus.StatesReader(Watched{w}, HTTPFetch)
|
||||
return frombus.StatesReader(Watched{w}, KeptFetch(HTTPFetch))
|
||||
}
|
||||
|
||||
// archivesAtMost bounds the archives KeptFetch keeps; past it they are emptied, which costs only a fetch again.
|
||||
const archivesAtMost = 64
|
||||
|
||||
// KeptFetch is fetch kept once per URL: an archive's URL names its blob by digest, so its body never changes under it
|
||||
// (frombus checks the digest on every read). A failed fetch is not kept.
|
||||
func KeptFetch(fetch frombus.Fetch) frombus.Fetch {
|
||||
var mu sync.Mutex
|
||||
kept := map[string][]byte{}
|
||||
return func(ctx context.Context, url string) ([]byte, error) {
|
||||
mu.Lock()
|
||||
body, ok := kept[url]
|
||||
mu.Unlock()
|
||||
if ok {
|
||||
return body, nil
|
||||
}
|
||||
body, err := fetch(ctx, url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mu.Lock()
|
||||
if len(kept) >= archivesAtMost {
|
||||
clear(kept)
|
||||
}
|
||||
kept[url] = body
|
||||
mu.Unlock()
|
||||
return body, nil
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPFetch reads an archive over HTTP, at most 256 MiB.
|
||||
|
||||
Reference in New Issue
Block a user