Lockable pools
Lease test data that two runs can't share — logins, phone numbers, accounts. One row per run, released at the end.
A lockable pool is a dataset of equal-length lists. Each index across the lists is one row, and a running test leases one row for the length of the run.
Platforms
Android · iOS
A row
The i-th value of every column, leased as one unit
Rule
Every column must be a list, and all columns the same length
Scope
Leases are global — honored across every plan, run and tab at once
Watch out
Pool size caps parallelism. A 4-row pool means at most 4 tests at once
Prerequisites
Drizz desktop app installed and signed in
A dataset whose every column is a list of the same length
A test plan, to lock the variables on
Copy this
The dataset — shopease_logins, with Lockable pool ticked:
phone: ["9000000001", "9000000002", "9000000003", "9000000004"]
otp: ["123456", "123456", "123456", "123456"]That is a pool of 4 rows. Row 0 is {phone: 9000000001, otp: 123456}.
The test that leases from it:
The script is identical to a normal dataset-driven test. The difference is in how the plan resolves {{phone}} and {{otp}}.
Data that can't be shared
Login credentials
A second login can invalidate the first session
Phone numbers waiting on an OTP
Two runs race for the same message
Accounts mid-flow
One run's cart is the other run's mystery bug
Ten parallel tests against one account produce ten flaky results. A pool gives each run its own row.
Set up a pool
Write every column as a list, all the same length.
Tick Lockable pool in the dataset editor.
Save the dataset.
Open the plan's data step and set each pooled variable's source to lock to a pool.
Run the plan and open the Locks panel to confirm rows are leased and released.
Save rejects a pool that isn't shaped right:
A column isn't a list
Rejected — it names the offending variables
Columns of different lengths
Rejected — it lists the lengths
An empty pool
Rejected — a pool needs at least one row
Single-quoted inline lists (['a', 'b']) don't parse as a list and are caught by the not-a-list check. Use double quotes, or block - items.
A variable that only a pool provides is locked automatically. A variable that both a pool and a normal dataset provide is left to be chosen — nothing is selected silently. Because a row is paired, locking one column also leases the pool's other referenced columns.
Lease behavior
Acquire
The test leases the lowest free row when it starts
Binding
Every column in that row binds into the run. {{phone}} and {{otp}} come from the same index, always paired
Hold
The row is held for the whole run, by at most one run at a time
Release
The row is released when the run finishes — passed, failed or timed out
Scope
Leases are global. A plan, a second plan, and a single test run from the editor draw from one registry, so no two get the same row
Exhaustion
The next run waits rather than failing, with pool_exhausted as the wait reason. It gives up after a timeout and fails with "no free value in pool"
Stale recovery
Leases older than the stale threshold are reclaimed the next time anything tries to acquire a row
Sizing
Rows ≥ maximum parallelism
Every run leases immediately
Rows < maximum parallelism
The extra runs queue. The suite still passes; it takes as long as running at the lower concurrency
Rows > maximum parallelism
No cost. Spare rows sit unleased
A plan that runs 10-wide needs a pool of 10 rows or more.
Several pools at once
A test can lease from more than one pool — credentials from one, phone numbers from another. Lock each variable to its pool.
Rows are acquired all-or-nothing. If one pool is full, whatever was already acquired is released and the run retries, which is what stops two runs deadlocking while each holds half of what it needs.
Each pool independently caps concurrency at its own size. A 2-row credentials pool plus a 2-row phone pool, with six cases needing both, runs at most 2 at a time.
Rules and limits
All columns must be the same length, or the pool can't be locked or used at all.
A
SETin the test wins over a leased value for the same variable. A straySET phone = "9000000000"silently defeats the pool.Locking gives different values, not the same one. Two runs that must use an identical value take a fixed value from a normal dataset instead.
Data-driven looping and lockable pools can't be combined on one plan.
Deleting or renaming a pool a saved plan uses stops that plan running until the plan is reopened and pools are chosen again. It never silently leases from somewhere else.
Two selected pools providing the same variable is flagged as a conflict and blocks save and run. Untick all but one.
When rows get stuck
A run that dies without releasing — a closed tab, a crashed session — leaves its row held. Two mechanisms recover it:
Leases older than the stale threshold are reclaimed automatically the next time anything tries to acquire a row.
The Locks panel has Clear stale and Release all, plus a per-row release.
Releasing a row that's still running frees it for reuse while the run continues, so two runs can then share that value. The panel marks running rows and asks for confirmation.
Common mistakes
phone as a list, otp as a single value
The pool can't be locked. Every column must be a list
Columns of different lengths
Rejected on save. Even a one-row difference blocks the pool
phone: ['9000000001'] — single quotes inline
Doesn't parse as a list. Use double quotes or block - items
A 4-row pool with concurrency set to 10
Six runs sit waiting. You'll see pool_exhausted
SET phone = "9000000000" in a test that leases phone
The SET wins and every parallel run uses the same number
Expecting two runs to get the same leased row
They never will. Use a fixed value instead
Renaming a pool a saved plan uses
The plan won't run until you reopen it and pick pools again
Next
Last updated: 6 August 2026
Last updated
Was this helpful?