-
Notifications
You must be signed in to change notification settings - Fork 7
Add configurable read/update benchmark with watch support #207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
e0ed945
Add configurable read/update benchmark with watch support
xinWeiWei24 6c84a57
Use staleList in read_update.yaml
xinWeiWei24 6477040
Add comment
xinWeiWei24 2ca62a7
Change to StaleList and tweak StaleList
xinWeiWei24 c148ea5
Set keySpaceSize to read-update-configmap-total in read_update.go
xinWeiWei24 27c10f1
Set shares to read-ratio in read_update.go
xinWeiWei24 75c6f18
Fix linter
xinWeiWei24 178e5b9
Refactor configmap context handling in benchReadUpdateRun
xinWeiWei24 538c687
Remove unused time import and redundant sleep in configmap watcher
xinWeiWei24 a7ed403
Make a new tweak fun for read_update pipeline
xinWeiWei24 73c1bfd
Fix bugs in output
xinWeiWei24 910cb9a
Limit configmaps size to 1 MiB
xinWeiWei24 dfff7e5
Wrap stopWatches in an anonymous deferred function
xinWeiWei24 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
package bench | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"sync" | ||
|
||
internaltypes "github.com/Azure/kperf/contrib/internal/types" | ||
"github.com/Azure/kperf/contrib/utils" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/watch" | ||
"k8s.io/klog/v2" | ||
|
||
"github.com/urfave/cli" | ||
) | ||
|
||
var appLabel = "runkperf" | ||
|
||
var benchReadUpdateCase = cli.Command{ | ||
Name: "read_update", | ||
Usage: ` | ||
The test suite sets up a benchmark that simulates a mix of read, watch, and update operations on ConfigMaps. | ||
It creates ConfigMaps, establishes watch connections, and then issues concurrent read and update requests based on a specified ratio to evaluate API server performance under combined load. | ||
`, | ||
Flags: append( | ||
[]cli.Flag{ | ||
cli.IntFlag{ | ||
Name: "total", | ||
Usage: "Total requests per runner (There are 10 runners totally and runner's rate is 10)", | ||
Value: 3600, | ||
}, | ||
cli.StringFlag{ | ||
Name: "read-update-namespace", | ||
Usage: "Kubernetes namespace to use. If not specified, it will use the default namespace.", | ||
Value: "default", | ||
}, | ||
cli.IntFlag{ | ||
Name: "read-update-configmap-total", | ||
Usage: "Total ConfigMaps need to create", | ||
Value: 100, | ||
}, | ||
cli.IntFlag{ | ||
Name: "read-update-configmap-size", | ||
Usage: "Size of each ConfigMap. ConfigMap must not exceed 3 MiB.", | ||
Value: 1024, // 1 MiB | ||
}, | ||
cli.StringFlag{ | ||
Name: "read-update-name-pattern", | ||
Usage: "Name pattern for the resources to create", | ||
Value: "kperf-read-update", | ||
}, | ||
cli.Float64Flag{ | ||
Name: "read-ratio", | ||
Usage: "Proportion of read requests among all requests (range: 0.0 to 1.0). For example, 0.5 indicates 50% of the requests are reads.", | ||
Value: 0.5, | ||
}, | ||
}, | ||
commonFlags..., | ||
), | ||
Action: func(cliCtx *cli.Context) error { | ||
_, err := renderBenchmarkReportInterceptor( | ||
addAPIServerCoresInfoInterceptor(benchReadUpdateRun), | ||
)(cliCtx) | ||
return err | ||
}, | ||
} | ||
|
||
// benchReadUpdateRun is for subcommand benchReadUpdateRun. | ||
func benchReadUpdateRun(cliCtx *cli.Context) (*internaltypes.BenchmarkReport, error) { | ||
ctx := context.Background() | ||
kubeCfgPath := cliCtx.GlobalString("kubeconfig") | ||
|
||
// Load the load profile | ||
rgCfgFile, rgSpec, rgCfgFileDone, err := newLoadProfileFromEmbed(cliCtx, | ||
"loadprofile/read_update.yaml") | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
defer func() { _ = rgCfgFileDone() }() | ||
|
||
total := cliCtx.Int("read-update-configmap-total") | ||
size := cliCtx.Int("read-update-configmap-size") | ||
namespace := cliCtx.String("read-update-namespace") | ||
namePattern := cliCtx.String("read-update-name-pattern") | ||
if total <= 0 || size <= 0 || total*size > 2*1024*1024 || size > 1024 { | ||
return nil, fmt.Errorf("invalid total (%d) or size (%d) for configmaps: total must be > 0, size must be > 0, and total*size must not exceed 2 GiB, size must not exceed 1 MiB", total, size) | ||
} | ||
|
||
// Create configmaps with specified name pattern | ||
client, err := utils.BuildClientset(kubeCfgPath) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to build clientset: %w", err) | ||
} | ||
|
||
err = utils.CreateConfigmaps(ctx, kubeCfgPath, namespace, namePattern, total, size, 2, 0) | ||
|
||
if err != nil { | ||
return nil, fmt.Errorf("failed to create configmaps: %w", err) | ||
} | ||
|
||
defer func() { | ||
// Delete the configmaps after the benchmark | ||
err = utils.DeleteConfigmaps(ctx, kubeCfgPath, namespace, namePattern, 0) | ||
if err != nil { | ||
klog.Errorf("Failed to delete configmaps: %v", err) | ||
} | ||
}() | ||
|
||
// Stop all the watches when the function returns | ||
watches := make([]watch.Interface, 0) | ||
defer func() { | ||
stopWatches(watches) | ||
}() | ||
|
||
var wg sync.WaitGroup | ||
defer wg.Wait() | ||
|
||
dpCtx, dpCancel := context.WithCancel(ctx) | ||
defer dpCancel() | ||
|
||
// Start to watch the configmaps | ||
for i := 0; i < total; i++ { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this supposed to simulate cilium's use case? In Cilium's use case, each pod is watched by each cilium agent, so it creates multiple watch updates for one mutation of a pod. |
||
wg.Add(1) | ||
go func() { | ||
defer wg.Done() | ||
watchReq, err := client.CoreV1().ConfigMaps(namespace). | ||
Watch(context.TODO(), metav1.ListOptions{ | ||
Watch: true, | ||
FieldSelector: fmt.Sprintf("metadata.name=%s-cm-%s-%d", appLabel, namePattern, i), | ||
}) | ||
|
||
watches = append(watches, watchReq) | ||
if err != nil { | ||
fmt.Printf("Error starting watch for configmap %s: %v\n", fmt.Sprintf("%s-cm-%s-%d", appLabel, namePattern, i), err) | ||
return | ||
} | ||
klog.V(5).Infof("Starting watch for configmap: %s\n", fmt.Sprintf("%s-cm-%s-%d", appLabel, namePattern, i)) | ||
|
||
// Process watch events proactively to prevent cache oversizing. | ||
for { | ||
select { | ||
case <-dpCtx.Done(): | ||
klog.V(5).Infof("Stopping watch for configmap: %s\n", fmt.Sprintf("%s-cm-%s-%d", appLabel, namePattern, i)) | ||
return | ||
case event := <-watchReq.ResultChan(): | ||
if event.Type == watch.Error { | ||
klog.Errorf("Error event received for configmap %s: %v", fmt.Sprintf("%s-cm-%s-%d", appLabel, namePattern, i), event.Object) | ||
return | ||
} | ||
klog.V(5).Infof("Event received for configmap %s: %v", fmt.Sprintf("%s-cm-%s-%d", appLabel, namePattern, i), event.Type) | ||
} | ||
} | ||
|
||
}() | ||
} | ||
|
||
// Deploy the runner group | ||
rgResult, derr := utils.DeployRunnerGroup(ctx, | ||
cliCtx.GlobalString("kubeconfig"), | ||
cliCtx.GlobalString("runner-image"), | ||
rgCfgFile, | ||
cliCtx.GlobalString("runner-flowcontrol"), | ||
cliCtx.GlobalString("rg-affinity"), | ||
) | ||
|
||
if derr != nil { | ||
return nil, fmt.Errorf("failed to deploy runner group: %w", derr) | ||
} | ||
|
||
return &internaltypes.BenchmarkReport{ | ||
Description: fmt.Sprintf(` | ||
Environment: Combine %d%% read requests and %d%% update requests during benchmarking. Workload: Deploy %d configmaps in %d KiB`, | ||
int(100*cliCtx.Float64("read-ratio")), 100-int(100*cliCtx.Float64("read-ratio")), total, size*total), | ||
LoadSpec: *rgSpec, | ||
Result: *rgResult, | ||
Info: map[string]interface{}{}, | ||
}, nil | ||
} | ||
|
||
// StopWatches stops all the watches | ||
func stopWatches(watches []watch.Interface) { | ||
for _, w := range watches { | ||
if w != nil { | ||
klog.V(5).Infof("Stopping watch: %v", w) | ||
w.Stop() | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
count: 10 | ||
loadProfile: | ||
version: 1 | ||
description: "read_update" | ||
spec: | ||
rate: 10 | ||
total: 3000 | ||
conns: 10 | ||
client: 10 | ||
contentType: json | ||
disableHTTP2: false | ||
maxRetries: 0 | ||
requests: | ||
- staleList: | ||
version: v1 | ||
resource: configmaps | ||
namespace: default | ||
shares: 50 | ||
- patch: | ||
version: v1 | ||
resource: configmaps | ||
namespace: default | ||
patchType: merge | ||
name: runkperf-cm-kperf-read-update | ||
keySpaceSize: 100 | ||
body: | | ||
{ | ||
"metadata": { | ||
"labels": { | ||
"test-label": "mutation-test" | ||
} | ||
} | ||
} | ||
shares: 50 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.