mirror of
https://github.com/hoernschen/dendrite.git
synced 2025-07-31 21:32:46 +00:00
Synchronous invites (#1273)
* Refactor invites to be synchronous * Fix synchronous invites * Fix client API return type for send invite error * Linter * Restore PerformError on rsAPI.PerformInvite * Update sytest-whitelist * Don't override PerformError with normal errors * Fix error passing * Un-whitelist a couple of tests * Update sytest-whitelist * Try to handle multiple invite rejections better * nolint * Update gomatrixserverlib * Fix /v1/invite test * Remove replace from go.mod
This commit is contained in:
parent
6820b3e024
commit
6cb1a65809
23 changed files with 334 additions and 443 deletions
|
@ -46,20 +46,18 @@ type destinationQueue struct {
|
|||
db storage.Database
|
||||
signing *SigningInfo
|
||||
rsAPI api.RoomserverInternalAPI
|
||||
client *gomatrixserverlib.FederationClient // federation client
|
||||
origin gomatrixserverlib.ServerName // origin of requests
|
||||
destination gomatrixserverlib.ServerName // destination of requests
|
||||
running atomic.Bool // is the queue worker running?
|
||||
backingOff atomic.Bool // true if we're backing off
|
||||
statistics *statistics.ServerStatistics // statistics about this remote server
|
||||
incomingInvites chan *gomatrixserverlib.InviteV2Request // invites to send
|
||||
transactionIDMutex sync.Mutex // protects transactionID
|
||||
transactionID gomatrixserverlib.TransactionID // last transaction ID
|
||||
transactionCount atomic.Int32 // how many events in this transaction so far
|
||||
pendingInvites []*gomatrixserverlib.InviteV2Request // owned by backgroundSend
|
||||
notifyPDUs chan bool // interrupts idle wait for PDUs
|
||||
notifyEDUs chan bool // interrupts idle wait for EDUs
|
||||
interruptBackoff chan bool // interrupts backoff
|
||||
client *gomatrixserverlib.FederationClient // federation client
|
||||
origin gomatrixserverlib.ServerName // origin of requests
|
||||
destination gomatrixserverlib.ServerName // destination of requests
|
||||
running atomic.Bool // is the queue worker running?
|
||||
backingOff atomic.Bool // true if we're backing off
|
||||
statistics *statistics.ServerStatistics // statistics about this remote server
|
||||
transactionIDMutex sync.Mutex // protects transactionID
|
||||
transactionID gomatrixserverlib.TransactionID // last transaction ID
|
||||
transactionCount atomic.Int32 // how many events in this transaction so far
|
||||
notifyPDUs chan bool // interrupts idle wait for PDUs
|
||||
notifyEDUs chan bool // interrupts idle wait for EDUs
|
||||
interruptBackoff chan bool // interrupts backoff
|
||||
}
|
||||
|
||||
// Send event adds the event to the pending queue for the destination.
|
||||
|
@ -138,18 +136,6 @@ func (oq *destinationQueue) sendEDU(receipt *shared.Receipt) {
|
|||
}
|
||||
}
|
||||
|
||||
// sendInvite adds the invite event to the pending queue for the
|
||||
// destination. If the queue is empty then it starts a background
|
||||
// goroutine to start sending events to that destination.
|
||||
func (oq *destinationQueue) sendInvite(ev *gomatrixserverlib.InviteV2Request) {
|
||||
if oq.statistics.Blacklisted() {
|
||||
// If the destination is blacklisted then drop the event.
|
||||
return
|
||||
}
|
||||
oq.wakeQueueIfNeeded()
|
||||
oq.incomingInvites <- ev
|
||||
}
|
||||
|
||||
// wakeQueueIfNeeded will wake up the destination queue if it is
|
||||
// not already running. If it is running but it is backing off
|
||||
// then we will interrupt the backoff, causing any federation
|
||||
|
@ -234,23 +220,6 @@ func (oq *destinationQueue) backgroundSend() {
|
|||
// We were woken up because there are new PDUs waiting in the
|
||||
// database.
|
||||
pendingEDUs = true
|
||||
case invite := <-oq.incomingInvites:
|
||||
// There's no strict ordering requirement for invites like
|
||||
// there is for transactions, so we put the invite onto the
|
||||
// front of the queue. This means that if an invite that is
|
||||
// stuck failing already, that it won't block our new invite
|
||||
// from being sent.
|
||||
oq.pendingInvites = append(
|
||||
[]*gomatrixserverlib.InviteV2Request{invite},
|
||||
oq.pendingInvites...,
|
||||
)
|
||||
// If there are any more things waiting in the channel queue
|
||||
// then read them. This is safe because we guarantee only
|
||||
// having one goroutine per destination queue, so the channel
|
||||
// isn't being consumed anywhere else.
|
||||
for len(oq.incomingInvites) > 0 {
|
||||
oq.pendingInvites = append(oq.pendingInvites, <-oq.incomingInvites)
|
||||
}
|
||||
case <-time.After(queueIdleTimeout):
|
||||
// The worker is idle so stop the goroutine. It'll get
|
||||
// restarted automatically the next time we have an event to
|
||||
|
@ -266,7 +235,6 @@ func (oq *destinationQueue) backgroundSend() {
|
|||
// It's been suggested that we should give up because the backoff
|
||||
// has exceeded a maximum allowable value. Clean up the in-memory
|
||||
// buffers at this point. The PDU clean-up is already on a defer.
|
||||
oq.cleanPendingInvites()
|
||||
log.Warnf("Blacklisting %q due to exceeding backoff threshold", oq.destination)
|
||||
return
|
||||
}
|
||||
|
@ -284,35 +252,9 @@ func (oq *destinationQueue) backgroundSend() {
|
|||
oq.statistics.Success()
|
||||
}
|
||||
}
|
||||
|
||||
// Try sending the next invite and see what happens.
|
||||
if len(oq.pendingInvites) > 0 {
|
||||
sent, ierr := oq.nextInvites(oq.pendingInvites)
|
||||
if ierr != nil {
|
||||
// We failed to send the transaction. Mark it as a failure.
|
||||
oq.statistics.Failure()
|
||||
} else if sent > 0 {
|
||||
// If we successfully sent the invites then clear out
|
||||
// the pending invites.
|
||||
oq.statistics.Success()
|
||||
// Reallocate so that the underlying array can be GC'd, as
|
||||
// opposed to growing forever.
|
||||
oq.cleanPendingInvites()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cleanPendingInvites cleans out the pending invite buffer,
|
||||
// removing all references so that the underlying objects can
|
||||
// be GC'd.
|
||||
func (oq *destinationQueue) cleanPendingInvites() {
|
||||
for i := 0; i < len(oq.pendingInvites); i++ {
|
||||
oq.pendingInvites[i] = nil
|
||||
}
|
||||
oq.pendingInvites = []*gomatrixserverlib.InviteV2Request{}
|
||||
}
|
||||
|
||||
// nextTransaction creates a new transaction from the pending event
|
||||
// queue and sends it. Returns true if a transaction was sent or
|
||||
// false otherwise.
|
||||
|
@ -427,66 +369,3 @@ func (oq *destinationQueue) nextTransaction() (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// nextInvite takes pending invite events from the queue and sends
|
||||
// them. Returns true if a transaction was sent or false otherwise.
|
||||
func (oq *destinationQueue) nextInvites(
|
||||
pendingInvites []*gomatrixserverlib.InviteV2Request,
|
||||
) (int, error) {
|
||||
done := 0
|
||||
for _, inviteReq := range pendingInvites {
|
||||
ev, roomVersion := inviteReq.Event(), inviteReq.RoomVersion()
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"event_id": ev.EventID(),
|
||||
"room_version": roomVersion,
|
||||
"destination": oq.destination,
|
||||
}).Info("sending invite")
|
||||
|
||||
inviteRes, err := oq.client.SendInviteV2(
|
||||
context.TODO(),
|
||||
oq.destination,
|
||||
*inviteReq,
|
||||
)
|
||||
switch e := err.(type) {
|
||||
case nil:
|
||||
done++
|
||||
case gomatrix.HTTPError:
|
||||
log.WithFields(log.Fields{
|
||||
"event_id": ev.EventID(),
|
||||
"state_key": ev.StateKey(),
|
||||
"destination": oq.destination,
|
||||
"status_code": e.Code,
|
||||
}).WithError(err).Error("failed to send invite due to HTTP error")
|
||||
// Check whether we should do something about the error or
|
||||
// just accept it as unavoidable.
|
||||
if e.Code >= 400 && e.Code <= 499 {
|
||||
// We tried but the remote side has sent back a client error.
|
||||
// It's no use retrying because it will happen again.
|
||||
done++
|
||||
continue
|
||||
}
|
||||
return done, err
|
||||
default:
|
||||
log.WithFields(log.Fields{
|
||||
"event_id": ev.EventID(),
|
||||
"state_key": ev.StateKey(),
|
||||
"destination": oq.destination,
|
||||
}).WithError(err).Error("failed to send invite")
|
||||
return done, err
|
||||
}
|
||||
|
||||
invEv := inviteRes.Event.Sign(string(oq.signing.ServerName), oq.signing.KeyID, oq.signing.PrivateKey).Headered(roomVersion)
|
||||
_, err = api.SendEvents(context.TODO(), oq.rsAPI, []gomatrixserverlib.HeaderedEvent{invEv}, oq.signing.ServerName, nil)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"event_id": ev.EventID(),
|
||||
"state_key": ev.StateKey(),
|
||||
"destination": oq.destination,
|
||||
}).WithError(err).Error("failed to return signed invite to roomserver")
|
||||
return done, err
|
||||
}
|
||||
}
|
||||
|
||||
return done, nil
|
||||
}
|
||||
|
|
|
@ -108,7 +108,6 @@ func (oqs *OutgoingQueues) getQueue(destination gomatrixserverlib.ServerName) *d
|
|||
destination: destination,
|
||||
client: oqs.client,
|
||||
statistics: oqs.statistics.ForServer(destination),
|
||||
incomingInvites: make(chan *gomatrixserverlib.InviteV2Request, 128),
|
||||
notifyPDUs: make(chan bool, 1),
|
||||
notifyEDUs: make(chan bool, 1),
|
||||
interruptBackoff: make(chan bool),
|
||||
|
@ -178,51 +177,6 @@ func (oqs *OutgoingQueues) SendEvent(
|
|||
return nil
|
||||
}
|
||||
|
||||
// SendEvent sends an event to the destinations
|
||||
func (oqs *OutgoingQueues) SendInvite(
|
||||
inviteReq *gomatrixserverlib.InviteV2Request,
|
||||
) error {
|
||||
ev := inviteReq.Event()
|
||||
stateKey := ev.StateKey()
|
||||
if stateKey == nil {
|
||||
log.WithFields(log.Fields{
|
||||
"event_id": ev.EventID(),
|
||||
}).Info("Invite had no state key, dropping")
|
||||
return nil
|
||||
}
|
||||
|
||||
_, destination, err := gomatrixserverlib.SplitID('@', *stateKey)
|
||||
if err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"event_id": ev.EventID(),
|
||||
"state_key": stateKey,
|
||||
}).Info("Failed to split destination from state key")
|
||||
return nil
|
||||
}
|
||||
|
||||
if stateapi.IsServerBannedFromRoom(
|
||||
context.TODO(),
|
||||
oqs.stateAPI,
|
||||
ev.RoomID(),
|
||||
destination,
|
||||
) {
|
||||
log.WithFields(log.Fields{
|
||||
"room_id": ev.RoomID(),
|
||||
"destination": destination,
|
||||
}).Info("Dropping invite to server which is prohibited by ACLs")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"event_id": ev.EventID(),
|
||||
"server_name": destination,
|
||||
}).Info("Sending invite")
|
||||
|
||||
oqs.getQueue(destination).sendInvite(inviteReq)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendEDU sends an EDU event to the destinations.
|
||||
func (oqs *OutgoingQueues) SendEDU(
|
||||
e *gomatrixserverlib.EDU, origin gomatrixserverlib.ServerName,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue