mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 18:17:35 +00:00
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:
@@ -2,7 +2,6 @@ package cockroach
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -94,12 +93,7 @@ func (c *Config) Type() string {
|
||||
}
|
||||
|
||||
func (c *Config) Timetravel(d time.Duration) string {
|
||||
// verify that it is at least 1 micro second
|
||||
if d < time.Microsecond {
|
||||
d = time.Microsecond
|
||||
}
|
||||
|
||||
return fmt.Sprintf(" AS OF SYSTEM TIME '-%d µs' ", d.Microseconds())
|
||||
return ""
|
||||
}
|
||||
|
||||
type User struct {
|
||||
|
@@ -1,61 +0,0 @@
|
||||
package cockroach
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestConfig_Timetravel(t *testing.T) {
|
||||
type args struct {
|
||||
d time.Duration
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "no duration",
|
||||
args: args{
|
||||
d: 0,
|
||||
},
|
||||
want: " AS OF SYSTEM TIME '-1 µs' ",
|
||||
},
|
||||
{
|
||||
name: "less than microsecond",
|
||||
args: args{
|
||||
d: 100 * time.Nanosecond,
|
||||
},
|
||||
want: " AS OF SYSTEM TIME '-1 µs' ",
|
||||
},
|
||||
{
|
||||
name: "10 microseconds",
|
||||
args: args{
|
||||
d: 10 * time.Microsecond,
|
||||
},
|
||||
want: " AS OF SYSTEM TIME '-10 µs' ",
|
||||
},
|
||||
{
|
||||
name: "10 milliseconds",
|
||||
args: args{
|
||||
d: 10 * time.Millisecond,
|
||||
},
|
||||
want: " AS OF SYSTEM TIME '-10000 µs' ",
|
||||
},
|
||||
{
|
||||
name: "1 second",
|
||||
args: args{
|
||||
d: 1 * time.Second,
|
||||
},
|
||||
want: " AS OF SYSTEM TIME '-1000000 µs' ",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
c := &Config{}
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := c.Timetravel(tt.args.d); got != tt.want {
|
||||
t.Errorf("Config.Timetravel() = %q, want %q", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,9 +1,12 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"reflect"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
|
||||
_ "github.com/zitadel/zitadel/internal/database/cockroach"
|
||||
"github.com/zitadel/zitadel/internal/database/dialect"
|
||||
_ "github.com/zitadel/zitadel/internal/database/postgres"
|
||||
@@ -24,6 +27,66 @@ type DB struct {
|
||||
dialect.Database
|
||||
}
|
||||
|
||||
func (db *DB) Query(scan func(*sql.Rows) error, query string, args ...any) error {
|
||||
return db.QueryContext(context.Background(), scan, query, args...)
|
||||
}
|
||||
|
||||
func (db *DB) QueryContext(ctx context.Context, scan func(rows *sql.Rows) error, query string, args ...any) (err error) {
|
||||
tx, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
rollbackErr := tx.Rollback()
|
||||
logging.OnError(rollbackErr).Info("commit of read only transaction failed")
|
||||
return
|
||||
}
|
||||
err = tx.Commit()
|
||||
}()
|
||||
|
||||
rows, err := 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 (db *DB) QueryRow(scan func(*sql.Row) error, query string, args ...any) (err error) {
|
||||
return db.QueryRowContext(context.Background(), scan, query, args...)
|
||||
}
|
||||
|
||||
func (db *DB) QueryRowContext(ctx context.Context, scan func(row *sql.Row) error, query string, args ...any) (err error) {
|
||||
tx, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
rollbackErr := tx.Rollback()
|
||||
logging.OnError(rollbackErr).Info("commit of read only transaction failed")
|
||||
return
|
||||
}
|
||||
err = tx.Commit()
|
||||
}()
|
||||
|
||||
row := tx.QueryRowContext(ctx, query, args...)
|
||||
|
||||
err = scan(row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return row.Err()
|
||||
}
|
||||
|
||||
func Connect(config Config, useAdmin bool) (*DB, error) {
|
||||
client, err := config.connector.Connect(useAdmin)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user