Skip to content

Add indexing scheduling plan density optimization - #6796

Merged
nadav-govari merged 3 commits into
nadav/plan3-testfrom
nadav/gcp-optimization
Sep 16, 2026
Merged

nadav-govari merged 3 commits into
nadav/plan3-testfrom
nadav/gcp-optimization

Conversation

@nadav-govari

Copy link
Copy Markdown
Collaborator

Description

Introduces a hill climbing density optimization pass for the scheduling algorithm. Copied from the Readme:

The idea behind optimization is simple: if we're able to perform one single action that improves the plan, over time, we'll eventually
converge on a plan that's quite good. This is a hill climbing algorithm - we don't attempt to solve the optimization problem in one shot,
because 1. It's very complex, and the algorithm to do so is (dangerously) exponential, and 2. it's very disruptive - we'd likely reset a lot of pipelines at once.

Optimization is gated on three things: locality being enabled, the plan being stable, and a 30 second cooldown.

The stability requirement is because resetting a pipeline, which optimization does, is expensive, as
we throw away uncommitted work for the shards in that pipeline. So we try to perform optimization only on a stable cluster.
This works doubly well with deployments, because deployments, which are already resetting many pipelines in their own right, also leave the cluster
in a chaotic state. Optimization can kick in once the plan is stable (and the deployment finished).

Optimization itself works in a naive, greedy, simple way. For each source, find two pipelines that are running under capacity; rearrange the shards each is indexing
so that one pipeline fills up.

If we do this enough times, eventually, we'll end up with the minimal number of pipelines, and optimal density.

How was this PR tested?

Unit tests and extensive cluster tests.

@nadav-govari
nadav-govari added this pull request to stack #6797 September 14, 2026 20:53
@nadav-govari
nadav-govari marked this pull request as ready for review September 14, 2026 20:54
@nadav-govari
nadav-govari requested a review from a team as a code owner September 14, 2026 20:54
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 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-16T15:24:56.606129Z 3d77337 New commits
ℹ️ 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: 53240078fd

ℹ️ 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".

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

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +419 to +423
let can_optimize_plan = is_plan_eligible_for_optimization(
&indexers,
&indexer_statuses,
is_locality_aware,
&mut self.state,

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 Trigger optimization from the stable control loop

When an underfilled plan becomes stable after a normal apply, the periodic ControlPlaneLoop calls control_running_plan, whose equal-node/equal-task path performs no rebuild, so this eligibility check is never revisited when the 30-second deadline expires. The event-driven rebuild that created the plan generally occurs before the new plan is stable, meaning a quiet cluster can remain underfilled indefinitely; schedule a repair rebuild from the stable control-loop path when the cooldown is due.

Useful? React with 👍 / 👎.

Comment on lines +142 to +144
let combined_num_shards = indexing_tasks[*donor_task_ord].shard_ids.len()
+ indexing_tasks[*receiver_task_ord].shard_ids.len();
if combined_num_shards <= max_num_shards {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Redistribute shards when partial pipelines cannot be merged

When the two smallest pipelines exceed the capacity in aggregate, this code makes no repair even when redistribution can reduce the pipeline count. For example, with capacity 10 and three pipelines containing 6 shards each, moving 4 shards produces [10, 2, 6], after which the latter two can merge into an optimal [10, 8]; the current implementation permanently stalls at [6, 6, 6], so the advertised hill-climbing convergence fails for valid layouts.

Useful? React with 👍 / 👎.

.map(|(task_ord, _)| task_ord)
.collect();
task_ords.sort_by_key(|task_ord| (indexing_tasks[*task_ord].shard_ids.len(), *task_ord));
let [donor_task_ord, receiver_task_ord, ..] = task_ords.as_slice() else {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

O_O I did not know we could do that. That's so cool!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

so you only consider merging opportunities right?
if max_num_shards == 3, and you have 3 pipelines with 2 shards, density won't get improved?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I considered this. The approach is then to always collapse pipelines so that one is full. ie. 2 + 2 should leave 3 + 1, and then the next pass will fill the 1.

There were two drawbacks to this:
The first, it will take longer for the plan to settle, and it will reset more pipelines. It will also create partially full pipelines for some period of time, ie. to go from pipelines of 2/2/2 to 3/3, you first need to go to 3/1/2, then 3/3, which means you're also producing more small splits until that part is done. Because indexers are shuffled (important to not hammer one indexer/keep one shard from getting indexed for too long), it could be some number of minutes before it's re-addressed.

The second is that it's more code, and a little more complexity as a result.

We can do this. The density percentage (% of pipelines in our plan compared to the mathematical minimum) difference was about 10% . (ie. 240 pipelines vs 260 pipelines). The net result is ~two less compactions and write amplification. Perhaps it's worth it.

(thinking out loud).

@nadav-govari
nadav-govari removed this pull request from stack #6797 September 16, 2026 15:22
@nadav-govari
nadav-govari merged commit 6e061f5 into nadav/plan3-test Sep 16, 2026
9 of 14 checks passed
@nadav-govari
nadav-govari deleted the nadav/gcp-optimization branch September 16, 2026 15:23
nadav-govari added a commit that referenced this pull request Sep 16, 2026
…6694)

* Add indexing scheduling plan density optimization (#6796)

* Add locality optimization to indexing scheduler (#6799)
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