fix: 支持 runtime store 连接池 YAML 配置
This commit is contained in:
@@ -334,6 +334,7 @@
|
||||
"runtimePath": { "type": "string" },
|
||||
"imageTagMode": { "type": "string", "enum": ["short", "full"] },
|
||||
"workbench": { "$ref": "#/$defs/workbenchUiPolicy" },
|
||||
"runtimeStore": { "$ref": "#/$defs/runtimeStore" },
|
||||
"sourceRepo": { "type": "string" },
|
||||
"envReuseServices": {
|
||||
"type": "array",
|
||||
@@ -359,6 +360,20 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"runtimeStore": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"postgres": { "$ref": "#/$defs/postgresRuntimeStore" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"postgresRuntimeStore": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"poolMax": { "type": "integer", "minimum": 1 }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"workbenchUiPolicy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -137,11 +137,13 @@ test("postgres pool config makes HWLAB_CLOUD_DB_SSL_MODE authoritative over URL
|
||||
const disabled = buildPostgresPoolConfig({
|
||||
dbUrl: "postgres://hwlab_user:fixture-pass@db.example.test:5432/hwlab?sslmode=require&application_name=hwlab",
|
||||
sslMode: "disable",
|
||||
timeoutMs: "2500"
|
||||
timeoutMs: "2500",
|
||||
poolMax: "20"
|
||||
});
|
||||
|
||||
assert.equal(disabled.ssl, false);
|
||||
assert.equal(disabled.connectionTimeoutMillis, 2500);
|
||||
assert.equal(disabled.max, 20);
|
||||
const disabledUrl = new URL(disabled.connectionString);
|
||||
assert.equal(disabledUrl.searchParams.get("sslmode"), null);
|
||||
assert.equal(disabledUrl.searchParams.get("ssl"), null);
|
||||
@@ -163,7 +165,8 @@ test("configured postgres runtime passes normalized pool config to pg", async ()
|
||||
env: {
|
||||
HWLAB_CLOUD_RUNTIME_ADAPTER: "postgres",
|
||||
HWLAB_CLOUD_RUNTIME_DURABLE: "true",
|
||||
HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS: "1800"
|
||||
HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS: "1800",
|
||||
HWLAB_CLOUD_DB_POOL_MAX: "20"
|
||||
},
|
||||
dbUrl: "postgres://hwlab_user:fixture-pass@db.example.test:5432/hwlab?sslmode=require",
|
||||
sslMode: "disable",
|
||||
@@ -186,6 +189,7 @@ test("configured postgres runtime passes normalized pool config to pg", async ()
|
||||
assert.equal(pools.length, 1);
|
||||
assert.equal(pools[0].config.ssl, false);
|
||||
assert.equal(pools[0].config.connectionTimeoutMillis, 1800);
|
||||
assert.equal(pools[0].config.max, 20);
|
||||
assert.equal(new URL(pools[0].config.connectionString).searchParams.get("sslmode"), null);
|
||||
assert.equal(store.summary().blocker, RUNTIME_DURABLE_ADAPTER_SCHEMA_BLOCKED);
|
||||
assert.equal(store.summary().durabilityContract.blockedLayer, "schema");
|
||||
|
||||
@@ -36,6 +36,7 @@ export const RUNTIME_DURABILITY_REQUIRED_EVIDENCE = "runtime_adapter_schema_migr
|
||||
|
||||
export const RUNTIME_ADAPTER_ENV = "HWLAB_CLOUD_RUNTIME_ADAPTER";
|
||||
export const RUNTIME_DURABLE_ENV = "HWLAB_CLOUD_RUNTIME_DURABLE";
|
||||
export const RUNTIME_DB_POOL_MAX_ENV = "HWLAB_CLOUD_DB_POOL_MAX";
|
||||
|
||||
const requiredPostgresSchema = CLOUD_RUNTIME_DURABLE_TABLE_COLUMNS;
|
||||
|
||||
@@ -88,12 +89,14 @@ export function createConfiguredCloudRuntimeStore(options = {}) {
|
||||
return createCloudRuntimeStore(options);
|
||||
}
|
||||
|
||||
export function buildPostgresPoolConfig({ dbUrl, sslMode = "require", timeoutMs } = {}) {
|
||||
export function buildPostgresPoolConfig({ dbUrl, sslMode = "require", timeoutMs, poolMax } = {}) {
|
||||
const normalizedSslMode = normalizePostgresSslMode(sslMode);
|
||||
const normalizedPoolMax = normalizeRuntimePoolMax(poolMax);
|
||||
return {
|
||||
connectionString: normalizePostgresConnectionStringForSslMode(dbUrl, normalizedSslMode),
|
||||
ssl: postgresSslOption(normalizedSslMode),
|
||||
connectionTimeoutMillis: normalizeRuntimeTimeoutMs(timeoutMs)
|
||||
connectionTimeoutMillis: normalizeRuntimeTimeoutMs(timeoutMs),
|
||||
...(normalizedPoolMax !== null ? { max: normalizedPoolMax } : {})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1446,7 +1449,8 @@ export class PostgresCloudRuntimeStore {
|
||||
this.pool = new Pool(buildPostgresPoolConfig({
|
||||
dbUrl: this.dbUrl,
|
||||
sslMode: this.sslMode,
|
||||
timeoutMs: this.env?.HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS
|
||||
timeoutMs: this.env?.HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS,
|
||||
poolMax: this.env?.[RUNTIME_DB_POOL_MAX_ENV]
|
||||
}));
|
||||
return this.pool;
|
||||
}
|
||||
@@ -2232,3 +2236,14 @@ function normalizeRuntimeTimeoutMs(value) {
|
||||
}
|
||||
return Math.min(parsed, 5000);
|
||||
}
|
||||
|
||||
function normalizeRuntimePoolMax(value) {
|
||||
if (value === undefined || value === null || String(value).trim() === "") return null;
|
||||
const parsed = Number.parseInt(String(value), 10);
|
||||
if (!Number.isInteger(parsed) || parsed <= 0 || String(parsed) !== String(value).trim()) {
|
||||
const error = new Error(`${RUNTIME_DB_POOL_MAX_ENV} must be a positive integer`);
|
||||
error.code = "HWLAB_CLOUD_DB_POOL_MAX_INVALID";
|
||||
throw error;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
@@ -935,7 +935,11 @@ function deployServicesForProfile(deploy, profile) {
|
||||
.filter((service) => allowed.has(service.serviceId))
|
||||
.map((service) => {
|
||||
const copy = cloneJson(service);
|
||||
copy.env = mergeEnvMaps(copy.env, serviceDeclarationEnvForProfile(deploy, profile, service.serviceId));
|
||||
copy.env = mergeEnvMaps(
|
||||
copy.env,
|
||||
serviceDeclarationEnvForProfile(deploy, profile, service.serviceId),
|
||||
runtimeStoreEnvForProfile(deploy, profile, service.serviceId)
|
||||
);
|
||||
return [service.serviceId, copy];
|
||||
}));
|
||||
const laneServices = isRuntimeLane(profile) && Array.isArray(deploy.lanes?.[profile]?.services)
|
||||
@@ -944,7 +948,10 @@ function deployServicesForProfile(deploy, profile) {
|
||||
for (const override of laneServices) {
|
||||
const existing = services.get(override.serviceId) ?? {
|
||||
serviceId: override.serviceId,
|
||||
env: serviceDeclarationEnvForProfile(deploy, profile, override.serviceId)
|
||||
env: mergeEnvMaps(
|
||||
serviceDeclarationEnvForProfile(deploy, profile, override.serviceId),
|
||||
runtimeStoreEnvForProfile(deploy, profile, override.serviceId)
|
||||
)
|
||||
};
|
||||
services.set(override.serviceId, {
|
||||
...existing,
|
||||
@@ -972,6 +979,15 @@ function runtimeWorkbenchTraceTimelinePolicy(deploy, profile) {
|
||||
return Object.keys(result).length > 0 ? result : null;
|
||||
}
|
||||
|
||||
function runtimeStoreEnvForProfile(deploy, profile, serviceId) {
|
||||
if (!isRuntimeLane(profile) || serviceId !== "hwlab-cloud-api") return {};
|
||||
const postgres = runtimeLaneConfig(deploy, profile)?.runtimeStore?.postgres;
|
||||
if (!postgres || typeof postgres !== "object" || Array.isArray(postgres)) return {};
|
||||
const env = {};
|
||||
if (Number.isInteger(postgres.poolMax)) env.HWLAB_CLOUD_DB_POOL_MAX = String(postgres.poolMax);
|
||||
return env;
|
||||
}
|
||||
|
||||
function booleanEnv(value) {
|
||||
return value ? "1" : "0";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user