Broadcast a session's QUIT once when its clients leave together (closes #154)
check / check (push) Waiting to run

Logout deleted the client, counted the session's clients and ran
BroadcastQuit as separate statements, and BroadcastQuit read the peers,
parted, deleted empty channels and deleted the session separately too.
Two logouts or QUITs of one session at once both sent its QUIT, and a
JOIN landing between a part and the channel's deletion got into a +k
channel without the key.

db.QuitSession now does all of that in one transaction, parting each
channel as PartChannelAndDeleteIfEmpty does, and reports whether it
deleted the session; only that caller sends the QUIT. db.LogoutClient
deletes the client and counts the rest in the same transaction.

Model: opus-5-5
This commit is contained in:
2026-10-20 21:09:39 +00:00
parent 108e381428
commit 6ed3fee559
6 changed files with 453 additions and 112 deletions
+3 -24
View File
@@ -3395,15 +3395,11 @@ func (hdlr *Handlers) HandleLogout() http.HandlerFunc {
return
}
ctx := request.Context()
err := hdlr.params.Database.DeleteClient(
ctx, clientID,
err := hdlr.svc.Logout(
request.Context(), sessionID, clientID, nick,
)
if err != nil {
hdlr.log.Error(
"delete client failed", "error", err,
)
hdlr.log.Error("logout failed", "error", err)
hdlr.respondError(
writer, request,
"internal error",
@@ -3413,23 +3409,6 @@ func (hdlr *Handlers) HandleLogout() http.HandlerFunc {
return
}
// If no clients remain, clean up the user fully:
// part all channels (notifying members) and
// delete the session.
remaining, err := hdlr.params.Database.
ClientCountForSession(ctx, sessionID)
if err != nil {
hdlr.log.Error(
"client count check failed", "error", err,
)
}
if remaining == 0 {
hdlr.cleanupUser(
ctx, sessionID, nick,
)
}
hdlr.clearAuthCookie(writer)
hdlr.respondJSON(writer, request,