Skip to content

Commit

Permalink
Merge pull request #123 from grafana/122-connection-options-in-the-op…
Browse files Browse the repository at this point in the history
…en-function

Connection options in the open function
  • Loading branch information
szkiba authored Feb 21, 2025
2 parents 70e80cb + 57449e1 commit d47a5cd
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true

[*.{yml,yaml,json,sh,bats}]
[*.{js,ts,yml,yaml,json,sh,bats}]
indent_size = 2
trim_trailing_whitespace = true

Expand Down
72 changes: 70 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* In order to use the `xk6-sql` API, in addition to the `k6/x/sql` module,
* it is also necessary to import at least one driver module.
* The default export of the driver module is a driver identifier symbol,
* which should be passed as a parameter of the `open()` function.
* which should be passed as a parameter of the {@link open} function.
*
* The driver module is typically available at `k6/x/sql/driver/FOO`,
* where `FOO` is the name of the driver.
Expand Down Expand Up @@ -65,6 +65,7 @@ export as namespace sql;
*
* @param dirverID driver identification symbol, the default export of the driver module
* @param dataSourceName driver-specific data source name, like a database name
* @param options connection related options
*
* @example
* ```ts file=examples/example.js
Expand All @@ -76,7 +77,74 @@ export as namespace sql;
* const db = sql.open(driver, "roster_db");
* ```
*/
export function open(dirverID: Symbol, dataSourceName: String): Database;
export function open(
dirverID: Symbol,
dataSourceName: String,
options?: Options
): Database;

/**
* Connection-related options for the {@link open} function.
* @example
* ```ts
* import sql from "k6/x/sql";
*
* // the actual database driver should be used instead of ramsql
* import driver from "k6/x/sql/driver/ramsql";
*
* const db = sql.open(driver, "roster_db", { conn_max_idle_time: "2s" });
* ```
*/
export interface Options {
/**
* Sets the maximum amount of time a connection may be idle.
* If 0, connections are not closed due to a connection's idle time.
* A duration string is a possibly signed sequence of decimal numbers,
* each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
* Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
*
* @example
* ```ts
* const db = sql.open(driver, "roster_db", { conn_max_idle_time: "1h10m10s" });
* ```
*/
conn_max_idle_time: string;
/**
* Sets the maximum amount of time a connection may be reused.
* If 0, connections are not closed due to a connection's age.
* A duration string is a possibly signed sequence of decimal numbers,
* each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
* Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
*
* @example
* ```ts
* const db = sql.open(driver, "roster_db", { conn_max_lifetime: "10h" });
* ```
*/
conn_max_lifetime: string;
/**
* Sets the maximum number of connections in the idle connection pool.
* If 0, no idle connections are retained.
* The default is currently 2.
*
* @example
* ```ts
* const db = sql.open(driver, "roster_db", { max_idle_conns: 3 });
* ```
*/
max_idle_conns: number;
/**
* Sets the maximum number of open connections to the database.
* If 0, then there is no limit on the number of open connections.
* The default is 0 (unlimited).
*
* @example
* ```ts
* const db = sql.open(driver, "roster_db", { max_open_conns: 100 });
* ```
*/
max_open_conns: number;
}

/**
* Database is a database handle representing a pool of zero or more underlying connections.
Expand Down
28 changes: 27 additions & 1 deletion releases/v1.0.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@ xk6-sql `v1.0.3` is here 🎉!

This release includes:

Bugfixes:
## New features

- [Connection options in the open function](https://github.com/grafana/xk6-sql/issues/122): An optional options parameter can be used in `open()` to specify database connection-related options.

```js
sql.open(driver, "roster_db", opts)
```

Properties:
- `conn_max_idle_time`: Sets the maximum amount of time a connection may be idle. If 0, connections are not closed due to a connection's idle time. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Example:
```js
const db = sql.open(driver, "roster_db", { conn_max_idle_time: "1h10m10s" });
```
- `conn_max_lifetime`: Sets the maximum amount of time a connection may be reused. If 0, connections are not closed due to a connection's age. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Example:
```js
const db = sql.open(driver, "roster_db", { conn_max_lifetime: "10h" });
```
- `max_idle_conns`: Sets the maximum number of connections in the idle connection pool. If 0, no idle connections are retained. The default is currently 2. Example:
```js
const db = sql.open(driver, "roster_db", { max_idle_conns: 3 });
```
- `max_open_conns`: Sets the maximum number of open connections to the database. If 0, then there is no limit on the number of open connections. The default is 0 (unlimited). Example:
```js
const db = sql.open(driver, "roster_db", { max_open_conns: 100 });
```

## Bugfixes

- [Symbol type driver parameter support](https://github.com/grafana/xk6-sql/issues/120): The `open()` function now accepts `Symbol` (class) type driver ids in addition to the primitive symbol type. This is because when a driver is imported with the `require()` function, it is not a primitive symbol that is imported, but a `Symbol` class type. Also fixes [#115](https://github.com/grafana/xk6-sql/issues/115)
6 changes: 5 additions & 1 deletion sql/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func asSymbol(value sobek.Value) (*sobek.Symbol, bool) {

// open establishes a connection to the specified database type using
// the provided connection string.
func (mod *module) Open(driverID sobek.Value, connectionString string) (*Database, error) {
func (mod *module) Open(driverID sobek.Value, connectionString string, opts *options) (*Database, error) {
driverSym, ok := asSymbol(driverID)

if !ok {
Expand All @@ -93,6 +93,10 @@ func (mod *module) Open(driverID sobek.Value, connectionString string) (*Databas
return nil, err
}

if err = opts.apply(db); err != nil {
return nil, err
}

return &Database{db: db}, nil
}

Expand Down
14 changes: 12 additions & 2 deletions sql/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ import (
//go:embed testdata/script.js
var script string

func TestMain(m *testing.M) {
sql.RegisterModule("ramsql")

m.Run()
}

// TestIntegration performs an integration test creating a ramsql database.
func TestIntegration(t *testing.T) {
t.Parallel()

sql.RegisterModule("ramsql")

sqltest.RunScript(t, "ramsql", "testdb", script)
}

func TestOptions(t *testing.T) {
t.Parallel()

sqltest.RunScript(t, "ramsql", "testdb", `const db = sql.open(driver, connection, { conn_max_idle_time: "5s" });`)
}
50 changes: 50 additions & 0 deletions sql/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package sql

import (
"database/sql"
"time"

"github.com/grafana/sobek"
)

// options represents connection related options for Open().
type options struct {
ConnMaxIdleTime sobek.Value
ConnMaxLifetime sobek.Value
MaxIdleConns sobek.Value
MaxOpenConns sobek.Value
}

func (o *options) apply(db *sql.DB) error {
if o == nil {
return nil
}

if o.ConnMaxIdleTime != nil {
d, err := time.ParseDuration(o.ConnMaxIdleTime.String())
if err != nil {
return err
}

db.SetConnMaxIdleTime(d)
}

if o.ConnMaxLifetime != nil {
d, err := time.ParseDuration(o.ConnMaxLifetime.String())
if err != nil {
return err
}

db.SetConnMaxLifetime(d)
}

if o.MaxIdleConns != nil {
db.SetMaxIdleConns(int(o.MaxIdleConns.ToInteger()))
}

if o.MaxOpenConns != nil {
db.SetMaxOpenConns(int(o.MaxOpenConns.ToInteger()))
}

return nil
}
29 changes: 29 additions & 0 deletions sql/options_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package sql

import (
"database/sql"
"testing"

"github.com/grafana/sobek"
"github.com/stretchr/testify/require"
)

func Test_options_apply(t *testing.T) {
t.Parallel()

db, err := sql.Open("ramsql", "foo")

require.NoError(t, err)

rt := sobek.New()

require.NoError(t, (&options{}).apply(db))
require.NoError(t, (&options{ConnMaxIdleTime: rt.ToValue("5s")}).apply(db))
require.NoError(t, (&options{ConnMaxLifetime: rt.ToValue("10m")}).apply(db))

require.NoError(t, (&options{MaxIdleConns: rt.ToValue(5)}).apply(db))
require.NoError(t, (&options{MaxOpenConns: rt.ToValue(10)}).apply(db))

require.Error(t, (&options{ConnMaxIdleTime: rt.ToValue("5g")}).apply(db))
require.Error(t, (&options{ConnMaxLifetime: rt.ToValue("10e")}).apply(db))
}
6 changes: 3 additions & 3 deletions sql/sql_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ func TestOpen(t *testing.T) { //nolint: paralleltest
driver := RegisterDriver("ramsql")
require.NotNil(t, driver)

db, err := mod.Open(driver, "")
db, err := mod.Open(driver, "", nil)

require.NoError(t, err)
require.NotNil(t, db)

_, err = mod.Open(sobek.New().ToValue("foo"), "testdb") // not a Symbol
_, err = mod.Open(sobek.New().ToValue("foo"), "testdb", nil) // not a Symbol

require.Error(t, err)

_, err = mod.Open(sobek.NewSymbol("ramsql"), "testdb") // not a registered Symbol
_, err = mod.Open(sobek.NewSymbol("ramsql"), "testdb", nil) // not a registered Symbol

require.Error(t, err)
}

0 comments on commit d47a5cd

Please sign in to comment.