package querylog import ( "net" "testing" "time" "github.com/AdguardTeam/golibs/timeutil" "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestQueryLog_Search_findClient(t *testing.T) { const knownClientID = "client-1" const knownClientName = "Known Client 1" const unknownClientID = "client-2" knownClient := &Client{ Name: knownClientName, } findClientCalls := 0 findClient := func(ids []string) (c *Client, _ error) { defer func() { findClientCalls++ }() if len(ids) == 0 { return nil, nil } if ids[0] == knownClientID { return knownClient, nil } return nil, nil } l, err := newQueryLog(Config{ FindClient: findClient, BaseDir: t.TempDir(), RotationIvl: timeutil.Day, MemSize: 100, Enabled: true, FileEnabled: true, AnonymizeClientIP: false, }) require.NoError(t, err) t.Cleanup(l.Close) q := &dns.Msg{ Question: []dns.Question{{ Name: "example.com", }}, } l.Add(&AddParams{ Question: q, ClientID: knownClientID, ClientIP: net.IP{1, 2, 3, 4}, }) // Add the same thing again to test the cache. l.Add(&AddParams{ Question: q, ClientID: knownClientID, ClientIP: net.IP{1, 2, 3, 4}, }) l.Add(&AddParams{ Question: q, ClientID: unknownClientID, ClientIP: net.IP{1, 2, 3, 5}, }) sp := &searchParams{ // Add some time to the "current" one to protect against // low-resolution timers on some Windows machines. // // TODO(a.garipov): Use some kind of timeSource interface // instead of relying on time.Now() in tests. olderThan: time.Now().Add(10 * time.Second), limit: 3, } entries, _ := l.search(sp) assert.Equal(t, 2, findClientCalls) require.Len(t, entries, 3) assert.Nil(t, entries[0].client) gotClient := entries[2].client require.NotNil(t, gotClient) assert.Equal(t, knownClientName, gotClient.Name) } // BenchmarkQueryLog_Search compares the speed of search with limit-offset // parameters and the one with oldenThan timestamp specified. func BenchmarkQueryLog_Search(b *testing.B) { l, err := newQueryLog(Config{ Enabled: true, RotationIvl: timeutil.Day, MemSize: 100, BaseDir: b.TempDir(), }) require.NoError(b, err) const ( entNum = 100000 firstPageDomain = "first.example.org" secondPageDomain = "second.example.org" ) // Add entries to the log. for i := 0; i < entNum; i++ { addEntry(l, secondPageDomain, net.IPv4(1, 1, 1, 1), net.IPv4(2, 2, 2, 1)) } // Write them to the first file. require.NoError(b, l.flushLogBuffer()) // Add more to the in-memory part of log. for i := 0; i < entNum; i++ { addEntry(l, firstPageDomain, net.IPv4(1, 1, 1, 1), net.IPv4(2, 2, 2, 1)) } b.Run("limit_offset", func(b *testing.B) { params := newSearchParams() b.ReportAllocs() for i := 0; i < b.N; i++ { params.offset += params.limit _, _ = l.search(params) } }) b.Run("timestamp", func(b *testing.B) { params := newSearchParams() params.olderThan = time.Now().Add(-1 * time.Hour) b.ReportAllocs() for i := 0; i < b.N; i++ { params.olderThan = params.olderThan.Add(1 * time.Minute) _, _ = l.search(params) } }) // Most recent result, on a MBP15: // // goos: darwin // goarch: amd64 // pkg: github.com/AdguardTeam/AdGuardHome/internal/querylog // cpu: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz // BenchmarkQueryLog_Search // BenchmarkQueryLog_Search/limit_offset // BenchmarkQueryLog_Search/limit_offset-12 547 2066079 ns/op 2325019 B/op 26633 allocs/op // BenchmarkQueryLog_Search/timestamp // BenchmarkQueryLog_Search/timestamp-12 1303 2028888 ns/op 2219337 B/op 25194 allocs/op }