-
Notifications
You must be signed in to change notification settings - Fork 10
feat(storage): add an optional FTWDB beta candidate #1096
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
92ff96c
feat(storage): add a bounded FTWDB history candidate
frahlg 6af6e29
fix(storage): pin the final FTWDB beta package
frahlg 3c3bab4
chore(storage): pin the verified FTWDB fault reports
frahlg f305fce
chore(storage): pin FTWDB segment and scan hardening
frahlg dd49a76
Merge branch 'master' into feat/ftwdb-beta
frahlg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| --- | ||
| "ftw": minor | ||
| --- | ||
| Add an optional FTWDB beta sidecar that copies committed live site history through a bounded memory queue. Keep SQLite and Parquet authoritative and report candidate gaps, errors and durable acknowledgements in health. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| name: FTWDB shadow contract | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths: | ||
| - ".github/workflows/ftwdb-shadow-contract.yml" | ||
| - "docker-compose.ftwdb-shadow.yml" | ||
| - "go/internal/ftwdbshadow/**" | ||
| - "go/internal/state/history_feed*" | ||
| - "go/internal/state/store_ts.go" | ||
| - "go/cmd/ftw/main.go" | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| go-rust-contract: | ||
| name: Go to Rust contract | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
| steps: | ||
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| - name: Check out pinned FTWDB | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| repository: srcfl/ftwdb | ||
| ref: 7bbae63532f695b10aca548bf4ee58c6d7ebb3a8 | ||
| path: .ftwdb-contract | ||
| - uses: actions/setup-go@v7 | ||
| with: | ||
| go-version-file: go/go.mod | ||
| - uses: dtolnay/rust-toolchain@2c7215f132e9ebf062739d9130488b56d53c060c # master | ||
| with: | ||
| toolchain: 1.97.1 | ||
| - name: Check the frozen v1 contract | ||
| run: diff -ru .ftwdb-contract/testdata/shadow-protocol-v1 go/internal/ftwdbshadow/testdata/shadow-protocol-v1 | ||
| - name: Build the pinned sidecar and reconcile tool | ||
| run: cargo build --locked --manifest-path .ftwdb-contract/Cargo.toml --bin ftwdb-shadow --bin ftwdb-shadow-reconcile | ||
| - name: Exercise live copy, lost ACK, limits, SIGKILL, restart and reconcile | ||
| working-directory: go | ||
| env: | ||
| FTWDB_SHADOW_BIN: ${{ github.workspace }}/.ftwdb-contract/target/debug/ftwdb-shadow | ||
| FTWDB_RECONCILE_BIN: ${{ github.workspace }}/.ftwdb-contract/target/debug/ftwdb-shadow-reconcile | ||
| FTWDB_SHADOW_FIXTURES: ${{ github.workspace }}/.ftwdb-contract/testdata/shadow-protocol-v1 | ||
| run: go test -race ./internal/ftwdbshadow ./internal/state -run 'TestBeta|TestHistoryMapping|TestRustSidecarInterop|TestLiveHistory|TestHealthOps|TestV1Golden|TestVendored' -count=1 -v |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # Optional beta overlay. Core and SQLite keep their normal lifecycle. | ||
| # Pin matches .github/workflows/ftwdb-shadow-contract.yml. | ||
| services: | ||
| ftw: | ||
| environment: | ||
| FTWDB_SHADOW_SOCKET: /run/ftwdb-shadow/shadow.sock | ||
| volumes: | ||
| - ftwdb-shadow-run:/run/ftwdb-shadow | ||
|
|
||
| ftwdb-shadow: | ||
| profiles: [ftwdb-shadow] | ||
| build: | ||
| context: https://github.com/srcfl/ftwdb.git#7bbae63532f695b10aca548bf4ee58c6d7ebb3a8 | ||
| image: ftwdb-shadow:7bbae63532f695b10aca548bf4ee58c6d7ebb3a8 | ||
| user: "100:101" | ||
| network_mode: none | ||
| read_only: true | ||
| cap_drop: [ALL] | ||
| security_opt: [no-new-privileges:true] | ||
| cpus: 0.25 | ||
| mem_limit: 256m | ||
| pids_limit: 64 | ||
| restart: "no" | ||
| stop_grace_period: 30s | ||
| volumes: | ||
| - ftwdb-shadow-data:/var/lib/ftwdb-shadow | ||
| - ftwdb-shadow-run:/run/ftwdb-shadow | ||
|
|
||
| volumes: | ||
| ftwdb-shadow-data: | ||
| ftwdb-shadow-run: | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| # FTWDB beta candidate | ||
|
|
||
| The optional FTWDB sidecar copies five numeric fields from successful live | ||
| SQLite history writes: grid power, PV power, battery power, house load and | ||
| battery state of charge. Watts keep the site sign convention; SoC stays a | ||
| 0–1 fraction. SQLite and Parquet still serve history. Config, forecasts, learned | ||
| models, schedules and control continue to use their current stores. | ||
|
|
||
| This is a bounded session recording. It does not copy old data, driver samples, | ||
| SQL imports, retention deletes, the energy ledger or forecast archives. It is | ||
| not a complete replica or a backup. Each Core start has a new session ID. Pending | ||
| memory work can be lost on restart; the SQLite source remains available. | ||
|
|
||
| ## Enable on a beta test box | ||
|
|
||
| Use a Core beta that contains this integration. From the FTW checkout: | ||
|
|
||
| ```sh | ||
| docker compose -f docker-compose.yml -f docker-compose.ftwdb-shadow.yml \ | ||
| --profile ftwdb-shadow build ftwdb-shadow | ||
| docker compose -f docker-compose.yml -f docker-compose.ftwdb-shadow.yml \ | ||
| --profile ftwdb-shadow up -d ftw ftwdb-shadow | ||
| ``` | ||
|
|
||
| The overlay builds a pinned FTWDB commit. It gives the sidecar its own data | ||
| volume, no network, a 256 MiB memory limit and a quarter CPU. Only the private | ||
| Unix socket volume is shared with Core. Both processes use UID 100, GID 101. | ||
| There is no startup or health dependency from Core to FTWDB. | ||
|
|
||
| For a native Linux install, use the pinned | ||
| [systemd service example](https://github.com/srcfl/ftwdb/blob/7bbae63532f695b10aca548bf4ee58c6d7ebb3a8/packaging/systemd/ftwdb-shadow.service) | ||
| with the same user as Core. That service listens on | ||
| `/run/ftwdb-shadow/ftwdb-shadow.sock`. Pass that path with | ||
| `-ftwdb-shadow-socket` or `FTWDB_SHADOW_SOCKET` to Core. | ||
| An empty value disables the candidate. | ||
| This is an install option; household Settings do not expose an experimental | ||
| storage switch. | ||
|
|
||
| ## Read the result | ||
|
|
||
| Read `ftwdb_shadow` from `GET /api/health`. Its state is independent of Core | ||
| health. Check these fields together: | ||
|
|
||
| - `session`, `started_at` and `scope` identify the covered run. | ||
| - `offered_ticks`, `queued_ticks`, `pending_ticks` and `dropped_ticks` show | ||
| collection and overload. `gaps` means at least one offered tick was lost. | ||
| - `acknowledged_ticks`, `durable_through_sequence` and `last_ack_at` report | ||
| durable sidecar receipts. A sent batch is not yet an acknowledgement. | ||
| `last_ack_ms` and `max_ack_ms` measure the commit request and durable reply. | ||
| - `errors` and `last_error` explain a pause. `sidecar` counters have their own | ||
| `sidecar_checked_at`; they can precede the latest batch acknowledgement. | ||
|
|
||
| The queue holds at most 256 small numeric records, plus one pending batch of | ||
| at most 128. Core tries a batch every 30 seconds. Connect, encode, socket I/O | ||
| and retry happen on a separate goroutine with two-second I/O deadlines. A full | ||
| queue drops candidate work and increments its counter. It never waits for the | ||
| sidecar from a device or control loop. | ||
|
|
||
| The sequence follows delivery of committed writes, not measurement time. | ||
| Late and same-time live history writes therefore remain distinct. Retries keep | ||
| one source ID, sequence, commit ID and the exact encoded bytes. The sidecar uses | ||
| always-sync durability. Core also pauses new writes once its reported store | ||
| size reaches 512 MiB. The sidecar enforces its own space limits. Store limits | ||
| are test budgets, not a claim that shared-disk I/O has no effect on control. | ||
|
|
||
| ## Stop the experiment | ||
|
|
||
| Stop the sidecar, then recreate Core with the normal Compose file: | ||
|
|
||
| ```sh | ||
| docker compose -f docker-compose.yml -f docker-compose.ftwdb-shadow.yml \ | ||
| --profile ftwdb-shadow stop ftwdb-shadow | ||
| docker compose -f docker-compose.yml up -d --no-deps ftw | ||
| ``` | ||
|
|
||
| Keep the candidate data volume when collecting a report. This flow does not | ||
| remove SQLite or Parquet. Do not use `down -v` to disable the experiment. | ||
|
|
||
| ## Validation | ||
|
|
||
| The contract workflow pins the same FTWDB commit as the overlay. It compares | ||
| shared byte fixtures, sends committed SQLite history to the real Rust process, | ||
| drops an acknowledgement, retries exact bytes, kills the process, checks the | ||
| reopened durable receipt and reconciles all copied points offline. Tests also | ||
| cover absent, unhealthy, non-durable and full sidecars, a full client queue, | ||
| failed SQLite commits, late writes, SI units and concurrent status reads. | ||
|
|
||
| Before increasing the scope, measure control latency, CPU, RSS, disk growth, | ||
| sync rate and gaps on a real box for at least 72 hours. Test disk pressure and | ||
| physical power loss on that hardware. Host tests and SIGKILL do not prove SD-card | ||
| power-loss behavior. Keep SQLite/Parquet as the source until those results and | ||
| an explicit data migration justify a separate replacement change. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a tester enables the candidate with this nonstandard overlay and then performs an in-app Core update, the updater recreates
ftwwithout these environment and volume entries, silently ending the experiment. The base service fixesFTW_UPDATER_COMPOSEtodocker-compose.yml, whilediscoverOverridesingo/cmd/ftw-updater/main.goonly adds conventionally named override files, and the update path runscompose up -dfrom that resulting list. Include this overlay in the updater's Compose inputs (or otherwise persist its settings) so updates retain the socket and mount.Useful? React with 👍 / 👎.