Skip to content

Commit 6325eb6

Browse files
dheerajodhaclaude
andcommitted
feat(EC-1816): add multi-component stress benchmark
Add a stress benchmark under benchmark/stress that validates a snapshot with many components using 35 workers, simulating the workload that caused the OOM incident (EC-1805). Component count and worker count are parameterized via EC_STRESS_COMPONENTS and EC_STRESS_WORKERS env vars. Resolves: EC-1816 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 495602e commit 6325eb6

2 files changed

Lines changed: 223 additions & 0 deletions

File tree

benchmark/stress/prepare_data.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
# Copyright The Conforma Contributors
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
# SPDX-License-Identifier: Apache-2.0
17+
18+
# Creates the files in the data directory that should contain all the data
19+
# needed to run the benchmark, uses the ../offliner for images and plain git
20+
# clone for the git data dependency. Uses the same golden-container image as the
21+
# simple benchmark -- the stress benchmark duplicates it across components at
22+
# runtime.
23+
set -o errexit
24+
set -o nounset
25+
set -o pipefail
26+
27+
offliner="$(git rev-parse --show-toplevel)/benchmark/offliner"
28+
29+
dir="$(mktemp -d)"
30+
trap 'rm -rf "${dir}"' EXIT
31+
32+
(
33+
cd "${dir}"
34+
35+
imgs=(
36+
quay.io/redhat-user-workloads/rhtap-contract-tenant/golden-container/golden-container@sha256:166e38c156fa81d577a7ba7a948b68c79005a06e302779d1bebc7d31e8bea315
37+
quay.io/konflux-ci/tekton-catalog/data-acceptable-bundles@sha256:1e70b8f672388838f20a7d45e145e31e99dab06cefa1c5514d6ce41c8bbea1b0
38+
quay.io/enterprise-contract/ec-release-policy@sha256:64617f0c45689ef7152c5cfbd4cd5709a3126e4ab7482eb6acd994387fe2d4ba
39+
)
40+
41+
for img in "${imgs[@]}"; do
42+
go run -C "${offliner}" . "${img}" "${dir}/data/registry/data"
43+
done
44+
45+
git clone --no-checkout https://github.com/release-engineering/rhtap-ec-policy.git data/git/rhtap-ec-policy.git
46+
)
47+
48+
tar czf data.tar.gz -C "${dir}" .

benchmark/stress/stress.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright The Conforma Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// SPDX-License-Identifier: Apache-2.0
16+
17+
// Stress benchmark validating a multi-component snapshot with a configurable
18+
// number of workers, simulating real-world release pipeline workloads. The
19+
// component count and worker count are controlled via the EC_STRESS_COMPONENTS
20+
// and EC_STRESS_WORKERS environment variables respectively. Uses the same
21+
// golden-container image data as the simple benchmark, duplicated across
22+
// components to create memory pressure. The prepare_data.sh script can be used
23+
// to re-populate the data directory.
24+
package main
25+
26+
import (
27+
"encoding/json"
28+
"fmt"
29+
"os"
30+
"path"
31+
"strconv"
32+
33+
"golang.org/x/benchmarks/driver"
34+
35+
"github.com/conforma/cli/benchmark/internal/registry"
36+
"github.com/conforma/cli/benchmark/internal/suite"
37+
"github.com/conforma/cli/benchmark/internal/untar"
38+
)
39+
40+
const (
41+
defaultComponents = 10
42+
defaultWorkers = 35
43+
)
44+
45+
func main() {
46+
driver.Main("Stress", benchmark)
47+
}
48+
49+
func envInt(name string, fallback int) int {
50+
v, ok := os.LookupEnv(name)
51+
if !ok {
52+
return fallback
53+
}
54+
n, err := strconv.Atoi(v)
55+
if err != nil {
56+
panic(fmt.Sprintf("invalid %s value %q: %v", name, v, err))
57+
}
58+
return n
59+
}
60+
61+
func setup() (string, suite.Closer) {
62+
dir, err := untar.UnTar("data.tar.gz")
63+
if err != nil {
64+
panic(err)
65+
}
66+
67+
closer, err := registry.Launch(path.Join(dir, "data/registry/data"))
68+
if err != nil {
69+
panic(err)
70+
}
71+
72+
return dir, func() {
73+
closer()
74+
os.RemoveAll(dir)
75+
}
76+
}
77+
78+
type component struct {
79+
Name string `json:"name"`
80+
ContainerImage string `json:"containerImage"`
81+
Source *source `json:"source,omitempty"`
82+
}
83+
84+
type source struct {
85+
Git gitSource `json:"git"`
86+
}
87+
88+
type gitSource struct {
89+
URL string `json:"url"`
90+
Revision string `json:"revision"`
91+
}
92+
93+
type snapshot struct {
94+
Components []component `json:"components"`
95+
}
96+
97+
func buildSnapshot(n int) string {
98+
s := snapshot{Components: make([]component, n)}
99+
for i := range s.Components {
100+
s.Components[i] = component{
101+
Name: fmt.Sprintf("golden-container-%d", i),
102+
ContainerImage: "quay.io/redhat-user-workloads/rhtap-contract-tenant/golden-container/golden-container@sha256:166e38c156fa81d577a7ba7a948b68c79005a06e302779d1bebc7d31e8bea315",
103+
Source: &source{
104+
Git: gitSource{
105+
URL: "https://github.com/conforma/golden-container",
106+
Revision: "2dec8f515a64ef2f21ee3e7b1ed41da77a5c5a9a",
107+
},
108+
},
109+
}
110+
}
111+
112+
data, err := json.Marshal(s)
113+
if err != nil {
114+
panic(err)
115+
}
116+
return string(data)
117+
}
118+
119+
func benchmark() driver.Result {
120+
dir, closer := setup()
121+
defer closer()
122+
123+
components := envInt("EC_STRESS_COMPONENTS", defaultComponents)
124+
workers := envInt("EC_STRESS_WORKERS", defaultWorkers)
125+
126+
return driver.Benchmark(run(dir, components, workers))
127+
}
128+
129+
func ec(dir string, components, workers int) func() {
130+
snap := buildSnapshot(components)
131+
132+
policy := fmt.Sprintf(`{
133+
"publicKey": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZP/0htjhVt2y0ohjgtIIgICOtQtA\nnaYJRuLprwIv6FDhZ5yFjYUEtsmoNcW7rx2KM6FOXGsCX3BNc7qhHELT+g==\n-----END PUBLIC KEY-----",
134+
"sources": [
135+
{
136+
"data": [
137+
"git::file://%s/data/git/rhtap-ec-policy.git//data?ref=a524ee2f2f7774f6f360eb64c4cb24004de52aae",
138+
"oci::quay.io/konflux-ci/tekton-catalog/data-acceptable-bundles@sha256:1e70b8f672388838f20a7d45e145e31e99dab06cefa1c5514d6ce41c8bbea1b0"
139+
],
140+
"policy": [
141+
"oci::quay.io/enterprise-contract/ec-release-policy@sha256:64617f0c45689ef7152c5cfbd4cd5709a3126e4ab7482eb6acd994387fe2d4ba"
142+
],
143+
"config": {
144+
"include": [
145+
"@redhat"
146+
]
147+
}
148+
}
149+
]
150+
}`, dir)
151+
152+
return func() {
153+
if err := suite.Execute([]string{
154+
"validate",
155+
"image",
156+
"--json-input",
157+
snap,
158+
"--policy",
159+
policy,
160+
"--ignore-rekor",
161+
"--workers",
162+
strconv.Itoa(workers),
163+
"--effective-time",
164+
"2024-12-10T00:00:00Z",
165+
}); err != nil {
166+
panic(err)
167+
}
168+
}
169+
}
170+
171+
func run(dir string, components, workers int) func(n uint64) {
172+
return func(n uint64) {
173+
driver.Parallel(n, 1, ec(dir, components, workers))
174+
}
175+
}

0 commit comments

Comments
 (0)