mirror of
https://github.com/netbirdio/netbird.git
synced 2025-08-09 23:27:58 +02:00
[client] Fix elapsed time calculation when machine is in sleep mode (#4140)
This commit is contained in:
@ -34,14 +34,14 @@ func NewActivityRecorder() *ActivityRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLastActivities returns a snapshot of peer last activity
|
// GetLastActivities returns a snapshot of peer last activity
|
||||||
func (r *ActivityRecorder) GetLastActivities() map[string]time.Time {
|
func (r *ActivityRecorder) GetLastActivities() map[string]monotime.Time {
|
||||||
r.mu.RLock()
|
r.mu.RLock()
|
||||||
defer r.mu.RUnlock()
|
defer r.mu.RUnlock()
|
||||||
|
|
||||||
activities := make(map[string]time.Time, len(r.peers))
|
activities := make(map[string]monotime.Time, len(r.peers))
|
||||||
for key, record := range r.peers {
|
for key, record := range r.peers {
|
||||||
unixNano := record.LastActivity.Load()
|
monoTime := record.LastActivity.Load()
|
||||||
activities[key] = time.Unix(0, unixNano)
|
activities[key] = monotime.Time(monoTime)
|
||||||
}
|
}
|
||||||
return activities
|
return activities
|
||||||
}
|
}
|
||||||
@ -51,18 +51,20 @@ func (r *ActivityRecorder) UpsertAddress(publicKey string, address netip.AddrPor
|
|||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
if pr, exists := r.peers[publicKey]; exists {
|
var record *PeerRecord
|
||||||
delete(r.addrToPeer, pr.Address)
|
record, exists := r.peers[publicKey]
|
||||||
pr.Address = address
|
if exists {
|
||||||
|
delete(r.addrToPeer, record.Address)
|
||||||
|
record.Address = address
|
||||||
} else {
|
} else {
|
||||||
record := &PeerRecord{
|
record = &PeerRecord{
|
||||||
Address: address,
|
Address: address,
|
||||||
}
|
}
|
||||||
record.LastActivity.Store(monotime.Now())
|
record.LastActivity.Store(int64(monotime.Now()))
|
||||||
r.peers[publicKey] = record
|
r.peers[publicKey] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
r.addrToPeer[address] = r.peers[publicKey]
|
r.addrToPeer[address] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ActivityRecorder) Remove(publicKey string) {
|
func (r *ActivityRecorder) Remove(publicKey string) {
|
||||||
@ -84,7 +86,7 @@ func (r *ActivityRecorder) record(address netip.AddrPort) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
now := monotime.Now()
|
now := int64(monotime.Now())
|
||||||
last := record.LastActivity.Load()
|
last := record.LastActivity.Load()
|
||||||
if now-last < saveFrequency {
|
if now-last < saveFrequency {
|
||||||
return
|
return
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestActivityRecorder_GetLastActivities(t *testing.T) {
|
func TestActivityRecorder_GetLastActivities(t *testing.T) {
|
||||||
@ -17,11 +19,7 @@ func TestActivityRecorder_GetLastActivities(t *testing.T) {
|
|||||||
t.Fatalf("Expected activity for peer %s, but got none", peer)
|
t.Fatalf("Expected activity for peer %s, but got none", peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.IsZero() {
|
if monotime.Since(p) > 5*time.Second {
|
||||||
t.Fatalf("Expected activity for peer %s, but got zero", peer)
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Before(time.Now().Add(-2 * time.Minute)) {
|
|
||||||
t.Fatalf("Expected activity for peer %s to be recent, but got %v", peer, p)
|
t.Fatalf("Expected activity for peer %s to be recent, but got %v", peer, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var zeroKey wgtypes.Key
|
var zeroKey wgtypes.Key
|
||||||
@ -277,6 +279,6 @@ func (c *KernelConfigurer) GetStats() (map[string]WGStats, error) {
|
|||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *KernelConfigurer) LastActivities() map[string]time.Time {
|
func (c *KernelConfigurer) LastActivities() map[string]monotime.Time {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface/bind"
|
"github.com/netbirdio/netbird/client/iface/bind"
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
nbnet "github.com/netbirdio/netbird/util/net"
|
nbnet "github.com/netbirdio/netbird/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -223,7 +224,7 @@ func (c *WGUSPConfigurer) FullStats() (*Stats, error) {
|
|||||||
return parseStatus(c.deviceName, ipcStr)
|
return parseStatus(c.deviceName, ipcStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *WGUSPConfigurer) LastActivities() map[string]time.Time {
|
func (c *WGUSPConfigurer) LastActivities() map[string]monotime.Time {
|
||||||
return c.activityRecorder.GetLastActivities()
|
return c.activityRecorder.GetLastActivities()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface/configurer"
|
"github.com/netbirdio/netbird/client/iface/configurer"
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WGConfigurer interface {
|
type WGConfigurer interface {
|
||||||
@ -19,5 +20,5 @@ type WGConfigurer interface {
|
|||||||
Close()
|
Close()
|
||||||
GetStats() (map[string]configurer.WGStats, error)
|
GetStats() (map[string]configurer.WGStats, error)
|
||||||
FullStats() (*configurer.Stats, error)
|
FullStats() (*configurer.Stats, error)
|
||||||
LastActivities() map[string]time.Time
|
LastActivities() map[string]monotime.Time
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/iface/device"
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
||||||
"github.com/netbirdio/netbird/client/iface/wgproxy"
|
"github.com/netbirdio/netbird/client/iface/wgproxy"
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -237,7 +238,7 @@ func (w *WGIface) GetStats() (map[string]configurer.WGStats, error) {
|
|||||||
return w.configurer.GetStats()
|
return w.configurer.GetStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WGIface) LastActivities() map[string]time.Time {
|
func (w *WGIface) LastActivities() map[string]monotime.Time {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
|
@ -226,7 +226,6 @@ func (e *ConnMgr) ActivatePeer(ctx context.Context, conn *peer.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if found := e.lazyConnMgr.ActivatePeer(conn.GetKey()); found {
|
if found := e.lazyConnMgr.ActivatePeer(conn.GetKey()); found {
|
||||||
conn.Log.Infof("activated peer from inactive state")
|
|
||||||
if err := conn.Open(ctx); err != nil {
|
if err := conn.Open(ctx); err != nil {
|
||||||
conn.Log.Errorf("failed to open connection: %v", err)
|
conn.Log.Errorf("failed to open connection: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/server/store"
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
"github.com/netbirdio/netbird/management/server/types"
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
relayClient "github.com/netbirdio/netbird/relay/client"
|
relayClient "github.com/netbirdio/netbird/relay/client"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
signal "github.com/netbirdio/netbird/signal/client"
|
signal "github.com/netbirdio/netbird/signal/client"
|
||||||
@ -96,7 +97,7 @@ type MockWGIface struct {
|
|||||||
GetInterfaceGUIDStringFunc func() (string, error)
|
GetInterfaceGUIDStringFunc func() (string, error)
|
||||||
GetProxyFunc func() wgproxy.Proxy
|
GetProxyFunc func() wgproxy.Proxy
|
||||||
GetNetFunc func() *netstack.Net
|
GetNetFunc func() *netstack.Net
|
||||||
LastActivitiesFunc func() map[string]time.Time
|
LastActivitiesFunc func() map[string]monotime.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockWGIface) FullStats() (*configurer.Stats, error) {
|
func (m *MockWGIface) FullStats() (*configurer.Stats, error) {
|
||||||
@ -187,7 +188,7 @@ func (m *MockWGIface) GetNet() *netstack.Net {
|
|||||||
return m.GetNetFunc()
|
return m.GetNetFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockWGIface) LastActivities() map[string]time.Time {
|
func (m *MockWGIface) LastActivities() map[string]monotime.Time {
|
||||||
if m.LastActivitiesFunc != nil {
|
if m.LastActivitiesFunc != nil {
|
||||||
return m.LastActivitiesFunc()
|
return m.LastActivitiesFunc()
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/iface/device"
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
||||||
"github.com/netbirdio/netbird/client/iface/wgproxy"
|
"github.com/netbirdio/netbird/client/iface/wgproxy"
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
)
|
)
|
||||||
|
|
||||||
type wgIfaceBase interface {
|
type wgIfaceBase interface {
|
||||||
@ -38,5 +39,5 @@ type wgIfaceBase interface {
|
|||||||
GetStats() (map[string]configurer.WGStats, error)
|
GetStats() (map[string]configurer.WGStats, error)
|
||||||
GetNet() *netstack.Net
|
GetNet() *netstack.Net
|
||||||
FullStats() (*configurer.Stats, error)
|
FullStats() (*configurer.Stats, error)
|
||||||
LastActivities() map[string]time.Time
|
LastActivities() map[string]monotime.Time
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func (d *Listener) ReadPackets() {
|
|||||||
n, remoteAddr, err := d.conn.ReadFromUDP(make([]byte, 1))
|
n, remoteAddr, err := d.conn.ReadFromUDP(make([]byte, 1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if d.isClosed.Load() {
|
if d.isClosed.Load() {
|
||||||
d.peerCfg.Log.Debugf("exit from activity listener")
|
d.peerCfg.Log.Infof("exit from activity listener")
|
||||||
} else {
|
} else {
|
||||||
d.peerCfg.Log.Errorf("failed to read from activity listener: %s", err)
|
d.peerCfg.Log.Errorf("failed to read from activity listener: %s", err)
|
||||||
}
|
}
|
||||||
@ -59,9 +59,11 @@ func (d *Listener) ReadPackets() {
|
|||||||
d.peerCfg.Log.Warnf("received %d bytes from %s, too short", n, remoteAddr)
|
d.peerCfg.Log.Warnf("received %d bytes from %s, too short", n, remoteAddr)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
d.peerCfg.Log.Infof("activity detected")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d.peerCfg.Log.Debugf("removing lazy endpoint: %s", d.endpoint.String())
|
||||||
if err := d.removeEndpoint(); err != nil {
|
if err := d.removeEndpoint(); err != nil {
|
||||||
d.peerCfg.Log.Errorf("failed to remove endpoint: %s", err)
|
d.peerCfg.Log.Errorf("failed to remove endpoint: %s", err)
|
||||||
}
|
}
|
||||||
@ -71,7 +73,7 @@ func (d *Listener) ReadPackets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Listener) Close() {
|
func (d *Listener) Close() {
|
||||||
d.peerCfg.Log.Infof("closing listener: %s", d.conn.LocalAddr().String())
|
d.peerCfg.Log.Infof("closing activity listener: %s", d.conn.LocalAddr().String())
|
||||||
d.isClosed.Store(true)
|
d.isClosed.Store(true)
|
||||||
|
|
||||||
if err := d.conn.Close(); err != nil {
|
if err := d.conn.Close(); err != nil {
|
||||||
@ -81,7 +83,6 @@ func (d *Listener) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Listener) removeEndpoint() error {
|
func (d *Listener) removeEndpoint() error {
|
||||||
d.peerCfg.Log.Debugf("removing lazy endpoint: %s", d.endpoint.String())
|
|
||||||
return d.wgIface.RemovePeer(d.peerCfg.PublicKey)
|
return d.wgIface.RemovePeer(d.peerCfg.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -18,7 +19,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type WgInterface interface {
|
type WgInterface interface {
|
||||||
LastActivities() map[string]time.Time
|
LastActivities() map[string]monotime.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
@ -124,6 +125,7 @@ func (m *Manager) checkStats() (map[string]struct{}, error) {
|
|||||||
|
|
||||||
idlePeers := make(map[string]struct{})
|
idlePeers := make(map[string]struct{})
|
||||||
|
|
||||||
|
checkTime := time.Now()
|
||||||
for peerID, peerCfg := range m.interestedPeers {
|
for peerID, peerCfg := range m.interestedPeers {
|
||||||
lastActive, ok := lastActivities[peerID]
|
lastActive, ok := lastActivities[peerID]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -132,8 +134,9 @@ func (m *Manager) checkStats() (map[string]struct{}, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Since(lastActive) > m.inactivityThreshold {
|
since := monotime.Since(lastActive)
|
||||||
peerCfg.Log.Infof("peer is inactive since: %v", lastActive)
|
if since > m.inactivityThreshold {
|
||||||
|
peerCfg.Log.Infof("peer is inactive since time: %s", checkTime.Add(-since).String())
|
||||||
idlePeers[peerID] = struct{}{}
|
idlePeers[peerID] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,14 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockWgInterface struct {
|
type mockWgInterface struct {
|
||||||
lastActivities map[string]time.Time
|
lastActivities map[string]monotime.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockWgInterface) LastActivities() map[string]time.Time {
|
func (m *mockWgInterface) LastActivities() map[string]monotime.Time {
|
||||||
return m.lastActivities
|
return m.lastActivities
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,8 +24,8 @@ func TestPeerTriggersInactivity(t *testing.T) {
|
|||||||
peerID := "peer1"
|
peerID := "peer1"
|
||||||
|
|
||||||
wgMock := &mockWgInterface{
|
wgMock := &mockWgInterface{
|
||||||
lastActivities: map[string]time.Time{
|
lastActivities: map[string]monotime.Time{
|
||||||
peerID: time.Now().Add(-20 * time.Minute),
|
peerID: monotime.Time(int64(monotime.Now()) - int64(20*time.Minute)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,8 +65,8 @@ func TestPeerTriggersActivity(t *testing.T) {
|
|||||||
peerID := "peer1"
|
peerID := "peer1"
|
||||||
|
|
||||||
wgMock := &mockWgInterface{
|
wgMock := &mockWgInterface{
|
||||||
lastActivities: map[string]time.Time{
|
lastActivities: map[string]monotime.Time{
|
||||||
peerID: time.Now().Add(-5 * time.Minute),
|
peerID: monotime.Time(int64(monotime.Now()) - int64(5*time.Minute)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,12 +258,13 @@ func (m *Manager) ActivatePeer(peerID string) (found bool) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg.Log.Infof("activate peer from inactive state by remote signal message")
|
||||||
|
|
||||||
if !m.activateSinglePeer(cfg, mp) {
|
if !m.activateSinglePeer(cfg, mp) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
m.activateHAGroupPeers(cfg)
|
m.activateHAGroupPeers(cfg)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,12 +572,12 @@ func (m *Manager) onPeerInactivityTimedOut(peerIDs map[string]struct{}) {
|
|||||||
// this is blocking operation, potentially can be optimized
|
// this is blocking operation, potentially can be optimized
|
||||||
m.peerStore.PeerConnIdle(mp.peerCfg.PublicKey)
|
m.peerStore.PeerConnIdle(mp.peerCfg.PublicKey)
|
||||||
|
|
||||||
mp.peerCfg.Log.Infof("start activity monitor")
|
|
||||||
|
|
||||||
mp.expectedWatcher = watcherActivity
|
mp.expectedWatcher = watcherActivity
|
||||||
|
|
||||||
m.inactivityManager.RemovePeer(mp.peerCfg.PublicKey)
|
m.inactivityManager.RemovePeer(mp.peerCfg.PublicKey)
|
||||||
|
|
||||||
|
mp.peerCfg.Log.Infof("start activity monitor")
|
||||||
|
|
||||||
if err := m.activityManager.MonitorPeerActivity(*mp.peerCfg); err != nil {
|
if err := m.activityManager.MonitorPeerActivity(*mp.peerCfg); err != nil {
|
||||||
mp.peerCfg.Log.Errorf("failed to create activity monitor: %v", err)
|
mp.peerCfg.Log.Errorf("failed to create activity monitor: %v", err)
|
||||||
continue
|
continue
|
||||||
|
@ -6,11 +6,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/monotime"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WGIface interface {
|
type WGIface interface {
|
||||||
RemovePeer(peerKey string) error
|
RemovePeer(peerKey string) error
|
||||||
UpdatePeer(peerKey string, allowedIps []netip.Prefix, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error
|
UpdatePeer(peerKey string, allowedIps []netip.Prefix, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error
|
||||||
IsUserspaceBind() bool
|
IsUserspaceBind() bool
|
||||||
LastActivities() map[string]time.Time
|
LastActivities() map[string]monotime.Time
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ var (
|
|||||||
baseWallNano int64
|
baseWallNano int64
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Time int64
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
baseWallTime = time.Now()
|
baseWallTime = time.Now()
|
||||||
baseWallNano = baseWallTime.UnixNano()
|
baseWallNano = baseWallTime.UnixNano()
|
||||||
@ -23,7 +25,11 @@ func init() {
|
|||||||
// and using time.Since() for elapsed calculation, this avoids repeated
|
// and using time.Since() for elapsed calculation, this avoids repeated
|
||||||
// time.Now() calls and leverages Go's internal monotonic clock for
|
// time.Now() calls and leverages Go's internal monotonic clock for
|
||||||
// efficient duration measurement.
|
// efficient duration measurement.
|
||||||
func Now() int64 {
|
func Now() Time {
|
||||||
elapsed := time.Since(baseWallTime)
|
elapsed := time.Since(baseWallTime)
|
||||||
return baseWallNano + int64(elapsed)
|
return Time(baseWallNano + int64(elapsed))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Since(t Time) time.Duration {
|
||||||
|
return time.Duration(Now() - t)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user