Zhen Ye c7b5c23ff6
enhance: filter the empty timetick from consuming side (#46541)
issue: #46540

Empty timetick is just used to sync up the time clock between different
component in milvus. So empty timetick can be ignored if we achieve the
lsn/mvcc semantic for timetick. Currently, some components need the
empty timetick to trigger some operation, such as flush/tsafe. So we
only slow down the empty time tick for 5 seconds.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
- Core invariant: with LSN/MVCC semantics consumers only need (a) the
first timetick that advances the latest-required-MVCC to unblock
MVCC-dependent waits and (b) occasional periodic timeticks (~≤5s) for
clock synchronization—therefore frequent non-persisted empty timeticks
can be suppressed without breaking MVCC correctness.
- Logic removed/simplified: per-message dispatch/consumption of frequent
non-persisted empty timeticks is suppressed — an MVCC-aware filter
emptyTimeTickSlowdowner (internal/util/pipeline/consuming_slowdown.go)
short-circuits frequent empty timeticks in the stream pipeline
(internal/util/pipeline/stream_pipeline.go), and the WAL flusher
rate-limits non-persisted timetick dispatch to one emission per ~5s
(internal/streamingnode/server/flusher/flusherimpl/wal_flusher.go); the
delegator exposes GetLatestRequiredMVCCTimeTick to drive the filter
(internal/querynodev2/delegator/delegator.go).
- Why this does NOT introduce data loss or regressions: the slowdowner
always refreshes latestRequiredMVCCTimeTick via
GetLatestRequiredMVCCTimeTick and (1) never filters timeticks <
latestRequiredMVCCTimeTick (so existing tsafe/flush waits stay
unblocked) and (2) always lets the first timetick ≥
latestRequiredMVCCTimeTick pass to notify pending MVCC waits;
separately, WAL flusher suppression applies only to non-persisted
timeticks and still emits when the 5s threshold elapses, preserving
periodic clock-sync messages used by flush/tsafe.
- Enhancement summary (where it takes effect): adds
GetLatestRequiredMVCCTimeTick on ShardDelegator and
LastestMVCCTimeTickGetter, wires emptyTimeTickSlowdowner into
NewPipelineWithStream (internal/util/pipeline), and adds WAL flusher
rate-limiting + metrics
(internal/streamingnode/server/flusher/flusherimpl/wal_flusher.go,
pkg/metrics) to reduce CPU/dispatch overhead while keeping MVCC
correctness and periodic synchronization.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: chyezh <chyezh@outlook.com>
2026-01-06 20:53:24 +08:00
..
2025-09-16 16:32:01 +08:00
2025-09-16 16:32:01 +08:00

WAL

wal package is the basic defination of wal interface of milvus streamingnode. wal use github.com/milvus-io/milvus/pkg/streaming/walimpls to implement the final wal service.

Project arrangement

  • wal
    • /: only define exposed interfaces.
    • /adaptor/: adaptors to implement wal interface from walimpls interface
    • /utility/: A utility code for common logic or data structure.
  • github.com/milvus-io/milvus/pkg/streaming/walimpls
    • /: define the underlying message system interfaces need to be implemented.
    • /registry/: A static lifetime registry to regsiter new implementation for inverting dependency.
    • /helper/: A utility used to help developer to implement walimpls conveniently.
    • /impls/: A official implemented walimpls sets.

Lifetime Of Interfaces

  • OpenerBuilder has a static lifetime in a programs:
  • Opener keep same lifetime with underlying resources (such as mq client).
  • WAL keep same lifetime with underlying writer of wal, and it's lifetime is always included in related Opener.
  • Scanner keep same lifetime with underlying reader of wal, and it's lifetime is always included in related WAL.

Add New Implemetation Of WAL

developper who want to add a new implementation of wal should implements the github.com/milvus-io/milvus/pkg/streaming/walimpls package interfaces. following interfaces is required:

  • walimpls.OpenerBuilderImpls
  • walimpls.OpenerImpls
  • walimpls.ScannerImpls
  • walimpls.WALImpls

OpenerBuilderImpls create OpenerImpls; OpenerImpls creates WALImpls; WALImpls create ScannerImpls. Then register the implmentation of walimpls.OpenerBuilderImpls into github.com/milvus-io/milvus/pkg/streaming/walimpls/registry package.

import "github.com/milvus-io/milvus/pkg/streaming/walimpls/registry"

var _ OpenerBuilderImpls = b{};
registry.RegisterBuilder(b{})

All things have been done.

Use WAL

import "github.com/milvus-io/milvus/internal/streamingnode/server/wal/registry"

name := "your builder name"
var yourCh *options.PChannelInfo

opener, err := registry.MustGetBuilder(name).Build()
if err != nil {
    panic(err)
}
ctx := context.Background()
logger, err := opener.Open(ctx, wal.OpenOption{
    Channel: yourCh  
})
if err != nil {
    panic(err)
}

Adaptor

package adaptor is used to adapt walimpls and wal together. common wal function should be implement by it. Such as:

  • lifetime management
  • interceptor implementation
  • scanner wrapped up
  • write ahead cache implementation