Skip to content

export buildkit history and structured lifecycle telemetry at teardown - #128

Open
piob-io wants to merge 1 commit into
mainfrom
devin/1787595905-docker-build-observability
Open

export buildkit history and structured lifecycle telemetry at teardown#128
piob-io wants to merge 1 commit into
mainfrom
devin/1787595905-docker-build-observability

Conversation

@piob-io

@piob-io piob-io commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Guest half of the docker-builds observability design: at post-step teardown the action exports the job's raw BuildKit history and ships it, with the runner step timeline and structured lifecycle facts, to the vm-agent's new ReportDockerBuild RPC on the existing StickyDiskService channel.

  • History export (src/build-telemetry.ts): exportBuildHistory streams Control.ListenBuildHistory (EarlyExit) against the local buildkitd, reads each BuildHistoryRecord as a typed protobuf-es message (Ref, CompletedAt, trace descriptor), clears the logs descriptor, and re-serializes with toBinary — protobuf-es retains unknown fields across the round trip, so record fields newer than the vendored proto still reach the vm-agent intact. It then fetches the solve-status trace attachment from buildkitd's containerd content service and applies per-record/per-trace/total payload caps. In-flight builds are exported incomplete; oversized ones are marked truncated. pruneBuildHistory then deletes the exported refs via UpdateBuildHistory{Delete} so history.db and its content-store blobs never get committed to the sticky disk.
  • Ordering in the post step: export history → prune history → buildkitd shutdown → integrity check → fs usage → unmount → commit/skip → report.
  • Lifecycle facts: builder mode + fallback reason (blacksmith-remote / local-fallback / existing, with stickydisk-setup-failed / buildkitd-failed reasons), commit decision + skip reason (step-failures, integrity, sigkill, cleanup-error, no-expose), integrity outcome + duration, buildctl du -v totals incl. a per-cache-mount Nested(mount_id, bytes, records) snapshot, fs used/size, prune bytes, hotload/ready/shutdown durations, sigkill flag.
  • Step timeline: the newest Worker _diag log ships raw (size-capped); parsing and build↔step attribution happen agent-side.
  • Identity is host-issued: the guest sends only build facts + expose_id; docker_build_ids come back in the fa response. All reporting is fail-soft with bounded timeouts (15s export, 10s report) — telemetry can never fail the customer job.

Protos live under proto/: the fa stickydisk proto plus BuildKit's control API and containerd's content API vendored verbatim from pinned upstream releases (buildkit v0.32.2, containerd v2.3.4) via scripts/fetch-protos.sh — no hand-maintained proto subsets and no hand-written wire-format parsing. TypeScript bindings are generated into src/gen/ at dev time via pnpm generate (buf + protoc-gen-es); nothing is fetched or compiled at action run time. dist/ is rebuilt.

Paired PRs: FastActions/fa (agent-side parsing/ingest) and useblacksmith/web (ClickHouse migration).

Link to Devin session: https://app.devin.ai/sessions/0c6ebc2a6de54933bebe821e21ab8b76
Requested by: @piob-io


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@piob-io
piob-io marked this pull request as ready for review August 24, 2026 21:15
@piob-io
piob-io marked this pull request as draft August 24, 2026 21:19
@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1787595905-docker-build-observability branch from 5ce4997 to 303b922 Compare August 24, 2026 21:52
@piob-io
piob-io marked this pull request as ready for review August 24, 2026 21:58
@piob-io
piob-io requested a review from ajwerner August 26, 2026 12:57
Comment thread src/main.ts
Comment on lines +632 to +635
// Export this job's build history (raw record + trace bytes) and delete
// the records afterwards so history.db and its content-store attachments
// never get committed to the sticky disk. Must happen before shutdown.
const builds = await exportBuildHistory(buildkitdAddr, lifecycle);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this valid? The sticky disk as a block io device doesn't know about which logical files have been deleted, right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right — the block device has no notion of file deletion, so residual bytes can remain in freed blocks after commit; we don't claim physical scrubbing. What the delete does guarantee is filesystem-level: it happens via buildkitd's UpdateBuildHistory{Delete} API (BoltDB record delete + content-store blob unref) before buildkitd shutdown → integrity check → unmount → commit, so the committed filesystem no longer references those records. The purpose is (1) history.db doesn't grow unboundedly across runs and (2) the next job's export doesn't re-export prior jobs' records — not data erasure. I've reworded the comment to say exactly that (was misleadingly phrased as "never get committed").

@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1787595905-docker-build-observability branch from 303b922 to 87c7d43 Compare August 26, 2026 15:07
@ajwerner

Copy link
Copy Markdown
Contributor

@devin do we bound the size of the report we generate in terms of bytes? I think we need a way to hard bound it somewhere around 10s of MiB and under 128 MiB

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Yes — the report is hard-bounded to ~10 MiB worst case, enforced at assembly time in src/build-telemetry.ts:

  • MAX_RECORD_BYTES = 512 KiB per history record (oversized records ship without payload, marked truncated)
  • MAX_TRACE_BYTES = 2 MiB per solve-status trace (dropped + truncated if over)
  • MAX_TOTAL_PAYLOAD_BYTES = 8 MiB running cap across all records+traces combined — once hit, remaining builds ship metadata-only
  • MAX_TIMELINE_BYTES = 1 MiB tail-cap on the runner _diag step timeline

Everything else in the report (lifecycle facts, du totals, per-cache-mount rows) is small fixed-size fields, so the whole ReportDockerBuild request tops out around 9–10 MiB and is typically a few hundred KiB. Well under the 128 MiB ceiling and inside the "10s of MiB" target.

At post-step teardown, export the job's raw BuildKit history records (plus
solve-status traces, logs excluded, size-capped) via ListenBuildHistory,
delete them so history.db and its content-store attachments are never
committed to the sticky disk, and ship them with the runner _diag step
timeline and structured lifecycle facts (builder mode/fallback reason,
commit decision/skip reason, integrity outcome, buildctl du totals incl.
per-cache-mount, fs usage) to the vm-agent's new ReportDockerBuild RPC.
docker_build_ids are issued host-side; all reporting is fail-soft with
bounded timeouts and never fails the customer job.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1787595905-docker-build-observability branch from 87c7d43 to b357178 Compare August 26, 2026 16:50
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