channels: drop stale membership caches on join/leave
After channels.leaveChannel, a client that polls channels.getFullChannel kept receiving a projection that still showed it as an active member (left=false) until the per-(viewer,channel) RPC projection cache and the store-level member cache lapsed on their own or the async read-model NOTIFY landed. The client therefore kept an open compose box while every send was already rejected with CHANNEL_PRIVATE - most visible on public forum supergroups, where getFullChannel keeps succeeding via the preview path instead of tearing the chat down. Every other membership-mutating path already busts these caches synchronously; join/leave/invite/request-approval did not. Add: - store: invalidateChannelMembershipCaches (row + member + dialog caches), called post-commit from JoinChannel, LeaveChannel, ImportInvite, InviteToChannel. - rpc: invalidateChannelMembershipProjection (channelFullProjectionCache pair), called from the join/leave/invite/hide-requests handlers for every user whose membership changed.
This commit is contained in:
parent
012f8a8d0e
commit
b047a90271
7 changed files with 118 additions and 1 deletions
|
|
@ -118,6 +118,33 @@ func (s *ChannelStore) boostCacheActive(db sqlcgen.DBTX) bool {
|
|||
return s.boostCache != nil && db == s.db
|
||||
}
|
||||
|
||||
// invalidateChannelMembershipCaches drops the in-process reads that a membership
|
||||
// change (join, leave, invite, kick) makes stale for the given users. The
|
||||
// ReadModelChangeListener also clears these off the async NOTIFY, but callers
|
||||
// must not depend on that round-trip: a client that polls channels.getFullChannel
|
||||
// right after channels.leaveChannel would otherwise keep seeing itself as an
|
||||
// active member (and keep an open compose box) until the notify lands. Call it
|
||||
// post-commit.
|
||||
func (s *ChannelStore) invalidateChannelMembershipCaches(channelID int64, userIDs ...int64) {
|
||||
if channelID == 0 {
|
||||
return
|
||||
}
|
||||
if s.rowCache != nil {
|
||||
s.rowCache.delete(channelID)
|
||||
}
|
||||
for _, userID := range userIDs {
|
||||
if userID == 0 {
|
||||
continue
|
||||
}
|
||||
if s.memberCache != nil {
|
||||
s.memberCache.delete(channelID, userID)
|
||||
}
|
||||
if s.dialogCache != nil {
|
||||
s.dialogCache.delete(userID, channelID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewChannelStore 基于 pgx 连接池(或事务)创建 ChannelStore。
|
||||
func NewChannelStore(db sqlcgen.DBTX, opts ...ChannelStoreOption) *ChannelStore {
|
||||
s := &ChannelStore{db: db}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue