folderstate.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright (C) 2015 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package model
  7. import (
  8. "log/slog"
  9. "sync"
  10. "time"
  11. "github.com/syncthing/syncthing/lib/events"
  12. )
  13. type folderState int
  14. const (
  15. FolderIdle folderState = iota
  16. FolderScanning
  17. FolderScanWaiting
  18. FolderSyncWaiting
  19. FolderSyncPreparing
  20. FolderSyncing
  21. FolderCleaning
  22. FolderCleanWaiting
  23. FolderError
  24. )
  25. func (s folderState) String() string {
  26. switch s {
  27. case FolderIdle:
  28. return "idle"
  29. case FolderScanning:
  30. return "scanning"
  31. case FolderScanWaiting:
  32. return "scan-waiting"
  33. case FolderSyncWaiting:
  34. return "sync-waiting"
  35. case FolderSyncPreparing:
  36. return "sync-preparing"
  37. case FolderSyncing:
  38. return "syncing"
  39. case FolderCleaning:
  40. return "cleaning"
  41. case FolderCleanWaiting:
  42. return "clean-waiting"
  43. case FolderError:
  44. return "error"
  45. default:
  46. return "unknown"
  47. }
  48. }
  49. type remoteFolderState int
  50. const (
  51. remoteFolderUnknown remoteFolderState = iota
  52. remoteFolderNotSharing
  53. remoteFolderPaused
  54. remoteFolderValid
  55. )
  56. func (s remoteFolderState) String() string {
  57. switch s {
  58. case remoteFolderUnknown:
  59. return "unknown"
  60. case remoteFolderNotSharing:
  61. return "notSharing"
  62. case remoteFolderPaused:
  63. return "paused"
  64. case remoteFolderValid:
  65. return "valid"
  66. default:
  67. return "unknown"
  68. }
  69. }
  70. func (s remoteFolderState) MarshalText() ([]byte, error) {
  71. return []byte(s.String()), nil
  72. }
  73. type stateTracker struct {
  74. folderID string
  75. evLogger events.Logger
  76. mut sync.Mutex
  77. current folderState
  78. err error
  79. changed time.Time
  80. }
  81. func newStateTracker(id string, evLogger events.Logger) stateTracker {
  82. return stateTracker{
  83. folderID: id,
  84. evLogger: evLogger,
  85. }
  86. }
  87. // setState sets the new folder state, for states other than FolderError.
  88. func (s *stateTracker) setState(newState folderState) {
  89. if newState == FolderError {
  90. panic("must use setError")
  91. }
  92. s.mut.Lock()
  93. defer s.mut.Unlock()
  94. if newState == s.current {
  95. return
  96. }
  97. defer func() {
  98. metricFolderState.WithLabelValues(s.folderID).Set(float64(s.current))
  99. }()
  100. eventData := map[string]interface{}{
  101. "folder": s.folderID,
  102. "to": newState.String(),
  103. "from": s.current.String(),
  104. }
  105. if !s.changed.IsZero() {
  106. eventData["duration"] = time.Since(s.changed).Seconds()
  107. }
  108. s.current = newState
  109. s.changed = time.Now().Truncate(time.Second)
  110. s.evLogger.Log(events.StateChanged, eventData)
  111. slog.Info("Folder changed state", "folder", s.folderID, "state", newState)
  112. }
  113. // getState returns the current state, the time when it last changed, and the
  114. // current error or nil.
  115. func (s *stateTracker) getState() (current folderState, changed time.Time, err error) {
  116. s.mut.Lock()
  117. current, changed, err = s.current, s.changed, s.err
  118. s.mut.Unlock()
  119. return
  120. }
  121. // setError sets the folder state to FolderError with the specified error or
  122. // to FolderIdle if the error is nil
  123. func (s *stateTracker) setError(err error) {
  124. s.mut.Lock()
  125. defer s.mut.Unlock()
  126. defer func() {
  127. metricFolderState.WithLabelValues(s.folderID).Set(float64(s.current))
  128. }()
  129. eventData := map[string]interface{}{
  130. "folder": s.folderID,
  131. "from": s.current.String(),
  132. }
  133. if err != nil {
  134. eventData["error"] = err.Error()
  135. s.current = FolderError
  136. } else {
  137. s.current = FolderIdle
  138. }
  139. eventData["to"] = s.current.String()
  140. if !s.changed.IsZero() {
  141. eventData["duration"] = time.Since(s.changed).Seconds()
  142. }
  143. s.err = err
  144. s.changed = time.Now().Truncate(time.Second)
  145. s.evLogger.Log(events.StateChanged, eventData)
  146. }