Reset former members' connections and grant open rooms in one statement

Deleting the memberships with delete_all skips Membership's after_destroy_commit, which resets a member's
connections when one membership is revoked. Until the job ran, a member who had the room open kept its
streams, and a message that still landed in the room (a request already past the membership check, a bot's
reply) reached them. The request now reads the members' ids in the transaction that deletes their
memberships, and the job resets their connections before it destroys the messages. It costs a Redis round
trip per member, about 1.5 s for 10,000, so it's done in the job rather than in the request.

User#grant_membership_to_open_rooms read the open rooms and inserted in a separate statement, so a user
created while a room was being closed could read it as open and be granted it after the close. It's now a
single insert ... select, which SQLite runs under the write lock, skipping duplicates as insert_all did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bj8KnxpTf9sj2Ysa8aLAVa
This commit is contained in:
Marcello Costagliola
2026-10-06 14:45:18 +02:00
parent 7b16014f01
commit 462eff10df
6 changed files with 52 additions and 6 deletions
+2 -1
View File
@@ -90,8 +90,9 @@ class RoomsControllerTest < ActionDispatch::IntegrationTest
test "destroy takes the room away from its members at once and leaves its messages to a job" do
room = rooms(:designers)
member_ids = room.memberships.pluck(:user_id)
assert_enqueued_with(job: Room::DestroyJob, args: [ room ]) do
assert_enqueued_with(job: Room::DestroyJob, args: [ room, member_ids ]) do
assert_no_difference -> { Message.count } do
delete room_url(room)
assert_redirected_to root_url