folderstate.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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. "time"
  9. "github.com/syncthing/syncthing/lib/events"
  10. "github.com/syncthing/syncthing/lib/sync"
  11. )
  12. type folderState int
  13. const (
  14. FolderIdle folderState = iota
  15. FolderScanning
  16. FolderScanWaiting
  17. FolderSyncWaiting
  18. FolderSyncPreparing
  19. FolderSyncing
  20. FolderCleaning
  21. FolderCleanWaiting
  22. FolderError
  23. )
  24. func (s folderState) String() string {
  25. switch s {
  26. case FolderIdle:
  27. return "idle"
  28. case FolderScanning:
  29. return "scanning"
  30. case FolderScanWaiting:
  31. return "scan-waiting"
  32. case FolderSyncWaiting:
  33. return "sync-waiting"
  34. case FolderSyncPreparing:
  35. return "sync-preparing"
  36. case FolderSyncing:
  37. return "syncing"
  38. case FolderCleaning:
  39. return "cleaning"
  40. case FolderCleanWaiting:
  41. return "clean-waiting"
  42. case FolderError:
  43. return "error"
  44. default:
  45. return "unknown"
  46. }
  47. }
  48. type remoteFolderState int
  49. const (
  50. remoteFolderUnknown remoteFolderState = iota
  51. remoteFolderNotSharing
  52. remoteFolderPaused
  53. remoteFolderValid
  54. )
  55. func (s remoteFolderState) String() string {
  56. switch s {
  57. case remoteFolderUnknown:
  58. return "unknown"
  59. case remoteFolderNotSharing:
  60. return "notSharing"
  61. case remoteFolderPaused:
  62. return "paused"
  63. case remoteFolderValid:
  64. return "valid"
  65. default:
  66. return "unknown"
  67. }
  68. }
  69. func (s remoteFolderState) MarshalText() ([]byte, error) {
  70. return []byte(s.String()), nil
  71. }
  72. type stateTracker struct {
  73. folderID string
  74. evLogger events.Logger
  75. mut sync.Mutex
  76. current folderState
  77. err error
  78. changed time.Time
  79. }
  80. func newStateTracker(id string, evLogger events.Logger) stateTracker {
  81. return stateTracker{
  82. folderID: id,
  83. evLogger: evLogger,
  84. mut: sync.NewMutex(),
  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. /* This should hold later...
  101. if s.current != FolderIdle && (newState == FolderScanning || newState == FolderSyncing) {
  102. panic("illegal state transition " + s.current.String() + " -> " + newState.String())
  103. }
  104. */
  105. eventData := map[string]interface{}{
  106. "folder": s.folderID,
  107. "to": newState.String(),
  108. "from": s.current.String(),
  109. }
  110. if !s.changed.IsZero() {
  111. eventData["duration"] = time.Since(s.changed).Seconds()
  112. }
  113. s.current = newState
  114. s.changed = time.Now().Truncate(time.Second)
  115. s.evLogger.Log(events.StateChanged, eventData)
  116. }
  117. // getState returns the current state, the time when it last changed, and the
  118. // current error or nil.
  119. func (s *stateTracker) getState() (current folderState, changed time.Time, err error) {
  120. s.mut.Lock()
  121. current, changed, err = s.current, s.changed, s.err
  122. s.mut.Unlock()
  123. return
  124. }
  125. // setError sets the folder state to FolderError with the specified error or
  126. // to FolderIdle if the error is nil
  127. func (s *stateTracker) setError(err error) {
  128. s.mut.Lock()
  129. defer s.mut.Unlock()
  130. defer func() {
  131. metricFolderState.WithLabelValues(s.folderID).Set(float64(s.current))
  132. }()
  133. eventData := map[string]interface{}{
  134. "folder": s.folderID,
  135. "from": s.current.String(),
  136. }
  137. if err != nil {
  138. eventData["error"] = err.Error()
  139. s.current = FolderError
  140. } else {
  141. s.current = FolderIdle
  142. }
  143. eventData["to"] = s.current.String()
  144. if !s.changed.IsZero() {
  145. eventData["duration"] = time.Since(s.changed).Seconds()
  146. }
  147. s.err = err
  148. s.changed = time.Now().Truncate(time.Second)
  149. s.evLogger.Log(events.StateChanged, eventData)
  150. }