# How the Periodic Sync Feature Works in Agents View

> Discover how Agents View uses a Go time.Ticker in a goroutine for periodic sync, automatically updating session files every 5 minutes with an idle tracker to prevent overlaps.

- Repository: [Kenn Software/agentsview](https://github.com/kenn-io/agentsview)
- Tags: internals
- Published: 2026-07-04

---

**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`](https://github.com/kenn-io/agentsview/blob/main/cmd/agentsview/main.go), which derives its value from the `--sync-interval` CLI flag parsed in [`internal/config/config.go`](https://github.com/kenn-io/agentsview/blob/main/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`](https://github.com/kenn-io/agentsview/blob/main/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:

```go
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`](https://github.com/kenn-io/agentsview/blob/main/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:

```go
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`](https://github.com/kenn-io/agentsview/blob/main/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:

```bash
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`](https://github.com/kenn-io/agentsview/blob/main/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`](https://github.com/kenn-io/agentsview/blob/main/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`](https://github.com/kenn-io/agentsview/blob/main/internal/sync/engine.go), specifically in the `SyncAll` method. The periodic loop in [`main.go`](https://github.com/kenn-io/agentsview/blob/main/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.