Files
syncthing/lib/model/folderstate.go
T

188 lines
4.0 KiB
Go
Raw Normal View History

2015-03-16 21:14:19 +01:00
// Copyright (C) 2015 The Syncthing Authors.
//
2015-04-13 05:12:01 +09:00
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
2015-03-16 21:14:19 +01:00
package model
import (
2025-08-07 11:19:36 +02:00
"log/slog"
"sync"
2015-03-16 21:14:19 +01:00
"time"
"github.com/syncthing/syncthing/internal/slogutil"
2015-08-06 11:29:25 +02:00
"github.com/syncthing/syncthing/lib/events"
2015-03-16 21:14:19 +01:00
)
type folderState int
const (
FolderIdle folderState = iota
FolderScanning
FolderScanWaiting
FolderSyncWaiting
FolderSyncPreparing
2015-03-16 21:14:19 +01:00
FolderSyncing
FolderCleaning
FolderCleanWaiting
2015-04-13 05:12:01 +09:00
FolderError
FolderStarting
2015-03-16 21:14:19 +01:00
)
func (s folderState) String() string {
switch s {
case FolderIdle:
return "idle"
case FolderStarting:
return "starting"
2015-03-16 21:14:19 +01:00
case FolderScanning:
return "scanning"
case FolderScanWaiting:
return "scan-waiting"
case FolderSyncWaiting:
return "sync-waiting"
case FolderSyncPreparing:
return "sync-preparing"
2015-03-16 21:14:19 +01:00
case FolderSyncing:
return "syncing"
case FolderCleaning:
return "cleaning"
case FolderCleanWaiting:
return "clean-waiting"
2015-04-13 05:12:01 +09:00
case FolderError:
return "error"
2015-03-16 21:14:19 +01:00
default:
return "unknown"
}
}
type remoteFolderState int
const (
remoteFolderUnknown remoteFolderState = iota
remoteFolderNotSharing
remoteFolderPaused
remoteFolderValid
)
func (s remoteFolderState) String() string {
switch s {
case remoteFolderUnknown:
return "unknown"
case remoteFolderNotSharing:
return "notSharing"
case remoteFolderPaused:
return "paused"
case remoteFolderValid:
return "valid"
default:
return "unknown"
}
}
func (s remoteFolderState) MarshalText() ([]byte, error) {
return []byte(s.String()), nil
}
2015-03-16 21:14:19 +01:00
type stateTracker struct {
folderID string
evLogger events.Logger
2015-03-16 21:14:19 +01:00
mut sync.Mutex
current folderState
2015-04-13 05:12:01 +09:00
err error
2015-03-16 21:14:19 +01:00
changed time.Time
}
func newStateTracker(id string, evLogger events.Logger) stateTracker {
return stateTracker{
folderID: id,
evLogger: evLogger,
}
}
2015-04-13 05:12:01 +09:00
// setState sets the new folder state, for states other than FolderError.
2015-03-16 21:14:19 +01:00
func (s *stateTracker) setState(newState folderState) {
2015-04-13 05:12:01 +09:00
if newState == FolderError {
panic("must use setError")
}
2015-03-16 21:14:19 +01:00
s.mut.Lock()
defer s.mut.Unlock()
if newState == s.current {
return
2015-03-16 21:14:19 +01:00
}
defer func() {
metricFolderState.WithLabelValues(s.folderID).Set(float64(s.current))
}()
eventData := map[string]interface{}{
"folder": s.folderID,
"to": newState.String(),
"from": s.current.String(),
}
if !s.changed.IsZero() {
eventData["duration"] = time.Since(s.changed).Seconds()
}
slog.Debug("Folder changed state", "folder", s.folderID, "state", newState, "from", s.current)
s.current = newState
s.changed = time.Now().Truncate(time.Second)
s.evLogger.Log(events.StateChanged, eventData)
2015-03-16 21:14:19 +01:00
}
2015-04-13 05:12:01 +09:00
// getState returns the current state, the time when it last changed, and the
// current error or nil.
func (s *stateTracker) getState() (current folderState, changed time.Time, err error) {
2015-03-16 21:14:19 +01:00
s.mut.Lock()
2015-04-13 05:12:01 +09:00
current, changed, err = s.current, s.changed, s.err
2015-03-16 21:14:19 +01:00
s.mut.Unlock()
return
}
2015-04-13 05:12:01 +09:00
// setError sets the folder state to FolderError with the specified error or
// to FolderIdle if the error is nil
2015-04-13 05:12:01 +09:00
func (s *stateTracker) setError(err error) {
s.mut.Lock()
defer s.mut.Unlock()
2015-04-13 05:12:01 +09:00
defer func() {
metricFolderState.WithLabelValues(s.folderID).Set(float64(s.current))
}()
eventData := map[string]interface{}{
"folder": s.folderID,
"from": s.current.String(),
}
2015-04-13 05:12:01 +09:00
if err != nil && s.current != FolderError {
slog.Warn("Folder is in error state", slog.String("folder", s.folderID), slogutil.Error(err))
} else if err == nil && s.current == FolderError {
slog.Info("Folder error state was cleared", slog.String("folder", s.folderID))
}
if err != nil {
eventData["error"] = err.Error()
2015-04-13 05:12:01 +09:00
s.current = FolderError
} else {
s.current = FolderIdle
2015-04-13 05:12:01 +09:00
}
eventData["to"] = s.current.String()
if !s.changed.IsZero() {
eventData["duration"] = time.Since(s.changed).Seconds()
}
s.err = err
s.changed = time.Now().Truncate(time.Second)
s.evLogger.Log(events.StateChanged, eventData)
}