mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-20 21:33:19 -06:00
Try to avoid database locks
This commit is contained in:
parent
005c4c0168
commit
5487edf1d6
4
eduserver/cache/cache.go
vendored
4
eduserver/cache/cache.go
vendored
|
|
@ -115,9 +115,9 @@ func (t *EDUCache) AddTypingUser(
|
||||||
func (t *EDUCache) AddSendToDeviceMessage() int64 {
|
func (t *EDUCache) AddSendToDeviceMessage() int64 {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
r := t.latestSyncPosition
|
||||||
t.latestSyncPosition++
|
t.latestSyncPosition++
|
||||||
return t.latestSyncPosition - 1
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// addUser with mutex lock & replace the previous timer.
|
// addUser with mutex lock & replace the previous timer.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Transaction is something that can be committed or rolledback.
|
// A Transaction is something that can be committed or rolledback.
|
||||||
|
|
@ -107,3 +109,44 @@ type DbProperties interface {
|
||||||
MaxOpenConns() int
|
MaxOpenConns() int
|
||||||
ConnMaxLifetime() time.Duration
|
ConnMaxLifetime() time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TransactionWriter struct {
|
||||||
|
running atomic.Bool
|
||||||
|
todo chan transactionWriterTask
|
||||||
|
}
|
||||||
|
|
||||||
|
type transactionWriterTask struct {
|
||||||
|
db *sql.DB
|
||||||
|
f func(txn *sql.Tx)
|
||||||
|
wait chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *TransactionWriter) Do(db *sql.DB, f func(txn *sql.Tx)) {
|
||||||
|
if w.todo == nil {
|
||||||
|
w.todo = make(chan transactionWriterTask)
|
||||||
|
}
|
||||||
|
if !w.running.Load() {
|
||||||
|
go w.run()
|
||||||
|
}
|
||||||
|
task := transactionWriterTask{
|
||||||
|
db: db,
|
||||||
|
f: f,
|
||||||
|
wait: make(chan struct{}),
|
||||||
|
}
|
||||||
|
w.todo <- task
|
||||||
|
<-task.wait
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *TransactionWriter) run() {
|
||||||
|
if !w.running.CAS(false, true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer w.running.Store(false)
|
||||||
|
for task := range w.todo {
|
||||||
|
_ = WithTransaction(task.db, func(txn *sql.Tx) error {
|
||||||
|
task.f(txn)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
close(task.wait)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/matrix-org/dendrite/syncapi/storage"
|
"github.com/matrix-org/dendrite/syncapi/storage"
|
||||||
"github.com/matrix-org/dendrite/syncapi/sync"
|
"github.com/matrix-org/dendrite/syncapi/sync"
|
||||||
"github.com/matrix-org/dendrite/syncapi/types"
|
"github.com/matrix-org/dendrite/syncapi/types"
|
||||||
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/util"
|
"github.com/matrix-org/util"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
@ -33,6 +34,7 @@ import (
|
||||||
type OutputSendToDeviceEventConsumer struct {
|
type OutputSendToDeviceEventConsumer struct {
|
||||||
sendToDeviceConsumer *internal.ContinualConsumer
|
sendToDeviceConsumer *internal.ContinualConsumer
|
||||||
db storage.Database
|
db storage.Database
|
||||||
|
serverName gomatrixserverlib.ServerName // our server name
|
||||||
notifier *sync.Notifier
|
notifier *sync.Notifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,6 +56,7 @@ func NewOutputSendToDeviceEventConsumer(
|
||||||
s := &OutputSendToDeviceEventConsumer{
|
s := &OutputSendToDeviceEventConsumer{
|
||||||
sendToDeviceConsumer: &consumer,
|
sendToDeviceConsumer: &consumer,
|
||||||
db: store,
|
db: store,
|
||||||
|
serverName: cfg.Matrix.ServerName,
|
||||||
notifier: n,
|
notifier: n,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +78,14 @@ func (s *OutputSendToDeviceEventConsumer) onMessage(msg *sarama.ConsumerMessage)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, domain, err := gomatrixserverlib.SplitID('@', output.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if domain != s.serverName {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
util.GetLogger(context.TODO()).WithFields(log.Fields{
|
util.GetLogger(context.TODO()).WithFields(log.Fields{
|
||||||
"sender": output.Sender,
|
"sender": output.Sender,
|
||||||
"user_id": output.UserID,
|
"user_id": output.UserID,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ type Database struct {
|
||||||
CurrentRoomState tables.CurrentRoomState
|
CurrentRoomState tables.CurrentRoomState
|
||||||
BackwardExtremities tables.BackwardsExtremities
|
BackwardExtremities tables.BackwardsExtremities
|
||||||
SendToDevice tables.SendToDevice
|
SendToDevice tables.SendToDevice
|
||||||
|
SendToDeviceWriter internal.TransactionWriter
|
||||||
EDUCache *cache.EDUCache
|
EDUCache *cache.EDUCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1045,9 +1046,13 @@ func (d *Database) StoreNewSendForDeviceMessage(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
// Delegate the database write task to the SendToDeviceWriter. It'll guarantee
|
||||||
|
// that we don't lock the table for writes in more than one place.
|
||||||
|
d.SendToDeviceWriter.Do(d.DB, func(txn *sql.Tx) {
|
||||||
err = d.AddSendToDeviceEvent(
|
err = d.AddSendToDeviceEvent(
|
||||||
ctx, nil, userID, deviceID, string(j),
|
ctx, txn, userID, deviceID, string(j),
|
||||||
)
|
)
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -1059,11 +1064,10 @@ func (d *Database) SendToDeviceUpdatesForSync(
|
||||||
userID, deviceID string,
|
userID, deviceID string,
|
||||||
token types.StreamingToken,
|
token types.StreamingToken,
|
||||||
) (events []types.SendToDeviceEvent, err error) {
|
) (events []types.SendToDeviceEvent, err error) {
|
||||||
err = internal.WithTransaction(d.DB, func(txn *sql.Tx) error {
|
|
||||||
// First of all, get our send-to-device updates for this user.
|
// First of all, get our send-to-device updates for this user.
|
||||||
events, err = d.SendToDevice.SelectSendToDeviceMessages(ctx, txn, userID, deviceID)
|
events, err = d.SendToDevice.SelectSendToDeviceMessages(ctx, nil, userID, deviceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("d.SendToDevice.SelectSendToDeviceMessages: %w", err)
|
return nil, fmt.Errorf("d.SendToDevice.SelectSendToDeviceMessages: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start by cleaning up any send-to-device messages that have older sent-by-tokens.
|
// Start by cleaning up any send-to-device messages that have older sent-by-tokens.
|
||||||
|
|
@ -1083,18 +1087,25 @@ func (d *Database) SendToDeviceUpdatesForSync(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we need to write to the database then we'll ask the SendToDeviceWriter to
|
||||||
|
// do that for us. It'll guarantee that we don't lock the table for writes in
|
||||||
|
// more than one place.
|
||||||
|
if len(toUpdate) > 0 || len(toDelete) > 0 {
|
||||||
|
d.SendToDeviceWriter.Do(d.DB, func(txn *sql.Tx) {
|
||||||
// Delete any send-to-device messages marked for deletion.
|
// Delete any send-to-device messages marked for deletion.
|
||||||
if err := d.SendToDevice.DeleteSendToDeviceMessages(ctx, txn, toDelete); err != nil {
|
if e := d.SendToDevice.DeleteSendToDeviceMessages(ctx, txn, toDelete); e != nil {
|
||||||
return fmt.Errorf("d.SendToDevice.DeleteSendToDeviceMessages: %w", err)
|
err = fmt.Errorf("d.SendToDevice.DeleteSendToDeviceMessages: %w", e)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now update any outstanding send-to-device messages with the new sync token.
|
// Now update any outstanding send-to-device messages with the new sync token.
|
||||||
if err := d.SendToDevice.UpdateSentSendToDeviceMessages(ctx, txn, token.String(), toUpdate); err != nil {
|
if e := d.SendToDevice.UpdateSentSendToDeviceMessages(ctx, txn, token.String(), toUpdate); e != nil {
|
||||||
return fmt.Errorf("d.SendToDevice.UpdateSentSendToDeviceMessages: %w", err)
|
err = fmt.Errorf("d.SendToDevice.UpdateSentSendToDeviceMessages: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -289,3 +289,9 @@ Existing members see new members' join events
|
||||||
Inbound federation can receive events
|
Inbound federation can receive events
|
||||||
Inbound federation can receive redacted events
|
Inbound federation can receive redacted events
|
||||||
Can logout current device
|
Can logout current device
|
||||||
|
Can send a message directly to a device using PUT /sendToDevice
|
||||||
|
Can recv a device message using /sync
|
||||||
|
Can send a to-device message to two users which both receive it using /sync
|
||||||
|
Can recv device messages until they are acknowledged
|
||||||
|
Device messages wake up /sync
|
||||||
|
Device messages over federation wake up /sync
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue