Skip to content

Add node-wide indexing memory backpressure - #6916

Open
PSeitz wants to merge 1 commit into
quickwit-oss:mainfrom
PSeitz:index-writer-memory-limit
Open

PSeitz wants to merge 1 commit into
quickwit-oss:mainfrom
PSeitz:index-writer-memory-limit

Conversation

@PSeitz

@PSeitz PSeitz commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator

Why

Avoid OOM crashes.

How

Before emitting a batch, each source checks the combined writer memory and in-flight batch sizes across all pipelines on the node, using the existing atomic-backed gauges.

When usage reaches indexer.indexing_memory_limit, sources stop feeding new batches into the pipeline. They check memory usage every 100 ms and resume once it drops below the limit. Downstream work drains and commits normally.

The limit is disabled by default. Enable it with:

indexer:
  indexing_memory_limit: 2GiB

This is a soft threshold, not a process-memory cap. Batch accounting uses raw input bytes rather than exact heap usage, and already-admitted work can overshoot. Source/network buffers and ingest queues are outside the budget.

Verification

  • make fmt
  • cargo nextest run -p quickwit-indexing -p quickwit-config: 369 passed, 1 skipped.
  • Re-ran both new tests after squashing: passed.
  • Production throughput and memory-overshoot measurements remain to be done.

@PSeitz
PSeitz requested a review from a team as a code owner October 20, 2026 21:15
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 20, 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-10-20T21:23:23.028584Z a20e2fd 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a20e2fdbe3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +405 to +407
if usage >= limit.as_u64() as f64 {
ctx.schedule_self_msg(Duration::from_millis(100), Loop);
return Ok(());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Propagate the limit to Kinesis shard consumers

When Kinesis is used and node usage remains above the limit, returning here stops only the parent SourceActor: the per-shard consumers continue calling get_records and sending payload-bearing messages (kinesis/shard_consumer.rs:180-208) into the 1,000-slot channel (kinesis/kinesis_source.rs:118), whose receiver is drained only by emit_batches. Those queued bytes are absent from all three gauges, so sustained input during a commit/serialization pause can accumulate up to 1,000 fetch responses and OOM the node despite the new backpressure setting; propagate the pause to the shard consumers or reserve/account the channel payloads.

Useful? React with 👍 / 👎.

@Mallets
Mallets self-requested a review October 21, 2026 07:30
| `cpu_capacity` | Advisory parameter used by the control plane. The value can expressed be in threads (e.g. `2`) or in term of millicpus (`2000m`). The control plane will attempt to schedule indexing pipelines on the different nodes proportionally to the cpu capacity advertised by the indexer. It is NOT used as a limit. All pipelines will be scheduled regardless of whether the cluster has sufficient capacity or not. The control plane does not attempt to spread the work equally when the load is well below the `cpu_capacity`. Users who need a balanced load on all of their indexer nodes can set the `cpu_capacity` to an arbitrarily low value as long as they keep it proportional to the number of threads available. | `num threads available` |
| `enable_cooperative_indexing` | Enable sharing resources more efficiently when the number of indexes actively written to is significantly higher than the number of cores but might decrease the overall indexing throughput. | `false` |

The `indexer.indexing_memory_limit` budget is shared across all indexing pipelines on a node. It accounts for reported Tantivy writer memory (including RAM directories) through serialization, plus raw and processed document batches queued or being handled. Both batch types are accounted for using their raw input byte sizes, not their exact heap usage; raw and processed batches can overlap during processing. When usage reaches the limit, sources stop reading and emitting new batches and retry every 100 ms. Downstream work drains normally, with existing commit timeouts and per-pipeline commit triggers unchanged. Already-admitted work can overshoot the limit. Source/network buffers, ingest queues, and parsing or indexing expansion are not fully accounted for, so this is not a strict cap on memory usage or total process RSS. It is disabled by default; set it to a positive byte size such as `2GiB` to enable it.

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.

What's the rule of thumb rule to set a good value if everything is not accounted for?

pub struct IndexerConfig {
/// Memory budget shared across all indexing pipelines on this node. Accounts for reported
/// Tantivy writer memory (including RAM directories) and raw input bytes of in-flight batches.
/// Sources stop emitting while at the limit; downstream work drains normally. Disabled by

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.

What happens when the memory limit goes back below the budget? Is there any kind of backoff or source resume emitting as fast as they can?

type Reply = ();

async fn handle(&mut self, _message: Loop, ctx: &SourceContext) -> Result<(), ActorExitStatus> {
if let Some(limit) = self.indexing_memory_limit {

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.

I think performing the check here alone may miscount the limit.

A burst from source can admit enough future work to cause an OOM later in the indexer, even after the sources have stopped. A more robust check should be done on the indexer that decides wether to consume the next batch and eventually do an early commit.

This branch has not been deployed

No deployments
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