Help us shape the future of Go! Take the 2026 Go Developer Survey

watch

package
v1.96.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

Documentation

Overview

Package watch follows a directory tree for changes, so a backup of it can take the directories nothing changed in whole from its previous commit. A Source reports what changed through one backend: fanotify or inotify on Linux, the NTFS change journal on Windows. A Tracker follows a Source for any number of sets, and a Schedule decides, per set, what a run reads and when a change starts one.

Index

Constants

View Source
const (
	// FullNotFollowing: no tracker follows the root, or it can't tell its
	// present, so the run has no position to record either.
	FullNotFollowing = "not-following"
	// FullFirst: the set has no record of a run yet.
	FullFirst = "first"
	// FullSettings: the run reads the tree with other settings than the
	// last, such as other filters.
	FullSettings = "settings-changed"
	// FullDue: the set's last full read is FullReadEvery ago.
	FullDue = "full-read-due"
	// FullOtherCommit: the set's latest commit isn't the one its last run
	// made.
	FullOtherCommit = "other-commit"
	// FullInDoubt: the tracker can't tell what changed since the last run,
	// as after a restart, a queue overflow or the root moving.
	FullInDoubt = "in-doubt"
)

The reasons a run reads the whole tree.

View Source
const MaxDirs = 50_000

MaxDirs bounds how many changed paths a Tracker keeps before it counts reading the tree in full as cheaper.

Variables

View Source
var ErrInDoubt = errors.New("the change tracking can't tell what changed")

ErrInDoubt is a Source's error when it can't tell what changed since a position: its queue overflowed, the position is from another stream or too old, or the root was moved or unmounted.

View Source
var ErrStarting = errors.New("the change tracking is still starting")

ErrStarting is a Source's error while it is still setting up; it has no position yet.

View Source
var ErrUnavailable = errors.New("change tracking is unavailable")

ErrUnavailable is Open's error when no backend can follow the root.

Functions

This section is empty.

Types

type Changes

type Changes struct {
	// Dirs are the directories whose entries changed, each with its
	// ancestors.
	Dirs map[string]int64 `json:"dirs,omitempty"`
	// Trees are the directories everything under which changed, as one
	// created or moved in.
	Trees map[string]int64 `json:"trees,omitempty"`
	// Unwatched are the subtrees the source can't follow, with why; they
	// count as changed until the source starts again.
	Unwatched map[string]string `json:"unwatched,omitempty"`
}

Changes is what changed under a root, as slash-separated paths below it ("" is the root), each with the Seq of its latest change.

func NewChanges

func NewChanges() *Changes

NewChanges returns empty Changes.

func (*Changes) Len

func (c *Changes) Len() int

Len is how many paths c holds.

func (*Changes) Mark

func (c *Changes) Mark(rel string, seq int64)

Mark records that the directory rel changed at seq, and so did each of its ancestors.

func (*Changes) MarkTree

func (c *Changes) MarkTree(rel string, seq int64)

MarkTree records that everything under the directory rel changed at seq.

func (*Changes) Unwatch

func (c *Changes) Unwatch(rel, why string)

Unwatch records that the source can't follow the subtree at rel.

type FileStore

type FileStore struct {
	// contains filtered or unexported fields
}

FileStore is a Store in one JSON file.

func OpenFileStore

func OpenFileStore(path string) (*FileStore, error)

OpenFileStore opens the FileStore at path, empty when there is no file there; one that doesn't parse is empty too, so every set is read in full once.

func (*FileStore) Load

func (s *FileStore) Load(set string) (Record, bool, error)

Load implements Store.

func (*FileStore) Save

func (s *FileStore) Save(set string, r Record) error

Save implements Store, replacing the file.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore is a Store that keeps Records in memory.

func (*MemoryStore) Load

func (s *MemoryStore) Load(set string) (Record, bool, error)

Load implements Store.

func (*MemoryStore) Save

func (s *MemoryStore) Save(set string, r Record) error

Save implements Store.

type Options

type Options struct {
	// MinGap is the least time from a run's start to a run on change.
	MinGap time.Duration
	// FullReadEvery is how often a run reads everything whatever the
	// tracker says; zero never.
	FullReadEvery time.Duration
	// Settings fingerprints what besides the tree decides what a run
	// reads, such as its filters; a run with other settings reads
	// everything.
	Settings string
}

Options are how a Schedule watches one set.

type Position

type Position struct {
	Stream string `json:"stream"`
	Seq    int64  `json:"seq"`
}

Position marks a point in a Source's changes: Stream names an unbroken run of them, and Seq orders the changes in it. Positions of different streams don't compare.

func (Position) IsZero

func (p Position) IsZero() bool

IsZero reports whether p marks nothing.

type Record

type Record struct {
	Position Position  `json:"position"`
	Commit   []byte    `json:"commit"`
	Settings string    `json:"settings"`
	FullAt   time.Time `json:"full_at"`
}

Record is what a set's last watched run left: the position taken before it read the tree, the commit it made or kept, the settings it read the tree with and when the set was last read in full.

type Run

type Run struct {
	Set string
	// At is the position to record once the run succeeds; zero when there
	// is none.
	At Position
	// Unchanged reports the directories the run takes from the set's
	// latest commit, for job.PathSettings.Unchanged; nil reads everything.
	Unchanged func(rel string) bool
	// Full is why the run reads everything, one of the Full reasons; empty
	// when Unchanged is set.
	Full string
	// Detail says more about Full, such as why the tracker isn't following.
	Detail string
	// contains filtered or unexported fields
}

Run is what one run of a set reads.

type Schedule

type Schedule struct {
	Store   Store
	Trigger Trigger
	// Now is the clock; nil is time.Now.
	Now    func() time.Time
	Logger *slog.Logger
	// contains filtered or unexported fields
}

Schedule decides for watched sets what each run reads, from the set's Record and its root's Tracker, and starts a run through Trigger once something changed and MinGap has passed since the set's last run started. A set without a Record, or whose tracker can't tell what changed, runs on change too.

func (*Schedule) Begin

func (s *Schedule) Begin(set string, latest []byte, at *Position) Run

Begin starts a run of set, whose latest commit is latest, and says what it reads. at is the position to record, taken before a snapshot the run reads; nil takes it now. End must follow.

func (*Schedule) Check

func (s *Schedule) Check()

Check reads every tracker's changes and starts the run of each watched set that changed since its last run, once its gap has passed. A set whose tracker isn't following runs on its plan alone.

func (*Schedule) End

func (s *Schedule) End(r Run, commit []byte, err error) error

End ends r. When err is nil it records r's position with commit, the commit the run made or kept, for the set's next run.

func (*Schedule) Run

func (s *Schedule) Run(ctx context.Context, interval time.Duration)

Run checks every interval until ctx ends.

func (*Schedule) Started

func (s *Schedule) Started(set string, at time.Time)

Started tells the schedule a run of set started at, when it began before the schedule saw it, as the scan start of the set's newest commit.

func (*Schedule) State

func (s *Schedule) State(set string) State

State reports how set is watched.

func (*Schedule) Unwatch

func (s *Schedule) Unwatch(set string)

Unwatch stops watching set; a run of it going on records nothing.

func (*Schedule) Watch

func (s *Schedule) Watch(set string, t *Tracker, o Options)

Watch watches set through t with o, or changes how it is watched.

type Source

type Source interface {
	// Backend names how the source follows the root, as inotify.
	Backend() string
	// Now is the position of the present: a change after it has a Seq at or
	// past its Seq.
	Now() (Position, error)
	// Read is what changed from from on, or ErrInDoubt, and the position it
	// read to, which the next Read starts from.
	Read(from Position) (*Changes, Position, error)
	Close() error
}

Source reports the changes under one root through one backend.

func Open

func Open(root string) (Source, error)

Open opens a Source following root: fanotify where the process may mark the root's whole filesystem and the kernel names the directory of each event (Linux 5.9 on, with CAP_SYS_ADMIN and CAP_DAC_READ_SEARCH), and inotify otherwise.

type State

type State struct {
	TrackerState
	Watched bool `json:"watched"`
}

State is how a set is watched.

type Store

type Store interface {
	Load(set string) (Record, bool, error)
	Save(set string, r Record) error
}

Store keeps each set's Record; a Schedule loads one as a run of the set starts and saves one once it succeeded.

type Tracker

type Tracker struct {
	// contains filtered or unexported fields
}

Tracker follows the changes of one root for every set backing it up, each of which asks what changed after its own position. It reads them from a Source when polled, opening a new one when the last one stopped following the root.

func Follow

func Follow(root string) *Tracker

Follow returns a Tracker of root through the best backend Open finds.

func NewTracker

func NewTracker(open func() (Source, error)) *Tracker

NewTracker returns a Tracker reading the Sources open opens. It opens the first one on the first Poll.

func (*Tracker) After

func (t *Tracker) After(p Position) (unchanged func(rel string) bool, ok bool)

After reports, for a set whose last run took position p before it read the tree, whether nothing at or under a slash-separated directory below the root changed since; ok is false when the tracker can't tell. unchanged is safe for concurrent use and doesn't change as the tracker reads on.

func (*Tracker) Changed

func (t *Tracker) Changed(p Position) bool

Changed reports whether something under the root changed after p, or the tracker can't tell; subtrees it can't follow don't count.

func (*Tracker) Close

func (t *Tracker) Close() error

Close closes the source; the tracker follows nothing after.

func (*Tracker) Following

func (t *Tracker) Following() bool

Following reports whether the tracker follows the root without a gap since it last started.

func (*Tracker) Now

func (t *Tracker) Now() (Position, error)

Now is the source's present, the position a run takes before it reads the tree.

func (*Tracker) Poll

func (t *Tracker) Poll(from Position) error

Poll reads what changed since the last Poll. A tracker that isn't following yet starts at from, the oldest position a set still asks about, or at the present when from is zero or the source can't read from it. When the source loses track, every set whose position is from before counts as in doubt, and the error says why.

func (*Tracker) Prune

func (t *Tracker) Prune(floor Position)

Prune forgets what changed before floor, which every set has read past.

func (*Tracker) State

func (t *Tracker) State() TrackerState

State reports how the tracker follows the root.

type TrackerState

type TrackerState struct {
	// Backend names the source's backend; empty before one opened.
	Backend   string `json:"backend,omitempty"`
	Following bool   `json:"following"`
	// Reason is why the tracker isn't following, or last lost track.
	Reason string `json:"reason,omitempty"`
	// Unwatched are the subtrees it can't follow, with why.
	Unwatched map[string]string `json:"unwatched,omitempty"`
}

TrackerState is what a Tracker reports about itself.

type Trigger

type Trigger interface {
	RunNow(set string)
}

Trigger starts a run of a set; a Schedule calls it once something under a watched set's root changed and the set's gap has passed. RunNow must not wait for the run.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL