Skip to content

Commit 782b2c9

Browse files
authored
fix(sync): suppress OpenCode SHM-only daemon watch pushes (#1321)
Daemon-delegated watch pushes currently treat every filesystem batch as dirty, so OpenCode's WAL shared-memory index can retrigger a zero-session push after the daemon reads a quiet database. The repeated `opencode.db-shm` sequence came from @ngocphamm's report. This adds a provider-owned changed-path relevance check at the push trigger boundary. OpenCode-family SHM-only events become no-ops while checkpointed database writes and WALs containing frames continue to trigger synchronization. The existing provider classification from #956 remains the source of the SHM and WAL rules, and authoritative recovery batches continue to push. All four watch notification owners route through the same gate: daemon PostgreSQL, daemon DuckDB, local PostgreSQL, and local DuckDB. Filesystem event delivery, local archive synchronization, coverage-degradation notification, startup and interval pushes, storage, and mirror schemas remain unchanged. Closes #1317 Co-authored-by: Rod Boev <rodboev@users.noreply.github.com>
1 parent e4b891a commit 782b2c9

7 files changed

Lines changed: 709 additions & 25 deletions

File tree

cmd/agentsview/archive_write_backend.go

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func archivePushWatchWatcherOptions(
179179

180180
func archivePushWatchBatchCallback(
181181
appCfg config.Config,
182-
engine *syncpkg.Engine,
182+
engine watchSyncer,
183183
loop *pushLoop,
184184
) syncpkg.WatchCallback {
185185
return func(callbackCtx context.Context, batch syncpkg.WatchBatch) error {
@@ -189,7 +189,9 @@ func archivePushWatchBatchCallback(
189189
if err := syncWatchBatch(callbackCtx, engine, batch, scope); err != nil {
190190
return err
191191
}
192-
return notifyPushForWatchBatch(callbackCtx, loop, batch)
192+
return notifyPushForWatchBatchWithConfig(
193+
callbackCtx, loop, appCfg, batch,
194+
)
193195
}
194196
}
195197

@@ -249,6 +251,97 @@ func notifyPushForWatchBatch(
249251
}
250252
}
251253

254+
func notifyPushForWatchBatchWithConfig(
255+
ctx context.Context,
256+
loop *pushLoop,
257+
cfg config.Config,
258+
batch syncpkg.WatchBatch,
259+
) error {
260+
if watchBatchIsAffirmativelyNonData(ctx, cfg, batch) {
261+
return nil
262+
}
263+
return notifyPushForWatchBatch(ctx, loop, batch)
264+
}
265+
266+
type watchPathRelevanceProvider struct {
267+
provider parser.Provider
268+
root string
269+
relevanceSupported bool
270+
}
271+
272+
func watchBatchIsAffirmativelyNonData(
273+
ctx context.Context,
274+
cfg config.Config,
275+
batch syncpkg.WatchBatch,
276+
) bool {
277+
if ctx.Err() != nil || len(batch.Paths) == 0 || batch.FullSync ||
278+
batch.LostEvents || len(batch.ReconcileRoots) > 0 ||
279+
len(batch.Renames) > 0 {
280+
return false
281+
}
282+
283+
providers := configuredWatchPathRelevanceProviders(cfg)
284+
if len(providers) == 0 {
285+
return false
286+
}
287+
for _, path := range batch.Paths {
288+
if path == "" {
289+
return false
290+
}
291+
path = absRootPath(path)
292+
matched := false
293+
for _, candidate := range providers {
294+
if !pathWithinRoot(path, candidate.root) {
295+
continue
296+
}
297+
matched = true
298+
if !candidate.relevanceSupported {
299+
return false
300+
}
301+
relevance, err := parser.ResolveChangedPathRelevance(
302+
ctx, candidate.provider, parser.ChangedPathRequest{
303+
Path: path, WatchRoot: candidate.root,
304+
},
305+
)
306+
if err != nil || relevance != parser.ChangedPathNonData {
307+
return false
308+
}
309+
}
310+
if !matched {
311+
return false
312+
}
313+
}
314+
return true
315+
}
316+
317+
func configuredWatchPathRelevanceProviders(
318+
cfg config.Config,
319+
) []watchPathRelevanceProvider {
320+
var providers []watchPathRelevanceProvider
321+
for _, factory := range parser.ProviderFactories() {
322+
relevanceSupported := factory.Capabilities().Source.ChangedPathRelevance ==
323+
parser.CapabilitySupported
324+
roots := cfg.ResolveDirs(factory.Definition().Type)
325+
for _, root := range roots {
326+
if root == "" {
327+
continue
328+
}
329+
root = absRootPath(root)
330+
var provider parser.Provider
331+
if relevanceSupported {
332+
provider = factory.NewProvider(parser.ProviderConfig{
333+
Roots: []string{root},
334+
})
335+
}
336+
providers = append(providers, watchPathRelevanceProvider{
337+
provider: provider,
338+
root: root,
339+
relevanceSupported: relevanceSupported,
340+
})
341+
}
342+
}
343+
return providers
344+
}
252345
func watchBatchNeedsPushAck(batch syncpkg.WatchBatch) bool {
253346
if batch.FullSync || len(batch.ReconcileRoots) > 0 {
254347
return true
@@ -471,7 +564,9 @@ func (b daemonArchiveWriteBackend) DuckDBPushWatch(
471564
stopWatcher, openDispatch, unwatchedDirs := startArchivePushWatcher(
472565
b.watchHooks, b.appCfg, nil,
473566
func(callbackCtx context.Context, batch syncpkg.WatchBatch) error {
474-
return notifyPushForWatchBatch(callbackCtx, loop, batch)
567+
return notifyPushForWatchBatchWithConfig(
568+
callbackCtx, loop, b.appCfg, batch,
569+
)
475570
},
476571
syncpkg.WatcherOptions{OnCoverageDegraded: loop.NotifyCoverageDegraded},
477572
)
@@ -601,7 +696,9 @@ func (b daemonArchiveWriteBackend) PGPushWatch(
601696
stopWatcher, openDispatch, unwatchedDirs := startArchivePushWatcher(
602697
b.watchHooks, b.appCfg, nil,
603698
func(callbackCtx context.Context, batch syncpkg.WatchBatch) error {
604-
return notifyPushForWatchBatch(callbackCtx, loop, batch)
699+
return notifyPushForWatchBatchWithConfig(
700+
callbackCtx, loop, b.appCfg, batch,
701+
)
605702
},
606703
syncpkg.WatcherOptions{OnCoverageDegraded: loop.NotifyCoverageDegraded},
607704
)

0 commit comments

Comments
 (0)