Skip to content

perf: Deduplicate concurrent polling requests with a flight group - #798

Draft
keelerm84 wants to merge 3 commits into
v9from
mk/SDK-2877/singleflight-for-poll
Draft

perf: Deduplicate concurrent polling requests with a flight group#798
keelerm84 wants to merge 3 commits into
v9from
mk/SDK-2877/singleflight-for-poll

Conversation

@keelerm84

Copy link
Copy Markdown
Member

The streaming endpoints have always collapsed concurrent replay builds
through a singleflight group, but every polling request snapshotted and
serialized the store on its own. The two server-side full-payload
polling endpoints (GET /sdk/poll and GET /sdk/flags) now share one
payload build per environment through a flight group exposed by the
EnvContext, keyed by the FDv2 basis where the payload depends on it.
The per-context evaluation endpoints and the per-key PHP endpoints are
unchanged: their results rarely collide, so there is little duplicate
work to share.

The store and serialize spans belong to the one request that executes
the build; every request records relay.singleflight.shared on its
request span so a trace without those child spans is explainable.

The streaming endpoints have always collapsed concurrent replay builds
through a singleflight group, but every polling request snapshotted and
serialized the store on its own. The two server-side full-payload
polling endpoints (GET /sdk/poll and GET /sdk/flags) now share one
payload build per environment through a flight group exposed by the
EnvContext, keyed by the FDv2 basis where the payload depends on it.
The per-context evaluation endpoints and the per-key PHP endpoints are
unchanged: their results rarely collide, so there is little duplicate
work to share.

The store and serialize spans belong to the one request that executes
the build; every request records relay.singleflight.shared on its
request span so a trace without those child spans is explainable.
…time

A request that received its payload from a flight another request was
executing now records how long it waited as relay.singleflight.wait_ms
on its request span. The executing request carries no wait attribute --
it did not wait, and its time is visible as the store and serialize
child spans -- so the attribute's presence alone identifies a request
that waited, and slow waits are queryable.

Both polling handlers now resolve their flights through a shared
runPollingFlight helper that owns the Do call and both span
annotations. That indirection also breaks the taint chain behind the
gosec G705 false positive on writeCacheableJSONResponse, so its nolint
directive is removed.
The streaming repositories' replay flight groups now record the same
information the polling endpoints do: relay.singleflight.shared on the
subscribing request's span, plus relay.singleflight.wait_ms when a
replay waited on a flight another subscriber was already executing.
The annotation logic moves into tracing.SingleflightDo so both sides
record identical information by construction.

The flags-only repository never implemented ReplayWithContext, so it
had no request context to annotate; it now advertises context support
the same way the main server-side repository does.
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.

1 participant