Skip to content

direct: store serialized_dashboard in state as a content hash - #6105

Open
Sankalp-Mittal wants to merge 23 commits into
mainfrom
sankalp-mittal/dashboards-sha-state
Open

direct: store serialized_dashboard in state as a content hash#6105
Sankalp-Mittal wants to merge 23 commits into
mainfrom
sankalp-mittal/dashboards-sha-state

Conversation

@Sankalp-Mittal

@Sankalp-Mittal Sankalp-Mittal commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

On the direct engine, store a dashboard's serialized_dashboard in state as a sha256_hashed_in_state:<hex> content hash instead of the full inlined dashboard JSON. The full contents are still sent to the API on every deploy; only the state copy is compacted.

Reimplements the approach from #5609 on current main.

Why

serialized_dashboard is a large blob (often multi-MB) that the direct engine only ever compares for equality — it is never read back out of state. Persisting the whole thing bloats the state file for no benefit, so a content hash is sufficient: two hashes compare equal iff the contents do.

The hash has to be applied consistently. Once the saved state holds a hash, every value entering the diff must also be hashed, or a comparison becomes hash-vs-content and reports a phantom change. So compaction runs on all three diff sides (saved, local config, remapped remote) as well as on every state write.

hashed_in_state is orthogonal to ignore_remote_changes. Because the remote side is hashed too, remote drift is still detected as hash != hash, so a field can be hashed without being ignored. serialized_dashboard happens to need both, for the independent reason that the server normalizes the content (adds pageType, reorders keys) so its remote hash never matches the config hash — drift is detected via etag instead.

Changes

  • New hashed_in_state []string lifecycle rule on ResourceLifecycleConfig, declared for dashboards.serialized_dashboard in resources.yml.
  • CompactState (state_compaction.go) replaces declared fields with a content hash; it is idempotent and never mutates the caller's value (the full contents are reused for the deploy API call). It only shallow-copies the state struct, so it rejects any nested path — a hashed_in_state field must be top-level, otherwise it would be mutated through memory shared with the deploy value and silently send the hash to the API.
  • Compaction is wired into every state write (apply.go, bind.go, migrate/build_state.go) and into all three diff sides in bundle_plan.go.
  • New libs/hash.OfJSON helper (deterministic sha256 of a value's JSON); libs/cache is rerouted through it, replacing its private fingerprintToHash.
  • No state version bump: legacy full-content state is hashed on read for comparison and rewritten compactly on the next save.

Tests

  • New acceptance test bundle/resources/dashboard-state-sha (direct engine, READPLAN in ["", "1"]): asserts the state file holds only the hash while the API receives the full content, across create / re-plan (no-op) / edit / update.
  • Unit tests for libs/hash, CompactState (including idempotency, legacy-content migration, and nested-path rejection), a config check that every declared hashed_in_state field is top-level, and the dashboard wiring.
  • Existing dashboard / migrate / bind goldens regenerated for the hashed state format. A [[Repls]] rule in acceptance/bundle/test.toml masks the hash as [HASH] (with Distinct, so equal hashes collapse to one token and a differing remote gets another) ahead of the generic numeric/id rules that would otherwise nibble hex out of the digest.

This pull request and its description were written by Isaac.

@github-actions

Copy link
Copy Markdown
Contributor

Approval status: pending

/acceptance/bundle/ - needs approval

20 files changed
Suggested: @denik
Also eligible: @pietern, @janniklasrose, @andrewnester, @shreyas-goenka, @anton-107, @lennartkats-db

/bundle/ - needs approval

10 files changed
Suggested: @denik
Also eligible: @pietern, @janniklasrose, @andrewnester, @shreyas-goenka, @anton-107, @lennartkats-db

General files (require maintainer)

6 files changed
Based on git history:

  • @denik -- recent work in bundle/direct/, bundle/direct/dresources/, bundle/migrate/

Any maintainer (@andrewnester, @anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose, @lennartkats-db) can approve all areas.
See OWNERS for ownership rules.

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 634ec63

Run: 30620572474

Env 🟨​KNOWN 🔄​flaky 💚​RECOVERED 🙈​SKIP ✅​pass 🙈​skip Time
🟨​ aws linux 3 1 1 4 333 1063 7:20
🟨​ aws windows 3 1 4 336 1061 7:22
🟨​ azure linux 3 1 4 334 1062 9:05
🟨​ azure windows 3 1 4 336 1060 8:35
💚​ gcp linux 1 5 333 1064 5:39
💚​ gcp windows 1 5 335 1062 9:11
9 interesting tests: 4 SKIP, 3 KNOWN, 1 RECOVERED, 1 flaky
Test Name aws linux aws windows azure linux azure windows gcp linux gcp windows
💚​ TestAccept 💚​R 💚​R 💚​R 💚​R 💚​R 💚​R
🙈​ TestAccept/bundle/invariant/no_drift 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/bundle/resources/vector_search_indexes/recreate/embedding_dimension 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🙈​ TestAccept/ssh/connection 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S 🙈​S
🔄​ TestClustersGet 🔄​f ✅​p ✅​p ✅​p ✅​p ✅​p
🟨​ TestFetchRepositoryInfoAPI_FromRepo 🟨​K 🟨​K 🟨​K 🟨​K 🙈​S 🙈​S
🟨​ TestFetchRepositoryInfoAPI_FromRepo/root 🟨​K 🟨​K 🟨​K 🟨​K
🟨​ TestFetchRepositoryInfoAPI_FromRepo/subdir 🟨​K 🟨​K 🟨​K 🟨​K
Top 3 slowest tests (at least 2 minutes):
duration env testname
6:06 gcp windows TestAccept
3:26 azure windows TestAccept
3:24 aws windows TestAccept

@Sankalp-Mittal
Sankalp-Mittal marked this pull request as ready for review July 30, 2026 15:55
Sankalp-Mittal and others added 3 commits July 31, 2026 07:29
Empty commit to re-run the pipeline; the previous integration run failed only
on gcp-linux TestFsCp* tests, which this PR does not touch (unrelated infra
flake).

Co-authored-by: Isaac
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.

2 participants