Repository navigation
feat: add CLI control for Milvus force-merge size and toggle - #871
jamesgao-jpg wants to merge 6 commits into
Conversation
Expose the Milvus force-merge compaction during optimize as tunable CLI options, addressing zilliztech#825 (unconditional force merge is costly and non-reproducible at 100M scale): - --force-merge-enabled/--no-force-merge-enabled: skip the force-merge stage for a sooner ready-to-search state at the cost of segments not merged to their fullest potential. - --force-merge-target-size-mb: bound the merged segment size (e.g. 1024 for bounded, reproducible segments); defaults to the current unbounded single-segment behavior for backward compatibility. Applied to all Milvus index CLI commands and to Zilliz Cloud AutoIndex, and registered in the web UI case configs. GPU index types keep skipping force merge. Disabling force merge keeps flush, normal compaction, index-wait, and refresh-load. Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: jamesgao-jpg The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
copy_fts_compatible_db_case_fields() now also carries force_merge_target_size_mb when a non-FTS vector config is routed to an FTS config class, so --force-merge-target-size-mb survives a --case-type FTSBm25Performance invocation (force_merge_enabled was already whitelisted). Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
All post-construction transformations of case configs now flow through finalize_db_case_config() in vectordb_bench/backend/db_case_config.py: - CLI-only FTS routing (previously select_cli_db_case_config in cli/cli.py) and the dataset-derived metric_type default (previously applied by mutating task.db_case_config in assembler.py) are composed in one resolver. - Both task entry points call it: cli.run() (with the CLI parameters dict) and the web UI generate_tasks() (without). - The assembler no longer mutates the config; consumers treat the finalized config as read-only. - select_cli_db_case_config remains as a thin backward-compatible wrapper for existing callers and tests. No behavior change: FTS-routing semantics, the metric_type default rule, and both entry-point results are preserved (covered by the new resolver tests plus the existing FTS-routing tests). Note: this refactor moves the FTS-compatible field whitelist (including force_merge_target_size_mb) into the resolver, so it subsumes the one-line whitelist fix carried by PR zilliztech#871's cli/cli.py. Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
All post-construction transformations of case configs now flow through finalize_db_case_config() in vectordb_bench/backend/db_case_config.py: - CLI-only FTS routing (previously select_cli_db_case_config in cli/cli.py) and the dataset-derived metric_type default (previously applied by mutating task.db_case_config in assembler.py) are composed in one resolver. - Both task entry points call it: cli.run() (with the CLI parameters dict) and the web UI generate_tasks() (without). - The assembler no longer mutates the config; consumers treat the finalized config as read-only. - select_cli_db_case_config remains as a thin backward-compatible wrapper for existing callers and tests. No behavior change: FTS-routing semantics, the metric_type default rule, and both entry-point results are preserved (covered by the new resolver tests plus the existing FTS-routing tests). Note: this refactor moves the FTS-compatible field whitelist (including force_merge_target_size_mb) into the resolver, so it subsumes the one-line whitelist fix carried by PR zilliztech#871's cli/cli.py. Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
All post-construction transformations of case configs now flow through finalize_db_case_config() in vectordb_bench/backend/db_case_config.py: - CLI-only FTS routing (previously select_cli_db_case_config in cli/cli.py) and the dataset-derived metric_type default (previously applied by mutating task.db_case_config in assembler.py) are composed in one resolver. - Both task entry points call it: cli.run() (with the CLI parameters dict) and the web UI generate_tasks() (without). - The assembler no longer mutates the config; consumers treat the finalized config as read-only. - select_cli_db_case_config remains as a thin backward-compatible wrapper for existing callers and tests. No behavior change: FTS-routing semantics, the metric_type default rule, and both entry-point results are preserved (covered by the new resolver tests plus the existing FTS-routing tests). Note: this refactor moves the FTS-compatible field whitelist (including force_merge_target_size_mb) into the resolver, so it subsumes the one-line whitelist fix carried by PR zilliztech#871's cli/cli.py. Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
jamesgao-jpg
left a comment
There was a problem hiding this comment.
Reviewed the effective diff at 84beccd. The force-merge toggle, target propagation, frontend wiring, GPU behavior, and FTS routing otherwise look correct. Focused Milvus/CLI/FTS tests passed (38 passed), and all four current CI jobs are green. Two findings remain below: regular Milvus CLI commands bypass the positive-size validator, and the public target-size wording overstates the backend's guarantees. No live Milvus/Zilliz functional probe was run.
| def _with_partition_key(db_case_config: BaseModel, parameters: dict) -> BaseModel: | ||
| return db_case_config.model_copy(update={"use_partition_key": _use_partition_key(parameters)}) | ||
| def _apply_milvus_case_defaults(db_case_config: BaseModel, parameters: dict) -> BaseModel: | ||
| return db_case_config.model_copy( |
There was a problem hiding this comment.
[P2] Validate the copied target size
model_copy(update=...) does not run Pydantic validators, so the validator in MilvusIndexConfig is bypassed on every regular Milvus command. VERIFIED: both --force-merge-target-size-mb 0 and -5 exit successfully and produce configs containing those values; PyMilvus rejects them only later in compact(), after a benchmark may already have loaded its dataset. Please use a CLI IntRange(min=1) or rebuild/revalidate the model, and add zero/negative CLI regressions.
| help=( | ||
| "Target merged segment size in MB for the force-merge compaction during " | ||
| "optimize. Defaults to the current unbounded single-segment behavior; set " | ||
| "e.g. 1024 for bounded, reproducible segments. Must be a positive integer." |
There was a problem hiding this comment.
[P2] Describe this as a nominal target, not a reproducibility guarantee
The public wording is stronger than the backend contract. PyMilvus 2.6.16 describes its maximum-size sentinel as letting the server choose the segment size automatically, while the Milvus force-merge docs describe explicit values as approximate and safety-clamped by topology/memory. The PR also notes that no live probe established a hard cap. Since segment layout affects benchmark latency, please replace unbounded single-segment / bounded, reproducible in the CLI, UI, and PR description with server-selected automatic target / requested or nominal target, plus the backend constraints.
Addresses two review findings on the force-merge CLI feature: - Regular Milvus/Zilliz CLI commands applied the flag through model_copy(update=...) in the per-command defaults helper, which skips pydantic validation, so non-positive values bypassed the field validator. Add a click callback (_validate_positive_int_or_none) on the option that rejects non-positive integers at the CLI boundary; direct config construction stays guarded by the pydantic validator. - Soften the public wording: the server-side behavior of target_size is not probed, so describe it as a requested cap whose effective behavior depends on the Milvus server instead of claiming bounded, reproducible segments. Adds CLI rejection tests for 0 and negative values on Milvus and Zilliz AutoIndex commands. Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
|
Both findings addressed in 1. CLI bypass of the positive-size validator — fixed. Confirmed the mechanism from pydantic source: Fix: a click callback 2. Public wording overstates backend guarantees — fixed. The CLI help and web-UI Verification: focused Milvus/CLI/FTS tests on the remote worktree at |
…ge-cli Resolve conflicts with PR zilliztech#876 (feat: add search level to Milvus AutoIndex): - vectordb_bench/backend/clients/milvus/config.py: combine imports — keep Field from zilliztech#876 and field_validator from zilliztech#871. - vectordb_bench/backend/clients/milvus/cli.py: MilvusAutoIndex now uses _apply_milvus_case_defaults(AutoIndexConfig(level=parameters["level"]), parameters) so both the search level and the force-merge defaults apply. - tests/test_milvus_zilliz_cli.py: keep both the force-merge tests and the search-level tests. Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
_validate_positive_int_or_none takes the required (ctx, param) callback arguments but only uses value; underscore-prefix them so the PR's lint check (ruff ARG001) passes. Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
…ge-cli Resolve conflicts with PR zilliztech#874 (feat: support configurable NQ for concurrent Milvus searches): - tests/test_milvus_zilliz_cli.py: keep the force-merge tests and the search-level tests alongside the new NQ test. Signed-off-by: jamesgao-jpg <james.gao@zilliz.com>
What
Adds CLI control of the Milvus force-merge compaction in
optimize(),addressing #825 (the unconditional force merge is costly and
non-reproducible at 100M scale).
Two new options on every Milvus CLI command and on
ZillizAutoIndex:--force-merge-enabled/--no-force-merge-enabled— skip the force-mergestage for a sooner ready-to-search state (segments are then not merged to
their fullest potential).
--force-merge-target-size-mb— request a bounded merged segment size(e.g.
1024MB); the effective cap depends on the Milvus server. Defaultkeeps the current unbounded single-segment behavior
(
((1 << 63) - 1) // (1024**2)MB).Both are also exposed in the web UI case configs for Milvus and Zilliz
Cloud (Load / Performance / FTS).
Behavior
_optimize()keeps flush → normal compaction → index-wait → refresh.When force merge is disabled, only the force-merge stage is skipped.
_force_merge()resolves the target size from the case config;Nonefalls back to today's constant, so existing runs are byte-for-byte
unchanged.
force_merge_target_size_mbis validated as a positive integer orNone.Compatibility
enabled=True,target=None) reproducecurrent behavior; no result-schema or metric changes (optimize is
pre-search preparation, not a measured result).
same knobs apply there too.
Testing
tests/test_milvus.py+tests/test_milvus_zilliz_cli.py, 31 passed / 1 integration deselected,including new tests for the configured target size, the disabled path,
config validation, and CLI flag mapping (Milvus / FTS / Zilliz Cloud).
target_sizeis honored asa hard segment-size cap by the target Milvus server still needs a small
functional probe before benchmark readiness can be declared.
Limitations / open items
Follow-up fix (84beccd)
`force_merge_target_size_mb` when a non-FTS vector config is routed to an
FTS config class, so `--force-merge-target-size-mb` survives a
`--case-type FTSBm25Performance` invocation.