@ -15,6 +15,7 @@ import (
"slices"
"slices"
"strings"
"strings"
"sync"
"sync"
"time"
xmaps "golang.org/x/exp/maps"
xmaps "golang.org/x/exp/maps"
"golang.org/x/net/dns/dnsmessage"
"golang.org/x/net/dns/dnsmessage"
@ -26,6 +27,46 @@ import (
"tailscale.com/util/slicesx"
"tailscale.com/util/slicesx"
)
)
// rateLogger responds to calls to update by adding a count for the current period and
// calling the callback if any previous period has finished since update was last called
type rateLogger struct {
interval time . Duration
start time . Time
periodStart time . Time
periodCount int64
now func ( ) time . Time
callback func ( int64 , time . Time )
}
func ( rl * rateLogger ) currentIntervalStart ( now time . Time ) time . Time {
millisSince := now . Sub ( rl . start ) . Milliseconds ( ) % rl . interval . Milliseconds ( )
return now . Add ( - ( time . Duration ( millisSince ) ) * time . Millisecond )
}
func ( rl * rateLogger ) update ( ) {
now := rl . now ( )
periodEnd := rl . periodStart . Add ( rl . interval )
if periodEnd . Before ( now ) {
if rl . periodCount != 0 {
rl . callback ( rl . periodCount , rl . periodStart )
}
rl . periodCount = 0
rl . periodStart = rl . currentIntervalStart ( now )
}
rl . periodCount ++
}
func newRateLogger ( now func ( ) time . Time , interval time . Duration , callback func ( int64 , time . Time ) ) * rateLogger {
nowTime := now ( )
return & rateLogger {
callback : callback ,
now : now ,
interval : interval ,
start : nowTime ,
periodStart : nowTime ,
}
}
// RouteAdvertiser is an interface that allows the AppConnector to advertise
// RouteAdvertiser is an interface that allows the AppConnector to advertise
// newly discovered routes that need to be served through the AppConnector.
// newly discovered routes that need to be served through the AppConnector.
type RouteAdvertiser interface {
type RouteAdvertiser interface {
@ -81,6 +122,9 @@ type AppConnector struct {
// queue provides ordering for update operations
// queue provides ordering for update operations
queue execqueue . ExecQueue
queue execqueue . ExecQueue
writeRateMinute * rateLogger
writeRateDay * rateLogger
}
}
// NewAppConnector creates a new AppConnector.
// NewAppConnector creates a new AppConnector.
@ -95,6 +139,12 @@ func NewAppConnector(logf logger.Logf, routeAdvertiser RouteAdvertiser, routeInf
ac . wildcards = routeInfo . Wildcards
ac . wildcards = routeInfo . Wildcards
ac . controlRoutes = routeInfo . Control
ac . controlRoutes = routeInfo . Control
}
}
ac . writeRateMinute = newRateLogger ( time . Now , time . Minute , func ( c int64 , s time . Time ) {
ac . logf ( "routeInfo write rate: %d in minute starting at %v" , c , s )
} )
ac . writeRateDay = newRateLogger ( time . Now , 24 * time . Hour , func ( c int64 , s time . Time ) {
ac . logf ( "routeInfo write rate: %d in 24 hours starting at %v" , c , s )
} )
return ac
return ac
}
}
@ -109,6 +159,8 @@ func (e *AppConnector) storeRoutesLocked() error {
if ! e . ShouldStoreRoutes ( ) {
if ! e . ShouldStoreRoutes ( ) {
return nil
return nil
}
}
e . writeRateMinute . update ( )
e . writeRateDay . update ( )
return e . storeRoutesFunc ( & RouteInfo {
return e . storeRoutesFunc ( & RouteInfo {
Control : e . controlRoutes ,
Control : e . controlRoutes ,
Domains : e . domains ,
Domains : e . domains ,