Documentation
¶
Overview ¶
Package index provides a persistent file index used for offline deletion detection and last-writer-wins conflict resolution.
Index ¶
- Constants
- func Wins(local, remote Entry) bool
- type Entry
- type Index
- func (idx *Index) All() map[string]Entry
- func (idx *Index) Clone() *Index
- func (idx *Index) Delete(path string)
- func (idx *Index) GCTombstones(now time.Time, ttl time.Duration) int
- func (idx *Index) Get(path string) (Entry, bool)
- func (idx *Index) Len() int
- func (idx *Index) Live() map[string]Entry
- func (idx *Index) Manifest() []ManifestEntry
- func (idx *Index) MarkDeleted(path string, at time.Time) Entry
- func (idx *Index) Path() string
- func (idx *Index) Save() error
- func (idx *Index) Set(e Entry) (prev Entry, had bool)
- func (idx *Index) SetIfWins(remote Entry) bool
- func (idx *Index) SetPath(path string)
- type ManifestEntry
Constants ¶
const DefaultTombstoneTTL = 30 * 24 * time.Hour
DefaultTombstoneTTL is how long deletion tombstones are retained before GC.
const Version = 1
Version is the on-disk schema version.
Variables ¶
This section is empty.
Functions ¶
func Wins ¶
Wins reports whether remote should replace local under last-writer-wins.
Ordering:
- Higher UpdatedAt wins.
- On equal UpdatedAt, prefer remote only when the entries differ, using a stable total order: Deleted (true > false), then Hash (string compare), then Mode, then ModTime (later mtime wins). Preferring remote always would flip-flop under concurrent mutual sync; a stable order guarantees both sides converge to the same winner.
Entries that differ only in ModTime are not treated as identical, so a touch-only peer update still participates in LWW (via UpdatedAt first, or the ModTime tie-break when clocks match).
Types ¶
type Entry ¶
type Entry struct {
// Path is relative to the sync root, using forward slashes.
Path string `json:"path"`
// Size is the file size in bytes (0 for tombstones).
Size int64 `json:"size"`
// ModTime is the file's modification time as observed locally.
ModTime time.Time `json:"mod_time"`
// Mode is the file permission bits.
Mode os.FileMode `json:"mode"`
// Hash is the hex-encoded SHA-256 of the file contents (empty for tombstones).
Hash string `json:"hash,omitempty"`
// Deleted is true when this entry is a deletion tombstone.
Deleted bool `json:"deleted,omitempty"`
// DeletedAt is when the deletion was recorded (zero if not deleted).
DeletedAt time.Time `json:"deleted_at"`
// UpdatedAt is when this entry last changed; used for LWW among peers.
// Callers should keep wall clocks roughly in sync (NTP); ties use a stable
// total order (see Wins).
UpdatedAt time.Time `json:"updated_at"`
}
Entry describes a known file (or a deletion tombstone).
func EntryFromManifest ¶
func EntryFromManifest(m ManifestEntry) Entry
EntryFromManifest converts a ManifestEntry to Entry (identity; kept for call sites).
type Index ¶
type Index struct {
// contains filtered or unexported fields
}
Index is a thread-safe map of relative path → Entry, backed by a JSON file.
Locking: Index uses its own RWMutex for all map access. The daemon holds a separate syncMu around multi-step reconcile/apply so scan→apply and pull→commit stay atomic relative to each other. Holding the daemon lock does not replace Index.mu; methods here still lock. Callers that need a frozen multi-step view relative to disk must serialize at a higher layer (daemon syncMu) in addition to relying on these internal locks.
func (*Index) GCTombstones ¶
GCTombstones removes deleted entries whose DeletedAt is older than ttl (relative to now). Returns the number of entries removed. Tombstones with zero DeletedAt use UpdatedAt as the age basis.
Resurrection risk: once a tombstone is dropped, a lagging peer that still has a live file for that path can re-introduce it as a plain add. Operators should keep the TTL longer than the maximum expected peer offline window, or use -peers with all members regularly online. Peer-ack GC is not implemented.
func (*Index) Manifest ¶
func (idx *Index) Manifest() []ManifestEntry
Manifest returns all entries as a list suitable for peer exchange.
func (*Index) MarkDeleted ¶
MarkDeleted records a tombstone for path.
func (*Index) Save ¶
Save writes the index atomically (temp file + rename). The mutex is held only while snapshotting; disk I/O runs unlocked.
type ManifestEntry ¶
type ManifestEntry = Entry
ManifestEntry is the wire/peer view of an index entry. It is a type alias of Entry so JSON field names stay identical on the wire.