issue: https://github.com/milvus-io/milvus/issues/42148
1. Support range search in hybrid search
Enable element-level vector fields to participate in hybrid range search
while preserving element identity and offsets.
2. Support group-by in hybrid search, with constraints
Enable element-level hybrid search with PK group-by and preserve element
offsets. For group-by, sub-searches that need element identity merging
must stay within the same struct array semantic scope.
Also, it fixes 2 related bugs:
1. Fix group-by related issues
Fix lost element indices and incorrect merging behavior in query/search
group-by paths, so multiple matching elements under the same PK can
still be returned with correct offsets.
2. Fix aggregation output issues
Fix aggregation/count output corruption when a collection contains
struct array fields. Proxy struct-field reconstruction now preserves
virtual aggregation fields such as `count(*)` instead of treating
`field_id=0` as a struct sub-field. As a result, `id + count(*)`,
element-level `count(*)`, and group-by `count(*)` return correctly named
fields.
---------
Signed-off-by: SpadeA <tangchenjie1210@gmail.com>
## Description
The proxy access log recorded the collection field as `Unknown` for any
request that does not expose a singular `GetCollectionName() string`.
This made it impossible to attribute traffic to a specific collection
from access logs — most notably when diagnosing per-collection flush
rate-limit rejections
(`milvus_proxy_rate_limit_req_count{msg_type="DDLFlush",
status="fail"}`, default limit
`quotaAndLimits.flushRate.collection.max=0.1`).
Affected proxy-facing requests:
| Request | Collection field | Before | After |
|---------|------------------|--------|-------|
| `Flush` | `collection_names []string` | `Unknown` | `[coll_a coll_b]`
|
| `ShowCollections` | `collection_names []string` | `Unknown` | `[coll_a
coll_b]` |
| `RenameCollection` | `OldName` / `NewName` | `Unknown` | `old_coll` |
| `BatchDescribeCollection` | `collection_name []string` | `Unknown` |
`[coll_a coll_b]` |
## Root cause
`GrpcAccessInfo.CollectionName()` / `RestfulInfo.CollectionName()` only
used `requestutil.GetCollectionNameFromRequest`, which relies on the
singular `CollectionNameGetter` (`GetCollectionName() string`).
`FlushRequest`/`ShowCollectionsRequest` only have the repeated
`GetCollectionNames() []string`, so the assertion failed and the field
fell back to `Unknown`. `RenameCollectionRequest` and
`BatchDescribeCollectionRequest` carry the collection under non-standard
fields.
## Fix
- Add `CollectionNamesGetter` + `GetCollectionNamesFromRequest` to
`requestutil` (mirrors the existing `PartitionNamesGetter`).
- In `CollectionName()`, fall back to the plural getter, then to the
request-specific fields, mirroring the existing `PartitionName()` plural
handling.
- Unit test covering nil / singular / `Flush` / `RenameCollection` /
`BatchDescribe`.
Internal coordinator RPCs (`DescribeSegment`, `ShowSegments`) only carry
`CollectionID` and do not flow through the proxy access log, so they are
out of scope.
Fixes#50741🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
issue: #50698Fixes#50698
## What this PR does
This PR fixes NULL/UNKNOWN validity handling in boolean filter execution
paths:
- Makes conjunctive filter short-circuit decisions preserve rows that
are still UNKNOWN and may be resolved by later predicates.
- Treats UNKNOWN predicate results as filtered out when converting
predicate results to filter bitsets.
- Preserves nullable parent JSON validity for `!=` instead of producing
`data=true, valid=false` that can leak through raw-bit consumers.
- Fixes chunked sealed variable-length offset filtering so skipped NULL
rows keep invalid validity instead of becoming definite false.
## Verification
- `ninja unittest/all_tests` linked successfully.
- Focused C++ test run passed 109/109:
- `ConjunctExprTest.*`
- `*TestUnaryRangeWithJSONNullable*`
- `*TestUnaryRangeJsonNullable*`
- `git diff --check` passed.
- Staged diff check passed.
Signed-off-by: Buqian Zheng <zhengbuqian@gmail.com>
Add missing fileNum increments in garbage collector walkers so
walkFileNum metrics/logs are accurate for text and JSON cleanup paths.
Signed-off-by: Congqi Xia <congqi.xia@zilliz.com>
issue: #46308
## Summary
Keep lexical highlight corpus texts aligned with search result rows when
highlighted string fields are nullable or empty. Null and empty rows now
still produce highlight data with an empty fragments list.
Signed-off-by: aoiasd <zhicheng.yue@zilliz.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- Allow mixed normal/import compaction results to use output binlog
timestamp ranges even when the fallback start position is earlier than
an import commit timestamp.
- Normalize fallback-only start/dml positions to the max input commit
timestamp when output timestamps are unavailable.
- Add regression coverage for manual mix compaction with normal rows
preceding committed import rows.
Fixes#50464
## Test Plan
- [x] `go test -tags dynamic,test -gcflags="all=-N -l" -count=1
./internal/datacoord -run '^TestMeta$' -ldflags="-r ${RPATH}"`
- [x] `make static-check`
Signed-off-by: Yihao Dai <yihao.dai@zilliz.com>
## Summary
issue: #50579
Cache estimated index load resources on `LoadIndexInfo` so segment load
paths can reuse the same estimate for raw-data checks and resource
accounting.
## Changes
- Store the cached load resource estimate as
`std::optional<LoadResourceRequest>`.
- Reuse cached load-resource estimates in sealed index loading and V1
translator paths.
- Keep `CheckIndexHasRawData` inline with a direct `IndexFactory`
fallback instead of a separate helper.
- Add unit coverage for optional assignment, copy, and reset behavior.
## Test Plan
- [x] `/opt/homebrew/opt/llvm@18/bin/clang-format --dry-run --Werror
internal/core/src/segcore/SegmentLoadInfo.cpp
internal/core/src/segcore/SegmentLoadInfo.h`
- [x] `git diff --check upstream/master..HEAD`
- [x] `rg "GetIndexLoadResource|has_load_resource_request"
internal/core/src/segcore internal/core/unittest/test_loading.cpp`
- [x] `make run-test-cpp
filter=IndexLoadTest.LoadResourceRequestCacheIsOptional`
- [x] `make SKIP_3RDPARTY=1 build-cpp-with-unittest` passed before the
final inline cleanup; after the cleanup, the same target compiled
`SegmentLoadInfo.cpp` successfully and was then stopped before
completion.
Signed-off-by: sijie-ni-0214 <sijie.ni@zilliz.com>
## Summary
Fix QueryNode schema freshness checks by separating two ordering
domains:
- `CollectionSchema.version` is the logical schema freshness version.
QueryNode uses it to prevent old schema payloads from overwriting newer
fields/functions.
- `schema_barrier_ts` is the DDL/update barrier timestamp. QueryNode
uses it to fence stale load results and to refresh same-version schema
payloads, such as collection properties (`ttl_field`) that do not bump
`schema.version`.
Fixes#50364
## Update rules
When QueryNode receives a schema payload:
- If incoming `schema.version < current schema.version`, skip the update
even if `schema_barrier_ts` is larger. This prevents schema rollback
from out-of-order replay/channel delivery.
- If incoming `schema.version > current schema.version`, apply the
update.
- If incoming `schema.version == current schema.version`, apply the
update only when `schema_barrier_ts` is newer than the current barrier.
This allows properties-only schema snapshots, for example TTL field
changes, to refresh the runtime schema.
- If the schema payload is absent, fall back to `schema_barrier_ts` for
legacy/rolling-upgrade compatibility.
Segcore still receives a single schema update token, but QueryNode now
derives that token with collection-local monotonic ordering across both
logical schema versions and barrier timestamps. This keeps segcore
accepting updates when a high-barrier same-version refresh is followed
by a higher logical schema version that carries a smaller barrier
timestamp.
## Delegator side effects
`shardDelegator.UpdateSchema` now runs the same stale/no-op schema check
before updating workers, load barriers, function runtime state, IDF
oracle state, or local collection metadata. This keeps stale schema
messages from producing delegator side effects when the collection
manager would skip the schema payload.
The delegator load barrier remains timestamp-based and monotonic. A
newer logical schema version with a smaller barrier timestamp must not
reopen older load results after a previous same-version property refresh
advanced the barrier.
## Changes
- Rename the legacy QueryCoord fields to `schema_barrier_ts` to reflect
their timestamp-barrier semantics.
- Track both logical schema version and schema barrier timestamp in
QueryNode collection schema snapshots.
- Use `CollectionSchema.version` as the logical QueryNode collection
schema freshness version for load, sync, pipeline replay, and
UpdateSchema paths.
- Allow same-version schema payload refresh only when the barrier
timestamp advances, so properties-only changes such as `ttl_field` take
effect without release/load.
- Generate a monotonic segcore schema update token from the current and
incoming logical/barrier values.
- Skip stale/no-op schema payloads in delegator before worker updates
and other schema side effects.
- Keep `schema_barrier_ts` in delegator/pipeline load fencing so stale
load results are rejected after schema-changing DDL.
- Update logs, comments, and tests to distinguish `schemaVersion` from
`schemaBarrierTs`.
## Test Plan
- [x] `make generated-proto-without-cpp`
- [x] `git diff --check upstream/master...HEAD`
- [x] `cd pkg && go test -count=1 ./proto/querypb`
- [x] `go test -tags dynamic,test -gcflags="all=-N -l" -count=1
./internal/querycoordv2/task -run
'TestUtils/TestPackLoadMetaSchemaVersions'`
- [x] `go test -tags dynamic,test -gcflags="all=-N -l" -count=1
./internal/querynodev2/segments -run
'TestCollectionManager/(TestUpdateSchema|TestPutOrRefKeepsFreshCollectionInSchemaVersionDomain|TestLoadMetaSchemaVersionCompatibility)'`
- [x] `go test -tags dynamic,test -gcflags="all=-N -l" -count=1
./internal/querynodev2/delegator -run
'TestDelegatorSuite/TestUpdateSchema|TestUpdateSchema'`
- [x] `env 'etcd.auth.enabled=false' go test -tags dynamic,test
-gcflags="all=-N -l" -count=1 ./internal/querynodev2 -run
'TestQueryNodeService/TestUpdateSchema'`
- [x] `make static-check` with Go 1.26.4 and shared C++ build
environment before the final upstream rebase
Notes:
- Full `./internal/querynodev2/delegator` was also attempted locally. It
currently fails in unrelated
`TestDelegatorDataSuite/TestLoadPartitionStats` because partition stats
deserialization reports `json: cannot unmarshal into Go value of type
storage.VectorFieldValue`; the schema/update-focused tests above pass.
- Per maintainer request, the final post-rebase `make static-check` run
was skipped before pushing the latest commit.
---------
Signed-off-by: Yihao Dai <yihao.dai@zilliz.com>
## Summary
This PR fixes a DataCoord GC race where `recycleUnusedSegIndexes` could
delete segment index metadata using a stale `SegmentIndex` candidate
with no recorded index files, leaving the actual index files orphaned.
Changes:
- Reload the latest segment index metadata by build ID before GC removes
files/meta.
- Skip segment index GC for non-terminal index tasks.
- Keep finished segment index metadata when no index files are recorded,
instead of deleting meta with an empty file set.
- Add v1 orphan index file prefix cleanup for cases where metadata is
already missing.
issue: #50658
## Test Plan
- `GOTOOLCHAIN=auto make static-check`
- `go test -tags dynamic,test -gcflags="all=-N -l" -count=1
./internal/datacoord -run
'TestGarbageCollector_recycleUnusedSegIndexes|TestGarbageCollector_recycleUnusedIndexFilesV1'`
- `go test -tags dynamic,test -gcflags="all=-N -l" -count=1
./internal/datacoord -run 'TestGarbageCollector'`
Note: full `./internal/datacoord` was also run locally and failed only
on the existing unrelated
`TestCompactionTriggerManagerSuite/TestGetExpectedSegmentSize/all_DISKANN`
assertion (`expected 209715200`, `actual 104857600`).
Signed-off-by: Yihao Dai <yihao.dai@zilliz.com>
issue: #50377
## Problem
Standalone crashes with SIGSEGV (exit 139) when an `ST_WITHIN` query
runs on a nullable GEOMETRY field of a **growing** segment concurrently
with schema-evolution (add field).
The reported stack:
```
GISFunctionFilterExpr.cpp -> segment_->bulk_subscript(...)
SegmentGrowingImpl.cpp -> insert_record_.get_valid_data(field_id)
/usr/include/c++/12/bits/unordered_map.h:880 (SIGSEGV)
```
## Root cause
`InsertRecordGrowing` keeps the per-field columns in two plain
`std::unordered_map`s (`data_` / `valid_data_`).
- The query path (`SegmentGrowingImpl::bulk_subscript` → `get_data_base`
/ `get_valid_data`, and every other growing-segment reader that funnels
through them) reads these maps **without any lock**.
- Schema evolution (`Reopen` → `fill_empty_field` → `append_field_meta`
→ `append_data` / `append_valid_data`) `emplace`s into the maps, which
can **rehash** the bucket array.
- A rehash concurrent with a lock-free `find()` / `at()` on a query
thread walks freed bucket memory → SIGSEGV. The GIS `ST_WITHIN` path
just happens to be a frequent reader (`bulk_subscript`).
Only `Insert` (shared) and `Reopen` (unique) took `sch_mutex_`; the
query path took nothing, so nothing serialized a reader against the
rehash.
## Fix
Add a dedicated `field_map_mutex_` to `InsertRecordGrowing`:
- structural writers (`append_*`, `drop_field_data`, `clear`) take it
**unique**;
- lookups (`get_data_base`, `get_valid_data`, `is_data_exist`,
`is_valid_data_exist`) take it **shared**.
The `append_data` paths resolve their reader-dependent values before
taking the unique lock to avoid recursive locking on the non-recursive
`shared_mutex`. The mutex is kept separate from the existing
`shared_mutex_` (which serializes pk inserts) so the hot read path does
not contend with inserts. Because all growing-segment readers funnel
through these accessors, this covers `bulk_subscript`, vector search and
expression chunk reads uniformly.
## Test
`internal/core/unittest/test_growing_concurrent_reopen.cpp` races
single-offset `bulk_subscript` readers against `Reopen`-driven field
additions. It reliably fails before the fix (failed map lookup under
debug/ASan; SIGSEGV in Release) and passes after.
---------
Signed-off-by: cai.zhang <cai.zhang@zilliz.com>
Signed-off-by: Cai Zhang <cai.zhang@zilliz.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Issue
issue: #50549
## What this PR does
`tombstoneScheduler.AddPending` panicked with
`unreachable: tombstone scheduler is closing when adding pending
tombstone`
when `s.notifier.Context()` was already `Done()`.
That path **is** reachable under concurrent shutdown: an in-flight
`ackCallbackScheduler.doAckCallback` goroutine (spawned by
`triggerAckCallback`)
can call `AddPending` after `Close()` cancels the tombstone scheduler's
context.
The panic in a background goroutine crashes the whole process —
surfacing as a
flaky `ci-v2/ut-go` failure
(`TestForcePromoteFailover/no_incomplete_tasks`,
`panic: unreachable: tombstone scheduler is closing ...`).
## Why dropping the enqueue on shutdown is safe
`AddPending` only adds the broadcastID to the **in-memory** GC tracking
list.
The durable state is already written before we get here:
1. `doAckCallback` calls `bt.MarkAckCallbackDone(...)` which sets the
task state to
`BROADCAST_TASK_STATE_TOMBSTONE` and persists it (`saveTaskIfDirty`) —
*then*
calls `AddPending`.
2. On the next startup, `broadcast_manager` recovers all `TOMBSTONE`
tasks into
`tombstoneIDs` and re-seeds the GC list via `Initialize`.
So skipping the enqueue during shutdown only defers GC of that one
tombstone to
the next restart, which is already how GC behaves (lifetime + count
thresholds).
No data/cleanup is lost.
## Change
Replace the `panic` with a log + `return` when the context is already
cancelled.
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#### What this PR does / why we need it:
Improve QueryCoord balance stability by making scheduler segment task
deltas aware of current segment distribution.
When pending task deltas are not aligned with actual segment
distribution, balance policies can repeatedly shift plans based on stale
workload information. This PR tracks segment task delta effects with
action records and filters effects that have already been reflected in
distribution.
This also caches collection row count and delegator count in score
workload status to avoid repeated distribution scans, and applies the
updated segment delta snapshot to score and row-count balance paths.
Round-robin segment assignment keeps the lightweight segment-count only
ordering.
#### Which issue(s) this PR fixes:
Fixes#49860
#### Special notes for your reviewer:
MultiTargetBalancer is intentionally unchanged because it does not
consume scheduler segment task deltas in the original design.
#### Tests
- `gofumpt -l internal/querycoordv2/task/scheduler.go
internal/querycoordv2/assign/assign_policy_score.go
internal/querycoordv2/assign/assign_policy_rowcount.go
internal/querycoordv2/assign/assign_policy_roundrobin.go`
- `git diff --check`
- `go test -v -count=1 -tags dynamic,test -gcflags="all=-N -l"
-ldflags="-r ${MILVUS_WORK_DIR}/cmake_build/lib -r
${MILVUS_WORK_DIR}/internal/core/output/lib"
./internal/querycoordv2/assign -run
"Test(ScoreBasedAssignPolicy|RowCountBasedAssignPolicy|RoundRobinAssignPolicy)"
-timeout 300s`
- `go test -v -count=1 -tags dynamic,test -gcflags="all=-N -l"
-ldflags="-r ${MILVUS_WORK_DIR}/cmake_build/lib -r
${MILVUS_WORK_DIR}/internal/core/output/lib"
./internal/querycoordv2/task -run
"TestTask/(TestCalculateTaskDelta|TestSegmentTaskDeltaWithDistFilter|TestChannelTaskDeltaCache)"
-timeout 300s`
---------
Signed-off-by: sijie-ni-0214 <sijie.ni@zilliz.com>
related: #46442
This commit adds proper RBAC (Role-Based Access Control) support for the
/expr HTTP endpoint, replacing the previous root-only authentication.
Changes:
- Add new rbac.go with CheckPrivilege function for HTTP endpoints
- Support HTTP Basic Auth only (removed non-standard Bearer token
format)
- Integrate with existing Casbin RBAC framework
- Add PrivilegeExpr to GlobalLevelPrivileges and ClusterAdminPrivileges
- Register GetUserRoleFunc callback in meta_cache.go
- Update tests for new RBAC behavior
Security features:
- Authentication required when authorization is enabled
- Root user bypass when RootShouldBindRole is false
- Proper 401/403 status code differentiation
- Integration with privilege result cache
---------
Signed-off-by: shaoting-huang <shaoting.huang@zilliz.com>
issue: #50444
## What this PR does
Expression evaluation is batch-driven (`PhyExpr::Eval()` once per
8192-row
batch, ~122× per 1M-row segment). The scalar-index **ByOffsets**
validity paths
recomputed per-row work every batch that does not change within a query.
This
removes three sources of it (all in `Expr.h`):
### 1. Cache `IsNotNull()` across batches (98.6x) — the core
`ScalarIndex::IsNotNull()` rebuilds a segment-sized bitmap on every call
(allocation + fill + AND, ~500KB traffic for 1M rows). The three
ByOffsets paths
(`ProcessIndexChunksByOffsets`, `ProcessIndexLookupByOffsets`,
`ProcessChunksForValidByOffsets`) called it once per batch, while the
ChunksAll
paths already cache it. Add `GetCachedIndexValidBitmap()`
(expression-instance
cache + precomputed all-valid flag). Measured **3.32ms → 0.03ms** per
expression
per 1M-row segment. All three callers assert `num_index_chunk_ == 1`, so
the
cached answer cannot change within a query.
### 2. All-valid fast paths
When the column has no nulls (the common case), replace the per-row
random
bitmap reads with one bulk `set()`, or skip the fill loop entirely (the
output
bitmap is pre-set).
### 3. Avoid chained proxy assignment
In `ProcessIndexLookupByOffsets`, `res[i] = valid_res[i] = v` forces a
store-to-load read-back of the word just written; materialize the bool
once.
## Scope note
This PR was split. The `ApplyValidData` vectorization (64-row pack +
`countr_zero`)
and its rollout to the per-kernel validity-masking loops across the
expression
kernels are handled in a follow-up PR; the `OffsetMapping`
`all()/none()`
vectorization landed separately in #50468.
## Tests
- `Expr.*:ExprTest.*:*Null*:OffsetMapping.*` re-run: passing (2047
tests).
- Microbenchmarks (Xeon Gold 6338) as quoted above.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: xiaofanluan <xiaofan.luan@zilliz.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## What this PR does
Pins `rapidjson` to the newest `cci.20230929` snapshot in
`internal/core/conanfile.py` so the C++ core builds under **clang 19 /
libc++ 19** (Homebrew LLVM on macOS Apple Silicon).
## Why
Arrow 17 pulls **`rapidjson/1.1.0`** (a 2016 release) as a transitive
dependency. Its headers fail to compile under clang 19 / libc++ 19.
Milvus core includes Arrow's JSON headers, which transitively pull in
rapidjson, so the **core build** breaks under clang 19 even though the
Arrow binary itself is consumed prebuilt.
This is the macOS/clang-19 path that was not exercised by #50616's Linux
CI (which resolves a prebuilt Arrow binary and never source-includes the
old rapidjson under clang 19).
## How
```python
self.requires("rapidjson/cci.20230929#0a3982e5f4fa453a9b9cd0dd5b1dcb3a", force=True)
```
- `force=True` overrides Arrow's transitive `rapidjson/1.1.0`.
- rapidjson is **header-only**, so this does **not** invalidate the
prebuilt Arrow `package_id` — `conan graph info` confirms Arrow stays
`Cache`-resolved.
- Recipe revision is pinned from the milvus production remote
(`default-conan-local2`), consistent with how arrow/azure/snappy/lz4 are
pinned.
## Verification
- `conan graph info` resolves cleanly: `rapidjson/1.1.0 ->
rapidjson/cci.20230929#0a3982e5...`, no conflict, Arrow unchanged.
- Full core build passes under macOS arm64 / Homebrew clang 19.
issue: #50615🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
issue: #50615
## What this PR does
Makes the C++ core build cleanly on **macOS (Apple Silicon) with
Homebrew LLVM/clang 19**.
> [!IMPORTANT]
> **Scope / impact is NOT macOS-only.** Although this PR is framed as
macOS/clang-19 enablement, two of the changes are **top-level
`requires(...)` in `internal/core/conanfile.py`, so they apply to every
platform, including Linux production builds:**
> - **`azure-sdk-for-cpp` `1.11.3@milvus/dev` → `1.16.0@milvus/dev`** (5
minor versions, `force=True`). This is the SDK Arrow uses for the
**Azure Blob remote-storage path**, so this is effectively a major Azure
SDK bump on the **production Azure object-storage path for Linux
deployments** too.
> - **`arrow` recipe `17.0.0@milvus/dev-2.6#c743ea7a…` →
`17.0.0@milvus/dev#f411aa73…`** — same Arrow version, but **rebuilt
against azure 1.16.0** with a patched `FindAzure.cmake`. The Arrow
**`package_id` (recipe revision) DID change**; it is not the same binary
as before.
> - **`libbson/1.30.6@milvus/dev`** added (also global, but new — no
pre-existing behavior to regress).
>
> **Testing blind spot:** CI green proves compile/link/UT/e2e pass, but
the UT and integration suites **do not exercise real Azure Blob
read/write**, so the Azure object-storage path is **not covered** by
this PR's CI. A real Azure Blob remote-storage regression is recommended
before merge (see Verification).
### Why the dependency bumps are required for clang 19
- **azure 1.16.0**: azure 1.11.3 bundles an older `nlohmann/json` that
uses `std::char_traits<unsigned char>`, which was **removed in libc++
19** → 1.11.3 fails to compile on clang 19. 1.16.0 fixes this.
- **arrow rebuilt against azure 1.16.0**: when Arrow is built from
source on clang 19 it must link the same azure 1.16.0; the recipe also
patches `FindAzure.cmake` to the monolithic `Azure` config so
`find_package(Azure)` resolves. Hence a new Arrow recipe revision.
### Toolchain / build enablement
- **`scripts/setenv.sh`**: probe LLVM 19/20/21 first (libc++ 17/18
`chrono operator<<` clashes with Arrow's vendored `date`; libc++ 19
fixes it).
- **`scripts/3rdparty_build.sh`**: arm64 `-march=armv8-a+crypto+crc` so
folly's F14 (SimdAndCrc on Apple Silicon) matches core/knowhere — fixes
`F14LinkCheck` undefined-symbol link errors.
- **`InvertedIndexTantivy.cpp`**: drop spurious `->template` on
non-template members (clang 19
`-Wmissing-template-arg-list-after-template-kw`).
### Drop bsoncxx / libmongoc → libbson only
The BSON layer used the **bsoncxx** C++ driver, whose CMake
unconditionally builds mongo-cxx-driver → **libmongoc** → bundled
**utf8proc**, which fails to configure under newer CMake/clang on macOS.
Milvus only needs BSON (de)serialization.
- New Conan package **`libbson/1.30.6@milvus/dev`** (libbson-only,
`ENABLE_MONGOC=OFF`; recipe published to milvus conanfiles &
production).
- New **`src/common/bson_shim.h`** — a thin `milvus::bson` C++ layer
over libbson's C API.
- Migrated `bson_view.h`, `bson_builder.{h,cpp}`, json_stats consumers
and unit tests off bsoncxx.
- CMake: `find_package(bson-1.0)` + link `mongo::bson_shared`; removed
FetchContent `thirdparty/bsoncxx`.
All three global Conan requires (arrow / libbson / azure) pin an
explicit recipe `#revision` for reproducibility.
## Verification
- Full clean rebuild on macOS arm64 / clang 19: `bin/milvus` builds;
`libmilvus_core` links **only `libbson-1.0.dylib`** (no
libmongoc/mongocxx/utf8proc).
- libbson / arrow / azure resolve from production Conan at the pinned
revisions.
- All 21 BSON unit tests (BsonView / BsonBuilder / DomNode) pass.
- Full ci-v2 suite green on Linux (build / build-ut-cov / ut-cpp / ut-go
/ integration / e2e / go-sdk).
- **TODO before merge:** real Azure Blob remote-storage read/write
regression to cover the azure-1.16.0 / arrow-rebuild blast radius (not
exercised by UT/integration).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
`TestRestfulStructArrayNullable::test_rest_v2_nullable_struct_array_schema_propagation`
was marked `xfail(strict=True)` for *'REST v2 schema.structFields
nullable=true is silently ignored instead of preserved in describe
output'*.
The behavior now correctly preserves `nullable=true` on struct array
fields in the describe output, so the test passes. With `strict=True` an
unexpected pass becomes `XPASS(strict)` which pytest reports as
**FAILED**, breaking `ci-v2/e2e-default`:
```
[XPASS(strict)] REST v2 schema.structFields nullable=true is silently ignored instead of preserved in describe output
```
This was masked until the file's bad `L1` marker was fixed (#50651);
once the file is collectable the stale xfail surfaces. Remove the
decorator so the test runs as a normal positive case validating the (now
correct) nullable propagation. Introduced by #50109.
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
Linking the C++ unit-test binaries fails on Linux with `multiple
definition of 'XXH...'`, breaking `ci-v2/build-ut-cov` (UT-CPP-Cov
stage) and `ci-v2/ut-cpp`:
```
FAILED: unittest/test_json_uint64
... multiple definition of `XXH32';
thirdparty/milvus-storage/.../rust/librust_bridge.a(...xxhash.o): first defined here
collect2: error: ld returned 1 exit status
```
`test_json_uint64` is just the first test binary linked — any target
linking both `milvus_conan_deps` and `milvus-storage` trips it.
## Root cause
Two copies of xxhash on the same static link line:
1. `milvus-storage`'s Rust bridge (`librust_bridge.a`) statically embeds
xxhash and **re-exports** its `XXH*` symbols globally.
2. The conan `xxhash/0.8.3` that `milvus_core` legitimately links for
`common/BloomFilter.h` and `minhash/MinHashComputer.cpp`.
The GNU linker errors on the duplicate global symbols. This surfaced
after the `milvus-common`/Knowhere bump in #50612 rebuilt the in-tree
milvus-storage Rust bridge with the embedded xxhash; it is cache-state
dependent, so unrelated PRs (#50616, #50645, #50636) hit the identical
error intermittently.
## Changes
- Add `-Wl,--allow-multiple-definition` (via
`add_link_options(LINKER:...)`, guarded by `if(LINUX)`) in
`internal/core/unittest/CMakeLists.txt`, so the linker keeps the first
xxhash definition for all unit-test targets.
## Follow-up
Proper root fix tracked in #50648: localize/hide the duplicate
third-party `XXH*` symbols in `librust_bridge.a` (symbol version script
/ `objcopy --localize-symbols`) in milvus-storage so it doesn't export a
second copy of xxhash.
issue: #50648🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
`TestCreateImportJobNegative::test_import_job_with_non_exist_files` is
flaky in `ci-v2/e2e-default`:
```
FAILED testcases/test_jobs_operation.py::TestCreateImportJobNegative::test_import_job_with_non_exist_files
AssertionError: assert 'Importing' == 'Failed'
```
## Root cause
The test created an import job for a non-existent file, slept a **fixed
5 seconds**, then asserted the job `state == "Failed"`:
```python
rsp = self.import_job_client.create_import_jobs(payload)
time.sleep(5)
rsp = self.import_job_client.get_import_job_progress(rsp["data"]["jobId"])
assert rsp["data"]["state"] == "Failed"
```
The import job advances `Pending -> Importing -> Failed`
**asynchronously** — it only becomes `Failed` after the datacoord import
scheduler actually attempts to read the missing file and propagates the
failure. On a fast/idle runner 5s is enough; under CI load the job is
still `Importing` at 5s, so the assertion fails. It's a test-side race,
not a product bug (the import does eventually fail).
## Fix
Poll the job progress until it reaches a terminal state
(`Failed`/`Completed`), bounded by the existing `IMPORT_TIMEOUT`, then
assert it failed — matching how every other import test in this file
waits. No production behavior change.
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
`TestRestfulStructArrayNullable::test_rest_v2_add_struct_array_field_rejected`
fails deterministically in `ci-v2/e2e-default`:
```
AssertionError: assert 'Invalid field type, type:ArrayOfStruct' in
'StructArray field must be added through /v2/vectordb/collections/struct_fields/add: invalid parameter'
```
The test verifies that adding an `ArrayOfStruct` field via the regular
`/collections/fields/add` endpoint is rejected. The rejection works
correctly (non-zero code, field not added, `structFields == []`), but
the test asserted a **stale error-message substring** (`Invalid field
type, type:ArrayOfStruct`). The server actually returns a clearer
message directing the caller to the dedicated endpoint: `StructArray
field must be added through /v2/vectordb/collections/struct_fields/add`.
This assertion was masked until now: the file failed collection on a bad
`L1` marker (fixed in #50651), so none of its tests ran. Once the file
is collectable, the stale assertion fails.
## Fix
Assert the stable `struct_fields/add` substring, which still verifies
the rejection guides the caller to the correct endpoint. Introduced by
#50109.
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
`tests/restful_client_v2/testcases/test_struct_array_nullable.py`
decorated its test class with a **bare `@pytest.mark.L1`** instead of
the suite convention `@pytest.mark.tags(CaseLabel.L1)`.
restful_client_v2's `pytest.ini` runs with `addopts = --strict`, which
turns any unregistered marker into a hard error. `L1` is not a
registered marker (only `tags` is), so pytest fails at **collection
time**:
```
ERROR collecting testcases/test_struct_array_nullable.py
Failed: 'L1' not found in `markers` configuration option
```
With `-x` (stop on first failure) this aborts the entire
`ci-v2/e2e-default` run — deterministically, on master and on every PR
whose e2e matrix includes the restful_client_v2 suite. Separately, the
bare marker also would not be matched by the framework's
`pytest_tagging` `--tags L1` level selection, so the level tag had no
effect anyway.
## Changes
- Import `CaseLabel` from `utils.constant`.
- Replace `@pytest.mark.L1` with `@pytest.mark.tags(CaseLabel.L1)`,
matching every other file in the suite.
Introduced by #50109.
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Summary
Bug #50424 — *"dropping a TextEmbedding function incorrectly removes the
output vector field"* — was fixed and closed by #50471 ("fix: Split
function drop semantics by request flag").
The three `drop_collection_function` e2e cases in
`test_text_embedding_function_e2e.py` were still marked
`@pytest.mark.xfail(strict=True)` referencing #50424. Now that the
behavior is correct, the tests **pass**, and with `strict=True` pytest
reports them as `XPASS(strict)` → **FAILED**, breaking
`ci-v2/e2e-default` on `master` and on every open PR.
```
[XPASS(strict)] issue: .../issues/50424, dropping TextEmbedding function incorrectly removes output vector field
= 3 failed, 5857 passed, 339 skipped, 2 xfailed, 1 xpassed, 9 rerun =
```
This is deterministic (not flaky): the `DataNotMatchException: Insert
missed an field 'dense'` log lines are the tests' own expected
negative-path assertions (after detaching the function, inserting
without the vector must fail).
## Changes
- Remove the three `@pytest.mark.xfail(...)` decorators from:
-
`TestTextEmbeddingFunctionCURD::test_drop_collection_function_verify_crud`
-
`TestTextEmbeddingFunctionCURD::test_drop_collection_function_one_of_multiple`
-
`TestTextEmbeddingFunctionCURD::test_drop_collection_function_then_add_again`
- Remove the now-unused `DROP_TEXT_EMBEDDING_FUNCTION_XFAIL_REASON`
constant.
The tests now run as normal positive cases validating the fixed
drop-function semantics from #50471.
issue: #50646🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## What type of PR is this?
/kind test
## What this PR does / why we need it:
Adds RESTful import 2PC coverage for Milvus import lifecycle and
CDC-focused scenarios.
This PR adds:
- REST import job helpers for `/describe`, `/commit`, `/abort`, and
state polling
- pytest options for secondary CDC endpoint/object storage/topology
parameters
- a new REST test suite for import 2PC lifecycle, visibility,
commit/abort, delete semantics, TTL, compaction, formats, data types,
indexes, partitions, DML interleaving, CDC, and fault-tolerance gates
- xfail regression coverage for known issues:
- #50458 REST import commit/abort authorization gap
- #50459 NaN/Inf FloatVector import/query behavior
- #50460 REST `options.auto_commit=null` validation gap
## Which issue(s) this PR fixes:
N/A
## Special notes for your reviewer:
The CDC cases require passing secondary cluster REST/MinIO options and
topology arguments. Without those options, CDC-specific cases skip with
an explicit environment precondition message.
The manual compaction-after-commit case is marked non-strict xfail
because compaction may remain `Executing` on current import 2PC test
builds.
## Test result:
```text
python -m py_compile tests/restful_client_v2/conftest.py tests/restful_client_v2/api/milvus.py tests/restful_client_v2/testcases/test_import_2pc_operation.py
ruff check tests/restful_client_v2/api/milvus.py tests/restful_client_v2/conftest.py tests/restful_client_v2/testcases/test_import_2pc_operation.py
python -m pytest --collect-only -q testcases/test_import_2pc_operation.py
141 tests collected in 2.14s
```
---------
Signed-off-by: zhuwenxing <wenxing.zhu@zilliz.com>
## Summary
- Upgrade Knowhere dependency to v3.0.4.
- Upgrade milvus-common dependency to the version required by Knowhere
v3.0.4.
issue: #50610
Signed-off-by: xianliang.li <xianliang.li@zilliz.com>
## Summary
- Disable the proxy search requery path when a collection uses namespace
partition mode.
- Apply the same behavior to normal search and hybrid search
initialization.
- Add coverage for the Always requery policy being overridden by
namespace partition mode.
issue: #50626
---------
Signed-off-by: sunby <sunbingyi1992@gmail.com>
## Summary
Enable the DataNode vortex storage format in the Helm values that
already enable Loon FFI storage:
- tests/_helm/values/e2e/standalone
- tests/_helm/values/e2e/distributed-pulsar
- tests/_helm/values/e2e-amd/standalone
- tests/_helm/values/e2e-amd/distributed-pulsar
- tests/_helm/values/e2e-arm/standalone
- tests/_helm/values/e2e-arm/distributed-pulsar
- tests/_helm/values/nightly/distributed-woodpecker
- tests/_helm/values/nightly/distributed-woodpecker-service
Fixes#50441
## Test Plan
- [x] ruby YAML parse check for the eight target values files, verifying
common.storage.useLoonFFI is true and dataNode.storage.format is vortex
- [x] git diff scope check confirmed only the two nightly woodpecker
files were added in the follow-up amend
Signed-off-by: Yanliang Qiao <yanliang.qiao@zilliz.com>
Fixes#50424
## Summary
This PR uses the `DropFunctionOutputFields` request flag to let
`AlterCollectionSchema` distinguish two client-side drop paths:
- `drop_collection_function` detaches the function and keeps its output
fields as normal schema fields.
- `drop_function_field` sets the flag and cascades supported
BM25/MinHash output fields.
MinHash detach keeps the output field because the MinHash signature is a
materialized `BinaryVector` field. Insert and compaction paths persist
the generated signature, raw retrieval is only blocked for BM25 function
outputs, and after detach the MinHash output field is kept with
`IsFunctionOutput=false` so it can stand alone as a normal vector field.
The proxy validation and RootCoord schema mutation logic now follow the
requested semantics, with tests covering detach-only and
output-field-drop behavior.
## Tests
- `make generated-proto-without-cpp`
- `make`
- `go test -v -count=1 -tags dynamic,test -gcflags="all=-N -l"
./internal/proxy -run
"TestAlterCollectionSchemaTask_PreExecute|TestValidateDropFunction"
-timeout 300s`
- `go test -v -count=1 -tags dynamic,test -gcflags="all=-N -l"
./internal/rootcoord -run
"TestBuildSchemaForDetachFunction|TestBuildSchemaForDropFunctionField"
-timeout 300s`
- `/Users/zilliz/dev/milvus_py`: `pytest
test_drop_collection_field.py::test_drop_function_field_bm25_cascades_output_field
test_drop_collection_field.py::test_drop_collection_function_detaches_minhash_output_field
test_drop_collection_field.py::test_drop_whole_struct_array_field_basic
-v -s`
Signed-off-by: sijie-ni-0214 <sijie.ni@zilliz.com>
## Summary
Decouple QueryCoord task dispatch from the dist pull loop.
When a QueryNode holds more and more segments, `GetDataDistribution`
returns a larger distribution and `pullDist` can take longer. If
dispatch only happens after dist pull finishes, QueryCoord task dispatch
frequency drops together with pull latency, which can hurt scheduling
stability. This PR keeps dispatch cadence independent from dist pull
latency.
issue: #50158
## Changes
- Add `queryCoord.dispatchInterval` with a default value of 500ms.
- Start separate dist pull and task dispatch loops in each dist handler.
- Keep `pullDist` and `handleDistResp` focused on updating distribution
state.
- Remove the old dispatch flag from dist response handling.
## Tests
- `gofumpt -l internal/querycoordv2/dist/dist_controller.go
internal/querycoordv2/dist/dist_controller_test.go
internal/querycoordv2/dist/dist_handler.go
internal/querycoordv2/dist/dist_handler_test.go
pkg/util/paramtable/component_param.go`
- `git diff upstream/master..HEAD --check`
- `go test -count=1 ./util/paramtable`
- `go test -v -count=1 -tags dynamic,test -gcflags="all=-N -l"
-ldflags="-r ${MILVUS_WORK_DIR}/cmake_build/lib -r
${MILVUS_WORK_DIR}/internal/core/output/lib"
./internal/querycoordv2/dist -run
"TestDistControllerSuite/TestSyncAll|TestDispatchLoopUsesDispatchInterval"
-timeout 300s` blocked locally because this worktree has no
`internal/core/output/lib/pkgconfig/milvus_core.pc`.
---------
Signed-off-by: sijie-ni-0214 <sijie.ni@zilliz.com>
issue: https://github.com/milvus-io/milvus/issues/50476
This PR rejects dropping a field when it is referenced by the collection
`ttl_field` property. Users need to drop the property first, which
avoids leaving dangling entity-level TTL metadata after Drop Field.
Validation:
- gofumpt -l internal/proxy/task.go internal/proxy/task_test.go
- git diff --check
- TestValidateDropField
Signed-off-by: sijie-ni-0214 <sijie.ni@zilliz.com>
## Summary
- Allow AlterCollectionSchema AddRequest to carry a field-only add
request while keeping the added field as a non-function output.
- Support function-only add requests against existing output fields, and
validate the merged function schema through the shared function
validator.
- Keep the BM25 field-plus-function path explicit, reject invalid BM25
add forms, and normalize TIMESTAMPTZ default values before broadcasting
field-only schema changes.
issue: #50119
## Test Plan
- [x] source scripts/setenv.sh && go test -v -count=1 -tags dynamic,test
-gcflags="all=-N -l" -ldflags="-r ${MILVUS_WORK_DIR}/cmake_build/lib -r
${MILVUS_WORK_DIR}/internal/core/output/lib" ./internal/proxy -run
'TestAlterCollectionSchemaTask($|_)' -timeout 300s
- [x] gofumpt -l internal/proxy/function_task.go
internal/proxy/function_task_test.go internal/proxy/task.go
internal/proxy/task_test.go internal/proxy/util.go
internal/proxy/util_test.go
internal/rootcoord/ddl_callbacks_alter_collection_schema.go
internal/rootcoord/ddl_callbacks_alter_collection_schema_test.go
internal/util/function/validator/validator.go
- [x] git diff --check upstream/master..HEAD
- [ ] source scripts/setenv.sh && go test -v -count=1 -tags dynamic,test
-gcflags="all=-N -l" -ldflags="-r ${MILVUS_WORK_DIR}/cmake_build/lib -r
${MILVUS_WORK_DIR}/internal/core/output/lib" ./internal/rootcoord -run
'TestDDLCallbacksBroadcastAlterCollectionSchema|TestDDLCallbacksAlterCollectionSchemaAddSkipsSchemaDropReady'
-timeout 300s (blocked locally at link time by stale C++/Loon symbols
such as _FreeCFieldMemSizeList and _loon_segment_writer_* in local core
libraries)
Signed-off-by: sijie-ni-0214 <sijie.ni@zilliz.com>
Related to #50591
Fix dropped-segment JSON path reconstruction by splitting legacy
json_key_index_log and new-format json_stats on JsonKeyStatsDataFormat.
Extend recycleUnusedBinlogFiles to scan text_log, legacy
json_key_index_log, and json_stats with path-specific segment ID parsing
while keeping V3 manifest-based GC unchanged.
Signed-off-by: Congqi Xia <congqi.xia@zilliz.com>
Honor top-level partitionNames for REST v2 hybrid_search so it only
searches requested partitions.
issue: #50396
---------
Signed-off-by: sunby <sunbingyi1992@gmail.com>
issue: #50542
## Summary
Keep cloned empty BM25 stats writable so recovered growing segments
cannot register nil stats in the IDF oracle. Add a regression test for
updating a growing segment after empty BM25 stats registration.
Signed-off-by: aoiasd <zhicheng.yue@zilliz.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
### What this PR does
Adds Python client and REST coverage for struct array nullable fields,
including dynamic field scenarios and nullable vector config variants.
### Tests
Not run; PR creation only.
---------
Signed-off-by: zhuwenxing <wenxing.zhu@zilliz.com>
issue: #50557
Summary:
- Add and validate `namespace.sharding.enabled` collection property.
- Default unset property to `false`.
- Reject changing or deleting it via alter collection properties.
Signed-off-by: sunby <sunbingyi1992@gmail.com>
## Summary
- Add `namespace.mode` collection schema property helpers with
`partition_key` as the default mode.
- Accept only `partition_key` and `partition` namespace modes; reject
invalid values such as `multitenant`.
- Validate namespace mode during collection creation so valid values are
persisted in `CollectionSchema.Properties`.
issue: #50589
---------
Signed-off-by: sunby <sunbingyi1992@gmail.com>