@ -20,6 +20,7 @@ import (
"io"
"io/ioutil"
"log"
"math"
"math/big"
"math/rand"
"os"
@ -27,6 +28,7 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"go4.org/mem"
@ -120,6 +122,7 @@ type Server struct {
multiForwarderCreated expvar . Int
multiForwarderDeleted expvar . Int
removePktForwardOther expvar . Int
avgQueueDuration * uint64 // In milliseconds; accessed atomically
mu sync . Mutex
closed bool
@ -182,6 +185,7 @@ func NewServer(privateKey key.Private, logf logger.Logf) *Server {
memSys0 : ms . Sys ,
watchers : map [ * sclient ] bool { } ,
sentTo : map [ key . Public ] map [ key . Public ] int64 { } ,
avgQueueDuration : new ( uint64 ) ,
}
s . initMetacert ( )
s . packetsRecvDisco = s . packetsRecvByKind . Get ( "disco" )
@ -611,8 +615,9 @@ func (c *sclient) handleFrameForwardPacket(ft frameType, fl uint32) error {
}
return c . sendPkt ( dst , pkt {
bs : contents ,
src : srcKey ,
bs : contents ,
enqueuedAt : time . Now ( ) ,
src : srcKey ,
} )
}
@ -665,8 +670,9 @@ func (c *sclient) handleFrameSendPacket(ft frameType, fl uint32) error {
}
p := pkt {
bs : contents ,
src : c . key ,
bs : contents ,
enqueuedAt : time . Now ( ) ,
src : c . key ,
}
return c . sendPkt ( dst , p )
}
@ -696,7 +702,7 @@ func (c *sclient) sendPkt(dst *sclient, p pkt) error {
}
select {
case <- dst . sendQueue :
case pkt := <- dst . sendQueue :
s . packetsDropped . Add ( 1 )
s . packetsDroppedQueueHead . Add ( 1 )
if verboseDropKeys [ dstKey ] {
@ -705,6 +711,7 @@ func (c *sclient) sendPkt(dst *sclient, p pkt) error {
msg := fmt . Sprintf ( "tail drop %s -> %s" , p . src . ShortString ( ) , dstKey . ShortString ( ) )
c . s . limitedLogf ( msg )
}
c . recordQueueTime ( pkt . enqueuedAt )
if debug {
c . logf ( "dropping packet from client %x queue head" , dstKey )
}
@ -927,11 +934,13 @@ type pkt struct {
// src is the who's the sender of the packet.
src key . Public
// enqueuedAt is when a packet was put onto a queue before it was sent,
// and is used for reporting metrics on the duration of packets in the queue.
enqueuedAt time . Time
// bs is the data packet bytes.
// The memory is owned by pkt.
bs [ ] byte
// TODO(danderson): enqueue time, to measure queue latency?
}
func ( c * sclient ) setPreferred ( v bool ) {
@ -959,6 +968,25 @@ func (c *sclient) setPreferred(v bool) {
}
}
// expMovingAverage returns the new moving average given the previous average,
// a new value, and an alpha decay factor.
// https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
func expMovingAverage ( prev , newValue , alpha float64 ) float64 {
return alpha * newValue + ( 1 - alpha ) * prev
}
// recordQueueTime updates the average queue duration metric after a packet has been sent.
func ( c * sclient ) recordQueueTime ( enqueuedAt time . Time ) {
elapsed := float64 ( time . Since ( enqueuedAt ) . Milliseconds ( ) )
for {
old := atomic . LoadUint64 ( c . s . avgQueueDuration )
newAvg := expMovingAverage ( math . Float64frombits ( old ) , elapsed , 0.1 )
if atomic . CompareAndSwapUint64 ( c . s . avgQueueDuration , old , math . Float64bits ( newAvg ) ) {
break
}
}
}
func ( c * sclient ) sendLoop ( ctx context . Context ) error {
defer func ( ) {
// If the sender shuts down unilaterally due to an error, close so
@ -1002,6 +1030,7 @@ func (c *sclient) sendLoop(ctx context.Context) error {
continue
case msg := <- c . sendQueue :
werr = c . sendPacket ( msg . src , msg . bs )
c . recordQueueTime ( msg . enqueuedAt )
continue
case <- keepAliveTick . C :
werr = c . sendKeepAlive ( )
@ -1025,6 +1054,7 @@ func (c *sclient) sendLoop(ctx context.Context) error {
continue
case msg := <- c . sendQueue :
werr = c . sendPacket ( msg . src , msg . bs )
c . recordQueueTime ( msg . enqueuedAt )
case <- keepAliveTick . C :
werr = c . sendKeepAlive ( )
}
@ -1290,6 +1320,9 @@ func (s *Server) ExpVar() expvar.Var {
m . Set ( "multiforwarder_created" , & s . multiForwarderCreated )
m . Set ( "multiforwarder_deleted" , & s . multiForwarderDeleted )
m . Set ( "packet_forwarder_delete_other_value" , & s . removePktForwardOther )
m . Set ( "average_queue_duration_ms" , expvar . Func ( func ( ) interface { } {
return math . Float64frombits ( atomic . LoadUint64 ( s . avgQueueDuration ) )
} ) )
var expvarVersion expvar . String
expvarVersion . Set ( version . Long )
m . Set ( "version" , & expvarVersion )