Skip to content

fix(returns): keep the return-picture session binding on a revision conflict - #621

Merged
eyevinn-developer[bot] merged 2 commits into
Eyevinn:mainfrom
markusnygard:fix/return-picture-binding-conflict
Nov 5, 2026
Merged

eyevinn-developer[bot] merged 2 commits into
Eyevinn:mainfrom
markusnygard:fix/return-picture-binding-conflict

Conversation

@markusnygard

Copy link
Copy Markdown
Contributor

Summary

A guest's return-picture WHEP POST binds the Strom session id to the guest session
(returnWhepSessionId, issue #380) by inserting the session doc it read at the start of the
request. While the Strom round trip runs, the same guest page may write that doc too: at join it
opens the return picture and the talkback WHIP/WHEP at the same time, and the talkback routes
bind their own ids (talkbackWhipSessionId / talkbackWhepSessionId). Mute and status changes
write it as well.

When that happens, the return-picture insert gets a CouchDB 409. It was only logged
(return picture WHEP: guest session-id bind failed), so returnWhepSessionId stayed unset:

  • the guest's later DELETE of that session gets a 403;
  • the Strom session lasts until ICE consent times out.

The talkback binding already re-reads and retries once on a 409 (bindTalkbackSession). The
return picture had no retry.

Change

  • src/routes/returns.ts: the binding goes through a new bindWhepSession. On a 409 it re-reads
    the doc, requires state !== 'left' (a guest who left is not brought back to life), applies
    only its own key, and retries: at most 4 writes. Log lines keep the return picture WHEP: prefix.
  • src/lib/db-errors.ts: isDocConflictError, next to isDocNotFoundError.

No API, schema or behaviour change otherwise.

Tests

src/__tests__/returns-guest-auth.test.ts, new block with a session store that enforces _rev
like CouchDB:

  • another write lands on the session during the Strom round trip: the return binding and the
    other write are both kept, and the guest can DELETE their session. Fails on the current code.
  • the guest leaves during the round trip: the offer still answers 201, the session stays left
    and is not bound.

pnpm run typecheck, pnpm run build and npx vitest run (174 files, 1966 tests) pass.

Why now

#542 (commentator preview) lets a guest open several WHEP sessions at once and uses this helper
for its own bindings. It needs this PR merged first.

🤖 Generated with Claude Code

…onflict

A guest's return-picture WHEP POST binds the Strom session id to the guest
session doc (issue Eyevinn#380) by inserting the doc it read at the start of the
request. While the Strom round trip runs, the same guest page may write that
doc too (the talkback WHIP/WHEP bindings at join, mute, status), so the insert
gets a CouchDB 409. The failure was only logged: returnWhepSessionId stayed
unset, the guest's later DELETE of the session got a 403, and the Strom session
lasted until ICE consent timed out.

The binding now goes through bindWhepSession: on a 409 it re-reads the doc,
requires it to be live (state !== 'left'), applies only its own key and
retries, at most 4 writes. New isDocConflictError in lib/db-errors.ts.

Tests: a _rev-enforcing session mock, a concurrent write during the Strom
round trip (binding and the other write both kept; fails on the old code),
and a guest leaving mid-request (not bound).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@eyevinn-developer

Copy link
Copy Markdown
Contributor

daily-backlog-pr Phase 3: the read-through review verdict for this PR is APPROVE. Local typecheck, build and vitest passed on the PR head and on the PR merged with main. This run brought the branch level with main (now e0f6f5a). The CI run for this head (41866898570) is waiting for first-time-contributor approval, which this run could not grant because of an automation error on our side. Once build-and-test is green on this head, the approval is submitted and the PR merges without another review.

@eyevinn-pr-reviewer eyevinn-pr-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: fix(returns): keep the return-picture session binding on a revision conflict

Verdict: APPROVE

Prior findings

# Source Finding / acceptance criterion Status
— PR #621 reviews, review comments, issue comments No earlier reviews or inline comments. The only comment is the daily-backlog-pr update-branch note, which raised no findings. No prior findings
— #622 comments Architect breakdown into #623/#624/studio#256. No findings on #621. No prior findings
AC1 #623 On a CouchDB 409, re-read the guest session doc FIXED (b7ee82a, src/routes/returns.ts:168-178: isDocConflictError then db.get(doc._id))
AC2 #623 Require state !== 'left' before re-binding FIXED (b7ee82a, src/routes/returns.ts:180-183)
AC3 #623 Merge only its own key FIXED (b7ee82a, src/routes/returns.ts:122-125: spreads the re-read doc, sets only returnWhepSessionId/updatedAt)
AC4 #623 Bounded retries FIXED (b7ee82a, src/routes/returns.ts:143 MAX_BIND_ATTEMPTS = 4, checked at :169)
AC5 #623 Fix the lost return-picture binding when return picture and talkback open at the same time FIXED (b7ee82a, src/__tests__/returns-guest-auth.test.ts:355+; fails against main's returns.ts, passes on this head)
AC6 #623 CI green FIXED (build-and-test pass, run 41866898570)

Critical Issues

None.

Warnings

None.

Nits (optional)

  • bindTalkbackSession (src/routes/guests.ts:488) still has its own single-retry 409 handling; it could move onto bindWhepSession later (e.g. in #542).

What Was Verified

  • 409 detection uses statusCode === 409, same as bindTalkbackSession (guests.ts:507); isDocConflictError sits next to isDocNotFoundError in src/lib/db-errors.ts:18. The state === 'left' check matches whip.ts:270 and guests.ts:188/203.
  • Loop terminates after at most 4 inserts; non-409 errors, a failed re-read or a left session log and return. Still best-effort: the 201 response is unchanged and the DELETE checks (returns.ts:290, :411) still fail closed on an unbound id.
  • Fork PR scrutiny: 3 files, all in scope. Auth and validation unchanged; no secrets, endpoints or dependencies added.
  • Locally on e0f6f5a: pnpm install --frozen-lockfile, typecheck and build pass; returns-guest-auth.test.ts 22/22; full vitest 175 files / 2013 tests pass. With main's returns.ts/db-errors.ts swapped in, the new concurrency test fails as the PR claims.

@eyevinn-developer
eyevinn-developer Bot merged commit 353e25d into Eyevinn:main Nov 5, 2026
1 check passed
@svensson00

Copy link
Copy Markdown

Verified on a fresh beta instance on 2026-11-07 with talkback on: after leave, kick, revoke and deactivate, the guest's return-picture WHEP session tears down cleanly, with no stuck session on the Strom flow. Validated by Magnus. Not yet in a release (after v0.7.0).

eyevinn-developer Bot added a commit that referenced this pull request Nov 9, 2026
Operators delay sources with their time_offset blocks to bring remote
commentary and picture into sync, which also delays every program-derived
WHEP output a commentator can watch. A source assignment marked
`commentatorPreview: true` (SRT, EFP and clip sources) now gets a
builtin.videoenc (4 Mbit/s) and a low-latency builtin.whep_output fed from
the input block's raw video and audio pads (for SRT, the audio stream
selected for the mixer), before the offsets and outside the mixer.

- Activation stores `previewWhepUrls` ({ mixerInput, sourceId, url,
  endpointId }); deactivation clears them. A preview is served only while
  the input is still flagged and still holds the source it was built for.
- Crew route POST/DELETE /api/v1/productions/:id/previews/:mixerInput/whep
  and guest alias POST/DELETE /api/v1/guests/:inviteId/previews/:mixerInput/whep,
  sharing the server-scoped proxy of the return picture (refactored into
  proxyScopedWhep, behaviour and log lines unchanged). Guests are scoped to
  their own invite's production; session ids are bound per guest for
  teardown.
- Guest preview session ids are bound through bindWhepSession, which
  retries on a CouchDB 409, so opening previews and the return picture at
  once doesn't lose a binding. That helper is on main since #621.
- Guest join/slot responses list `previews: [{ mixerInput, label, url }]`.
- OpenAPI and docs/specs/guest-calling-intercom.md updated; tests added,
  including a _rev-enforcing guest session mock.

Carried forward from #542 (markusnygard:feat/commentator-preview,
commit 2cbadb3) onto main after #621 (353e25d), per the decision on #624.
src/routes/returns.ts conflict resolved as posted on #542: this commit's
side, with #621's shorter call-site comment. The TEST_API_KEY fixture in
commentator-preview-routes.test.ts gets a `: string` annotation so the
commit guard's secret pattern doesn't flag the shared test literal.

Co-authored-by: markusnygard <markus.nygard@yle.fi>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: eyevinn-developer[bot] <343978855+eyevinn-developer[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants