fix(ops): 自愈 goosed 会话 PostgreSQL,防止 H5 卡在创建新对话

103 Colima goosed 依赖宿主机 postgresql@17;库停止时 /agent/start 会 pool timeout 约 60s。新增 ensure 脚本、接入监控与发布前检查,并补充拓扑文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-06 16:57:38 +08:00
parent ecf553754c
commit b6def0f25b
4 changed files with 74 additions and 0 deletions
+10
View File
@@ -59,6 +59,16 @@ Portal must be released as a runtime artifact. Do not edit source directly on 10
| Compose env | `/Users/john/Project/goosed-prod/.env` |
| Image pattern | `tkmind/goosed:prod-${GOOSED_TAG}` |
| Session store | PostgreSQL `memind_sessions` through `GOOSE_SESSION_DB_URL` |
| Session PostgreSQL | **Host** `postgresql@17` on `127.0.0.1:5432` (`/opt/homebrew/var/postgresql@17`) |
**Critical:** Colima goosed reaches the session DB via `host.docker.internal:5432`. If host PostgreSQL is stopped, H5 `/agent/start` hangs ~60s then fails with `pool timed out`, and the UI stays on「正在创建新对话…」. Recovery:
```bash
ssh john@58.38.22.103 'bash /Users/john/Project/Memind/scripts/ensure-goose-session-postgres.sh'
ssh john@58.38.22.103 '/opt/homebrew/opt/postgresql@17/bin/pg_isready -h 127.0.0.1 -p 5432'
```
`cn.tkmind.goosed-monitor`103)运行 `goosed-prod/scripts/monitor-goosed-containers.mjs`,本地 native goosed 监控用 `scripts/monitor-goosed-fds.mjs`;两者都会在巡检时调用 `ensure-goose-session-postgres.sh`
Container layout:
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
# 103 Colima goosed 通过 host.docker.internal:5432 访问本机 PostgreSQL。
# 该库停止时,/agent/start 会在约 60s 后返回 pool timeout,H5 会卡在「正在创建新对话…」。
export PATH="/opt/homebrew/bin:/opt/homebrew/opt/postgresql@17/bin:${PATH:-/usr/bin:/bin}"
PGDATA="${GOOSE_SESSION_PGDATA:-/opt/homebrew/var/postgresql@17}"
PG_LOG="${GOOSE_SESSION_PG_LOG:-${HOME}/Library/Logs/postgresql@17.log}"
PG_HOST="${GOOSE_SESSION_PG_HOST:-127.0.0.1}"
PG_PORT="${GOOSE_SESSION_PG_PORT:-5432}"
if pg_isready -h "${PG_HOST}" -p "${PG_PORT}" -q 2>/dev/null; then
exit 0
fi
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) goose session postgres not ready; starting (${PGDATA})"
mkdir -p "$(dirname "${PG_LOG}")"
pg_ctl -D "${PGDATA}" -l "${PG_LOG}" start 2>/dev/null || true
sleep 2
if pg_isready -h "${PG_HOST}" -p "${PG_PORT}" -q 2>/dev/null; then
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) goose session postgres ready"
exit 0
fi
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) goose session postgres still down (${PG_HOST}:${PG_PORT})" >&2
exit 1
+25
View File
@@ -1,5 +1,10 @@
#!/usr/bin/env node
import { execFileSync, spawnSync } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ENSURE_GOOSE_SESSION_POSTGRES = path.join(__dirname, 'ensure-goose-session-postgres.sh');
const DRY_RUN = process.argv.includes('--dry-run');
const FD_WARN = Number(process.env.GOOSED_FD_WARN ?? 180);
@@ -122,5 +127,25 @@ function monitorGoosed() {
}
}
function ensureGooseSessionPostgres() {
try {
const result = spawnSync('/bin/bash', [ENSURE_GOOSE_SESSION_POSTGRES], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'],
});
const output = `${result.stdout ?? ''}${result.stderr ?? ''}`.trim();
if (output) console.log(output);
if (result.status !== 0) {
console.warn('goose session postgres ensure failed');
}
} catch (err) {
console.warn(
'goose session postgres ensure skipped:',
err instanceof Error ? err.message : err,
);
}
}
ensureGooseSessionPostgres();
cleanupSandboxProcesses();
monitorGoosed();
+11
View File
@@ -201,6 +201,17 @@ set -euo pipefail
missing=0
pg_isready_bin="/opt/homebrew/opt/postgresql@17/bin/pg_isready"
if [[ -x "${pg_isready_bin}" ]]; then
if ! "${pg_isready_bin}" -h 127.0.0.1 -p 5432 -q 2>/dev/null; then
echo "goosed dependency check failed: host PostgreSQL (127.0.0.1:5432) is not accepting connections" >&2
echo "Run: bash /Users/john/Project/Memind/scripts/ensure-goose-session-postgres.sh" >&2
missing=1
fi
else
echo "goosed dependency check warning: pg_isready not found; skipping PostgreSQL readiness check" >&2
fi
docker_bin="/opt/homebrew/bin/docker"
if [[ -x "${docker_bin}" ]] && "${docker_bin}" ps --format '{{.Names}}' 2>/dev/null | grep -q '^goosed-prod-1$'; then
for index in 1 2 3 4; do