mirror of
https://github.com/rancher/steve.git
synced 2025-08-31 15:11:31 +00:00
sql: use a closure to wrap transactions (#469)
This introduces the a `WithTransaction` function, which is then used for all transactional work in Steve. Because `WithTransaction` takes care of all `Begin`s, `Commit`s and `Rollback`s, it eliminates the problem where forgotten open transactions can block all other operations (with long stalling and `SQLITE_BUSY` errors). This also: - merges together the disparate `DBClient` interfaces in one only `db.Client` interface with one unexported non-test implementation. I found this much easier to follow - refactors the transaction package in order to make it as minimal as possible, and as close to the wrapped `sql.Tx` and `sql.Stmt` functions as possible, in order to reduce cognitive load when working with this part of the codebase - simplifies tests accordingly - adds a couple of known files to `.gitignore` Credits to @tomleb for suggesting the approach: https://github.com/rancher/lasso/pull/121#pullrequestreview-2515872507
This commit is contained in:
@@ -62,45 +62,34 @@ type Indexer struct {
|
||||
var _ cache.Indexer = (*Indexer)(nil)
|
||||
|
||||
type Store interface {
|
||||
DBClient
|
||||
db.Client
|
||||
cache.Store
|
||||
|
||||
GetByKey(key string) (item any, exists bool, err error)
|
||||
GetName() string
|
||||
RegisterAfterUpsert(f func(key string, obj any, tx db.TXClient) error)
|
||||
RegisterAfterDelete(f func(key string, tx db.TXClient) error)
|
||||
RegisterAfterUpsert(f func(key string, obj any, tx transaction.Client) error)
|
||||
RegisterAfterDelete(f func(key string, tx transaction.Client) error)
|
||||
GetShouldEncrypt() bool
|
||||
GetType() reflect.Type
|
||||
}
|
||||
|
||||
type DBClient interface {
|
||||
BeginTx(ctx context.Context, forWriting bool) (db.TXClient, error)
|
||||
QueryForRows(ctx context.Context, stmt transaction.Stmt, params ...any) (*sql.Rows, error)
|
||||
ReadObjects(rows db.Rows, typ reflect.Type, shouldDecrypt bool) ([]any, error)
|
||||
ReadStrings(rows db.Rows) ([]string, error)
|
||||
ReadInt(rows db.Rows) (int, error)
|
||||
Prepare(stmt string) *sql.Stmt
|
||||
CloseStmt(stmt db.Closable) error
|
||||
}
|
||||
|
||||
// NewIndexer returns a cache.Indexer backed by SQLite for objects of the given example type
|
||||
func NewIndexer(indexers cache.Indexers, s Store) (*Indexer, error) {
|
||||
tx, err := s.BeginTx(context.Background(), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbName := db.Sanitize(s.GetName())
|
||||
createTableQuery := fmt.Sprintf(createTableFmt, dbName)
|
||||
err = tx.Exec(createTableQuery)
|
||||
if err != nil {
|
||||
return nil, &db.QueryError{QueryString: createTableQuery, Err: err}
|
||||
}
|
||||
createIndexQuery := fmt.Sprintf(createIndexFmt, dbName)
|
||||
err = tx.Exec(createIndexQuery)
|
||||
if err != nil {
|
||||
return nil, &db.QueryError{QueryString: createIndexQuery, Err: err}
|
||||
}
|
||||
err = tx.Commit()
|
||||
|
||||
err := s.WithTransaction(context.Background(), true, func(tx transaction.Client) error {
|
||||
createTableQuery := fmt.Sprintf(createTableFmt, dbName)
|
||||
_, err := tx.Exec(createTableQuery)
|
||||
if err != nil {
|
||||
return &db.QueryError{QueryString: createTableQuery, Err: err}
|
||||
}
|
||||
createIndexQuery := fmt.Sprintf(createIndexFmt, dbName)
|
||||
_, err = tx.Exec(createIndexQuery)
|
||||
if err != nil {
|
||||
return &db.QueryError{QueryString: createIndexQuery, Err: err}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -129,9 +118,9 @@ func NewIndexer(indexers cache.Indexers, s Store) (*Indexer, error) {
|
||||
/* Core methods */
|
||||
|
||||
// AfterUpsert updates indices of an object
|
||||
func (i *Indexer) AfterUpsert(key string, obj any, tx db.TXClient) error {
|
||||
func (i *Indexer) AfterUpsert(key string, obj any, tx transaction.Client) error {
|
||||
// delete all
|
||||
err := tx.StmtExec(tx.Stmt(i.deleteIndicesStmt), key)
|
||||
_, err := tx.Stmt(i.deleteIndicesStmt).Exec(key)
|
||||
if err != nil {
|
||||
return &db.QueryError{QueryString: i.deleteIndicesQuery, Err: err}
|
||||
}
|
||||
@@ -146,7 +135,7 @@ func (i *Indexer) AfterUpsert(key string, obj any, tx db.TXClient) error {
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
err = tx.StmtExec(tx.Stmt(i.addIndexStmt), indexName, value, key)
|
||||
_, err = tx.Stmt(i.addIndexStmt).Exec(indexName, value, key)
|
||||
if err != nil {
|
||||
return &db.QueryError{QueryString: i.addIndexQuery, Err: err}
|
||||
}
|
||||
|
Reference in New Issue
Block a user