Skip to content

perf(ci): isolate bottlecap build-directory cache per concurrent build - #1369

Draft
lucaspimentel wants to merge 1 commit into
mainfrom
lpimentel/isolate-bottlecap-build-cache
Draft

perf(ci): isolate bottlecap build-directory cache per concurrent build#1369
lucaspimentel wants to merge 1 commit into
mainfrom
lpimentel/isolate-bottlecap-build-cache

Conversation

@lucaspimentel

Copy link
Copy Markdown
Member

Overview

The two bottlecap compile Dockerfiles mount the cargo build directory (target) as a BuildKit cache mount with no sharing mode. All concurrently-scheduled bottlecap (*) compile jobs that use the same Dockerfile therefore share one cache mount, and cargo serializes them on its build-directory lock: while one build holds the lock, the others log Blocking waiting for file lock on build directory and sit idle.

Measured over 240 successful compile attempts across 30 main pipelines (2026-06-25 to 2026-09-09):

  • 14.2% of attempts (34/240) hit the cargo build-directory lock wait
  • Lock wait: p50 4.0m, p90 5.6m, max 13.1m
  • 8.6% of all compile wall time is spent waiting on this lock (2.3h of 26.7h)
  • Jobs that hit the lock take +2.8m longer at the median (8.5m vs 5.7m)
  • Worst flavors: amd64 33%, arm64, fips 23%

This adds sharing=private to the target cache mount only, in both images/Dockerfile.bottlecap.compile and images/Dockerfile.bottlecap.alpine.compile. Each concurrent build now gets its own cache instance instead of blocking.

Why not a per-flavor id=? A per-flavor cache id= (e.g. id=target-${PLATFORM}-fips${FIPS}) would also isolate the caches, but neither Dockerfile has a # syntax= directive and CI builds under docker:20.10, so build-arg expansion inside a mount id= may silently not apply on that frontend. If expansion failed, all flavors would collapse into one literal id, which is worse than the current behavior and hard to notice. sharing=private needs no variable expansion.

Why the registry/git mounts are unchanged: the cargo registry and git cache mounts are read-mostly and shared registry cache is genuinely useful across flavors. The exclusive lock that caused every measured wait is on the build directory, not the registry. Keeping this to one variable also means any change in the lock-wait rate after merge is attributable to this change.

Known risk (worth flagging, not buried): 5 of 10 analyzed compile-job stalls occur at the final LTO link (lto = true, codegen-units = 1, bottlecap/Cargo.toml:129-131). If those stalls are memory pressure from concurrent LTO links on a shared runner, the cargo lock has been accidentally serializing them, and this change lets up to 4 same-Dockerfile flavors hit LTO simultaneously, which could raise the job_execution_timeout rate. This is an unvalidated hypothesis (would need runner co-tenancy data; 9-10 events is likely underpowered), but it means the timeout rate should be watched after merge rather than assumed to improve.

Testing

  • docker build --check (Docker 29.8.0, current buildkit frontend) parses both modified Dockerfiles with no warnings. The Dockerfile parser strips full-line comments inside continued RUN instructions, so the added comment lines are inert.
  • The real validation is CI on this branch, and the effect is statistical rather than per-run: compile jobs should succeed on all 8 flavors, and no Blocking waiting for file lock on build directory should appear in their traces. A single green pipeline does not prove the improvement, since only 14.2% of attempts hit the lock.

The target cache mount in the compile Dockerfiles had no sharing mode, so
concurrent bottlecap compile jobs using the same Dockerfile shared one cache
and serialized on cargo's build-directory lock. Measured across 240 compile
attempts on 30 main pipelines: 14.2% of attempts waited on the lock (median
wait 4.0m, max 13.1m), adding 2.8m to the median job duration when hit.

sharing=private gives each concurrent build its own target cache instead of
blocking. Cache reuse is barely affected: 89% of attempts are already fully
cold. The cargo registry and git mounts stay shared, since the measured
contention was only on the target directory.
Copilot AI lite review requested due to automatic review settings September 10, 2026 19:18
@lucaspimentel
lucaspimentel requested a review from a team as a code owner September 10, 2026 19:18
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-10T19:20:02.620317Z a2b222f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copilot AI left a comment

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.

🟡 Changes recommended

The added full-line comments are embedded inside continued RUN --mount=... instructions and can be interpreted as part of the shell command on some Dockerfile frontends, risking broken builds or dropping the intended mount behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR aims to reduce CI wall time for Bottlecap compile jobs by preventing concurrent builds from contending on Cargo’s shared target build-directory lock when using BuildKit cache mounts.

Changes:

  • Updates the target BuildKit cache mount to use sharing=private in the AL2 compile Dockerfile.
  • Updates the target BuildKit cache mount to use sharing=private in the Alpine compile Dockerfile.
File summaries
File Description
images/Dockerfile.bottlecap.compile Sets the target cache mount to sharing=private to avoid cross-job contention on Cargo’s build-directory lock.
images/Dockerfile.bottlecap.alpine.compile Same sharing=private change for the Alpine-based compile image to isolate concurrent builds.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 31 to +34
--mount=type=bind,source=.,target=/tmp/dd,rw \
--mount=type=cache,target=/tmp/dd/bottlecap/target \
# sharing=private gives each concurrent build its own target cache instead of
# blocking on cargo's build-directory lock while waiting for a shared mount
--mount=type=cache,sharing=private,target=/tmp/dd/bottlecap/target \
Comment on lines 32 to +35
--mount=type=bind,source=.,target=/tmp/dd,rw \
--mount=type=cache,target=/tmp/dd/bottlecap/target \
# sharing=private gives each concurrent build its own target cache instead of
# blocking on cargo's build-directory lock while waiting for a shared mount
--mount=type=cache,sharing=private,target=/tmp/dd/bottlecap/target \
@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Sep 10, 2026

Copy link
Copy Markdown

Pipelines

⚠️ Warnings

Your PR has failed checks. Please review the issues below and take necessary action before merging.

🚦 1 Pipeline job failed

DataDog/datadog-lambda-extension | publish layer e2e sandbox (amd64, fips)

View more details · View in GitLab

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: a2b222f | Docs | View more details | Give us feedback!

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