Skip to content

Commit 17b291f

Browse files
committed
👷 Improve bs-wrapper for parallel BrowserStack execution
Wait for available sessions instead of blocking on any running build. Isolate tunnels with a unique localIdentifier per run. Add jitter to the availability check to prevent thundering herd.
1 parent df95319 commit 17b291f

2 files changed

Lines changed: 19 additions & 11 deletions

File tree

‎scripts/test/bs-wrapper.ts‎

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// This wrapper script ensures that no other test is running in BrowserStack before launching the
1+
// This wrapper script waits until a BrowserStack parallel session is available before launching the
22
// test command, to avoid overloading the service and making tests more flaky than necessary. This
3-
// is also handled by the CI (exclusive lock on the "browserstack" resource), but it is helpful when
4-
// launching tests outside of the CI.
3+
// is also handled by the CI (resource groups), but it is helpful when launching tests outside of
4+
// the CI.
55
//
66
// It used to re-run the test command based on its output (in particular, when the BrowserStack
77
// session failed to be created), but we observed that:
@@ -13,6 +13,7 @@
1313
// after killing it. There might be a better way of prematurely aborting the test command if we need
1414
// to in the future.
1515

16+
import { randomUUID } from 'node:crypto'
1617
import { spawn, type ChildProcess } from 'node:child_process'
1718
import browserStack from 'browserstack-local'
1819
import { printLog, runMain, timeout, printError } from '../lib/executionUtils.ts'
@@ -21,9 +22,10 @@ import { browserStackRequest } from '../lib/bsUtils.ts'
2122

2223
const AVAILABILITY_CHECK_DELAY = 30_000
2324
const NO_OUTPUT_TIMEOUT = 5 * 60_000
24-
const BS_BUILD_URL = 'https://api.browserstack.com/automate/builds.json?status=running'
25+
const BS_PLAN_URL = 'https://api.browserstack.com/automate/plan.json'
2526

2627
const bsLocal = new browserStack.Local()
28+
const localIdentifier = `browser-sdk-${randomUUID()}`
2729

2830
runMain(async () => {
2931
if (command`git tag --points-at HEAD`.run()) {
@@ -44,15 +46,19 @@ runMain(async () => {
4446
})
4547

4648
async function waitForAvailability(): Promise<void> {
47-
while (await hasRunningBuild()) {
48-
printLog('Other build running, waiting...')
49-
await timeout(AVAILABILITY_CHECK_DELAY)
49+
while (await isAtCapacity()) {
50+
const jitter = Math.floor(Math.random() * 3_000)
51+
printLog('All BrowserStack sessions occupied, waiting...')
52+
await timeout(AVAILABILITY_CHECK_DELAY + jitter)
5053
}
5154
}
5255

53-
async function hasRunningBuild(): Promise<boolean> {
54-
const builds = (await browserStackRequest(BS_BUILD_URL)) as any[]
55-
return builds.length > 0
56+
async function isAtCapacity(): Promise<boolean> {
57+
const plan = (await browserStackRequest(BS_PLAN_URL)) as {
58+
parallel_sessions_running: number
59+
parallel_sessions_max_allowed: number
60+
}
61+
return plan.parallel_sessions_running >= plan.parallel_sessions_max_allowed
5662
}
5763

5864
function startBsLocal(): Promise<void> {
@@ -62,8 +68,8 @@ function startBsLocal(): Promise<void> {
6268
bsLocal.start(
6369
{
6470
key: process.env.BS_ACCESS_KEY,
71+
localIdentifier,
6572
forceLocal: true,
66-
forceKill: true,
6773
onlyAutomate: true,
6874
},
6975
(error?: Error) => {
@@ -97,6 +103,7 @@ function runTests(): Promise<boolean> {
97103
...process.env,
98104
FORCE_COLOR: 'true',
99105
BROWSER_STACK: 'true',
106+
BROWSERSTACK_LOCAL_IDENTIFIER: localIdentifier,
100107
},
101108
})
102109

‎test/unit/karma.bs.conf.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default function (config) {
3030
browserStack: {
3131
username: process.env.BS_USERNAME,
3232
accessKey: process.env.BS_ACCESS_KEY,
33+
localIdentifier: process.env.BROWSERSTACK_LOCAL_IDENTIFIER,
3334
project: 'browser sdk unit',
3435
build: getBuildInfos(),
3536
video: false,

0 commit comments

Comments
 (0)