fix: sync scoped connection and outbox exclusion updates

This commit is contained in:
A 2026-07-12 12:01:53 +08:00
parent aa21bd04e1
commit cbccd6a8d9
58 changed files with 919 additions and 1435 deletions

View file

@ -77,12 +77,7 @@ func (r *Router) onAuthBindTempAuthKey(ctx context.Context, req *tg.AuthBindTemp
r.tempKeyResolveCache.Delete(id)
}
if r.deps.Sessions != nil {
if scoped, ok := r.scopedSessions(); ok {
rawAuthKeyID, _ := RawAuthKeyIDFrom(ctx)
scoped.BindAuthKeyForSession(rawAuthKeyID, sessionID, authKeyIDFromInt64(req.PermAuthKeyID))
} else {
r.deps.Sessions.BindAuthKey(sessionID, authKeyIDFromInt64(req.PermAuthKeyID))
}
r.deps.Sessions.BindAuthKeyForSession(id, sessionID, authKeyIDFromInt64(req.PermAuthKeyID))
}
r.invalidateAuthUserCache(id)
return true, nil
@ -192,14 +187,8 @@ func (r *Router) bindLoginTokenTarget(target loginTokenTarget, userID int64) {
if r.deps.Sessions == nil || target.sessionID == 0 {
return
}
if scoped, ok := r.scopedSessions(); ok && target.rawAuthKeyID != ([8]byte{}) {
scoped.BindAuthKeyForSession(target.rawAuthKeyID, target.sessionID, target.authKeyID)
scoped.BindUserForAuthKey(target.rawAuthKeyID, target.sessionID, userID)
r.announceSessionOnline(loginTokenTargetContext(target, userID), userID)
return
}
r.deps.Sessions.BindAuthKey(target.sessionID, target.authKeyID)
r.deps.Sessions.BindUser(target.sessionID, userID)
r.deps.Sessions.BindAuthKeyForSession(target.rawAuthKeyID, target.sessionID, target.authKeyID)
r.deps.Sessions.BindUserForAuthKey(target.rawAuthKeyID, target.sessionID, userID)
r.announceSessionOnline(loginTokenTargetContext(target, userID), userID)
}
@ -220,19 +209,13 @@ func (r *Router) pushLoginTokenAccepted(ctx context.Context, target loginTokenTa
Update: &tg.UpdateLoginToken{},
Date: int(r.clock.Now().Unix()),
}
if immediate, ok := r.deps.Sessions.(ScopedImmediateSessionPusher); ok && target.rawAuthKeyID != ([8]byte{}) {
if immediate, ok := r.deps.Sessions.(ImmediateSessionPusher); ok {
if err := immediate.PushToSessionForAuthKeyImmediate(ctx, target.rawAuthKeyID, target.sessionID, proto.MessageFromServer, updates); err != nil {
r.log.Debug("push login token accepted immediate", zap.Int64("session_id", target.sessionID), zap.Error(err))
}
return
}
if scoped, ok := r.scopedSessions(); ok && target.rawAuthKeyID != ([8]byte{}) {
if err := scoped.PushToSessionForAuthKey(ctx, target.rawAuthKeyID, target.sessionID, proto.MessageFromServer, updates); err != nil {
r.log.Debug("push login token accepted", zap.Int64("session_id", target.sessionID), zap.Error(err))
}
return
}
if err := r.deps.Sessions.PushToSession(ctx, target.sessionID, proto.MessageFromServer, updates); err != nil {
if err := r.deps.Sessions.PushToSessionForAuthKey(ctx, target.rawAuthKeyID, target.sessionID, proto.MessageFromServer, updates); err != nil {
r.log.Debug("push login token accepted", zap.Int64("session_id", target.sessionID), zap.Error(err))
}
}
@ -713,13 +696,7 @@ func (r *Router) bindSessionUser(ctx context.Context, userID int64) {
if !ok {
return
}
if scoped, ok := r.scopedSessions(); ok {
rawAuthKeyID, _ := RawAuthKeyIDFrom(ctx)
scoped.BindUserForAuthKey(rawAuthKeyID, sessionID, userID)
r.announceSessionOnline(ctx, userID)
return
}
r.deps.Sessions.BindUser(sessionID, userID)
r.deps.Sessions.BindUserForAuthKey(rawAuthKeyIDForOrigin(ctx), sessionID, userID)
r.announceSessionOnline(ctx, userID)
}
@ -776,13 +753,7 @@ func (r *Router) pushSignInServiceNotificationToOthers(ctx context.Context, u do
go func() {
pushCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if scoped, ok := r.scopedSessions(); ok {
if sent, err := scoped.PushToUserExceptAuthKeySession(pushCtx, u.ID, rawAuthKeyID, sessionID, proto.MessageFromServer, notification); err != nil {
r.log.Debug("push sign-in service notification", zap.Int64("user_id", u.ID), zap.Int("sent", sent), zap.Error(err))
}
return
}
if sent, err := r.deps.Sessions.PushToUserExceptSession(pushCtx, u.ID, sessionID, proto.MessageFromServer, notification); err != nil {
if sent, err := r.deps.Sessions.PushToUserExceptAuthKeySession(pushCtx, u.ID, rawAuthKeyID, sessionID, proto.MessageFromServer, notification); err != nil {
r.log.Debug("push sign-in service notification", zap.Int64("user_id", u.ID), zap.Int("sent", sent), zap.Error(err))
}
}()

View file

@ -65,7 +65,7 @@ func newRecoveryFanoutSessions(onlineChannels []int64, release <-chan struct{})
}
}
func (s *recoveryFanoutSessions) PushToUserExceptSession(ctx context.Context, _ int64, _ int64, _ proto.MessageType, msg bin.Encoder) (int, error) {
func (s *recoveryFanoutSessions) PushToUserExceptAuthKeySession(ctx context.Context, _ int64, _ [8]byte, _ int64, _ proto.MessageType, msg bin.Encoder) (int, error) {
s.startOnce.Do(func() { close(s.pushStarted) })
if s.pushRelease != nil {
select {
@ -397,7 +397,7 @@ func newOverflowNudgeSessions(onlineByChannel map[int64][]int64) *overflowNudgeS
}
}
func (s *overflowNudgeSessions) PushToUserExceptSession(ctx context.Context, userID, excludeSessionID int64, typ proto.MessageType, msg bin.Encoder) (int, error) {
func (s *overflowNudgeSessions) PushToUserExceptAuthKeySession(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, typ proto.MessageType, msg bin.Encoder) (int, error) {
if updates, ok := msg.(*tg.Updates); ok && len(updates.Updates) == 1 {
if nudge, ok := updates.Updates[0].(*tg.UpdateChannelTooLong); ok {
pts, _ := nudge.GetPts()
@ -407,7 +407,7 @@ func (s *overflowNudgeSessions) PushToUserExceptSession(ctx context.Context, use
s.mu.Unlock()
}
}
return s.captureSessions.PushToUserExceptSession(ctx, userID, excludeSessionID, typ, msg)
return s.captureSessions.PushToUserExceptAuthKeySession(ctx, userID, excludeAuthKeyID, excludeSessionID, typ, msg)
}
func (s *overflowNudgeSessions) OnlineChannelMemberUserIDsExcluding(channelID int64, exclude map[int64]struct{}, limit int) []int64 {
@ -445,11 +445,11 @@ func newNudgeSessions(online []int64) *nudgeSessions {
return &nudgeSessions{captureSessions: &captureSessions{}, online: online, byUser: map[int64]bin.Encoder{}}
}
func (s *nudgeSessions) PushToUserExceptSession(ctx context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
func (s *nudgeSessions) PushToUserExceptAuthKeySession(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
s.mu.Lock()
s.byUser[userID] = msg
s.mu.Unlock()
return s.captureSessions.PushToUserExceptSession(ctx, userID, excludeSessionID, t, msg)
return s.captureSessions.PushToUserExceptAuthKeySession(ctx, userID, excludeAuthKeyID, excludeSessionID, t, msg)
}
func (s *nudgeSessions) OnlineChannelMemberUserIDsExcluding(_ int64, exclude map[int64]struct{}, limit int) []int64 {

View file

@ -29,26 +29,12 @@ func (r *Router) currentUserID(ctx context.Context) (int64, bool, error) {
}
if r.deps.Sessions != nil {
if sessionID, ok := SessionIDFrom(ctx); ok {
if scoped, ok := r.scopedSessions(); ok {
if rawAuthKeyID, ok := RawAuthKeyIDFrom(ctx); ok {
if userID, resolved := scoped.UserIDResolvedForAuthKey(rawAuthKeyID, sessionID); resolved {
if userID == 0 {
if authKeyID, ok := AuthKeyIDFrom(ctx); ok {
if cachedUserID, ok := r.positiveCachedAuthUser(authKeyID); ok {
scoped.BindUserForAuthKey(rawAuthKeyID, sessionID, cachedUserID)
r.announceSessionOnline(ctx, cachedUserID)
return cachedUserID, true, nil
}
}
}
return userID, userID != 0, nil
}
}
} else if userID, resolved := r.deps.Sessions.UserIDResolved(sessionID); resolved {
rawAuthKeyID := rawAuthKeyIDForOrigin(ctx)
if userID, resolved := r.deps.Sessions.UserIDResolvedForAuthKey(rawAuthKeyID, sessionID); resolved {
if userID == 0 {
if authKeyID, ok := AuthKeyIDFrom(ctx); ok {
if cachedUserID, ok := r.positiveCachedAuthUser(authKeyID); ok {
r.deps.Sessions.BindUser(sessionID, cachedUserID)
r.deps.Sessions.BindUserForAuthKey(rawAuthKeyID, sessionID, cachedUserID)
r.announceSessionOnline(ctx, cachedUserID)
return cachedUserID, true, nil
}

View file

@ -49,36 +49,26 @@ type AuthService interface {
}
// SessionBinder 抽象登录后 session 与 user 的在线绑定。
//
// MTProto session 的完整身份是 raw auth_key_id + session_id。所有定位单个 session
// 的方法都必须携带这两个值;禁止退回只按 session_id 查询,否则不同 auth key 复用
// 同一随机 session_id 时会产生跨账号绑定、排除或推送歧义。
type SessionBinder interface {
BindAuthKey(sessionID int64, authKeyID [8]byte)
AuthKeyID(sessionID int64) ([8]byte, bool)
BindUser(sessionID, userID int64)
UserID(sessionID int64) (int64, bool)
UserIDResolved(sessionID int64) (userID int64, resolved bool)
UnbindAuthKey(authKeyID [8]byte) int
SetReceivesUpdates(sessionID int64, receives bool)
PushToSession(ctx context.Context, sessionID int64, t proto.MessageType, msg bin.Encoder) error
PushToUserExceptSession(ctx context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error)
}
// ScopedSessionBinder 是 SessionBinder 的精确版本:所有定位都带 raw auth_key_id + session_id。
// 生产 mtprotoedge.SessionManager 实现它;测试替身和旧实现可以只实现 SessionBinder。
type ScopedSessionBinder interface {
BindAuthKeyForSession(rawAuthKeyID [8]byte, sessionID int64, authKeyID [8]byte)
AuthKeyIDForSession(rawAuthKeyID [8]byte, sessionID int64) ([8]byte, bool)
BindUserForAuthKey(rawAuthKeyID [8]byte, sessionID, userID int64)
UserIDForAuthKey(rawAuthKeyID [8]byte, sessionID int64) (int64, bool)
UserIDResolvedForAuthKey(rawAuthKeyID [8]byte, sessionID int64) (userID int64, resolved bool)
UnbindAuthKey(authKeyID [8]byte) int
SetReceivesUpdatesForAuthKey(rawAuthKeyID [8]byte, sessionID int64, receives bool)
PushToSessionForAuthKey(ctx context.Context, rawAuthKeyID [8]byte, sessionID int64, t proto.MessageType, msg bin.Encoder) error
// excludeAuthKeyID is the physical/raw auth key, paired with session_id.
// excludeAuthKeyID/excludeSessionID 必须同时为零(不排除)或同时非零(精确排除)。
PushToUserExceptAuthKeySession(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error)
}
// ScopedImmediateSessionPusher 是可选的登录前信号直推能力。
// ImmediateSessionPusher 是可选的登录前信号直推能力。
// 它绕过登录后 updates-ready 队列,只能用于会解锁登录流程本身的握手消息,
// 例如 updateLoginToken。
type ScopedImmediateSessionPusher interface {
type ImmediateSessionPusher interface {
PushToSessionForAuthKeyImmediate(ctx context.Context, rawAuthKeyID [8]byte, sessionID int64, t proto.MessageType, msg bin.Encoder) error
}
@ -110,13 +100,9 @@ type RawSessionTerminator interface {
CloseSessionsForRawAuthKeyExcept(authKeyID [8]byte, exceptSessionID int64) int
}
// BestEffortSessionBinder 是 updates fanout 的短超时推送接口;不用于 RPC result/ack。
// BestEffortSessionBinder 是带 raw auth_key_id 精确排除当前设备的短超时推送接口;
// 不用于 RPC result/ack。
type BestEffortSessionBinder interface {
PushToUserExceptSessionBestEffort(ctx context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder, timeout time.Duration) (int, error)
}
// ScopedBestEffortSessionBinder 是带 raw auth_key_id 精确排除当前设备的 best-effort 版本。
type ScopedBestEffortSessionBinder interface {
PushToUserExceptAuthKeySessionBestEffort(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder, timeout time.Duration) (int, error)
}

View file

@ -29,7 +29,10 @@ const (
defaultOutboxMaxIdleInterval = 1 * time.Second
)
var errMissingOutboxEvent = errors.New("missing outbox update event")
var (
errMissingOutboxEvent = errors.New("missing outbox update event")
errInvalidOutboxExclusionPair = errors.New("outbox exclusion requires both raw auth key and session id")
)
// OutboxDispatcher 把 PG transactional outbox 中的 update 批量推给在线 session。
// 多 worker 并发 claimClaimPending 用 FOR UPDATE SKIP LOCKEDworker 间认领不重叠。
@ -273,6 +276,23 @@ type outboxEventKey struct {
// dispatchBatch 批量加载已 claim 事件、逐条 push、批量标记 delivered失败项单独退避重试。
func (d *OutboxDispatcher) dispatchBatch(ctx context.Context, items []store.DispatchOutboxItem, loader batchEventLoader, marker batchOutboxMarker) {
valid := make([]store.DispatchOutboxItem, 0, len(items))
blockedUsers := make(map[int64]struct{})
for _, item := range items {
if _, blocked := blockedUsers[item.TargetUserID]; blocked {
continue
}
if err := validateOutboxExclusionPair(item); err != nil {
d.markDispatchFailed(ctx, item, err)
blockedUsers[item.TargetUserID] = struct{}{}
continue
}
valid = append(valid, item)
}
if len(valid) == 0 {
return
}
items = valid
cursors := make([]store.EventCursor, len(items))
for i, item := range items {
cursors[i] = store.EventCursor{UserID: item.TargetUserID, Pts: item.Pts}
@ -299,7 +319,7 @@ func (d *OutboxDispatcher) dispatchBatch(ctx context.Context, items []store.Disp
start := time.Now()
ready := make([]outboxDispatchReady, 0, len(items))
requests := make([]OutboxUpdateRequest, 0, len(items))
blockedUsers := make(map[int64]struct{})
clear(blockedUsers)
for _, item := range items {
if _, blocked := blockedUsers[item.TargetUserID]; blocked {
continue
@ -362,6 +382,10 @@ type outboxDispatchReady struct {
func (d *OutboxDispatcher) dispatchItem(ctx context.Context, item store.DispatchOutboxItem) bool {
start := time.Now()
if err := validateOutboxExclusionPair(item); err != nil {
d.markDispatchFailed(ctx, item, err)
return false
}
events, err := d.events.ListAfter(ctx, item.TargetUserID, item.Pts-1, 1)
if err != nil {
d.markDispatchFailed(ctx, item, err)
@ -443,23 +467,29 @@ func (d *OutboxDispatcher) buildOutboxUpdates(ctx context.Context, requests []Ou
// 接口剩余的非 context 错误通常是确定性的编码/构造错误,必须进入 failed不能永久占着
// dispatching head 靠租约空转。只有 dispatcher shutdown/deadline 属于可重试中断。
func (d *OutboxDispatcher) pushOutboxUpdate(ctx context.Context, item store.DispatchOutboxItem, update *tg.Updates) (sent int, retriable bool, err error) {
var zeroAuthKeyID [8]byte
if err := validateOutboxExclusionPair(item); err != nil {
return 0, false, errInvalidOutboxExclusionPair
}
if d.pushTimeout > 0 {
if scoped, ok := d.sessions.(ScopedBestEffortSessionBinder); ok && item.ExcludeAuthKeyID != zeroAuthKeyID {
sent, err = scoped.PushToUserExceptAuthKeySessionBestEffort(ctx, item.TargetUserID, item.ExcludeAuthKeyID, item.ExcludeSessionID, proto.MessageFromServer, update, d.pushTimeout)
return sent, outboxPushInterrupted(err), err
}
if bestEffort, ok := d.sessions.(BestEffortSessionBinder); ok {
sent, err = bestEffort.PushToUserExceptSessionBestEffort(ctx, item.TargetUserID, item.ExcludeSessionID, proto.MessageFromServer, update, d.pushTimeout)
sent, err = bestEffort.PushToUserExceptAuthKeySessionBestEffort(ctx, item.TargetUserID, item.ExcludeAuthKeyID, item.ExcludeSessionID, proto.MessageFromServer, update, d.pushTimeout)
return sent, outboxPushInterrupted(err), err
}
}
if scoped, ok := d.sessions.(ScopedSessionBinder); ok && item.ExcludeAuthKeyID != zeroAuthKeyID {
sent, err = scoped.PushToUserExceptAuthKeySession(ctx, item.TargetUserID, item.ExcludeAuthKeyID, item.ExcludeSessionID, proto.MessageFromServer, update)
return sent, false, err
sent, err = d.sessions.PushToUserExceptAuthKeySession(ctx, item.TargetUserID, item.ExcludeAuthKeyID, item.ExcludeSessionID, proto.MessageFromServer, update)
if err != nil {
return sent, outboxPushInterrupted(err), err
}
sent, err = d.sessions.PushToUserExceptSession(ctx, item.TargetUserID, item.ExcludeSessionID, proto.MessageFromServer, update)
return sent, false, err
return sent, false, nil
}
func validateOutboxExclusionPair(item store.DispatchOutboxItem) error {
var zeroAuthKeyID [8]byte
if (item.ExcludeAuthKeyID != zeroAuthKeyID) != (item.ExcludeSessionID != 0) {
return errInvalidOutboxExclusionPair
}
return nil
}
func outboxPushInterrupted(err error) bool {

View file

@ -33,6 +33,7 @@ func TestOutboxDispatcherPushesNewMessageAndMarksDelivered(t *testing.T) {
TargetUserID: msg.OwnerUserID,
Pts: msg.Pts,
EventType: domain.UpdateEventNewMessage,
ExcludeAuthKeyID: [8]byte{1},
ExcludeSessionID: 99,
}}}
events := &captureUpdateEventStore{events: []domain.UpdateEvent{{
@ -103,6 +104,78 @@ func TestOutboxDispatcherUsesScopedAuthKeyExclusion(t *testing.T) {
}
}
func TestOutboxDispatcherRejectsPartialSessionExclusion(t *testing.T) {
tests := []struct {
name string
authKeyID [8]byte
sessionID int64
}{
{name: "auth key only", authKeyID: [8]byte{1}},
{name: "session only", sessionID: 99},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
const userID = int64(1000000002)
outbox := &captureDispatchOutbox{items: []store.DispatchOutboxItem{{
ID: 58,
TargetUserID: userID,
Pts: 10,
EventType: domain.UpdateEventPeerSettings,
ExcludeAuthKeyID: tt.authKeyID,
ExcludeSessionID: tt.sessionID,
}}}
// No event exists: exclusion shape must win before event loading so a bad
// durable row is not mislabeled as merely missing its payload.
events := &captureUpdateEventStore{}
sessions := &captureSessions{}
metrics := &captureOutboxMetrics{}
dispatcher := NewOutboxDispatcher(events, outbox, sessions, zaptest.NewLogger(t), WithOutboxMetrics(metrics))
dispatcher.DispatchOnce(context.Background())
if !outbox.failed || outbox.delivered {
t.Fatalf("outbox failed=%v delivered=%v, want failed without delivery", outbox.failed, outbox.delivered)
}
if outbox.failedError != errInvalidOutboxExclusionPair.Error() {
t.Fatalf("failed error = %q, want %q", outbox.failedError, errInvalidOutboxExclusionPair)
}
if sessions.message != nil {
t.Fatalf("invalid exclusion unexpectedly pushed %T", sessions.message)
}
if metrics.failed != 1 || metrics.delivered != 0 {
t.Fatalf("metrics failed=%d delivered=%d, want 1/0", metrics.failed, metrics.delivered)
}
})
}
}
func TestOutboxDispatcherBatchRejectsPartialExclusionBeforeNoop(t *testing.T) {
const userID = int64(1000000002)
events := &batchEventStore{captureUpdateEventStore: &captureUpdateEventStore{events: []domain.UpdateEvent{{
UserID: userID,
Type: domain.UpdateEventNoop,
Pts: 10,
}}}}
outbox := &batchDispatchOutbox{captureDispatchOutbox: &captureDispatchOutbox{items: []store.DispatchOutboxItem{{
ID: 59,
TargetUserID: userID,
Pts: 10,
EventType: domain.UpdateEventNoop,
ExcludeAuthKeyID: [8]byte{1},
}}}}
dispatcher := NewOutboxDispatcher(events, outbox, &captureSessions{}, zaptest.NewLogger(t))
dispatcher.DispatchOnce(context.Background())
if !outbox.failed || outbox.delivered || len(outbox.deliveredBatch) != 0 {
t.Fatalf("batch invalid pair failed=%v delivered=%v batch=%v", outbox.failed, outbox.delivered, outbox.deliveredBatch)
}
if outbox.failedError != errInvalidOutboxExclusionPair.Error() {
t.Fatalf("failed error = %q, want %q", outbox.failedError, errInvalidOutboxExclusionPair)
}
if len(events.batchCursors) != 0 {
t.Fatalf("invalid pair reached batch event loader: %+v", events.batchCursors)
}
}
// TestOutboxDispatcherBatchPath 覆盖生产批量路径store 同时具备 BatchByCursor + MarkDeliveredBatch
// 时DispatchOnce 一次批量取事件、推送、再批量标记 delivered而非逐条。
func TestOutboxDispatcherBatchPath(t *testing.T) {
@ -129,6 +202,7 @@ func TestOutboxDispatcherBatchPath(t *testing.T) {
TargetUserID: msg.OwnerUserID,
Pts: msg.Pts,
EventType: domain.UpdateEventNewMessage,
ExcludeAuthKeyID: [8]byte{1},
ExcludeSessionID: 99,
}}}}
sessions := &captureSessions{}
@ -661,22 +735,34 @@ func TestOutboxDispatcherUsesBestEffortPush(t *testing.T) {
Message: msg,
Users: []domain.User{{ID: msg.From.ID, FirstName: "Sender"}},
}}}
outbox := &captureDispatchOutbox{items: []store.DispatchOutboxItem{{
ID: 55,
TargetUserID: msg.OwnerUserID,
Pts: msg.Pts,
EventType: domain.UpdateEventNewMessage,
ExcludeSessionID: 99,
}}}
sessions := &captureBestEffortSessions{captureSessions: &captureSessions{}}
dispatcher := NewOutboxDispatcher(events, outbox, sessions, zaptest.NewLogger(t), WithOutboxPushTimeout(50*time.Millisecond))
dispatcher.DispatchOnce(context.Background())
for _, tt := range []struct {
name string
authKeyID [8]byte
sessionID int64
}{
{name: "exclude origin", authKeyID: [8]byte{1}, sessionID: 99},
{name: "exclude none"},
} {
t.Run(tt.name, func(t *testing.T) {
outbox := &captureDispatchOutbox{items: []store.DispatchOutboxItem{{
ID: 55,
TargetUserID: msg.OwnerUserID,
Pts: msg.Pts,
EventType: domain.UpdateEventNewMessage,
ExcludeAuthKeyID: tt.authKeyID,
ExcludeSessionID: tt.sessionID,
}}}
sessions := &captureBestEffortSessions{captureSessions: &captureSessions{}}
dispatcher := NewOutboxDispatcher(events, outbox, sessions, zaptest.NewLogger(t), WithOutboxPushTimeout(50*time.Millisecond))
dispatcher.DispatchOnce(context.Background())
if !sessions.bestEffort || sessions.timeout != 50*time.Millisecond {
t.Fatalf("best-effort push = %v timeout %v, want true/50ms", sessions.bestEffort, sessions.timeout)
}
if !outbox.delivered || outbox.failed {
t.Fatalf("outbox delivered=%v failed=%v, want delivered after accepted best-effort push", outbox.delivered, outbox.failed)
if !sessions.bestEffort || sessions.timeout != 50*time.Millisecond {
t.Fatalf("best-effort push = %v timeout %v, want true/50ms", sessions.bestEffort, sessions.timeout)
}
if !outbox.delivered || outbox.failed {
t.Fatalf("outbox delivered=%v failed=%v, want delivered after accepted best-effort push", outbox.delivered, outbox.failed)
}
})
}
}
@ -686,10 +772,10 @@ type captureBestEffortSessions struct {
timeout time.Duration
}
func (s *captureBestEffortSessions) PushToUserExceptSessionBestEffort(ctx context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder, timeout time.Duration) (int, error) {
func (s *captureBestEffortSessions) PushToUserExceptAuthKeySessionBestEffort(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder, timeout time.Duration) (int, error) {
s.bestEffort = true
s.timeout = timeout
return s.PushToUserExceptSession(ctx, userID, excludeSessionID, t, msg)
return s.PushToUserExceptAuthKeySession(ctx, userID, excludeAuthKeyID, excludeSessionID, t, msg)
}
type orderedOutboxCaptureSessions struct {
@ -709,7 +795,7 @@ type selectiveFailOutboxSessions struct {
attempts []outboxPushAttempt
}
func (s *selectiveFailOutboxSessions) PushToUserExceptSession(_ context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
func (s *selectiveFailOutboxSessions) PushToUserExceptAuthKeySession(_ context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
pts := 0
if updates, ok := msg.(*tg.Updates); ok {
pts = firstOutboxUpdatePts(updates)
@ -718,18 +804,18 @@ func (s *selectiveFailOutboxSessions) PushToUserExceptSession(_ context.Context,
if userID == s.failUserID && pts == s.failPts {
return 0, errors.New("injected outbox push failure")
}
return s.captureSessions.PushToUserExceptSession(context.Background(), userID, excludeSessionID, t, msg)
return s.captureSessions.PushToUserExceptAuthKeySession(context.Background(), userID, excludeAuthKeyID, excludeSessionID, t, msg)
}
func (s *selectiveFailOutboxSessions) pushAttempts() []outboxPushAttempt {
return append([]outboxPushAttempt(nil), s.attempts...)
}
func (s *orderedOutboxCaptureSessions) PushToUserExceptSession(_ context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
func (s *orderedOutboxCaptureSessions) PushToUserExceptAuthKeySession(_ context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
if updates, ok := msg.(*tg.Updates); ok {
s.pushed = append(s.pushed, firstOutboxUpdatePts(updates))
}
return s.captureSessions.PushToUserExceptSession(context.Background(), userID, excludeSessionID, t, msg)
return s.captureSessions.PushToUserExceptAuthKeySession(context.Background(), userID, excludeAuthKeyID, excludeSessionID, t, msg)
}
func (s *orderedOutboxCaptureSessions) pushedPts() []int {
@ -916,32 +1002,30 @@ func (s *captureScopedSessions) immediatePushSnapshot() (proto.MessageType, bin.
}
func (s *captureScopedSessions) BindAuthKeyForSession(rawAuthKeyID [8]byte, sessionID int64, authKeyID [8]byte) {
s.BindAuthKey(sessionID, authKeyID)
s.captureSessions.BindAuthKeyForSession(rawAuthKeyID, sessionID, authKeyID)
s.setScopedAuthKeyID(rawAuthKeyID)
}
func (s *captureScopedSessions) AuthKeyIDForSession([8]byte, int64) ([8]byte, bool) {
return s.AuthKeyID(0)
func (s *captureScopedSessions) AuthKeyIDForSession(rawAuthKeyID [8]byte, sessionID int64) ([8]byte, bool) {
return s.captureSessions.AuthKeyIDForSession(rawAuthKeyID, sessionID)
}
func (s *captureScopedSessions) BindUserForAuthKey(rawAuthKeyID [8]byte, sessionID, userID int64) {
s.BindUser(sessionID, userID)
s.captureSessions.BindUserForAuthKey(rawAuthKeyID, sessionID, userID)
s.setScopedAuthKeyID(rawAuthKeyID)
}
func (s *captureScopedSessions) UserIDForAuthKey([8]byte, int64) (int64, bool) {
return s.UserID(0)
func (s *captureScopedSessions) UserIDResolvedForAuthKey(rawAuthKeyID [8]byte, sessionID int64) (int64, bool) {
return s.captureSessions.UserIDResolvedForAuthKey(rawAuthKeyID, sessionID)
}
func (s *captureScopedSessions) UserIDResolvedForAuthKey([8]byte, int64) (int64, bool) {
return s.UserIDResolved(0)
func (s *captureScopedSessions) SetReceivesUpdatesForAuthKey(rawAuthKeyID [8]byte, sessionID int64, receives bool) {
s.captureSessions.SetReceivesUpdatesForAuthKey(rawAuthKeyID, sessionID, receives)
}
func (s *captureScopedSessions) SetReceivesUpdatesForAuthKey([8]byte, int64, bool) {}
func (s *captureScopedSessions) PushToSessionForAuthKey(_ context.Context, rawAuthKeyID [8]byte, sessionID int64, t proto.MessageType, msg bin.Encoder) error {
s.setScopedAuthKeyID(rawAuthKeyID)
return s.PushToSession(context.Background(), sessionID, t, msg)
return s.captureSessions.PushToSessionForAuthKey(context.Background(), rawAuthKeyID, sessionID, t, msg)
}
func (s *captureScopedSessions) PushToSessionForAuthKeyImmediate(_ context.Context, rawAuthKeyID [8]byte, sessionID int64, t proto.MessageType, msg bin.Encoder) error {
@ -951,12 +1035,12 @@ func (s *captureScopedSessions) PushToSessionForAuthKeyImmediate(_ context.Conte
s.immediateType = t
s.immediateMsg = msg
s.scopedMu.Unlock()
return s.PushToSession(context.Background(), sessionID, t, msg)
return s.captureSessions.PushToSessionForAuthKey(context.Background(), rawAuthKeyID, sessionID, t, msg)
}
func (s *captureScopedSessions) PushToUserExceptAuthKeySession(_ context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
s.setScopedAuthKeyID(excludeAuthKeyID)
return s.PushToUserExceptSession(context.Background(), userID, excludeSessionID, t, msg)
return s.captureSessions.PushToUserExceptAuthKeySession(context.Background(), userID, excludeAuthKeyID, excludeSessionID, t, msg)
}
func (s *captureDispatchOutbox) ClaimPending(context.Context, int) ([]store.DispatchOutboxItem, error) {
@ -1035,7 +1119,7 @@ type interruptedBestEffortSessions struct {
attempts int
}
func (s *interruptedBestEffortSessions) PushToUserExceptSessionBestEffort(_ context.Context, _ int64, _ int64, _ proto.MessageType, _ bin.Encoder, _ time.Duration) (int, error) {
func (s *interruptedBestEffortSessions) PushToUserExceptAuthKeySessionBestEffort(_ context.Context, _ int64, _ [8]byte, _ int64, _ proto.MessageType, _ bin.Encoder, _ time.Duration) (int, error) {
s.attempts++
return 0, context.DeadlineExceeded
}
@ -1065,6 +1149,7 @@ func TestOutboxDispatcherDefersOnPushInterruption(t *testing.T) {
TargetUserID: msg.OwnerUserID,
Pts: msg.Pts,
EventType: domain.UpdateEventNewMessage,
ExcludeAuthKeyID: [8]byte{1},
ExcludeSessionID: 99,
}}}
sessions := &interruptedBestEffortSessions{captureSessions: &captureSessions{}}

View file

@ -63,11 +63,9 @@ func (r *Router) pushPhoneSignalingData(ctx context.Context, targetUserID int64,
Date: int(r.clock.Now().Unix()),
Seq: 0,
}
if !device.Zero() {
if scoped, ok := r.scopedSessions(); ok {
if err := scoped.PushToSessionForAuthKey(ctx, device.RawAuthKeyID, device.SessionID, proto.MessageFromServer, upd); err == nil {
return
}
if !device.Zero() && r.deps.Sessions != nil {
if err := r.deps.Sessions.PushToSessionForAuthKey(ctx, device.RawAuthKeyID, device.SessionID, proto.MessageFromServer, upd); err == nil {
return
}
}
r.pushUserMessage(ctx, targetUserID, "phone call signaling", upd)

View file

@ -25,6 +25,7 @@ import (
// phonePushRecord 记录一次定向推送(目标用户、被排除的 session、载荷
type phonePushRecord struct {
userID int64
targetSession int64
excludeSession int64
msg bin.Encoder
}
@ -35,18 +36,25 @@ type phoneCaptureSessions struct {
log []phonePushRecord
}
func (s *phoneCaptureSessions) BindAuthKey(int64, [8]byte) {}
func (s *phoneCaptureSessions) AuthKeyID(int64) ([8]byte, bool) { return [8]byte{}, false }
func (s *phoneCaptureSessions) BindUser(int64, int64) {}
func (s *phoneCaptureSessions) UserID(int64) (int64, bool) { return 0, false }
func (s *phoneCaptureSessions) UserIDResolved(int64) (int64, bool) { return 0, false }
func (s *phoneCaptureSessions) UnbindAuthKey([8]byte) int { return 0 }
func (s *phoneCaptureSessions) SetReceivesUpdates(int64, bool) {}
func (s *phoneCaptureSessions) PushToSession(context.Context, int64, proto.MessageType, bin.Encoder) error {
func (s *phoneCaptureSessions) BindAuthKeyForSession([8]byte, int64, [8]byte) {}
func (s *phoneCaptureSessions) AuthKeyIDForSession([8]byte, int64) ([8]byte, bool) {
return [8]byte{}, false
}
func (s *phoneCaptureSessions) BindUserForAuthKey([8]byte, int64, int64) {}
func (s *phoneCaptureSessions) UserIDResolvedForAuthKey([8]byte, int64) (int64, bool) {
return 0, false
}
func (s *phoneCaptureSessions) UnbindAuthKey([8]byte) int { return 0 }
func (s *phoneCaptureSessions) SetReceivesUpdatesForAuthKey([8]byte, int64, bool) {}
func (s *phoneCaptureSessions) PushToSessionForAuthKey(_ context.Context, _ [8]byte, sessionID int64, _ proto.MessageType, msg bin.Encoder) error {
s.mu.Lock()
defer s.mu.Unlock()
s.log = append(s.log, phonePushRecord{targetSession: sessionID, msg: msg})
return nil
}
func (s *phoneCaptureSessions) PushToUserExceptSession(_ context.Context, userID, excludeSessionID int64, _ proto.MessageType, msg bin.Encoder) (int, error) {
func (s *phoneCaptureSessions) PushToUserExceptAuthKeySession(_ context.Context, userID int64, _ [8]byte, excludeSessionID int64, _ proto.MessageType, msg bin.Encoder) (int, error) {
s.mu.Lock()
defer s.mu.Unlock()
s.log = append(s.log, phonePushRecord{userID: userID, excludeSession: excludeSessionID, msg: msg})
@ -308,8 +316,8 @@ func TestPhoneCallRPCHappyPath(t *testing.T) {
t.Fatalf("sendSignalingData = %v err=%v", okSig, err)
}
pushes = f.sessions.records()
if len(pushes) != 1 || pushes[0].userID != f.callee.ID {
t.Fatalf("signaling pushes = %+v, want one to callee", pushes)
if len(pushes) != 1 || pushes[0].targetSession != phoneCalleeSession {
t.Fatalf("signaling pushes = %+v, want one to callee session", pushes)
}
sigUpdates := pushes[0].msg.(*tg.Updates)
sig, ok := sigUpdates.Updates[0].(*tg.UpdatePhoneCallSignalingData)

View file

@ -567,20 +567,11 @@ func (r *Router) pushSelfPhotoUpdateToCurrentSession(ctx context.Context, update
if !ok {
return
}
rawAuthKeyID, hasRawAuthKeyID := RawAuthKeyIDFrom(ctx)
rawAuthKeyID := rawAuthKeyIDForOrigin(ctx)
push := func() {
pushCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if scoped, ok := r.scopedSessions(); ok {
if !hasRawAuthKeyID {
return
}
if err := scoped.PushToSessionForAuthKey(pushCtx, rawAuthKeyID, sessionID, proto.MessageFromServer, updates); err != nil {
r.log.Debug("push self photo update to current session", zap.Int64("session_id", sessionID), zap.Error(err))
}
return
}
if err := r.deps.Sessions.PushToSession(pushCtx, sessionID, proto.MessageFromServer, updates); err != nil {
if err := r.deps.Sessions.PushToSessionForAuthKey(pushCtx, rawAuthKeyID, sessionID, proto.MessageFromServer, updates); err != nil {
r.log.Debug("push self photo update to current session", zap.Int64("session_id", sessionID), zap.Error(err))
}
}

View file

@ -15,16 +15,8 @@ func (r *Router) pushUserMessage(ctx context.Context, userID int64, logMessage s
sessionID, _ := SessionIDFrom(ctx)
if timeout := r.cfg.OutboundPushTimeout; timeout > 0 {
authKeyID := rawAuthKeyIDForOrigin(ctx)
if scoped, ok := r.deps.Sessions.(ScopedBestEffortSessionBinder); ok {
if sent, err := scoped.PushToUserExceptAuthKeySessionBestEffort(ctx, userID, authKeyID, sessionID, proto.MessageFromServer, msg, timeout); err != nil {
r.log.Debug(logMessage, zap.Int64("user_id", userID), zap.Int("sent", sent), zap.Duration("timeout", timeout), zap.Error(err))
return sent
} else {
return sent
}
}
if bestEffort, ok := r.deps.Sessions.(BestEffortSessionBinder); ok {
if sent, err := bestEffort.PushToUserExceptSessionBestEffort(ctx, userID, sessionID, proto.MessageFromServer, msg, timeout); err != nil {
if sent, err := bestEffort.PushToUserExceptAuthKeySessionBestEffort(ctx, userID, authKeyID, sessionID, proto.MessageFromServer, msg, timeout); err != nil {
r.log.Debug(logMessage, zap.Int64("user_id", userID), zap.Int("sent", sent), zap.Duration("timeout", timeout), zap.Error(err))
return sent
} else {
@ -32,16 +24,8 @@ func (r *Router) pushUserMessage(ctx context.Context, userID int64, logMessage s
}
}
}
if scoped, ok := r.scopedSessions(); ok {
authKeyID := rawAuthKeyIDForOrigin(ctx)
if sent, err := scoped.PushToUserExceptAuthKeySession(ctx, userID, authKeyID, sessionID, proto.MessageFromServer, msg); err != nil {
r.log.Debug(logMessage, zap.Int64("user_id", userID), zap.Int("sent", sent), zap.Error(err))
return sent
} else {
return sent
}
}
if sent, err := r.deps.Sessions.PushToUserExceptSession(ctx, userID, sessionID, proto.MessageFromServer, msg); err != nil {
authKeyID := rawAuthKeyIDForOrigin(ctx)
if sent, err := r.deps.Sessions.PushToUserExceptAuthKeySession(ctx, userID, authKeyID, sessionID, proto.MessageFromServer, msg); err != nil {
r.log.Debug(logMessage, zap.Int64("user_id", userID), zap.Int("sent", sent), zap.Error(err))
return sent
} else {
@ -76,17 +60,7 @@ func (r *Router) pushCurrentSessionMessage(ctx context.Context, logMessage strin
if !ok {
return
}
if scoped, ok := r.scopedSessions(); ok {
rawAuthKeyID, ok := RawAuthKeyIDFrom(ctx)
if !ok {
return
}
if err := scoped.PushToSessionForAuthKey(ctx, rawAuthKeyID, sessionID, proto.MessageFromServer, msg); err != nil {
r.log.Debug(logMessage, zap.Int64("session_id", sessionID), zap.Error(err))
}
return
}
if err := r.deps.Sessions.PushToSession(ctx, sessionID, proto.MessageFromServer, msg); err != nil {
if err := r.deps.Sessions.PushToSessionForAuthKey(ctx, rawAuthKeyIDForOrigin(ctx), sessionID, proto.MessageFromServer, msg); err != nil {
r.log.Debug(logMessage, zap.Int64("session_id", sessionID), zap.Error(err))
}
}

View file

@ -308,12 +308,7 @@ func (r *Router) effectiveAuthKeyID(ctx context.Context, rawAuthKeyID [8]byte, s
hasCached bool
)
if r.deps.Sessions != nil {
if scoped, ok := r.deps.Sessions.(ScopedSessionBinder); ok {
if id, ok := scoped.AuthKeyIDForSession(rawAuthKeyID, sessionID); ok {
cached = id
hasCached = true
}
} else if id, ok := r.deps.Sessions.AuthKeyID(sessionID); ok {
if id, ok := r.deps.Sessions.AuthKeyIDForSession(rawAuthKeyID, sessionID); ok {
cached = id
hasCached = true
}
@ -381,39 +376,22 @@ func (r *Router) effectiveAuthKeyID(ctx context.Context, rawAuthKeyID [8]byte, s
func (r *Router) bindEffectiveAuthKey(rawAuthKeyID [8]byte, sessionID int64, effective [8]byte) {
if r.deps.Sessions != nil {
if scoped, ok := r.deps.Sessions.(ScopedSessionBinder); ok {
scoped.BindAuthKeyForSession(rawAuthKeyID, sessionID, effective)
} else {
r.deps.Sessions.BindAuthKey(sessionID, effective)
}
r.deps.Sessions.BindAuthKeyForSession(rawAuthKeyID, sessionID, effective)
}
}
func (r *Router) effectiveUserID(ctx context.Context, rawAuthKeyID, authKeyID [8]byte, sessionID int64) (int64, bool, error) {
if userID, ok := UserIDFrom(ctx); ok {
if scoped, ok := r.scopedSessions(); ok {
scoped.BindUserForAuthKey(rawAuthKeyID, sessionID, userID)
} else if r.deps.Sessions != nil {
r.deps.Sessions.BindUser(sessionID, userID)
if r.deps.Sessions != nil {
r.deps.Sessions.BindUserForAuthKey(rawAuthKeyID, sessionID, userID)
}
return userID, true, nil
}
if r.deps.Sessions != nil {
if scoped, ok := r.deps.Sessions.(ScopedSessionBinder); ok {
if userID, resolved := scoped.UserIDResolvedForAuthKey(rawAuthKeyID, sessionID); resolved {
if userID == 0 {
if cachedUserID, ok := r.positiveCachedAuthUser(authKeyID); ok {
scoped.BindUserForAuthKey(rawAuthKeyID, sessionID, cachedUserID)
r.announceSessionOnline(ctx, cachedUserID)
return cachedUserID, true, nil
}
}
return userID, userID != 0, nil
}
} else if userID, resolved := r.deps.Sessions.UserIDResolved(sessionID); resolved {
if userID, resolved := r.deps.Sessions.UserIDResolvedForAuthKey(rawAuthKeyID, sessionID); resolved {
if userID == 0 {
if cachedUserID, ok := r.positiveCachedAuthUser(authKeyID); ok {
r.deps.Sessions.BindUser(sessionID, cachedUserID)
r.deps.Sessions.BindUserForAuthKey(rawAuthKeyID, sessionID, cachedUserID)
r.announceSessionOnline(ctx, cachedUserID)
return cachedUserID, true, nil
}
@ -434,32 +412,16 @@ func (r *Router) effectiveUserID(ctx context.Context, rawAuthKeyID, authKeyID [8
return 0, false, err
}
if r.deps.Sessions != nil {
if scoped, ok := r.deps.Sessions.(ScopedSessionBinder); ok {
if cachedUserID, resolved := scoped.UserIDResolvedForAuthKey(rawAuthKeyID, sessionID); resolved {
if cachedUserID != 0 || !found {
return cachedUserID, cachedUserID != 0, nil
}
}
} else {
if cachedUserID, resolved := r.deps.Sessions.UserIDResolved(sessionID); resolved {
if cachedUserID != 0 || !found {
return cachedUserID, cachedUserID != 0, nil
}
if cachedUserID, resolved := r.deps.Sessions.UserIDResolvedForAuthKey(rawAuthKeyID, sessionID); resolved {
if cachedUserID != 0 || !found {
return cachedUserID, cachedUserID != 0, nil
}
}
if found {
if scoped, ok := r.deps.Sessions.(ScopedSessionBinder); ok {
scoped.BindUserForAuthKey(rawAuthKeyID, sessionID, userID)
} else {
r.deps.Sessions.BindUser(sessionID, userID)
}
r.deps.Sessions.BindUserForAuthKey(rawAuthKeyID, sessionID, userID)
r.announceSessionOnline(ctx, userID)
} else {
if scoped, ok := r.deps.Sessions.(ScopedSessionBinder); ok {
scoped.BindUserForAuthKey(rawAuthKeyID, sessionID, 0)
} else {
r.deps.Sessions.BindUser(sessionID, 0)
}
r.deps.Sessions.BindUserForAuthKey(rawAuthKeyID, sessionID, 0)
}
}
return userID, found, nil
@ -532,14 +494,6 @@ func (r *Router) invalidateAuthUserCache(authKeyID [8]byte) {
r.authUserSF.Forget(authKeyClientInfoSingleflightPrefix + key)
}
func (r *Router) scopedSessions() (ScopedSessionBinder, bool) {
if r.deps.Sessions == nil {
return nil, false
}
scoped, ok := r.deps.Sessions.(ScopedSessionBinder)
return scoped, ok
}
func (r *Router) dispatch(ctx context.Context, b *bin.Buffer, depth int) (bin.Encoder, error) {
if depth > maxWrapperDepth {
return nil, wrapperTooDeepErr()

View file

@ -26,7 +26,7 @@ func newAuthBindingCaptureSessions() *authBindingCaptureSessions {
return &authBindingCaptureSessions{captureSessions: &captureSessions{}}
}
func (s *authBindingCaptureSessions) PushToUserExceptSession(_ context.Context, userID, _ int64, t proto.MessageType, msg bin.Encoder) (int, error) {
func (s *authBindingCaptureSessions) PushToUserExceptAuthKeySession(_ context.Context, userID int64, _ [8]byte, _ int64, t proto.MessageType, msg bin.Encoder) (int, error) {
s.mu.Lock()
defer s.mu.Unlock()
s.messageType = t
@ -43,8 +43,8 @@ func TestDispatchPromotesNegativeSessionCacheFromPositiveAuthCache(t *testing.T)
userID = int64(1000000001)
)
sessions := newAuthBindingCaptureSessions()
sessions.BindAuthKey(sessionID, authKeyID)
sessions.BindUser(sessionID, 0)
sessions.BindAuthKeyForSession(authKeyID, sessionID, authKeyID)
sessions.BindUserForAuthKey(authKeyID, sessionID, 0)
auth := &captureAuthService{}
r := New(Config{}, Deps{
Auth: auth,

View file

@ -9,6 +9,7 @@ import (
type captureSessions struct {
mu sync.Mutex
rawAuthKeyID [8]byte
sessionID int64
userID int64
userResolved bool
@ -81,39 +82,35 @@ func (s *captureSessions) clearMessages() {
s.pushUserIDs = nil
}
func (s *captureSessions) BindAuthKey(sessionID int64, authKeyID [8]byte) {
func (s *captureSessions) BindAuthKeyForSession(rawAuthKeyID [8]byte, sessionID int64, authKeyID [8]byte) {
s.mu.Lock()
defer s.mu.Unlock()
if s.authKeyResolved && s.authKeyID != authKeyID {
s.userID = 0
s.userResolved = false
}
s.rawAuthKeyID = rawAuthKeyID
s.sessionID = sessionID
s.authKeyID = authKeyID
s.authKeyResolved = true
}
func (s *captureSessions) AuthKeyID(int64) ([8]byte, bool) {
func (s *captureSessions) AuthKeyIDForSession([8]byte, int64) ([8]byte, bool) {
s.mu.Lock()
defer s.mu.Unlock()
return s.authKeyID, s.authKeyResolved
}
func (s *captureSessions) BindUser(sessionID, userID int64) {
func (s *captureSessions) BindUserForAuthKey(rawAuthKeyID [8]byte, sessionID, userID int64) {
s.mu.Lock()
defer s.mu.Unlock()
s.rawAuthKeyID = rawAuthKeyID
s.sessionID = sessionID
s.userID = userID
s.userResolved = true
}
func (s *captureSessions) UserID(int64) (int64, bool) {
s.mu.Lock()
defer s.mu.Unlock()
return s.userID, s.userID != 0
}
func (s *captureSessions) UserIDResolved(int64) (int64, bool) {
func (s *captureSessions) UserIDResolvedForAuthKey([8]byte, int64) (int64, bool) {
s.mu.Lock()
defer s.mu.Unlock()
return s.userID, s.userResolved
@ -130,26 +127,29 @@ func (s *captureSessions) UnbindAuthKey(authKeyID [8]byte) int {
return 0
}
func (s *captureSessions) SetReceivesUpdates(sessionID int64, receives bool) {
func (s *captureSessions) SetReceivesUpdatesForAuthKey(rawAuthKeyID [8]byte, sessionID int64, receives bool) {
s.mu.Lock()
defer s.mu.Unlock()
s.rawAuthKeyID = rawAuthKeyID
s.sessionID = sessionID
s.receives = receives
}
func (s *captureSessions) PushToSession(_ context.Context, sessionID int64, t proto.MessageType, msg bin.Encoder) error {
func (s *captureSessions) PushToSessionForAuthKey(_ context.Context, rawAuthKeyID [8]byte, sessionID int64, t proto.MessageType, msg bin.Encoder) error {
s.mu.Lock()
defer s.mu.Unlock()
s.rawAuthKeyID = rawAuthKeyID
s.sessionID = sessionID
s.messageType = t
s.message = msg
return nil
}
func (s *captureSessions) PushToUserExceptSession(_ context.Context, userID, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
func (s *captureSessions) PushToUserExceptAuthKeySession(_ context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg bin.Encoder) (int, error) {
s.mu.Lock()
defer s.mu.Unlock()
s.userID = userID
s.rawAuthKeyID = excludeAuthKeyID
s.sessionID = excludeSessionID
s.messageType = t
s.message = msg

View file

@ -192,13 +192,7 @@ func (r *Router) markSessionReceivesUpdates(ctx context.Context, userID int64) {
if !ok {
return
}
if scoped, ok := r.scopedSessions(); ok {
if rawAuthKeyID, ok := RawAuthKeyIDFrom(ctx); ok {
scoped.SetReceivesUpdatesForAuthKey(rawAuthKeyID, sessionID, true)
return
}
}
r.deps.Sessions.SetReceivesUpdates(sessionID, true)
r.deps.Sessions.SetReceivesUpdatesForAuthKey(rawAuthKeyIDForOrigin(ctx), sessionID, true)
}
func ptr[T any](v T) *T { return &v }