Revert "Durable transactions, some more refactoring"

This reverts commit 5daf924eaa.
This commit is contained in:
Neil Alexander 2020-12-08 15:55:01 +00:00
parent 5daf924eaa
commit 029f9ce522
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
10 changed files with 240 additions and 345 deletions

View file

@ -33,6 +33,10 @@ import (
) )
const ( const (
maxPDUsPerTransaction = 50
maxEDUsPerTransaction = 50
maxPDUsInMemory = 128
maxEDUsInMemory = 128
queueIdleTimeout = time.Second * 30 queueIdleTimeout = time.Second * 30
) )
@ -50,14 +54,13 @@ type destinationQueue struct {
running atomic.Bool // is the queue worker running? running atomic.Bool // is the queue worker running?
backingOff atomic.Bool // true if we're backing off backingOff atomic.Bool // true if we're backing off
overflowed atomic.Bool // the queues exceed maxPDUsInMemory/maxEDUsInMemory, so we should consult the database for more overflowed atomic.Bool // the queues exceed maxPDUsInMemory/maxEDUsInMemory, so we should consult the database for more
transactionID gomatrixserverlib.TransactionID // the ID to commit to the database with
transactionIDMutex sync.RWMutex // protects transactionID
transactionPDUCount atomic.Int32 // how many PDUs in database transaction?
transactionEDUCount atomic.Int32 // how many EDUs in database transaction?
statistics *statistics.ServerStatistics // statistics about this remote server statistics *statistics.ServerStatistics // statistics about this remote server
transactionIDMutex sync.Mutex // protects transactionID
transactionID gomatrixserverlib.TransactionID // last transaction ID if retrying, or "" if last txn was successful
notify chan struct{} // interrupts idle wait pending PDUs/EDUs notify chan struct{} // interrupts idle wait pending PDUs/EDUs
pendingTransactions queuedTransactions // transactions waiting to be sent pendingPDUs []*queuedPDU // PDUs waiting to be sent
pendingMutex sync.RWMutex // protects pendingTransactions pendingEDUs []*queuedEDU // EDUs waiting to be sent
pendingMutex sync.RWMutex // protects pendingPDUs and pendingEDUs
interruptBackoff chan bool // interrupts backoff interruptBackoff chan bool // interrupts backoff
} }
@ -69,56 +72,40 @@ func (oq *destinationQueue) sendEvent(event *gomatrixserverlib.HeaderedEvent, re
log.Errorf("attempt to send nil PDU with destination %q", oq.destination) log.Errorf("attempt to send nil PDU with destination %q", oq.destination)
return return
} }
// Try to queue the PDU up in memory. If there was enough free
// space then we'll get a transaction ID back.
oq.pendingMutex.Lock()
transactionID := oq.pendingTransactions.queuePDUs(receipt, event)
oq.pendingMutex.Unlock()
// Check if we got a transaction ID back.
if transactionID == "" {
// If we hit this point then we weren't able to fit the event
// into the memory cache, therefore we need to generate a new
// transaction ID to commit to the database. If we don't have
// a transaction ID for the database, or we've exceeded the
// number of PDUs we can fit in the last one, generate a new
// one.
oq.transactionIDMutex.Lock()
if oq.transactionID == "" || oq.transactionPDUCount.Load() > maxPDUsPerTransaction {
now := gomatrixserverlib.AsTimestamp(time.Now())
oq.transactionID = gomatrixserverlib.TransactionID(fmt.Sprintf("%d-%d", now, oq.statistics.SuccessCount()))
oq.transactionPDUCount.Store(0)
oq.transactionEDUCount.Store(0)
transactionID = oq.transactionID
}
oq.transactionIDMutex.Unlock()
oq.overflowed.Store(true)
}
// Create a database entry that associates the given PDU NID with // Create a database entry that associates the given PDU NID with
// this destination queue. We'll then be able to retrieve the PDU // this destination queue. We'll then be able to retrieve the PDU
// later. // later.
if err := oq.db.AssociatePDUWithDestination( if err := oq.db.AssociatePDUWithDestination(
context.TODO(), context.TODO(),
transactionID, // the transaction ID "", // TODO: remove this, as we don't need to persist the transaction ID
oq.destination, // the destination server name oq.destination, // the destination server name
receipt, // NIDs from federationsender_queue_json table receipt, // NIDs from federationsender_queue_json table
); err != nil { ); err != nil {
log.WithError(err).Errorf("failed to associate PDU %q with destination %q", event.EventID(), oq.destination) log.WithError(err).Errorf("failed to associate PDU %q with destination %q", event.EventID(), oq.destination)
return return
} }
// Check if the destination is blacklisted. If it isn't then wake
// We've successfully added a PDU to the transaction so increase // up the queue.
// the counter. if !oq.statistics.Blacklisted() {
oq.transactionPDUCount.Add(1) // If there's room in memory to hold the event then add it to the
// list.
// Wake up the queue. oq.pendingMutex.Lock()
if len(oq.pendingPDUs) < maxPDUsInMemory {
oq.pendingPDUs = append(oq.pendingPDUs, &queuedPDU{
pdu: event,
receipt: receipt,
})
} else {
oq.overflowed.Store(true)
}
oq.pendingMutex.Unlock()
// Wake up the queue if it's asleep.
oq.wakeQueueIfNeeded() oq.wakeQueueIfNeeded()
select { select {
case oq.notify <- struct{}{}: case oq.notify <- struct{}{}:
default: default:
} }
}
} }
// sendEDU adds the EDU event to the pending queue for the destination. // sendEDU adds the EDU event to the pending queue for the destination.
@ -129,58 +116,39 @@ func (oq *destinationQueue) sendEDU(event *gomatrixserverlib.EDU, receipt *share
log.Errorf("attempt to send nil EDU with destination %q", oq.destination) log.Errorf("attempt to send nil EDU with destination %q", oq.destination)
return return
} }
// Create a database entry that associates the given PDU NID with
// Try to queue the PDU up in memory. If there was enough free // this destination queue. We'll then be able to retrieve the PDU
// space then we'll get a transaction ID back.
oq.pendingMutex.Lock()
transactionID := oq.pendingTransactions.queueEDUs(receipt, event)
oq.pendingMutex.Unlock()
// Check if we got a transaction ID back.
if transactionID == "" {
// If we hit this point then we weren't able to fit the event
// into the memory cache, therefore we need to generate a new
// transaction ID to commit to the database. If we don't have
// a transaction ID for the database, or we've exceeded the
// number of PDUs we can fit in the last one, generate a new
// one.
/*
oq.transactionIDMutex.Lock()
if oq.transactionID == "" || oq.transactionPDUCount.Load() > maxPDUsPerTransaction {
now := gomatrixserverlib.AsTimestamp(time.Now())
oq.transactionID = gomatrixserverlib.TransactionID(fmt.Sprintf("%d-%d", now, oq.statistics.SuccessCount()))
oq.transactionPDUCount.Store(0)
oq.transactionEDUCount.Store(0)
transactionID = oq.transactionID
}
oq.transactionIDMutex.Unlock()
*/
oq.overflowed.Store(true)
}
// Create a database entry that associates the given EDU NID with
// this destination queue. We'll then be able to retrieve the EDU
// later. // later.
if err := oq.db.AssociateEDUWithDestination( if err := oq.db.AssociateEDUWithDestination(
context.TODO(), context.TODO(),
//transactionID, // the transaction ID
oq.destination, // the destination server name oq.destination, // the destination server name
receipt, // NIDs from federationsender_queue_json table receipt, // NIDs from federationsender_queue_json table
); err != nil { ); err != nil {
log.WithError(err).Errorf("failed to associate EDU with destination %q", oq.destination) log.WithError(err).Errorf("failed to associate EDU with destination %q", oq.destination)
return return
} }
// Check if the destination is blacklisted. If it isn't then wake
// We've successfully added a PDU to the transaction so increase // up the queue.
// the counter. if !oq.statistics.Blacklisted() {
oq.transactionEDUCount.Add(1) // If there's room in memory to hold the event then add it to the
// list.
// Wake up the queue. oq.pendingMutex.Lock()
if len(oq.pendingEDUs) < maxEDUsInMemory {
oq.pendingEDUs = append(oq.pendingEDUs, &queuedEDU{
edu: event,
receipt: receipt,
})
} else {
oq.overflowed.Store(true)
}
oq.pendingMutex.Unlock()
// Wake up the queue if it's asleep.
oq.wakeQueueIfNeeded() oq.wakeQueueIfNeeded()
select { select {
case oq.notify <- struct{}{}: case oq.notify <- struct{}{}:
default: default:
} }
}
} }
// wakeQueueIfNeeded will wake up the destination queue if it is // wakeQueueIfNeeded will wake up the destination queue if it is
@ -199,33 +167,66 @@ func (oq *destinationQueue) wakeQueueIfNeeded() {
} }
} }
// getNextTransactionFromDatabase will look at the database and see // getPendingFromDatabase will look at the database and see if
// if there are any persisted events that haven't been sent to this // there are any persisted events that haven't been sent to this
// destination yet. If so, they will be queued up. // destination yet. If so, they will be queued up.
// nolint:gocyclo // nolint:gocyclo
func (oq *destinationQueue) getNextTransactionFromDatabase() { func (oq *destinationQueue) getPendingFromDatabase() {
// Check to see if there's anything to do for this server // Check to see if there's anything to do for this server
// in the database. // in the database.
retrieved := false
ctx := context.Background() ctx := context.Background()
oq.pendingMutex.Lock() oq.pendingMutex.Lock()
defer oq.pendingMutex.Unlock() defer oq.pendingMutex.Unlock()
transactionID, pdus, pduReceipt, err := oq.db.GetNextTransactionPDUs(ctx, oq.destination, maxPDUsPerTransaction) // Take a note of all of the PDUs and EDUs that we already
if err != nil { // have cached. We will index them based on the receipt,
// which ultimately just contains the index of the PDU/EDU
// in the database.
gotPDUs := map[string]struct{}{}
gotEDUs := map[string]struct{}{}
for _, pdu := range oq.pendingPDUs {
gotPDUs[pdu.receipt.String()] = struct{}{}
}
for _, edu := range oq.pendingEDUs {
gotEDUs[edu.receipt.String()] = struct{}{}
}
if pduCapacity := maxPDUsInMemory - len(oq.pendingPDUs); pduCapacity > 0 {
// We have room in memory for some PDUs - let's request no more than that.
if pdus, err := oq.db.GetPendingPDUs(ctx, oq.destination, pduCapacity); err == nil {
for receipt, pdu := range pdus {
if _, ok := gotPDUs[receipt.String()]; ok {
continue
}
oq.pendingPDUs = append(oq.pendingPDUs, &queuedPDU{receipt, pdu})
retrieved = true
}
} else {
logrus.WithError(err).Errorf("Failed to get pending PDUs for %q", oq.destination) logrus.WithError(err).Errorf("Failed to get pending PDUs for %q", oq.destination)
} }
}
edus, eduReceipt, err := oq.db.GetNextTransactionEDUs(ctx, oq.destination, maxEDUsPerTransaction) if eduCapacity := maxEDUsInMemory - len(oq.pendingEDUs); eduCapacity > 0 {
if err != nil { // We have room in memory for some EDUs - let's request no more than that.
if edus, err := oq.db.GetPendingEDUs(ctx, oq.destination, eduCapacity); err == nil {
for receipt, edu := range edus {
if _, ok := gotEDUs[receipt.String()]; ok {
continue
}
oq.pendingEDUs = append(oq.pendingEDUs, &queuedEDU{receipt, edu})
retrieved = true
}
} else {
logrus.WithError(err).Errorf("Failed to get pending EDUs for %q", oq.destination) logrus.WithError(err).Errorf("Failed to get pending EDUs for %q", oq.destination)
} }
}
oq.pendingTransactions.createNew(transactionID) // If we've retrieved all of the events from the database with room to spare
oq.pendingTransactions.queuePDUs(pduReceipt, pdus...) // in memory then we'll no longer consider this queue to be overflowed.
oq.pendingTransactions.queueEDUs(eduReceipt, edus...) if len(oq.pendingPDUs) < maxPDUsInMemory && len(oq.pendingEDUs) < maxEDUsInMemory {
oq.overflowed.Store(false)
}
// If we've retrieved some events then notify the destination queue goroutine. // If we've retrieved some events then notify the destination queue goroutine.
if len(pdus) > 0 || len(edus) > 0 { if retrieved {
select { select {
case oq.notify <- struct{}{}: case oq.notify <- struct{}{}:
default: default:
@ -251,7 +252,7 @@ func (oq *destinationQueue) backgroundSend() {
// If we are overflowing memory and have sent things out to the // If we are overflowing memory and have sent things out to the
// database then we can look up what those things are. // database then we can look up what those things are.
if oq.overflowed.Load() { if oq.overflowed.Load() {
oq.getNextTransactionFromDatabase() oq.getPendingFromDatabase()
} }
// If we have nothing to do then wait either for incoming events, or // If we have nothing to do then wait either for incoming events, or
@ -278,10 +279,14 @@ func (oq *destinationQueue) backgroundSend() {
// buffers at this point. The PDU clean-up is already on a defer. // buffers at this point. The PDU clean-up is already on a defer.
log.Warnf("Blacklisting %q due to exceeding backoff threshold", oq.destination) log.Warnf("Blacklisting %q due to exceeding backoff threshold", oq.destination)
oq.pendingMutex.Lock() oq.pendingMutex.Lock()
for i := range oq.pendingTransactions.queue { for i := range oq.pendingPDUs {
oq.pendingTransactions.queue[i] = nil oq.pendingPDUs[i] = nil
} }
oq.pendingTransactions.queue = nil for i := range oq.pendingEDUs {
oq.pendingEDUs[i] = nil
}
oq.pendingPDUs = nil
oq.pendingEDUs = nil
oq.pendingMutex.Lock() oq.pendingMutex.Lock()
return return
} }
@ -298,15 +303,21 @@ func (oq *destinationQueue) backgroundSend() {
// Work out which PDUs/EDUs to include in the next transaction. // Work out which PDUs/EDUs to include in the next transaction.
oq.pendingMutex.RLock() oq.pendingMutex.RLock()
if len(oq.pendingTransactions.queue) == 0 { pduCount := len(oq.pendingPDUs)
continue eduCount := len(oq.pendingEDUs)
if pduCount > maxPDUsPerTransaction {
pduCount = maxPDUsPerTransaction
} }
next := oq.pendingTransactions.queue[0] if eduCount > maxEDUsPerTransaction {
eduCount = maxEDUsPerTransaction
}
toSendPDUs := oq.pendingPDUs[:pduCount]
toSendEDUs := oq.pendingEDUs[:eduCount]
oq.pendingMutex.RUnlock() oq.pendingMutex.RUnlock()
// If we have pending PDUs or EDUs then construct a transaction. // If we have pending PDUs or EDUs then construct a transaction.
// Try sending the next transaction and see what happens. // Try sending the next transaction and see what happens.
transaction, _, _, terr := oq.nextTransaction(next) transaction, pc, ec, terr := oq.nextTransaction(toSendPDUs, toSendEDUs)
if terr != nil { if terr != nil {
// We failed to send the transaction. Mark it as a failure. // We failed to send the transaction. Mark it as a failure.
oq.statistics.Failure() oq.statistics.Failure()
@ -316,13 +327,14 @@ func (oq *destinationQueue) backgroundSend() {
// the pending events and EDUs, and wipe our transaction ID. // the pending events and EDUs, and wipe our transaction ID.
oq.statistics.Success() oq.statistics.Success()
oq.pendingMutex.Lock() oq.pendingMutex.Lock()
for i := range next.pdus { for i := range oq.pendingPDUs[:pc] {
next.pdus[i] = nil oq.pendingPDUs[i] = nil
} }
for i := range next.edus { for i := range oq.pendingEDUs[:ec] {
next.edus[i] = nil oq.pendingEDUs[i] = nil
} }
oq.pendingTransactions.queue = oq.pendingTransactions.queue[1:] oq.pendingPDUs = oq.pendingPDUs[pc:]
oq.pendingEDUs = oq.pendingEDUs[ec:]
oq.pendingMutex.Unlock() oq.pendingMutex.Unlock()
} }
} }
@ -332,7 +344,10 @@ func (oq *destinationQueue) backgroundSend() {
// queue and sends it. Returns true if a transaction was sent or // queue and sends it. Returns true if a transaction was sent or
// false otherwise. // false otherwise.
// nolint:gocyclo // nolint:gocyclo
func (oq *destinationQueue) nextTransaction(transaction *queuedTransaction) (bool, int, int, error) { func (oq *destinationQueue) nextTransaction(
pdus []*queuedPDU,
edus []*queuedEDU,
) (bool, int, int, error) {
// If there's no projected transaction ID then generate one. If // If there's no projected transaction ID then generate one. If
// the transaction succeeds then we'll set it back to "" so that // the transaction succeeds then we'll set it back to "" so that
// we generate a new one next time. If it fails, we'll preserve // we generate a new one next time. If it fails, we'll preserve
@ -356,27 +371,32 @@ func (oq *destinationQueue) nextTransaction(transaction *queuedTransaction) (boo
// If we didn't get anything from the database and there are no // If we didn't get anything from the database and there are no
// pending EDUs then there's nothing to do - stop here. // pending EDUs then there's nothing to do - stop here.
if len(transaction.pdus) == 0 && len(transaction.edus) == 0 { if len(pdus) == 0 && len(edus) == 0 {
return false, 0, 0, nil return false, 0, 0, nil
} }
var pduReceipts []*shared.Receipt
var eduReceipts []*shared.Receipt
// Go through PDUs that we retrieved from the database, if any, // Go through PDUs that we retrieved from the database, if any,
// and add them into the transaction. // and add them into the transaction.
for _, pdu := range transaction.pdus { for _, pdu := range pdus {
if pdu == nil { if pdu == nil || pdu.pdu == nil {
continue continue
} }
// Append the JSON of the event, since this is a json.RawMessage type in the // Append the JSON of the event, since this is a json.RawMessage type in the
// gomatrixserverlib.Transaction struct // gomatrixserverlib.Transaction struct
t.PDUs = append(t.PDUs, pdu.JSON()) t.PDUs = append(t.PDUs, pdu.pdu.JSON())
pduReceipts = append(pduReceipts, pdu.receipt)
} }
// Do the same for pending EDUS in the queue. // Do the same for pending EDUS in the queue.
for _, edu := range transaction.edus { for _, edu := range edus {
if edu == nil { if edu == nil || edu.edu == nil {
continue continue
} }
t.EDUs = append(t.EDUs, *edu) t.EDUs = append(t.EDUs, *edu.edu)
eduReceipts = append(eduReceipts, edu.receipt)
} }
logrus.WithField("server_name", oq.destination).Debugf("Sending transaction %q containing %d PDUs, %d EDUs", t.TransactionID, len(t.PDUs), len(t.EDUs)) logrus.WithField("server_name", oq.destination).Debugf("Sending transaction %q containing %d PDUs, %d EDUs", t.TransactionID, len(t.PDUs), len(t.EDUs))
@ -391,15 +411,15 @@ func (oq *destinationQueue) nextTransaction(transaction *queuedTransaction) (boo
switch err.(type) { switch err.(type) {
case nil: case nil:
// Clean up the transaction in the database. // Clean up the transaction in the database.
for _, receipt := range transaction.pduReceipts { if pduReceipts != nil {
//logrus.Infof("Cleaning PDUs %q", pduReceipt.String()) //logrus.Infof("Cleaning PDUs %q", pduReceipt.String())
if err = oq.db.CleanPDUs(context.Background(), oq.destination, receipt); err != nil { if err = oq.db.CleanPDUs(context.Background(), oq.destination, pduReceipts); err != nil {
log.WithError(err).Errorf("Failed to clean PDUs for server %q", t.Destination) log.WithError(err).Errorf("Failed to clean PDUs for server %q", t.Destination)
} }
} }
for _, receipt := range transaction.eduReceipts { if eduReceipts != nil {
//logrus.Infof("Cleaning EDUs %q", eduReceipt.String()) //logrus.Infof("Cleaning EDUs %q", eduReceipt.String())
if err = oq.db.CleanEDUs(context.Background(), oq.destination, receipt); err != nil { if err = oq.db.CleanEDUs(context.Background(), oq.destination, eduReceipts); err != nil {
log.WithError(err).Errorf("Failed to clean EDUs for server %q", t.Destination) log.WithError(err).Errorf("Failed to clean EDUs for server %q", t.Destination)
} }
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/matrix-org/dendrite/federationsender/statistics" "github.com/matrix-org/dendrite/federationsender/statistics"
"github.com/matrix-org/dendrite/federationsender/storage" "github.com/matrix-org/dendrite/federationsender/storage"
"github.com/matrix-org/dendrite/federationsender/storage/shared"
"github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -100,6 +101,16 @@ type SigningInfo struct {
PrivateKey ed25519.PrivateKey PrivateKey ed25519.PrivateKey
} }
type queuedPDU struct {
receipt *shared.Receipt
pdu *gomatrixserverlib.HeaderedEvent
}
type queuedEDU struct {
receipt *shared.Receipt
edu *gomatrixserverlib.EDU
}
func (oqs *OutgoingQueues) getQueue(destination gomatrixserverlib.ServerName) *destinationQueue { func (oqs *OutgoingQueues) getQueue(destination gomatrixserverlib.ServerName) *destinationQueue {
oqs.queuesMutex.Lock() oqs.queuesMutex.Lock()
defer oqs.queuesMutex.Unlock() defer oqs.queuesMutex.Unlock()
@ -116,7 +127,6 @@ func (oqs *OutgoingQueues) getQueue(destination gomatrixserverlib.ServerName) *d
interruptBackoff: make(chan bool), interruptBackoff: make(chan bool),
signing: oqs.signing, signing: oqs.signing,
} }
oq.pendingTransactions.statistics = oq.statistics
oqs.queues[destination] = oq oqs.queues[destination] = oq
} }
return oq return oq

View file

@ -1,92 +0,0 @@
package queue
import (
"fmt"
"time"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/dendrite/federationsender/statistics"
"github.com/matrix-org/dendrite/federationsender/storage/shared"
)
const (
maxTransactionsInMemory = 4
maxPDUsPerTransaction = 50
maxEDUsPerTransaction = 50
)
type queuedTransaction struct {
id gomatrixserverlib.TransactionID
pdus []*gomatrixserverlib.HeaderedEvent
edus []*gomatrixserverlib.EDU
pduReceipts []*shared.Receipt
eduReceipts []*shared.Receipt
}
type queuedTransactions struct {
statistics *statistics.ServerStatistics
queue []*queuedTransaction
}
func (q *queuedTransactions) createNew(transactionID gomatrixserverlib.TransactionID) bool {
now := gomatrixserverlib.AsTimestamp(time.Now())
if len(q.queue) == maxTransactionsInMemory {
return false
}
if transactionID == "" {
transactionID = gomatrixserverlib.TransactionID(fmt.Sprintf("%d-%d", now, q.statistics.SuccessCount()))
}
q.queue = append(q.queue, &queuedTransaction{
id: transactionID,
})
return true
}
func (q *queuedTransactions) getNewestForPDU() *queuedTransaction {
if len(q.queue) == 0 || len(q.queue[len(q.queue)-1].pdus) == maxPDUsPerTransaction {
if len(q.queue) == maxTransactionsInMemory {
return nil
}
if !q.createNew("") {
return nil
}
}
return q.queue[len(q.queue)-1]
}
func (q *queuedTransactions) getNewestForEDU() *queuedTransaction {
if len(q.queue) == 0 || len(q.queue[len(q.queue)-1].edus) == maxEDUsPerTransaction {
if len(q.queue) == maxTransactionsInMemory {
return nil
}
if !q.createNew("") {
return nil
}
}
return q.queue[len(q.queue)-1]
}
func (q *queuedTransactions) queuePDUs(receipt *shared.Receipt, pdu ...*gomatrixserverlib.HeaderedEvent) gomatrixserverlib.TransactionID {
last := q.getNewestForPDU()
if last == nil {
return ""
}
last.pdus = append(last.pdus, pdu...)
if receipt != nil {
last.pduReceipts = append(last.pduReceipts, receipt)
}
return last.id
}
func (q *queuedTransactions) queueEDUs(receipt *shared.Receipt, edu ...*gomatrixserverlib.EDU) gomatrixserverlib.TransactionID {
last := q.getNewestForEDU()
if last == nil {
return ""
}
last.edus = append(last.edus, edu...)
if receipt != nil {
last.eduReceipts = append(last.eduReceipts, receipt)
}
return last.id
}

View file

@ -36,14 +36,14 @@ type Database interface {
StoreJSON(ctx context.Context, js string) (*shared.Receipt, error) StoreJSON(ctx context.Context, js string) (*shared.Receipt, error)
GetPendingPDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, limit int) (pdus map[*shared.Receipt]*gomatrixserverlib.HeaderedEvent, err error)
GetPendingEDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, limit int) (edus map[*shared.Receipt]*gomatrixserverlib.EDU, err error)
AssociatePDUWithDestination(ctx context.Context, transactionID gomatrixserverlib.TransactionID, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt) error AssociatePDUWithDestination(ctx context.Context, transactionID gomatrixserverlib.TransactionID, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt) error
AssociateEDUWithDestination(ctx context.Context, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt) error AssociateEDUWithDestination(ctx context.Context, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt) error
GetNextTransactionPDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, limit int) (gomatrixserverlib.TransactionID, []*gomatrixserverlib.HeaderedEvent, *shared.Receipt, error) CleanPDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, receipts []*shared.Receipt) error
GetNextTransactionEDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, limit int) ([]*gomatrixserverlib.EDU, *shared.Receipt, error) CleanEDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, receipts []*shared.Receipt) error
CleanPDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt) error
CleanEDUs(ctx context.Context, serverName gomatrixserverlib.ServerName, receipt *shared.Receipt) error
GetPendingPDUCount(ctx context.Context, serverName gomatrixserverlib.ServerName) (int64, error) GetPendingPDUCount(ctx context.Context, serverName gomatrixserverlib.ServerName) (int64, error)
GetPendingEDUCount(ctx context.Context, serverName gomatrixserverlib.ServerName) (int64, error) GetPendingEDUCount(ctx context.Context, serverName gomatrixserverlib.ServerName) (int64, error)

View file

@ -45,16 +45,10 @@ const insertQueuePDUSQL = "" +
const deleteQueuePDUSQL = "" + const deleteQueuePDUSQL = "" +
"DELETE FROM federationsender_queue_pdus WHERE server_name = $1 AND json_nid = ANY($2)" "DELETE FROM federationsender_queue_pdus WHERE server_name = $1 AND json_nid = ANY($2)"
const selectQueuePDUNextTransactionIDSQL = "" + const selectQueuePDUsSQL = "" +
"SELECT transaction_id FROM federationsender_queue_pdus" +
" WHERE server_name = $1" +
" ORDER BY transaction_id ASC" +
" LIMIT 1"
const selectQueuePDUsByTransactionSQL = "" +
"SELECT json_nid FROM federationsender_queue_pdus" + "SELECT json_nid FROM federationsender_queue_pdus" +
" WHERE server_name = $1 AND transaction_id = $2" + " WHERE server_name = $1" +
" LIMIT $3" " LIMIT $2"
const selectQueuePDUReferenceJSONCountSQL = "" + const selectQueuePDUReferenceJSONCountSQL = "" +
"SELECT COUNT(*) FROM federationsender_queue_pdus" + "SELECT COUNT(*) FROM federationsender_queue_pdus" +
@ -71,8 +65,7 @@ type queuePDUsStatements struct {
db *sql.DB db *sql.DB
insertQueuePDUStmt *sql.Stmt insertQueuePDUStmt *sql.Stmt
deleteQueuePDUsStmt *sql.Stmt deleteQueuePDUsStmt *sql.Stmt
selectQueuePDUNextTransactionIDStmt *sql.Stmt selectQueuePDUsStmt *sql.Stmt
selectQueuePDUsByTransactionStmt *sql.Stmt
selectQueuePDUReferenceJSONCountStmt *sql.Stmt selectQueuePDUReferenceJSONCountStmt *sql.Stmt
selectQueuePDUsCountStmt *sql.Stmt selectQueuePDUsCountStmt *sql.Stmt
selectQueuePDUServerNamesStmt *sql.Stmt selectQueuePDUServerNamesStmt *sql.Stmt
@ -92,10 +85,7 @@ func NewPostgresQueuePDUsTable(db *sql.DB) (s *queuePDUsStatements, err error) {
if s.deleteQueuePDUsStmt, err = s.db.Prepare(deleteQueuePDUSQL); err != nil { if s.deleteQueuePDUsStmt, err = s.db.Prepare(deleteQueuePDUSQL); err != nil {
return return
} }
if s.selectQueuePDUNextTransactionIDStmt, err = s.db.Prepare(selectQueuePDUNextTransactionIDSQL); err != nil { if s.selectQueuePDUsStmt, err = s.db.Prepare(selectQueuePDUsSQL); err != nil {
return
}
if s.selectQueuePDUsByTransactionStmt, err = s.db.Prepare(selectQueuePDUsByTransactionSQL); err != nil {
return return
} }
if s.selectQueuePDUReferenceJSONCountStmt, err = s.db.Prepare(selectQueuePDUReferenceJSONCountSQL); err != nil { if s.selectQueuePDUReferenceJSONCountStmt, err = s.db.Prepare(selectQueuePDUReferenceJSONCountSQL); err != nil {
@ -137,18 +127,6 @@ func (s *queuePDUsStatements) DeleteQueuePDUs(
return err return err
} }
func (s *queuePDUsStatements) SelectQueuePDUNextTransactionID(
ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName,
) (gomatrixserverlib.TransactionID, error) {
var transactionID gomatrixserverlib.TransactionID
stmt := sqlutil.TxStmt(txn, s.selectQueuePDUNextTransactionIDStmt)
err := stmt.QueryRowContext(ctx, serverName).Scan(&transactionID)
if err == sql.ErrNoRows {
return "", nil
}
return transactionID, err
}
func (s *queuePDUsStatements) SelectQueuePDUReferenceJSONCount( func (s *queuePDUsStatements) SelectQueuePDUReferenceJSONCount(
ctx context.Context, txn *sql.Tx, jsonNID int64, ctx context.Context, txn *sql.Tx, jsonNID int64,
) (int64, error) { ) (int64, error) {
@ -182,11 +160,10 @@ func (s *queuePDUsStatements) SelectQueuePDUCount(
func (s *queuePDUsStatements) SelectQueuePDUs( func (s *queuePDUsStatements) SelectQueuePDUs(
ctx context.Context, txn *sql.Tx, ctx context.Context, txn *sql.Tx,
serverName gomatrixserverlib.ServerName, serverName gomatrixserverlib.ServerName,
transactionID gomatrixserverlib.TransactionID,
limit int, limit int,
) ([]int64, error) { ) ([]int64, error) {
stmt := sqlutil.TxStmt(txn, s.selectQueuePDUsByTransactionStmt) stmt := sqlutil.TxStmt(txn, s.selectQueuePDUsStmt)
rows, err := stmt.QueryContext(ctx, serverName, transactionID, limit) rows, err := stmt.QueryContext(ctx, serverName, limit)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -17,7 +17,6 @@ package shared
import ( import (
"context" "context"
"database/sql" "database/sql"
"encoding/json"
"fmt" "fmt"
"github.com/matrix-org/dendrite/federationsender/storage/tables" "github.com/matrix-org/dendrite/federationsender/storage/tables"
@ -44,16 +43,11 @@ type Database struct {
// to pass them back so that we can clean up if the transaction sends // to pass them back so that we can clean up if the transaction sends
// successfully. // successfully.
type Receipt struct { type Receipt struct {
nids []int64 nid int64
} }
func (e *Receipt) Empty() bool { func (r *Receipt) String() string {
return len(e.nids) == 0 return fmt.Sprintf("%d", r.nid)
}
func (e *Receipt) String() string {
j, _ := json.Marshal(e.nids)
return string(j)
} }
// UpdateRoom updates the joined hosts for a room and returns what the joined // UpdateRoom updates the joined hosts for a room and returns what the joined
@ -146,7 +140,7 @@ func (d *Database) StoreJSON(
return nil, fmt.Errorf("d.insertQueueJSON: %w", err) return nil, fmt.Errorf("d.insertQueueJSON: %w", err)
} }
return &Receipt{ return &Receipt{
nids: []int64{nid}, nid: nid,
}, nil }, nil
} }

View file

@ -33,46 +33,40 @@ func (d *Database) AssociateEDUWithDestination(
receipt *Receipt, receipt *Receipt,
) error { ) error {
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
for _, nid := range receipt.nids {
if err := d.FederationSenderQueueEDUs.InsertQueueEDU( if err := d.FederationSenderQueueEDUs.InsertQueueEDU(
ctx, // context ctx, // context
txn, // SQL transaction txn, // SQL transaction
"", // TODO: EDU type for coalescing "", // TODO: EDU type for coalescing
serverName, // destination server name serverName, // destination server name
nid, // NID from the federationsender_queue_json table receipt.nid, // NID from the federationsender_queue_json table
); err != nil { ); err != nil {
return fmt.Errorf("InsertQueueEDU: %w", err) return fmt.Errorf("InsertQueueEDU: %w", err)
} }
}
return nil return nil
}) })
} }
// GetNextTransactionEDUs retrieves events from the database for // GetNextTransactionEDUs retrieves events from the database for
// the next pending transaction, up to the limit specified. // the next pending transaction, up to the limit specified.
func (d *Database) GetNextTransactionEDUs( func (d *Database) GetPendingEDUs(
ctx context.Context, ctx context.Context,
serverName gomatrixserverlib.ServerName, serverName gomatrixserverlib.ServerName,
limit int, limit int,
) ( ) (
edus []*gomatrixserverlib.EDU, edus map[*Receipt]*gomatrixserverlib.EDU,
receipt *Receipt,
err error, err error,
) { ) {
edus = make(map[*Receipt]*gomatrixserverlib.EDU)
err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
nids, err := d.FederationSenderQueueEDUs.SelectQueueEDUs(ctx, txn, serverName, limit) nids, err := d.FederationSenderQueueEDUs.SelectQueueEDUs(ctx, txn, serverName, limit)
if err != nil { if err != nil {
return fmt.Errorf("SelectQueueEDUs: %w", err) return fmt.Errorf("SelectQueueEDUs: %w", err)
} }
receipt = &Receipt{
nids: nids,
}
retrieve := make([]int64, 0, len(nids)) retrieve := make([]int64, 0, len(nids))
for _, nid := range nids { for _, nid := range nids {
if edu, ok := d.Cache.GetFederationSenderQueuedEDU(nid); ok { if edu, ok := d.Cache.GetFederationSenderQueuedEDU(nid); ok {
edus = append(edus, edu) edus[&Receipt{nid}] = edu
} else { } else {
retrieve = append(retrieve, nid) retrieve = append(retrieve, nid)
} }
@ -83,12 +77,12 @@ func (d *Database) GetNextTransactionEDUs(
return fmt.Errorf("SelectQueueJSON: %w", err) return fmt.Errorf("SelectQueueJSON: %w", err)
} }
for _, blob := range blobs { for nid, blob := range blobs {
var event gomatrixserverlib.EDU var event gomatrixserverlib.EDU
if err := json.Unmarshal(blob, &event); err != nil { if err := json.Unmarshal(blob, &event); err != nil {
return fmt.Errorf("json.Unmarshal: %w", err) return fmt.Errorf("json.Unmarshal: %w", err)
} }
edus = append(edus, &event) edus[&Receipt{nid}] = &event
} }
return nil return nil
@ -101,19 +95,24 @@ func (d *Database) GetNextTransactionEDUs(
func (d *Database) CleanEDUs( func (d *Database) CleanEDUs(
ctx context.Context, ctx context.Context,
serverName gomatrixserverlib.ServerName, serverName gomatrixserverlib.ServerName,
receipt *Receipt, receipts []*Receipt,
) error { ) error {
if receipt == nil { if len(receipts) == 0 {
return errors.New("expected receipt") return errors.New("expected receipt")
} }
nids := make([]int64, len(receipts))
for i := range receipts {
nids[i] = receipts[i].nid
}
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
if err := d.FederationSenderQueueEDUs.DeleteQueueEDUs(ctx, txn, serverName, receipt.nids); err != nil { if err := d.FederationSenderQueueEDUs.DeleteQueueEDUs(ctx, txn, serverName, nids); err != nil {
return err return err
} }
var deleteNIDs []int64 var deleteNIDs []int64
for _, nid := range receipt.nids { for _, nid := range nids {
count, err := d.FederationSenderQueueEDUs.SelectQueueEDUReferenceJSONCount(ctx, txn, nid) count, err := d.FederationSenderQueueEDUs.SelectQueueEDUReferenceJSONCount(ctx, txn, nid)
if err != nil { if err != nil {
return fmt.Errorf("SelectQueueEDUReferenceJSONCount: %w", err) return fmt.Errorf("SelectQueueEDUReferenceJSONCount: %w", err)

View file

@ -34,31 +34,27 @@ func (d *Database) AssociatePDUWithDestination(
receipt *Receipt, receipt *Receipt,
) error { ) error {
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
for _, nid := range receipt.nids {
if err := d.FederationSenderQueuePDUs.InsertQueuePDU( if err := d.FederationSenderQueuePDUs.InsertQueuePDU(
ctx, // context ctx, // context
txn, // SQL transaction txn, // SQL transaction
transactionID, // transaction ID transactionID, // transaction ID
serverName, // destination server name serverName, // destination server name
nid, // NID from the federationsender_queue_json table receipt.nid, // NID from the federationsender_queue_json table
); err != nil { ); err != nil {
return fmt.Errorf("InsertQueuePDU: %w", err) return fmt.Errorf("InsertQueuePDU: %w", err)
} }
}
return nil return nil
}) })
} }
// GetNextTransactionPDUs retrieves events from the database for // GetNextTransactionPDUs retrieves events from the database for
// the next pending transaction, up to the limit specified. // the next pending transaction, up to the limit specified.
func (d *Database) GetNextTransactionPDUs( func (d *Database) GetPendingPDUs(
ctx context.Context, ctx context.Context,
serverName gomatrixserverlib.ServerName, serverName gomatrixserverlib.ServerName,
limit int, limit int,
) ( ) (
transactionID gomatrixserverlib.TransactionID, events map[*Receipt]*gomatrixserverlib.HeaderedEvent,
events []*gomatrixserverlib.HeaderedEvent,
receipt *Receipt,
err error, err error,
) { ) {
// Strictly speaking this doesn't need to be using the writer // Strictly speaking this doesn't need to be using the writer
@ -66,29 +62,17 @@ func (d *Database) GetNextTransactionPDUs(
// a guarantee of transactional isolation, it's actually useful // a guarantee of transactional isolation, it's actually useful
// to know in SQLite mode that nothing else is trying to modify // to know in SQLite mode that nothing else is trying to modify
// the database. // the database.
events = make(map[*Receipt]*gomatrixserverlib.HeaderedEvent)
err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
transactionID, err = d.FederationSenderQueuePDUs.SelectQueuePDUNextTransactionID(ctx, txn, serverName) nids, err := d.FederationSenderQueuePDUs.SelectQueuePDUs(ctx, txn, serverName, limit)
if err != nil {
return fmt.Errorf("SelectQueuePDUNextTransactionID: %w", err)
}
if transactionID == "" {
return nil
}
nids, err := d.FederationSenderQueuePDUs.SelectQueuePDUs(ctx, txn, serverName, transactionID, limit)
if err != nil { if err != nil {
return fmt.Errorf("SelectQueuePDUs: %w", err) return fmt.Errorf("SelectQueuePDUs: %w", err)
} }
receipt = &Receipt{
nids: nids,
}
retrieve := make([]int64, 0, len(nids)) retrieve := make([]int64, 0, len(nids))
for _, nid := range nids { for _, nid := range nids {
if event, ok := d.Cache.GetFederationSenderQueuedPDU(nid); ok { if event, ok := d.Cache.GetFederationSenderQueuedPDU(nid); ok {
events = append(events, event) events[&Receipt{nid}] = event
} else { } else {
retrieve = append(retrieve, nid) retrieve = append(retrieve, nid)
} }
@ -104,7 +88,7 @@ func (d *Database) GetNextTransactionPDUs(
if err := json.Unmarshal(blob, &event); err != nil { if err := json.Unmarshal(blob, &event); err != nil {
return fmt.Errorf("json.Unmarshal: %w", err) return fmt.Errorf("json.Unmarshal: %w", err)
} }
events = append(events, &event) events[&Receipt{nid}] = &event
d.Cache.StoreFederationSenderQueuedPDU(nid, &event) d.Cache.StoreFederationSenderQueuedPDU(nid, &event)
} }
@ -119,19 +103,24 @@ func (d *Database) GetNextTransactionPDUs(
func (d *Database) CleanPDUs( func (d *Database) CleanPDUs(
ctx context.Context, ctx context.Context,
serverName gomatrixserverlib.ServerName, serverName gomatrixserverlib.ServerName,
receipt *Receipt, receipts []*Receipt,
) error { ) error {
if receipt == nil { if len(receipts) == 0 {
return errors.New("expected receipt") return errors.New("expected receipt")
} }
nids := make([]int64, len(receipts))
for i := range receipts {
nids[i] = receipts[i].nid
}
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error { return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
if err := d.FederationSenderQueuePDUs.DeleteQueuePDUs(ctx, txn, serverName, receipt.nids); err != nil { if err := d.FederationSenderQueuePDUs.DeleteQueuePDUs(ctx, txn, serverName, nids); err != nil {
return err return err
} }
var deleteNIDs []int64 var deleteNIDs []int64
for _, nid := range receipt.nids { for _, nid := range nids {
count, err := d.FederationSenderQueuePDUs.SelectQueuePDUReferenceJSONCount(ctx, txn, nid) count, err := d.FederationSenderQueuePDUs.SelectQueuePDUReferenceJSONCount(ctx, txn, nid)
if err != nil { if err != nil {
return fmt.Errorf("SelectQueuePDUReferenceJSONCount: %w", err) return fmt.Errorf("SelectQueuePDUReferenceJSONCount: %w", err)

View file

@ -53,10 +53,10 @@ const selectQueueNextTransactionIDSQL = "" +
" ORDER BY transaction_id ASC" + " ORDER BY transaction_id ASC" +
" LIMIT 1" " LIMIT 1"
const selectQueuePDUsByTransactionSQL = "" + const selectQueuePDUsSQL = "" +
"SELECT json_nid FROM federationsender_queue_pdus" + "SELECT json_nid FROM federationsender_queue_pdus" +
" WHERE server_name = $1 AND transaction_id = $2" + " WHERE server_name = $1" +
" LIMIT $3" " LIMIT $2"
const selectQueuePDUsReferenceJSONCountSQL = "" + const selectQueuePDUsReferenceJSONCountSQL = "" +
"SELECT COUNT(*) FROM federationsender_queue_pdus" + "SELECT COUNT(*) FROM federationsender_queue_pdus" +
@ -73,7 +73,7 @@ type queuePDUsStatements struct {
db *sql.DB db *sql.DB
insertQueuePDUStmt *sql.Stmt insertQueuePDUStmt *sql.Stmt
selectQueueNextTransactionIDStmt *sql.Stmt selectQueueNextTransactionIDStmt *sql.Stmt
selectQueuePDUsByTransactionStmt *sql.Stmt selectQueuePDUsStmt *sql.Stmt
selectQueueReferenceJSONCountStmt *sql.Stmt selectQueueReferenceJSONCountStmt *sql.Stmt
selectQueuePDUsCountStmt *sql.Stmt selectQueuePDUsCountStmt *sql.Stmt
selectQueueServerNamesStmt *sql.Stmt selectQueueServerNamesStmt *sql.Stmt
@ -97,7 +97,7 @@ func NewSQLiteQueuePDUsTable(db *sql.DB) (s *queuePDUsStatements, err error) {
if s.selectQueueNextTransactionIDStmt, err = db.Prepare(selectQueueNextTransactionIDSQL); err != nil { if s.selectQueueNextTransactionIDStmt, err = db.Prepare(selectQueueNextTransactionIDSQL); err != nil {
return return
} }
if s.selectQueuePDUsByTransactionStmt, err = db.Prepare(selectQueuePDUsByTransactionSQL); err != nil { if s.selectQueuePDUsStmt, err = db.Prepare(selectQueuePDUsSQL); err != nil {
return return
} }
if s.selectQueueReferenceJSONCountStmt, err = db.Prepare(selectQueuePDUsReferenceJSONCountSQL); err != nil { if s.selectQueueReferenceJSONCountStmt, err = db.Prepare(selectQueuePDUsReferenceJSONCountSQL); err != nil {
@ -193,11 +193,10 @@ func (s *queuePDUsStatements) SelectQueuePDUCount(
func (s *queuePDUsStatements) SelectQueuePDUs( func (s *queuePDUsStatements) SelectQueuePDUs(
ctx context.Context, txn *sql.Tx, ctx context.Context, txn *sql.Tx,
serverName gomatrixserverlib.ServerName, serverName gomatrixserverlib.ServerName,
transactionID gomatrixserverlib.TransactionID,
limit int, limit int,
) ([]int64, error) { ) ([]int64, error) {
stmt := sqlutil.TxStmt(txn, s.selectQueuePDUsByTransactionStmt) stmt := sqlutil.TxStmt(txn, s.selectQueuePDUsStmt)
rows, err := stmt.QueryContext(ctx, serverName, transactionID, limit) rows, err := stmt.QueryContext(ctx, serverName, limit)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -25,10 +25,9 @@ import (
type FederationSenderQueuePDUs interface { type FederationSenderQueuePDUs interface {
InsertQueuePDU(ctx context.Context, txn *sql.Tx, transactionID gomatrixserverlib.TransactionID, serverName gomatrixserverlib.ServerName, nid int64) error InsertQueuePDU(ctx context.Context, txn *sql.Tx, transactionID gomatrixserverlib.TransactionID, serverName gomatrixserverlib.ServerName, nid int64) error
DeleteQueuePDUs(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, jsonNIDs []int64) error DeleteQueuePDUs(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, jsonNIDs []int64) error
SelectQueuePDUNextTransactionID(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName) (gomatrixserverlib.TransactionID, error)
SelectQueuePDUReferenceJSONCount(ctx context.Context, txn *sql.Tx, jsonNID int64) (int64, error) SelectQueuePDUReferenceJSONCount(ctx context.Context, txn *sql.Tx, jsonNID int64) (int64, error)
SelectQueuePDUCount(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName) (int64, error) SelectQueuePDUCount(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName) (int64, error)
SelectQueuePDUs(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, transactionID gomatrixserverlib.TransactionID, limit int) ([]int64, error) SelectQueuePDUs(ctx context.Context, txn *sql.Tx, serverName gomatrixserverlib.ServerName, limit int) ([]int64, error)
SelectQueuePDUServerNames(ctx context.Context, txn *sql.Tx) ([]gomatrixserverlib.ServerName, error) SelectQueuePDUServerNames(ctx context.Context, txn *sql.Tx) ([]gomatrixserverlib.ServerName, error)
} }