Skip to content

Retry NotReady, fix the resilience registrations, add protocol tooling - #44

Merged
Agash merged 4 commits into
masterfrom
feat/retry-and-protocol-tooling
Sep 22, 2026
Merged

Agash merged 4 commits into
masterfrom
feat/retry-and-protocol-tooling

Conversation

@Agash

@Agash Agash commented Sep 22, 2026 •

Copy link
Copy Markdown
Owner

Closes #27, #29, #32, #43.

NotReady retry

OBS answers NotReady (207) while changing scene collection or shutting down, and documents it as
retryable. WebSocketServer_Protocol.cpp:219 rejects the request inside an if (_obsReady) guard
before RequestHandler::ProcessRequest runs, and the batch path does the same for the whole batch,
so nothing is partially applied and a mutation is as safe to resend as a read.

Off by default, configured by ObsWebSocketClientOptions.NotReadyRetry. Requests and batches run
through a pipeline registered under ObsWebSocketResilience.NotReadyPipelineKey; register your own
under that key to replace it. Each attempt sends a fresh request id, because OBS pairs a response to
the id it was sent with.

Not applied to the handshake: Identify has no _obsReady guard and fails with WebSocket close
codes, not a RequestStatus, so 207 cannot occur there.

Resilience registrations

AddObsWebSocketReconnectPipeline registered a pipeline nothing resolved, and the docs claimed
registering your own under ReconnectPipelineKey replaced the reconnect policy. It did not.
Reconnect cannot be a Polly pipeline: a clean disconnect is not an exception, so the connection loop
owns attempt counting and fatal classification. The delay curve is now IObsReconnectDelays, which
the loop does resolve and an application can replace.

Response metadata

CallAsync took a JsonTypeInfo for the request and had no equivalent for the response, which
ended at GetTypeInfo against this library's generated context. There is no reflection fallback, so
a consumer type threw and JsonElement was the only response that worked. It also diverged by
transport, since MessagePack resolves its own contracts and accepted the same type.

CallAsync, CallAsyncValue and CallRequiredAsync take responseTypeInfo next to
requestTypeInfo. On the serializer this is one method per shape rather than an overload pair;
a default interface method would have been intercepted by mocks instead of falling through.

Stub audit

ObsWebSocket.StubAudit diffs the stub types against the obs-websocket C++ that builds them,
reporting fields OBS emits that no stub declares, stub fields nothing emits, and numerics narrower
than the C type. It reflects over the shipped types rather than restating them. Run by hand against
local clones, so the package still builds without an obs-studio checkout. Clean against the pinned
revision.

Protocol refresh

Weekly job compares the newest upstream commit touching protocol.json against the pinned one,
regenerates with ObsCodegenForceRegeneration=true, builds, tests, and opens a PR. Nothing merges
itself, and the live OBS gate runs on the result.

Breaking

  • IWebSocketMessageSerializer.DeserializePayload and DeserializeValuePayload take an optional
    JsonTypeInfo<TPayload>.
  • CallAsync, CallAsyncValue and CallRequiredAsync take responseTypeInfo before timeoutMs.
  • ObsWebSocketResilience.ReconnectPipelineKey and AddObsWebSocketReconnectPipeline are gone,
    along with WithReconnectPipeline(). Use IObsReconnectDelays for the curve, and
    WithNotReadyPipeline() for the retry.

Testing

185 tests per TFM, zero warnings. The retry is covered on both sides: refused twice then accepted
sends three times, disabled sends once, and another status code is not retried. Live validation
against OBS over both transports exits 0.

Verification of the refresh job

Simulated against the current release. Detection reports no drift (pinned and newest upstream commit
touching the file are both bc50ba1). Forcing a refresh to the previous upstream commit rewrote the
lock file and sha256 and regenerated 40+ sources, then failed the build on GetCanvasList, which is
the gate working: an incompatible definition stops the job before it opens a pull request.

@Agash
Agash force-pushed the feat/retry-and-protocol-tooling branch from aff25b5 to 62b7fbb Compare September 22, 2026 13:12
@Agash
Agash merged commit 21960b0 into master Sep 22, 2026
10 checks passed
@Agash
Agash deleted the feat/retry-and-protocol-tooling branch September 22, 2026 13:23
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.

Optionally retry requests OBS rejects with NotReady

1 participant