Skip to content

Fix Streaming EA outages when receiving different casing for same asset#720

Closed
alecgard wants to merge 2 commits intomainfrom
fix/case-sensitive-feed-outage
Closed

Fix Streaming EA outages when receiving different casing for same asset#720
alecgard wants to merge 2 commits intomainfrom
fix/case-sensitive-feed-outage

Conversation

@alecgard
Copy link
Contributor

@alecgard alecgard commented Mar 19, 2026

Two commits

  • 51d2e3f adds tests to reproduce the bug where requests with different casing for the same asset can cause a Streaming EA to return no data
  • 1916a02 implements the fix - not overwriting the subscription if we already have it stored with a different casing. Also added a warning log, as this is a sign that different jobs are using different casing, which is unexpected.

Can see the repro tests failed before the fix:
image

And passed after:
image

@alecgard alecgard changed the title Fix case sensitive feed outages Fix Streaming EA outages when receiving different casing for same asset Mar 19, 2026
@alecgard alecgard force-pushed the fix/case-sensitive-feed-outage branch 2 times, most recently from dbf18b6 to 3142128 Compare March 19, 2026 12:01
…eed death

  When two requests share the same normalised cache key but differ in casing
  (e.g. USDe/USD and usde/usd), ExpiringSortedSet.add() was overwriting the
  stored params with the new casing. This caused StreamingTransport's
  JSON.stringify diff to see a spurious change on the next background execute,
  triggering an unnecessary unsubscribe then resubscribe. Because sendMessages
  sends subscribes before unsubscribes, a case-insensitive provider would start
  the feed then immediately kill it — and because localSubscriptions was then set
  to desiredSubs, no diff would fire again, leaving the feed permanently dead and
  both request variants returning 504 after cache expiry.

  Fix: when a key already exists in ExpiringSortedSet, only refresh the TTL and
  keep the original value (first-writer-wins). Emit a warning when the incoming
  value differs from the stored one, as this indicates inconsistent request casing
  that may cause issues with case-sensitive providers.
@alecgard alecgard force-pushed the fix/case-sensitive-feed-outage branch from 3142128 to 261b937 Compare March 19, 2026 12:03
@alecgard alecgard marked this pull request as ready for review March 19, 2026 12:12
@alecgard alecgard requested a review from a team as a code owner March 19, 2026 12:12
@alecgard alecgard requested a review from alejoberardino March 19, 2026 15:55
Copy link
Contributor

@alejoberardino alejoberardino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alecgard @cl-efornaciari @cawthorne

I am against doing solving our current problem (some feeds have conflicts) with a framework change right now in general, as I believe there's better alternatives:

  • Changing the jobs is slow, but it's the most performant and also keeps our source of truth the cleanest. This should be done regardless, but does fix the issues we have.
  • I believe the implemented EAs themselves cause this issue: many of them erase casing at the wrong stage (e.g. before subscribing) instead of making this apply before keys are resolved (i.e. as request transforms). That erasure is not performant, and when trying to squeeze out performance for high throughput like we do for these EAs it becomes very relevant; however, if necessary, we should at least fix the EAs from doing it in the wrong place.
  • A framework change increases the surface for risk much more than individual EA changes or job fixes.

That said a framework should serve its users, so if the desire is for case insensitivity I can only urge caution as I do see some currently working cases breaking from the change in expectations. Happy to chat more about this too, I'm not currently maintaining this framework or the individual EAs so if you don't agree let me know or dismiss explicitly

add(value: T, ttl: number, key: string) {
let node = this.map.get(key)
if (node) {
if (JSON.stringify(value) !== JSON.stringify(node.data.value)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stringifies are expensive, we should not be performing these here

`Subscription set received a value for key "${key}" that differs from the stored value. ` +
`Keeping the original value to avoid unnecessary subscription churn. ` +
`This indicates requests are using inconsistent parameter casing - ` +
`stored: ${JSON.stringify(node.data.value)}, incoming: ${JSON.stringify(value)}`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even more stringifies haha

@alecgard alecgard closed this Mar 19, 2026
@alecgard
Copy link
Contributor Author

Closing this PR as after discussing with @alejoberardino we found a case where this could cause the EA to return data from the wrong asset if DP is case sensitive.

Back to the drawing board.

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.

4 participants