Files
teleport/lib/cache/node_test.go
T
Luke Okraszewski c223947413 [ci] add differential benchmark workflows (#63100)
This commit does the following:
- Move existing smoke benchmark workflows to seperate workflow.
- Add differential benchmark workflows
- Split benchmarks into heavy and micro categories
- Add make target arguments for benchmark time and count
- Move smoketests to run only on PRs
- Differential benchmarks run on merge queue

Benchmarks above 1ms/op are considered heavy and should make
use of iteration based benchtime to prevent the CI job from taking
too much time. Benchmarks are skipped based on env vars, this is an
alternative to splitting benchmark targets into seperate files with
build tags.

The smoke tests should be much faster and as such are set to run
on PRs to ensure the targets are not broken in the change.

The current parameters for differential benchmarks attempt to balance
CI runner time and confidence for benchstat.

Differential benchmarks determine the base commit to run the tests against,
this happens within the same job to remove the runner to runner variance.
2026-02-20 15:31:03 +00:00

201 lines
5.4 KiB
Go

// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package cache
import (
"context"
"testing"
"time"
"github.com/google/uuid"
"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/api/client/proto"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
)
// TestNodes tests nodes cache
func TestNodes(t *testing.T) {
t.Parallel()
t.Run("GetNodes", func(t *testing.T) {
t.Parallel()
p := newTestPack(t, ForProxy)
t.Cleanup(p.Close)
testResources(t, p, testFuncs[types.Server]{
newResource: func(name string) (types.Server, error) {
return NewServer(types.KindNode, name, "127.0.0.1:2022", apidefaults.Namespace), nil
},
create: withKeepalive(p.presenceS.UpsertNode),
list: getAllAdapter(func(ctx context.Context) ([]types.Server, error) {
return p.presenceS.GetNodes(ctx, apidefaults.Namespace)
}),
cacheGet: func(ctx context.Context, name string) (types.Server, error) {
return p.cache.GetNode(ctx, apidefaults.Namespace, name)
},
cacheList: getAllAdapter(func(ctx context.Context) ([]types.Server, error) { return p.cache.GetNodes(ctx, apidefaults.Namespace) }),
update: withKeepalive(p.presenceS.UpsertNode),
deleteAll: func(ctx context.Context) error {
return p.presenceS.DeleteAllNodes(ctx, apidefaults.Namespace)
},
}, withSkipPaginationTest())
})
t.Run("ListResources", func(t *testing.T) {
t.Parallel()
p := newTestPack(t, ForProxy)
t.Cleanup(p.Close)
testResources(t, p, testFuncs[types.Server]{
newResource: func(name string) (types.Server, error) {
return NewServer(types.KindNode, name, "127.0.0.1:2022", apidefaults.Namespace), nil
},
create: withKeepalive(p.presenceS.UpsertNode),
list: func(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) {
req := proto.ListResourcesRequest{
ResourceType: types.KindNode,
Limit: int32(pageSize),
StartKey: pageToken,
}
var out []types.Server
resp, err := p.presenceS.ListResources(ctx, req)
if err != nil {
return nil, "", trace.Wrap(err)
}
for _, s := range resp.Resources {
out = append(out, s.(types.Server))
}
return out, resp.NextKey, nil
},
cacheGet: func(ctx context.Context, name string) (types.Server, error) {
return p.cache.GetNode(ctx, apidefaults.Namespace, name)
},
cacheList: func(ctx context.Context, pageSize int, pageToken string) ([]types.Server, string, error) {
req := proto.ListResourcesRequest{
ResourceType: types.KindNode,
Limit: int32(pageSize),
StartKey: pageToken,
}
var out []types.Server
resp, err := p.cache.ListResources(ctx, req)
if err != nil {
return nil, "", trace.Wrap(err)
}
for _, s := range resp.Resources {
out = append(out, s.(types.Server))
}
return out, resp.NextKey, nil
},
update: withKeepalive(p.presenceS.UpsertNode),
deleteAll: func(ctx context.Context) error {
return p.presenceS.DeleteAllNodes(ctx, apidefaults.Namespace)
},
})
})
}
func BenchmarkGetMaxNodes(b *testing.B) {
if testing.Short() {
b.Skip("skipping heavy benchmark")
}
benchGetNodes(b, 1_000_000)
}
func benchGetNodes(b *testing.B, nodeCount int) {
p, err := newPack(b, ForAuth)
require.NoError(b, err)
defer p.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
createErr := make(chan error, 1)
go func() {
for range nodeCount {
server := NewServer(types.KindNode, uuid.New().String(), "127.0.0.1:2022", apidefaults.Namespace)
_, err := p.presenceS.UpsertNode(ctx, server)
if err != nil {
createErr <- err
return
}
}
}()
timeout := time.After(time.Second * 90)
for i := range nodeCount {
select {
case event := <-p.eventsC:
if event.Type == RelativeExpiry {
continue
}
require.Equal(b, EventProcessed, event.Type)
case err := <-createErr:
b.Fatalf("failed to create node: %v", err)
case <-timeout:
b.Fatalf("timeout waiting for event, progress=%d", i)
}
}
b.ResetTimer()
b.Run("GetNodes", func(b *testing.B) {
for b.Loop() {
nodes, err := p.cache.GetNodes(ctx, apidefaults.Namespace)
require.NoError(b, err)
require.Len(b, nodes, nodeCount)
}
})
b.Run("ListResources", func(b *testing.B) {
for b.Loop() {
req := proto.ListResourcesRequest{
ResourceType: types.KindNode,
}
nodes := make([]types.ResourceWithLabels, 0, nodeCount)
for {
resp, err := p.cache.ListResources(ctx, req)
require.NoError(b, err)
req.StartKey = resp.NextKey
nodes = append(nodes, resp.Resources...)
if req.StartKey == "" {
break
}
}
require.Len(b, nodes, nodeCount)
}
})
}