Conversation
|
@launchdarkly/js-sdk-common size report |
|
@launchdarkly/js-client-sdk-common size report |
|
@launchdarkly/js-client-sdk size report |
7ac87e5 to
aa2b162
Compare
| if (e.headers?.[`x-ld-fd-fallback`] === `true`) { | ||
| fallbackRequested = true; | ||
| } |
There was a problem hiding this comment.
🟡 Stale fallback survives a reconnect
When a fallback-marked connection drops before a payload, fallbackRequested remains true after an unmarked reconnect. The next payload incorrectly switches to FDv1.
Learn more
The event source reuses this processor and its onopen callback across automatic reconnections. The fallback header applies to the connection response that carries it. Assigning only on true makes the prior connection's directive sticky when a retry opens without that header. The client-side streaming base resets its pending fallback state on every open in the open handler.
Example: Connection A opens with x-ld-fd-fallback: true, then disconnects before a payload. Connection B opens without the header and sends a valid payload. That payload receives fallbackToFDv1: true, although Connection B never requested fallback.
Recommended fix: Assign fallbackRequested from the current open response on every onopen, making absent or non-true headers clear stale state.
| if (e.headers?.[`x-ld-fd-fallback`] === `true`) { | |
| fallbackRequested = true; | |
| } | |
| fallbackRequested = e.headers?.[`x-ld-fd-fallback`] === `true`; |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
I don't think we want to do this. If the server passes a fallback directive, then we will respect it until we are able to go through the whole recovery process... even if there is a reconnect, we cannot assume that FDv2 is online whenever we reconnect the stream.
aa2b162 to
9e49a09
Compare
9e49a09 to
34f07d6
Compare
Summary
Hardens FDv2 streaming error handling in the client-side streaming base, the server-side streaming processor, and the shared FDv2 payload stream reader. This is the first layer of the eventsource migration stack, but it is independent of the new package: the fixes are correct with the current transport and with the one that replaces it.
SSE delivers two different things under the same
errorevent type: server-sent FDv2event: errorprotocol frames, and connection-level failures. The FDv2 data sources handled that overlap in a fragile way, in two directions:errorlistener throughaddEventListenerand then assignedonerror. An EventSource implementation may implementon*assignment by replacing the registered listeners of that type - the transport currently in use does - so the assignment silently removed the protocol listener. Server-signalled FDv2 error payloads were swallowed, and the payload processor's error path was unreachable.onerrorslot, where it would be reported as a fabricated network error; and a connection-level event reachingPayloadStreamReaderread as a protocol violation ("missing data") and closed a stream the error filter had chosen to retry.Changes:
on*handler is assigned before the protocol listeners register, so registrations stay intact under either assignment semantics.datapayload are treated as protocol frames. The clientonerrorslot ignores them (the protocol listener owns them), andPayloadStreamReaderinjs-sdk-commonnow ignores a datalesserrorevent instead of reporting a protocol violation - connection failures stay with the error filter, which owns retry decisions. This puts the rule in the reader itself rather than requiring every consumer to filter events before handing over the stream, and matches the semantics the client-side FDv2 listener already used. Missing data on every other event type is still reported.oncloseis log-only, matching both FDv1 processors.onclosesignals a deliberateclose(); reportingClosedfrom there surfaced as a spuriousInterruptedfrom the composite data source during normal shutdown. Terminal errors were already reported through the error filter.on*slots independent of the listener registry, slot-first ordering, and a loud failure when an event has no receiver - so a lost listener fails tests instead of passing silently. New tests cover the frame/failure distinction in the reader and on both data-source sides.Addresses:
sdk-3162
sdk-3073
Note
Overview
Fixes FDv2 streaming where SSE
errorevents mean two different things—server protocol frames vs connection failures—and whereon*handler assignment could wipeaddEventListenerlisteners.PayloadStreamReaderno longer treats a datalesserrorevent as a protocol violation; missingdataon other event types is still reported.Client
StreamingFDv2Baseand serverStreamingProcessorFDv2now set allonopen/onerror/onclose/onretryinghandlers before registering FDv2 protocol listeners, so transports that replace listeners onon*assignment keep protocol handling. Clientonerrorskips events whose payload has stringdataso server error frames are not double-reported asNETWORK_ERROR. Serveroncloseis log-only (noClosedstatus), aligning with FDv1 and avoiding spurious compositeInterruptedon shutdown.Tests upgrade mock EventSources to dispatch like real transports (independent
on*slots + listener registry, slot-first) and add coverage for frame vs failure classification and listener retention.Reviewed by Cursor Bugbot for commit 34f07d6. Bugbot is set up for automated code reviews on this repo. Configure here.