Fix write/readonly connection usage and linter error

This commit is contained in:
Patrick Pacher 2022-08-02 14:04:12 +02:00
parent 6a86596736
commit 01f7ab24b3
No known key found for this signature in database
GPG key ID: E8CD2DA160925A6D

View file

@ -41,14 +41,10 @@ type (
// It's use is tailored for persistence and querying of network.Connection. // It's use is tailored for persistence and querying of network.Connection.
// Access to the underlying SQLite database is synchronized. // Access to the underlying SQLite database is synchronized.
// //
// TODO(ppacher): somehow I'm receiving SIGBUS or SIGSEGV when no doing
// synchronization in *Database. Check what exactly sqlite.OpenFullMutex, etc..
// are actually supposed to do.
//
Database struct { Database struct {
Schema *orm.TableSchema Schema *orm.TableSchema
pool *puddle.Pool[*sqlite.Conn] readConnPool *puddle.Pool[*sqlite.Conn]
l sync.Mutex l sync.Mutex
writeConn *sqlite.Conn writeConn *sqlite.Conn
@ -105,11 +101,12 @@ type (
} }
) )
// New opens a new database at path. The database is opened with Full-Mutex, Write-Ahead-Log (WAL) // New opens a new in-memory database named path.
// and Shared-Cache enabled.
// //
// TODO(ppacher): check which sqlite "open flags" provide the best performance and don't cause // The returned Database used connection pooling for read-only connections
// SIGBUS/SIGSEGV when used with out a dedicated mutex in *Database. // (see Execute). To perform database writes use either Save() or ExecuteWrite().
// Note that write connections are serialized by the Database object before being
// handed over to SQLite.
// //
func New(path string) (*Database, error) { func New(path string) (*Database, error) {
constructor := func(ctx context.Context) (*sqlite.Conn, error) { constructor := func(ctx context.Context) (*sqlite.Conn, error) {
@ -129,7 +126,9 @@ func New(path string) (*Database, error) {
} }
destructor := func(resource *sqlite.Conn) { destructor := func(resource *sqlite.Conn) {
resource.Close() if err := resource.Close(); err != nil {
log.Errorf("failed to close pooled SQlite database connection: %s", err)
}
} }
pool := puddle.NewPool(constructor, destructor, 10) pool := puddle.NewPool(constructor, destructor, 10)
@ -154,7 +153,7 @@ func New(path string) (*Database, error) {
} }
return &Database{ return &Database{
pool: pool, readConnPool: pool,
Schema: schema, Schema: schema,
writeConn: writeConn, writeConn: writeConn,
}, nil }, nil
@ -186,18 +185,19 @@ func (db *Database) ApplyMigrations() error {
// get the create-table SQL statement from the inferred schema // get the create-table SQL statement from the inferred schema
sql := db.Schema.CreateStatement(true) sql := db.Schema.CreateStatement(true)
return db.withConn(context.Background(), func(conn *sqlite.Conn) error { db.l.Lock()
defer db.l.Unlock()
// execute the SQL // execute the SQL
if err := sqlitex.ExecuteTransient(conn, sql, nil); err != nil { if err := sqlitex.ExecuteTransient(db.writeConn, sql, nil); err != nil {
return fmt.Errorf("failed to create schema: %w", err) return fmt.Errorf("failed to create schema: %w", err)
} }
return nil return nil
})
} }
func (db *Database) withConn(ctx context.Context, fn func(conn *sqlite.Conn) error) error { func (db *Database) withConn(ctx context.Context, fn func(conn *sqlite.Conn) error) error {
res, err := db.pool.Acquire(ctx) res, err := db.readConnPool.Acquire(ctx)
if err != nil { if err != nil {
return err return err
} }
@ -206,7 +206,19 @@ func (db *Database) withConn(ctx context.Context, fn func(conn *sqlite.Conn) err
return fn(res.Value()) return fn(res.Value())
} }
// Execute executes a custom SQL query against the SQLite database used by db. // ExecuteWrite executes a custom SQL query using a writable connection against the SQLite
// database used by db.
// It uses orm.RunQuery() under the hood so please refer to the orm package for
// more information about available options.
func (db *Database) ExecuteWrite(ctx context.Context, sql string, args ...orm.QueryOption) error {
db.l.Lock()
defer db.l.Unlock()
return orm.RunQuery(ctx, db.writeConn, sql, args...)
}
// Execute executes a custom SQL query using a read-only connection against the SQLite
// database used by db.
// It uses orm.RunQuery() under the hood so please refer to the orm package for // It uses orm.RunQuery() under the hood so please refer to the orm package for
// more information about available options. // more information about available options.
func (db *Database) Execute(ctx context.Context, sql string, args ...orm.QueryOption) error { func (db *Database) Execute(ctx context.Context, sql string, args ...orm.QueryOption) error {
@ -262,7 +274,7 @@ func (db *Database) Cleanup(ctx context.Context, threshold time.Time) (int, erro
return 0, fmt.Errorf("unexpected number of rows, expected 1 got %d", len(result)) return 0, fmt.Errorf("unexpected number of rows, expected 1 got %d", len(result))
} }
err := db.Execute(ctx, sql, args) err := db.ExecuteWrite(ctx, sql, args)
if err != nil { if err != nil {
return 0, err return 0, err
} }