Skip to content

Commit 3142128

Browse files
committed
fix: preserve subscription casing on duplicate cache key to prevent feed 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.
1 parent 69f5609 commit 3142128

File tree

1 file changed

+16
-1
lines changed

1 file changed

+16
-1
lines changed

src/util/subscription-set/expiring-sorted-set.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,23 @@ export class ExpiringSortedSet<T> implements SubscriptionSet<T> {
3232
add(value: T, ttl: number, key: string) {
3333
let node = this.map.get(key)
3434
if (node) {
35+
if (JSON.stringify(value) !== JSON.stringify(node.data.value)) {
36+
logger.warn(
37+
`Subscription set received a value for key "${key}" that differs from the stored value. ` +
38+
`Keeping the original value to avoid unnecessary subscription churn. ` +
39+
`This indicates requests are using inconsistent parameter casing - ` +
40+
`stored: ${JSON.stringify(node.data.value)}, incoming: ${JSON.stringify(value)}`,
41+
)
42+
}
3543
node.data = {
36-
value,
44+
// Preserve the existing value rather than overwriting it. The key is the
45+
// normalised cache key (e.g. lowercased), so two entries that share a key
46+
// represent the same logical subscription. Overwriting the value with a
47+
// differently-cased variant would cause the streaming transport's
48+
// JSON.stringify-based diff to see a change, triggering an
49+
// unnecessary unsubscribe + resubscribe cycle that can permanently kill
50+
// the provider feed. Only the TTL needs refreshing here.
51+
value: node.data.value,
3752
expirationTimestamp: Date.now() + ttl,
3853
}
3954
this.moveToTail(node)

0 commit comments

Comments
 (0)