mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 14:50:56 +00:00
Add stats package and node related statistics model
This commit is contained in:
parent
eee702f299
commit
0cdb0daa8c
@ -6,6 +6,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@ -24,7 +25,6 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"crypto/tls"
|
||||
"code.google.com/p/go.crypto/bcrypt"
|
||||
"github.com/syncthing/syncthing/auto"
|
||||
"github.com/syncthing/syncthing/config"
|
||||
|
@ -125,6 +125,7 @@ The following enviroment variables are interpreted by syncthing:
|
||||
- "net" (the main package; connections & network messages)
|
||||
- "model" (the model package)
|
||||
- "scanner" (the scanner package)
|
||||
- "stats" (the stats package)
|
||||
- "upnp" (the upnp package)
|
||||
- "xdr" (the xdr package)
|
||||
- "all" (all of the above)
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/syncthing/syncthing/lamport"
|
||||
"github.com/syncthing/syncthing/protocol"
|
||||
"github.com/syncthing/syncthing/scanner"
|
||||
"github.com/syncthing/syncthing/stats"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
||||
@ -72,11 +73,12 @@ type Model struct {
|
||||
clientName string
|
||||
clientVersion string
|
||||
|
||||
repoCfgs map[string]config.RepositoryConfiguration // repo -> cfg
|
||||
repoFiles map[string]*files.Set // repo -> files
|
||||
repoNodes map[string][]protocol.NodeID // repo -> nodeIDs
|
||||
nodeRepos map[protocol.NodeID][]string // nodeID -> repos
|
||||
rmut sync.RWMutex // protects the above
|
||||
repoCfgs map[string]config.RepositoryConfiguration // repo -> cfg
|
||||
repoFiles map[string]*files.Set // repo -> files
|
||||
repoNodes map[string][]protocol.NodeID // repo -> nodeIDs
|
||||
nodeRepos map[protocol.NodeID][]string // nodeID -> repos
|
||||
nodeStatRefs map[protocol.NodeID]*stats.NodeStatisticsReference // nodeID -> statsRef
|
||||
rmut sync.RWMutex // protects the above
|
||||
|
||||
repoState map[string]repoState // repo -> state
|
||||
repoStateChanged map[string]time.Time // repo -> time when state changed
|
||||
@ -114,6 +116,7 @@ func NewModel(indexDir string, cfg *config.Configuration, nodeName, clientName,
|
||||
repoFiles: make(map[string]*files.Set),
|
||||
repoNodes: make(map[string][]protocol.NodeID),
|
||||
nodeRepos: make(map[protocol.NodeID][]string),
|
||||
nodeStatRefs: make(map[protocol.NodeID]*stats.NodeStatisticsReference),
|
||||
repoState: make(map[string]repoState),
|
||||
repoStateChanged: make(map[string]time.Time),
|
||||
protoConn: make(map[protocol.NodeID]protocol.Connection),
|
||||
@ -122,6 +125,10 @@ func NewModel(indexDir string, cfg *config.Configuration, nodeName, clientName,
|
||||
sentLocalVer: make(map[protocol.NodeID]map[string]uint64),
|
||||
}
|
||||
|
||||
for _, node := range cfg.Nodes {
|
||||
m.nodeStatRefs[node.NodeID] = stats.NewNodeStatisticsReference(db, node.NodeID)
|
||||
}
|
||||
|
||||
var timeout = 20 * 60 // seconds
|
||||
if t := os.Getenv("STDEADLOCKTIMEOUT"); len(t) > 0 {
|
||||
it, err := strconv.Atoi(t)
|
||||
@ -199,6 +206,15 @@ func (m *Model) ConnectionStats() map[string]ConnectionInfo {
|
||||
return res
|
||||
}
|
||||
|
||||
// Returns statistics about each node
|
||||
func (m *Model) NodeStatistics() map[string]stats.NodeStatistics {
|
||||
var res = make(map[string]stats.NodeStatistics)
|
||||
for _, node := range m.cfg.Nodes {
|
||||
res[node.NodeID.String()] = m.nodeStatRefs[node.NodeID].GetStatistics()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Returns the completion status, in percent, for the given node and repo.
|
||||
func (m *Model) Completion(node protocol.NodeID, repo string) float64 {
|
||||
var tot int64
|
||||
@ -535,6 +551,9 @@ func (cf cFiler) CurrentFile(file string) protocol.FileInfo {
|
||||
func (m *Model) ConnectedTo(nodeID protocol.NodeID) bool {
|
||||
m.pmut.RLock()
|
||||
_, ok := m.protoConn[nodeID]
|
||||
if ok {
|
||||
m.nodeStatRefs[nodeID].WasSeen()
|
||||
}
|
||||
m.pmut.RUnlock()
|
||||
return ok
|
||||
}
|
||||
@ -563,6 +582,7 @@ func (m *Model) AddConnection(rawConn io.Closer, protoConn protocol.Connection)
|
||||
fs := m.repoFiles[repo]
|
||||
go sendIndexes(protoConn, repo, fs)
|
||||
}
|
||||
m.nodeStatRefs[nodeID].WasSeen()
|
||||
m.rmut.RUnlock()
|
||||
m.pmut.Unlock()
|
||||
}
|
||||
|
17
stats/debug.go
Executable file
17
stats/debug.go
Executable file
@ -0,0 +1,17 @@
|
||||
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package stats
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/syncthing/syncthing/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
debug = strings.Contains(os.Getenv("STTRACE"), "stats") || os.Getenv("STTRACE") == "all"
|
||||
l = logger.DefaultLogger
|
||||
)
|
10
stats/leveldb.go
Executable file
10
stats/leveldb.go
Executable file
@ -0,0 +1,10 @@
|
||||
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package stats
|
||||
|
||||
// Same key space as files/leveldb.go keyType* constants
|
||||
const (
|
||||
keyTypeNodeStatistic = iota + 30
|
||||
)
|
102
stats/node.go
Executable file
102
stats/node.go
Executable file
@ -0,0 +1,102 @@
|
||||
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package stats
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/protocol"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
||||
const (
|
||||
nodeStatisticTypeLastSeen = iota
|
||||
)
|
||||
|
||||
var nodeStatisticsTypes = []byte{
|
||||
nodeStatisticTypeLastSeen,
|
||||
}
|
||||
|
||||
type NodeStatistics struct {
|
||||
LastSeen time.Time
|
||||
}
|
||||
|
||||
type NodeStatisticsReference struct {
|
||||
db *leveldb.DB
|
||||
node protocol.NodeID
|
||||
}
|
||||
|
||||
func NewNodeStatisticsReference(db *leveldb.DB, node protocol.NodeID) *NodeStatisticsReference {
|
||||
return &NodeStatisticsReference{
|
||||
db: db,
|
||||
node: node,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *NodeStatisticsReference) key(stat byte) []byte {
|
||||
k := make([]byte, 1+1+32)
|
||||
k[0] = keyTypeNodeStatistic
|
||||
k[1] = stat
|
||||
copy(k[1+1:], s.node[:])
|
||||
return k
|
||||
}
|
||||
|
||||
func (s *NodeStatisticsReference) GetLastSeen() time.Time {
|
||||
value, err := s.db.Get(s.key(nodeStatisticTypeLastSeen), nil)
|
||||
if err != nil {
|
||||
if err != leveldb.ErrNotFound {
|
||||
l.Warnln("NodeStatisticsReference: Failed loading last seen value for", s.node, ":", err)
|
||||
}
|
||||
return time.Unix(0, 0)
|
||||
}
|
||||
|
||||
rtime := time.Time{}
|
||||
err = rtime.UnmarshalBinary(value)
|
||||
if err != nil {
|
||||
l.Warnln("NodeStatisticsReference: Failed parsing last seen value for", s.node, ":", err)
|
||||
return time.Unix(0, 0)
|
||||
}
|
||||
if debug {
|
||||
l.Debugln("stats.NodeStatisticsReference.GetLastSeen:", s.node, rtime)
|
||||
}
|
||||
return rtime
|
||||
}
|
||||
|
||||
func (s *NodeStatisticsReference) WasSeen() {
|
||||
if debug {
|
||||
l.Debugln("stats.NodeStatisticsReference.WasSeen:", s.node)
|
||||
}
|
||||
value, err := time.Now().MarshalBinary()
|
||||
if err != nil {
|
||||
l.Warnln("NodeStatisticsReference: Failed serializing last seen value for", s.node, ":", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = s.db.Put(s.key(nodeStatisticTypeLastSeen), value, nil)
|
||||
if err != nil {
|
||||
l.Warnln("Failed serializing last seen value for", s.node, ":", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Never called, maybe because it's worth while to keep the data
|
||||
// or maybe because we have no easy way of knowing that a node has been removed.
|
||||
func (s *NodeStatisticsReference) Delete() error {
|
||||
for _, stype := range nodeStatisticsTypes {
|
||||
err := s.db.Delete(s.key(stype), nil)
|
||||
if debug && err == nil {
|
||||
l.Debugln("stats.NodeStatisticsReference.Delete:", s.node, stype)
|
||||
}
|
||||
if err != nil && err != leveldb.ErrNotFound {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NodeStatisticsReference) GetStatistics() NodeStatistics {
|
||||
return NodeStatistics{
|
||||
LastSeen: s.GetLastSeen(),
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user