Skip to content

Use predefined deferred ID#8329

Merged
IvanPleshkov merged 19 commits into
devfrom
use-predefined-deferred-internal-id
Mar 10, 2026
Merged

Use predefined deferred ID#8329
IvanPleshkov merged 19 commits into
devfrom
use-predefined-deferred-internal-id

Conversation

@IvanPleshkov

Copy link
Copy Markdown
Contributor

In dev we calculate the deferred id dynamically. It was a requirement for multivectors where the size of the vector is unknown.

But a dynamic approach doesn't solve the problem with deferred multivectors. Moreover, we know the deferred status of the point only after insertion.

After brainstorm it was decided to use 16 inner vectors as a constant for deferred id calculation. Such a decision makes everything easier and allows the use of deferred id for HNSW-indexed vectors only.

In this PR we calculate the deferred point id on the collection-config level and provide it to the segment, where we apply it only for appendable segments.

PR Pros:

  • Fewer segment fields, deferred point id only without threshold.
  • We know deferred point status before upsertion.
  • Multivectors are supported, and their deferred id is stable between runs.
  • Deferred point id is calculated for HNSW vectors only. If the user sets no-HNSW for some vector name, these vectors are not counted.

PR Cons:

  • Low accuracy for multivectors. But it's a fine drawback. The goal of the milestone is to keep search time low, exact following the threshold is not necessary.
  • Now the deferred point id calculation is a collection config method. Maybe standalone function is better - question to discuss.
  • Deferred point id is provided in optimizers as a field of thresholds. I find it OK but it's it may be a question to discuss.

@IvanPleshkov IvanPleshkov added this to the deferred updates milestone Mar 8, 2026
@IvanPleshkov IvanPleshkov changed the title Use predefined deferred internal Use predefined deferred ID Mar 8, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/segment/src/segment_constructor/segment_constructor_base.rs`:
- Around line 654-656: Update the documentation comment for the field
deferred_internal_id to reflect the new invariant: when segment.is_appendable()
the field stores the configured threshold immediately upon construction (it is
not necessarily None), and points are considered deferred only when their
internal ID is >= deferred_internal_id; rely on the accessors
has_deferred_points() and point_is_deferred() to check deferred status rather
than assuming None means “no deferred points.” Mention deferred_internal_id,
has_deferred_points(), point_is_deferred(), and segment.is_appendable() in the
comment so readers know where to check the runtime semantics.

In `@src/actix/api/snapshot_api.rs`:
- Line 38: Split the combined import statement `use {actix_web_validator as
valid, fs_err as fs};` into two separate `use` lines (one for
`actix_web_validator as valid` and one for `fs_err as fs`) to match the file's
existing import style and resolve the rustfmt failure; update the import section
where the combined statement appears (referencing the symbol `valid` and `fs`)
so each crate is imported on its own `use` statement.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 414c4254-bb59-4387-9d99-7304bc44a380

📥 Commits

Reviewing files that changed from the base of the PR and between 69b9253 and d6a599f.

📒 Files selected for processing (23)
  • lib/collection/src/collection_manager/fixtures.rs
  • lib/collection/src/collection_manager/optimizers/config_mismatch_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/merge_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/vacuum_optimizer.rs
  • lib/collection/src/config.rs
  • lib/collection/src/optimizers_builder.rs
  • lib/collection/src/shards/local_shard/mod.rs
  • lib/collection/src/shards/local_shard/snapshot.rs
  • lib/collection/src/tests/mod.rs
  • lib/collection/src/update_workers/optimization_worker.rs
  • lib/edge/src/optimize.rs
  • lib/segment/src/segment/mod.rs
  • lib/segment/src/segment/segment_ops.rs
  • lib/segment/src/segment/tests.rs
  • lib/segment/src/segment_constructor/segment_builder.rs
  • lib/segment/src/segment_constructor/segment_constructor_base.rs
  • lib/shard/src/operations/optimization.rs
  • lib/shard/src/optimize.rs
  • lib/shard/src/optimizers/segment_optimizer.rs
  • lib/shard/src/segment_holder/mod.rs
  • lib/shard/src/segment_holder/snapshot.rs
  • src/actix/api/snapshot_api.rs
💤 Files with no reviewable changes (1)
  • lib/segment/src/segment/mod.rs

Comment thread lib/segment/src/segment_constructor/segment_constructor_base.rs
Comment thread src/actix/api/snapshot_api.rs Outdated
@IvanPleshkov IvanPleshkov force-pushed the use-predefined-deferred-internal-id branch from d6a599f to 705b35c Compare March 8, 2026 17:20

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/collection/src/config.rs`:
- Around line 230-246: The computed value threshold_bytes.div_ceil(vector_bytes)
can exceed PointOffsetType (u32) and must be clamped or checked before the
unchecked `as PointOffsetType` cast in the closure; change the cast to a checked
conversion (e.g. usize::try_into with handling) or saturate by taking min(value,
PointOffsetType::MAX as usize) and then cast, referencing the closure that
computes element_bytes (Datatype::Float16/Uint8/Float32), dim (params.size),
MULTIVECTOR_SIZE and params.multivector_config so you clamp/handle overflow of
the result of threshold_bytes.div_ceil(vector_bytes) instead of using `as
PointOffsetType`.
- Around line 221-246: The size calculation here uses raw datatype * dim and
ignores vector/collection-level quantization, causing deferred_internal_id to be
undersized; update the sizing logic in this iterator (the closure that computes
vector_bytes) to use the same effective vector data inputs as used by
CollectionParams::to_base_vector_data() (i.e., resolve effective
per-vector/collection quantization and multivector layout before computing
element size and vector length) so element_bytes and vector multiplier match
appendable segment construction; either call or replicate the same computation
from CollectionParams::to_base_vector_data() when deriving vector_bytes
(respecting params.multivector_config and collection-level
appendable_quantization) so threshold_bytes.div_ceil(...) uses the correct
effective byte size.

In `@lib/segment/src/segment_constructor/segment_constructor_base.rs`:
- Line 32: Remove the unused import NonAppendableSegmentEntry from
segment_constructor_base.rs: locate the use statement referencing
NonAppendableSegmentEntry and delete it so the file no longer imports that
unused symbol.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 639181f8-d208-46a6-a924-629d4e6ebd81

📥 Commits

Reviewing files that changed from the base of the PR and between d6a599f and 705b35c.

📒 Files selected for processing (22)
  • lib/collection/src/collection_manager/fixtures.rs
  • lib/collection/src/collection_manager/optimizers/config_mismatch_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/merge_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/vacuum_optimizer.rs
  • lib/collection/src/config.rs
  • lib/collection/src/optimizers_builder.rs
  • lib/collection/src/shards/local_shard/mod.rs
  • lib/collection/src/shards/local_shard/snapshot.rs
  • lib/collection/src/tests/mod.rs
  • lib/collection/src/update_workers/optimization_worker.rs
  • lib/edge/src/optimize.rs
  • lib/segment/src/segment/mod.rs
  • lib/segment/src/segment/segment_ops.rs
  • lib/segment/src/segment/tests.rs
  • lib/segment/src/segment_constructor/segment_builder.rs
  • lib/segment/src/segment_constructor/segment_constructor_base.rs
  • lib/shard/src/operations/optimization.rs
  • lib/shard/src/optimize.rs
  • lib/shard/src/optimizers/segment_optimizer.rs
  • lib/shard/src/segment_holder/mod.rs
  • lib/shard/src/segment_holder/snapshot.rs
💤 Files with no reviewable changes (1)
  • lib/segment/src/segment/mod.rs
✅ Files skipped from review due to trivial changes (1)
  • lib/segment/src/segment_constructor/segment_builder.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • lib/shard/src/operations/optimization.rs
  • lib/shard/src/optimizers/segment_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs
  • lib/collection/src/update_workers/optimization_worker.rs
  • lib/segment/src/segment/tests.rs
  • lib/edge/src/optimize.rs
  • lib/collection/src/tests/mod.rs
  • lib/collection/src/collection_manager/optimizers/vacuum_optimizer.rs
  • lib/collection/src/shards/local_shard/snapshot.rs

Comment thread lib/collection/src/config.rs
Comment thread lib/collection/src/config.rs
Comment thread lib/segment/src/segment_constructor/segment_constructor_base.rs
@IvanPleshkov IvanPleshkov force-pushed the use-predefined-deferred-internal-id branch from 771921a to 4e063cf Compare March 8, 2026 17:33
@IvanPleshkov IvanPleshkov changed the base branch from dev to update-delete-points-logic-on-deferred March 8, 2026 17:33
@IvanPleshkov IvanPleshkov marked this pull request as draft March 8, 2026 17:51
@qdrant qdrant deleted a comment from coderabbitai Bot Mar 8, 2026
@IvanPleshkov IvanPleshkov marked this pull request as ready for review March 8, 2026 23:53
Base automatically changed from update-delete-points-logic-on-deferred to dev March 9, 2026 09:05
@IvanPleshkov IvanPleshkov force-pushed the use-predefined-deferred-internal-id branch from 97d5c50 to 69b2082 Compare March 9, 2026 10:55

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🧹 Nitpick comments (4)
lib/segment/src/segment/tests.rs (1)

780-823: Prefer the normal upsert path in this persistence test.

Lines 780-823 currently use insert_new_vectors() and then patch segment.version on Line 823. That bypasses the usual point-version bookkeeping, so the flushed segment does not match a production-written one. Using upsert_point() here would cover the same deferred-ID behavior without manual state mutation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/segment/src/segment/tests.rs` around lines 780 - 823, The test currently
calls insert_new_vectors in a loop and then mutates segment.version, which
bypasses real per-point version bookkeeping; replace the insert_new_vectors(...)
calls with calls to upsert_point(...) (passing the same PointIdType,
payload/vector via only_default_vector, and hw_counter) so versions are set via
the normal upsert path and the deferred-ID behavior is exercised naturally, and
remove the manual assignment to segment.version (line setting segment.version =
Some(20)); keep the remainder of the assertions (has_deferred_points,
deferred_internal_id, point_is_deferred checks) unchanged.
lib/shard/src/fixtures.rs (1)

164-196: Align this fixture with the new HNSW-backed deferred path.

deferred_internal_id is now derived from HNSW-configured vectors, but this helper still builds a plain-only segment and asserts deferred behavior anyway. That makes the fixture less representative of the production path. I’d either switch the vector config here to HNSW or rename/document this as an intentionally artificial segment state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/shard/src/fixtures.rs` around lines 164 - 196, The fixture
build_segment_with_deferred_1 currently constructs a plain-only segment
(Indexes::Plain) but uses a deferred_internal_id that in production is derived
from HNSW-configured vectors; update the fixture to reflect the production path
by switching the vector index to an HNSW index (replace Indexes::Plain with the
appropriate Indexes::Hnsw configuration in the SegmentConfig/VectorDataConfig
for DEFAULT_VECTOR_NAME and ensure any required HNSW config fields are
provided), or if you intentionally want an artificial plain-only state, rename
the helper and its docstring to explicitly state it’s a synthetic test case that
does not reflect HNSW-derived deferred IDs (update function name
build_segment_with_deferred_1 and its comment accordingly) so the intent is
clear.
lib/collection/src/tests/mod.rs (1)

241-246: Add a non-None regression case for deferred-ID propagation.

This setup still passes if ensure_appendable_segment_with_capacity() stops forwarding deferred_internal_id, because the test never exercises that path. A small follow-up that creates the appendable segment with Some(...) and asserts deferred behavior after a few inserts would lock the new wiring down.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/collection/src/tests/mod.rs` around lines 241 - 246, The test uses
OptimizerThresholds with deferred_internal_id = None so it doesn't exercise
deferred-ID propagation; add a second regression case that sets
OptimizerThresholds.deferred_internal_id = Some(<unique id>) before calling
ensure_appendable_segment_with_capacity (the same helper used in the test) then
perform a few inserts (the existing insert loop) and assert that the created
appendable segment (inspect the segment returned by
ensure_appendable_segment_with_capacity or the collection's active segment)
carries/propagates the deferred_internal_id as expected (e.g.,
segment.internal_id or deferred marker) to lock down deferred-ID wiring.
lib/segment/src/segment_constructor/segment_constructor_base.rs (1)

781-785: Document the new deferred_internal_id parameter on the public constructors.

load_segment() and build_segment() now expose this parameter publicly, but the rustdoc still doesn't explain what it represents or that it is only honored for appendable segments. Adding that here would save callers from having to reverse-engineer the invariant from create_segment().

Also applies to: 865-869

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/segment/src/segment_constructor/segment_constructor_base.rs` around lines
781 - 785, Update the public rustdoc for the constructors (notably load_segment
and build_segment) to document the new deferred_internal_id parameter: explain
what deferred_internal_id represents (the next internal point offset to be
used), that it is only honored for appendable segments, and any invariants/cases
callers must observe (e.g., should be None for non-appendable segments or when
creating from scratch). Also mirror the same documentation change where
build_segment and create_segment are documented so callers can find the behavior
consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/collection/src/config.rs`:
- Around line 224-229: The current filter uses the merged effective_hnsw to
decide if a vector has HNSW, which incorrectly counts vectors that explicitly
disabled HNSW via a per-vector override (e.g., params.hnsw_config with m = 0)
when collection-level payload_m is non-zero; change the predicate to respect
explicit per-vector overrides: determine HNSW-enabled by checking
params.hnsw_config first (if Some, use that config's m or payload_m to decide
enabled) and only if None fall back to the collection-level hnsw_config values;
update the closure around update_opt / effective_hnsw to use this
per-vector-first predicate so deferred_internal_id is computed correctly for
mixed indexed/non-indexed collections.

In `@lib/collection/src/optimizers_builder.rs`:
- Around line 266-272: threshold_config is computed using a single
collection-level num_indexing_threads which ignores per-vector HNSW overrides;
change the call to optimizers_config.optimizer_thresholds so
num_indexing_threads is first derived by iterating the effective per-vector HNSW
configs (resolve each config's max_indexing_threads where 0 is treated as the
fallback/default thread count) and taking the max() across them, then pass that
resolved max into optimizer_thresholds; update references around
collection_params.get_deferred_point_id, hnsw_config, and
optimizers_config.get_deferred_points_threshold_bytes() accordingly so
threshold_config uses the per-vector-resolved thread count.

In `@lib/segment/src/segment/mod.rs`:
- Around line 97-99: Fix the typo in the doc comment for the struct field
deferred_internal_id: change "Avaliable for appendable segments only." to
"Available for appendable segments only." so Codespell passes; the change should
be in the doc string immediately above pub(crate) deferred_internal_id:
Option<PointOffsetType>.

In `@lib/segment/src/segment/segment_ops.rs`:
- Around line 656-660: has_deferred_points() currently compares
deferred_internal_id against total_point_count(), which is the count of assigned
internal IDs and stays > deferred_from even after those points are deleted;
change the check to derive from live mappings instead. Update the body of
has_deferred_points() (referencing deferred_internal_id, is_appendable(),
total_point_count()) to compute whether any non-deleted internal ID >=
deferred_from by scanning the live mapping structure (e.g., the
internal->external mapping or use the
highest_non_deleted_internal_id()/highest_live_internal_id() helper if
available) and return self.is_appendable() &&
(there_exists_live_internal_id_geq_deferred_from). Ensure the scan uses the
canonical live-mapping data structure so deletions are respected.

---

Nitpick comments:
In `@lib/collection/src/tests/mod.rs`:
- Around line 241-246: The test uses OptimizerThresholds with
deferred_internal_id = None so it doesn't exercise deferred-ID propagation; add
a second regression case that sets OptimizerThresholds.deferred_internal_id =
Some(<unique id>) before calling ensure_appendable_segment_with_capacity (the
same helper used in the test) then perform a few inserts (the existing insert
loop) and assert that the created appendable segment (inspect the segment
returned by ensure_appendable_segment_with_capacity or the collection's active
segment) carries/propagates the deferred_internal_id as expected (e.g.,
segment.internal_id or deferred marker) to lock down deferred-ID wiring.

In `@lib/segment/src/segment_constructor/segment_constructor_base.rs`:
- Around line 781-785: Update the public rustdoc for the constructors (notably
load_segment and build_segment) to document the new deferred_internal_id
parameter: explain what deferred_internal_id represents (the next internal point
offset to be used), that it is only honored for appendable segments, and any
invariants/cases callers must observe (e.g., should be None for non-appendable
segments or when creating from scratch). Also mirror the same documentation
change where build_segment and create_segment are documented so callers can find
the behavior consistently.

In `@lib/segment/src/segment/tests.rs`:
- Around line 780-823: The test currently calls insert_new_vectors in a loop and
then mutates segment.version, which bypasses real per-point version bookkeeping;
replace the insert_new_vectors(...) calls with calls to upsert_point(...)
(passing the same PointIdType, payload/vector via only_default_vector, and
hw_counter) so versions are set via the normal upsert path and the deferred-ID
behavior is exercised naturally, and remove the manual assignment to
segment.version (line setting segment.version = Some(20)); keep the remainder of
the assertions (has_deferred_points, deferred_internal_id, point_is_deferred
checks) unchanged.

In `@lib/shard/src/fixtures.rs`:
- Around line 164-196: The fixture build_segment_with_deferred_1 currently
constructs a plain-only segment (Indexes::Plain) but uses a deferred_internal_id
that in production is derived from HNSW-configured vectors; update the fixture
to reflect the production path by switching the vector index to an HNSW index
(replace Indexes::Plain with the appropriate Indexes::Hnsw configuration in the
SegmentConfig/VectorDataConfig for DEFAULT_VECTOR_NAME and ensure any required
HNSW config fields are provided), or if you intentionally want an artificial
plain-only state, rename the helper and its docstring to explicitly state it’s a
synthetic test case that does not reflect HNSW-derived deferred IDs (update
function name build_segment_with_deferred_1 and its comment accordingly) so the
intent is clear.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e8c25874-9f87-4097-88c9-9d3cdf6aaff4

📥 Commits

Reviewing files that changed from the base of the PR and between b7fc4b6 and 69b2082.

📒 Files selected for processing (24)
  • lib/collection/src/collection_manager/fixtures.rs
  • lib/collection/src/collection_manager/optimizers/config_mismatch_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/merge_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/vacuum_optimizer.rs
  • lib/collection/src/config.rs
  • lib/collection/src/optimizers_builder.rs
  • lib/collection/src/shards/local_shard/mod.rs
  • lib/collection/src/shards/local_shard/snapshot.rs
  • lib/collection/src/tests/mod.rs
  • lib/collection/src/update_workers/optimization_worker.rs
  • lib/edge/src/optimize.rs
  • lib/segment/src/segment/entry.rs
  • lib/segment/src/segment/mod.rs
  • lib/segment/src/segment/segment_ops.rs
  • lib/segment/src/segment/tests.rs
  • lib/segment/src/segment_constructor/segment_builder.rs
  • lib/segment/src/segment_constructor/segment_constructor_base.rs
  • lib/shard/src/fixtures.rs
  • lib/shard/src/operations/optimization.rs
  • lib/shard/src/optimize.rs
  • lib/shard/src/optimizers/segment_optimizer.rs
  • lib/shard/src/segment_holder/mod.rs
  • lib/shard/src/segment_holder/snapshot.rs

Comment thread lib/collection/src/config.rs
Comment thread lib/collection/src/optimizers_builder.rs
Comment thread lib/segment/src/segment/mod.rs
Comment thread lib/segment/src/segment/segment_ops.rs
@qdrant qdrant deleted a comment from coderabbitai Bot Mar 9, 2026
}
}
if let Some(deferred_from) = self.deferred_internal_id {
return self.is_appendable() && self.total_point_count() > deferred_from as usize;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Was a bit confused first and thought we needed >=. But we index from zero, so it is correct as is.

@IvanPleshkov IvanPleshkov force-pushed the use-predefined-deferred-internal-id branch from e1c76ee to 13ba740 Compare March 10, 2026 00:27
@coderabbitai

coderabbitai Bot commented Mar 10, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request renames a threshold field across the optimizer and segment systems from deferred_points_threshold_bytes: Option<NonZeroUsize> to deferred_internal_id: Option<PointOffsetType>. A new method get_deferred_point_id is added to CollectionParams to compute the deferred point ID based on threshold bytes and vector configuration. The field rename and type change propagate through optimizer configuration builders, segment construction functions (create/load/build), snapshot creation, and optimizer initialization across the collection and shard modules. Multiple public method signatures are updated to accept the new parameter type.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • Deferred threshold integration #8246 — Modifies the same deferred-points infrastructure (OptimizerThresholds, Segment fields, segment constructor functions, and snapshot/optimizer call sites) across the same codebase layers.
  • refactor/opt #8065 — Updates OptimizerThresholds and related optimizer/segment initialization paths that are directly affected by this PR's field rename and type changes.
  • Configurable HNSW healing threshold #6756 — Modifies optimizer construction paths (build_optimizers and optimizer constructors) that are updated in this PR to thread the new deferred_internal_id parameter.

Suggested reviewers

  • JojiiOfficial
  • agourlay
  • timvisee
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title "Use predefined deferred ID" is concise and clearly describes the main change: switching from dynamic deferred ID calculation to using a predefined/constant deferred ID approach.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the rationale for switching to predefined deferred IDs, the implementation approach, and both benefits and drawbacks.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch use-predefined-deferred-internal-id

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/edge/src/config/shard.rs (1)

203-212: ⚠️ Potential issue | 🔴 Critical

Extract and reuse deferred-point calculation in edge shards.

EdgeOptimizersConfig exposes prevent_unoptimized, but optimizer_thresholds() hardcodes deferred_internal_id: None. Unlike the collection path, edge shards never compute a deferred point ID from the prevent_unoptimized flag and indexing threshold, so deferred-point blocking is non-functional for edge HNSW segments.

The collection-side code (lib/collection/src/optimizers_builder.rs) computes this via get_deferred_points_threshold_bytes()collection_params.get_deferred_point_id(). The edge path must do the same.

Options:

  1. Extract get_deferred_points_threshold_bytes() from lib/shard and reuse it in edge, then call collection_params.get_deferred_point_id() in optimizer_thresholds().
  2. Add a helper to EdgeOptimizersConfig mirroring get_deferred_points_threshold_bytes().
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/edge/src/config/shard.rs` around lines 203 - 212, optimizer_thresholds
currently hardcodes deferred_internal_id: None causing prevent_unoptimized on
EdgeOptimizersConfig to be ignored; compute the deferred point ID like the
collection path by reusing the deferred-threshold logic and calling
collection_params.get_deferred_point_id() (or add a mirror helper on
EdgeOptimizersConfig). Specifically, obtain the deferred bytes via
get_deferred_points_threshold_bytes(...) (extract and reuse it from lib/shard or
add equivalent to EdgeOptimizersConfig), pass that and the indexing_threshold_kb
(converted to bytes) into collection_params.get_deferred_point_id(), and set
OptimizerThresholds.deferred_internal_id to the returned Option so edge shards
honor prevent_unoptimized in optimizer_thresholds.
🧹 Nitpick comments (2)
lib/segment/src/segment/tests.rs (1)

830-847: Add a non-appendable regression case here.

This reopen block still only validates the appendable path. Since load_segment is given Some(DEFERRED_POINTS_ID) again on Line 834, it does not exercise the new self.is_appendable() gate in point_is_deferred(). Please add a case that builds or loads a non-appendable segment and asserts those points are no longer treated as deferred there.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/segment/src/segment/tests.rs` around lines 830 - 847, Add a regression
test that exercises the non-appendable path: after creating the appendable
segment test you must also create or load a non-appendable segment (so that
segment.is_appendable() returns false) and reopen it without passing
Some(DEFERRED_POINTS_ID) to load_segment; then assert that
segment.has_deferred_points() is false and segment.deferred_internal_id is None
(or not Some(DEFERRED_POINTS_ID)). Locate the relevant code near load_segment,
point_is_deferred(), is_appendable(), DEFERRED_POINTS_ID, deferred_internal_id,
and has_deferred_points() and add the new case that builds/loads a
non-appendable segment and verifies deferred points are not treated as deferred
there.
lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs (1)

137-142: Please add one non-None deferred-ID case here.

All of the updated threshold fixtures in this file still set deferred_internal_id: None, so the new appendable-segment path can regress without a failing optimizer test. A small Some(...) case in test_multi_vector_optimization would lock down the new plumbing where this PR matters most.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs`
around lines 137 - 142, Add a test case where
OptimizerThresholds.deferred_internal_id is not None to exercise the
appendable-segment path: modify the thresholds used in
test_multi_vector_optimization to include a small Some(<valid id>) value instead
of None (e.g., a simple internal id used elsewhere in tests), so the new
appendable-segment plumbing is exercised; update any setup/fixtures that
construct OptimizerThresholds to pass this Some(...) case while keeping other
threshold values the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/segment/src/segment_constructor/segment_constructor_base.rs`:
- Around line 865-869: Update the rustdoc for the public function build_segment
to include the new deferred_internal_id parameter: describe its type
(Option<PointOffsetType>), meaning (what a deferred internal id represents), and
how it affects behavior when Some(...) vs None, alongside the existing
descriptions for segments_path, config, and ready so the function signature and
docs stay in sync with the public API.

---

Outside diff comments:
In `@lib/edge/src/config/shard.rs`:
- Around line 203-212: optimizer_thresholds currently hardcodes
deferred_internal_id: None causing prevent_unoptimized on EdgeOptimizersConfig
to be ignored; compute the deferred point ID like the collection path by reusing
the deferred-threshold logic and calling
collection_params.get_deferred_point_id() (or add a mirror helper on
EdgeOptimizersConfig). Specifically, obtain the deferred bytes via
get_deferred_points_threshold_bytes(...) (extract and reuse it from lib/shard or
add equivalent to EdgeOptimizersConfig), pass that and the indexing_threshold_kb
(converted to bytes) into collection_params.get_deferred_point_id(), and set
OptimizerThresholds.deferred_internal_id to the returned Option so edge shards
honor prevent_unoptimized in optimizer_thresholds.

---

Nitpick comments:
In `@lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs`:
- Around line 137-142: Add a test case where
OptimizerThresholds.deferred_internal_id is not None to exercise the
appendable-segment path: modify the thresholds used in
test_multi_vector_optimization to include a small Some(<valid id>) value instead
of None (e.g., a simple internal id used elsewhere in tests), so the new
appendable-segment plumbing is exercised; update any setup/fixtures that
construct OptimizerThresholds to pass this Some(...) case while keeping other
threshold values the same.

In `@lib/segment/src/segment/tests.rs`:
- Around line 830-847: Add a regression test that exercises the non-appendable
path: after creating the appendable segment test you must also create or load a
non-appendable segment (so that segment.is_appendable() returns false) and
reopen it without passing Some(DEFERRED_POINTS_ID) to load_segment; then assert
that segment.has_deferred_points() is false and segment.deferred_internal_id is
None (or not Some(DEFERRED_POINTS_ID)). Locate the relevant code near
load_segment, point_is_deferred(), is_appendable(), DEFERRED_POINTS_ID,
deferred_internal_id, and has_deferred_points() and add the new case that
builds/loads a non-appendable segment and verifies deferred points are not
treated as deferred there.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53758852-03fb-4c15-ac37-19490420b6b2

📥 Commits

Reviewing files that changed from the base of the PR and between 7e15d34 and 13ba740.

📒 Files selected for processing (24)
  • lib/collection/src/collection_manager/fixtures.rs
  • lib/collection/src/collection_manager/optimizers/config_mismatch_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/indexing_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/merge_optimizer.rs
  • lib/collection/src/collection_manager/optimizers/vacuum_optimizer.rs
  • lib/collection/src/config.rs
  • lib/collection/src/optimizers_builder.rs
  • lib/collection/src/shards/local_shard/mod.rs
  • lib/collection/src/shards/local_shard/snapshot.rs
  • lib/collection/src/tests/mod.rs
  • lib/collection/src/update_workers/optimization_worker.rs
  • lib/edge/src/config/shard.rs
  • lib/segment/src/segment/entry.rs
  • lib/segment/src/segment/mod.rs
  • lib/segment/src/segment/segment_ops.rs
  • lib/segment/src/segment/tests.rs
  • lib/segment/src/segment_constructor/segment_builder.rs
  • lib/segment/src/segment_constructor/segment_constructor_base.rs
  • lib/shard/src/fixtures.rs
  • lib/shard/src/operations/optimization.rs
  • lib/shard/src/optimize.rs
  • lib/shard/src/optimizers/segment_optimizer.rs
  • lib/shard/src/segment_holder/mod.rs
  • lib/shard/src/segment_holder/snapshot.rs

Comment on lines 865 to 869
pub fn build_segment(
segments_path: &Path,
config: &SegmentConfig,
deferred_points_threshold_bytes: Option<NonZeroUsize>,
deferred_internal_id: Option<PointOffsetType>,
ready: bool,

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.

⚠️ Potential issue | 🟡 Minor

Document the new deferred_internal_id parameter in build_segment.

The rustdoc argument list above this public function still mentions only segments_path, config, and ready, so the signature change is now undocumented for callers.

📝 Proposed doc update
 /// # Arguments
 ///
 /// * `segments_path` - Path to the segments directory. Segment folder will be created in this directory
 /// * `config` - Segment configuration
+/// * `deferred_internal_id` - Deferred cutoff for appendable segments; ignored for non-appendable segments
 /// * `ready` - Whether the segment is ready after building; will save segment version
 ///
 /// To load a segment, saving the segment version is required. If `ready` is false, the version
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/segment/src/segment_constructor/segment_constructor_base.rs` around lines
865 - 869, Update the rustdoc for the public function build_segment to include
the new deferred_internal_id parameter: describe its type
(Option<PointOffsetType>), meaning (what a deferred internal id represents), and
how it affects behavior when Some(...) vs None, alongside the existing
descriptions for segments_path, config, and ready so the function signature and
docs stay in sync with the public API.

@IvanPleshkov IvanPleshkov merged commit 4b9b39c into dev Mar 10, 2026
16 checks passed
@IvanPleshkov IvanPleshkov deleted the use-predefined-deferred-internal-id branch March 10, 2026 01:17
generall pushed a commit that referenced this pull request Mar 26, 2026
* Adjust points selection for deferred points update

* adjust proxy segment implementation

* simplify

* use simpler proxy impl

* stick to Entry API

* renaming to stay closer to the original

* two passes and simpler impl.

* fmt

* fmt

* use predefined deferred internal id

* calculate deferred point id

* move deferred check to the entry

* fix after rebase

* fmt

* fix tests

* review remarks

* fix tests

* codespell fix

* are you happy clippy

---------

Co-authored-by: Arnaud Gourlay <arnaud.gourlay@gmail.com>
KShivendu pushed a commit that referenced this pull request Mar 30, 2026
* Adjust points selection for deferred points update

* adjust proxy segment implementation

* simplify

* use simpler proxy impl

* stick to Entry API

* renaming to stay closer to the original

* two passes and simpler impl.

* fmt

* fmt

* use predefined deferred internal id

* calculate deferred point id

* move deferred check to the entry

* fix after rebase

* fmt

* fix tests

* review remarks

* fix tests

* codespell fix

* are you happy clippy

---------

Co-authored-by: Arnaud Gourlay <arnaud.gourlay@gmail.com>
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.

3 participants