Skip to content

Let a deployment bound what DuckDB holds - #977

Merged
skearnes merged 4 commits into
mainfrom
agent/corpus-memory-limit
Aug 18, 2026
Merged

Let a deployment bound what DuckDB holds#977
skearnes merged 4 commits into
mainfrom
agent/corpus-memory-limit

Conversation

@skearnes

@skearnes skearnes commented Aug 18, 2026

Copy link
Copy Markdown
Member

Summary

Corpus created its DuckDB connection with no configuration, so a caller could not set memory_limit without reaching into _connection. In a container that is not a small gap: DuckDB reads the cgroup, so it sizes its default limit from the container's own cap rather than the host's — but that limit bounds DuckDB's buffers, not the process, and the default claims about 80% of the cap for them. The occurrence index build holds up to 2.8 GB beside that accounting, so the default is over the cap by construction, and reaching a cap is a kill: no exception, no log line, exit 137.

Measured in a container over the full corpus (2,428,291 reactions), calling check_index():

cap memory_limit outcome
4 GiB 3.1 GiB (DuckDB's default) OOM-killed, exit 137
4 GiB 2 GiB OOM-killed, exit 137
4 GiB 1 GiB OutOfMemoryException at 195s, peak RSS 3.91 GiB
8 GiB 6.3 GiB (DuckDB's default) OOM-killed at 85s
12 GiB 5 GiB OutOfMemoryException at 194s, peak RSS 8.65 GiB
12 GiB 6500MiB 18,847,978 occurrences, peak RSS 9.29 GiB

The catchable exception check_index() documents only arrives when the limit leaves room; the docstring promised it unconditionally.

Changes

  • Corpus(..., memory_limit=...) passes the setting to duckdb.connect.
  • _warn_when_the_cap_leaves_no_headroom reads the cgroup at open (v2, then v1, treating max and the top-of-range sentinel as no cap) and warns when it leaves less than 4 GB above the limit.
  • check_index and the README's deployment section say what a cap has to leave, and the floor reads 5–6.5 GB rather than 5 GB — 5 GiB raised where 6500MiB finished.

Testing

uv run pytest -n auto — 1189 passed, 2 skipped.

Eleven new tests cover the setting reaching DuckDB, the size parser, max and the v1 sentinel reading as no cap, and the warning firing on a cap with no headroom while staying quiet on one with headroom and on a machine with no cgroup to read.

End to end in an 8 GiB container, the configuration that was killed above:

WARNING execute.py:547: this container may hold 8.0 GB and DuckDB's memory_limit is 6.3 GB,
leaving 1.7 GB for everything else the process holds, which building the occurrence index
has been measured to want 4.0 GB of. Reaching the cap is a kill rather than an exception,
so open the Corpus with a memory_limit that leaves that much, or give the container more.

Notes

The default is unchanged: no memory_limit means DuckDB decides, as before. The warning is the only new behavior on that path, and it fires only where a cgroup cap is readable.

Wall-clock figures above are inflated — the container read the artifacts and spilled through a bind mount. Resident sizes, which are what the change is about, are unaffected.

🤖 Generated with Claude Code

Greptile Summary

The PR lets callers configure DuckDB's memory limit and adds deployment guidance for budgeting memory under container caps.

  • Passes an optional memory_limit through Corpus to DuckDB.
  • Detects effective caps across ancestor cgroups and warns when insufficient process headroom remains.
  • Adds focused parsing, cap-detection, warning, and configuration tests.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains.

No blocking failure remains.

Important Files Changed

Filename Overview
ord_schema/search/execute.py Adds configurable DuckDB memory limits, cgroup-cap detection, and a headroom warning during Corpus initialization.
ord_schema/search/execute_test.py Adds unit coverage for memory-limit propagation, setting parsing, ancestor cap selection, and warning behavior.
ord_schema/search/README.md Documents measured memory requirements and recommended container memory-limit configuration.

Reviews (2): Last reviewed commit: "Read the cgroup this process is in, not ..." | Re-trigger Greptile

skearnes and others added 2 commits August 17, 2026 20:24
DuckDB reads the cgroup, so a container gets a memory_limit sized to its
own cap rather than to the host. That limit bounds DuckDB's buffers, not
the process, and the default takes about 80% of the cap for them: over
ORD the index build holds up to 2.8 GB outside that accounting, so an
8 GiB container running the default 6.3 GiB limit is killed 85s in,
while the same build under a 6500MiB limit and a 12 GiB cap finishes at
9.3 GB resident.

Corpus took no configuration, so a caller could not set the limit
without reaching into the connection. It takes memory_limit now, and
warns at open when a cap it can read leaves less headroom than a build
wants, since reaching the cap arrives as neither an exception nor a log
line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread ord_schema/search/execute.py Outdated
skearnes and others added 2 commits August 17, 2026 20:46
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A cap can be stated at any level between the process's own cgroup and
the mount root, and reading only the root misses it wherever the process
sits below one. Under --cgroupns=host the root states no cap at all, so
the warning stayed silent in exactly the deployment that needed it:

    /proc/self/cgroup: 0::/docker/afe7dc23...
    cap        : 8.0 GB
    root alone : None

The path comes from /proc/self/cgroup now, matched by controller name so
a v1 line is not read against the v2 mount, and every level from there up
is consulted. The smallest cap wins, since that is the one the process
reaches first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@skearnes
skearnes merged commit 900f0ca into main Aug 18, 2026
23 checks passed
@skearnes
skearnes deleted the agent/corpus-memory-limit branch August 18, 2026 00:53
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