Compare commits

...

4 Commits

Author SHA1 Message Date
Michael H
c313d846f1 docs: remove flag from roadmap 2025-05-13 00:28:06 +10:00
Jarred Sumner
274fd0c708 Update bun_js.zig 2025-05-11 07:12:25 -07:00
Jarred Sumner
83636eb73c Update sql.md 2025-05-11 07:05:57 -07:00
Jarred Sumner
16c07cc617 Add --sql-preconnect flag 2025-05-11 07:05:52 -07:00
4 changed files with 57 additions and 6 deletions

View File

@@ -321,6 +321,22 @@ const db = new SQL({
});
```
## Preconnect at startup
The `--sql-preconnect` flag automatically connects to the database at startup before any other code is executed. This works by reading the existing connection URL from the environment variables.
```bash
bun --sql-preconnect my-script.ts
```
If your app takes some time to load or your database is slow to connect, you can use the `--sql-preconnect` flag to preconnect to speed up the first query.
Internally, this is equivalent to:
```ts
Bun.sql.connect().then(() => {});
```
## Dynamic passwords
When clients need to use alternative authentication schemes such as access tokens or connections to databases with rotating passwords, provide either a synchronous or asynchronous function that will resolve the dynamic password value at connection time.
@@ -633,7 +649,6 @@ console.log(typeof x, x); // "bigint" 9223372036854777n
There's still some things we haven't finished yet.
- Connection preloading via `--db-preconnect` Bun CLI flag
- MySQL support: [we're working on it](https://github.com/oven-sh/bun/pull/15274)
- SQLite support: planned, but not started. Ideally, we implement it natively instead of wrapping `bun:sqlite`.
- Column name transforms (e.g. `snake_case` to `camelCase`). This is mostly blocked on a unicode-aware implementation of changing the case in C++ using WebKit's `WTF::String`.

View File

@@ -305,22 +305,52 @@ pub const Run = struct {
// PropertyCallback which means we don't have a WriteBarrier we can access
const global = vm.global;
const bun_object = vm.global.toJSValue().get(global, "Bun") catch |err| {
vm.global.reportActiveExceptionAsUnhandled(err);
global.reportActiveExceptionAsUnhandled(err);
break :do_redis_preconnect;
} orelse break :do_redis_preconnect;
const redis = bun_object.get(global, "redis") catch |err| {
vm.global.reportActiveExceptionAsUnhandled(err);
global.reportActiveExceptionAsUnhandled(err);
break :do_redis_preconnect;
} orelse break :do_redis_preconnect;
const client = redis.as(bun.valkey.JSValkeyClient) orelse break :do_redis_preconnect;
// If connection fails, this will become an unhandled promise rejection, which is fine.
_ = client.doConnect(vm.global, redis) catch |err| {
vm.global.reportActiveExceptionAsUnhandled(err);
_ = client.doConnect(global, redis) catch |err| {
global.reportActiveExceptionAsUnhandled(err);
break :do_redis_preconnect;
};
}
}
// This does the equivalent of:
// ```ts
// try {
// Bun.sql.connect();
// } catch (err) {
// reportError(err);
// }
// ```
do_postgres_preconnect: {
if (this.ctx.runtime_options.sql_preconnect) {
const global = vm.global;
const bun_object = vm.global.toJSValue().get(global, "Bun") catch |err| {
global.reportActiveExceptionAsUnhandled(err);
break :do_postgres_preconnect;
} orelse break :do_postgres_preconnect;
const sql_object = bun_object.get(global, "sql") catch |err| {
global.reportActiveExceptionAsUnhandled(err);
break :do_postgres_preconnect;
} orelse break :do_postgres_preconnect;
const connect_fn = sql_object.get(global, "connect") catch |err| {
global.reportActiveExceptionAsUnhandled(err);
break :do_postgres_preconnect;
} orelse break :do_postgres_preconnect;
_ = connect_fn.call(global, sql_object, &.{}) catch |err| {
global.reportActiveExceptionAsUnhandled(err);
break :do_postgres_preconnect;
};
}
}
switch (this.ctx.debug.hot_reload) {
.hot => JSC.hot_reloader.HotReloader.enableHotModuleReloading(vm),
.watch => JSC.hot_reloader.WatchReloader.enableHotModuleReloading(vm),

View File

@@ -245,6 +245,7 @@ pub const Arguments = struct {
clap.parseParam("--throw-deprecation Determine whether or not deprecation warnings result in errors.") catch unreachable,
clap.parseParam("--title <STR> Set the process title") catch unreachable,
clap.parseParam("--zero-fill-buffers Boolean to force Buffer.allocUnsafe(size) to be zero-filled.") catch unreachable,
clap.parseParam("--sql-preconnect Preconnect to $DATABASE_URL at startup") catch unreachable,
clap.parseParam("--redis-preconnect Preconnect to $REDIS_URL at startup") catch unreachable,
clap.parseParam("--no-addons Throw an error if process.dlopen is called, and disable export condition \"node-addons\"") catch unreachable,
};
@@ -716,6 +717,10 @@ pub const Arguments = struct {
ctx.runtime_options.redis_preconnect = true;
}
if (args.flag("--sql-preconnect")) {
ctx.runtime_options.sql_preconnect = true;
}
if (args.flag("--no-addons")) {
// used for disabling process.dlopen and
// for disabling export condition "node-addons"
@@ -1546,6 +1551,7 @@ pub const Command = struct {
debugger: Debugger = .{ .unspecified = {} },
if_present: bool = false,
redis_preconnect: bool = false,
sql_preconnect: bool = false,
eval: struct {
script: []const u8 = "",
eval_and_print: bool = false,

View File

@@ -5,7 +5,7 @@
- `./node` contains all `node:*` modules
- `./bun` contains all `bun:*` modules
- `./thirdparty` contains npm modules we replace like `ws`
- `./internal` contains modules that aren't assigned to the module resolver
- `./internal` contains modules that aren't directly exposed to userland but can be used by other internal modules. There's no registration required, you can just `require("internal/path/to/module")` and it'll work.
Each `.ts`/`.js` file above is assigned a numeric id at compile time and inlined into an array of lazily initialized modules. Internal modules referencing each other is extremely optimized, skipping the module resolver entirely.