diff --git a/federationapi/internal/perform.go b/federationapi/internal/perform.go index 970f2fc39..28a2d8ba2 100644 --- a/federationapi/internal/perform.go +++ b/federationapi/internal/perform.go @@ -24,6 +24,11 @@ func (r *FederationInternalAPI) PerformDirectoryLookup( request *api.PerformDirectoryLookupRequest, response *api.PerformDirectoryLookupResponse, ) (err error) { + stats := r.statistics.ForServer(request.ServerName) + if stats.AssumedOffline() && len(stats.KnownMailservers()) > 0 { + return fmt.Errorf("not performing federation since server is assumed offline with known mailboxes") + } + dir, err := r.federation.LookupRoomAlias( ctx, request.ServerName, @@ -143,6 +148,11 @@ func (r *FederationInternalAPI) performJoinUsingServer( supportedVersions []gomatrixserverlib.RoomVersion, unsigned map[string]interface{}, ) error { + stats := r.statistics.ForServer(serverName) + if stats.AssumedOffline() && len(stats.KnownMailservers()) > 0 { + return fmt.Errorf("not performing federation since server is assumed offline with known mailboxes") + } + // Try to perform a make_join using the information supplied in the // request. respMakeJoin, err := r.federation.MakeJoin( @@ -398,6 +408,11 @@ func (r *FederationInternalAPI) performOutboundPeekUsingServer( serverName gomatrixserverlib.ServerName, supportedVersions []gomatrixserverlib.RoomVersion, ) error { + stats := r.statistics.ForServer(serverName) + if stats.AssumedOffline() && len(stats.KnownMailservers()) > 0 { + return fmt.Errorf("not performing federation since server is assumed offline with known mailboxes") + } + // create a unique ID for this peek. // for now we just use the room ID again. In future, if we ever // support concurrent peeks to the same room with different filters @@ -501,6 +516,11 @@ func (r *FederationInternalAPI) PerformLeave( // Try each server that we were provided until we land on one that // successfully completes the make-leave send-leave dance. for _, serverName := range request.ServerNames { + stats := r.statistics.ForServer(serverName) + if stats.AssumedOffline() && len(stats.KnownMailservers()) > 0 { + continue + } + // Try to perform a make_leave using the information supplied in the // request. respMakeLeave, err := r.federation.MakeLeave( @@ -594,6 +614,11 @@ func (r *FederationInternalAPI) PerformInvite( return fmt.Errorf("gomatrixserverlib.SplitID: %w", err) } + stats := r.statistics.ForServer(destination) + if stats.AssumedOffline() && len(stats.KnownMailservers()) > 0 { + return fmt.Errorf("not performing federation since server is assumed offline with known mailboxes") + } + logrus.WithFields(logrus.Fields{ "event_id": request.Event.EventID(), "user_id": *request.Event.StateKey(), diff --git a/federationapi/statistics/statistics.go b/federationapi/statistics/statistics.go index 76f9091b4..1bf956f18 100644 --- a/federationapi/statistics/statistics.go +++ b/federationapi/statistics/statistics.go @@ -65,8 +65,9 @@ func (s *Statistics) ForServer(serverName gomatrixserverlib.ServerName) *ServerS if !found { s.mutex.Lock() server = &ServerStatistics{ - statistics: s, - serverName: serverName, + statistics: s, + serverName: serverName, + knownMailservers: []gomatrixserverlib.ServerName{}, } s.servers[serverName] = server s.mutex.Unlock() @@ -85,16 +86,17 @@ func (s *Statistics) ForServer(serverName gomatrixserverlib.ServerName) *ServerS // many times we failed etc. It also manages the backoff time and black- // listing a remote host if it remains uncooperative. type ServerStatistics struct { - statistics *Statistics // - serverName gomatrixserverlib.ServerName // - blacklisted atomic.Bool // is the node blacklisted - assumedOffline atomic.Bool // is the node assumed to be offline - backoffStarted atomic.Bool // is the backoff started - backoffUntil atomic.Value // time.Time until this backoff interval ends - backoffCount atomic.Uint32 // number of times BackoffDuration has been called - successCounter atomic.Uint32 // how many times have we succeeded? - backoffNotifier func() // notifies destination queue when backoff completes - notifierMutex sync.Mutex + statistics *Statistics // + serverName gomatrixserverlib.ServerName // + blacklisted atomic.Bool // is the node blacklisted + assumedOffline atomic.Bool // is the node assumed to be offline + backoffStarted atomic.Bool // is the backoff started + backoffUntil atomic.Value // time.Time until this backoff interval ends + backoffCount atomic.Uint32 // number of times BackoffDuration has been called + successCounter atomic.Uint32 // how many times have we succeeded? + backoffNotifier func() // notifies destination queue when backoff completes + notifierMutex sync.Mutex + knownMailservers []gomatrixserverlib.ServerName } const maxJitterMultiplier = 1.4 @@ -245,3 +247,7 @@ func (s *ServerStatistics) RemoveBlacklist() { func (s *ServerStatistics) SuccessCount() uint32 { return s.successCounter.Load() } + +func (s *ServerStatistics) KnownMailservers() []gomatrixserverlib.ServerName { + return s.knownMailservers +}