When does the SDK hit the network
The atomic snapshot guarantee
The snapshot is replaced as one immutable struct value, behind async.RWMutex. Two goroutines reading concurrently either see the same old state or the same new state - never a mix.
GetAllConfigs() or GetAllFlags() without worrying about an entry being added or removed mid-loop. The returned map is also a defensive copy.
ETag / 304
Every successful sync stores the server’sETag. The next sync sends If-None-Match: <etag>. If nothing has changed:
SyncedAt timestamp and serves the existing snapshot. No payload transferred.
When the etag changes, the new payload replaces the cache atomically.
Background sync errors
Background syncs never return errors to the caller. If the network call fails:- The error is logged via
Logger.Errorf(...) - The existing cache continues to be served
- The next read after the TTL elapses retries automatically
Cache lifetime
The cache lives as long as the*Client. Cancelling the context passed to NewClient does NOT release the cache; it only affects the initial sync. A *Client typically lives for the entire process - share by passing a pointer, don’t construct per-request.
Diagnostics
When you wire up a
Logger, the Debugf level emits one line per sync:
Concurrency model
- All reads are safe to call from any goroutine - internally guarded by
sync.RWMutex. - Background sync goroutine - at most one at a time; spawned on TTL expiry.
- SSE goroutine - at most one, started when you call
go client.RunStream(ctx).
os.TempDir() on every sync when the value changes. All temp files are removed when client.Close() is called.