06.07 — OTP coordination flow + deliberate imprecision#
The backend’s reason to exist: let N parallel Ocarina workers retrieve the right OTP for their user, even when several OTPs are generated.
Flow#
┌───────────────────────────────────────────────────────────────────┐
│ Worker (among --workers 3 of Ocarina) │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ Selenium opens the Dashboard login page │
└─────────────────────────────────┬─────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ Acquire the distributed Redis lock (OTP_SEND_LOCK_KEY) │
│ → only this worker can click "Send OTP" during ACQ │
└─────────────────────────────────┬─────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ min_utc_date = datetime.now(UTC) │
└─────────────────────────────────┬─────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ L1 cache: record min_utc_date + username in the cache │
│ (keys reserved by reserve_free_cache_key) │
└─────────────────────────────────┬─────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ Selenium types username + password + ticks OTP + click "REQ OTP" │
└─────────────────────────────────┬─────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ IGORISTAN UI: fetch /api/otp?_user=<username> │
│ (x-api-key typed by Selenium into the UI) │
└─────────────────────────────────┬─────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ TESTS-WORKERS /api/otp: │
│ generate(secret) → otpCode │
│ createdAt = floor(now/1000)*1000 ← ms stripped │
│ event = { _user, otpCode, createdAt, expiresAt, ... } │
│ redis.set("otp:<now>:<uuid>", JSON, EX 360) │
│ return event │
└──────────────────────────────────┬────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ IGORISTAN UI: displays OTP screen │
└──────────────────────────────────┬────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ Release the OTP_SEND_LOCK_KEY Redis lock │
└──────────────────────────────────┬────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ Selenium: retrieve_dashboard_otp_code(min_utc_date, _user) │
│ ↓ │
│ GET /api/otp-history (x-api-key = IGOR_API_KEY) │
│ ↓ │
│ TESTS-WORKERS: SCAN otp:* + MGET → all events │
│ ↓ │
│ client-side filter by _user │
│ filter createdAt >= min_utc_date - 1s │
│ sort ASC by createdAt │
│ return first.otpCode │
└──────────────────────────────────┬────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────────────┐
│ Selenium types the OTP into the Igoristan UI │
│ → AUTHENTICATED_WITH_MFA │
└───────────────────────────────────────────────────────────────────┘Race conditions#
- Worker A:
min_utc_date_A = 13:27:53.123, generates OTP_A at 13:27:53.250. - Worker B:
min_utc_date_B = 13:27:53.130, generates OTP_B at 13:27:53.470.
Without coordination, A could pick up OTP_B instead of OTP_A — the timestamps are close and truncated to the second.
Distributed lock#
The OTP_SEND_LOCK_KEY lock serializes REQ OTP calls — A finishes before B starts.
Two different users requesting simultaneously don’t block each other (per-user lock). Both OTPs can land in the same second. That’s what the _user filter is for.
_user filter#
def _entry_matches(entry):
expected_user = expected_login or env.get_credentials("dashboard")["login"]
is_testing_entry = entry["_user"] == expected_user
if not is_testing_entry:
return False
created_at = datetime.fromisoformat(entry["createdAtTimestampLackingMsPrecision"])
return created_at >= min_utc_date - timedelta(seconds=1)Stripped ms#
const createdAtTimestampLackingMsPrecision = new Date(
now - (now % 1000),
).toISOString();createdAt is rounded to the second. All OTPs generated in the same second have identical createdAt values. Sorting by timestamp alone is useless.
Cycle and coordination#
Worker A: min_utc_date_A = 13:27:53.500
OTP issued: createdAt = 13:27:53.000 (stripped)
Filter: 13:27:53.000 >= 13:27:53.500 - 1s = 13:27:52.500 ✅ passesWithout the -1s margin, we’d miss the OTP we just requested.
To create a stable time window, the test forces a 2.5s sleep inside the lock in start_to_login_with_otp:
if workers > 1:
with _get_lock():
time.sleep(2.5)
_send(username)
else:
_send(username)The system combines _user filtering, delta timing, and sequential locks to reliably identify its own OTP.
An implementation that relied on timestamp alone would break the moment you parallelize.
Excerpt from tests-workers’s README#
### Quirks
Returns ISO timestamps precise to seconds only (`.000Z`) to force proper concurrency handling in test frameworks.Deliberate, documented, and a lovely gift from the backend: a subtle quirk the test suite must handle correctly.
Why Redis and not just a local lock#
Three Ocarina workers = three threads, one WebDriver process per thread. One main process, N threads, each driving a Selenium session.
threading.Lock would work in that scenario. But scale horizontally — two machines running ocarina-example simultaneously, or two terminals on the same machine — and threading.Lock is no longer enough. You need a distributed lock. Redis provides one via setnx-based redis.lock().
Conclusion#
tests-workers is intentionally designed to make test coordination hard but possible. It’s a training ground. Real SUTs have this kind of imprecision all the time — rate limits, timestamps precise only to the second, race-prone APIs.