Use crypto.randomUUID() for ActionCableLink channelId to prevent collisions#5642
Merged
rmosolgo merged 2 commits intoMay 26, 2026
Merged
Conversation
The previous channelId generator, `Math.round(Date.now() + Math.random() * 100000).toString(16)`, had only ~100,000 distinct values per millisecond. When multiple subscriptions are opened around the same time, occasional collisions are inevitable. When two subscriptions share the same identifier, the server-side ActionCable deduplicates the subscribe, but the client retains both Subscription objects and routes every incoming payload to both `received` callbacks, leaking one subscription's payload into another's listener. Switch to `crypto.randomUUID()` (Web Crypto API), which is available in all evergreen browsers and Node 14.17+, so collisions are effectively impossible. Fixes rmosolgo#5639
Owner
|
Thanks again for taking this on.
This is a good point -- I doubt anyone will need it, but I imagine a global crypto polyfill would work, too. I added a commit that also accepts an injected |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace the channelId generator in
ActionCableLinkto usecrypto.randomUUID()so that simultaneously-created subscriptions no longer collide.Fixes #5639.
Background
The previous channelId generator,
draws from only ~100,001 distinct values per millisecond. When multiple
useSubscriptionhooks mount during the same render pass (or simply within a short time window), collisions are inevitable. With N subscriptions opened within ~100 seconds of each other, the collision probability is approximatelyC(N, 2) / 100_000(e.g. ~0.05% for N=10, ~0.2% for N=20).When two subscriptions share the same identifier:
subscribe(return if subscriptions.key?(id_key)inaction_cable/connection/subscriptions.rb).Subscriptionobjects — ActionCable JS does not deduplicate.receivedon all local subscriptions whose identifier matches.Missing field 'X' while writing result {...}warnings and downstreamonDatahandlers to crash withTypeError.Changes
ActionCableLink.ts: replace the time+random channelId withcrypto.randomUUID(). Add a short comment explaining why.__tests__/ActionCableLinkTest.ts: add a test that creates 1000 subscriptions in a tight loop and asserts the channelIds are all unique. With the old generator this would fail frequently (collisions are expected when many IDs are drawn within a single ms); withcrypto.randomUUID()it always passes.Compatibility
crypto.randomUUID()is available in:globalThis.cryptoin Node 19+)If wider compatibility is required I'm happy to add a fallback (e.g.
crypto.getRandomValuesover a 128-bit buffer, or a module-scoped counter + Date.now).Test plan
npm test(full suite) passes locally — 92 passed / 3 skipped (unchanged from before)generates a unique channelId for each subscriptionpasses