@ -9,6 +9,7 @@ import (
"context"
"context"
"crypto/x509"
"crypto/x509"
"encoding/asn1"
"encoding/asn1"
"encoding/binary"
"expvar"
"expvar"
"fmt"
"fmt"
"log"
"log"
@ -20,6 +21,7 @@ import (
"testing"
"testing"
"time"
"time"
"github.com/axiomhq/hyperloglog"
qt "github.com/frankban/quicktest"
qt "github.com/frankban/quicktest"
"go4.org/mem"
"go4.org/mem"
"golang.org/x/time/rate"
"golang.org/x/time/rate"
@ -755,6 +757,35 @@ func TestParseSSOutput(t *testing.T) {
}
}
}
}
func TestServeDebugTrafficUniqueSenders ( t * testing . T ) {
s := New ( key . NewNode ( ) , t . Logf )
defer s . Close ( )
clientKey := key . NewNode ( ) . Public ( )
c := & sclient {
key : clientKey ,
s : s ,
logf : logger . Discard ,
senderCardinality : hyperloglog . New ( ) ,
}
for i := 0 ; i < 5 ; i ++ {
c . senderCardinality . Insert ( key . NewNode ( ) . Public ( ) . AppendTo ( nil ) )
}
s . mu . Lock ( )
cs := & clientSet { }
cs . activeClient . Store ( c )
s . clients [ clientKey ] = cs
s . mu . Unlock ( )
estimate := c . EstimatedUniqueSenders ( )
t . Logf ( "Estimated unique senders: %d" , estimate )
if estimate < 4 || estimate > 6 {
t . Errorf ( "EstimatedUniqueSenders() = %d, want ~5 (4-6 range)" , estimate )
}
}
func TestGetPerClientSendQueueDepth ( t * testing . T ) {
func TestGetPerClientSendQueueDepth ( t * testing . T ) {
c := qt . New ( t )
c := qt . New ( t )
envKey := "TS_DEBUG_DERP_PER_CLIENT_SEND_QUEUE_DEPTH"
envKey := "TS_DEBUG_DERP_PER_CLIENT_SEND_QUEUE_DEPTH"
@ -780,3 +811,167 @@ func TestGetPerClientSendQueueDepth(t *testing.T) {
} )
} )
}
}
}
}
func TestSenderCardinality ( t * testing . T ) {
s := New ( key . NewNode ( ) , t . Logf )
defer s . Close ( )
c := & sclient {
key : key . NewNode ( ) . Public ( ) ,
s : s ,
logf : logger . WithPrefix ( t . Logf , "test client: " ) ,
}
if got := c . EstimatedUniqueSenders ( ) ; got != 0 {
t . Errorf ( "EstimatedUniqueSenders() before init = %d, want 0" , got )
}
c . senderCardinality = hyperloglog . New ( )
if got := c . EstimatedUniqueSenders ( ) ; got != 0 {
t . Errorf ( "EstimatedUniqueSenders() with no senders = %d, want 0" , got )
}
senders := make ( [ ] key . NodePublic , 10 )
for i := range senders {
senders [ i ] = key . NewNode ( ) . Public ( )
c . senderCardinality . Insert ( senders [ i ] . AppendTo ( nil ) )
}
estimate := c . EstimatedUniqueSenders ( )
t . Logf ( "Estimated unique senders after 10 inserts: %d" , estimate )
if estimate < 8 || estimate > 12 {
t . Errorf ( "EstimatedUniqueSenders() = %d, want ~10 (8-12 range)" , estimate )
}
for i := 0 ; i < 5 ; i ++ {
c . senderCardinality . Insert ( senders [ i ] . AppendTo ( nil ) )
}
estimate2 := c . EstimatedUniqueSenders ( )
t . Logf ( "Estimated unique senders after duplicates: %d" , estimate2 )
if estimate2 < 8 || estimate2 > 12 {
t . Errorf ( "EstimatedUniqueSenders() after duplicates = %d, want ~10 (8-12 range)" , estimate2 )
}
}
func TestSenderCardinality100 ( t * testing . T ) {
s := New ( key . NewNode ( ) , t . Logf )
defer s . Close ( )
c := & sclient {
key : key . NewNode ( ) . Public ( ) ,
s : s ,
logf : logger . WithPrefix ( t . Logf , "test client: " ) ,
senderCardinality : hyperloglog . New ( ) ,
}
numSenders := 100
for i := 0 ; i < numSenders ; i ++ {
c . senderCardinality . Insert ( key . NewNode ( ) . Public ( ) . AppendTo ( nil ) )
}
estimate := c . EstimatedUniqueSenders ( )
t . Logf ( "Estimated unique senders for 100 actual senders: %d" , estimate )
if estimate < 85 || estimate > 115 {
t . Errorf ( "EstimatedUniqueSenders() = %d, want ~100 (85-115 range)" , estimate )
}
}
func TestSenderCardinalityTracking ( t * testing . T ) {
s := New ( key . NewNode ( ) , t . Logf )
defer s . Close ( )
c := & sclient {
key : key . NewNode ( ) . Public ( ) ,
s : s ,
logf : logger . WithPrefix ( t . Logf , "test client: " ) ,
senderCardinality : hyperloglog . New ( ) ,
}
zeroKey := key . NodePublic { }
if zeroKey != ( key . NodePublic { } ) {
c . senderCardinality . Insert ( zeroKey . AppendTo ( nil ) )
}
if estimate := c . EstimatedUniqueSenders ( ) ; estimate != 0 {
t . Errorf ( "EstimatedUniqueSenders() after zero key = %d, want 0" , estimate )
}
sender1 := key . NewNode ( ) . Public ( )
sender2 := key . NewNode ( ) . Public ( )
if sender1 != ( key . NodePublic { } ) {
c . senderCardinality . Insert ( sender1 . AppendTo ( nil ) )
}
if sender2 != ( key . NodePublic { } ) {
c . senderCardinality . Insert ( sender2 . AppendTo ( nil ) )
}
estimate := c . EstimatedUniqueSenders ( )
t . Logf ( "Estimated unique senders after 2 senders: %d" , estimate )
if estimate < 1 || estimate > 3 {
t . Errorf ( "EstimatedUniqueSenders() = %d, want ~2 (1-3 range)" , estimate )
}
}
func BenchmarkHyperLogLogInsert ( b * testing . B ) {
hll := hyperloglog . New ( )
sender := key . NewNode ( ) . Public ( )
senderBytes := sender . AppendTo ( nil )
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
hll . Insert ( senderBytes )
}
}
func BenchmarkHyperLogLogInsertUnique ( b * testing . B ) {
hll := hyperloglog . New ( )
b . ResetTimer ( )
buf := make ( [ ] byte , 32 )
for i := 0 ; i < b . N ; i ++ {
binary . LittleEndian . PutUint64 ( buf , uint64 ( i ) )
hll . Insert ( buf )
}
}
func BenchmarkHyperLogLogEstimate ( b * testing . B ) {
hll := hyperloglog . New ( )
for i := 0 ; i < 100 ; i ++ {
hll . Insert ( key . NewNode ( ) . Public ( ) . AppendTo ( nil ) )
}
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
_ = hll . Estimate ( )
}
}
func BenchmarkSenderCardinalityOverhead ( b * testing . B ) {
hll := hyperloglog . New ( )
sender := key . NewNode ( ) . Public ( )
b . Run ( "WithTracking" , func ( b * testing . B ) {
b . ReportAllocs ( )
for i := 0 ; i < b . N ; i ++ {
if hll != nil {
hll . Insert ( sender . AppendTo ( nil ) )
}
}
} )
b . Run ( "WithoutTracking" , func ( b * testing . B ) {
b . ReportAllocs ( )
for i := 0 ; i < b . N ; i ++ {
_ = sender . AppendTo ( nil )
}
} )
}