lib/model: Incremental block stats usage reporting

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4498
LGTM: calmh
This commit is contained in:
Audrius Butkevicius 2017-11-09 21:16:29 +00:00
parent a9d422e008
commit 386cb274bd
6 changed files with 34 additions and 47 deletions

View File

@ -104,7 +104,7 @@ type modelIntf interface {
CurrentSequence(folder string) (int64, bool) CurrentSequence(folder string) (int64, bool)
RemoteSequence(folder string) (int64, bool) RemoteSequence(folder string) (int64, bool)
State(folder string) (string, time.Time, error) State(folder string) (string, time.Time, error)
UsageReportingStats(version int) map[string]interface{} UsageReportingStats(version int, preview bool) map[string]interface{}
} }
type configIntf interface { type configIntf interface {
@ -980,7 +980,7 @@ func (s *apiService) getReport(w http.ResponseWriter, r *http.Request) {
if val, _ := strconv.Atoi(r.URL.Query().Get("version")); val > 0 { if val, _ := strconv.Atoi(r.URL.Query().Get("version")); val > 0 {
version = val version = val
} }
sendJSON(w, reportData(s.cfg, s.model, s.connectionsService, version)) sendJSON(w, reportData(s.cfg, s.model, s.connectionsService, version, true))
} }
func (s *apiService) getRandomString(w http.ResponseWriter, r *http.Request) { func (s *apiService) getRandomString(w http.ResponseWriter, r *http.Request) {

View File

@ -115,6 +115,6 @@ func (m *mockedModel) State(folder string) (string, time.Time, error) {
return "", time.Time{}, nil return "", time.Time{}, nil
} }
func (m *mockedModel) UsageReportingStats(version int) map[string]interface{} { func (m *mockedModel) UsageReportingStats(version int, preview bool) map[string]interface{} {
return nil return nil
} }

View File

@ -36,7 +36,7 @@ const usageReportVersion = 3
// reportData returns the data to be sent in a usage report. It's used in // reportData returns the data to be sent in a usage report. It's used in
// various places, so not part of the usageReportingManager object. // various places, so not part of the usageReportingManager object.
func reportData(cfg configIntf, m modelIntf, connectionsService connectionsIntf, version int) map[string]interface{} { func reportData(cfg configIntf, m modelIntf, connectionsService connectionsIntf, version int, preview bool) map[string]interface{} {
opts := cfg.Options() opts := cfg.Options()
res := make(map[string]interface{}) res := make(map[string]interface{})
res["urVersion"] = version res["urVersion"] = version
@ -310,7 +310,7 @@ func reportData(cfg configIntf, m modelIntf, connectionsService connectionsIntf,
res["guiStats"] = guiStatsInterface res["guiStats"] = guiStatsInterface
} }
for key, value := range m.UsageReportingStats(version) { for key, value := range m.UsageReportingStats(version, preview) {
res[key] = value res[key] = value
} }
@ -338,7 +338,7 @@ func newUsageReportingService(cfg *config.Wrapper, model *model.Model, connectio
} }
func (s *usageReportingService) sendUsageReport() error { func (s *usageReportingService) sendUsageReport() error {
d := reportData(s.cfg, s.model, s.connectionsService, s.cfg.Options().URAccepted) d := reportData(s.cfg, s.model, s.connectionsService, s.cfg.Options().URAccepted, false)
var b bytes.Buffer var b bytes.Buffer
json.NewEncoder(&b).Encode(d) json.NewEncoder(&b).Encode(d)

View File

@ -73,10 +73,6 @@ func (f *folder) Stop() {
f.cancel() f.cancel()
} }
func (f *folder) BlockStats() map[string]int {
return nil
}
// CheckHealth checks the folder for common errors, updates the folder state // CheckHealth checks the folder for common errors, updates the folder state
// and returns the current folder error, or nil if the folder is healthy. // and returns the current folder error, or nil if the folder is healthy.
func (f *folder) CheckHealth() error { func (f *folder) CheckHealth() error {

View File

@ -54,7 +54,6 @@ type service interface {
Scan(subs []string) error Scan(subs []string) error
Serve() Serve()
Stop() Stop()
BlockStats() map[string]int
CheckHealth() error CheckHealth() error
getState() (folderState, time.Time, error) getState() (folderState, time.Time, error)
@ -397,19 +396,20 @@ func (m *Model) RestartFolder(cfg config.FolderConfiguration) {
m.fmut.Unlock() m.fmut.Unlock()
} }
func (m *Model) UsageReportingStats(version int) map[string]interface{} { func (m *Model) UsageReportingStats(version int, preview bool) map[string]interface{} {
stats := make(map[string]interface{}) stats := make(map[string]interface{})
if version >= 3 { if version >= 3 {
// Block stats // Block stats
m.fmut.Lock() blockStatsMut.Lock()
blockStats := make(map[string]int) copyBlockStats := make(map[string]int)
for _, folder := range m.folderRunners { for k, v := range blockStats {
for k, v := range folder.BlockStats() { copyBlockStats[k] = v
blockStats[k] += v if !preview {
blockStats[k] = 0
} }
} }
m.fmut.Unlock() blockStatsMut.Unlock()
stats["blockStats"] = blockStats stats["blockStats"] = copyBlockStats
// Transport stats // Transport stats
m.pmut.Lock() m.pmut.Lock()

View File

@ -29,6 +29,11 @@ import (
"github.com/syncthing/syncthing/lib/weakhash" "github.com/syncthing/syncthing/lib/weakhash"
) )
var (
blockStats = make(map[string]int)
blockStatsMut = sync.NewMutex()
)
func init() { func init() {
folderFactories[config.FolderTypeSendReceive] = newSendReceiveFolder folderFactories[config.FolderTypeSendReceive] = newSendReceiveFolder
} }
@ -91,9 +96,6 @@ type sendReceiveFolder struct {
errors map[string]string // path -> error string errors map[string]string // path -> error string
errorsMut sync.Mutex errorsMut sync.Mutex
blockStats map[string]int
blockStatsMut sync.Mutex
} }
func newSendReceiveFolder(model *Model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service { func newSendReceiveFolder(model *Model, cfg config.FolderConfiguration, ver versioner.Versioner, fs fs.Filesystem) service {
@ -107,9 +109,6 @@ func newSendReceiveFolder(model *Model, cfg config.FolderConfiguration, ver vers
pullScheduled: make(chan struct{}, 1), // This needs to be 1-buffered so that we queue a pull if we're busy when it comes. pullScheduled: make(chan struct{}, 1), // This needs to be 1-buffered so that we queue a pull if we're busy when it comes.
errorsMut: sync.NewMutex(), errorsMut: sync.NewMutex(),
blockStats: make(map[string]int),
blockStatsMut: sync.NewMutex(),
} }
f.configureCopiersAndPullers() f.configureCopiersAndPullers()
@ -886,10 +885,10 @@ func (f *sendReceiveFolder) renameFile(source, target protocol.FileInfo) {
} }
if err == nil { if err == nil {
f.blockStatsMut.Lock() blockStatsMut.Lock()
f.blockStats["total"] += len(target.Blocks) blockStats["total"] += len(target.Blocks)
f.blockStats["renamed"] += len(target.Blocks) blockStats["renamed"] += len(target.Blocks)
f.blockStatsMut.Unlock() blockStatsMut.Unlock()
// The file was renamed, so we have handled both the necessary delete // The file was renamed, so we have handled both the necessary delete
// of the source and the creation of the target. Fix-up the metadata, // of the source and the creation of the target. Fix-up the metadata,
@ -1457,14 +1456,16 @@ func (f *sendReceiveFolder) finisherRoutine(in <-chan *sharedPullerState) {
if err != nil { if err != nil {
f.newError("finisher", state.file.Name, err) f.newError("finisher", state.file.Name, err)
} else { } else {
f.blockStatsMut.Lock() blockStatsMut.Lock()
f.blockStats["total"] += state.reused + state.copyTotal + state.pullTotal blockStats["total"] += state.reused + state.copyTotal + state.pullTotal
f.blockStats["reused"] += state.reused blockStats["reused"] += state.reused
f.blockStats["pulled"] += state.pullTotal blockStats["pulled"] += state.pullTotal
f.blockStats["copyOrigin"] += state.copyOrigin // copyOriginShifted is counted towards copyOrigin due to progress bar reasons
f.blockStats["copyOriginShifted"] += state.copyOriginShifted // for reporting reasons we want to separate these.
f.blockStats["copyElsewhere"] += state.copyTotal - state.copyOrigin blockStats["copyOrigin"] += state.copyOrigin - state.copyOriginShifted
f.blockStatsMut.Unlock() blockStats["copyOriginShifted"] += state.copyOriginShifted
blockStats["copyElsewhere"] += state.copyTotal - state.copyOrigin
blockStatsMut.Unlock()
} }
events.Default.Log(events.ItemFinished, map[string]interface{}{ events.Default.Log(events.ItemFinished, map[string]interface{}{
@ -1482,16 +1483,6 @@ func (f *sendReceiveFolder) finisherRoutine(in <-chan *sharedPullerState) {
} }
} }
func (f *sendReceiveFolder) BlockStats() map[string]int {
f.blockStatsMut.Lock()
stats := make(map[string]int)
for k, v := range f.blockStats {
stats[k] = v
}
f.blockStatsMut.Unlock()
return stats
}
// Moves the given filename to the front of the job queue // Moves the given filename to the front of the job queue
func (f *sendReceiveFolder) BringToFront(filename string) { func (f *sendReceiveFolder) BringToFront(filename string) {
f.queue.BringToFront(filename) f.queue.BringToFront(filename)