How the Periodic Sync Feature Works in Agents View

Agents View implements periodic sync using a Go time.Ticker that runs every configurable interval (default 5 minutes) in a dedicated goroutine, triggering the sync engine to discover and update session files while preventing overlaps via an idle tracker.

The kenn-io/agentsview repository maintains a real-time SQLite database of agent sessions through a robust periodic sync feature. This background process runs continuously alongside the main daemon, ensuring that new or modified session files are discovered and indexed at regular intervals—whether set to 5, 15, or any other configurable duration—without blocking the main server.

How the Periodic Sync Loop is Initialized

Ticker Creation and Configuration

The sync interval is defined by the periodicSyncInterval constant in cmd/agentsview/main.go, which derives its value from the --sync-interval CLI flag parsed in internal/config/config.go. By default, this is set to 5 minutes, though users can specify any duration, such as 15 minutes, to control how frequently the database updates.

Goroutine Launch in main.go

The startPeriodicSync function is launched as a separate goroutine immediately after the daemon initializes in main.go. This non-blocking approach allows the server to accept HTTP requests while the background loop waits for ticker events.

The Periodic Sync Execution Flow

The Ticker Loop Structure

The goroutine constructs a time.Ticker and enters a for loop that selects on either the ticker channel or a context cancellation signal:

func startPeriodicSync(
    ctx context.Context,
    cfg config.Config,
    engine *sync.Engine,
    database *db.DB,
    idleTracker *server.IdleTracker,
    validRemotes bool,
    emitter sync.Emitter,
) {
    // … remote‑host sync setup omitted …
    ticker := time.NewTicker(periodicSyncInterval) // e.g. 5 min
    defer ticker.Stop()
    for {
        select {
        case <-ctx.Done():
            return                     // stop on shutdown
        case <-ticker.C:               // each interval
        }
        log.Println("Running scheduled sync...")
        idleTracker.Do(func() {
            engine.SyncAll(ctx, nil)               // full discovery + write
            recomputePendingSessions(engine, database) // update signals/secrets
        })
    }
}

SyncAll and Database Updates

On each tick, the loop executes engine.SyncAll(ctx, nil) from internal/sync/engine.go. This method walks all configured agent root directories, classifies files, parses changed sessions, and writes the resulting rows to the SQLite database:

func (e *Engine) SyncAll(ctx context.Context, onProgress ProgressFunc) (stats SyncStats) {
    if e.refuseWriteInForceParse("SyncAll") { return SyncStats{} }
    e.syncMu.Lock()
    defer func() { if stats.Synced > 0 { e.emit("sessions") } }()
    defer e.syncMu.Unlock()
    defer e.clearCurrentProgress()
    stats = e.syncAllLocked(ctx, onProgress, time.Time{}, nil, syncWriteDefault, true)
    return
}

Pending Session Recomputation

Immediately following SyncAll, the loop calls recomputePendingSessions(engine, database) to flush any deferred signal or secret field calculations that were queued during normal sync operations.

Concurrency Safety with IdleTracker

All sync work is wrapped in idleTracker.Do() from internal/server/idletracker.go. This serialization mechanism prevents overlapping sync operations if a previous cycle is still running when the next tick occurs, ensuring that only one sync runs at a time.

Graceful Shutdown Handling

The implementation respects Go's context cancellation patterns. A defer ticker.Stop() ensures resources are released, while the select statement listens for <-ctx.Done() to exit the loop cleanly during daemon shutdown.

Configuration and Customization

To run the periodic sync every 15 minutes instead of the default 5, specify the interval when starting the daemon:

agentsview serve \
    --data-dir=$HOME/.agentsview \
    --sync-interval=15m   # <-- periodic sync interval

Summary

  • The periodic sync feature uses a time.Ticker created in cmd/agentsview/main.go with a configurable periodicSyncInterval defaulting to 5 minutes.
  • The startPeriodicSync goroutine executes engine.SyncAll and recomputePendingSessions on each tick to update the SQLite database.
  • An IdleTracker prevents concurrent sync operations, ensuring data consistency even when the interval is short.
  • The complementary file watcher in internal/sync/watcher.go triggers immediate syncs on filesystem events, while the periodic ticker ensures eventual consistency.
  • The loop exits cleanly when the parent context is cancelled, making it safe for production deployments.

Frequently Asked Questions

How often does the periodic sync run by default?

By default, the sync runs every 5 minutes. You can modify this to 15 minutes or any other duration using the --sync-interval flag, accepting values like 10m or 30m.

What happens if a sync is still running when the next tick occurs?

The IdleTracker.Do() method serializes execution, so the next sync waits until the current one completes. This prevents race conditions and database corruption.

Where is the sync logic actually implemented?

The heavy lifting occurs in internal/sync/engine.go, specifically in the SyncAll method. The periodic loop in main.go merely triggers this method at timed intervals.

How does the feature handle daemon shutdown?

The goroutine listens for context cancellation via <-ctx.Done(). When the daemon receives a shutdown signal, the ticker stops immediately via defer ticker.Stop(), and the loop exits gracefully.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →