mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
feat(storage): read only transactions (#6417)
feat(storage): read only transactions for queries (#6415) * fix: tests * bastle wie en grosse * fix(database): scan as callback * fix tests * fix merge failures * remove as of system time * refactor: remove unused test * refacotr: remove unused lines
This commit is contained in:
@@ -161,13 +161,18 @@ func (db *CRDB) Push(ctx context.Context, events []*repository.Event, uniqueCons
|
||||
var instanceRegexp = regexp.MustCompile(`eventstore\.i_[0-9a-zA-Z]{1,}_seq`)
|
||||
|
||||
func (db *CRDB) CreateInstance(ctx context.Context, instanceID string) error {
|
||||
row := db.QueryRowContext(ctx, "SELECT CONCAT('eventstore.i_', $1::TEXT, '_seq')", instanceID)
|
||||
if row.Err() != nil {
|
||||
return caos_errs.ThrowInvalidArgument(row.Err(), "SQL-7gtFA", "Errors.InvalidArgument")
|
||||
}
|
||||
var sequenceName string
|
||||
if err := row.Scan(&sequenceName); err != nil || !instanceRegexp.MatchString(sequenceName) {
|
||||
return caos_errs.ThrowInvalidArgument(err, "SQL-7gtFA", "Errors.InvalidArgument")
|
||||
err := db.QueryRowContext(ctx,
|
||||
func(row *sql.Row) error {
|
||||
if err := row.Scan(&sequenceName); err != nil || !instanceRegexp.MatchString(sequenceName) {
|
||||
return caos_errs.ThrowInvalidArgument(err, "SQL-7gtFA", "Errors.InvalidArgument")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"SELECT CONCAT('eventstore.i_', $1::TEXT, '_seq')", instanceID,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := db.ExecContext(ctx, "CREATE SEQUENCE "+sequenceName); err != nil {
|
||||
@@ -220,9 +225,9 @@ func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueC
|
||||
}
|
||||
|
||||
// Filter returns all events matching the given search query
|
||||
func (db *CRDB) Filter(ctx context.Context, searchQuery *repository.SearchQuery) (events []*repository.Event, err error) {
|
||||
func (crdb *CRDB) Filter(ctx context.Context, searchQuery *repository.SearchQuery) (events []*repository.Event, err error) {
|
||||
events = []*repository.Event{}
|
||||
err = query(ctx, db, searchQuery, &events)
|
||||
err = query(ctx, crdb, searchQuery, &events)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -250,8 +255,8 @@ func (db *CRDB) InstanceIDs(ctx context.Context, searchQuery *repository.SearchQ
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (db *CRDB) db() *sql.DB {
|
||||
return db.DB.DB
|
||||
func (db *CRDB) db() *database.DB {
|
||||
return db.DB
|
||||
}
|
||||
|
||||
func (db *CRDB) orderByEventSequence(desc bool) string {
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/call"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/database/dialect"
|
||||
z_errors "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
@@ -24,13 +25,33 @@ type querier interface {
|
||||
eventQuery() string
|
||||
maxSequenceQuery() string
|
||||
instanceIDsQuery() string
|
||||
db() *sql.DB
|
||||
db() *database.DB
|
||||
orderByEventSequence(desc bool) string
|
||||
dialect.Database
|
||||
}
|
||||
|
||||
type scan func(dest ...interface{}) error
|
||||
|
||||
type tx struct {
|
||||
*sql.Tx
|
||||
}
|
||||
|
||||
func (t *tx) QueryContext(ctx context.Context, scan func(rows *sql.Rows) error, query string, args ...any) error {
|
||||
rows, err := t.Tx.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
closeErr := rows.Close()
|
||||
logging.OnError(closeErr).Info("rows.Close failed")
|
||||
}()
|
||||
|
||||
if err = scan(rows); err != nil {
|
||||
return err
|
||||
}
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func query(ctx context.Context, criteria querier, searchQuery *repository.SearchQuery, dest interface{}) error {
|
||||
query, rowScanner := prepareColumns(criteria, searchQuery.Columns)
|
||||
where, values := prepareCondition(criteria, searchQuery.Filters)
|
||||
@@ -56,26 +77,27 @@ func query(ctx context.Context, criteria querier, searchQuery *repository.Search
|
||||
query = criteria.placeholder(query)
|
||||
|
||||
var contextQuerier interface {
|
||||
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||
QueryContext(context.Context, func(rows *sql.Rows) error, string, ...interface{}) error
|
||||
}
|
||||
contextQuerier = criteria.db()
|
||||
if searchQuery.Tx != nil {
|
||||
contextQuerier = searchQuery.Tx
|
||||
contextQuerier = &tx{Tx: searchQuery.Tx}
|
||||
}
|
||||
|
||||
rows, err := contextQuerier.QueryContext(ctx, query, values...)
|
||||
err := contextQuerier.QueryContext(ctx,
|
||||
func(rows *sql.Rows) error {
|
||||
for rows.Next() {
|
||||
err := rowScanner(rows.Scan, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, query, values...)
|
||||
if err != nil {
|
||||
logging.New().WithError(err).Info("query failed")
|
||||
return z_errors.ThrowInternal(err, "SQL-KyeAx", "unable to filter events")
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
err = rowScanner(rows.Scan, dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -741,7 +741,7 @@ func Test_query_events_mocked(t *testing.T) {
|
||||
},
|
||||
},
|
||||
fields: fields{
|
||||
mock: newMockClient(t).expectQuery(t,
|
||||
mock: newMockClient(t).expectQueryScanErr(t,
|
||||
`SELECT creation_date, event_type, event_sequence, previous_aggregate_sequence, previous_aggregate_type_sequence, event_data, editor_service, editor_user, resource_owner, instance_id, aggregate_type, aggregate_id, aggregate_version FROM eventstore.events WHERE \( aggregate_type = \$1 \) ORDER BY creation_date DESC, event_sequence DESC`,
|
||||
[]driver.Value{repository.AggregateType("user")},
|
||||
&repository.Event{Sequence: 100}),
|
||||
@@ -853,7 +853,21 @@ type dbMock struct {
|
||||
}
|
||||
|
||||
func (m *dbMock) expectQuery(t *testing.T, expectedQuery string, args []driver.Value, events ...*repository.Event) *dbMock {
|
||||
m.mock.ExpectBegin()
|
||||
query := m.mock.ExpectQuery(expectedQuery).WithArgs(args...)
|
||||
m.mock.ExpectCommit()
|
||||
rows := sqlmock.NewRows([]string{"event_sequence"})
|
||||
for _, event := range events {
|
||||
rows = rows.AddRow(event.Sequence)
|
||||
}
|
||||
query.WillReturnRows(rows).RowsWillBeClosed()
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *dbMock) expectQueryScanErr(t *testing.T, expectedQuery string, args []driver.Value, events ...*repository.Event) *dbMock {
|
||||
m.mock.ExpectBegin()
|
||||
query := m.mock.ExpectQuery(expectedQuery).WithArgs(args...)
|
||||
m.mock.ExpectRollback()
|
||||
rows := sqlmock.NewRows([]string{"event_sequence"})
|
||||
for _, event := range events {
|
||||
rows = rows.AddRow(event.Sequence)
|
||||
@@ -863,6 +877,7 @@ func (m *dbMock) expectQuery(t *testing.T, expectedQuery string, args []driver.V
|
||||
}
|
||||
|
||||
func (m *dbMock) expectQueryErr(t *testing.T, expectedQuery string, args []driver.Value, err error) *dbMock {
|
||||
m.mock.ExpectBegin()
|
||||
m.mock.ExpectQuery(expectedQuery).WithArgs(args...).WillReturnError(err)
|
||||
return m
|
||||
}
|
||||
|
Reference in New Issue
Block a user