Skip to content

feat: embedded PostgreSQL — zero-config single-binary startup#77

Open
RyanLee-Dev wants to merge 1 commit into
mainfrom
worktree-embedded-pg
Open

feat: embedded PostgreSQL — zero-config single-binary startup#77
RyanLee-Dev wants to merge 1 commit into
mainfrom
worktree-embedded-pg

Conversation

@RyanLee-Dev

Copy link
Copy Markdown
Collaborator

Summary

像 Coder 一样,DATABASE_URL 为空时自动启动内嵌 PostgreSQL,实现 一条命令启动完整 Parsar 服务(后端 + 前端 + 数据库),无需 Docker、无需外部数据库、无需 .env 文件。

改动概览

文件 改动
server/internal/embeddedpg/embeddedpg.go 新增 — 封装 embedded-postgres-go,提供 Start/Stop/ConnectionURL
server/cmd/server/main.go 修改 — 在 openPool() 前检测空 DATABASE_URL,启动内嵌 PG,自动跑 goose migration,接入 graceful shutdown
start.sh 新增 — 一键启动脚本(含前端 build)
go.mod / go.sum 新增 github.com/fergusstrange/embedded-postgres v1.34.0 依赖

工作原理

parsar-server 启动
    ↓
检查 DATABASE_URL 是否设置
    ↓ 未设置 + dev profile
从 Maven 中央仓库下载预编译 PostgreSQL 二进制(首次约 30 秒)
    ↓
解压到 ~/.parsar/postgres/bin/
    ↓
initdb 初始化数据目录 ~/.parsar/postgres/data/(首次)
    ↓
fork 子进程启动 PostgreSQL,监听 127.0.0.1:15432
    ↓
CREATE DATABASE parsar(如不存在)
    ↓
自动执行 goose migration(server/migrations/*.sql)
    ↓
openPool() 连接内嵌 PG,进入正常启动流程
    ↓
Ctrl+C → graceful shutdown → 停止内嵌 PG

孤儿进程处理

如果上次进程异常退出(如 Ctrl+C 杀了 go run 但 PG 子进程未关停),再次启动时会自动检测端口 15432 是否被占用,如果是,先 kill 孤儿 PG 进程再启动新实例。

快速启动

方式 1:一键脚本

# 默认端口 18080
./start.sh

# 指定端口
./start.sh 19090

脚本会:

  1. 检查 apps/web/dist 是否存在,不存在则先 pnpm build 前端
  2. 设置必要环境变量(mock auth、public URL 等)
  3. 启动 Go 服务(含内嵌 PG)

方式 2:手动启动

# 1. 先 build 前端(只需一次)
cd apps/web && pnpm install && pnpm build && cd ../..

# 2. 启动服务
PARSAR_FEISHU_MOCK=true \
PARSAR_AGENT_DAEMON_OWNER_URL=http://127.0.0.1:18080 \
PARSAR_PUBLIC_URL=http://127.0.0.1:18080 \
PARSAR_WEB_DIST=$PWD/apps/web/dist \
go run ./server/cmd/server

方式 3:使用外部 PostgreSQL(生产 / 已有 DB)

# 设了 DATABASE_URL 就走原有逻辑,内嵌 PG 完全不触发
DATABASE_URL=postgres://user:pass@your-pg:5432/parsar \
go run ./server/cmd/server

与 Docker Compose 方案对比

Docker Compose 本 PR
启动命令 docker compose -f docker-compose.local.yml up ./start.sh
依赖 Docker Engine + Compose Go 工具链 + pnpm
数据库 独立 PG 容器 内嵌 PG 子进程
数据位置 Docker volume ~/.parsar/postgres/
首次启动 pull image (~300MB) 下载 PG binary (~30s)
后续启动 容器启动 ~5s 进程启动 <1s

两种方案并存,docker-compose.local.yml 不受任何影响。

数据目录结构

~/.parsar/
└── postgres/
    ├── bin/         # PostgreSQL 二进制(自动下载)
    ├── data/        # 数据目录(initdb 生成)
    └── runtime/     # PID 文件等运行时状态

注意事项

  • 仅限 dev profilePARSAR_FEISHU_MOCK=true 或 loopback PublicURL),生产环境仍强制要求外部 DATABASE_URL
  • 首次启动需联网(从 Maven 下载 PG 二进制),后续离线可用
  • 数据持久化在 ~/.parsar/postgres/data/,删掉即重置
  • 内嵌 PG 端口固定 15432,与 docker-compose.local.yml 的 PG 端口一致

Test plan

  • go build ./server/cmd/server 编译通过
  • 不设 DATABASE_URL./start.sh 19090 启动 — PG 自动下载、migration 完成、服务就绪
  • curl http://127.0.0.1:19090/healthz → 200
  • curl http://127.0.0.1:19090/ → 200 text/html(前端 SPA)
  • Ctrl+C → "embedded PostgreSQL stopped",PG 子进程正常关停
  • 再次启动 → 复用已有数据目录,不重新 initdb
  • 孤儿进程场景:kill -9 主进程后再次启动 → 自动 kill 残留 PG,正常启动
  • DATABASE_URL 指向外部 PG → 确认跳过内嵌逻辑,行为不变

🤖 Generated with Claude Code

When DATABASE_URL is unset and the server is in dev profile, the server
automatically downloads a PostgreSQL binary (via embedded-postgres-go),
initializes a data directory under ~/.parsar/postgres, runs goose
migrations, and connects — no Docker, no external DB, no .env required.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant