Retry NotReady, fix the resilience registrations, add protocol tooling - #44
Merged
Merged
Conversation
Agash
force-pushed
the
feat/retry-and-protocol-tooling
branch
from
September 22, 2026 13:12
aff25b5 to
62b7fbb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #27, #29, #32, #43.
NotReady retry
OBS answers
NotReady(207) while changing scene collection or shutting down, and documents it asretryable.
WebSocketServer_Protocol.cpp:219rejects the request inside anif (_obsReady)guardbefore
RequestHandler::ProcessRequestruns, 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 runthrough a pipeline registered under
ObsWebSocketResilience.NotReadyPipelineKey; register your ownunder 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:
Identifyhas no_obsReadyguard and fails with WebSocket closecodes, not a
RequestStatus, so 207 cannot occur there.Resilience registrations
AddObsWebSocketReconnectPipelineregistered a pipeline nothing resolved, and the docs claimedregistering your own under
ReconnectPipelineKeyreplaced 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, whichthe loop does resolve and an application can replace.
Response metadata
CallAsynctook aJsonTypeInfofor the request and had no equivalent for the response, whichended at
GetTypeInfoagainst this library's generated context. There is no reflection fallback, soa consumer type threw and
JsonElementwas the only response that worked. It also diverged bytransport, since MessagePack resolves its own contracts and accepted the same type.
CallAsync,CallAsyncValueandCallRequiredAsynctakeresponseTypeInfonext torequestTypeInfo. 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.StubAuditdiffs 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.jsonagainst the pinned one,regenerates with
ObsCodegenForceRegeneration=true, builds, tests, and opens a PR. Nothing mergesitself, and the live OBS gate runs on the result.
Breaking
IWebSocketMessageSerializer.DeserializePayloadandDeserializeValuePayloadtake an optionalJsonTypeInfo<TPayload>.CallAsync,CallAsyncValueandCallRequiredAsynctakeresponseTypeInfobeforetimeoutMs.ObsWebSocketResilience.ReconnectPipelineKeyandAddObsWebSocketReconnectPipelineare gone,along with
WithReconnectPipeline(). UseIObsReconnectDelaysfor the curve, andWithNotReadyPipeline()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 thelock file and sha256 and regenerated 40+ sources, then failed the build on
GetCanvasList, which isthe gate working: an incompatible definition stops the job before it opens a pull request.