Skip to content

feat(examples): compare native, full and spec sources with measurements - #67

Merged
sunib merged 2 commits into
feat/native-watch-resumefrom
feat/native-watch-resume-and-comparison
Oct 6, 2026
Merged

sunib merged 2 commits into
feat/native-watch-resumefrom
feat/native-watch-resume-and-comparison

Conversation

@sunib

@sunib sunib commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #66 (native watch resumption). This diff is the comparison commit only; retarget to main once #66 merges.

Summary

This PR adds a focused comparison example, not a dashboard or a benchmark framework. It runs native access, krm-full/v1 and krm-spec/v1 side by side, with unshared and shared gateway watches, on identical objects, subscriber counts and workloads.

  • Host (gateway/kube/examples/comparison) serves everything from one origin, with a session check on every path:
    • Native proxy: a credential-holding /k8s proxy. It forwards GET and a conditional merge PATCH validated by ValidateNativeMergePatch, and only for its scratch namespace's Widgets and Secrets.
    • Gateway routes: four of them (full and spec, each unshared and shared), each with its own counting Backend, Observer and Authorizer.
    • Saves: projected save endpoints for gateway editing.
    • Refused session: ends with a terminal FORBIDDEN and is never followed by a native request.
    • Counters and controls: counters use bounded labels only. Admin endpoints run workloads and force-disconnect downstream connections.
  • Page (examples/comparison):
    • Editing: it edits every source with the existing nativeEditor, conditional editor and keep-local recipe. That includes later typing during a save, conflicts and recovery.
    • Disclosure: native shows Secret values; full and spec show only redacted paths and their revisions.
    • Suppression and counters: spec's status-only suppression is visible, and the page shows client counters and host metrics.
    • Entry points: ?entry=index|bundle.
  • Driver (measure.ts) runs the real connectors and stores under quiet, burst, churn and forced-disconnect schedules. A run counts only if its workload did everything it planned and every store converged with the cluster, with projected Secret stores holding each Secret's final rotation version. It counts:
    • LIST and WATCH requests, including resumed WATCHes
    • snapshots and resets
    • upstream watches
    • downstream bytes and events
    • store notifications and coalesced renders
    • Authorizer calls and host session checks
  • Tasks: task compare, task compare-measure, task compare-native-baseline BASELINE_REF=<sha>.
  • Docs: report in docs/facts/comparison-2026-10-05.md. The delivery plan, field report, CONTRIBUTING and examples index are updated. docs/saving.md now notes that typing in the same field during its own save makes the echo a conflict.

Measurements (real API, k3d v1.36.4+k3s1; 3 repetitions × 20 s per workload)

Native before/after resumption (baseline d259394, 48/48 runs passed both gates):

workload N LIST resets KiB to browser time-to-live median ms
quiet 10 40 → 0 40 → 0 190 → 0 20 → 7.5
churn 10 40 → 0 40 → 0 2302 → 2113 13 → 6
forced-disconnect 10 120 → 0 120 → 0 1295 → 723 12 → 5

The same writes were delivered in both runs. Expiry still re-lists by design and was not part of these workloads.

Sources compared (matrix with this branch's library, all 24 runs passed both gates):

native full spec shared gateway
KiB to browser, churn N=10 2112 1100 152 same as unshared
modified events, churn N=1 214 214 15 (199 status-only writes suppressed) same as unshared
upstream requests on reconnect, quiet N=10 0 LIST + 40 resumed WATCH 40 watches 40 watches 4 watches
Secret rotations seen 6 value changes 6 revision bumps, no values 6 revision bumps, no values same as unshared

Authorization. Authorizer calls are made per subscriber, whether the watch is shared or not: 100 per route at N=10.

Not measured:

  • the API server's own RBAC checks on native requests
  • browser paint
  • memory and CPU
  • per-user identities: every upstream request uses one kubeconfig identity
  • save progress under churn

These figures come from one disposable cluster on one shared machine; they are not capacity claims.

Review fixes (fix(examples): …)

  • A focused field no longer silently overwrites another writer. The page used to skip re-rendering a focused input. Now it follows the store's draft there too, keeping the caret (fields.ts). Half-typed numbers are still left alone.
  • Incomplete workloads are rejected. A run with workload errors, or fewer steps done than planned, fails instead of being measured.
  • A missed Secret rotation is caught. The host reports each Secret's last successful rotation version. Projected stores must hold that version; intermediate rotations may coalesce.
  • Regressions:
    • task test-compare (in task test): fields.test.ts, gates.test.ts, and a typecheck of the page and driver
    • a Go workload test
    • task compare-browser: Chromium against the real host on both entry points, for native, full and spec. 6/6 pass; 6/6 fail with the old render rule.
  • The matrix and the before/after were rerun under both gates, and the report updated.

Validation

  • task lint and task test-kube. The new package has a unit test that needs no cluster: schedules, proxy policy and counters, and the gateway route's refusal.
  • Headless Chromium smoke check against task compare on both entry points, all passing:
    • every source live
    • native, full and spec edits saved and echoed
    • typing during a save preserved
    • conflicts resolved with take-theirs and keep-mine
    • "disconnect all" recovered every source
    • the refused session made 0 native requests
    • the shared routes live

🤖 Generated with Claude Code

One host (gateway/kube/examples/comparison) serves the same Widgets and
Secrets from one origin through a credential-holding /k8s native proxy and
four gateway routes (krm-full/v1 and krm-spec/v1, unshared and shared), with
projected save endpoints, a session check on every path, a refused session,
deterministic workloads (quiet, burst, churn, forced-disconnect), forced
downstream disconnects and low-cardinality counters, including a counting
Backend wrapper per route instead of cluster-wide API-server gauges.

examples/comparison adds a side-by-side page that edits through the existing
native and conditional editors and keep-local recipe on both entry points,
and measure.ts, which drives the real connectors and stores from a
configurable library build, gates every run on convergence with the cluster
and writes JSON and markdown. measure.sh runs it against a fresh host, and
with --baseline-ref builds an earlier library for a native before/after.

Tasks: build-compare-page, compare, compare-measure, compare-native-baseline.
docs/facts/comparison-2026-10-05.md records the baseline run and leaves the
native AFTER section for the resumable connector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 9431bf21-069a-4c88-881b-2f26a74fae65
📥 Commits

Reviewing files that changed from the base of the PR and between 6adc4e2 and 8c70b47.

📒 Files selected for processing (31)
  • CONTRIBUTING.md
  • Taskfile.yml
  • docs/facts/comparison-2026-10-05.md
  • docs/field-reports/third-our-identity.md
  • docs/proposals/0006-stream-and-save-implementation-plan.md
  • docs/saving.md
  • examples/README.md
  • examples/comparison/.gitignore
  • examples/comparison/README.md
  • examples/comparison/build.mjs
  • examples/comparison/fields.test.ts
  • examples/comparison/fields.ts
  • examples/comparison/gates.test.ts
  • examples/comparison/gates.ts
  • examples/comparison/index.html
  • examples/comparison/measure.sh
  • examples/comparison/measure.ts
  • examples/comparison/page.ts
  • examples/comparison/tsconfig.json
  • examples/vanilla-browser/compare.config.ts
  • examples/vanilla-browser/compare/comparison.spec.ts
  • gateway/kube/examples/comparison/cmd/compare/main.go
  • gateway/kube/examples/comparison/comparison_test.go
  • gateway/kube/examples/comparison/gateway.go
  • gateway/kube/examples/comparison/host.go
  • gateway/kube/examples/comparison/metrics.go
  • gateway/kube/examples/comparison/native.go
  • gateway/kube/examples/comparison/objects.go
  • gateway/kube/examples/comparison/save.go
  • gateway/kube/examples/comparison/workload.go
  • gateway/kube/examples/comparison/workload_test.go
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Review fixes for the comparison example.

The page skipped re-rendering a focused input, so when another writer
changed a focused, unedited field the input kept the old text and the
next keystroke saved it over the newer value, with no conflict. fields.ts
now follows the store's draft in a focused input too, keeping the caret,
and rewrites an input only when the draft moved, so half-typed numbers
survive.

measure.ts never checked the workload outcome: a run whose writes all
failed matched the unchanged cluster and was reported as a measurement.
The workload gate now rejects any run with errors or with fewer done
steps than planned. A projected Secret store that missed a rotation kept
the same redacted key and passed; the host now reports each Secret's last
successful rotation version and the correctness gate requires it.

Regressions: fields.test.ts and gates.test.ts (task test-compare, part of
task test, with the page and driver typecheck), a Go workload test, and
task compare-browser, which runs the focused-field case in Chromium
against the real host on both entry points (6/6 pass; 6/6 fail with the
old render rule). The matrix and the native before/after were rerun under
both gates (72/72 passed) and the report updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sunib
sunib added this pull request to stack #70 October 6, 2026 08:24
@sunib
sunib merged commit 1bad87d into main Oct 6, 2026
13 checks passed
@sunib
sunib deleted the feat/native-watch-resume-and-comparison branch October 6, 2026 08:27
This was referenced Oct 6, 2026
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