Repository navigation
smp server: deliver messages sent while a subscription is registered - #1997
Draft
evgeny-simplex wants to merge 12 commits into
Draft
evgeny-simplex wants to merge 12 commits into
evgeny-simplex wants to merge 12 commits into
Conversation
…to ep/smp-sub-race
This was referenced Oct 30, 2026
epoberezkin
reviewed
Oct 30, 2026
| lookupDeleteSubscribedClient :: EntityId -> SubscribedClients s -> STM (Maybe (Client s)) | ||
| lookupDeleteSubscribedClient entId (SubscribedClients cs) = | ||
| TM.lookupDelete entId cs $>>= (`swapTVar` Nothing) | ||
| TM.lookup entId cs $>>= \cv -> TM.delete entId cs >> swapTVar cv Nothing |
Member
There was a problem hiding this comment.
why? aren't they identical?
This branch has not been deployed
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.
Plan: https://github.com/evgeny-simplex/simplexmq/blob/ep/smp-sub-race/plans/2026-10-26-sub-send-race.md
Scope
This pull request is split into three (plan, section "Pull requests"):
Commits:
Problem
Change
FOR UPDATEinrecipient_idorder, the first message of each queue read, credentials verified against the locked records, subscribers registered, commit.write_messagetakes the same row lock.deliveredset in one STM transaction, so that a message read by the fold is pushed once, either by SEND or by the fold.setQueueServiceslocks the queue rows inrecipient_idorder before the association UPDATE (D4).Tests
should deliver message sent in the same block as SUB: SEND processed between the SUB read and the registration. Fails on master.should deliver message sent in the same block as SUBS once: SEND in the same block as SUBS; it can be processed during the SUBS message fold and, on master, before serverThread registers the service. One MSG expected.should end service subscription with queues remaining after DEL: service subscription count after DEL and SUBS from other connections of the same service. It checks the final outcome and passes on master; the remaining DEL window is described in plan §4 item 9.Questions
FOR UPDATEor advisory transaction locks. Implemented as recommended:FOR UPDATE(a), kept after V5: ≈ 395 B of WAL per subscription in a full restart storm (measurements in smp server: shorten row locks of PostgreSQL SUB batches #2007).setQueueServices. Implemented as recommended (include).[SUB q, GET q]). Implemented as recommended (accept).SubThreadstate after a delivery thread exits (present on master). Moved to smp server: deliver later messages after SUB while a delivery waits #2006, with test T4.synchronous_commitoff for the SUB transaction. Moved to smp server: shorten row locks of PostgreSQL SUB batches #2007.LIMIT 1per queue. Moved to smp server: shorten row locks of PostgreSQL SUB batches #2007.servicesrows. Not applied, as recommended (leave unchanged).The V4 measurements below were taken before the split, on dd436b0 and 9d9e8e0, which include D7, D9 and D10.
Measurements: V4 contention, reduced (plan §5 item 7)
In-memory store; server on 4 cores, load generator on 4 others; 4 senders × 8 threads without a rate cap; A = master c0c416d, C = dd436b0 (this PR before the review commits); 3 alternating pairs per scenario; median [min-max]; host shared with other workloads.
Measurements: V4 contention, full (plan §5 item 7)
A = master c0c416d, C = 9d9e8e0 (this PR), both with benchmark-only counters (registration transaction executions, pool acquisition and hold times); SMP server (
-N4) and PostgreSQL 16 (fresh cluster, pool size 10, defaults forfsyncandsynchronous_commit) on 4 cores, load generator on 4 others; 4 senders × 8 threads without a rate cap; 50,000 queues (individual: 10,000), one prefilled message each unless noted; storm = all connections subscribe at once; displace = set A subscribes all queues, then set B subscribes them while A stays connected; 3 alternating pairs per configuration; A / C medians; 1-minute load 4-14 at run start.PostgreSQL:
In-memory store:
ERR AUTH(in-memory, 32 connections, no prefill; 1 of 300,000 SUBs in 6 runs of C, 0 of 300,000 in 6 runs of A, 0 in other configurations); the server does not log which AUTH path it took, and the cause is not yet determined.Measurements: V4, remaining scenarios
The two configurations left out of the full V4 run (plan §5 item 7): the PostgreSQL storm without prefill, and the service-association storm. Setup as in the full run: A = master c0c416d, C = 9d9e8e0 (this PR), the same benchmark-only counters, SMP server (
-N4) and PostgreSQL 16 (fresh cluster, pool size 10) on 4 cores, load generator on 4 others, 4 senders × 8 threads without a rate cap, 50,000 queues without prefill, 3 alternating pairs per configuration, A / C medians, 1-minute load 7-15 at run start. In the service-association storm each connection has its own service certificate, and each SUB associates a queue created without a service with that service.PostgreSQL:
In-memory store:
totalServiceSubs(D12 fallback 2).ERR AUTHfor one SUB of C again (PostgreSQL association storm, 32 connections; the run stopped and has no window): 1 of 21 C runs, 0 of 21 A runs. Both occurrences in V4 are in C, with 32 connections and no prefill, one per store. PostgreSQL logged no error; the server does not log which AUTH path was taken (verification, association, missingtryPeekMsgsentry, locked-key mismatch,updateQueueTime), and the cause is not yet determined.CI with all tests after the split passed in https://github.com/simplex-chat/simplexmq/actions/runs/41097274652 (1449 examples, 0 failures).