mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-09 00:00:12 +09:00
Merge pull request #329: Delete a room's messages in a job, one transaction each
Reviewed and merged by GPT on behalf of DHH.
This commit is contained in:
@@ -45,6 +45,34 @@ class Room < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
# Takes the room away from its members at once, and leaves its messages to Room::DestroyJob. Destroying them in
|
||||
# the room's own transaction held the database's write lock, and stopped every other write, until the last one.
|
||||
# An open room is closed first, so that no one who joins the account before the job runs is let in.
|
||||
def destroy_later
|
||||
room = open? ? becomes!(Rooms::Closed) : self
|
||||
|
||||
former_member_ids = transaction do
|
||||
room.save!
|
||||
room.memberships.pluck(:user_id).tap { room.memberships.delete_all }
|
||||
end
|
||||
|
||||
Room::DestroyJob.perform_later(room, former_member_ids)
|
||||
end
|
||||
|
||||
# Deleting the memberships skips the reset that revoking one does, so the former members get it here, all at once,
|
||||
# before the messages go: their connections stop receiving the room's streams. It's done in the job, not the
|
||||
# request, because it costs a Redis round trip per member.
|
||||
def reset_remote_connections_of(user_ids)
|
||||
User.where(id: user_ids).find_each(&:reset_remote_connections)
|
||||
end
|
||||
|
||||
# Each message is destroyed in its own transaction, so other writes get through in between. Any message posted
|
||||
# meanwhile goes with the room.
|
||||
def destroy_one_message_at_a_time
|
||||
messages.find_each(&:destroy)
|
||||
destroy
|
||||
end
|
||||
|
||||
def receive(message)
|
||||
unread_memberships(message)
|
||||
push_later(message)
|
||||
|
||||
+11
-1
@@ -50,8 +50,18 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
private
|
||||
# One statement, which SQLite runs under the write lock, so no room is closed between reading the open ones and
|
||||
# inserting. A room that Room#destroy_later closes is either closed before, and skipped, or after, and takes this
|
||||
# membership away with the others. A room that granted itself to every user when it was created is skipped too,
|
||||
# as insert_all did.
|
||||
def grant_membership_to_open_rooms
|
||||
Membership.insert_all(Rooms::Open.pluck(:id).collect { |room_id| { room_id: room_id, user_id: id } })
|
||||
now = Time.current
|
||||
|
||||
Membership.connection.execute Membership.sanitize_sql([ <<~SQL, id, now, now ])
|
||||
insert into memberships (room_id, user_id, created_at, updated_at)
|
||||
select id, ?, ?, ? from rooms where type = 'Rooms::Open'
|
||||
on conflict do nothing
|
||||
SQL
|
||||
end
|
||||
|
||||
def deactived_email_address
|
||||
|
||||
Reference in New Issue
Block a user