Skip to content

Commit 7cadb24

Browse files
authored
refactor: remove JSON database backend and update db dump workflow (#560)
* refactor: remove JSON database backend [skip ci] * fix: keep feedback data out of public dumps * docs: sanitize backup restore placeholder * fix: restore xz database dump compression * docs: improve database dump instructions
1 parent 61f62d4 commit 7cadb24

52 files changed

Lines changed: 157 additions & 3252 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
# ╔══════════════════════════════════════════════════════════════════════════╗
22
# ║ Local development ║
33
# ║ ║
4-
# ║ Choose ONE option: JSON dump, TCP database, or HTTP database.
4+
# ║ Choose ONE option: standard PostgreSQL or Neon.
55
# ╚══════════════════════════════════════════════════════════════════════════╝
66

77
# LAN / remote dev: set this machine's IP so the dev server accepts cross-origin requests
88
# NEXT_DEV_ALLOWED_ORIGINS=10.112.9.49
99

10-
# Option A: JSON dump (no database needed)
11-
# Download a DB dump release, unzip it, and point this to the directory.
12-
# See README for setup instructions.
13-
# DUMP_DIR=./inferencex-dump/inferencex-dump-2026-03-30
14-
15-
# Option B: TCP database (postgres.js — works with any Postgres instance)
10+
# Option A: Standard PostgreSQL (postgres.js TCP driver)
1611
# DATABASE_READONLY_URL=postgresql://postgres:postgres@localhost:5432/postgres
1712
# DATABASE_DRIVER=postgres
1813
# DATABASE_SSL=false
1914

20-
# Option C: HTTP database (Neon serverless driver — requires a Neon-hosted instance)
21-
# DATABASE_READONLY_URL=
15+
# Option B: Neon (serverless HTTP driver)
16+
# DATABASE_READONLY_URL=postgresql://user:password@ep-example.us-east-1.aws.neon.tech/database
2217
# DATABASE_DRIVER=neon
2318
# DATABASE_SSL=true
2419

.github/workflows/db-backup.yml

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,44 +19,47 @@ jobs:
1919
permissions:
2020
contents: write # Create the database dump release
2121
steps:
22-
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
23-
with:
24-
persist-credentials: false
25-
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
26-
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
27-
with:
28-
node-version: '24'
29-
cache: pnpm
30-
- name: Install dependencies
31-
run: pnpm install --filter @semianalysisai/inferencex-db...
22+
- name: Dump and split database
3223
env:
33-
CYPRESS_INSTALL_BINARY: '0'
34-
35-
- name: Dump database
36-
env:
37-
DATABASE_READONLY_URL: ${{ secrets.DATABASE_READONLY_URL }}
24+
# Use primary; replica recovery can cancel long pg_dump snapshots.
25+
DATABASE_URL: ${{ secrets.DATABASE_WRITE_URL }}
3826
run: |
3927
set -euo pipefail
40-
DUMP_DIR="inferencex-dump-$(date -u +%Y-%m-%d)"
41-
pnpm admin:db:dump "$DUMP_DIR"
42-
echo "DUMP_DIR=${DUMP_DIR}" >> "$GITHUB_ENV"
43-
echo "TAG=db-dump/$(date -u +%Y-%m-%d)" >> "$GITHUB_ENV"
28+
DUMP_FILE="inferencex-$(date -u +%Y-%m-%d).dump.xz"
4429
45-
- name: Compress and split dump
46-
run: |
47-
set -euo pipefail
48-
( cd packages/db
49-
tar -cf - "$DUMP_DIR" \
50-
| xz -v -T0 --memlimit-compress=99% --lzma2=preset=9e,dict=192MiB \
51-
| split -b 1900m -d -a 2 - "${GITHUB_WORKSPACE}/${DUMP_DIR}.tar.xz.part" )
52-
echo "DUMP_GLOB=${DUMP_DIR}.tar.xz.part*" >> "$GITHUB_ENV"
30+
docker run --rm --env DATABASE_URL \
31+
postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 \
32+
sh -ceu 'exec pg_dump \
33+
--dbname="$DATABASE_URL" \
34+
--format=custom \
35+
--compress=none \
36+
--no-owner \
37+
--no-privileges \
38+
--exclude-table-data=public.user_feedback \
39+
--verbose' \
40+
| xz -v -T0 --memlimit-compress=99% --lzma2=preset=9e,dict=192MiB \
41+
| split -b 1900m -d -a 2 - "${DUMP_FILE}.part"
42+
43+
echo "DUMP_FILE=${DUMP_FILE}" >> "$GITHUB_ENV"
44+
echo "DUMP_GLOB=${DUMP_FILE}.part*" >> "$GITHUB_ENV"
45+
echo "TAG=db-dump/$(date -u +%Y-%m-%d)" >> "$GITHUB_ENV"
5346
5447
- name: Create release
5548
env:
5649
GH_TOKEN: ${{ github.token }}
5750
run: |
51+
cat > release-notes.md <<'EOF'
52+
Weekly InferenceX database dump.
53+
54+
Reassemble and decompress:
55+
`cat *.dump.xz.part* | xz -d > inferencex.dump`
56+
57+
Restore to PostgreSQL or Neon:
58+
`pg_restore --exit-on-error --clean --if-exists --no-owner --no-privileges --dbname="YOUR_TARGET_DATABASE_URL" inferencex.dump`
59+
EOF
60+
5861
# $DUMP_GLOB is intentionally unquoted so the shell expands it to every part file.
5962
gh release create "$TAG" $DUMP_GLOB \
6063
--title "DB Dump $(date -u +%Y-%m-%d)" \
61-
--notes 'Weekly full database dump (xz-compressed, split into parts under 2 GiB). Reassemble: `cat *.tar.xz.part* | xz -d | tar -x`' \
64+
--notes-file release-notes.md \
6265
--latest=false

README.md

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,30 +55,37 @@ cd InferenceX-app
5555
pnpm install
5656
```
5757

58-
### 2. Set Up Data Source
58+
### 2. Set Up a Database
5959

60-
You can run the dashboard against either a live database or a static JSON dump. The JSON dump approach requires no database setup and is the easiest way to get started.
60+
The dashboard requires a live PostgreSQL database. Set `DATABASE_READONLY_URL` in `.env`; the application supports both standard PostgreSQL servers and Neon.
6161

62-
#### Option A: JSON Dump (no database required, local dev only)
62+
#### Option A: Standard PostgreSQL
6363

64-
Download the latest DB dump from [GitHub Releases](https://github.com/SemiAnalysisAI/InferenceX-app/releases), unpack it, and point `DUMP_DIR` at the directory. The dump is xz-compressed and split into one or more `.tar.xz.part*` files; reassemble them by piping `cat` through `xz`. This only works with `pnpm dev`; production builds require a live database.
64+
Use the `postgres` driver for a local or remotely hosted PostgreSQL server:
6565

6666
```bash
6767
cp .env.example .env
68-
69-
# Download and unpack the latest dump (requires xz; `brew install xz` on macOS)
70-
gh release download db-dump/2026-03-30 -p 'inferencex-dump-*.tar.xz.part*'
71-
cat inferencex-dump-2026-03-30.tar.xz.part* | xz -d -T0 | tar -x
72-
73-
# Add to .env
74-
echo 'DUMP_DIR=./inferencex-dump-2026-03-30' >> .env
68+
cat >> .env <<'EOF'
69+
DATABASE_READONLY_URL=postgresql://postgres:postgres@localhost:5432/postgres
70+
DATABASE_DRIVER=postgres
71+
DATABASE_SSL=false
72+
EOF
7573
```
7674

77-
Make sure `DATABASE_READONLY_URL` is not set (or is commented out) in your `.env`.
75+
Remote PostgreSQL servers use TLS by default. Omit `DATABASE_SSL` unless the server explicitly requires it to be disabled.
76+
77+
#### Option B: Neon
7878

79-
#### Option B: Live Database
79+
Set `DATABASE_READONLY_URL` to a Neon PostgreSQL connection string. Neon hosts use the serverless HTTP driver automatically; `DATABASE_DRIVER=neon` can be set explicitly.
8080

81-
Set `DATABASE_READONLY_URL` in your `.env` to a Neon PostgreSQL connection string. See [`.env.example`](.env.example) for details.
81+
```bash
82+
cp .env.example .env
83+
cat >> .env <<'EOF'
84+
DATABASE_READONLY_URL=postgresql://user:password@ep-example.us-east-1.aws.neon.tech/database
85+
DATABASE_DRIVER=neon
86+
DATABASE_SSL=true
87+
EOF
88+
```
8289

8390
### 3. Run the Development Server
8491

README_zh.md

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,30 +55,37 @@ cd InferenceX-app
5555
pnpm install
5656
```
5757

58-
### 2. 配置数据源
58+
### 2. 配置数据库
5959

60-
仪表板可以对接实时数据库,也可以使用静态 JSON dump。JSON dump 方式无需配置数据库,是最简单的上手方式
60+
仪表板需要实时 PostgreSQL 数据库。在 `.env` 中设置 `DATABASE_READONLY_URL`;应用同时支持标准 PostgreSQL 服务器和 Neon
6161

62-
#### 方式 A:JSON Dump(无需数据库,仅限本地开发)
62+
#### 方式 A:标准 PostgreSQL
6363

64-
[GitHub Releases](https://github.com/SemiAnalysisAI/InferenceX-app/releases) 下载最新的数据库 dump,解包后将 `DUMP_DIR` 指向该目录。dump 以 xz 压缩并拆分为一个或多个 `.tar.xz.part*` 文件;用 `cat` 管道接 `xz` 重新组装。此方式仅适用于 `pnpm dev`;生产构建需要实时数据库。
64+
本地或远程托管的 PostgreSQL 服务器使用 `postgres` 驱动:
6565

6666
```bash
6767
cp .env.example .env
68-
69-
# 下载并解包最新 dump(需要 xz;macOS 上执行 `brew install xz`)
70-
gh release download db-dump/2026-03-30 -p 'inferencex-dump-*.tar.xz.part*'
71-
cat inferencex-dump-2026-03-30.tar.xz.part* | xz -d -T0 | tar -x
72-
73-
# 写入 .env
74-
echo 'DUMP_DIR=./inferencex-dump-2026-03-30' >> .env
68+
cat >> .env <<'EOF'
69+
DATABASE_READONLY_URL=postgresql://postgres:postgres@localhost:5432/postgres
70+
DATABASE_DRIVER=postgres
71+
DATABASE_SSL=false
72+
EOF
7573
```
7674

77-
确保 `.env` 中没有设置(或已注释掉)`DATABASE_READONLY_URL`
75+
远程 PostgreSQL 服务器默认使用 TLS。除非服务器明确要求禁用 TLS,否则请省略 `DATABASE_SSL`
76+
77+
#### 方式 B:Neon
7878

79-
#### 方式 B:实时数据库
79+
`DATABASE_READONLY_URL` 设为 Neon PostgreSQL 连接串。Neon 主机会自动使用无服务器 HTTP 驱动;也可以显式设置 `DATABASE_DRIVER=neon`
8080

81-
`.env` 中将 `DATABASE_READONLY_URL` 设为 Neon PostgreSQL 连接串。详见 [`.env.example`](.env.example)
81+
```bash
82+
cp .env.example .env
83+
cat >> .env <<'EOF'
84+
DATABASE_READONLY_URL=postgresql://user:password@ep-example.us-east-1.aws.neon.tech/database
85+
DATABASE_DRIVER=neon
86+
DATABASE_SSL=true
87+
EOF
88+
```
8289

8390
### 3. 启动开发服务器
8491

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
"admin:db:ingest:ci": "pnpm --filter *db db:ingest:ci",
3434
"admin:db:ingest:gcs": "pnpm --filter *db db:ingest:gcs",
3535
"admin:db:ingest:supplemental": "pnpm --filter *db db:ingest:supplemental",
36-
"admin:db:dump": "pnpm --filter *db db:dump",
37-
"admin:db:load-dump": "pnpm --filter *db db:load-dump --",
3836
"admin:db:migrate": "pnpm --filter *db db:migrate",
3937
"admin:db:apply-overrides": "pnpm --filter *db db:apply-overrides",
4038
"admin:db:reset": "pnpm --filter *db db:reset",

packages/app/src/app/api/v1/agentic-aggregates/route.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { JSON_MODE, getDb } from '@semianalysisai/inferencex-db/connection';
2-
import * as jsonProvider from '@semianalysisai/inferencex-db/json-provider';
1+
import { getDb } from '@semianalysisai/inferencex-db/connection';
2+
33
import {
44
getAgenticAggregates,
55
STATS_VERSION,
@@ -24,10 +24,7 @@ export const dynamic = 'force-dynamic';
2424
export const CACHE_KEY_PREFIX = `agentic-aggregates-v${STATS_VERSION}`;
2525

2626
const getCachedAgenticAggregates = cachedQuery(
27-
(ids: number[]): Promise<AgenticAggregateMap> => {
28-
if (JSON_MODE) return Promise.resolve(jsonProvider.getAgenticAggregates(ids));
29-
return getAgenticAggregates(getDb(), ids);
30-
},
27+
(ids: number[]): Promise<AgenticAggregateMap> => getAgenticAggregates(getDb(), ids),
3128
CACHE_KEY_PREFIX,
3229
{ blobOnly: true },
3330
);

packages/app/src/app/api/v1/agentic-cache-keys.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { describe, expect, it, vi } from 'vitest';
1414
// in the blob cache — stub both so importing the route is side-effect-free.
1515
vi.mock('@semianalysisai/inferencex-db/connection', () => ({
1616
getDb: vi.fn(() => 'mock-sql'),
17-
JSON_MODE: false,
1817
FIXTURES_MODE: false,
1918
}));
2019

packages/app/src/app/api/v1/availability/route.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ const { mockGetAvailabilityData, mockGetDb } = vi.hoisted(() => ({
77

88
vi.mock('@semianalysisai/inferencex-db/connection', () => ({
99
getDb: mockGetDb,
10-
JSON_MODE: false,
1110
FIXTURES_MODE: false,
1211
}));
1312

packages/app/src/app/api/v1/availability/route.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
import { NextResponse } from 'next/server';
22

3-
import { FIXTURES_MODE, JSON_MODE, getDb } from '@semianalysisai/inferencex-db/connection';
4-
import * as jsonProvider from '@semianalysisai/inferencex-db/json-provider';
3+
import { FIXTURES_MODE, getDb } from '@semianalysisai/inferencex-db/connection';
4+
55
import { getAvailabilityData } from '@semianalysisai/inferencex-db/queries/workflow-info';
66

77
import { cachedJson, cachedQuery } from '@/lib/api-cache';
88
import { loadFixture } from '@/lib/test-fixtures';
99

1010
export const dynamic = 'force-dynamic';
1111

12-
const getCachedAvailability = cachedQuery(() => {
13-
if (JSON_MODE) return Promise.resolve(jsonProvider.getAvailabilityData());
14-
return getAvailabilityData(getDb());
15-
}, 'availability');
12+
const getCachedAvailability = cachedQuery(() => getAvailabilityData(getDb()), 'availability');
1613

1714
export async function GET() {
1815
if (FIXTURES_MODE) return cachedJson(loadFixture('availability'));

packages/app/src/app/api/v1/benchmark-siblings/route.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { JSON_MODE, getDb } from '@semianalysisai/inferencex-db/connection';
2-
import * as jsonProvider from '@semianalysisai/inferencex-db/json-provider';
1+
import { getDb } from '@semianalysisai/inferencex-db/connection';
2+
33
import {
44
getBenchmarkSiblings,
55
type BenchmarkSiblings,
@@ -11,10 +11,10 @@ import { idQueryRoute } from '../id-routes';
1111

1212
export const dynamic = 'force-dynamic';
1313

14-
const getCachedSiblings = cachedQuery((id: number): Promise<BenchmarkSiblings | null> => {
15-
if (JSON_MODE) return Promise.resolve(jsonProvider.getBenchmarkSiblings(id));
16-
return getBenchmarkSiblings(getDb(), id);
17-
}, 'benchmark-siblings');
14+
const getCachedSiblings = cachedQuery(
15+
(id: number): Promise<BenchmarkSiblings | null> => getBenchmarkSiblings(getDb(), id),
16+
'benchmark-siblings',
17+
);
1818

1919
/**
2020
* GET /api/v1/benchmark-siblings?id=N

0 commit comments

Comments
 (0)