1
0
mirror of https://github.com/rancher/steve.git synced 2025-09-05 17:30:39 +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:
Silvio Moioli
2025-02-05 10:05:52 +01:00
committed by GitHub
parent 6a46a1e091
commit 772dc7577e
28 changed files with 1543 additions and 2059 deletions

View File

@@ -56,21 +56,6 @@ func (mr *MockStoreMockRecorder) Add(arg0 any) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockStore)(nil).Add), arg0)
}
// BeginTx mocks base method.
func (m *MockStore) BeginTx(arg0 context.Context, arg1 bool) (db.TXClient, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "BeginTx", arg0, arg1)
ret0, _ := ret[0].(db.TXClient)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// BeginTx indicates an expected call of BeginTx.
func (mr *MockStoreMockRecorder) BeginTx(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTx", reflect.TypeOf((*MockStore)(nil).BeginTx), arg0, arg1)
}
// CloseStmt mocks base method.
func (m *MockStore) CloseStmt(arg0 db.Closable) error {
m.ctrl.T.Helper()
@@ -201,6 +186,20 @@ func (mr *MockStoreMockRecorder) ListKeys() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListKeys", reflect.TypeOf((*MockStore)(nil).ListKeys))
}
// NewConnection mocks base method.
func (m *MockStore) NewConnection() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "NewConnection")
ret0, _ := ret[0].(error)
return ret0
}
// NewConnection indicates an expected call of NewConnection.
func (mr *MockStoreMockRecorder) NewConnection() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewConnection", reflect.TypeOf((*MockStore)(nil).NewConnection))
}
// Prepare mocks base method.
func (m *MockStore) Prepare(arg0 string) *sql.Stmt {
m.ctrl.T.Helper()
@@ -281,7 +280,7 @@ func (mr *MockStoreMockRecorder) ReadStrings(arg0 any) *gomock.Call {
}
// RegisterAfterDelete mocks base method.
func (m *MockStore) RegisterAfterDelete(arg0 func(string, db.TXClient) error) {
func (m *MockStore) RegisterAfterDelete(arg0 func(string, transaction.Client) error) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "RegisterAfterDelete", arg0)
}
@@ -293,7 +292,7 @@ func (mr *MockStoreMockRecorder) RegisterAfterDelete(arg0 any) *gomock.Call {
}
// RegisterAfterUpsert mocks base method.
func (m *MockStore) RegisterAfterUpsert(arg0 func(string, any, db.TXClient) error) {
func (m *MockStore) RegisterAfterUpsert(arg0 func(string, any, transaction.Client) error) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "RegisterAfterUpsert", arg0)
}
@@ -345,3 +344,31 @@ func (mr *MockStoreMockRecorder) Update(arg0 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockStore)(nil).Update), arg0)
}
// Upsert mocks base method.
func (m *MockStore) Upsert(arg0 transaction.Client, arg1 *sql.Stmt, arg2 string, arg3 any, arg4 bool) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Upsert", arg0, arg1, arg2, arg3, arg4)
ret0, _ := ret[0].(error)
return ret0
}
// Upsert indicates an expected call of Upsert.
func (mr *MockStoreMockRecorder) Upsert(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockStore)(nil).Upsert), arg0, arg1, arg2, arg3, arg4)
}
// WithTransaction mocks base method.
func (m *MockStore) WithTransaction(arg0 context.Context, arg1 bool, arg2 db.WithTransactionFunction) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "WithTransaction", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// WithTransaction indicates an expected call of WithTransaction.
func (mr *MockStoreMockRecorder) WithTransaction(arg0, arg1, arg2 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WithTransaction", reflect.TypeOf((*MockStore)(nil).WithTransaction), arg0, arg1, arg2)
}