mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-22 15:59:56 -05:00
fix(kysely-adapter): allow concurrent queries on the D1 dialect (#11490)
Co-authored-by: Taesu <bytaesu@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@better-auth/kysely-adapter": patch
|
||||
---
|
||||
|
||||
Allow concurrent D1 queries on a shared auth instance with Kysely 0.29, avoiding cross-request I/O errors.
|
||||
@@ -3,11 +3,13 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@better-auth/core": "workspace:*",
|
||||
"@better-auth/kysely-adapter": "workspace:*",
|
||||
"@better-auth/sso": "workspace:*",
|
||||
"better-auth": "workspace:*",
|
||||
"hono": "^4.13.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"kysely-029": "npm:kysely@0.29.6",
|
||||
"vitest": "catalog:vitest",
|
||||
"wrangler": "4.123.0"
|
||||
},
|
||||
|
||||
+57
@@ -1,3 +1,4 @@
|
||||
import { env } from "cloudflare:workers";
|
||||
import type { AuthEndpointContext } from "@better-auth/core/context";
|
||||
import {
|
||||
getCurrentAdapter,
|
||||
@@ -9,12 +10,48 @@ import type {
|
||||
DBAdapter,
|
||||
DBTransactionAdapter,
|
||||
} from "@better-auth/core/db/adapter";
|
||||
import { createKyselyAdapter } from "@better-auth/kysely-adapter";
|
||||
import { getMigrations } from "better-auth/db/migration";
|
||||
import { Hono } from "hono";
|
||||
import { auth } from "./auth";
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
let slowQueryStarted = false;
|
||||
let releaseSlowQuery = false;
|
||||
const delayedDatabase = {
|
||||
prepare(query: string) {
|
||||
const statement = env.DB.prepare(query);
|
||||
return {
|
||||
bind(...parameters: unknown[]) {
|
||||
const bound = statement.bind(...parameters);
|
||||
return {
|
||||
async all() {
|
||||
if (query.includes("slow_marker")) {
|
||||
slowQueryStarted = true;
|
||||
try {
|
||||
while (!releaseSlowQuery) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
}
|
||||
} finally {
|
||||
slowQueryStarted = false;
|
||||
releaseSlowQuery = false;
|
||||
}
|
||||
}
|
||||
return bound.all();
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
batch: (...statements: D1PreparedStatement[]) => env.DB.batch(statements),
|
||||
exec: (query: string) => env.DB.exec(query),
|
||||
} as unknown as D1Database;
|
||||
const { kysely: concurrencyDatabase } = await createKyselyAdapter({
|
||||
database: delayedDatabase,
|
||||
});
|
||||
if (!concurrencyDatabase) throw new Error("D1 test database was not created");
|
||||
|
||||
app.all("/api/auth/*", (c) => auth.handler(c.req.raw));
|
||||
|
||||
app.get("/_test/session", async (c) => {
|
||||
@@ -24,6 +61,26 @@ app.get("/_test/session", async (c) => {
|
||||
return c.json(session);
|
||||
});
|
||||
|
||||
app.get("/_test/d1-concurrency/:query", async (c) => {
|
||||
if (c.req.param("query") === "status") {
|
||||
return c.json({ slowQueryStarted });
|
||||
}
|
||||
if (c.req.param("query") === "release") {
|
||||
releaseSlowQuery = true;
|
||||
return c.body(null, 204);
|
||||
}
|
||||
|
||||
const slow = c.req.param("query") === "slow";
|
||||
const query = concurrencyDatabase.selectFrom("sqlite_master");
|
||||
await (slow
|
||||
? query.select("name as slow_marker")
|
||||
: query.select("name as fast_marker")
|
||||
)
|
||||
.limit(1)
|
||||
.execute();
|
||||
return c.body(null, 204);
|
||||
});
|
||||
|
||||
app.post("/_test/migrate", async (c) => {
|
||||
const { runMigrations } = await getMigrations(auth.options);
|
||||
await runMigrations();
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { afterAll, afterEach, beforeAll, expect, it, vi } from "vitest";
|
||||
import { createTestHarness } from "wrangler";
|
||||
|
||||
const server = createTestHarness({
|
||||
workers: [{ configPath: "./wrangler.json" }],
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
await server.listen();
|
||||
});
|
||||
|
||||
afterEach(({ task }) => {
|
||||
if (task.result?.state === "fail") server.debug();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
/**
|
||||
* @see https://github.com/better-auth/better-auth/issues/11483
|
||||
*/
|
||||
it("lets independent D1 requests complete while another query is pending", async () => {
|
||||
const slowRequest = server.fetch(
|
||||
"http://localhost:8787/_test/d1-concurrency/slow",
|
||||
);
|
||||
let slowStatus: number;
|
||||
try {
|
||||
await vi.waitFor(
|
||||
async () => {
|
||||
const response = await server.fetch(
|
||||
"http://localhost:8787/_test/d1-concurrency/status",
|
||||
);
|
||||
expect(await response.json()).toEqual({ slowQueryStarted: true });
|
||||
},
|
||||
{ timeout: 5000 },
|
||||
);
|
||||
|
||||
const fastResponse = await server.fetch(
|
||||
"http://localhost:8787/_test/d1-concurrency/fast",
|
||||
{ signal: AbortSignal.timeout(5000) },
|
||||
);
|
||||
expect(fastResponse.status).toBe(204);
|
||||
} finally {
|
||||
await server.fetch("http://localhost:8787/_test/d1-concurrency/release");
|
||||
slowStatus = (await slowRequest).status;
|
||||
}
|
||||
|
||||
expect(slowStatus).toBe(204);
|
||||
}, 15_000);
|
||||
@@ -5,6 +5,9 @@
|
||||
"name": "cloudflare",
|
||||
"upload_source_maps": true,
|
||||
"compatibility_flags": ["nodejs_compat"],
|
||||
"alias": {
|
||||
"kysely": "kysely-029"
|
||||
},
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
|
||||
@@ -81,7 +81,11 @@ export function createD1IndexIntrospector(
|
||||
};
|
||||
}
|
||||
|
||||
class D1SqliteAdapter extends SqliteAdapter {}
|
||||
class D1SqliteAdapter extends SqliteAdapter {
|
||||
get supportsMultipleConnections(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Config for the D1 SQLite dialect.
|
||||
|
||||
Generated
+12
@@ -887,6 +887,9 @@ importers:
|
||||
'@better-auth/core':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../../packages/core
|
||||
'@better-auth/kysely-adapter':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../../packages/kysely-adapter
|
||||
'@better-auth/sso':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../../packages/sso
|
||||
@@ -897,6 +900,9 @@ importers:
|
||||
specifier: ^4.13.7
|
||||
version: 4.13.7
|
||||
devDependencies:
|
||||
kysely-029:
|
||||
specifier: npm:kysely@0.29.6
|
||||
version: kysely@0.29.6
|
||||
vitest:
|
||||
specifier: catalog:vitest
|
||||
version: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@25.9.4)(@vitest/coverage-istanbul@5.0.0)(@vitest/ui@5.0.0)(happy-dom@20.8.9)(msw@2.14.6(@types/node@25.9.4)(typescript@6.0.3))(vite@8.2.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))
|
||||
@@ -11275,6 +11281,10 @@ packages:
|
||||
resolution: {integrity: sha512-VHtBdW6XB/pgoTSqraM3UAa2rYoYdNXqnNPpX+8XXP+cwYbVEFuAp3HyPt1vpNfU9l7Y2kpUrA9QDPsy8uUqOQ==}
|
||||
engines: {node: '>=22.0.0'}
|
||||
|
||||
kysely@0.29.6:
|
||||
resolution: {integrity: sha512-hHaB8C/rfzDDtr/t8YZwxAuPJTT0zHyaPoVzcXwDYhYNAgH/4sIfVhi/XLLIY+bL/FqaIJnjATDbi8ObSELmxg==}
|
||||
engines: {node: '>=22.0.0'}
|
||||
|
||||
lan-network@0.1.7:
|
||||
resolution: {integrity: sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ==}
|
||||
hasBin: true
|
||||
@@ -27524,6 +27534,8 @@ snapshots:
|
||||
|
||||
kysely@0.29.3: {}
|
||||
|
||||
kysely@0.29.6: {}
|
||||
|
||||
lan-network@0.1.7: {}
|
||||
|
||||
launch-editor@2.14.1:
|
||||
|
||||
Reference in New Issue
Block a user