2445 Commits
Author SHA1 Message Date
pymilvus-bot 1e943dc23b test: Increase PyMilvus version to 3.1.0rc92 from master for master branch (#54068)
Automated bump from pymilvus master branch to milvus master branch.
Updates tests/python_client/requirements.txt.

Signed-off-by: pymilvus-bot <[email protected]>
2026-10-10 09:53:44 +08:00
junjiejiangjjj 8cfeb14750 test: Enable hybrid rerank chain test (#54041)
issue: https://github.com/milvus-io/milvus/issues/46565

Signed-off-by: junjie.jiang <[email protected]>
2026-10-09 10:41:39 +08:00
Jamesandxiaofanluan d1cb277f0c test: wait for compactable segments in struct partial-update lifecycle (#53964)
issue: #53962

The Struct Array partial-update lifecycle test can request manual
compaction while its freshly flushed segments are still being sorted.
DataCoord then returns the valid no-plan result `-1`, and the immediate
positive-ID assertion fails before the compaction/reload checks run.

Wait for vector-index readiness, then retry only `-1` against a
monotonic 120-second deadline, passing the remaining budget to each
compact RPC. Require an accepted compaction to complete and check the
baseline both immediately afterward and after release/load. RPC
failures, unexpected IDs, timeouts, and data mismatches remain failures.

This follows the existing Struct nullable lifecycle test's approach from
#52108. The observed failure is in [PR #53941's E2E
run](https://jenkins-milvus-ci.milvus.io/job/MILVUS-CI-V2-PR-PIPELINES/job/milvus-e2e-pipeline-gcp/61102/console);
this change does not modify server compaction behavior.

Validation:
- Ruff lint and format checks, Python syntax compilation, and `git diff
--check`.
- Executed the changed statements extracted from the actual file with
simulated time/RPC responses: retry then success, persistent no-plan
timeout, invalid IDs, RPC failure, index/compaction wait failures, and
both post-compaction and post-reload data mismatches.
- Live E2E has not been run against this fix locally; CI validation is
pending. The separately requested rerun of #53941 uses its original head
and does not validate this patch.

Signed-off-by: xiaofanluan <[email protected]>
Co-authored-by: xiaofanluan <[email protected]>
2026-10-05 23:15:48 +08:00
15fcd798cf test: add continuous streaming data integrity draft (#53307) (#53868)
issue: #53307

## What changed

- Add a Phase 2 draft continuous-streaming DataIntegrity L3 workload
with concurrent MutationIngress and ImportIngress lanes, no manual
flush, and no manual compaction during streaming.
- Extend the Phase 1 framework through opt-in sequence, level, and
filtered-verification helpers while keeping existing cases unchanged.
- Add the Phase 2 design contract and include the draft workload in the
existing dedicated serial DataIntegrity job.
- After automatic coverage and final ingress verification pass, request
one post-ingress compaction and require Phase 1 lineage/serving
checkpoints, fenced full-data verification, and release/load
verification; this does not establish per-Delete L0 attribution.
- Keep fixed epochs without producer/validator coordination; missing
suffix progress fails with explicit coverage diagnostics for CI
failure-rate measurement.

## Validation

- Related DataIntegrity unit tests: 202 passed.
- Ruff, format checks, and scoped git diff checks passed.
- This review update was verified in medium mode; the new post-ingress
compaction phase has not yet been rerun against a live cluster.

Signed-off-by: MrPresent-Han <[email protected]>
Co-authored-by: MrPresent-Han <[email protected]>
Co-authored-by: GPT-5 <[email protected]>
2026-09-30 14:21:36 +08:00
zhuwenxing 734acf93f0 test: expand import and snapshot restore data integrity coverage (#53820)
## Summary

- Add Python client import coverage for nullable values, defaults, row
identity, scalar boundaries, vector encodings, dynamic fields, BM25,
geometry, and timestamps across supported file formats.
- Cover multi-file progress, empty-file commit/abort, malformed input,
and preservation of existing data after failed imports.
- Correct legacy import fixtures and assertions, including JSON field
NULLs and the distinction between non-finite and null array elements.
- Verify packed Storage V2 binlog restore after adding nullable
dense/sparse vectors. Native Storage V3 uses the snapshot export/restore
path instead.
- Strengthen the REST v2 L0 snapshot lifecycle test with per-row and
schema comparisons, NULL filtering, vector search, restore after
dropping the source snapshot and collection, reload checks, and
exported-object cleanup.

## Validation

Tested against Milvus commit `1ae8642b8b1` using `milvus-dev-cli` E2E
runners.

- REST v2 snapshot lifecycle: passed on Storage V2 (76.93s) and Storage
V3 (77.97s).
- Packed Storage V2 binlog restore: 2 passed (dense and sparse nullable
vectors).
- Nullable timestamp defaults: 3 passed (JSON, CSV, and Parquet).
- Targeted import and legacy bulk-insert runs covered nullable values,
defaults, partition keys, malformed files, vector encodings, scalar
boundaries, dynamic fields, BM25, and invalid array elements.
- Python import testcase collection after renaming: 60 cases collected.
- Changed snapshot testcase: Ruff formatting/lint, Python compilation,
and pytest collection passed. Renamed import testcase: Ruff lint, Python
compilation, and pytest collection passed.

Validation used targeted runs during development; the complete final
branch test selection has not been rerun as one suite. Snapshot
validation used serial REST runs. The default CI storage configuration
is not changed by this PR.

## Scope

Test-only changes. No production behavior changes.

---------

Signed-off-by: zhuwenxing <[email protected]>
2026-09-29 11:11:35 +08:00
marcelo-cjl ad4887b38c fix: support vector null predicates (#52442)
issue: #52239

## Summary
- Support IS NULL and IS NOT NULL predicates for ordinary vector fields.
- Read logical-row validity through vector-index HasValidData/IsRowValid
when raw field data is unavailable, remaining compatible with #50524's
Knowhere IdMap ownership.
- Rebase onto current master while preserving master's broader
search-by-PK result reordering and tests.

## Test Plan
- [x] Rebase onto master `38c4699891` with exactly two commits and no
merge commit.
- [x] `git range-diff` confirms both patches are unchanged by the
rebase.
- [x] `git diff --check origin/master...HEAD`
- [x] `go test ./internal/parser/planparserv2`
- [x] `go test -c -o /tmp/milvus-pr-52442-rebased-testcases.test
./testcases` (from `tests/go_client`)
- [x] C++ clang-format, Rust format, and Go format checks.
- [x] `bash scripts/check_segcore_error_boundaries.sh`
- [x] `ruff check` for the four changed Python test files.
- [x] `python3 -m py_compile` for the four changed Python test files.
- [x] Pre-rebase private Release+ASan integration build at `d839d36b`;
the rebased commits are patch-equivalent.
- [ ] CI on rebased head `a9e9d31ff2`.

---------

Signed-off-by: marcelo-cjl <[email protected]>
2026-09-28 22:13:36 +08:00
Bingyi Sun 215c79e293 test: wait for indexes in external table source override (#53883)
The external-table add-field source-override test can load a refreshed
snapshot before its embedding index is ready. Use a FLAT/L2 index for
this data/schema test and wait for `pending_index_rows == 0` before
loading each refreshed snapshot.

Validation: Python syntax parsing, helper-signature review, clang-format
15, and `git diff --check` pass. The Milvus/MinIO E2E case was not run
locally.

`make lint-fix` ran Go formatting, then stopped at unrelated native
type-check errors in `internal/storagev2` (missing Loon filesystem
metrics APIs).

Signed-off-by: sunby <[email protected]>
2026-09-28 17:05:36 +08:00
dependabot[bot] ca8c70ba6f build(deps): bump deepdiff from 8.6.1 to 8.6.2 in /tests/python_client (#53674)
Bumps [deepdiff](https://github.com/qlustered/deepdiff) from 8.6.1 to
8.6.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/qlustered/deepdiff/releases">deepdiff's
releases</a>.</em></p>
<blockquote>
<p>8.6.2 - Fix (CVE-2025-58367)</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/qlustered/deepdiff/commit/0d07ec21d12b46ef4e489383b363eadc22d990fb"><code>0d07ec2</code></a>
Merge commit from fork</li>
<li><a
href="https://github.com/qlustered/deepdiff/commit/791f5aac51b2da7f90375ab204256ef6a6b40206"><code>791f5aa</code></a>
updating CVE number</li>
<li><a
href="https://github.com/qlustered/deepdiff/commit/a6aafea3ba5498aab6f9c77047c54949e7e968ae"><code>a6aafea</code></a>
updating docs</li>
<li><a
href="https://github.com/qlustered/deepdiff/commit/a0950abbe6263298bc4bdbc3ebb57edc1af079b4"><code>a0950ab</code></a>
Bump version: 8.6.1 → 8.6.2</li>
<li><a
href="https://github.com/qlustered/deepdiff/commit/887128abe5e510341cc4a82f6914a82d5ff1b6a7"><code>887128a</code></a>
Fix (CVE-2025-58367)</li>
<li>See full diff in <a
href="https://github.com/qlustered/deepdiff/compare/8.6.1...8.6.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=deepdiff&package-manager=pip&previous-version=8.6.1&new-version=8.6.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/milvus-io/milvus/network/alerts).

</details>

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 15:53:35 +08:00
zhuwenxing dde236e5f9 test: decouple external add-field type matrix from E2E (#53777)
## What changed
- Move the proto and PyMilvus DataType matrix check into a separate test
so enum changes cannot prevent the external add-field E2E from reaching
Milvus.
- Classify the new proto-only UUID type and check that DataType
categories are disjoint and cover both proto and SDK enums.
- Verify each added field's type, external field mapping, and nullable
setting before refresh and index creation.

## Verification
- Ruff check and format check passed.
- The standalone DataType matrix test passed with PyMilvus 3.1.0rc89.
- The targeted external add-field E2E passed against Milvus master
commit cb7e0edacd with PyMilvus 3.1.0rc89 and StorageV3 enabled.

Signed-off-by: zhuwenxing <[email protected]>
2026-09-28 11:23:35 +08:00
Zhen YeandClaude Opus 4.6 06ba9d2298 enhance: refactor asynchronous WAL recovery and summary persistence (#53595)
issue: #40451

- WAL recovery: replace the legacy flusher with asynchronous
VChannel/Segment persistence, retained-message completion, and
owner-fenced snapshots before a single global checkpoint. Publish
schemas before VChannel base metadata and use the base checkpoint to
filter incomplete schema publication during recovery; delegate task
concurrency to the shared NodeScheduler. Preserve transaction state
through RecoveryBarrier with one scanner, reconcile legacy segment state
during migration, and mark the WAL unavailable if background checkpoint
persistence terminates unexpectedly.
- WALSummary and idempotency: keep summaries and keyed-write
deduplication always available; persist chunks/manifests independently,
restore deduplication windows, provide bounded Delete reads, and manage
WAL coverage, retention, and bootstrap replay boundaries. Seal on staged
size or WAL tail pressure, report terminal Summary failures to the WAL
owner, and perform physical GC outside reader/publication locks. Remove
obsolete idempotency retention settings.
- Data persistence: materialize missing function outputs before growing
persistence, publish growing L1 data to DataCoord before completing
Inserts, and track commits with DataVersion. Retire empty L1 segments
explicitly through DataCoord without generating a manifest or publishing
DataView; install the SN tombstone without a DataVersion only after
retirement is confirmed, allowing ManualFlush to complete. Materialize
retained Deletes into L0 using size, age, recovery pressure, or explicit
WAL boundaries; retain the Summary-based materializer for future
QueryView integration. Report complete recovery positions and poison
work after WAL ownership loss.
- Lifecycle completion: synchronize ManualFlush, FlushAll, and snapshot
creation through consuming-side L1/L0 completion and broadcast
acknowledgements. Let Broadcaster add CChannel centrally while callers
supply the required collection VChannels. Rename the import ID-range
update message to UpdateImport while preserving its payload and
behavior. Preserve coordinator-owned Import visibility and legacy
CommitImport completion during upgrades, and flush surviving partitions
on DropPartition. Fence dropped objects in memory, publish durable
tombstones only after asynchronous dependencies finish, and retire
metadata and historical schemas through checkpoint- and Summary-gated
GC. Cancel stale QueryCoord orphan-segment cleanup when a loaded
collection reassigns the node to a replica.

---------

Signed-off-by: chyezh <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-09-26 01:09:36 +08:00
Feilong HouandEric Hou 38c4699891 fix: Repair Spark backfill toolbox build dependencies (#53770)
The Spark backfill toolbox init container fails when the pinned Avro
recipe downloads the unavailable 1.12.1 archive from Apache's current
mirror. The source build also needs additional Rust prerequisites, and
the clean Spark runtime cannot load JNI unless libaio is included in the
artifacts.

This change:
- Builds Avro C++ 1.12.2 from its official archive with a pinned SHA256.
Reuses only the existing pinned recipe's build instructions, exports a
distinct local recipe, and forces the storage dependency graph to that
exact revision.
- Installs pkg-config, libssl-dev, libclang-dev, and protobuf-compiler
for the Rust bridge.
- Bundles libaio.so.1 alongside the Conan runtime libraries.
- Records the Avro recipe reference in build evidence and documents the
prerequisites.

issue: #52130

### Validation

- Shell syntax, embedded Python syntax, and git diff --check pass.
- Verified Conan source download/checksum and repeatable recipe export;
the resolved storage graph uses Avro 1.12.2.
- Completed a Kubernetes toolbox build with Spark 4.0.1 / Java 21 on
Linux AMD64, using spark-milvus commit
37d2217fe64c352048db71e4c928068b8cdaada6.
- Verified artifact hashes, all JNI shared-library dependencies
(including Avro 1.12.2), Python imports, BackfillApp class loading, and
JNI loading in the Spark JVM.
- A local Spark/Parquet round trip returned 100 rows with ID sum 4950.

The build completed using caches preserved across retries. The final
libaio packaging step was tested separately against the built artifacts,
followed by the clean-runtime checks above; a fresh full build was not
repeated after that packaging-only change. Milvus/S3 backfill was not
exercised. Assembly emitted warnings about skipped Java 21/22
multi-release entries; assembly and the listed runtime checks passed.

Signed-off-by: Eric Hou <[email protected]>
Co-authored-by: Eric Hou <[email protected]>
2026-09-24 11:05:33 +08:00
zhuwenxing 2dfd51b8d7 test: add external table reload checker (#53784)
issue: #53778

## Changes
- Add `ExternalTableLoadChecker` to repeatedly release and load a fixed
external collection, then verify its row count without refreshing the
source.
- Register `external_table_load` as a chaos operation.

## Verification
- `ruff check tests/python_client/chaos/checker.py`
- Python 3.12 compile and import checks for the checker and operation
enum.

Signed-off-by: zhuwenxing <[email protected]>
2026-09-23 19:17:33 +08:00
junjiejiangjjj 8c317d2ce1 test: fix topology-dependent FunctionChain tests (#53743)
Remove three Python E2E cases that assume L1 rerank runs on globally
merged candidates. L1 pagination and GroupBy candidate selection depend
  on how segments are distributed across workers.

  Add QueryNode regression tests for worker-local L1 pagination and
  GroupBy candidate selection before L1 rerank.

  issue: #53737

Signed-off-by: junjie.jiang <[email protected]>
2026-09-23 10:07:34 +08:00
5ddad821c9 test: add V2 projection data-integrity case (#53307) (#53631)
issue: #53307

## Summary
- Add an independent L3 Storage V2 vector-projection workload: persist
deterministic PK-bound narrow and wide vectors, verify the full dataset,
drop the binary-vector field with schema bump disabled, and require an
adopted MixCompaction rewrite before checking every retained cell.
- Reuse the existing exclusive configuration fixture, lineage/serving
checkpoints, full-retrieve fences, canonical byte comparisons, and
cleanup; leave ordinary workloads, server code, and shared CI runners
unchanged.
- Pin both PyMilvus requirement entries to `3.1.0rc89`, whose published
wheel includes `list_segments`, `list_compaction_tasks`,
`list_loaded_segments`, and the required public enums.
- Check dropped-index absence by field ownership (`list_indexes(...,
field_name=dropped_field)`), with regression coverage for same-name and
custom-name stale indexes and an unrelated index sharing the dropped
field's name.
- Cover deterministic payloads, row-preserving vector corruption,
verification ordering, and configuration restoration across success and
failure paths in the explicitly collected helper unit-test module;
unequal field widths are adversarial inputs, not proof of a particular
native reader refill.

## Verification
- Isolated published `pymilvus==3.1.0rc89`, not the local editable SDK:
163 helper/oracle unit tests and 50 configuration/reporting/runner tests
passed.
- All 8 dedicated L3 workloads collect successfully; server-backed E2E
was not rerun for this medium-mode update.
- Negative control: restoring the old name-only assertion in memory
causes 6 expected unit-test failures.
- Scoped Ruff checks, Ruff format checks, and `git diff --check` passed.

Signed-off-by: MrPresent-Han <[email protected]>
Co-authored-by: MrPresent-Han <[email protected]>
Co-authored-by: Codex <[email protected]>
2026-09-21 20:21:36 +08:00
zhuwenxing 3cd8755ee2 test: update snapshot restore alias conflict expectation (#53682)
After #53547, restoring a snapshot to an existing alias is rejected
during restore admission with code 1100 and an "already exists in
database" message. The alias-conflict test still expected the later
CreateCollection error.

Update `test_restore_target_name_equals_existing_alias_fails` to match
the admission error, explicitly assert code 1100, and correct its path
documentation. Preserve the snapshot check and subsequent restore to a
fresh target.

Validation:
- Reproduced the original assertion failure against Milvus commit
`09d3618c818` with PyMilvus `3.1.0rc83`.
- Targeted E2E on the same instance: 3 passed (alias target, existing
collection target, and failed-restore resource cleanup).
- Ruff lint/format, Python compilation, and `git diff --check` passed.

Signed-off-by: zhuwenxing <[email protected]>
2026-09-21 19:01:36 +08:00
eb7e63ebcf test: add Spark-Milvus backfill E2E coverage (#52155)
## What this PR does

Adds dedicated Spark-Milvus Backfill E2E coverage and the runtime
tooling needed to execute it against real Storage V3 and Storage V2
segments.

- Adds opt-in, serial-only pytest coverage for Backfill modes, null
semantics, snapshot boundaries, schema fencing, manifest versions,
compaction, and invalid inputs.
- Adds Kubernetes Job and reusable Spark Toolbox runners with credential
redaction, RBAC validation, evidence collection, and cleanup.
- Builds `zilliztech/spark-milvus` in the toolbox pod and runs Spark
locally through the validated wrapper and native-library runtime.
- Separates Storage V3 and Storage V2 execution through pytest markers
and independent Milvus deployments.
- Uses the dedicated `SparkBackfill` tag, so the normal `L0 L1 L2
ClusterOnly` nightly selection does not include this suite.
- Documents manual toolbox deployment and dedicated-suite execution.

Fixes #52130

## Pipeline isolation

The suite is ignored during ordinary Python client collection unless
`--run-spark-backfill` is explicitly provided. The dedicated pipeline
additionally selects `--tags SparkBackfill`; existing PR, nightly, and
weekly tag sets therefore select zero Spark Backfill cases.

V3 and V2 remain separate marker selections because they require
different Milvus storage configurations and must run serially without
pytest-xdist.

## Test plan

- [x] Dedicated Spark Backfill pipeline completed successfully.
- [x] Storage V3: 30 cases collected; 26 passed and 4 strict xfails
track the known schema-fencing gap in #51318.
- [x] Dedicated `SparkBackfill` tag collects 31 cases: 30 V3 and 1 V2.
- [x] Normal nightly tags `L0 L1 L2 ClusterOnly` select 0 Spark Backfill
cases, even when the opt-in collection gate is opened.
- [x] `ruff check tests/python_client/common/common_type.py
tests/python_client/conftest.py tests/python_client/spark_backfill`
- [x] `git diff upstream/master...HEAD --check`

## Operational notes

- Spark execution uses `spark-submit --master local[2]` in one toolbox
pod.
- The toolbox is discovered by `app=spark-milvus-toolbox` and is managed
outside pytest.
- V3 and V2 must use independent Milvus deployments.

---------

Signed-off-by: Eric Hou <[email protected]>
Co-authored-by: Eric Hou <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-09-21 15:41:36 +08:00
zhuwenxing fda7d59e39 test: fix missing primary key expectation for AutoID partition upsert (#53611)
AutoID collections support Upsert, but callers must supply a primary key
for lookup. `test_partition_upsert_with_auto_id` omits that key, so
PyMilvus rejects the request before sending an Upsert RPC. The
expectation restored in #53158 incorrectly matched the server-side
error.

Expect SDK error code `1` and the missing-primary-key message,
preserving the existing negative test.

Validation:
- Ruff lint/format, Python compilation, and pytest collection passed.
- Targeted E2E passed with PyMilvus `3.1.0rc83` and Milvus commit
`09d3618c818`: the corrected missing-key test passed; two diagnostic
cases confirmed Int64/VarChar AutoID Upsert preserves existing IDs,
allocates IDs for missing lookup keys, and supports partial updates.
Diagnostic cases are not included in this PR.

Signed-off-by: zhuwenxing <[email protected]>
2026-09-21 14:29:36 +08:00
zhikunyao ed7c7ff0d6 fix: initialize Docker bind mount ownership (#53549)
issue: #51221
related: #49775

## What this changes

- Add a one-shot volume initializer to the CPU and GPU standalone
Compose files.
- Resolve the numeric UID/GID from the same Milvus image and migrate
`/var/lib/milvus` before the non-root server starts.
- Write a UID/GID-specific marker only after a successful recursive
migration, so large existing volumes are not scanned on every start and
interrupted migrations remain retryable.
- Prepare the bind mount in `standalone_embed.sh`, including when a
previously created container is stopped after a permission failure.
- Teach Docker deployment health checks to exclude one-shot services.
- Document the ownership and restore contract.

The initializer has no network, uses a read-only root filesystem, and
mounts only the Milvus data directory. The Milvus server continues to
run as `milvus:milvus`.

This fixes both fresh rootful-Docker installations, where Docker creates
the bind source as `root:root`, and upgrades from root-running images
with root-owned data or cache files. It avoids the ineffective
image-layer `chmod` approach from #51226 because a bind mount hides
image-layer ownership and mode.

## Compatibility

- The recursive ownership migration runs once for each image UID/GID.
- A failed or interrupted migration does not create the marker and is
retried.
- Root-running older images can still read and write the migrated files
on rollback.
- After restoring files owned by another user, remove
`.milvus-volume-owner-*` before starting Milvus to rerun migration.

## Validation

- `git diff --check`
- `bash -n scripts/standalone_embed.sh
tests/python_client/deploy/utils.sh
tests/python_client/deploy/check_healthy.sh`
- `docker compose -f deployments/docker/standalone/docker-compose.yml
config --quiet`
- `docker compose -f
deployments/docker/gpu/standalone/docker-compose.yml config --quiet`
- Verified the deployment health-count expressions return the three
long-running services.
- Simulated the existing release bump regexes and confirmed both
init/server image references and `MILVUS_IMAGE` are updated.

Container-level startup was not run locally because the Docker daemon is
unavailable; the Linux deployment CI must exercise the fresh-install and
root-owned upgrade paths.

---------

Signed-off-by: Zhikun Yao <[email protected]>
2026-09-20 10:41:32 +08:00
Chun HanandMrPresent-Han 33d4d826f3 enhance: expose compaction lifecycle observability (#53308)
issue: #53307

Protocol dependency: milvus-io/milvus-proto#668 (merged).

## What changed

- Reuse the existing RPCs to expose retained segment lifecycle metadata
and direct compaction sources; retain the existing sealed/loaded segment
reporting contract without adding Growing segments to
GetQuerySegmentInfo.
- Expose collection-scoped retained compaction tasks, with complete task
identity, public type/state enums, source/target sets, and failure
reasons, while preserving manual job-ID lookup.
- Preserve failure reasons across worker cleanup without changing task
cleanup or metadata GC semantics.
- Add seven **L3-only** data-integrity workloads: fixed V2/V3 Insert,
fixed V2/V3 Import, V2 add/drop-field evolution through MixCompaction
with schema bump disabled, V3 schema evolution, and V2-to-V3-to-V2
storage transitions.
- Validate deterministic full-row/full-field data against an independent
oracle, fenced persistence/serving checkpoints, and actual lineage
transitions, allowing either automatic or manual compaction.
- Provide `tests/scripts/ci_compaction_integrity.sh` as a dedicated,
single-invocation, serial entry; leave the shared
`tests/scripts/ci_e2e.sh` unchanged from the PR base.
- Retain successful audit records only for the dedicated workload
marker, preserving ordinary tests' JSON, HTML, and worker-report
formats.

## Compatibility and execution boundary

- Omitted segment states preserve the existing persistent-state
behavior; task and dropped-segment history are subject to existing
retention/GC, not a fixed response-size or record-count limit.
- These workloads are not added to per-PR E2E or ordinary Nightly
pipelines.
- The dedicated job must own the deployment/config namespace
exclusively, install a compatible PyMilvus build, and provide connection
options plus a unique `CI_LOG_PATH`.
- The entry enforces `-n 0`, the L3 workload marker, and explicit
opt-in, while forwarding connection options, filters, and pytest's exit
status.
- Insert/Import workloads use three rounds by default; insert load units
are batches, not a promise of a physical segment count, and dense vector
dimensions are 16.

## Observability semantics and follow-ups

- **Segment listing is not an atomic snapshot.** The Proxy first calls
`GetSegmentsByStates`, then `GetSegmentInfo`; returned details are
filtered again against the requested states (or the legacy default:
Flushing, Flushed, Sealed). A segment whose fetched state no longer
matches is omitted, and a segment becoming eligible after ID enumeration
may be absent. Consumers must not treat a single response as a
consistent lifecycle snapshot.
- **Existing lookup errors are preserved.** `IncludeUnHealthy` is
enabled only when Dropped is explicitly requested. In the default
healthy-only path, a selected source becoming Dropped before detail
lookup still returns `SegmentNotFound`, as before; that error is not
converted into a successful partial list. A history segment being GC'd
between the two RPCs can also fail the lookup.
- **Aggregate `Completed` means no running tasks, not universal
success.** This preserves the existing aggregation semantics:
all-failed/all-timeout retained tasks, or an empty task set, can report
Completed. Inspect each task's `state` and `failure_reason` for its
outcome; proving an actual data transition additionally requires
lineage, active-set adoption, and serving-set evidence. This PR does not
redefine task retry or cleanup behavior.
- **History capacity is a follow-up.** Dropped-segment queries enumerate
the retained history and fetch its details, while collection-scoped
compaction queries return retained tasks across triggers. Neither path
currently provides pagination or a fixed count/size cap, so scan, copy,
and response costs grow with the retained record count despite
retention/GC. Pagination and capacity controls for both paths are
deferred; they must preserve the ability to enumerate complete retained
history rather than silently truncate lineage or task evidence.

## Previously recorded verification

The results below are earlier recorded runs, including the original
six-workload suite before the V2 DDL workload was added; the
documentation/review-response update did not rerun tests.

- 22 server-independent CI boundary tests passed, covering
ordinary/special audit output, worker report merging, scoped execution
guards, and the dedicated entry's argument/exit-code behavior.
- 20 differential checks against the PR-base logging plugin produced
byte-identical ordinary worker JSON, report JSON, and HTML across all
five result outcomes.
- 14 existing DataIntegrity and etcd-helper unit tests passed.
- The three original L1 DataIntegrity parameter instances passed with
`-n 2 --dist loadgroup` in 59.39 seconds; their merged successful JSON
records retained the original metadata-only shape.
- Ruff lint/format, shell syntax, and scoped PR-base whitespace checks
passed; the shared runner is byte-identical to the PR base.
- The new dedicated entry ran all six workloads serially against the
existing local cluster: **6 passed in 3373.52 seconds**, with three
rounds for each Insert/Import workload and the storage-transition
workload, plus five DDL checkpoints over 25,000 rows.
- All six successful reports retained their audit evidence (403
structured events), including every expected round/DDL checkpoint, full
segment/task snapshots, serving frontiers, and matching
expected/retrieved PK digests.

## Design and job entry

- [Data-integrity design
contract](https://github.com/MrPresent-Han/milvus/blob/codex/compaction-e2e-observability/docs/design-docs/design_docs/compaction_data_integrity_e2e_design.md)
- [Dedicated serial
entry](https://github.com/MrPresent-Han/milvus/blob/codex/compaction-e2e-observability/tests/scripts/ci_compaction_integrity.sh)

Signed-off-by: MrPresent-Han <[email protected]>
Co-authored-by: MrPresent-Han <[email protected]>
2026-09-18 15:13:36 +08:00
junjiejiangjjj bf02c32d35 enhance: chain supports dynamic field (#53493)
https://github.com/milvus-io/milvus/issues/46565

Signed-off-by: junjie.jiang <[email protected]>
2026-09-18 14:39:37 +08:00
wei liu f887463b25 fix: Serialize snapshot restores targeting the same collection (#53547)
issue: #53513

Concurrent internal restores can both pass Phase 0 and reuse the same
target
collection through idempotent CreateCollection, producing duplicate copy
jobs.

Share the existing per-target external restore lock with internal
restores and
validate target absence under the Phase 0 DDL lock before pinning the
source.
Hold the target lock through collection creation, job submission and
synchronous
cleanup, while releasing the DDL lock before nested DDL calls. Normalize
an empty
database name to `default` so both spellings share the same lock.

An existing restore target is now rejected during admission, before the
source
snapshot is pinned. Its error message changes from "create duplicate
collection
with different parameters" to "target collection ... already exists in
database
..."; the error code remains 1100. Update both existing Python rejection
tests
to check the new message and explicitly assert the code, since the
shared error
checker only validates message substrings.

Add regression tests for internal/external contention, independent
targets,
admission failures, rollback retry and registered-broadcast ACK
timeouts. Add an
L2 Python e2e that starts two restores to the same target and requires
one
success, one already-exists error, one completed job and matching
restored data.

### Validation

- Targeted restore Go tests passed with `-tags dynamic,test`,
`-gcflags="all=-N -l"` and `-race`. Changed restore functions have 100%
statement
  coverage; this is not whole-package coverage.
-
`TestMilvusClientSnapshotConcurrency::test_concurrent_restore_same_target`
passed with `--count=3`: 3 passed in 33.08s. Each run checks all 256
primary keys,
  scalar fields and vectors. Test collections were cleaned up.
- After rebuilding Go on base `f5475123f2`, the two existing-target
rejection
  tests (`test_snapshot_restore_collection_exist` and
`test_snapshot_restore_failure_no_resource_leak`) and the
concurrent-target
regression passed together: 3 passed in 35.42s. The existing-target
tests
explicitly verify error code 1100 as well as the admission error
message.
  Independent cleanup verification found no remaining collections.
- Ruff lint/format checks and `git diff --check` passed.
- Live testing used this worktree's binary in isolated standalone mode
with
embedded etcd, RocksMQ and local storage. Mixed internal/external
contention
  is unit-tested; the original cluster/S3 topology was not exercised.

The lock serializes restore requests within the snapshot manager. It
does not
add fencing against unrelated CreateCollection/DropCollection operations
or
coordinator failover.

---------

Signed-off-by: Wei Liu <[email protected]>
2026-09-18 09:21:35 +08:00
wei liu 2cbfc53b81 feat: preserve primary keys in full AutoID upsert (#53158)
issue: #53157

## What changed

- Full AutoID Upsert shares preparation with Partial Upsert and
  classifies supplied lookup keys with one PK-only Strong Retrieve using
  standard Query visibility and TTL handling. It adds no Full-specific
  fixed MVCC or guarantee-timestamp mechanism.
- Existing rows preserve their primary keys, so their delete and insert
  route to the same VChannel transaction.
- Missing rows always receive allocator-owned IDs. No Delete is emitted
  for a missing lookup key. No insert-on-not-found configuration or new
  not-found error code is introduced.
- Partial Upsert retains master's Strong Query and actual per-channel
CAS
snapshots. AutoID Partial preserves existing keys and allocates new IDs
  for missing rows with complete insert fields, retaining them across
  internal CAS retries. Full Upsert does not collect or bind CAS proofs.
- The Go row client retains AutoID lookup keys for Upsert, and the
response
  returns final IDs in request order.
- Shared `prepareUpsert` performs Retrieve, PK classification and
  missing AutoID allocation. Full Upsert returns before Partial-only
  field merging and CAS processing, preserving the complete request
  payload and row order.
- Missing business PKs are allocated during preparation; internal RowIDs
  remain initialized in `insertPreExecute`. One PK helper validates keys
and rewrites only missing positions in a copy of the PK field. Response
  IDs also drive Insert routing, without separate routing state.

## Compatibility and scope

This preserves the historical create-on-not-found outcome while fixing
identity replacement for existing rows. Full AutoID Upsert now depends
on
the standard Query/load-state path. Retrying a missing lookup key after
an
ambiguous outcome may create another generated identity; this is not an
idempotency mechanism or a cross-VChannel request transaction.

No RootCoord behavior, collection property, public protobuf, WAL format,
or replication protocol is added. Deployments must use homogeneous Proxy
versions during the semantic transition, as described in the design.

Design: [Preserve Primary Keys in Full AutoID
Upsert](https://github.com/weiliu1031/milvus/blob/refine-autoid-upsert/docs/design-docs/design_docs/20260803-autoid-upsert-primary-key-preservation.md)

## Validation

Latest publication (`24e2ae1974`, 2026-09-15):

- Rebased onto master `fee4ed4316` without conflicts. Both branch-only
commits are equivalent in range-diff; the aggregate patch ID and changed
  paths are unchanged.
- Post-rebase `SKIP_3RDPARTY=1 make milvus` passed in the designated
  worktree, rebuilding Milvus C++ and Go code while reusing third-party
  dependencies already built there by the preceding full `make milvus`.
- 82 targeted Proxy tests passed after rebase with `-tags dynamic,test`,
  `-gcflags='all=-N -l'`, `-count=1` and local macOS linker options.
  Coverage includes shared Full/Partial preparation, PK validation,
  missing-ID allocation, CAS retries, routing and Query visibility.
  These mocked tests required no shared-service reset.
- Full Proxy golangci-lint 2.11.3 passed with 0 issues. `gofmt` checks
and
  `git diff --check` passed; the worktree was clean before publication.
- At the user's direction, full-suite tests, the 99% coverage gate and
  repository-wide verifiers were skipped. No live E2E or fresh coverage
  result is claimed for this HEAD. New-head CI remains required.

Previous publication (`e6000d8b67`, 2026-09-15; historical):

- Rebased onto master `423b8e483d`. Resolved query/upsert conflicts by
preserving master's Partial Strong-read/CAS protocol and AutoID
fallback,
  while keeping Full classification on its ordinary visibility path.
- The mmap counter collision fix is already upstream and was dropped as
a duplicate. No new feature paths were added; the design and tests were
  aligned with the authorized Partial behavior.
- `make build-cpp` passed in the designated worktree.
- 56 targeted Proxy tests passed with `-tags dynamic,test`,
  `-gcflags='all=-N -l'`, `-count=1` and local macOS linker options,
covering Full/Partial AutoID, CAS retries, snapshot binding, routing and
  Query visibility. These mocked tests required no shared-service reset.
- Full Proxy golangci-lint 2.11.3 passed with 0 issues; formatter and
  `git diff --check` passed.
- Full-suite tests, coverage and live E2E were not rerun. New-head CI
remains required; earlier-head green results do not validate this head.

Previous publication (`ffa01a4d1d`, 2026-09-10; historical):

- Rebased onto master `13cd0e99f9` without conflicts. All seven commits
are equivalent in range-diff; aggregate patch ID and paths are
unchanged.
- Full merr package tests and the Proxy paramtable subtest passed.
- Formatter and git diff checks passed.
- Post-rebase Proxy tests and full Proxy lint were attempted but blocked
  by stale local C++ headers missing master's new initcore C interfaces.
  Rebuilding this worktree with `make build-cpp` then failed at
  `LoadAdmissionController.cpp:87,91,131`: returning `future` calls the
  deleted `folly::coro::Future<LoadAdmissionLease>` copy constructor.
  That source file is identical to master and is not changed by this PR.
  No unrelated C++ repair was included. Post-rebase integration package
  compilation was not reached; Proxy validation remains incomplete.
- Mock tests did not require starting or resetting shared services.
- Full-suite tests, coverage and live E2E were not run for this
publication.
  New-head CI must still validate the complete branch.

Historical validation, not fresh evidence for this HEAD:

- Before rebase, the switch-removal candidate passed 19 targeted Proxy
  tests, full merr tests, the Proxy paramtable subtest, full Proxy lint
2.11.3, formatter checks and HelloMilvus integration package
compilation.
- Earlier revisions ran targeted CAS/retry/routing tests, Go SDK
  `TestUpsertAutoIDRows` and `TestUpsertAutoIDMixedBatch`, REST v2
  `test_upsert_vector_pk_auto_id` for Int64/VarChar, builds and Python
  syntax checks.
- The prior QueryNode `TestFillOutputFieldsOrdered_NoCMemoryLeak` CI
  failure was not repaired by this change; its implementation and
thresholds remain untouched. New-head CI is needed to establish status.

---------

Signed-off-by: Wei Liu <[email protected]>
2026-09-17 11:01:35 +08:00
junjiejiangjjj c24624e280 feat: HybridSearch support l0/l1/l2 rerank chains (#52884)
https://github.com/milvus-io/milvus/issues/46565
doc:
docs/design-docs/design_docs/20260818-hybrid-search-function-chain.md

Signed-off-by: junjie.jiang <[email protected]>
2026-09-15 15:49:30 +08:00
wei liu 10beedd721 enhance: Avoid TimeTick waits in partial update reads (#53336)
issue: #53335

Serial partial updates currently use a fixed MVCC read that can wait for
the next TimeTick. Use a fresh Strong query for the first attempt and
each safe CAS retry, then bind QueryNode's actual per-channel snapshots
to the CAS proof. StreamingNode rejects proofs older than its current
WAL tracking lifecycle, including a reopened WAL with the same channel
term.

Require a nonzero snapshot for every write channel before merging or
writing. Existing conflict, retention, and incomplete-transaction guards
remain active. All participating Proxy, QueryNode, and StreamingNode
instances must implement this protocol; mixed-version rollout is outside
this change.

Design: [Partial Update Optimistic
Lock](docs/design-docs/design_docs/20260601-partial-update-optimistic-lock-design.md).

AutoID partial updates now preserve existing PKs and allocate a new
AutoID for each missing row with complete insert fields. Each attempt
resolves all possible destination terms before one Strong query, then
selects the final write channels from its actual snapshots after
allocation. Freshly allocated IDs are not queried again. Destination IDs
remain stable within internal CAS retries; original fields stay
immutable and returned IDs retain input order. Missing source PKs
generate no Delete, and successful results return destination IDs in
input order. Cross-vchannel transactions remain independent, and this
does not add cross-request deduplication.

Missing-PK insert failures name the PK and missing required field,
without a row index, while retaining the non-retriable parameter error.
The design and Python e2e error assertions are synchronized.

Earlier validation (before the latest refactor):
- Targeted Proxy query/upsert regression tests, QueryNode
executed-snapshot tests, and StreamingNode lifecycle-history tests
passed locally.
- Earlier Strong-read Release build and serial A/B/B/A benchmark (before
the AutoID fallback update): batch 16 / 128 / 512 mean latency changed
from 199.771 / 199.774 / 199.731 ms to 1.293 / 1.838 / 4.658 ms. Each
version/batch has 300 measured requests; 2,160 total requests including
warmups succeeded, with full scalar-field and sampled
vector-preservation checks.
- Benchmark scope: Apple M5, Standalone, RocksMQ, 2 shards, 8,192 rows,
128-dimensional vectors, scalar REPLACE. Both versions used identical
local native libraries, including a separate local compiler
compatibility adjustment. This is not a distributed or concurrent-load
benchmark.
- `git diff --check` passed. Distributed WAL failover/fault injection
and the full repository test suite were not run.

- AutoID fallback: targeted Proxy tests cover Int64/VarChar IDs,
absent/existing rows, real message packing with mocked mixed-channel
success/CAS failure, stable retry IDs, result order, and missing-field
error classification. QueryNode snapshot and StreamingNode lifecycle
tests were also rerun on this branch.
- Python error assertions were updated and syntax-checked; Python e2e
and distributed fault injection were not run for this update.

Latest refactor validation:

- Updated the design and tests for one query per attempt, stable
AutoIDs, and shared PK handling. Added RPC-to-CAS packing cases for
distinct channel snapshots, empty destinations, missing/zero snapshots,
and fixed namespace routing.
- Formatting and diff checks passed. No tests or local builds were run
for this update, as requested; earlier test and benchmark results do not
validate this refactor.

---------

Signed-off-by: Wei Liu <[email protected]>
2026-09-14 16:29:36 +08:00
lorenzozane 2ad9913d41 fix: align XGBoost corrupted model assertion with segcore diagnostics (#53401)
issue: #53398

## Summary

The corrupted-XGBoost-UBJ e2e test asserted that `segcoreCode=` never
appears in the error message. That contradicts the current error
contract: `merr` keeps the original segcore code in the diagnostic, and
the code travels the wire directly. Update the assertion to require
`segcoreCode=2042` instead of rejecting the decoration.

The test still verifies the explicit parse failure, wire code 2042,
input-error classification, non-retriable behavior, and successful
reranking with the valid model.

## Test Plan

- `python3 -m py_compile
tests/python_client/milvus_client/test_milvus_client_function_chain.py`
- `ruff check --config tests/ruff.toml
tests/python_client/milvus_client/test_milvus_client_function_chain.py`
- CI on this PR: `ci-v2/e2e-default`, `ci-v2/go-sdk`, and `Ruff (changed
files only)` pass.

Fixes #53398

Signed-off-by: lorenzozanee <[email protected]>
2026-09-14 11:11:34 +08:00
82bcce8867 enhance: classify segcore errors across producers and enforce classification end-to-end (#50768)
## What

Consume the producer-owned error classification at the segcore boundary
and make the whole C++→Go classification drift-proof, so a segcore error
is classified as **input** (caller's fault, non-retriable),
**transient** (retriable) or **permanent** (non-retriable) instead of
flattening to `UnexpectedError(2001)` or carrying the wrong retry
default.

Design + tracking: #50903.

## Changes

- **T1** — register the storage fallback pair in
`pkg/util/merr/segcore.go`: `StorageError(2044)` non-retriable,
`StorageTransientError(2045)` retriable.
- **T2** — `KnowhereStatusToErrorCode` → a switch with **no `default` +
`-Werror=switch`** over the full `knowhere::Status`; add build-path
variant `KnowhereBuildStatusToErrorCode` so a build-time OOM / disk read
stays **retriable** instead of collapsing into a permanent
`IndexBuildError`.
- **T3/T4** — `ArrowStatusToErrorCode` delegates to the producer's
`milvus_storage::ToSegcoreError` (retires milvus's duplicate mapper);
audited and routed **25 storage arrow-status sites** that were
collapsing to `2001` through the single mapper (extracted to
`storage/StatusToErrorCode.h`), always preserving the arrow sub-code in
the message.
- **T5** — unmapped-code observability: `UnmappedSegcoreCodeTotal{code}`
counter + rate-limited WARN via an observer hook (merr is a leaf
package); registered on QueryNode and DataNode. Unknown code degrades to
non-retriable, never panics.
- **T6** — codegen + compile-time enforcement: a generated `SegcoreCode`
type (from milvus-common's `EasyAssert.h`) + an exhaustive
`classForCode` switch marked `//exhaustive:enforce`, with the
`exhaustive` golangci-lint enabled opt-in — a new C++ code that is not
classified fails lint (the C++→Go analog of `-Werror=switch`).
- **§3 B-tier** — classify `marisa` and `simdjson` errors
(build/load/parse) instead of collapsing to `2001`, sub-code in the
message; simdjson optional-access (`NO_SUCH_FIELD`/`INCORRECT_TYPE`)
stays a benign skip; the `loon_ffi` FFI boundary is untouched.
- **Boundary hardening (adversarial self-review of this PR's own diff)**
— closed the escapes that would defeat the mapping above: a `throw e;`
slicing rethrow in `LoadWithStrategy` that destroyed the very codes the
columnar-read mapping attaches (bare `throw;` now), the same slice in
`MinioChunkManager::PreCheck`; `GetCoreMetrics` /
`EstimateLoadIndexResource` / init-and-config entry points that could
let an exception cross the C ABI and terminate the process; and every
remaining extern-C entry that caught only `std::exception` now ends in
`catch(...)` via the shared `CGoCatch.h` macros.
- **Pin + semantics** — bump `milvus-storage_VERSION` to `11f8a36` (the
milvus-io/milvus-storage#574 merge, which also contains #575) and align
the no-detail `IOError` expectation with the settled semantics: the
producer tags every known-transient failure with a retryable
`ExtendStatusDetail`, so a bare `IOError` with no detail is unclassified
and deliberately falls back to permanent `StorageError(2044)` — a
stripped-detail NotFound now degrades to non-retriable (safe) instead of
retriable (retry storm on a permanent 404).

- **Wire pass-through (client-visible)** — a segcore error now reaches
the client with its ORIGINAL code (2009 stays 2009, 2024 stays 2024)
instead of collapsing to the `ErrSegcore(2000)` umbrella with the real
code buried in the message. Family identity for `errors.Is` is preserved
via inner/Unwrap; input/system/retriable classification unchanged.
Guardrails: only in-band (2000-2099) codes pass through (garbage still
collapses to 2000); cross-family mappings (2046 → wire 110) keep their
sentinel's code. `ErrSegcoreUnsupported`/`ErrSegcorePretendFinished`
move to the C++ values they represent (2001→2003, 2002→2033) — their old
numbers squatted on C++ UnexpectedError/NotImplemented and would
false-match under code-based `errors.Is`. Verified end-to-end on a live
standalone (ef<k reaches the client as 2042, unsupported tokenizer as
2001); the three e2e assertions pinning the old 2000 updated.

- **Remaining code-destroying sites** — the three classes that still
swallowed a producer's classification before the cgo boundary are now
gone from `internal/core/src` and `internal/core/thirdparty`:
status-consuming `AssertInfo` (104 → 0, incl. ~47 arrow builder paths
whose commonest failure is OOM, now retriable `MemAllocateFailed`
instead of a permanent 2001), bare `throw
std::runtime_error/logic_error/bad_alloc` (68 → 0 — these were not
`SegcoreError`, so they collapsed to 2001 *and* falsely fired the
untyped-exception observer), and `throw fmt::format(...)` (12 → 0 — it
throws a `std::string`, which `catch (std::exception&)` cannot see at
all). tantivy's 73 `AssertInfo(res.result_->success, ...)` (plus 10
raw-`RustResult` stragglers found later) now classify the rust error —
originally by its Display prefix, since replaced by a proper
`#[repr(i32)]` discriminant carried in `RustResult.error_code` (see the
Aug-10 update below). Typed `ThrowInfo` sites: 894 → 1081. The ~1500
genuine invariant asserts are untouched — 2001 is correct for them. The
long-standing FIXME about `err_code` not surviving the nested LOON FFI
boundary is also resolved, delegating to
`milvus_storage::ToSegcoreErrorCode` rather than duplicating its table.

## Verification

**Verified in this PR:**

- **Mapping correctness (unit-tested, in-process):**
`test_knowhere_status_mapping.cpp` / `test_storage_error_code.cpp` /
`test_exec.cpp` cover every mapper branch (knowhere Status incl. the
build variant, arrow/extend status incl.
`AwsErrorNotFound→ObjectNotExist(2017)`, permanent-S3 vs transient),
plus `FailureCStatus` code preservation and both observer hooks firing.
- **Code projection to Go (one hop, unit-tested):** `segcore_test.go`
pins `classForCode` for every generated code and asserts
`merr.Status(err).GetRetriable()` for transient codes; the T6 generator
is idempotent and the `exhaustive` lint fails on an unclassified code.
- **Full C++ suite:** 8213/8223 unit tests pass locally (10 skipped;
Azure connectivity tests excluded), 8648 in CI, rebased on current
master (one pre-existing, unrelated concurrency test excluded:
`GrowingConcurrentReopenTest` deadlocks deterministically on current
master with or without this PR — rwlock writer starvation in
growing-segment reopen code this PR does not touch; reported
separately).
- **Static audit (grep-verifiable):** every storage arrow-status
consumption site on the read path routes through
`ArrowStatusToErrorCode`, and every extern-C boundary ends in a
`catch(...)` tail.

**Explicitly NOT verified here (follow-up):**

- **Runtime fault injection.** No S3 throttle / 404 / OOM / corrupt-file
failure has been triggered end-to-end in a running cluster. Transient
codes reach Go with `retriable=true` (unit-tested projection), but the
downstream consumption — `lb_policy` replica reroute on
`merr.IsRetryableErr`, index/analyze scheduler retry — is pre-existing
logic from #50221 and has **not** been driven by a real segcore
transient error in this PR. This PR preserves classification for
observability and correct retry defaults; the retry behavior itself is
exercised only by its own pre-existing tests.

## Dependencies

- ~~milvus-common `StorageTransientError(2045)` —
zilliztech/milvus-common#102~~ **merged**.
- ~~milvus-storage `ToSegcoreError` / packed `ExtendStatusCode` —
milvus-io/milvus-storage#575 + #574~~ **merged; pin bumped in-tree to
`11f8a36`**.
- ~~knowhere three-way classification — zilliztech/knowhere#1704~~
**merged** (the milvus-side `KnowhereStatusToErrorCode` → thin delegate
to knowhere's own `ToSegcoreErrorCode` is a follow-up, gated on a
knowhere version bump).
- ~~milvus-common untyped-cgo-exception observer —
zilliztech/milvus-common#112~~ **merged and released as `1.0.0-1fd1160`;
the pin now points at the published package.** All dependencies are in.

## Update (Aug 10) — full-population audit, LOON path, runtime
observability

The originally deferred FFI/LOON path is now **done on the milvus
side**, and the audit was extended from the three grep-able classes to
the *entire* 2001-producing population:

- **Every remaining 2001 site read.** All 1,517 `AssertInfo` (four
sweeps: errno fingerprint, failure-keyword messages, condition
morphology, and finally **data provenance** — does the guarded value
come from disk/network?) and all 198 explicit
`ThrowInfo(UnexpectedError)` sites. ~290 were externally-triggerable and
now carry typed codes: file/remote IO ->
`FileOpen/Create/Read/WriteFailed` (retriable), mmap/allocation ->
`MmapError`/`MemAllocateFailed` (retriable), persisted-format damage
(CRC/magic/parquet meta/index-meta keys) -> `DataFormatBroken`,
deployment config -> `ConfigInvalid`, request content ->
`InvalidParameter`, a cancel-race -> `FollyCancel`. The ~1,400 kept
sites are genuine invariants or cgo contracts where 2001 is the correct
report.
- **Two infinite-retry bugs.** Statically-impossible conditions
(index_type x metric blacklist, per-type metric allowlists,
json/geometry index gates) threw 2001 -> generic retry -> the build task
spun forever; they now throw `Unsupported`, which `getStateFromError`
maps to a terminal `JobStateFailed`. Missing
`index_type`/`metric_type`/`min_gram`/`max_gram` keys in persisted index
meta had the same loop on the load path; they are `DataFormatBroken`
now.
- **knowhere `expected<>` bypasses closed** (8 sites in
`QueryResult.h`/`CachedSearchIterator`): iterator failures went through
`AssertInfo` and discarded the Status knowhere had already classified;
they now route through `KnowhereStatusToErrorCode`, so an OOM/disk
failure during search iteration stays retriable. Preflight rewraps in
`segment_c`/`boost_score` similarly preserved the original
`SegcoreError` code instead of flattening to 2001+string.
- **tantivy discriminant over the FFI.** `RustResult` now carries
`error_code` (`#[repr(i32)] TantivyBindingErrorCode`,
cbindgen-exported); the C++ mapper switches on the enum instead of
parsing the Display text, and the inner `tantivy::TantivyError` is
discriminated too (`IoError/Open*Error` -> Io/retriable,
`DataCorruption/IncompatibleIndex` -> DataCorruption). Wording changes
on the rust side can no longer silently degrade classification.
- **LOON / FFI path (the deferred item), milvus side complete.** The Go
funnel `HandleLoonFFIResult` dropped `err_code` entirely and wrapped
every failure as `ErrLoonTransient` — a 404/access-denied/corrupt-data
retried as transient. It now classifies by the producer's own
`loon_ffi_is_retryable_errcode`; permanent failures carry the new
`ErrLoonPermanent` and terminate retry loops (`pack_writer_v3` via
`retry.Unrecoverable`; the external-refresh manager guard extended so
behavior does not invert). On the C++ side `LoonErrCodeToErrorCode` is
the single classification entry (low band -> hand table, extend band ->
producer's `ToSegcoreErrorCode`, unknown -> producer's retryable probe),
unifying the two previously-divergent `ThrowIfFFIError` helpers —
`LOON_FILE_NOT_FOUND(12)` now converges to `ObjectNotExist(2017)` on
both integration paths. Remaining LOON items (e.g. promoting
FileNotFound into `ExtendStatusCode`) live in the milvus-storage repo.
- **Regression guards.** `scripts/check_segcore_error_boundaries.sh`
wired into `make static-check`: every `throw` in `internal/core/src`
must carry a milvus ErrorCode (zero-tolerance; currently 0 violations);
vendored `fmindex::` is confined to its boundary files;
knowhere/arrow/milvus_storage/tantivy are ratcheted by a checked-in
file-set baseline (new consumer files fail the check; shrinking is
free).
- **Runtime observability for what is left.**
`milvus_cgo_unexpected_segcore_origin_total{origin="<file>:<line>"}`
counts every 2001 crossing the cgo boundary by its C++ source location
(parsed from the ` at file:line` suffix `AssertInfo` already emits,
build paths collapsed to repo-relative). A site that fires in production
names itself — reclassification becomes evidence-driven instead of
re-reading ~1,400 asserts.

Site count for the 2001 family: 1,955 on master -> 1,525 on this branch;
the delta is reclassification into actionable codes, not deletion of
checks.

## Deferred

- milvus-storage-side LOON improvements: promote `LOON_FILE_NOT_FOUND`
into `ExtendStatusCode`, category byte (design §4.7) — tracked in the
storage repo.
- knowhere-side: thin-delegate `KnowhereStatusToErrorCode` to knowhere's
own `ToSegcoreErrorCode`, gated on a knowhere version bump.


issue: #50903

---------

Signed-off-by: Zack <[email protected]>
Co-authored-by: Zack <[email protected]>
Co-authored-by: Claude Fable 5 <[email protected]>
Co-authored-by: xiaofanluan <[email protected]>
2026-09-11 14:18:26 -07:00
48a5f578c0 test: update client cases for storage and sdk changes (#53305)
issue: #51544

Reopens #51755, whose author has left the project. The branch is rebased
onto current master and the conflicts are resolved.

## What changed

- Derive the default Python client test namespace from `MILVUS_NS` or
`MILVUS_HELM_NAMESPACE` before falling back to `chaos-testing`.
- Add an optional external-source MinIO endpoint override
(`MILVUS_EXTERNAL_SOURCE_MINIO_HOST`) for external table tests, so the
client upload endpoint and the server-side `external_source` endpoint
can be configured independently.
- Resolve the bulk insert MinIO endpoint from the existing `minio_host`
/ `minio_bucket` fixtures instead of looking up the MinIO pod IP through
the k8s API, so these cases no longer require in-cluster access.
- Extend TEXT LOB layout validation to read Vortex segment data files in
addition to the existing parquet layout. The Vortex files are decoded
with `vortex-data`; when the installed reader cannot decode Milvus
StorageV3's custom encoding the case skips rather than asserting on
synthesized references.

## Dropped from the original PR

Two changes from #51755 have since landed on master independently and
are not included here:

- The multi-primary-key invalid schema assertion — master already
validates at `add_field` time, via the framework's `CheckTasks.err_res`
helper rather than `pytest.raises`.
- The `Date` / `Time` / `Decimal` entries in the external table
add-field matrix.

## Verification

- `test_text_lob_bulk_import_json_csv_parquet`
- `test_text_lob_inline_vs_lob_object_layout`
- `test_milvus_client_external_table_add_all_supported_field_types`
- `test_milvus_client_xgboost_supports_all_numeric_feature_types`

Signed-off-by: zhenshan.cao <[email protected]>
Co-authored-by: nico <[email protected]>
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 15:29:31 +08:00
zhuwenxing dd75544f37 test: fix external refresh and oversized insert expectations (#53148)
issue: #53133
issue: #52648

## What was changed

- Handle both valid outcomes of two back-to-back external collection
refresh requests:
  - reject or reuse the request while the first job is active;
  - allow a new job only after the first job reaches a terminal state.
- Update the oversized insert test to verify the current
application-level
  `maxInsertSize` rejection.
- Assert error code `1102` and the stable `exceeds maxInsertSize`
message instead
  of the obsolete gRPC `message larger than max` error.

## Why

The external refresh test assumed that the first job would always remain
active
until the second request arrived. On faster environments, the first job
can
complete before the second request, making a new refresh job valid.

The oversized insert test assumed a 64 MiB gRPC receive limit. The Proxy
receive
limit is now 128 MiB, while oversized materialized inserts are rejected
by the
64 MiB `quotaAndLimits.limits.maxInsertSize` guard.

## Verification

- `ruff check` passed for both changed files.
- `ruff format --check` passed for both changed files.
- Python compilation checks passed.
- External refresh regression E2E: passed.
- Oversized insert regression E2E: passed with error code `1102`.

---------

Signed-off-by: zhuwenxing <[email protected]>
2026-09-07 15:13:31 +08:00
yanliang567 3067e2542d test: add Go, PyMilvus, and REST e2e for FMINDEX and membership_match (#52629)
## Summary

Add end-to-end coverage for FMINDEX and the unified `membership_match`
expression across the Go SDK, PyMilvus, and RESTful v2.

Related issues: #51375, #52778, #53036

## Coverage

### FMINDEX

- Build parameter defaults, boundaries, combined parameters, and invalid
types or ranges.
- VARCHAR-only validation, including JSON, scalar, and struct-array
sub-fields.
- Prefix, infix, suffix, general LIKE recheck, regex fallback, escaped
wildcards, NULL, and UTF-8 behavior.
- Long-text low-hit fixtures and sealed segments above
`minSegmentNumRowsToEnableIndex`, preventing fake-finished index
coverage.
- Exact indexed-field versus unindexed twin-field PK comparison.
- Growing plus sealed visibility, mutation, upsert, named-index drop,
and rebuild lifecycle checks.
- Query and Search coverage with exact projected fields, distance
ordering, index identity, type, and row-progress assertions.
- VARCHAR AUTOINDEX resolution is verified against the configured
scalar-index policy without hard-coding a specific index type.

### membership_match

- Bloom and Roaring wire compatibility through a shared static manifest
while each SDK keeps independent collection setup.
- Bloom zero-false-negative checks plus a statistically bounded
false-positive check tied to the configured FPR; Roaring uses exact PK
ground truth.
- Query and Search must return the same complete Bloom-filtered PK set
when topK covers the fixture.
- Integer widths, negative values, INT64 boundaries, VARCHAR, JSON
strict typing, nested paths, dynamic fields, NULL semantics, and
unsupported top-level BOOL/FLOAT fields.
- Positive, negated, empty, malformed, domain-mismatch, delete, growing
plus sealed, and struct-array rejection cases.
- Explicit `STL_SORT`, `INVERTED`, `BITMAP`, and `TRIE` scalar-index
paths.
- Internal-only `HYBRID` paths are exercised through the supported
`AUTOINDEX` request and verified against DataCoord's resolved build
parameters for primitive and JSON-path indexes.
- Mutation tests use isolated collections; shared read-only fixtures
register cleanup before setup begins.

### RESTful v2

- FMINDEX anchored LIKE Query and Search compared with exact PK ground
truth and an unindexed twin field.
- Index identity, type, persisted parameters, row progress, completion,
and collection load state are verified before assertions.
- Search validates exact projected values by primary key.
- JSON string and missing membership template parameters are verified as
server-side validation failures.
- Python `bytes` are verified to fail local JSON serialization before
HTTP. A positive REST binary-template case remains blocked by #53036.

## Validation

- `go test -tags dynamic,test -gcflags='all=-N -l' -c ./testcases`
- `go vet -tags dynamic,test ./testcases`
- `go test ./membership/sbbf -count=1`
- Ruff check and format check for all changed Python and REST files
- Python byte-compilation for all changed Python and REST files
- Pytest collection: 68 PyMilvus/FMINDEX tests and 7 REST tests
- Cross-SDK membership wire-manifest verification
- `git diff --check`

The Proxy parameter-resolution unit test and Bloom integration package
cannot execute on the host because it lacks the native `milvus_core`,
`rdkafka`, and `rocksdb` pkg-config packages. A targeted PyMilvus
FMINDEX run reached index creation but timed out after 120 seconds
before assertions; its temporary collection was cleaned up. Full
server-backed E2E was not completed.

Signed-off-by: Yanliang Qiao <[email protected]>
2026-09-07 14:07:30 +08:00
zhuwenxing 87058e5dc7 test: stabilize TTL E2E cases under slow CI (#53175)
issue: #53171

## What this PR does

- Preserve collection TTL expiration coverage while measuring from
before the first write with a monotonic deadline.
- Verify newly inserted rows remain visible under the original short
collection TTL before extending it for slower multi-RPC checks.
- Replace setup-sensitive TTL windows in timezone-offset and partition
cases with explicit expired and unexpired timestamps.
- Preserve real-time entity expiration after release and reload by
starting the short TTL window after reload completes.
- Use monotonic polling with attempt and elapsed-time diagnostics.

## How was it tested?

- Ruff check and format check passed.
- Python compilation check passed.
- The complete TTL test file passed with pytest-xdist using 6 workers
and load-group scheduling: 37 passed.

---------

Signed-off-by: zhuwenxing <[email protected]>
2026-09-06 18:07:38 +08:00
32b08caeb5 fix: fail invalid milvus-table refresh requests (#52304)
issue: #52294

## Summary

- add PyMilvus E2E coverage for using Milvus snapshots as milvus-table
external sources
- classify invalid snapshot metadata paths as input errors so refresh
jobs fail terminally instead of remaining retryable
- verify real and virtual primary keys, deletes, refreshes, schema
alignment, mixed data types, and failure atomicity
- mark the target-owned BM25 output case as a strict xfail pending
#52303

## Design document

docs/design-docs/design_docs/20260526-milvus-table-external-source.md

## Test plan

- [x] python3 -m compileall -q
milvus_client/test_milvus_client_snapshot_external.py
- [x] python3 -m ruff check
milvus_client/test_milvus_client_snapshot_external.py
- [x] targeted BM25 E2E reports XFAIL with the #52303 reason
- [x] pytest collection discovers all 17 test variants
- [ ] targeted Go unit tests were not runnable locally because
milvus_core, milvus-storage, and RocksDB pkg-config dependencies are
unavailable; CI will run them

---------

Signed-off-by: Eric Hou <[email protected]>
Co-authored-by: Eric Hou <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-09-04 17:39:30 +08:00
WangshuandClaude Opus 5 2736a1ee33 test: align boost-ranker invalid-filter assertion with server wording (#53101)
`test_search_boost_ranker_invalid_filter` asserts that the error message
for an
invalid boost-ranker filter contains `parse expr failed`. That wording
no longer
exists anywhere in the server, so the case fails deterministically on
master.

#52778 (6648d9371b) changed `CreateSearchScorer` from

```go
return nil, merr.WrapErrQueryPlan(err, "parse expr failed")
```

to

```go
// merr.Wrap, not WrapErrQueryPlan: the latter replaces the inner
// code, so an over-limit scorer filter surfaced as 2201 while the
// identical main predicate surfaced as 1102.
return nil, merr.Wrap(err, "parse scorer filter failed")
```

The server still rejects the request as intended, with the same code the
test
expects — preserving that code was the point of the change. Only the
human-readable substring moved, so this is a stale assertion, not a
product
regression.

## Change

```diff
-            check_items={"err_code": 1100, "err_msg": "parse expr failed"},
+            check_items={"err_code": 1100, "err_msg": "parse scorer filter failed"},
```

`parse scorer filter failed` is the current server phrase, and it
matches the
convention of the other three cases in this file, which each pin the
specific
server-side phrase for the failure under test (`must set weight params`,
`segment scorer with group_by`).

## Verification

- `parse expr failed` appears nowhere in `internal/`, `pkg/` or `tests/`
after
  this change — the assertion could never have matched.
- `parse scorer filter failed` is emitted at
`internal/parser/planparserv2/plan_parser_v2.go:447`, and is a substring
of
  the message quoted in the issue:
`failed to create query plan: parse scorer filter failed: cannot parse
expression: ...`
- `CheckTasks.err_res` compares `err_msg` as a plain substring
(`tests/python_client/check/func_check.py:172`); the `err_code`
assertion
there is commented out, so the substring was the only failing condition.
- Swept the other error literals changed by 6648d9371b (the
`bloom_match`/`roaring_match` → `membership_match` rename): the
integration
suites exercising them were updated in that same commit, so this is the
only
assertion the refactor left stranded. The other three assertions in this
file
  still match current source.

**Not verified:** the case was not executed against a live server — no
Milvus
instance was available in the environment this was prepared in. The
change is
verified statically against the message quoted in the issue.

issue: #53083

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Wangshu Pang <[email protected]>
Co-authored-by: Claude Opus 5 <[email protected]>
2026-09-03 05:09:29 +08:00
yanliang567 06a86d47c9 test: Add search-by-PK input order regression test (#53091)
## Summary

- Add Python Client E2E regression coverage for search-by-PK requests
with non-ascending IDs.
- Compare each search-by-PK top-1 result block with an equivalent
ordinary vector search to verify positional correspondence.
- Use an independent FLAT/L2 collection with strong consistency and
deterministic orthogonal vectors.

issue: #52862

## Test Plan

- [x] Run the new case against
`harbor.milvus.io/milvusdb/milvus:3.0-20260901-eb4e2a97` (1 passed).
- [x] Repeat the new case three times against the same deployment (3
passed).
- [x] `python -m py_compile
tests/python_client/milvus_client/test_milvus_client_search_by_pk.py`
- [x] `git diff --check origin/master...HEAD`

Signed-off-by: Yanliang Qiao <[email protected]>
2026-09-02 15:27:29 +08:00
yihao.daiandClaude Fable 5 c30447158d test: retry CDC topology restore in force-promote teardown (#52993)
## Problem

The CDC force-promote failover tests kill containers with chaos-mesh and
restore the A→B topology in teardown. `wait_for_pods_ready` returns as
soon as the Pod reports Ready, but the proxy can still refuse
connections for a few seconds after that, so the single-shot switchover
loses the race — and the teardown swallowed the failure as a warning,
letting the test pass with the topology left broken for every later
test.

Observed in `milvus_cdc_failover_test` #55:

- `03:08:23` `wait pods mcf-s-55: rc=0` (pods reported Ready)
- `03:08:24` teardown starts restoring A→B
- `03:08:25` `Fail connecting to server on <upstream>:19530` → logged as
a warning, test still PASSED
- next test then failed with `create collection ... sync failed after
30.11s timeout`, and its cleanup reported `cluster is not primary,
cannot do any DDL/DCL`

One root cause, a misleading failure in a different test.

## Changes

**`tests/python_client/cdc/conftest.py`**
- `switchover_helper` retries for up to 180s while a cluster is still
refusing connections, rebuilding and closing its clients on each
attempt. This covers every caller, including `test_switchover.py`.

**`tests/python_client/cdc/testcases/test_force_promote.py`**
- The six teardowns record a restore failure, still run
`cleanup_resources()`, then re-raise it instead of warning — a broken
topology can no longer silently poison later tests.

## Test Plan

Injected connection failures into the shipped `do_switchover` path:

- [x] transient refusals (the #55 scenario): retries, restores the
topology, sends the config once
- [x] cluster never recovers: raises after the deadline instead of
continuing silently
- [x] healthy cluster: succeeds on the first attempt, both clients
closed, behavior unchanged
- [x] `ruff check` with the repo's `tests/ruff.toml` passes

Not exercised against a live cluster; the retry window (180s) is a first
estimate based on the ~1s gap seen in #55 plus headroom.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: bigsheeper <[email protected]>
Co-authored-by: Claude Fable 5 <[email protected]>
2026-09-02 01:49:30 +08:00
zhuwenxing 81be5438a5 test: run growing segment snapshot regression in CI (#53038)
issue: #52989

## What changed

Promote `test_snapshot_growing_segment_without_flush` from L2 to L0 so
the regression test runs in CI.

## Why

This test directly covers the snapshot restore regression reported in
#52989. Running it in CI helps prevent the issue from recurring.

## Validation

- `ruff check
tests/python_client/milvus_client/test_milvus_client_snapshot.py`
- Python syntax compilation passed
- L0 collection check: 1 test collected
- Targeted Python SDK E2E: 1 passed

Signed-off-by: zhuwenxing <[email protected]>
2026-09-01 14:07:24 +08:00
zhuwenxing 9adaf280f6 test: update outdated Python client expectations (#52997)
### What this PR does

Update two outdated Python SDK E2E expectations:

- Classify the newly added protobuf-only `Date`, `Time`, and `Decimal`
data types as unsupported for external table add-field operations.
- Validate duplicate primary keys during schema construction, where
current PyMilvus raises the error, instead of waiting for collection
creation.

Without the first classification, the external-table case fails with:

`external add-field proto DataType matrix is stale;
unclassified={28,29,30}`

### Verification

- Ruff checks passed.
- Python compilation passed.
- Both affected pytest nodes were collected successfully.
- Both affected Python SDK E2E cases passed against a master build:
-
`TestMilvusClientExternalTableAddField::test_milvus_client_external_table_add_all_supported_field_types`
-
`TestMilvusClientCollectionInvalid::test_milvus_client_collection_invalid_schema_multi_pk`

---------

Signed-off-by: zhuwenxing <[email protected]>
2026-08-31 15:51:28 +08:00
zhuwenxingandCodex 9c7f7329a6 test: cover embedding list indexes with trailing empty arrays (#52935)
## What this PR does

Add Python SDK E2E coverage for Embedding List indexes whose sealed data
ends with empty arrays.

The test:

- inserts non-empty Embedding Lists followed by trailing empty arrays;
- builds and loads an HNSW index;
- verifies filtered search over the indexed data;
- retrieves the final non-empty row and the last two empty rows;
- checks that trailing empty rows remain present and return empty
arrays.

## Verification

- Reproduction commit `b0d70ae`: failed with `failed to get emb list,
invalid args`.
- Current master-based image: passed.
- `ruff check`: passed.
- Python compilation check: passed.

---------

Signed-off-by: zhuwenxing <[email protected]>
Co-authored-by: Codex <[email protected]>
2026-08-31 15:39:33 +08:00
David Dallakyanandxiaofanluan 3dfae89e93 feat: route general LIKE through FMINDEX with candidate recheck (#52697)
General LIKE with interior `%` or `_` was declined by FMINDEX even
though PrefixMatch, PostfixMatch, and InnerMatch already use it. This
routes VARCHAR Match through `ShouldUseOp`: split the pattern on
wildcards, take candidates from the rarest literal fragment, then
recheck those rows on sealed VARCHAR with `LikePatternMatcher` via
`ProcessDataByOffsets`.

FMINDEX load no longer materializes ISA. Match phase 2 rechecks
candidate rows on sealed VARCHAR via ProcessDataByOffsets. The Match
guard is locate-only again, the same occ times sa_sample_rate bound as
Prefix, Inner, and Postfix.

Empty-fragment patterns still decline. RegexMatch, equality, IN, TEXT,
JSON, ARRAY, and growing segments are unchanged. FMINDEX Match is never
served as unrechecked PatternMatch candidates through the ScalarIndex
path.

Design: `docs/design-docs/design_docs/20260708-fm_index_scalar_index.md`

issue: #52683

## How to test

`LOCAL_STORAGE_SIZE=10` is required on this host.

Without the VARCHAR recheck (product from `47f5cf5791`, tests kept): 5
FAILED (`ShouldUseOp(Match)` still prices Extract, so `%RARE%` /
`%ZEBRA%` stay declined).

With `7a3e274a36`:

```
source scripts/setenv.sh
export DYLD_LIBRARY_PATH="$PWD/cmake_build/src:$DYLD_LIBRARY_PATH"
LOCAL_STORAGE_SIZE=10 cmake_build/unittest/all_tests --gtest_filter='FMIndex.*'
[==========] 23 tests from 1 test suite ran.
[  PASSED  ] 23 tests.
```

Includes `FMIndex.ExecutorPathMatchRechecksVarchar` and
`FMIndex.MatchOracleEqualsBruteForce`.

Whole C++ module (three Darwin crashers excluded):

```
export LOCAL_STORAGE_SIZE=10
internal/core/output/unittest/all_tests --gtest_filter='-JsonIndexTest.LargeInt64LiteralDoesNotAliasInDoublePathIndex:JsonFlatIndexExprTest.PreservesLargeInt64LiteralPrecision:SortBufferTest.*'
[==========] Running 8326 tests from 473 test suites.
[  PASSED  ] 8167 tests.
```

None of the 138 failures are `FMIndex.*`. Reverse deps: Ngram 10/10,
scalar/hybrid/inverted 26/26, json_stats_test 79/79, LikeConjunctExpr
1/1.

/cc @xiaofan-luan

---------

Signed-off-by: David Dallakyan <[email protected]>
Signed-off-by: xiaofanluan <[email protected]>
Co-authored-by: xiaofanluan <[email protected]>
2026-08-30 22:40:06 +08:00
zhuwenxing 4461a40a2d test: stabilize chaos checker lifecycle and snapshot restore validation (#52901)
## What this PR does

- bound and stagger expensive chaos checker operations
- stop checker threads as a coordinated group and fail fast when they
stall
- isolate snapshot restore validation in a dedicated collection
- verify restored row counts and sampled primary keys
- avoid rebuilding indexes unnecessarily during chaos recovery
- add structured checker observability and focused unit coverage
- document entity-level TTL behavior

## Why

Concurrent heavy checker operations can accumulate after fault recovery,
delay snapshot restoration, and cause the validation stage to exceed its
timeout. This change bounds checker lifecycle work, improves cleanup and
recovery behavior, and makes stalled operations easier to diagnose.

## Validation

- `ruff check` passed for all changed Python files
- `git diff --check upstream/master...HEAD`
- chaos checker unit tests: 27 passed
- `test_concurrent_operation.py`: passed in 1005.20s
  - snapshot restore jobs: 8/8 completed
- `test_single_request_operation.py`: passed in 1649.80s
  - snapshot restore jobs: 4/4 completed
- standalone + Woodpecker validation against Milvus commit `f5eef7da8ab`
- all 12 restore jobs completed in 2.6–22.0 seconds without restore
timeout

## Notes

The end-to-end validation covered the concurrent baseline without active
chaos injection. Fault-injection recovery remains covered by the
scheduled chaos pipelines.

---------

Signed-off-by: zhuwenxing <[email protected]>
2026-08-28 15:37:28 +08:00
b50db3004e test: e2e for schema-bump backfill over added-field function input and TEXT LOB (#52662)
### What problem
master e2e never covers two schema-bump additive intersections: (1) an
`add_function_field` whose input VARCHAR was itself added via
`add_collection_field` after the data was sealed (the exact crash shape
of issue #52159); (2) an `add_function_field` bump triggered while the
collection already holds a TEXT LOB column. Today
`test_add_function_field_feature.py` uses only create-time VARCHAR
inputs (no `add_collection_field`), and `test_milvus_client_text_lob.py`
has no `add_function_field` at all — the two concerns never meet.

### What this does
Add two pymilvus e2e cases into the existing files (reusing their
helpers, no new file):
- `test_add_bm25_function_field_on_backfilled_varchar_input`: seal rows
without the varchar, `add_collection_field(VARCHAR)`, then
`add_function_field` BM25 whose input is that added field. Asserts: old
rows read back null; sibling columns (code/vec) are byte-exact per row
against the inserted values; schema_version increments across both
bumps; null-input rows are never matched by BM25.
- `test_text_lob_add_function_field_bump_preserves_lob`: sealed TEXT LOB
rows in four states (external/inline/empty/null) plus an
`add_function_field` BM25 bump. Asserts: every `content` payload is
preserved byte-exact; sibling columns (tag/vector) stay intact per row;
a post-bump insert is still exact.

Add base helper `wait_for_schema_version_consistency`: gate bump
completion on the DataCoord-emitted `schema_version_consistent_segments
== schema_version_total_segments` stats (via `get_collection_stats`)
instead of a fixed sleep; combined with a `describe_collection`
schema_version increment assertion so the gate cannot pass on the
pre-bump state.

### Dependency / notes
- Depends on the master fix landing first: `pr: #52484` (issue #52159).
Until it merges,
`test_add_bm25_function_field_on_backfilled_varchar_input` reproduces a
`panic: no such field` on the datanode (exactly the regression this test
guards), so CI is expected RED — this is intended.
- Validated locally with a with-fix / without-fix two-arm comparison:
the without-fix binary reproduces the panic (RED), the with-fix binary
passes both tests (GREEN). External LOB goes through the `.vx` ref and
the bump uses REUSE_ALL (LOB path unchanged), both confirmed against
on-disk storage.
- Requires `dataCoord.compaction.bumpSchemaVersion.enabled: true`
(already set in the three e2e helm values).
- Related enhancement tracking: #51649.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


Also folds in a one-line repair of a stale L2 assertion (issue: #52878):
#52219 intentionally dropped the computed expected dim from the minhash
dim-mismatch message (overflow-safe division check), so the assertion no
longer expects the literal `512`.

Signed-off-by: MrPresent-Han <[email protected]>
Co-authored-by: MrPresent-Han <[email protected]>
Co-authored-by: Claude Opus 4.8 <[email protected]>
2026-08-27 10:27:23 +08:00
zhenshan.cao b277503c9b test: stop pinning the knowhere codec list in test_sparse_index_rejects_invalid_codec (#52758)
<!-- team2-testfix:52645 -->
fix: #52645

## What

Test-only change. `test_sparse_index_rejects_invalid_codec` asserted the
full server error message, including the `supported: [...]` list that
knowhere generates. That list legitimately grows when knowhere is
upgraded (it gained `block_adaptive`), so the assertion breaks on a
correct server. The expectation is narrowed to the part that does not
drift:

```
"inverted_index_codec invalid_codec not found or not supported"
```

The check is a substring match (`assert error_dict[err_msg] in
res.message`), so the shortened expectation still verifies that the
invalid codec is rejected, and the error-code assertion (1100) is
unchanged. Sibling negative cases in the same file
(`inverted_index_algo`, `quant_type`) already use this short-prefix
form; this makes the codec case consistent with them. No product code is
touched.

## Why this is not a server bug

- `grep -rn "block_streamvbyte\|block_maskedvbyte\|block_adaptive"` over
the repository matches only this test file. The parameter name and the
`supported: [...]` list come from the knowhere binary; Milvus passes the
parameters through `ValidateIndexParams` (cgo).
- The error code is 1100 on both sides and the rejection path executes
correctly — only the set of supported codecs grew.
- There is no upstream fix to backport: the hardcoded string was
introduced in `0bbe4d02a8` (#50775) and has not been touched since.
- No cross-repo change is involved: pymilvus only relays the server's
error message; the proto is unchanged.

## Evidence

The single failing case was run against one running Milvus instance,
alternating unpatched/patched twice each
(`milvus_client/test_milvus_client_sparse_inverted_index.py::TestSparseInvertedIndexV3Negative::test_sparse_index_rejects_invalid_codec`):

| run | unpatched | patched |
|---|---|---|
| 1 | fails with the reported assertion | passes |
| 2 | fails with the reported assertion | passes |

---
_Automated draft from the team2 pipeline. Not auto-merged; please
review._

Signed-off-by: zhenshan.cao <[email protected]>
2026-08-25 17:48:41 -07:00
junjiejiangjjj cfa24c7488 enhance: refactor merge operator for declarative construction (#52665)
- construct merge operators from representations with runtime search
metrics
- validate multi-input layouts and preserve native row and element
identities
  - round-trip element indices through DataFrame conversion
  - remove synthetic hybrid element keys and the select operator
  - add design documents and regression coverage

issue: #https://github.com/milvus-io/milvus/issues/52319

Signed-off-by: junjie.jiang <[email protected]>
2026-08-26 03:09:23 +08:00
yanliang567 82edd6b254 test: reduce e2e setup cost via shared collections (T2 geometry/minhash + reranker dedup) (#52765)
## Goal

pytest E2E runtime is dominated by setup (create + insert + index);
search/query is negligible. This PR adopts the "one setup, many
verification points" pattern (shared collections / wide tables) to cut
redundant setup **without reducing any coverage** (all parametrized
cases and assertions are preserved, no values trimmed).

## Changes (4 commits)

### 1. Geometry shared collections (pilot, measured −20% wall-clock)
- `TestMilvusClientGeometryInsertShared`: 14 insert-family cases share 1
collection (ids partitioned per WKT type), collapsing 14 setups → 1.
- `TestMilvusClientGeometrySpatialShared`: 14 spatial-operator cases
share 1 dataset provisioned in 2 collections (RTREE / no geo index),
collapsing 14 setups → 2.
- Template: `@pytest.mark.xdist_group` + module-scoped fixture +
`force_teardown=False` + `request.addfinalizer`.
- Measured against a test cluster: 123 passed / 3 skipped unchanged,
**137.2s → 110.4s**.

### 2. search.py stale reranker dedup
- `test_milvus_client_search_with_reranker` (288 combos, in-body setup)
is a duplicate of
`search_reranker.py::TestMilvusClientSearchDecayRerankShared` (identical
schema/parametrization/assertions). Removed the stale copy (−288
setups/PR); coverage kept by the shared version.

### 3. minhash default-schema shared collection
- `TestMilvusClientMinHashDefaultShared`: 3 read tests (search_basic /
search_with_filter / query_by_id) share 1 collection. Results identical
to baseline (the file was already fast, so the gain is marginal).

### 4. Review fixes
- Replaced module-level globals with class attributes; cached client
across the class; added clarifying comments (RTREE-on-empty, geo-index
semantic change); extra dedup of
`test_milvus_client_search_with_reranker_default_offset_decay` (shared
version is a strict superset). All commits carry `Signed-off-by`.

## Verification
- geometry + minhash run against a test cluster: 196 passed; the 2
`TestMinHashBulkImport` failures are pre-existing environment issues
(also fail on master, need MinIO config).
- `ruff check` / `ruff format` pass.
- No parametrized values changed — zero coverage loss.

## Background
First batch of the e2e runtime optimization (T2:
one-setup-many-queries). Follow-ups planned per schema clustering
(struct_array_element_search, external_table, search.py remaining,
restful tag audit).

---------

Signed-off-by: Yanliang Qiao <[email protected]>
2026-08-24 19:27:22 +08:00
yanliang567 8f3de28158 test: tolerate rerank score drift when comparing rerank results (#52742)
## What this PR does / why we need it

Fixes #52737.

`test_milvus_client_single_vector_search_with_model_rerank` compares
Milvus rerank results against a fresh ground-truth `/rerank` call with
**exact list equality**. The reranker service is not bit-reproducible
run to run on GPU (nondeterministic reduction order + float16 amplifies
it into ~1e-3 relative score movement), so the assertion fails on
near-ties — two neighbouring candidates whose scores differ by less than
the service's own reproducibility error swap places.

## Changes

In both `compare_milvus_rerank_with_origin_rerank` implementations
(`test_milvus_client_search.py` and
`test_milvus_client_search_reranker.py`):

- Compare the reranked document **set** exactly (`sorted(actual) ==
sorted(gt)`).
- Enforce **ordering only between documents whose ground-truth scores
differ beyond a tolerance** (`rel_tol=0.05`, `abs_tol=1e-6`); near-ties
are allowed to swap.

## Why this doesn't mask real bugs

- CPU backends are bit-identical, so the tolerance never loosens a
deterministic run.
- A genuine ordering/pairing bug moves a score by far more than the
tolerance, so the epsilon-guarded order check (and the set check) still
catch it.

## Verification

- Ran `[True-tei]` / `[False-tei]` single-vector rerank and `[tei]`
hybrid rerank in both files against a standalone Milvus + TEI reranker
(CPU float32) — all passed.
- Standalone check with the failing run's data: near-tie swap is
tolerated, far-apart swap and wrong-set are still caught.

Signed-off-by: Yanliang Qiao <[email protected]>
2026-08-24 09:49:21 +08:00
junjiejiangjjj e6f4533744 feat: Support L1 rerank (#52376)
doc: docs/design-docs/design_docs/20260624-function-chain-api.md
#52319

Signed-off-by: junjie.jiang <[email protected]>
2026-08-21 14:25:20 +08:00
yanliang567 f744f11e82 test: avoid float-int collision in json_contains_any test data (#52616)
## Related Issue

Fixes #52594

## What type of PR is this?

- [x] test

## What this PR does

Fixes a flaky test
`test_milvus_client_query_expr_all_datatype_json_contains_any` that
intermittently fails with `1501 == 1500`.

## Root cause

The JSON field's `listMix` float element was built as `i * 1.1`. Under
IEEE 754 double arithmetic, some products round exactly to an integer
(e.g. `1190 * 1.1 == 1309.0`). When the random `_id` lands on such a
value, segcore's `json_contains_any` (intentionally) matches the JSON
double `1309.0` against the int64 query element, so an extra even row
satisfies the predicate and the hardcoded `default_nb // 2` assertion
fails.

This is a test-data defect, not a product bug — the server result is
correct for the inserted data.

## Changes

Replace `i * 1.1` with `i + 0.5` (which can never equal an integer) in
the three affected spots in
`tests/python_client/milvus_client/test_milvus_client_query.py`:
- `listMix` in
`test_milvus_client_query_expr_list_all_datatype_json_contains_all`
- `listMix` in
`test_milvus_client_query_expr_all_datatype_json_contains_any` (the
flaky test)
- `mix_data` in
`test_milvus_client_query_expr_list_all_datatype_json_contains_any`

## Verification

- Reproduced before the fix: `json_contains_any(listMix, [1309, True])`
returned 1501 rows (extra even row `int64=1190`).
- After the fix, forcing the previously-colliding `_id=1309` returns
exactly 1500 rows.
- `test_milvus_client_query_expr_all_datatype_json_contains_any` passed
5 consecutive rounds (10 parametrized runs).
- The other two touched tests passed as well (2 + 4 parametrized runs).

Signed-off-by: Yanliang Qiao <[email protected]>
2026-08-20 14:21:19 +08:00
yanliang567 9b6f05b0cb test: fix external table drop-function e2e to use drop_function_field (#52674)
## What type of PR is this?
- [x] test

## What this PR does / why we need it
Fixes `test_milvus_client_external_table_drop_function_rejected`, which
asserted the old external-collection rejection message. The
`DropCollectionFunction` RPC is deprecated and the proxy now rejects it
earlier (impl.go:1409) with a different message, before the
external-collection schema check runs, so the test failed on message
comparison.

Switch the test from `drop_collection_function` to
`drop_function_field`, which routes through `AlterCollectionSchema` and
still exercises the external-collection schema protection, producing the
expected `alter collection schema operation is not supported for
external collection` rejection.

## Which issues does this PR resolve?
Fixes #52646

## Test evidence
- Reproduced the failure against master + pymilvus 3.1.0rc80, then
verified the fixed test passes:
`pytest milvus_client/test_milvus_client_external_table.py -k
test_milvus_client_external_table_drop_function_rejected` → 1 passed

Signed-off-by: Yanliang Qiao <[email protected]>
2026-08-20 09:45:18 +08:00
Li Yiyang 9672073f7a test: add e2e for query order by with vector output (#52574)
## Summary

Add E2E regression test OB-063 for #52463: scalar Query ORDER BY
combined with a dense vector in `output_fields` used to fail with
`unsupported data type VECTOR_FLOAT` (segcoreCode=2001) because the
vector was materialized in the pre-TopK ProjectNode (single-project
mode). Fixed by #52498, which defers vector outputs to late
materialization after TopK.

issue: #52463

### Test design

- All-fixed-width schema (`id` / `rank` / `vec`, no variable-width
field), so the pre-fix planner takes the single-project path that
triggers the bug
- Sort key `rank = (i*7)%10` is a coprime permutation of 0..9: unique
keys, deterministic total order, and a real row permutation, so the
deferred post-TopK vector fetch must actually reorder rows (an `id:asc`
sort would be an identity permutation and could mask a reordering bug)
- Covers both explicit (`["id","rank","vec"]`) and wildcard (`["*"]`)
output_fields
- Asserts ascending rank order and verifies each returned vector's
payload against its row id (`vec[0] == id`)

### Test plan

- [x] Fails on pre-fix build `3.0-20260814-566ace73` with the exact
issue error: `unsupported data type VECTOR_FLOAT at Utils.cpp:1712`,
code=2000
- [x] Passes on post-fix build `master-20260817-c2a92a8a` (contains
#52498), cluster mode, both via LB endpoint and port-forward

Signed-off-by: lyyyuna <[email protected]>
2026-08-18 10:51:18 +08:00
Li YiyangandClaude Opus 4.6 e2a193598b test: add REST ordering, aggregation, and schema evolution coverage (#52170)
## Summary

- Add REST v2 query and search coverage for `orderByFields`, query
aggregation, and `searchAggregation`.
- Add REST v2 schema-evolution coverage for drop field and atomic
add/drop function-field operations.
- Extend the REST test client with `add_function_field` and
`drop_function_field` helpers.
- Keep schema-evolution cases focused on REST request parameters and
resulting metadata, without duplicating segment lifecycle or
multi-replica coverage.

## Coverage

- Query/search ordering with filters, pagination, nullable fields,
deterministic member assertions, and deliberately distinct ANN/scalar/PK
ordering.
- Query/search aggregation with exact metrics and hit-score oracles,
nullable group keys, composite/nested groups, `searchSize`, `topHits`,
and multi-query ownership.
- Drop field invalid identifier combinations, protected or unknown
fields, repeated drops, and complete before/after schema-index
snapshots.
- Add function field required objects, field-name binding, default
AutoIndex behavior, output-field/function/index metadata, and
rejected-request atomicity.
- Drop function field selective cascade across two bindings, invalid or
repeated drops, and field dependency validation.

Related changes: #52071, #51360, #51411

## Test Plan

- `python3 -m pytest testcases/test_query_order_by.py
testcases/test_search_order_by.py testcases/test_query_aggregation.py
testcases/test_search_aggregation.py
testcases/test_drop_field_operations.py
testcases/test_function_field_operations.py --endpoint
http://127.0.0.1:19530 --token root:Milvus --minio_host 127.0.0.1 -q -s`
  - 66 passed
- `python3 -m pytest
milvus_client/test_milvus_client_search_aggregation.py --host 127.0.0.1
--port 19530 --user root --password Milvus --token root:Milvus -q -s`
  - 51 passed, 1 existing xfailed
- `ruff check` on all seven changed test files
- `ruff format --check` on all seven changed test files
- `git diff --check`

---------

Signed-off-by: lyyyuna <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-08-18 10:41:18 +08:00
Li YiyangandClaude Opus 4.6 abe869648e test: strengthen add function field search readiness coverage (#52131)
## Summary

- Align add-function-field search polling with the schema-evolution
readiness policy.
- Record transient search errors and empty-result timing, then require a
2-second post-hit wait followed by five successful fixed verification
rounds.
- Require exact ordered topK IDs for whole-result comparisons and fail
immediately on non-empty unexpected results.
- Add regression coverage for the #51366 path with a post-created base
index and 512 total rows.

Related: #51366

## Test Plan

- `git diff --check`
- `ruff check
tests/python_client/milvus_client/test_add_function_field_feature.py`
- `ruff format --check
tests/python_client/milvus_client/test_add_function_field_feature.py`
- `python -m py_compile
tests/python_client/milvus_client/test_add_function_field_feature.py`
- Targeted E2E on `schema-evolution-v3-latest` using
`3.0-20260810-e2820e27`: 3 passed in 320.79s
- Full `test_add_function_field_feature.py` run after the 2-second wait
change on `schema-evolution-v3-latest`: 23 passed in 900.08s

---------

Signed-off-by: lyyyuna <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-08-14 18:00:24 +08:00