Skip to content

Commit c0bcc34

Browse files
authored
ci: run browser concurrency levels sequentially per provider (computesdk#305)
The five concurrency levels ran as parallel matrix jobs that all started within two seconds of each other against the same provider account, so a c1 measurement actually observed up to 1+5+10+25+50 concurrent sessions. Steel's c1 job failed rounds 0-6 and then ran 43 clean rounds, exactly tracking the window when its sibling jobs were live. The matrix is now provider-only and the levels run one after another inside each job, with a cooldown so sessions released by one level clear the provider's quota before the next starts. Round counts are rebalanced to 10/10/5/3/3, which also cuts the c1 baseline off the critical path. Matches the copy on add-browser-concurrency-bench byte for byte so the two cannot conflict.
1 parent b224f11 commit c0bcc34

1 file changed

Lines changed: 60 additions & 29 deletions

File tree

.github/workflows/browser-concurrent-benchmarks.yml

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,18 @@ jobs:
6868
echo "Resolved ${#urls[@]} shared article URLs for all providers"
6969
7070
bench:
71-
name: Bench ${{ matrix.provider }} at c${{ matrix.concurrency_level }}
71+
name: Bench ${{ matrix.provider }}
7272
runs-on: namespace-profile-default;permissions.additional_grant=vault/object:*:list;permissions.additional_grant=vault/object:*:describe
7373
permissions:
7474
contents: read
7575
id-token: write
7676
needs: setup
77-
timeout-minutes: 60
77+
timeout-minutes: 120
7878
strategy:
7979
fail-fast: false
8080
matrix:
81+
# Only the provider is a matrix dimension. Concurrency levels run
82+
# sequentially inside the job, see the benchmark step for why.
8183
provider:
8284
- browserbase
8385
- browseruse
@@ -86,19 +88,6 @@ jobs:
8688
- notte
8789
- steel
8890
- tilion
89-
concurrency_level: [1, 5, 10, 25, 50]
90-
include:
91-
# Scale iterations inversely with concurrency so total sessions ≈ 50 per level
92-
- concurrency_level: 1
93-
iterations: 50
94-
- concurrency_level: 5
95-
iterations: 10
96-
- concurrency_level: 10
97-
iterations: 5
98-
- concurrency_level: 25
99-
iterations: 2
100-
- concurrency_level: 50
101-
iterations: 1
10291
steps:
10392
- uses: actions/checkout@v4
10493
- uses: pnpm/action-setup@v4
@@ -117,24 +106,66 @@ jobs:
117106
run: |
118107
. benchmarks/scripts/load-vault-secrets.sh '^(BROWSERBASE_API_KEY|BROWSERBASE_PROJECT_ID|BROWSER_USE_API_KEY|HYPERBROWSER_API_KEY|KERNEL_API_KEY|NOTTE_API_KEY|STEEL_API_KEY|TILION_API_KEY|TILION_BASE_URL|COMPUTESDK_ADMIN_API_KEY|BENCHMARKS_PLATFORM_API_KEY)'
119108
120-
# Matrix jobs at the same concurrency level share one platform run.
121-
# The benchmark slug is level-independent, so the level is part of
122-
# the key; otherwise a provider would register once per level in the
123-
# same run and the platform rejects the duplicate with 409 Conflict.
124-
RUN_KEY="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-c${{ matrix.concurrency_level }}"
125-
# On push (smoke test) use 3 iterations regardless of concurrency
126-
# level; on schedule/workflow_dispatch use the full matrix value.
127-
ITERS=${{ github.event_name == 'push' && '3' || matrix.iterations }}
128-
npx tsx packages/benchsdk-runner/dist/bin.js run benchmarks/browser/browser-concurrent.bench.ts \
129-
--provider ${{ matrix.provider }} \
130-
--concurrency-level ${{ matrix.concurrency_level }} \
131-
--iterations "$ITERS" \
132-
--run-key "$RUN_KEY"
109+
# Concurrency levels run one after another inside this job, never as
110+
# parallel matrix jobs. Parallel level jobs all target the same
111+
# provider account at once, so the c1 job would measure its baseline
112+
# while 5 + 10 + 25 + 50 sibling sessions were live on that account
113+
# and every level's numbers would depend on scheduling overlap.
114+
#
115+
# A cooldown between levels lets sessions released at the end of one
116+
# level clear the provider's quota before the next level starts.
117+
if [ "${{ github.event_name }}" = "push" ]; then
118+
LEVELS="1 5" # smoke test only
119+
COOLDOWN=10
120+
else
121+
LEVELS="1 5 10 25 50"
122+
COOLDOWN=60
123+
fi
124+
125+
# Rounds per level. Session-level metrics pool every session at a
126+
# level, so a handful of rounds at high concurrency already yields a
127+
# large sample; the round count only needs to give the barrier
128+
# wall-clock a few observations.
129+
iterations_for() {
130+
case "$1" in
131+
1) echo 10 ;;
132+
5) echo 10 ;;
133+
10) echo 5 ;;
134+
25) echo 3 ;;
135+
50) echo 3 ;;
136+
*) echo 3 ;;
137+
esac
138+
}
139+
140+
status=0
141+
for level in $LEVELS; do
142+
if [ "${{ github.event_name }}" = "push" ]; then
143+
iters=1
144+
else
145+
iters=$(iterations_for "$level")
146+
fi
147+
148+
echo "::group::c${level} (${iters} rounds)"
149+
# One platform run per level: the benchmark slug is
150+
# level-independent, so without the level in the key a provider
151+
# would register as the same participant five times and the
152+
# platform rejects the duplicate with 409 Conflict.
153+
RUN_KEY="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-c${level}"
154+
npx tsx packages/benchsdk-runner/dist/bin.js run benchmarks/browser/browser-concurrent.bench.ts \
155+
--provider ${{ matrix.provider }} \
156+
--concurrency-level "$level" \
157+
--iterations "$iters" \
158+
--run-key "$RUN_KEY" || status=$?
159+
echo "::endgroup::"
160+
161+
sleep "$COOLDOWN"
162+
done
163+
exit $status
133164
- name: Upload results
134165
if: always()
135166
uses: actions/upload-artifact@v4
136167
with:
137-
name: browser-concurrent-results-${{ matrix.provider }}-c${{ matrix.concurrency_level }}
168+
name: browser-concurrent-results-${{ matrix.provider }}
138169
path: results/browser-concurrent/
139170
if-no-files-found: ignore
140171
retention-days: 7

0 commit comments

Comments
 (0)