From 2f0f83992efc4cef851f9e520d778cb77e4596dc Mon Sep 17 00:00:00 2001 From: mattthew Date: Wed, 8 Jul 2026 14:47:40 -0400 Subject: [PATCH 1/2] chore(docker): jemalloc-accurate local image + contrib/Dockerfile cleanup Add `make local-image-jemalloc`, which compiles a jemalloc-enabled, CGO, native-Linux dgraph binary inside a Linux container and packages it via contrib/Dockerfile -- the same runtime image CI/CD publishes. The plain local-image target cross-compiles from macOS with BUILD_TAGS= empty, so on a Mac it ships a non-jemalloc binary that misrepresents production memory behavior. Building natively in a container sidesteps the macOS->Linux jemalloc cross-compile entirely. - contrib/Dockerfile.build: new multi-stage builder; exports the compiled binary to ./linux via BuildKit for contrib/Dockerfile's `ADD linux`. - Makefile: local-image-jemalloc target plus BUILD_PLATFORM/GO_VERSION knobs (defaults to the host arch so the jemalloc compile runs natively). - contrib/Dockerfile: drop libjemalloc-dev -- the binary statically links jemalloc, so no runtime .so is loaded -- and remove the no-op GODEBUG=madvdontneed=1 (Go's default since 1.16, and it never governed jemalloc's off-heap arenas). Document JE_MALLOC_CONF as the real tuning knob, prefixed because jemalloc is built --with-jemalloc-prefix=je_. - .dockerignore: exclude dgraphtest/datafiles (~24GB) and cached binaries so the build context is ~1GB instead of ~25GB. Keep linux/ so `ADD linux` works. Co-Authored-By: Claude Opus 4.8 (1M context) --- .dockerignore | 12 +++++++++++ Makefile | 21 +++++++++++++++++++ contrib/Dockerfile | 19 +++++++++++++++-- contrib/Dockerfile.build | 45 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 contrib/Dockerfile.build diff --git a/.dockerignore b/.dockerignore index 50d169d3f4c..b686c15e9e6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,14 @@ wiki .github + +# Large, build-irrelevant paths. `make dgraph` only compiles Go + jemalloc, so +# none of these belong in the build context. dgraphtest/datafiles alone is ~24GB +# of LDBC/test fixtures and will fill the Docker VM disk if copied in. +dgraphtest/datafiles +dgraphtest/binaries + +# Local build artifacts. NOTE: do NOT ignore `linux/` here -- both local-image +# and local-image-jemalloc stage the built binary there for contrib/Dockerfile's +# `ADD linux /usr/local/bin`, and ignoring it breaks that step. +dgraph/dgraph +test-results.xml diff --git a/Makefile b/Makefile index 248c32f3151..508df9fa65d 100644 --- a/Makefile +++ b/Makefile @@ -196,6 +196,27 @@ endif .PHONY: image-local image-local: local-image ## Alias for local-image +# Overridable knobs for local-image-jemalloc. BUILD_PLATFORM defaults to the +# host arch so the build (and jemalloc compile) runs natively under Docker +# Desktop; override to linux/amd64 to match amd64 CI (emulated via qemu). +BUILD_PLATFORM ?= linux/$(GOHOSTARCH) +GO_VERSION ?= 1.26 + +.PHONY: local-image-jemalloc +local-image-jemalloc: ## Build jemalloc-enabled dgraph/dgraph:local by compiling natively in a Linux container (macOS-friendly; mirrors the CI/release build) + @echo "==> Compiling jemalloc-enabled dgraph binary in a Linux container ($(BUILD_PLATFORM), go $(GO_VERSION))" + @mkdir -p linux + @docker build \ + --platform=$(BUILD_PLATFORM) \ + --build-arg GO_VERSION=$(GO_VERSION) \ + --target=artifact \ + --output=type=local,dest=./linux \ + -f contrib/Dockerfile.build . + @echo "==> Packaging dgraph/dgraph:local via contrib/Dockerfile (same runtime image as CI/CD)" + @docker build --platform=$(BUILD_PLATFORM) -f contrib/Dockerfile -t dgraph/dgraph:local . + @rm -rf linux + @echo "==> Done: dgraph/dgraph:local is in Docker Desktop ($(BUILD_PLATFORM), jemalloc enabled)" + .PHONY: clean clean: ## Clean build artifacts $(MAKE) -C dgraph clean diff --git a/contrib/Dockerfile b/contrib/Dockerfile index 95a177527ea..2492da61c4e 100644 --- a/contrib/Dockerfile +++ b/contrib/Dockerfile @@ -28,7 +28,6 @@ RUN set -eu; \ curl \ iputils-ping \ jq \ - libjemalloc-dev \ less \ sysstat \ gnupg \ @@ -44,5 +43,21 @@ ADD linux /usr/local/bin RUN mkdir /dgraph WORKDIR /dgraph -ENV GODEBUG=madvdontneed=1 +# jemalloc tuning knob. The binary statically links jemalloc (built with +# --with-jemalloc-prefix=je_ and a compile-time +# --with-malloc-conf='background_thread:true,metadata_thp:auto'; see +# dgraph/Makefile), so no libjemalloc shared object is loaded at runtime. +# Because of the je_ prefix, the runtime override variable is JE_MALLOC_CONF, +# not the standard MALLOC_CONF. To tighten RSS return under a cgroup memory +# limit, run with e.g. +# -e JE_MALLOC_CONF=dirty_decay_ms:5000,muzzy_decay_ms:5000 +# (background_thread stays on via the compile-time default). +# On a CPU-limited container also consider narenas: or +# percpu_arena:percpu -- jemalloc sizes arenas from the CPUs it detects (cpuset, +# not the CFS quota), so it over-allocates arenas under a --cpus limit. Full +# option reference: https://jemalloc.net/jemalloc.3.html +# +# The former GODEBUG=madvdontneed=1 was removed: MADV_DONTNEED is the Go runtime +# default since Go 1.16, and it only ever governed the Go heap, never jemalloc's +# off-heap arenas where Dgraph's large allocations live. It was a no-op here. CMD ["dgraph"] diff --git a/contrib/Dockerfile.build b/contrib/Dockerfile.build new file mode 100644 index 00000000000..919ad89c546 --- /dev/null +++ b/contrib/Dockerfile.build @@ -0,0 +1,45 @@ +# syntax=docker/dockerfile:1 +# Multi-stage builder for `make local-image-jemalloc`. +# +# Compiles a jemalloc-enabled, CGO, native-Linux dgraph binary the same way CI +# and the release build do -- natively inside a Linux container -- so no +# macOS->Linux cross-compilation is required. (That cross-compile is exactly why +# the plain `local-image` target ships a non-jemalloc binary on macOS: the +# jemalloc static archive would have to be cross-built for the Linux target.) +# BuildKit exports the compiled binary to ./linux via `--output=type=local`, and +# contrib/Dockerfile then packages it into the same runtime image CI/CD publishes. + +ARG GO_VERSION=1.26 + +FROM golang:${GO_VERSION} AS builder +# dgraph/Makefile's `jemalloc` target downloads jemalloc 5.3.1 and builds it from +# source, statically linking /usr/local/lib/libjemalloc.a with the baked +# malloc_conf (background_thread:true,metadata_thp:auto). That needs a C/C++ +# toolchain plus curl/bzip2 to fetch and unpack the release tarball. +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + bzip2 \ + ca-certificates \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /src +# Prime the module cache first so dependency downloads survive source-only edits. +COPY go.mod go.sum ./ +RUN --mount=type=cache,target=/go/pkg/mod go mod download + +COPY . . +# `make dgraph` builds jemalloc from source and compiles with the default +# BUILD_TAGS=jemalloc and CGO enabled: the exact CI/release build. USER_ID +# resolves to 0 (root) in the container, so the jemalloc `make install` step +# needs no sudo. +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + make dgraph + +# Export-only stage. `docker build --target=artifact --output=type=local,dest=./linux` +# writes this stage's root filesystem to the host, producing ./linux/dgraph for +# contrib/Dockerfile's `ADD linux /usr/local/bin`. +FROM scratch AS artifact +COPY --from=builder /src/dgraph/dgraph /dgraph From a2233e744cf342940ff5ccd765678c97dc5c138d Mon Sep 17 00:00:00 2001 From: mattthew Date: Wed, 12 Aug 2026 19:51:10 -0400 Subject: [PATCH 2/2] Tie the curl and tar command for cleaner exits --- dgraph/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dgraph/Makefile b/dgraph/Makefile index 4857a179e9c..c625a8349f0 100644 --- a/dgraph/Makefile +++ b/dgraph/Makefile @@ -111,7 +111,7 @@ jemalloc: @if [ -z "$(HAS_JEMALLOC)" ] ; then \ mkdir -p /tmp/jemalloc-temp && cd /tmp/jemalloc-temp ; \ echo "Downloading jemalloc" ; \ - curl -f -s -L ${JEMALLOC_URL} -o jemalloc.tar.bz2 ; \ + curl -f -s -L ${JEMALLOC_URL} -o jemalloc.tar.bz2 && \ tar xjf ./jemalloc.tar.bz2 ; \ cd jemalloc-5.3.1 ; \ sed -i.bak 's|std::__throw_bad_alloc()|throw std::bad_alloc()|g' src/jemalloc_cpp.cpp ; \