Use transactional isolation only if available

This commit is contained in:
Neil Alexander 2020-09-08 17:44:29 +01:00
parent e66c057297
commit 06c75fadf4
No known key found for this signature in database
GPG key ID: A02A2019A2BB0944
3 changed files with 61 additions and 51 deletions

View file

@ -79,17 +79,18 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*SyncServerDatasource, e
return nil, err return nil, err
} }
d.Database = shared.Database{ d.Database = shared.Database{
DB: d.db, DB: d.db,
Writer: d.writer, TransactionalIsolation: true,
Invites: invites, Writer: d.writer,
AccountData: accountData, Invites: invites,
OutputEvents: events, AccountData: accountData,
Topology: topology, OutputEvents: events,
CurrentRoomState: currState, Topology: topology,
BackwardExtremities: backwardExtremities, CurrentRoomState: currState,
Filter: filter, BackwardExtremities: backwardExtremities,
SendToDevice: sendToDevice, Filter: filter,
EDUCache: cache.New(), SendToDevice: sendToDevice,
EDUCache: cache.New(),
} }
return &d, nil return &d, nil
} }

View file

@ -36,17 +36,31 @@ import (
// Database is a temporary struct until we have made syncserver.go the same for both pq/sqlite // Database is a temporary struct until we have made syncserver.go the same for both pq/sqlite
// For now this contains the shared functions // For now this contains the shared functions
type Database struct { type Database struct {
DB *sql.DB DB *sql.DB
Writer sqlutil.Writer TransactionalIsolation bool
Invites tables.Invites Writer sqlutil.Writer
AccountData tables.AccountData Invites tables.Invites
OutputEvents tables.Events AccountData tables.AccountData
Topology tables.Topology OutputEvents tables.Events
CurrentRoomState tables.CurrentRoomState Topology tables.Topology
BackwardExtremities tables.BackwardsExtremities CurrentRoomState tables.CurrentRoomState
SendToDevice tables.SendToDevice BackwardExtremities tables.BackwardsExtremities
Filter tables.Filter SendToDevice tables.SendToDevice
EDUCache *cache.EDUCache Filter tables.Filter
EDUCache *cache.EDUCache
}
func (d *Database) getIsolatedTransactionIfAvailable(ctx context.Context, succeeded *bool) (txn *sql.Tx, done func(), err error) {
if d.TransactionalIsolation {
txn, err = d.DB.BeginTx(ctx, &txReadOnlySnapshot)
if err != nil {
return nil, nil, err
}
done = func() {
sqlutil.EndTransactionWithCheck(txn, succeeded, &err)
}
}
return
} }
// Events lookups a list of event by their event ID. // Events lookups a list of event by their event ID.
@ -422,17 +436,14 @@ func (d *Database) addPDUDeltaToResponse(
wantFullState bool, wantFullState bool,
res *types.Response, res *types.Response,
) (joinedRoomIDs []string, err error) { ) (joinedRoomIDs []string, err error) {
txn, err := d.DB.BeginTx(ctx, &txReadOnlySnapshot)
if err != nil {
return nil, err
}
succeeded := false succeeded := false
defer func() { txn, done, err := d.getIsolatedTransactionIfAvailable(ctx, &succeeded)
_ = d.Writer.Do(d.DB, txn, func(txn *sql.Tx) error { if err != nil {
sqlutil.EndTransactionWithCheck(txn, &succeeded, &err) return nil, fmt.Errorf("getIsolatedTransactionIfAvailable: %w", err)
return nil }
}) if txn != nil {
}() defer done()
}
stateFilter := gomatrixserverlib.DefaultStateFilter() // TODO: use filter provided in request stateFilter := gomatrixserverlib.DefaultStateFilter() // TODO: use filter provided in request
@ -620,17 +631,14 @@ func (d *Database) getResponseWithPDUsForCompleteSync(
// a consistent view of the database throughout. This includes extracting the sync position. // a consistent view of the database throughout. This includes extracting the sync position.
// This does have the unfortunate side-effect that all the matrixy logic resides in this function, // This does have the unfortunate side-effect that all the matrixy logic resides in this function,
// but it's better to not hide the fact that this is being done in a transaction. // but it's better to not hide the fact that this is being done in a transaction.
txn, err := d.DB.BeginTx(ctx, &txReadOnlySnapshot) succeeded := false
txn, done, err := d.getIsolatedTransactionIfAvailable(ctx, &succeeded)
if err != nil { if err != nil {
return return
} }
succeeded := false if txn != nil {
defer func() { defer done()
_ = d.Writer.Do(d.DB, txn, func(txn *sql.Tx) error { }
sqlutil.EndTransactionWithCheck(txn, &succeeded, &err)
return nil
})
}()
// Get the current sync position which we will base the sync response on. // Get the current sync position which we will base the sync response on.
toPos, err = d.syncPositionTx(ctx, txn) toPos, err = d.syncPositionTx(ctx, txn)

View file

@ -92,17 +92,18 @@ func (d *SyncServerDatasource) prepare() (err error) {
return err return err
} }
d.Database = shared.Database{ d.Database = shared.Database{
DB: d.db, DB: d.db,
Writer: d.writer, TransactionalIsolation: false,
Invites: invites, Writer: d.writer,
AccountData: accountData, Invites: invites,
OutputEvents: events, AccountData: accountData,
BackwardExtremities: bwExtrem, OutputEvents: events,
CurrentRoomState: roomState, BackwardExtremities: bwExtrem,
Topology: topology, CurrentRoomState: roomState,
Filter: filter, Topology: topology,
SendToDevice: sendToDevice, Filter: filter,
EDUCache: cache.New(), SendToDevice: sendToDevice,
EDUCache: cache.New(),
} }
return nil return nil
} }