Commit 79c86bd
fix(fpss): reconcile subscription state against server responses (#876)
* fix(fpss): reconcile subscription state against server responses
A subscribe is tracked in the reconnect-replay set the instant the frame is sent, but the server answers asynchronously and may reject it. Until now nothing removed a rejected entry, so a MaxStreamsReached or InvalidPerms contract was re-attempted on every reconnect forever and permanently over-reported by active_subscriptions(). Correlate the REQ_RESPONSE to the subscribe it answers by the req_id allocated at send time: a rejected subscription is untracked, not replayed, and an accepted one stays tracked.
The derived OHLCVC accumulator read the trade date only on its first trade. Once initialized, a trade carrying a new date merged onto the prior date's open/high/low and cumulative volume/count while the emitted bar date stayed stale. The derived bar now rolls to a fresh bar on a date change before applying the trade.
A write failure during the paced reconnect replay was only warn-logged while the loop continued, leaving a contract silently unsubscribed on the new session until the next disconnect. A replay write failure now re-enters reconnect, matching how every other mid-replay I/O failure escalates; the per-burst flush path is made consistent so it escalates the same way.
Full-stream and per-contract subscriptions are tracked independently and the server can broadcast a contract on both feeds; the subscribe surface now documents that overlapping the same kind and security type across both scopes double-delivers, since the client does not de-duplicate across them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(fpss): untrack rejected subscriptions by request identity, not value
A duplicate per-contract or full-stream subscribe shares the one tracked entry the first subscribe created, but the subscribe paths registered an untrack-capable pending correlation on every call. A server rejection of the duplicate (for example MaxStreamsReached, the realistic trigger once the first subscribe has claimed the stream slot) then removed the entry by value, dropping the still-live original from active_subs and from the reconnect-replay set that clones it, silently silencing the stream. Only the subscribe that actually added the tracked entry now carries an untrack-capable correlation, so a duplicate's rejection can no longer touch the live subscription.
Bound the pending correlation registry. Entries were removed only by a matching req_id response or the reconnect-time clear, so a session whose response the server suppresses, or that echoes the uncorrelated -1 sentinel a matching remove can never key on, leaked an entry for the life of a session that never reconnects. Each correlation now records its insertion instant; the registry is swept on insert, evicting entries past a generous response deadline and capping resident size with an oldest-first drop and a single warn-level log.
Make WakeFd::from_raw_write_fd an unsafe fn. It adopts a caller-supplied raw descriptor and closes it on Drop, so a safe signature let a caller pass a borrowed or aliased fd and invite a double-close or operation on a recycled descriptor. The unsafe marker documents the ownership-transfer contract, matching the FromRawFd convention; the two call sites are wrapped in audited unsafe blocks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(fpss): gate the subscription-reconcile test helper to test builds only
The helper is only called from in-crate unit tests, so cfg(test) is the correct
gate. The previous test-helpers feature gate left it compiled but unused in the
library target, tripping dead_code under -D warnings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: preview <noreply@anthropic.com>1 parent 225a801 commit 79c86bd
6 files changed
Lines changed: 705 additions & 27 deletions
File tree
- crates/thetadatadx/src/fpss
- io_loop
- protocol
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
78 | | - | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
79 | 86 | | |
80 | 87 | | |
81 | 88 | | |
| |||
222 | 229 | | |
223 | 230 | | |
224 | 231 | | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
225 | 261 | | |
226 | 262 | | |
227 | 263 | | |
| |||
0 commit comments