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

@ -80,6 +80,7 @@ func NewDatabase(dbProperties *config.DatabaseOptions) (*SyncServerDatasource, e
} }
d.Database = shared.Database{ d.Database = shared.Database{
DB: d.db, DB: d.db,
TransactionalIsolation: true,
Writer: d.writer, Writer: d.writer,
Invites: invites, Invites: invites,
AccountData: accountData, AccountData: accountData,

View file

@ -37,6 +37,7 @@ import (
// 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
TransactionalIsolation bool
Writer sqlutil.Writer Writer sqlutil.Writer
Invites tables.Invites Invites tables.Invites
AccountData tables.AccountData AccountData tables.AccountData
@ -49,6 +50,19 @@ type Database struct {
EDUCache *cache.EDUCache 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.
// Returns a list of events matching the requested IDs found in the database. // Returns a list of events matching the requested IDs found in the database.
// If an event is not found in the database then it will be omitted from the list. // If an event is not found in the database then it will be omitted from the list.
@ -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

@ -93,6 +93,7 @@ func (d *SyncServerDatasource) prepare() (err error) {
} }
d.Database = shared.Database{ d.Database = shared.Database{
DB: d.db, DB: d.db,
TransactionalIsolation: false,
Writer: d.writer, Writer: d.writer,
Invites: invites, Invites: invites,
AccountData: accountData, AccountData: accountData,