From a62ba06e085adc65090299dddd6d7eacef71201c Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Tue, 8 Oct 2024 11:25:41 -0700 Subject: [PATCH 1/9] :bug: Support reused connection for better performance. Signed-off-by: Jeff Ortel --- database/driver.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/database/driver.go b/database/driver.go index 14bfbb1f..bcbcf645 100644 --- a/database/driver.go +++ b/database/driver.go @@ -57,12 +57,14 @@ func (c *Conn) Ping(ctx context.Context) (err error) { } func (c *Conn) ResetSession(ctx context.Context) (err error) { + c.release() if p, cast := c.wrapped.(driver.SessionResetter); cast { err = p.ResetSession(ctx) } return } func (c *Conn) IsValid() (b bool) { + b = true if p, cast := c.wrapped.(driver.Validator); cast { b = p.IsValid() } From 8a3e80cc0e4064608bb368fd42e61048f034f66f Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Tue, 8 Oct 2024 15:11:46 -0700 Subject: [PATCH 2/9] add subobjects Signed-off-by: Jeff Ortel --- database/db_test.go | 34 ++++++++++ database/driver.go | 156 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 172 insertions(+), 18 deletions(-) diff --git a/database/db_test.go b/database/db_test.go index dde8c6fa..f490186e 100644 --- a/database/db_test.go +++ b/database/db_test.go @@ -13,6 +13,40 @@ import ( var N, _ = env.GetInt("TEST_CONCURRENT", 10) +func TestDriver(t *testing.T) { + pid := os.Getpid() + Settings.DB.Path = fmt.Sprintf("/tmp/driver-%d.db", pid) + defer func() { + _ = os.Remove(Settings.DB.Path) + }() + db, err := Open(true) + if err != nil { + panic(err) + } + key := "driver" + m := &model.Setting{Key: key, Value: "Test"} + // insert. + err = db.Create(m).Error + if err != nil { + panic(err) + } + // update + err = db.Save(m).Error + if err != nil { + panic(err) + } + // select + err = db.First(m, m.ID).Error + if err != nil { + panic(err) + } + // delete + err = db.Delete(m).Error + if err != nil { + panic(err) + } +} + func TestConcurrent(t *testing.T) { pid := os.Getpid() Settings.DB.Path = fmt.Sprintf("/tmp/concurrent-%d.db", pid) diff --git a/database/driver.go b/database/driver.go index bcbcf645..ca092d55 100644 --- a/database/driver.go +++ b/database/driver.go @@ -57,7 +57,7 @@ func (c *Conn) Ping(ctx context.Context) (err error) { } func (c *Conn) ResetSession(ctx context.Context) (err error) { - c.release() + defer c.release() if p, cast := c.wrapped.(driver.SessionResetter); cast { err = p.ResetSession(ctx) } @@ -72,6 +72,7 @@ func (c *Conn) IsValid() (b bool) { } func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) { + defer c.release() if c.needsMutex(query) { c.acquire() } @@ -81,20 +82,26 @@ func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.Nam return } -func (c *Conn) PrepareContext(ctx context.Context, query string) (s driver.Stmt, err error) { +func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { + defer c.release() if c.needsMutex(query) { c.acquire() } - if p, cast := c.wrapped.(driver.ConnPrepareContext); cast { - s, err = p.PrepareContext(ctx, query) + if p, cast := c.wrapped.(driver.ExecerContext); cast { + r, err = p.ExecContext(ctx, query, args) } return } -func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { +func (c *Conn) Begin() (tx driver.Tx, err error) { c.acquire() - if p, cast := c.wrapped.(driver.ExecerContext); cast { - r, err = p.ExecContext(ctx, query, args) + tx, err = c.wrapped.Begin() + if err != nil { + return + } + tx = &Tx{ + conn: c, + wrapped: tx, } return } @@ -106,29 +113,44 @@ func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx } else { tx, err = c.wrapped.Begin() } + tx = &Tx{ + conn: c, + wrapped: tx, + } return } -func (c *Conn) Prepare(query string) (s driver.Stmt, err error) { +func (c *Conn) Prepare(query string) (stmt driver.Stmt, err error) { if c.needsMutex(query) { c.acquire() } - s, err = c.wrapped.Prepare(query) + stmt, err = c.wrapped.Prepare(query) + stmt = &Stmt{ + conn: c, + wrapped: stmt, + } return } -func (c *Conn) Close() (err error) { - err = c.wrapped.Close() - c.release() +func (c *Conn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { + if c.needsMutex(query) { + c.acquire() + } + if p, cast := c.wrapped.(driver.ConnPrepareContext); cast { + stmt, err = p.PrepareContext(ctx, query) + } else { + stmt, err = c.Prepare(query) + } + stmt = &Stmt{ + conn: c, + wrapped: stmt, + } return } -func (c *Conn) Begin() (tx driver.Tx, err error) { - c.acquire() - tx, err = c.wrapped.Begin() - if err != nil { - return - } +func (c *Conn) Close() (err error) { + err = c.wrapped.Close() + c.release() return } @@ -159,3 +181,101 @@ func (c *Conn) release() { c.hasMutex = false } } + +type Stmt struct { + wrapped driver.Stmt + conn *Conn +} + +func (s *Stmt) Close() (err error) { + defer s.conn.release() + err = s.wrapped.Close() + return +} + +func (s *Stmt) NumInput() (n int) { + n = s.wrapped.NumInput() + return +} +func (s *Stmt) Exec(args []driver.Value) (r driver.Result, err error) { + defer s.conn.release() + r, err = s.wrapped.Exec(args) + return +} + +func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { + defer s.conn.release() + if p, cast := s.wrapped.(driver.StmtExecContext); cast { + r, err = p.ExecContext(ctx, args) + } else { + r, err = s.Exec(s.values(args)) + } + return +} + +func (s *Stmt) Query(args []driver.Value) (r driver.Rows, err error) { + r, err = s.wrapped.Query(args) + r = &Rows{ + conn: s.conn, + wrapped: r, + } + return +} + +func (s *Stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (r driver.Rows, err error) { + if p, cast := s.wrapped.(driver.StmtQueryContext); cast { + r, err = p.QueryContext(ctx, args) + } else { + r, err = s.Query(s.values(args)) + } + r = &Rows{ + conn: s.conn, + wrapped: r, + } + return +} + +func (s *Stmt) values(named []driver.NamedValue) (out []driver.Value) { + for i := range named { + out = append(out, named[i].Value) + } + return +} + +type Tx struct { + wrapped driver.Tx + conn *Conn +} + +func (t *Tx) Commit() (err error) { + defer t.conn.release() + err = t.wrapped.Commit() + return +} + +func (t *Tx) Rollback() (err error) { + defer t.conn.release() + err = t.wrapped.Rollback() + return +} + +type Rows struct { + conn *Conn + wrapped driver.Rows +} + +func (r *Rows) Columns() (s []string) { + s = r.wrapped.Columns() + return +} + +func (r *Rows) Close() (err error) { + defer r.conn.release() + err = r.wrapped.Close() + return +} + +func (r *Rows) Next(object []driver.Value) (err error) { + err = r.wrapped.Next(object) + return +} From 9af9450334954845594a313bfb01ff551409b1cb Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Wed, 9 Oct 2024 09:13:13 -0700 Subject: [PATCH 3/9] checkpoint Signed-off-by: Jeff Ortel --- Makefile | 3 ++ database/driver.go | 100 +++++++++++++++++++++++++++++---------------- 2 files changed, 68 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index b092ad5f..77de6b71 100644 --- a/Makefile +++ b/Makefile @@ -141,6 +141,9 @@ endif test: go test -count=1 -v $(shell go list ./... | grep -v "hub/test") +test-db: + go test -count=1 -timeout=6h -v ./database... + # Run Hub REST API tests. test-api: HUB_BASE_URL=$(HUB_BASE_URL) go test -count=1 -p=1 -v -failfast ./test/api/... diff --git a/database/driver.go b/database/driver.go index ca092d55..7f60962d 100644 --- a/database/driver.go +++ b/database/driver.go @@ -9,12 +9,16 @@ import ( "github.com/mattn/go-sqlite3" ) +// Driver is a wrapper around the SQLite driver. +// The purpose is to prevent database locked errors using +// a mutex around write operations. type Driver struct { mutex sync.Mutex wrapped driver.Driver dsn string } +// Open a connection. func (d *Driver) Open(dsn string) (conn driver.Conn, err error) { d.wrapped = &sqlite3.SQLiteDriver{} conn, err = d.wrapped.Open(dsn) @@ -28,27 +32,33 @@ func (d *Driver) Open(dsn string) (conn driver.Conn, err error) { return } +// OpenConnector opens a connection. func (d *Driver) OpenConnector(dsn string) (dc driver.Connector, err error) { d.dsn = dsn dc = d return } +// Connect opens a connection. func (d *Driver) Connect(context.Context) (conn driver.Conn, err error) { conn, err = d.Open(d.dsn) return } +// Driver returns the underlying driver. func (d *Driver) Driver() driver.Driver { return d } +// Conn is a DB connection. type Conn struct { mutex *sync.Mutex wrapped driver.Conn hasMutex bool + hasTx bool } +// Ping the DB. func (c *Conn) Ping(ctx context.Context) (err error) { if p, cast := c.wrapped.(driver.Pinger); cast { err = p.Ping(ctx) @@ -56,13 +66,22 @@ func (c *Conn) Ping(ctx context.Context) (err error) { return } +// ResetSession reset the connection. +// - Reset the Tx. +// - Release the mutex. func (c *Conn) ResetSession(ctx context.Context) (err error) { - defer c.release() + defer func() { + c.hasTx = false + c.release() + }() if p, cast := c.wrapped.(driver.SessionResetter); cast { err = p.ResetSession(ctx) } return } + +// IsValid returns true when the connection is valid. +// When true, the connection may be reused by the sql package. func (c *Conn) IsValid() (b bool) { b = true if p, cast := c.wrapped.(driver.Validator); cast { @@ -71,6 +90,7 @@ func (c *Conn) IsValid() (b bool) { return } +// QueryContext execute a query with context. func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) { defer c.release() if c.needsMutex(query) { @@ -82,6 +102,7 @@ func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.Nam return } +// ExecContext executes an SQL/DDL statement with context. func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { defer c.release() if c.needsMutex(query) { @@ -93,6 +114,7 @@ func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.Name return } +// Begin a transaction. func (c *Conn) Begin() (tx driver.Tx, err error) { c.acquire() tx, err = c.wrapped.Begin() @@ -103,9 +125,11 @@ func (c *Conn) Begin() (tx driver.Tx, err error) { conn: c, wrapped: tx, } + c.hasTx = true return } +// BeginTx begins a transaction. func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx, err error) { c.acquire() if p, cast := c.wrapped.(driver.ConnBeginTx); cast { @@ -117,9 +141,11 @@ func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx conn: c, wrapped: tx, } + c.hasTx = true return } +// Prepare a statement. func (c *Conn) Prepare(query string) (stmt driver.Stmt, err error) { if c.needsMutex(query) { c.acquire() @@ -128,10 +154,12 @@ func (c *Conn) Prepare(query string) (stmt driver.Stmt, err error) { stmt = &Stmt{ conn: c, wrapped: stmt, + query: query, } return } +// PrepareContext prepares a statement with context. func (c *Conn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { if c.needsMutex(query) { c.acquire() @@ -144,16 +172,20 @@ func (c *Conn) PrepareContext(ctx context.Context, query string) (stmt driver.St stmt = &Stmt{ conn: c, wrapped: stmt, + query: query, } return } +// Close the connection. func (c *Conn) Close() (err error) { err = c.wrapped.Close() + c.hasMutex = false c.release() return } +// needsMutex returns true when the query should is a write operation. func (c *Conn) needsMutex(query string) (matched bool) { if query == "" { return @@ -168,6 +200,9 @@ func (c *Conn) needsMutex(query string) (matched bool) { return } +// acquire the mutex. +// Since Locks are not reentrant, the mutex is acquired +// only if this connection has not already acquired it. func (c *Conn) acquire() { if !c.hasMutex { c.mutex.Lock() @@ -175,36 +210,45 @@ func (c *Conn) acquire() { } } +// release the mutex. +// Released only when: +// - This connection has acquired it +// - Not in a transaction. func (c *Conn) release() { - if c.hasMutex { + if c.hasMutex && !c.hasTx { c.mutex.Unlock() c.hasMutex = false } } +// Stmt is a SQL/DDL statement. type Stmt struct { wrapped driver.Stmt conn *Conn + query string } +// Close the statement. func (s *Stmt) Close() (err error) { defer s.conn.release() err = s.wrapped.Close() return } +// NumInput returns the number of (query) input parameters. func (s *Stmt) NumInput() (n int) { n = s.wrapped.NumInput() return } + +// Exec executes the statement. func (s *Stmt) Exec(args []driver.Value) (r driver.Result, err error) { - defer s.conn.release() r, err = s.wrapped.Exec(args) return } +// ExecContext executes the statement with context. func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { - defer s.conn.release() if p, cast := s.wrapped.(driver.StmtExecContext); cast { r, err = p.ExecContext(ctx, args) } else { @@ -213,28 +257,23 @@ func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r dri return } +// Query executes a query. func (s *Stmt) Query(args []driver.Value) (r driver.Rows, err error) { r, err = s.wrapped.Query(args) - r = &Rows{ - conn: s.conn, - wrapped: r, - } return } +// QueryContext executes a query. func (s *Stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (r driver.Rows, err error) { if p, cast := s.wrapped.(driver.StmtQueryContext); cast { r, err = p.QueryContext(ctx, args) } else { r, err = s.Query(s.values(args)) } - r = &Rows{ - conn: s.conn, - wrapped: r, - } return } +// values converts named-values to values. func (s *Stmt) values(named []driver.NamedValue) (out []driver.Value) { for i := range named { out = append(out, named[i].Value) @@ -242,40 +281,31 @@ func (s *Stmt) values(named []driver.NamedValue) (out []driver.Value) { return } +// Tx is a transaction. type Tx struct { wrapped driver.Tx conn *Conn } +// Commit the transaction. +// Releases the mutex. func (t *Tx) Commit() (err error) { - defer t.conn.release() + defer func() { + t.conn.hasTx = false + t.conn.release() + }() err = t.wrapped.Commit() return } +// +// Rollback the transaction. +// Releases the mutex. func (t *Tx) Rollback() (err error) { - defer t.conn.release() + defer func() { + t.conn.hasTx = false + t.conn.release() + }() err = t.wrapped.Rollback() return } - -type Rows struct { - conn *Conn - wrapped driver.Rows -} - -func (r *Rows) Columns() (s []string) { - s = r.wrapped.Columns() - return -} - -func (r *Rows) Close() (err error) { - defer r.conn.release() - err = r.wrapped.Close() - return -} - -func (r *Rows) Next(object []driver.Value) (err error) { - err = r.wrapped.Next(object) - return -} From cb4ebeeb50a99fb39eddfb74c27371691c60687e Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Wed, 9 Oct 2024 15:00:12 -0700 Subject: [PATCH 4/9] mutex stmt execution. Signed-off-by: Jeff Ortel --- database/driver.go | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/database/driver.go b/database/driver.go index 7f60962d..e7ecb442 100644 --- a/database/driver.go +++ b/database/driver.go @@ -147,9 +147,6 @@ func (c *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (tx driver.Tx // Prepare a statement. func (c *Conn) Prepare(query string) (stmt driver.Stmt, err error) { - if c.needsMutex(query) { - c.acquire() - } stmt, err = c.wrapped.Prepare(query) stmt = &Stmt{ conn: c, @@ -161,9 +158,6 @@ func (c *Conn) Prepare(query string) (stmt driver.Stmt, err error) { // PrepareContext prepares a statement with context. func (c *Conn) PrepareContext(ctx context.Context, query string) (stmt driver.Stmt, err error) { - if c.needsMutex(query) { - c.acquire() - } if p, cast := c.wrapped.(driver.ConnPrepareContext); cast { stmt, err = p.PrepareContext(ctx, query) } else { @@ -230,7 +224,6 @@ type Stmt struct { // Close the statement. func (s *Stmt) Close() (err error) { - defer s.conn.release() err = s.wrapped.Close() return } @@ -243,12 +236,20 @@ func (s *Stmt) NumInput() (n int) { // Exec executes the statement. func (s *Stmt) Exec(args []driver.Value) (r driver.Result, err error) { + if s.needsMutex() { + s.conn.acquire() + defer s.conn.release() + } r, err = s.wrapped.Exec(args) return } // ExecContext executes the statement with context. func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { + if s.needsMutex() { + s.conn.acquire() + defer s.conn.release() + } if p, cast := s.wrapped.(driver.StmtExecContext); cast { r, err = p.ExecContext(ctx, args) } else { @@ -259,12 +260,20 @@ func (s *Stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (r dri // Query executes a query. func (s *Stmt) Query(args []driver.Value) (r driver.Rows, err error) { + if s.needsMutex() { + s.conn.acquire() + defer s.conn.release() + } r, err = s.wrapped.Query(args) return } // QueryContext executes a query. func (s *Stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (r driver.Rows, err error) { + if s.needsMutex() { + s.conn.acquire() + defer s.conn.release() + } if p, cast := s.wrapped.(driver.StmtQueryContext); cast { r, err = p.QueryContext(ctx, args) } else { @@ -281,6 +290,12 @@ func (s *Stmt) values(named []driver.NamedValue) (out []driver.Value) { return } +// needsMutex returns true when the query should is a write operation. +func (s *Stmt) needsMutex() (matched bool) { + matched = s.conn.needsMutex(s.query) + return +} + // Tx is a transaction. type Tx struct { wrapped driver.Tx From 8bb4d77e54aadb78a2e29e8694125102eafd7944 Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Wed, 9 Oct 2024 15:04:09 -0700 Subject: [PATCH 5/9] mutex released when needed. Signed-off-by: Jeff Ortel --- database/driver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/driver.go b/database/driver.go index e7ecb442..7d22ae6c 100644 --- a/database/driver.go +++ b/database/driver.go @@ -92,9 +92,9 @@ func (c *Conn) IsValid() (b bool) { // QueryContext execute a query with context. func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) { - defer c.release() if c.needsMutex(query) { c.acquire() + defer c.release() } if p, cast := c.wrapped.(driver.QueryerContext); cast { r, err = p.QueryContext(ctx, query, args) @@ -104,9 +104,9 @@ func (c *Conn) QueryContext(ctx context.Context, query string, args []driver.Nam // ExecContext executes an SQL/DDL statement with context. func (c *Conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { - defer c.release() if c.needsMutex(query) { c.acquire() + defer c.release() } if p, cast := c.wrapped.(driver.ExecerContext); cast { r, err = p.ExecContext(ctx, query, args) From a6cc2f04c1be46c75bcf51f6a313ab0640d7607d Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Wed, 9 Oct 2024 16:47:43 -0700 Subject: [PATCH 6/9] checkpoint Signed-off-by: Jeff Ortel --- database/driver.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/database/driver.go b/database/driver.go index 7d22ae6c..63bbbabd 100644 --- a/database/driver.go +++ b/database/driver.go @@ -215,6 +215,11 @@ func (c *Conn) release() { } } +// endTx report transaction has ended. +func (c *Conn) endTx() { + c.hasTx = false +} + // Stmt is a SQL/DDL statement. type Stmt struct { wrapped driver.Stmt @@ -306,7 +311,7 @@ type Tx struct { // Releases the mutex. func (t *Tx) Commit() (err error) { defer func() { - t.conn.hasTx = false + t.conn.endTx() t.conn.release() }() err = t.wrapped.Commit() @@ -318,7 +323,7 @@ func (t *Tx) Commit() (err error) { // Releases the mutex. func (t *Tx) Rollback() (err error) { defer func() { - t.conn.hasTx = false + t.conn.endTx() t.conn.release() }() err = t.wrapped.Rollback() From d51f37d2fddee8349df0d26a8a5a4bce3e6a48ff Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Thu, 10 Oct 2024 12:06:26 -0700 Subject: [PATCH 7/9] checkpoint Signed-off-by: Jeff Ortel --- database/db_test.go | 42 ++++++++++++++++++++++++++++++++++++++++-- database/pkg.go | 2 +- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/database/db_test.go b/database/db_test.go index f490186e..3a4590f0 100644 --- a/database/db_test.go +++ b/database/db_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/konveyor/tackle2-hub/api" "github.com/konveyor/tackle2-hub/model" "gorm.io/gorm" "k8s.io/utils/env" @@ -57,12 +58,35 @@ func TestConcurrent(t *testing.T) { if err != nil { panic(err) } + + type A struct { + model.Model + } + + type B struct { + N int + model.Model + A A + AID uint + } + err = db.Migrator().AutoMigrate(&A{}, &B{}) + if err != nil { + panic(err) + } + + a := A{} + err = db.Create(&a).Error + if err != nil { + panic(err) + } + dq := make(chan int, N) for w := 0; w < N; w++ { go func(id int) { fmt.Printf("Started %d\n", id) - for n := 0; n < N*10; n++ { - m := &model.Setting{Key: fmt.Sprintf("key-%d-%d", id, n), Value: n} + for n := 0; n < N*100; n++ { + m := &B{N: n, A: a} + m.CreateUser = "Test" fmt.Printf("(%.4d) CREATE: %.4d\n", id, n) uErr := db.Create(m).Error if uErr != nil { @@ -79,6 +103,20 @@ func TestConcurrent(t *testing.T) { panic(uErr) } } + for i := 0; i < 10; i++ { + fmt.Printf("(%.4d) LIST: %.4d/%.4d\n", id, n, i) + page := api.Page{} + cursor := api.Cursor{} + mx := B{} + dbx := db.Model(mx) + dbx = dbx.Joins("A") + dbx = dbx.Limit(10) + cursor.With(dbx, page) + for cursor.Next(&mx) { + time.Sleep(time.Millisecond + 10) + fmt.Printf("(%.4d) NEXT: %.4d/%.4d ID=%d\n", id, n, i, mx.ID) + } + } for i := 0; i < 4; i++ { uErr = db.Transaction(func(tx *gorm.DB) (err error) { time.Sleep(time.Millisecond * 10) diff --git a/database/pkg.go b/database/pkg.go index 6e9c5eb2..caee14d0 100644 --- a/database/pkg.go +++ b/database/pkg.go @@ -18,7 +18,7 @@ var log = logr.WithName("db") var Settings = &settings.Settings const ( - ConnectionString = "file:%s?_journal=WAL&_timeout=100" + ConnectionString = "file:%s?_journal=WAL&_timeout=5000" FKsOn = "&_foreign_keys=yes" FKsOff = "&_foreign_keys=no" ) From 671e71380ef82da5ae7c950330c264198a533cca Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Thu, 10 Oct 2024 12:19:00 -0700 Subject: [PATCH 8/9] checkpoint Signed-off-by: Jeff Ortel --- database/driver.go | 1 - 1 file changed, 1 deletion(-) diff --git a/database/driver.go b/database/driver.go index 63bbbabd..0c50360b 100644 --- a/database/driver.go +++ b/database/driver.go @@ -318,7 +318,6 @@ func (t *Tx) Commit() (err error) { return } -// // Rollback the transaction. // Releases the mutex. func (t *Tx) Rollback() (err error) { From edf47e70ad8d9483c3bf9dea74ee2e81ff98cddf Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Thu, 10 Oct 2024 12:37:53 -0700 Subject: [PATCH 9/9] checkpoint Signed-off-by: Jeff Ortel --- database/pkg.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/pkg.go b/database/pkg.go index caee14d0..83cfc4f3 100644 --- a/database/pkg.go +++ b/database/pkg.go @@ -18,7 +18,7 @@ var log = logr.WithName("db") var Settings = &settings.Settings const ( - ConnectionString = "file:%s?_journal=WAL&_timeout=5000" + ConnectionString = "file:%s?_journal=WAL" FKsOn = "&_foreign_keys=yes" FKsOff = "&_foreign_keys=no" )