diff --git a/server/websocket.go b/server/websocket.go index 9267bc9ff..6ab74faba 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -598,7 +598,15 @@ func (p *Plugin) wsReader(us *session, authSessionID, handlerID string) { } s, appErr := p.API.GetSession(authSessionID) - if appErr != nil || s == nil || (s.ExpiresAt != 0 && time.Now().UnixMilli() >= s.ExpiresAt) { + if appErr != nil { + // A lookup error (e.g. transient DB failure during a pod roll) is not + // the same as a definitively revoked or expired session. Skip this tick + // and retry on the next interval rather than force-disconnecting. + p.LogWarn("failed to get session, will retry", "channelID", us.channelID, "userID", us.userID, "connID", us.connID, "err", appErr.Error()) + continue + } + + if s == nil || (s.ExpiresAt != 0 && time.Now().UnixMilli() >= s.ExpiresAt) { fields := []any{ "channelID", us.channelID, @@ -608,10 +616,8 @@ func (p *Plugin) wsReader(us *session, authSessionID, handlerID string) { us.connID, } - if appErr == nil && s == nil { - p.LogWarn("no appErr and no session found", fields...) - } else if appErr != nil { - fields = append(fields, "err", appErr.Error()) + if s == nil { + p.LogWarn("no session found", fields...) } else { fields = append(fields, "sessionID", s.Id, "expiresAt", fmt.Sprintf("%d", s.ExpiresAt)) } diff --git a/server/websocket_test.go b/server/websocket_test.go index 6bddcc79a..f51cddea8 100644 --- a/server/websocket_test.go +++ b/server/websocket_test.go @@ -348,7 +348,9 @@ func TestWSReader(t *testing.T) { wg.Wait() }) - t.Run("revoked session", func(_ *testing.T) { + // A transient GetSession error (e.g. DB blip during a pod roll) must not + // force-disconnect the session; the check is retried on the next tick. + t.Run("transient session lookup error", func(_ *testing.T) { defer mockAPI.AssertExpectations(t) us := newUserSession("userID", "channelID", "connID", "callID", false) @@ -356,15 +358,11 @@ func TestWSReader(t *testing.T) { mockAPI.On("GetSession", "authSessionID").Return(nil, model.NewAppError("GetSessionById", "We encountered an error finding the session.", nil, "", http.StatusUnauthorized)).Once() - mockAPI.On("LogInfo", "invalid or expired session, closing RTC session", + mockAPI.On("LogWarn", "failed to get session, will retry", "origin", mock.AnythingOfType("string"), "channelID", us.channelID, "userID", us.userID, "connID", us.connID, "err", "GetSessionById: We encountered an error finding the session.").Once() - mockAPI.On("LogDebug", "closeRTCSession", - "origin", mock.AnythingOfType("string"), - "userID", us.userID, "connID", us.connID, "channelID", us.channelID).Once() - var wg sync.WaitGroup wg.Add(1) go func() { @@ -372,7 +370,9 @@ func TestWSReader(t *testing.T) { p.wsReader(us, "authSessionID", "handlerID") }() - time.Sleep(time.Second * 2) + // Sleep long enough for one tick to fire (1s interval), then close + // before the second tick so no second GetSession call is made. + time.Sleep(1200 * time.Millisecond) close(us.wsCloseCh) wg.Wait() @@ -386,7 +386,7 @@ func TestWSReader(t *testing.T) { mockAPI.On("GetSession", "authSessionID").Return(nil, nil).Once() - mockAPI.On("LogWarn", "no appErr and no session found", + mockAPI.On("LogWarn", "no session found", "origin", mock.AnythingOfType("string"), "channelID", us.channelID, "userID", us.userID, "connID", us.connID)