Fix dev startup with embedded postgres reuse
This commit is contained in:
@@ -35,6 +35,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@paperclipai/shared": "workspace:*",
|
"@paperclipai/shared": "workspace:*",
|
||||||
"drizzle-orm": "^0.38.4",
|
"drizzle-orm": "^0.38.4",
|
||||||
|
"embedded-postgres": "^18.1.0-beta.16",
|
||||||
"postgres": "^3.4.5"
|
"postgres": "^3.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { existsSync, readFileSync, rmSync } from "node:fs";
|
import { existsSync, readFileSync, rmSync } from "node:fs";
|
||||||
import { createRequire } from "node:module";
|
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
||||||
import { ensurePostgresDatabase } from "./client.js";
|
import { ensurePostgresDatabase } from "./client.js";
|
||||||
import { resolveDatabaseTarget } from "./runtime-config.js";
|
import { resolveDatabaseTarget } from "./runtime-config.js";
|
||||||
|
|
||||||
@@ -52,17 +50,8 @@ function readPidFilePort(postmasterPidFile: string): number | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadEmbeddedPostgresCtor(): Promise<EmbeddedPostgresCtor> {
|
async function loadEmbeddedPostgresCtor(): Promise<EmbeddedPostgresCtor> {
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
const resolveCandidates = [
|
|
||||||
path.resolve(fileURLToPath(new URL("../..", import.meta.url))),
|
|
||||||
path.resolve(fileURLToPath(new URL("../../server", import.meta.url))),
|
|
||||||
path.resolve(fileURLToPath(new URL("../../cli", import.meta.url))),
|
|
||||||
process.cwd(),
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resolvedModulePath = require.resolve("embedded-postgres", { paths: resolveCandidates });
|
const mod = await import("embedded-postgres");
|
||||||
const mod = await import(pathToFileURL(resolvedModulePath).href);
|
|
||||||
return mod.default as EmbeddedPostgresCtor;
|
return mod.default as EmbeddedPostgresCtor;
|
||||||
} catch {
|
} catch {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -79,6 +68,20 @@ async function ensureEmbeddedPostgresConnection(
|
|||||||
const postmasterPidFile = path.resolve(dataDir, "postmaster.pid");
|
const postmasterPidFile = path.resolve(dataDir, "postmaster.pid");
|
||||||
const runningPid = readRunningPostmasterPid(postmasterPidFile);
|
const runningPid = readRunningPostmasterPid(postmasterPidFile);
|
||||||
const runningPort = readPidFilePort(postmasterPidFile);
|
const runningPort = readPidFilePort(postmasterPidFile);
|
||||||
|
const preferredAdminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${preferredPort}/postgres`;
|
||||||
|
|
||||||
|
if (!runningPid) {
|
||||||
|
try {
|
||||||
|
await ensurePostgresDatabase(preferredAdminConnectionString, "paperclip");
|
||||||
|
return {
|
||||||
|
connectionString: `postgres://paperclip:paperclip@127.0.0.1:${preferredPort}/paperclip`,
|
||||||
|
source: `embedded-postgres@${preferredPort}`,
|
||||||
|
stop: async () => {},
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
// Fall through and attempt to start the configured embedded cluster.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (runningPid) {
|
if (runningPid) {
|
||||||
const port = runningPort ?? preferredPort;
|
const port = runningPort ?? preferredPort;
|
||||||
@@ -108,10 +111,15 @@ async function ensureEmbeddedPostgresConnection(
|
|||||||
if (existsSync(postmasterPidFile)) {
|
if (existsSync(postmasterPidFile)) {
|
||||||
rmSync(postmasterPidFile, { force: true });
|
rmSync(postmasterPidFile, { force: true });
|
||||||
}
|
}
|
||||||
await instance.start();
|
try {
|
||||||
|
await instance.start();
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to start embedded PostgreSQL at ${dataDir} on port ${preferredPort}: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const adminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${preferredPort}/postgres`;
|
await ensurePostgresDatabase(preferredAdminConnectionString, "paperclip");
|
||||||
await ensurePostgresDatabase(adminConnectionString, "paperclip");
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
connectionString: `postgres://paperclip:paperclip@127.0.0.1:${preferredPort}/paperclip`,
|
connectionString: `postgres://paperclip:paperclip@127.0.0.1:${preferredPort}/paperclip`,
|
||||||
|
|||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -226,6 +226,9 @@ importers:
|
|||||||
drizzle-orm:
|
drizzle-orm:
|
||||||
specifier: ^0.38.4
|
specifier: ^0.38.4
|
||||||
version: 0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4)
|
version: 0.38.4(@electric-sql/pglite@0.3.15)(@types/react@19.2.14)(kysely@0.28.11)(pg@8.18.0)(postgres@3.4.8)(react@19.2.4)
|
||||||
|
embedded-postgres:
|
||||||
|
specifier: ^18.1.0-beta.16
|
||||||
|
version: 18.1.0-beta.16
|
||||||
postgres:
|
postgres:
|
||||||
specifier: ^3.4.5
|
specifier: ^3.4.5
|
||||||
version: 3.4.8
|
version: 3.4.8
|
||||||
|
|||||||
@@ -323,45 +323,53 @@ export async function startServer(): Promise<StartedServer> {
|
|||||||
if (runningPid) {
|
if (runningPid) {
|
||||||
logger.warn(`Embedded PostgreSQL already running; reusing existing process (pid=${runningPid}, port=${port})`);
|
logger.warn(`Embedded PostgreSQL already running; reusing existing process (pid=${runningPid}, port=${port})`);
|
||||||
} else {
|
} else {
|
||||||
const detectedPort = await detectPort(configuredPort);
|
const configuredAdminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${configuredPort}/postgres`;
|
||||||
if (detectedPort !== configuredPort) {
|
try {
|
||||||
logger.warn(`Embedded PostgreSQL port is in use; using next free port (requestedPort=${configuredPort}, selectedPort=${detectedPort})`);
|
await ensurePostgresDatabase(configuredAdminConnectionString, "paperclip");
|
||||||
}
|
logger.warn(
|
||||||
port = detectedPort;
|
`Embedded PostgreSQL appears to already be reachable without a pid file; reusing existing server on configured port ${configuredPort}`,
|
||||||
logger.info(`Using embedded PostgreSQL because no DATABASE_URL set (dataDir=${dataDir}, port=${port})`);
|
);
|
||||||
embeddedPostgres = new EmbeddedPostgres({
|
} catch {
|
||||||
databaseDir: dataDir,
|
const detectedPort = await detectPort(configuredPort);
|
||||||
user: "paperclip",
|
if (detectedPort !== configuredPort) {
|
||||||
password: "paperclip",
|
logger.warn(`Embedded PostgreSQL port is in use; using next free port (requestedPort=${configuredPort}, selectedPort=${detectedPort})`);
|
||||||
port,
|
}
|
||||||
persistent: true,
|
port = detectedPort;
|
||||||
initdbFlags: ["--encoding=UTF8", "--locale=C"],
|
logger.info(`Using embedded PostgreSQL because no DATABASE_URL set (dataDir=${dataDir}, port=${port})`);
|
||||||
onLog: appendEmbeddedPostgresLog,
|
embeddedPostgres = new EmbeddedPostgres({
|
||||||
onError: appendEmbeddedPostgresLog,
|
databaseDir: dataDir,
|
||||||
});
|
user: "paperclip",
|
||||||
|
password: "paperclip",
|
||||||
if (!clusterAlreadyInitialized) {
|
port,
|
||||||
|
persistent: true,
|
||||||
|
initdbFlags: ["--encoding=UTF8", "--locale=C"],
|
||||||
|
onLog: appendEmbeddedPostgresLog,
|
||||||
|
onError: appendEmbeddedPostgresLog,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!clusterAlreadyInitialized) {
|
||||||
|
try {
|
||||||
|
await embeddedPostgres.initialise();
|
||||||
|
} catch (err) {
|
||||||
|
logEmbeddedPostgresFailure("initialise", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info(`Embedded PostgreSQL cluster already exists (${clusterVersionFile}); skipping init`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existsSync(postmasterPidFile)) {
|
||||||
|
logger.warn("Removing stale embedded PostgreSQL lock file");
|
||||||
|
rmSync(postmasterPidFile, { force: true });
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await embeddedPostgres.initialise();
|
await embeddedPostgres.start();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logEmbeddedPostgresFailure("initialise", err);
|
logEmbeddedPostgresFailure("start", err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
} else {
|
embeddedPostgresStartedByThisProcess = true;
|
||||||
logger.info(`Embedded PostgreSQL cluster already exists (${clusterVersionFile}); skipping init`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existsSync(postmasterPidFile)) {
|
|
||||||
logger.warn("Removing stale embedded PostgreSQL lock file");
|
|
||||||
rmSync(postmasterPidFile, { force: true });
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await embeddedPostgres.start();
|
|
||||||
} catch (err) {
|
|
||||||
logEmbeddedPostgresFailure("start", err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
embeddedPostgresStartedByThisProcess = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const embeddedAdminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${port}/postgres`;
|
const embeddedAdminConnectionString = `postgres://paperclip:paperclip@127.0.0.1:${port}/postgres`;
|
||||||
|
|||||||
Reference in New Issue
Block a user