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]>
Integration test
This folder contains the integration test for Milvus components.
How it works
The Milvus integration test framework is a comprehensive testing solution that runs multiple Milvus components as separate processes to simulate a real deployment environment. It provides a MiniClusterV3 that manages the lifecycle of core Milvus components including MixCoord, Proxy, DataNode, QueryNode and StreamingNode.
The framework allows developers to:
- Start/stop a new milvus cluster
- Start/stop individual Milvus components
- Monitor component states and metadata through etcd
- Execute end-to-end test scenarios
- Execute the method of any component from its client
- Simulate component failures and recovery
- Modify the milvus configuration at runtime or startup
The test framework is built on top of Go's testing package and the testify/suite framework, making it easy to write structured and maintainable integration tests.
How to run integration test locally
Because integration test is a multi-process framework, it requires some components to start:
- a built milvus binary
- etcd
- minio
- pulsar
Build the milvus binary first.
make milvus
# test framework will use the env `MILVUS_WORK_DIR` to find the milvus binary.
# already done in the scripts/setenv.sh
# or you can set it manually
export MILVUS_WORK_DIR=$(pwd)
Run the docker compose to start the etcd, minio and pulsar.
cd [milvus-folder]/deployments/docker/dev && docker compose up -d
Run the integration test.
make integration-test
If you want to run single test case, you could execute command like this example
# mq, etcd, minio ready before
cd [milvus-folder]
source scripts/setenv.sh
cd tests/integration/[testcase-folder]/
go test -run "$testCaseName^" -testify.m "$subTestifyCaseName^" -race -v
Recommended coding style for add new cases
Should I add a new test case into integration?
It's a good choice to add a new test case into integration test if:
- The test case need to control the lifecycle of Milvus components, such as starting/stopping components or modifying configuration then executing some end-to-end scenarios.
- If the test case is hard to apply in the unit-test and E2E test, such as testing in a non-default configured Milvus cluster, and need to be tested between multiple components.
Should not add a new test case into integration test if:
- Function-verification that can be covered by unit test or already be covered by E2E test.
- Performance test.
Using suite
MiniClusterandMiniClusterSuite` provides lots of comment preset tool function to execute intergration test.
It is recommend to add a new test with testify/suite
import (
// ...
"github.com/milvus-io/milvus/tests/integration"
)
type NewSuite struct {
integration.MiniClusterSuite
}
// Setups and teardowns, optional if no custom logic needed
// example to suite setup & teardown, same logic applies to test setup&teardown
func (s *NewSuite) SetupSuite() {
s.MiniClusterSuite.SetupSuite()
// customized setup
}
func (s *NewSuite) TearDownSuite() {
s.MiniClusterSuite.TearDownSuite()
// customized teardown
}
A suite will start a new empty milvus cluster, and the cluster will be reused for all test cases in the suite. We recommend to add more useful utility methods into MiniClusterSuite or MilvusClusterV3 to interact with the cluster, to speed up the integration test development.
Some utility methods are provided in MiniClusterSuite to interact with the cluster:
method of MiniClusterSuite
- Use
s.WithMilvusConfigto modify the milvus configuration at startup inSetupSuitemethod. - Use
s.WithOptionsto modify the test options at startup inSetupSuitemethod. - Use
s.Clusterto get theMiniClusterV3instance, which provides methods to interact with the Milvus cluster. - Some useful milvus method is provided in
util_files, such asCreateCollection,Insert,Flush...,
method of MiniClusterV3
- Use
s.Cluster.MustModifyMilvusConfigto modify the milvus configuration at runtime, it will return a guard function to restore the modified configuration. It doesn't promise that the configuration will be applied immediately, milvus may not support the dynamic configuration change for some configurations or some configuration may be applied slowly. - Use
s.Cluster.Add*to add components to the cluster, such asAddMixCoord,AddProxy,AddDataNode,AddQueryNode,AddStreamingNode. it will return theMilvusProcessobject to manage the lifetime of new incoming component. It will block until the component is healthy by default, useWithoutWaitForReadyoption to avoid it. - Use
s.Cluster.Default*to get the default component, such asDefaultMixCoord,DefaultProxy,DefaultDataNode,DefaultQueryNode,DefaultStreamingNode. - Use
s.*Clientto get the grpc client of the default component that can be got froms.Cluster.Default*, such ass.MixCoordClient,s.ProxyClient,s.DataNodeClient,s.QueryNodeClient,s.StreamingNodeClient. - Use
s.MilvusClientto get the grpc client of the Milvus server, which is connnected to the proxy that is returned fromDefaultProxy().
method of MilvusProcess
- Use
p.MustGetClientto get the grpc client of the component, which provides methods to interact with the component. - Use
p.MustWaitForReadyto wait for the component to be ready, it will block until the component is healthy. - Use
p.Stopto stop the component, it will block until the component is stopped. It will perform a graceful shutdown by default. When the given deadline is excceed,ForceStopis performed. - Use
p.ForceStopto force stop the component, it will not wait for the component to be stopped. - Use
p.IsWorkingto check if the component is working, it will return false if the component is stopped.
New folder for each new scenario
It's a known issue that integration test cases run in same process might affect due to some singleton component not fully cleaned.
As a temp solution, test cases are separated into different packages to run independently.
Some tips
- Sometimes, if the test case is killed by some SIGKILL, it will leave some orphan milvus process running in the background. You could use
killall milvusto kill all milvus process, orkillall -9 milvusto kill all milvus process forcefully. - Because the test framework use some determined port (such as 53100 for coord, 19530 for proxy), it will be failed to start a new milvus process if the port is already in use. You could use
lsof -i :53100to check if the port is already in use. - The test coverage of milvus can not be generated by the integration test, because that the integration test use multi-process. the test coverage only cover the code that is executed by the integration test itself.