mount: advance the handle watermark for every acknowledged local mutation

Flush was the only path stamping the watermark, so a truncate or
setattr through saveEntry, a server-side CopyFileRange, or a remote
cache download whose response carried no metadata event could still be
rolled back by an older queued subscription event in a read-through
directory. saveEntry now stamps any open handle for the saved path, and
every site tolerates a response without an event: the metadata cache
tracks the newest filer log timestamp seen in any applied event, and
that position, captured before the RPC, is a safe lower bound for the
state the filer served — anything it returns is at least as new as
every event already delivered.
This commit is contained in:
Chris Lu committed 2026-07-22 15:22:30 -07:00
1 parent ed015b0d6b
commit 36b25fca76
9 files changed
+214 -3

No files matched your search

+23
View File
@@ -6,6 +6,7 @@ import (
"math"
"os"
"sync"
"sync/atomic"
"time"
"golang.org/x/sync/singleflight"
@@ -42,6 +43,7 @@ type MetaCache struct {
buildingDirs map[util.FullPath]*directoryBuildState
dedupRing dedupRingBuffer
includeSystemEntries bool
latestEventTsNs atomic.Int64 // newest filer log timestamp seen in any applied event
// Entry invalidations run on a worker, not inline on the apply loop:
// invalidateFunc takes the fh lock, which a flush can hold while waiting on
@@ -574,6 +576,7 @@ type metadataResponseSideEffects struct {
}
func (mc *MetaCache) applyMetadataResponseNow(ctx context.Context, resp *filer_pb.SubscribeMetadataResponse, options MetadataResponseApplyOptions) error {
mc.advanceLatestEventTs(resp.TsNs)
if mc.shouldSkipDuplicateEvent(resp) {
return nil
}
@@ -651,6 +654,26 @@ func (mc *MetaCache) WaitForEntryInvalidations() {
mc.invalidateWorker.Drain()
}
func (mc *MetaCache) advanceLatestEventTs(tsNs int64) {
if tsNs == 0 {
return
}
for {
current := mc.latestEventTsNs.Load()
if tsNs <= current || mc.latestEventTsNs.CompareAndSwap(current, tsNs) {
return
}
}
}
// LatestEventTsNs returns the newest filer log timestamp seen in any applied
// event. A filer RPC issued after reading this serves state at least this
// new, so it is a safe watermark baseline when the RPC response carries no
// metadata event of its own.
func (mc *MetaCache) LatestEventTsNs() int64 {
return mc.latestEventTsNs.Load()
}
func (mc *MetaCache) applyMetadataResponseLocked(ctx context.Context, resp *filer_pb.SubscribeMetadataResponse, _ MetadataResponseApplyOptions, allowUncachedInsert bool) (metadataResponseSideEffects, error) {
message := resp.GetEventNotification()
if message == nil {