Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

connection pooling for duckdb #1405

Merged
merged 43 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1ffb6e8
connection pooling for duckdb
pjain1 Dec 8, 2022
c12984a
close channel
pjain1 Dec 8, 2022
5c9fa5b
set pool size
pjain1 Dec 8, 2022
a4158dc
fix test
pjain1 Dec 8, 2022
79f465d
fix stopping of pw
pjain1 Dec 8, 2022
4571c4c
formatting
pjain1 Dec 8, 2022
515f840
start mulitple jobs are getting unpaused
pjain1 Dec 8, 2022
a4b8dbf
conn pool dequeue
pjain1 Dec 8, 2022
da0d5e7
fix test
pjain1 Dec 8, 2022
ef1aa5f
better concurrency test of priority worker
pjain1 Dec 9, 2022
b5eccec
comment
pjain1 Dec 9, 2022
3be68ad
Merging with context affinity
begelundmuller Dec 15, 2022
d25fca0
Pass pool size through DSN
begelundmuller Dec 15, 2022
a83af54
Formatting
begelundmuller Dec 15, 2022
3692201
Added a priority semaphore
begelundmuller Dec 16, 2022
feceaf1
Update duckdb driver to use priority semaphore
begelundmuller Dec 16, 2022
189184d
Maybe fix failing test case
begelundmuller Dec 16, 2022
7ead16a
Document priorityqueue.Semaphore
begelundmuller Dec 16, 2022
c565dca
Bind time series to one connection
begelundmuller Dec 16, 2022
dd3b4d2
Use sqlx.Conn + fix hanging errors
begelundmuller Dec 16, 2022
f285a1b
Temporarily committing spending benchmark in examples
begelundmuller Dec 16, 2022
ecb1a6a
fix failing test
pjain1 Dec 17, 2022
b24142b
formatting
pjain1 Dec 17, 2022
ca82b58
Commit js benchmark
begelundmuller Dec 21, 2022
597b41f
Removing benchmarks
begelundmuller Dec 22, 2022
2248870
duckdb driver fix, tests with pool size > 1, separate meta connection
pjain1 Dec 22, 2022
2592da0
use conn from pool in migrate
pjain1 Dec 23, 2022
3f8224e
use single conn in migrate
pjain1 Dec 23, 2022
da6960a
use built in connection pool
pjain1 Dec 27, 2022
e50f987
fix conn release race condition
pjain1 Dec 28, 2022
7fa8f9d
fix linter errors
pjain1 Dec 28, 2022
36e207c
fmt
pjain1 Dec 28, 2022
e056910
gofmt fix
pjain1 Dec 28, 2022
0466f77
gofmt fix
pjain1 Dec 28, 2022
f6a49c3
fix tests
pjain1 Dec 28, 2022
52a80ee
upgrade duckdb driver
pjain1 Dec 29, 2022
7a10d70
Meta and OLAP sems; ensuring safe release
begelundmuller Jan 3, 2023
daeed05
Merge branch 'main' into conn_pool
begelundmuller Jan 3, 2023
42f1c5f
Use WithConnection for temporary tables
begelundmuller Jan 3, 2023
c3b9e6c
Merge branch 'main' into conn_pool
pjain1 Jan 4, 2023
3d544ac
formatting
pjain1 Jan 4, 2023
92f3cd0
Merge branch 'main' into conn_pool
begelundmuller Jan 4, 2023
ab7265d
Review
begelundmuller Jan 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix linter errors
  • Loading branch information
pjain1 committed Dec 28, 2022
commit 7fa8f9d0dbf58611f4ff8f88c7f3dffc354cf30a
14 changes: 9 additions & 5 deletions runtime/caches.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,27 @@ import (
"fmt"
"sync"


"go.uber.org/zap"

lru "github.com/hashicorp/golang-lru"
"github.com/hashicorp/golang-lru/simplelru"
"github.com/rilldata/rill/runtime/drivers"
"github.com/rilldata/rill/runtime/services/catalog"
)

type connectionCache struct {
cache *simplelru.LRU
lock sync.Mutex
cache *simplelru.LRU
lock sync.Mutex
logger *zap.Logger
}

func newConnectionCache(size int) *connectionCache {
func newConnectionCache(size int, logger *zap.Logger) *connectionCache {
cache, err := simplelru.NewLRU(size, nil)
if err != nil {
panic(err)
}
return &connectionCache{cache: cache}
return &connectionCache{cache: cache, logger: logger}
}

func (c *connectionCache) get(ctx context.Context, instanceID, driver, dsn string) (drivers.Connection, error) {
Expand All @@ -34,7 +38,7 @@ func (c *connectionCache) get(ctx context.Context, instanceID, driver, dsn strin
key := instanceID + driver + dsn
val, ok := c.cache.Get(key)
if !ok {
conn, err := drivers.Open(driver, dsn)
conn, err := drivers.Open(driver, dsn, c.logger)
if err != nil {
return nil, err
}
Expand Down
8 changes: 5 additions & 3 deletions runtime/drivers/drivers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"

"go.uber.org/zap"
)

// ErrClosed indicates the connection is closed.
Expand All @@ -24,13 +26,13 @@ func Register(name string, driver Driver) {
}

// Open opens a new connection
func Open(driver string, dsn string) (Connection, error) {
func Open(driver, dsn string, logger *zap.Logger) (Connection, error) {
d, ok := Drivers[driver]
if !ok {
return nil, fmt.Errorf("unknown database driver: %s", driver)
}

conn, err := d.Open(dsn)
conn, err := d.Open(dsn, logger)
if err != nil {
return nil, err
}
Expand All @@ -40,7 +42,7 @@ func Open(driver string, dsn string) (Connection, error) {

// Driver represents an underlying DB.
type Driver interface {
Open(dsn string) (Connection, error)
Open(dsn string, logger *zap.Logger) (Connection, error)
}

// Connection represents a connection to an underlying DB.
Expand Down
4 changes: 3 additions & 1 deletion runtime/drivers/druid/druid.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package druid
import (
"context"

"go.uber.org/zap"

"github.com/jmoiron/sqlx"
"github.com/rilldata/rill/runtime/drivers"

Expand All @@ -18,7 +20,7 @@ type driver struct{}

// Open connects to Druid using Avatica.
// Note that the Druid connection string must have the form "http://host/druid/v2/sql/avatica-protobuf/".
func (d driver) Open(dsn string) (drivers.Connection, error) {
func (d driver) Open(dsn string, logger *zap.Logger) (drivers.Connection, error) {
db, err := sqlx.Open("avatica", dsn)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion runtime/drivers/duckdb/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func (c *connection) UpdateEntry(ctx context.Context, instanceID string, e *driv
return nil
}

func (c *connection) DeleteEntry(ctx context.Context, instanceID string, name string) error {
func (c *connection) DeleteEntry(ctx context.Context, instanceID, name string) error {
conn, release, err := c.getConn(ctx)
defer release()
if err != nil {
Expand Down
68 changes: 42 additions & 26 deletions runtime/drivers/duckdb/duckdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"context"
"database/sql"
"database/sql/driver"

"github.com/jmoiron/sqlx"
"github.com/marcboeker/go-duckdb"
"go.uber.org/zap"

"github.com/rilldata/rill/runtime/drivers"

Expand All @@ -18,30 +20,32 @@ func init() {

type Driver struct{}

func (d Driver) Open(dsn string) (drivers.Connection, error) {
func (d Driver) Open(dsn string, logger *zap.Logger) (drivers.Connection, error) {
cfg, err := newConfig(dsn)
if err != nil {
return nil, err
}
connector, err := duckdb.NewConnector(cfg.DSN, func(execer driver.Execer) error {
bootQueries := []string{
"INSTALL 'json'",
"LOAD 'json'",
"INSTALL 'parquet'",
"LOAD 'parquet'",
"INSTALL 'httpfs'",
"LOAD 'httpfs'",
"SET max_expression_depth TO 250",
}

for _, qry := range bootQueries {
_, err = execer.Exec(qry, nil)
if err != nil {
return err
connector, err := duckdb.NewConnector(cfg.DSN,
// nolint:staticcheck // TODO: remove when go-duckdb implements the driver.ExecerContext interface
func(execer driver.Execer) error {
bootQueries := []string{
"INSTALL 'json'",
"LOAD 'json'",
"INSTALL 'parquet'",
"LOAD 'parquet'",
"INSTALL 'httpfs'",
"LOAD 'httpfs'",
"SET max_expression_depth TO 250",
}

for _, qry := range bootQueries {
_, err = execer.Exec(qry, nil)
if err != nil {
return err
}
}
}
return nil
})
return nil
})
if err != nil {
return nil, err
}
Expand All @@ -51,16 +55,18 @@ func (d Driver) Open(dsn string) (drivers.Connection, error) {
db.SetMaxOpenConns(cfg.PoolSize)

c := &connection{
db: db,
sem: priorityqueue.NewSemaphore(cfg.PoolSize),
db: db,
sem: priorityqueue.NewSemaphore(cfg.PoolSize),
logger: logger,
}

return c, nil
}

type connection struct {
db *sqlx.DB
sem *priorityqueue.Semaphore
db *sqlx.DB
sem *priorityqueue.Semaphore
logger *zap.Logger
}

// Close implements drivers.Connection.
Expand Down Expand Up @@ -90,16 +96,26 @@ func (c *connection) OLAPStore() (drivers.OLAPStore, bool) {

// getConn gets a connection from the pool.
// It returns a function that puts the connection back in the pool if applicable.
func (c *connection) getConn(ctx context.Context) (conn *sqlx.Conn, release func() error, err error) {
func (c *connection) getConn(ctx context.Context) (conn *sqlx.Conn, release func(), err error) {
// Try to get conn from context
conn = connFromContext(ctx)
if conn != nil {
return conn, func() error { return nil }, nil
return conn, func() {}, nil
}

conn, err = c.db.Connx(ctx)
if err != nil {
return nil, nil, err
}
return conn, conn.Close, nil
release = func() {
// call release in a goroutine as it will block until rows.close() is called so if a method returns rows
// and the caller is responsible for closing them, the method will not return and cause deadlock
go func() {
err := conn.Close()
if err != nil {
c.logger.Error("error releasing connection", zap.Error(err))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we just panic here ?

}
}()
}
return conn, release, nil
}
2 changes: 1 addition & 1 deletion runtime/drivers/duckdb/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (c *connection) migrateSingle(ctx context.Context, conn *sqlx.Conn, name st
}

// MigrationStatus implements drivers.Connection.
func (c *connection) MigrationStatus(ctx context.Context) (current int, desired int, err error) {
func (c *connection) MigrationStatus(ctx context.Context) (current, desired int, err error) {
conn, release, err := c.getConn(ctx)
if err != nil {
return 0, 0, err
Expand Down
6 changes: 1 addition & 5 deletions runtime/drivers/duckdb/olap.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ func (c *connection) Execute(ctx context.Context, stmt *drivers.Statement) (*dri
if err != nil {
return nil, err
}
defer func() {
// call release in a goroutine as it will block until rows.close() is called
// TODO log error
go release()
}()
defer release()
conn = connx
}

Expand Down
3 changes: 2 additions & 1 deletion runtime/drivers/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"go.uber.org/zap"

"github.com/rilldata/rill/runtime/drivers"
)
Expand All @@ -14,7 +15,7 @@ func init() {

type driver struct{}

func (d driver) Open(dsn string) (drivers.Connection, error) {
func (d driver) Open(dsn string, logger *zap.Logger) (drivers.Connection, error) {
c := &connection{root: dsn}
if err := c.checkRoot(); err != nil {
return nil, err
Expand Down
3 changes: 2 additions & 1 deletion runtime/drivers/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package postgres
import (
"github.com/jmoiron/sqlx"
"github.com/rilldata/rill/runtime/drivers"
"go.uber.org/zap"

// Load postgres driver
_ "github.com/jackc/pgx/v4/stdlib"
Expand All @@ -14,7 +15,7 @@ func init() {

type driver struct{}

func (d driver) Open(dsn string) (drivers.Connection, error) {
func (d driver) Open(dsn string, logger *zap.Logger) (drivers.Connection, error) {
db, err := sqlx.Connect("pgx", dsn)
if err != nil {
return nil, err
Expand Down
3 changes: 2 additions & 1 deletion runtime/drivers/sqlite/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sqlite
import (
"github.com/jmoiron/sqlx"
"github.com/rilldata/rill/runtime/drivers"
"go.uber.org/zap"

// Load sqlite driver
_ "modernc.org/sqlite"
Expand All @@ -14,7 +15,7 @@ func init() {

type driver struct{}

func (d driver) Open(dsn string) (drivers.Connection, error) {
func (d driver) Open(dsn string, logger *zap.Logger) (drivers.Connection, error) {
db, err := sqlx.Connect("sqlite", dsn)
if err != nil {
return nil, err
Expand Down
6 changes: 3 additions & 3 deletions runtime/pkg/priorityqueue/semaphore.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func NewSemaphore(size int) *Semaphore {
func (s *Semaphore) Acquire(ctx context.Context, priority int) error {
s.mu.Lock()
if s.size-s.cur >= 1 && s.pq.Len() == 0 {
s.cur += 1
s.cur++
s.mu.Unlock()
return nil
}
Expand Down Expand Up @@ -61,7 +61,7 @@ func (s *Semaphore) TryAcquire() bool {
s.mu.Lock()
ok := s.size-s.cur >= 1 && s.pq.Len() == 0
if ok {
s.cur += 1
s.cur++
}
s.mu.Unlock()
return ok
Expand All @@ -70,7 +70,7 @@ func (s *Semaphore) TryAcquire() bool {
// Release releases a semaphore previously acquired with Acquire or TryAcquire.
func (s *Semaphore) Release() {
s.mu.Lock()
s.cur -= 1
s.cur--
if s.cur < 0 {
s.mu.Unlock()
panic("semaphore released more times than acquired")
Expand Down
4 changes: 2 additions & 2 deletions runtime/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (r *Runtime) FindInstance(ctx context.Context, instanceID string) (*drivers

func (r *Runtime) CreateInstance(ctx context.Context, inst *drivers.Instance) error {
// Check OLAP connection
olap, err := drivers.Open(inst.OLAPDriver, inst.OLAPDSN)
olap, err := drivers.Open(inst.OLAPDriver, inst.OLAPDSN, r.logger)
if err != nil {
return err
}
Expand All @@ -28,7 +28,7 @@ func (r *Runtime) CreateInstance(ctx context.Context, inst *drivers.Instance) er
}

// Check repo connection
repo, err := drivers.Open(inst.RepoDriver, inst.RepoDSN)
repo, err := drivers.Open(inst.RepoDriver, inst.RepoDSN, r.logger)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type Runtime struct {

func New(opts *Options, logger *zap.Logger) (*Runtime, error) {
// Open metadata db connection
metastore, err := drivers.Open(opts.MetastoreDriver, opts.MetastoreDSN)
metastore, err := drivers.Open(opts.MetastoreDriver, opts.MetastoreDSN, logger)
if err != nil {
return nil, fmt.Errorf("could not connect to metadata db: %w", err)
}
Expand All @@ -45,7 +45,7 @@ func New(opts *Options, logger *zap.Logger) (*Runtime, error) {
opts: opts,
metastore: metastore,
logger: logger,
connCache: newConnectionCache(opts.ConnectionCacheSize),
connCache: newConnectionCache(opts.ConnectionCacheSize, logger),
catalogCache: newCatalogCache(),
queryCache: newQueryCache(opts.QueryCacheSize),
}, nil
Expand Down