mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 14:50:56 +00:00
This adds a statistic to track the last connection duration per device. It isn't used for much in this PR, but it's available for #7223 to use in deciding how to order device connection attempts (deprioritizing devices that just dropped our connection the last time).
This commit is contained in:
parent
c48eb4241a
commit
b13b15758d
@ -87,7 +87,7 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
|
|||||||
return internalConn{}, errors.Wrap(err, "open stream")
|
return internalConn{}, errors.Wrap(err, "open stream")
|
||||||
}
|
}
|
||||||
|
|
||||||
return internalConn{&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, quicPriority}, nil
|
return newInternalConn(&quicTlsConn{session, stream, createdConn}, connTypeQUICClient, quicPriority), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type quicDialerFactory struct {
|
type quicDialerFactory struct {
|
||||||
|
@ -150,7 +150,7 @@ func (t *quicListener) serve(ctx context.Context) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t.conns <- internalConn{&quicTlsConn{session, stream, nil}, connTypeQUICServer, quicPriority}
|
t.conns <- newInternalConn(&quicTlsConn{session, stream, nil}, connTypeQUICServer, quicPriority)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ func (d *relayDialer) Dial(ctx context.Context, id protocol.DeviceID, uri *url.U
|
|||||||
return internalConn{}, err
|
return internalConn{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return internalConn{tc, connTypeRelayClient, relayPriority}, nil
|
return newInternalConn(tc, connTypeRelayClient, relayPriority), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type relayDialerFactory struct{}
|
type relayDialerFactory struct{}
|
||||||
|
@ -105,7 +105,7 @@ func (t *relayListener) serve(ctx context.Context) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t.conns <- internalConn{tc, connTypeRelayServer, relayPriority}
|
t.conns <- newInternalConn(tc, connTypeRelayServer, relayPriority)
|
||||||
|
|
||||||
// Poor mans notifier that informs the connection service that the
|
// Poor mans notifier that informs the connection service that the
|
||||||
// relay URI has changed. This can only happen when we connect to a
|
// relay URI has changed. This can only happen when we connect to a
|
||||||
|
@ -35,8 +35,9 @@ type tlsConn interface {
|
|||||||
// came from (type, priority).
|
// came from (type, priority).
|
||||||
type internalConn struct {
|
type internalConn struct {
|
||||||
tlsConn
|
tlsConn
|
||||||
connType connType
|
connType connType
|
||||||
priority int
|
priority int
|
||||||
|
establishedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type connType int
|
type connType int
|
||||||
@ -82,6 +83,15 @@ func (t connType) Transport() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newInternalConn(tc tlsConn, connType connType, priority int) internalConn {
|
||||||
|
return internalConn{
|
||||||
|
tlsConn: tc,
|
||||||
|
connType: connType,
|
||||||
|
priority: priority,
|
||||||
|
establishedAt: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c internalConn) Close() error {
|
func (c internalConn) Close() error {
|
||||||
// *tls.Conn.Close() does more than it says on the tin. Specifically, it
|
// *tls.Conn.Close() does more than it says on the tin. Specifically, it
|
||||||
// sends a TLS alert message, which might block forever if the
|
// sends a TLS alert message, which might block forever if the
|
||||||
@ -119,6 +129,10 @@ func (c internalConn) Transport() string {
|
|||||||
return transport + "6"
|
return transport + "6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c internalConn) EstablishedAt() time.Time {
|
||||||
|
return c.establishedAt
|
||||||
|
}
|
||||||
|
|
||||||
func (c internalConn) String() string {
|
func (c internalConn) String() string {
|
||||||
return fmt.Sprintf("%s-%s/%s/%s", c.LocalAddr(), c.RemoteAddr(), c.Type(), c.Crypto())
|
return fmt.Sprintf("%s-%s/%s/%s", c.LocalAddr(), c.RemoteAddr(), c.Type(), c.Crypto())
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func (d *tcpDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL)
|
|||||||
return internalConn{}, err
|
return internalConn{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return internalConn{tc, connTypeTCPClient, tcpPriority}, nil
|
return newInternalConn(tc, connTypeTCPClient, tcpPriority), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcpDialerFactory struct{}
|
type tcpDialerFactory struct{}
|
||||||
|
@ -137,7 +137,7 @@ func (t *tcpListener) serve(ctx context.Context) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
t.conns <- internalConn{tc, connTypeTCPServer, tcpPriority}
|
t.conns <- newInternalConn(tc, connTypeTCPServer, tcpPriority)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ func NewModel(cfg config.Wrapper, id protocol.DeviceID, clientName, clientVersio
|
|||||||
started: make(chan struct{}),
|
started: make(chan struct{}),
|
||||||
}
|
}
|
||||||
for devID := range cfg.Devices() {
|
for devID := range cfg.Devices() {
|
||||||
m.deviceStatRefs[devID] = stats.NewDeviceStatisticsReference(m.db, devID.String())
|
m.deviceStatRefs[devID] = stats.NewDeviceStatisticsReference(m.db, devID)
|
||||||
}
|
}
|
||||||
m.Add(m.progressEmitter)
|
m.Add(m.progressEmitter)
|
||||||
m.Add(svcutil.AsService(m.serve, m.String()))
|
m.Add(svcutil.AsService(m.serve, m.String()))
|
||||||
@ -1684,6 +1684,7 @@ func (m *model) Closed(conn protocol.Connection, err error) {
|
|||||||
m.pmut.Unlock()
|
m.pmut.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(m.conn, device)
|
delete(m.conn, device)
|
||||||
delete(m.connRequestLimiters, device)
|
delete(m.connRequestLimiters, device)
|
||||||
delete(m.helloMessages, device)
|
delete(m.helloMessages, device)
|
||||||
@ -1695,6 +1696,7 @@ func (m *model) Closed(conn protocol.Connection, err error) {
|
|||||||
m.pmut.Unlock()
|
m.pmut.Unlock()
|
||||||
|
|
||||||
m.progressEmitter.temporaryIndexUnsubscribe(conn)
|
m.progressEmitter.temporaryIndexUnsubscribe(conn)
|
||||||
|
m.deviceDidClose(device, time.Since(conn.EstablishedAt()))
|
||||||
|
|
||||||
l.Infof("Connection to %s at %s closed: %v", device, conn, err)
|
l.Infof("Connection to %s at %s closed: %v", device, conn, err)
|
||||||
m.evLogger.Log(events.DeviceDisconnected, map[string]string{
|
m.evLogger.Log(events.DeviceDisconnected, map[string]string{
|
||||||
@ -2175,7 +2177,16 @@ func (m *model) deviceWasSeen(deviceID protocol.DeviceID) {
|
|||||||
sr, ok := m.deviceStatRefs[deviceID]
|
sr, ok := m.deviceStatRefs[deviceID]
|
||||||
m.fmut.RUnlock()
|
m.fmut.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
sr.WasSeen()
|
_ = sr.WasSeen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *model) deviceDidClose(deviceID protocol.DeviceID, duration time.Duration) {
|
||||||
|
m.fmut.RLock()
|
||||||
|
sr, ok := m.deviceStatRefs[deviceID]
|
||||||
|
m.fmut.RUnlock()
|
||||||
|
if ok {
|
||||||
|
_ = sr.LastConnectionDuration(duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2674,7 +2685,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
|
|||||||
for deviceID, toCfg := range toDevices {
|
for deviceID, toCfg := range toDevices {
|
||||||
fromCfg, ok := fromDevices[deviceID]
|
fromCfg, ok := fromDevices[deviceID]
|
||||||
if !ok {
|
if !ok {
|
||||||
sr := stats.NewDeviceStatisticsReference(m.db, deviceID.String())
|
sr := stats.NewDeviceStatisticsReference(m.db, deviceID)
|
||||||
m.fmut.Lock()
|
m.fmut.Lock()
|
||||||
m.deviceStatRefs[deviceID] = sr
|
m.deviceStatRefs[deviceID] = sr
|
||||||
m.fmut.Unlock()
|
m.fmut.Unlock()
|
||||||
|
@ -152,6 +152,7 @@ type ConnectionInfo interface {
|
|||||||
Priority() int
|
Priority() int
|
||||||
String() string
|
String() string
|
||||||
Crypto() string
|
Crypto() string
|
||||||
|
EstablishedAt() time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type rawConnection struct {
|
type rawConnection struct {
|
||||||
|
@ -10,26 +10,34 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/lib/db"
|
"github.com/syncthing/syncthing/lib/db"
|
||||||
|
"github.com/syncthing/syncthing/lib/db/backend"
|
||||||
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
lastSeenKey = "lastSeen"
|
||||||
|
connDurationKey = "lastConnDuration"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeviceStatistics struct {
|
type DeviceStatistics struct {
|
||||||
LastSeen time.Time `json:"lastSeen"`
|
LastSeen time.Time `json:"lastSeen"`
|
||||||
|
LastConnectionDurationS float64 `json:"lastConnectionDurationS"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeviceStatisticsReference struct {
|
type DeviceStatisticsReference struct {
|
||||||
ns *db.NamespacedKV
|
ns *db.NamespacedKV
|
||||||
device string
|
device protocol.DeviceID
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDeviceStatisticsReference(ldb *db.Lowlevel, device string) *DeviceStatisticsReference {
|
func NewDeviceStatisticsReference(dba backend.Backend, device protocol.DeviceID) *DeviceStatisticsReference {
|
||||||
return &DeviceStatisticsReference{
|
return &DeviceStatisticsReference{
|
||||||
ns: db.NewDeviceStatisticsNamespace(ldb, device),
|
ns: db.NewDeviceStatisticsNamespace(dba, device.String()),
|
||||||
device: device,
|
device: device,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DeviceStatisticsReference) GetLastSeen() (time.Time, error) {
|
func (s *DeviceStatisticsReference) GetLastSeen() (time.Time, error) {
|
||||||
t, ok, err := s.ns.Time("lastSeen")
|
t, ok, err := s.ns.Time(lastSeenKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, err
|
return time.Time{}, err
|
||||||
} else if !ok {
|
} else if !ok {
|
||||||
@ -41,9 +49,25 @@ func (s *DeviceStatisticsReference) GetLastSeen() (time.Time, error) {
|
|||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DeviceStatisticsReference) GetLastConnectionDuration() (time.Duration, error) {
|
||||||
|
d, ok, err := s.ns.Int64(connDurationKey)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else if !ok {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
l.Debugln("stats.DeviceStatisticsReference.GetLastConnectionDuration:", s.device, d)
|
||||||
|
return time.Duration(d), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DeviceStatisticsReference) WasSeen() error {
|
func (s *DeviceStatisticsReference) WasSeen() error {
|
||||||
l.Debugln("stats.DeviceStatisticsReference.WasSeen:", s.device)
|
l.Debugln("stats.DeviceStatisticsReference.WasSeen:", s.device)
|
||||||
return s.ns.PutTime("lastSeen", time.Now())
|
return s.ns.PutTime(lastSeenKey, time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DeviceStatisticsReference) LastConnectionDuration(d time.Duration) error {
|
||||||
|
l.Debugln("stats.DeviceStatisticsReference.LastConnectionDuration:", s.device, d)
|
||||||
|
return s.ns.PutInt64(connDurationKey, d.Nanoseconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DeviceStatisticsReference) GetStatistics() (DeviceStatistics, error) {
|
func (s *DeviceStatisticsReference) GetStatistics() (DeviceStatistics, error) {
|
||||||
@ -51,7 +75,12 @@ func (s *DeviceStatisticsReference) GetStatistics() (DeviceStatistics, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return DeviceStatistics{}, err
|
return DeviceStatistics{}, err
|
||||||
}
|
}
|
||||||
|
lastConnDuration, err := s.GetLastConnectionDuration()
|
||||||
|
if err != nil {
|
||||||
|
return DeviceStatistics{}, err
|
||||||
|
}
|
||||||
return DeviceStatistics{
|
return DeviceStatistics{
|
||||||
LastSeen: lastSeen,
|
LastSeen: lastSeen,
|
||||||
|
LastConnectionDurationS: lastConnDuration.Seconds(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
// Copyright (C) 2016 The Syncthing Authors.
|
|
||||||
//
|
|
||||||
// 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/.
|
|
||||||
|
|
||||||
// The existence of this file means we get 0% test coverage rather than no
|
|
||||||
// test coverage at all. Remove when implementing an actual test.
|
|
||||||
|
|
||||||
package stats
|
|
43
lib/stats/stats_test.go
Normal file
43
lib/stats/stats_test.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (C) 2020 The Syncthing Authors.
|
||||||
|
//
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
// The existence of this file means we get 0% test coverage rather than no
|
||||||
|
// test coverage at all. Remove when implementing an actual test.
|
||||||
|
|
||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/lib/db/backend"
|
||||||
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeviceStat(t *testing.T) {
|
||||||
|
db := backend.OpenLevelDBMemory()
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
sr := NewDeviceStatisticsReference(db, protocol.LocalDeviceID)
|
||||||
|
if err := sr.WasSeen(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := sr.LastConnectionDuration(42 * time.Second); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, err := sr.GetStatistics()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d := time.Since(stat.LastSeen); d > 5*time.Second {
|
||||||
|
t.Error("Last seen far in the past:", d)
|
||||||
|
}
|
||||||
|
if d := stat.LastConnectionDurationS; d != 42 {
|
||||||
|
t.Error("Bad last duration:", d)
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrClosed = errors.New("closed")
|
var ErrClosed = errors.New("closed")
|
||||||
@ -90,6 +91,10 @@ func (f *FakeConnectionInfo) String() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeConnectionInfo) EstablishedAt() time.Time {
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
type FakeAddr struct{}
|
type FakeAddr struct{}
|
||||||
|
|
||||||
func (FakeAddr) Network() string {
|
func (FakeAddr) Network() string {
|
||||||
|
Loading…
Reference in New Issue
Block a user