|
|
|
|
@ -11,6 +11,7 @@ package appc |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"net/netip" |
|
|
|
|
"slices" |
|
|
|
|
"strings" |
|
|
|
|
@ -21,6 +22,7 @@ import ( |
|
|
|
|
"golang.org/x/net/dns/dnsmessage" |
|
|
|
|
"tailscale.com/types/logger" |
|
|
|
|
"tailscale.com/types/views" |
|
|
|
|
"tailscale.com/util/clientmetric" |
|
|
|
|
"tailscale.com/util/dnsname" |
|
|
|
|
"tailscale.com/util/execqueue" |
|
|
|
|
"tailscale.com/util/mak" |
|
|
|
|
@ -78,6 +80,42 @@ type RouteAdvertiser interface { |
|
|
|
|
UnadvertiseRoute(...netip.Prefix) error |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
metricStoreRoutesRateBuckets = []int64{1, 2, 3, 4, 5, 10, 100, 1000} |
|
|
|
|
metricStoreRoutesNBuckets = []int64{1, 2, 3, 4, 5, 10, 100, 1000, 10000} |
|
|
|
|
metricStoreRoutesRate []*clientmetric.Metric |
|
|
|
|
metricStoreRoutesN []*clientmetric.Metric |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func initMetricStoreRoutes() { |
|
|
|
|
for _, n := range metricStoreRoutesRateBuckets { |
|
|
|
|
metricStoreRoutesRate = append(metricStoreRoutesRate, clientmetric.NewCounter(fmt.Sprintf("appc_store_routes_rate_%d", n))) |
|
|
|
|
} |
|
|
|
|
metricStoreRoutesRate = append(metricStoreRoutesRate, clientmetric.NewCounter("appc_store_routes_rate_over")) |
|
|
|
|
for _, n := range metricStoreRoutesNBuckets { |
|
|
|
|
metricStoreRoutesN = append(metricStoreRoutesN, clientmetric.NewCounter(fmt.Sprintf("appc_store_routes_n_routes_%d", n))) |
|
|
|
|
} |
|
|
|
|
metricStoreRoutesN = append(metricStoreRoutesN, clientmetric.NewCounter("appc_store_routes_n_routes_over")) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func recordMetric(val int64, buckets []int64, metrics []*clientmetric.Metric) { |
|
|
|
|
if len(buckets) < 1 { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
// finds the first bucket where val <=, or len(buckets) if none match
|
|
|
|
|
// for bucket values of 1, 10, 100; 0-1 goes to [0], 2-10 goes to [1], 11-100 goes to [2], 101+ goes to [3]
|
|
|
|
|
bucket, _ := slices.BinarySearch(buckets, val) |
|
|
|
|
metrics[bucket].Add(1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func metricStoreRoutes(rate, nRoutes int64) { |
|
|
|
|
if len(metricStoreRoutesRate) == 0 { |
|
|
|
|
initMetricStoreRoutes() |
|
|
|
|
} |
|
|
|
|
recordMetric(rate, metricStoreRoutesRateBuckets, metricStoreRoutesRate) |
|
|
|
|
recordMetric(nRoutes, metricStoreRoutesNBuckets, metricStoreRoutesN) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// RouteInfo is a data structure used to persist the in memory state of an AppConnector
|
|
|
|
|
// so that we can know, even after a restart, which routes came from ACLs and which were
|
|
|
|
|
// learned from domains.
|
|
|
|
|
@ -141,6 +179,7 @@ func NewAppConnector(logf logger.Logf, routeAdvertiser RouteAdvertiser, routeInf |
|
|
|
|
} |
|
|
|
|
ac.writeRateMinute = newRateLogger(time.Now, time.Minute, func(c int64, s time.Time, l int64) { |
|
|
|
|
ac.logf("routeInfo write rate: %d in minute starting at %v (%d routes)", c, s, l) |
|
|
|
|
metricStoreRoutes(c, l) |
|
|
|
|
}) |
|
|
|
|
ac.writeRateDay = newRateLogger(time.Now, 24*time.Hour, func(c int64, s time.Time, l int64) { |
|
|
|
|
ac.logf("routeInfo write rate: %d in 24 hours starting at %v (%d routes)", c, s, l) |
|
|
|
|
|