Skip to content

refactor(store): stage 4 - the session record is an explicit session schema (v2), configuration no longer persisted#1026

Open
acouvreur wants to merge 1 commit into
mainfrom
refactor/session-record-v2
Open

refactor(store): stage 4 - the session record is an explicit session schema (v2), configuration no longer persisted#1026
acouvreur wants to merge 1 commit into
mainfrom
refactor/session-record-v2

Conversation

@acouvreur

Copy link
Copy Markdown
Member

Finding

Stage 4 of the InstanceInfo split (stages 1-3: #1023, #1024, #1025). Stage 3 introduced the versioned envelope but still persisted the whole domain struct inside it. This stage makes the record an explicit session schema - and uses the version machinery for exactly what it was built for: the schema change is a v1 -> v2 bump with transparent upgrade, not a silent break.

The schema decision, field by field

The stored record is served back to callers on the warm path with zero provider calls (Sablier's warm-session performance model), so every field kept/dropped was decided against that constraint:

Kept (session state):

Field Why
name, status, message, replicas What the waiting page / plugins display
groups Metrics attribution and group semantics
readyAfter / readyAt / readyOnStart IsReady() on the warm path is decided from these
runningHours / runningDays Drive session-window extension; refreshed on every not-ready inspect
provider + docker/swarm/kubernetes/podman details Referenced by custom waiting-page themes (published theme schema)

Dropped (configuration, never read from the store):

Field Evidence it is safe
enabled Only checked on new sessions (fresh inspect); the expiration re-check inspects live
antiAffinity Anti-affinity uses its own live index, never the store
scaleConfig Read from live labels at provider action time
config (typed bag from #1023) Parsed fresh at every boundary; never shipped in a release

Observable change (deliberate, discussed)

Warm-path session responses stop echoing the four configuration fields (all omitempty - they are simply absent). They were configuration echoes riding along because the god struct was persisted, not session state; everything plugins and themes consume is preserved, and readiness semantics are unchanged (pinned by test). Landing this before the next release means the config field never becomes public wire surface at all.

Migration

UnmarshalJSON upgrades both previous generations transparently:

  • v1 envelopes ({"v":1,"instance":{...}} - existed only on main, never released),
  • v0 bare InstanceInfo documents (all shipped releases),

so live valkey sessions and state.json snapshots survive the upgrade. Unknown newer versions decode best-effort through the known fields. The Store interface still speaks InstanceInfo - core and providers untouched.

Tests

  • TestSessionRecordDropsConfiguration - config gone, readiness semantics intact (IsReady() still true on the warm-path view).
  • TestSessionRecordUpgrades - v0 and v1 payloads upgrade with fields intact.
  • Round-trip, foreign-payload tolerance, inmemory snapshot versioning ("v":2), valkey testcontainers migration suite (Get + Range), and the sabliercmd legacy-fixture state-file tests - all passing; full core + store suites and lint green.

Docs

No docs change: the four fields remain documented as optional response fields (they still appear on cold-path responses); the record itself is persistence-internal.

…tion

Stage 4 of the InstanceInfo split. The session record becomes an explicit
schema (version 2) with its own fields instead of wrapping the whole domain
struct: name, replicas, status, message, groups, provider details, the
readiness semantics IsReady depends on (ready-after/ready-at/
ready-on-start), and the keep-warm window snapshot.

Label configuration (enabled, anti-affinity, scale profiles, the typed
config bag) is no longer persisted: it is parsed fresh from the provider at
every boundary, and freezing it into sessions is how stale configuration
used to leak into decisions and responses. Consequently warm-path session
responses stop echoing those fields; they were configuration echoes, not
session state, and the typed config field never shipped in a release.
Everything the waiting page and plugins consume (status, replicas, names,
provider details) is preserved, and readiness semantics on the warm path
are unchanged.

UnmarshalJSON upgrades both previous generations transparently: v1
envelopes (unreleased) and pre-versioning bare InstanceInfo documents, so
live sessions in valkey and state.json snapshots survive the upgrade. The
Store interface still speaks InstanceInfo; core and providers are
untouched.
@github-actions

Copy link
Copy Markdown

Test Results

✅ All tests passed! | 798 tests in 140.276s

⚠️ 1 test(s) were flaky (failed then passed on rerun)

  • github.com/sablierapp/sablier/pkg/provider/kubernetes/TestKubernetesProvider_InstanceList
    View HTML Test Report

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
┌───────────────────────────────────────────────────────────────────────────────────────┐
│ Diff between sablier and sablier                                                      │
├─────────┬─────────────────────────────────────────────┬──────────┬──────────┬─────────┤
│ PERCENT │ NAME                                        │ OLD SIZE │ NEW SIZE │ DIFF    │
├─────────┼─────────────────────────────────────────────┼──────────┼──────────┼─────────┤
│ +0.56%  │ github.com/sablierapp/sablier               │ 654 kB   │ 658 kB   │ +3.7 kB │
│ +0.32%  │ go.opentelemetry.io/otel                    │ 1.1 MB   │ 1.1 MB   │ +3.4 kB │
│ +0.58%  │ go.yaml.in/yaml/v3                          │ 312 kB   │ 314 kB   │ +1.8 kB │
│ +0.02%  │ runtime                                     │ 3.3 MB   │ 3.3 MB   │ +691 B  │
│ +0.19%  │ go.yaml.in/yaml/v2                          │ 275 kB   │ 275 kB   │ +521 B  │
│ +0.02%  │ github.com/quic-go/quic-go                  │ 1.3 MB   │ 1.4 MB   │ +277 B  │
│ +0.03%  │ encoding                                    │ 418 kB   │ 418 kB   │ +107 B  │
│ +0.16%  │ golang.org/x/crypto                         │ 64 kB    │ 64 kB    │ +101 B  │
│ +0.23%  │ vendor/golang.org/x/net/http2/hpack         │ 35 kB    │ 35 kB    │ +80 B   │
│ +0.46%  │ vendor/golang.org/x/sys/cpu                 │ 6.4 kB   │ 6.4 kB   │ +29 B   │
│ +0.01%  │ log                                         │ 126 kB   │ 126 kB   │ +16 B   │
│ +0.00%  │ k8s.io/apimachinery                         │ 1.8 MB   │ 1.8 MB   │ +11 B   │
│ +0.00%  │ crypto                                      │ 1.9 MB   │ 1.9 MB   │ +9 B    │
│ +0.01%  │ github.com/spf13/viper                      │ 73 kB    │ 73 kB    │ +7 B    │
│ +0.00%  │ github.com/emicklei/go-restful/v3           │ 134 kB   │ 134 kB   │ +1 B    │
│ +0.00%  │ net                                         │ 1.7 MB   │ 1.7 MB   │ +1 B    │
│ -0.01%  │ github.com/pmezard/go-difflib               │ 17 kB    │ 17 kB    │ -1 B    │
│ -0.00%  │ go.mongodb.org/mongo-driver/v2              │ 674 kB   │ 674 kB   │ -2 B    │
│ -0.00%  │ github.com/google/gnostic-models            │ 1.6 MB   │ 1.6 MB   │ -2 B    │
│ -0.00%  │ k8s.io/api                                  │ 17 MB    │ 17 MB    │ -3 B    │
│ -0.02%  │ golang.org/x/sys                            │ 46 kB    │ 46 kB    │ -10 B   │
│ -0.05%  │ k8s.io/utils                                │ 32 kB    │ 32 kB    │ -16 B   │
│ -0.31%  │ internal/cpu                                │ 6.1 kB   │ 6.1 kB   │ -19 B   │
│ -0.11%  │ vendor/golang.org/x/net/idna                │ 22 kB    │ 22 kB    │ -25 B   │
│ -0.00%  │ k8s.io/client-go                            │ 14 MB    │ 14 MB    │ -41 B   │
│ -0.01%  │ golang.org/x/net                            │ 902 kB   │ 902 kB   │ -55 B   │
│ -0.02%  │ github.com/gin-gonic/gin                    │ 337 kB   │ 337 kB   │ -56 B   │
│ -0.14%  │ vendor/golang.org/x/crypto/chacha20poly1305 │ 71 kB    │ 71 kB    │ -101 B  │
│ -0.06%  │ sigs.k8s.io/json                            │ 173 kB   │ 173 kB   │ -107 B  │
│ -2.57%  │ go.shape                                    │ 4.8 kB   │ 4.7 kB   │ -123 B  │
│ -0.48%  │ go.opentelemetry.io/auto/sdk                │ 89 kB    │ 88 kB    │ -427 B  │
│ -0.76%  │ gopkg.in/yaml.v3                            │ 306 kB   │ 304 kB   │ -2.3 kB │
├─────────┼─────────────────────────────────────────────┼──────────┼──────────┼─────────┤
│ +0.01%  │ .rodata                                     │ 2.6 MB   │ 2.6 MB   │ +191 B  │
├─────────┼─────────────────────────────────────────────┼──────────┼──────────┼─────────┤
│ +0.01%  │ sablier                                     │ 62 MB    │ 62 MB    │ +8.2 kB │
│         │ sablier                                     │          │          │         │
└─────────┴─────────────────────────────────────────────┴──────────┴──────────┴─────────┘

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