pgserve 1.1.3-rc.7 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/context/windows-debug.md +119 -0
- package/.github/workflows/build-all-platforms.yml +20 -7
- package/README.md +113 -19
- package/assets/icon.ico +0 -0
- package/bin/pglite-server.js +61 -8
- package/eslint.config.js +2 -0
- package/package.json +1 -1
- package/src/cluster.js +88 -10
- package/src/dashboard.js +10 -4
- package/src/index.js +2 -0
- package/src/postgres.js +143 -16
- package/src/stats-collector.js +267 -0
- package/src/stats-dashboard.js +382 -0
- package/tests/benchmarks/runner.js +871 -50
- package/tests/benchmarks/vector-generator.js +368 -0
- package/tests/quick-bench.js +135 -0
- package/tests/stress-test.js +439 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Windows Debug Context
|
|
2
|
+
|
|
3
|
+
## RESOLVED (2025-12-12)
|
|
4
|
+
|
|
5
|
+
The Windows router binding issue has been fixed. See "Solution Applied" below.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Original Issue
|
|
10
|
+
PostgreSQL starts successfully on port 9432, but the router/proxy on port 8432 was NOT listening.
|
|
11
|
+
Clients could not connect because 8432 wasn't bound.
|
|
12
|
+
|
|
13
|
+
## Root Causes Found
|
|
14
|
+
|
|
15
|
+
### 1. `reusePort: true` - Linux-only feature (PRIMARY CAUSE)
|
|
16
|
+
**Location:** `src/cluster.js:75`
|
|
17
|
+
|
|
18
|
+
The cluster mode uses `Bun.listen({ reusePort: true })` which maps to `SO_REUSEPORT`.
|
|
19
|
+
This socket option is Linux-only and **silently fails on Windows**.
|
|
20
|
+
|
|
21
|
+
### 2. Auto-enabled cluster mode on multi-core Windows systems
|
|
22
|
+
**Location:** `bin/pglite-server.js:106`
|
|
23
|
+
|
|
24
|
+
Windows systems with multiple CPU cores automatically entered cluster mode,
|
|
25
|
+
triggering the `reusePort` failure.
|
|
26
|
+
|
|
27
|
+
### 3. TCP port opens before PostgreSQL ready for protocol handshakes
|
|
28
|
+
**Location:** `src/postgres.js:803-821`
|
|
29
|
+
|
|
30
|
+
PostgreSQL was marked "ready" when TCP port opened, but it wasn't actually
|
|
31
|
+
ready for protocol-level handshakes. Admin pool connection timed out.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Solution Applied
|
|
36
|
+
|
|
37
|
+
### Fix 1: Disable cluster mode on Windows
|
|
38
|
+
**File:** `bin/pglite-server.js:98-108`
|
|
39
|
+
```javascript
|
|
40
|
+
const isWindows = os.platform() === 'win32';
|
|
41
|
+
cluster: cpuCount > 1 && !isWindows,
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Fix 2: Add platform check to reusePort
|
|
45
|
+
**File:** `src/cluster.js:72-76`
|
|
46
|
+
```javascript
|
|
47
|
+
const isWindows = os.platform() === 'win32';
|
|
48
|
+
this.server = Bun.listen({
|
|
49
|
+
reusePort: !isWindows, // SO_REUSEPORT only works on Linux/macOS
|
|
50
|
+
...
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Fix 3: Add port binding verification
|
|
55
|
+
**File:** `src/cluster.js:99-102`
|
|
56
|
+
```javascript
|
|
57
|
+
if (!this.server || !this.server.port) {
|
|
58
|
+
throw new Error(`Failed to bind to port ${this.port} - reusePort may not be supported`);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Fix 4: Add Windows readiness delay
|
|
63
|
+
**File:** `src/postgres.js:813-817`
|
|
64
|
+
```javascript
|
|
65
|
+
if (isWindows) {
|
|
66
|
+
await Bun.sleep(2000); // 2 second delay for Windows
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Fix 5: Increase admin pool retry for Windows
|
|
71
|
+
**File:** `src/postgres.js:598-599`
|
|
72
|
+
```javascript
|
|
73
|
+
const maxRetries = isWindows ? 10 : 5;
|
|
74
|
+
const baseDelay = isWindows ? 2000 : 1000;
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Verification
|
|
80
|
+
|
|
81
|
+
After applying fixes, server runs correctly on Windows:
|
|
82
|
+
- Router: `127.0.0.1:7432` ✅
|
|
83
|
+
- PostgreSQL: `127.0.0.1:8432` ✅
|
|
84
|
+
- Database auto-provisioning: ✅
|
|
85
|
+
- Query execution: ✅
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Server started successfully!
|
|
89
|
+
|
|
90
|
+
Endpoint: postgresql://127.0.0.1:7432/<database>
|
|
91
|
+
Mode: In-memory (ephemeral)
|
|
92
|
+
PostgreSQL: Port 8432 (internal)
|
|
93
|
+
Auto-create: Enabled
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Test Commands (Run from Windows)
|
|
99
|
+
```cmd
|
|
100
|
+
# Build binary
|
|
101
|
+
bun build --compile bin/pglite-server.js --outfile dist/pgserve-windows-x64.exe
|
|
102
|
+
|
|
103
|
+
# Start server
|
|
104
|
+
dist\pgserve-windows-x64.exe
|
|
105
|
+
|
|
106
|
+
# Check ports are listening (should see BOTH)
|
|
107
|
+
netstat -an | findstr "LISTEN" | findstr "8432 9432"
|
|
108
|
+
|
|
109
|
+
# Test connection
|
|
110
|
+
psql postgresql://localhost:8432/testdb
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Related Files
|
|
116
|
+
- `bin/pglite-server.js` - Entry point, cluster mode decision
|
|
117
|
+
- `src/cluster.js` - Cluster mode with reusePort
|
|
118
|
+
- `src/router.js` - Single-process mode (works on Windows)
|
|
119
|
+
- `src/postgres.js` - PostgreSQL startup and admin pool
|
|
@@ -70,21 +70,34 @@ jobs:
|
|
|
70
70
|
- name: Install dependencies
|
|
71
71
|
run: bun install
|
|
72
72
|
|
|
73
|
-
# Windows: native build with
|
|
74
|
-
- name: Build for Windows (with
|
|
73
|
+
# Windows: native build with icon and proper metadata
|
|
74
|
+
- name: Build for Windows (with branding)
|
|
75
75
|
if: matrix.platform == 'windows-x64'
|
|
76
76
|
run: |
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
New-Item -ItemType Directory -Force -Path dist | Out-Null
|
|
78
|
+
$RAW_VERSION = node -p "require('./package.json').version"
|
|
79
|
+
# Convert 1.1.3-rc.10 to 1.1.3.10 (Windows requires X.Y.Z.W format)
|
|
80
|
+
$WIN_VERSION = $RAW_VERSION -replace '-rc\.', '.'
|
|
81
|
+
Write-Host "Raw version: $RAW_VERSION -> Windows version: $WIN_VERSION"
|
|
82
|
+
bun build --compile `
|
|
83
|
+
--define BUILD_VERSION="'$RAW_VERSION'" `
|
|
84
|
+
--windows-icon=assets/icon.ico `
|
|
85
|
+
--windows-title="pgserve" `
|
|
86
|
+
--windows-publisher="Namastex Labs" `
|
|
87
|
+
--windows-description="Embedded PostgreSQL Server - Zero config, auto-provision, unlimited connections" `
|
|
88
|
+
--windows-version="$WIN_VERSION" `
|
|
89
|
+
--windows-copyright="Copyright (c) 2025 Namastex Labs" `
|
|
90
|
+
bin/pglite-server.js --outfile dist/${{ matrix.output }}
|
|
91
|
+
Get-ChildItem dist/
|
|
92
|
+
shell: pwsh
|
|
81
93
|
|
|
82
94
|
# Linux/macOS: native build
|
|
83
95
|
- name: Build for ${{ matrix.platform }}
|
|
84
96
|
if: matrix.platform != 'windows-x64'
|
|
85
97
|
run: |
|
|
86
98
|
mkdir -p dist
|
|
87
|
-
|
|
99
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
100
|
+
bun build --compile --define BUILD_VERSION="'$VERSION'" bin/pglite-server.js --outfile dist/${{ matrix.output }}
|
|
88
101
|
ls -lh dist/
|
|
89
102
|
|
|
90
103
|
- name: Upload artifact
|
package/README.md
CHANGED
|
@@ -260,46 +260,140 @@ pgserve --sync-to "postgresql://..." --sync-databases "myapp,tenant_*"
|
|
|
260
260
|
|
|
261
261
|
## Performance
|
|
262
262
|
|
|
263
|
-
###
|
|
263
|
+
### CRUD Benchmarks
|
|
264
264
|
|
|
265
265
|
<table>
|
|
266
266
|
<tr>
|
|
267
267
|
<th>Scenario</th>
|
|
268
268
|
<th>SQLite</th>
|
|
269
269
|
<th>PGlite</th>
|
|
270
|
-
<th>
|
|
271
|
-
<th>pgserve
|
|
270
|
+
<th>PostgreSQL</th>
|
|
271
|
+
<th>pgserve</th>
|
|
272
272
|
<th>pgserve --ram</th>
|
|
273
273
|
</tr>
|
|
274
274
|
<tr>
|
|
275
275
|
<td><b>Concurrent Writes</b> (10 agents)</td>
|
|
276
|
-
<td>
|
|
277
|
-
<td>
|
|
278
|
-
<td>
|
|
279
|
-
<td>
|
|
280
|
-
<td><b>4,
|
|
276
|
+
<td>91 qps</td>
|
|
277
|
+
<td>204 qps</td>
|
|
278
|
+
<td>1,667 qps</td>
|
|
279
|
+
<td>2,273 qps</td>
|
|
280
|
+
<td><b>4,167 qps</b> 🏆</td>
|
|
281
281
|
</tr>
|
|
282
282
|
<tr>
|
|
283
283
|
<td><b>Mixed Workload</b></td>
|
|
284
|
-
<td>
|
|
285
|
-
<td>
|
|
286
|
-
<td>
|
|
287
|
-
<td>
|
|
288
|
-
<td><b>
|
|
284
|
+
<td>383 qps</td>
|
|
285
|
+
<td>484 qps</td>
|
|
286
|
+
<td>507 qps</td>
|
|
287
|
+
<td>1,133 qps</td>
|
|
288
|
+
<td><b>2,109 qps</b> 🏆</td>
|
|
289
289
|
</tr>
|
|
290
290
|
<tr>
|
|
291
291
|
<td><b>Write Lock</b> (50 writers)</td>
|
|
292
|
-
<td>
|
|
293
|
-
<td>
|
|
294
|
-
<td>
|
|
295
|
-
<td>
|
|
292
|
+
<td>111 qps</td>
|
|
293
|
+
<td>228 qps</td>
|
|
294
|
+
<td>2,857 qps</td>
|
|
295
|
+
<td>3,030 qps</td>
|
|
296
296
|
<td><b>4,348 qps</b> 🏆</td>
|
|
297
297
|
</tr>
|
|
298
298
|
</table>
|
|
299
299
|
|
|
300
|
-
|
|
300
|
+
### Vector Benchmarks (pgvector)
|
|
301
|
+
|
|
302
|
+
<table>
|
|
303
|
+
<tr>
|
|
304
|
+
<th>Metric</th>
|
|
305
|
+
<th>PGlite</th>
|
|
306
|
+
<th>PostgreSQL</th>
|
|
307
|
+
<th>pgserve</th>
|
|
308
|
+
<th>pgserve --ram</th>
|
|
309
|
+
</tr>
|
|
310
|
+
<tr>
|
|
311
|
+
<td><b>Vector INSERT</b> (1000 × 1536-dim)</td>
|
|
312
|
+
<td>152/sec</td>
|
|
313
|
+
<td>392/sec</td>
|
|
314
|
+
<td>387/sec</td>
|
|
315
|
+
<td><b>1,082/sec</b> 🏆</td>
|
|
316
|
+
</tr>
|
|
317
|
+
<tr>
|
|
318
|
+
<td><b>k-NN Search</b> (k=10, 10k corpus)</td>
|
|
319
|
+
<td>22 qps</td>
|
|
320
|
+
<td>33 qps</td>
|
|
321
|
+
<td>31 qps</td>
|
|
322
|
+
<td>30 qps</td>
|
|
323
|
+
</tr>
|
|
324
|
+
<tr>
|
|
325
|
+
<td><b>Recall@10</b></td>
|
|
326
|
+
<td>100%</td>
|
|
327
|
+
<td>100%</td>
|
|
328
|
+
<td>100%</td>
|
|
329
|
+
<td>100%</td>
|
|
330
|
+
</tr>
|
|
331
|
+
</table>
|
|
332
|
+
|
|
333
|
+
> <b>Why pgserve wins on writes:</b> RAM mode uses <code>/dev/shm</code> (tmpfs), eliminating fsync latency. Vector search is CPU-bound, so RAM mode shows minimal benefit there.
|
|
334
|
+
|
|
335
|
+
### Final Score
|
|
336
|
+
|
|
337
|
+
<table>
|
|
338
|
+
<tr>
|
|
339
|
+
<th>Engine</th>
|
|
340
|
+
<th>CRUD QPS</th>
|
|
341
|
+
<th>Vec QPS</th>
|
|
342
|
+
<th>Recall</th>
|
|
343
|
+
<th>P50</th>
|
|
344
|
+
<th>P99</th>
|
|
345
|
+
<th>Score</th>
|
|
346
|
+
</tr>
|
|
347
|
+
<tr>
|
|
348
|
+
<td>SQLite</td>
|
|
349
|
+
<td>195</td>
|
|
350
|
+
<td>N/A</td>
|
|
351
|
+
<td>N/A</td>
|
|
352
|
+
<td>6.3ms</td>
|
|
353
|
+
<td>17.3ms</td>
|
|
354
|
+
<td>117</td>
|
|
355
|
+
</tr>
|
|
356
|
+
<tr>
|
|
357
|
+
<td>PGlite</td>
|
|
358
|
+
<td>305</td>
|
|
359
|
+
<td>65</td>
|
|
360
|
+
<td>100%</td>
|
|
361
|
+
<td>3.3ms</td>
|
|
362
|
+
<td>7.0ms</td>
|
|
363
|
+
<td>209</td>
|
|
364
|
+
</tr>
|
|
365
|
+
<tr>
|
|
366
|
+
<td>PostgreSQL</td>
|
|
367
|
+
<td>1,677</td>
|
|
368
|
+
<td>152</td>
|
|
369
|
+
<td>100%</td>
|
|
370
|
+
<td>6.0ms</td>
|
|
371
|
+
<td>19.0ms</td>
|
|
372
|
+
<td>1,067</td>
|
|
373
|
+
</tr>
|
|
374
|
+
<tr>
|
|
375
|
+
<td>pgserve</td>
|
|
376
|
+
<td>2,145</td>
|
|
377
|
+
<td>149</td>
|
|
378
|
+
<td>100%</td>
|
|
379
|
+
<td>5.3ms</td>
|
|
380
|
+
<td>13.0ms</td>
|
|
381
|
+
<td>1,347</td>
|
|
382
|
+
</tr>
|
|
383
|
+
<tr>
|
|
384
|
+
<td><b>pgserve --ram</b></td>
|
|
385
|
+
<td><b>3,541</b></td>
|
|
386
|
+
<td><b>381</b></td>
|
|
387
|
+
<td><b>100%</b></td>
|
|
388
|
+
<td><b>3.3ms</b></td>
|
|
389
|
+
<td><b>10.7ms</b></td>
|
|
390
|
+
<td><b>2,277</b> 🏆</td>
|
|
391
|
+
</tr>
|
|
392
|
+
</table>
|
|
393
|
+
|
|
394
|
+
> <b>Methodology:</b> Recall@k measured against brute-force ground truth (industry standard). PostgreSQL baseline is Docker <code>pgvector/pgvector:pg17</code>. RAM mode available on Linux and WSL2.
|
|
301
395
|
>
|
|
302
|
-
> Run benchmarks:
|
|
396
|
+
> Run benchmarks yourself: <code>bun tests/benchmarks/runner.js --include-vector</code>
|
|
303
397
|
|
|
304
398
|
<br>
|
|
305
399
|
|
package/assets/icon.ico
CHANGED
|
Binary file
|
package/bin/pglite-server.js
CHANGED
|
@@ -53,6 +53,8 @@ OPTIONS:
|
|
|
53
53
|
--no-provision Disable auto-provisioning of databases
|
|
54
54
|
--sync-to <url> Sync to real PostgreSQL (async replication)
|
|
55
55
|
--sync-databases Database patterns to sync (comma-separated, e.g. "myapp,tenant_*")
|
|
56
|
+
--no-stats Disable real-time stats dashboard (enabled by default)
|
|
57
|
+
--max-connections Max concurrent connections (default: 1000)
|
|
56
58
|
--help Show this help message
|
|
57
59
|
|
|
58
60
|
MODES:
|
|
@@ -94,7 +96,9 @@ FEATURES:
|
|
|
94
96
|
*/
|
|
95
97
|
function parseArgs() {
|
|
96
98
|
// Auto-enable cluster mode on multi-core systems for best performance
|
|
99
|
+
// Note: Cluster mode uses SO_REUSEPORT which is not supported on Windows
|
|
97
100
|
const cpuCount = os.cpus().length;
|
|
101
|
+
const isWindows = os.platform() === 'win32';
|
|
98
102
|
|
|
99
103
|
const options = {
|
|
100
104
|
port: 8432,
|
|
@@ -103,10 +107,12 @@ function parseArgs() {
|
|
|
103
107
|
useRam: false, // Use /dev/shm for true RAM storage (Linux only)
|
|
104
108
|
logLevel: 'info',
|
|
105
109
|
autoProvision: true,
|
|
106
|
-
cluster: cpuCount > 1, // Auto-enable on multi-core (
|
|
110
|
+
cluster: cpuCount > 1 && !isWindows, // Auto-enable on multi-core (disabled on Windows - no SO_REUSEPORT)
|
|
107
111
|
workers: null, // null = use CPU count
|
|
108
112
|
syncTo: null, // Sync target PostgreSQL URL
|
|
109
|
-
syncDatabases: null // Database patterns to sync (comma-separated)
|
|
113
|
+
syncDatabases: null, // Database patterns to sync (comma-separated)
|
|
114
|
+
showStats: true, // Show real-time stats dashboard (default: enabled)
|
|
115
|
+
maxConnections: 1000 // Max concurrent connections (high default for multi-tenant)
|
|
110
116
|
};
|
|
111
117
|
|
|
112
118
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -161,6 +167,18 @@ function parseArgs() {
|
|
|
161
167
|
options.syncDatabases = args[++i];
|
|
162
168
|
break;
|
|
163
169
|
|
|
170
|
+
case '--stats':
|
|
171
|
+
options.showStats = true;
|
|
172
|
+
break;
|
|
173
|
+
|
|
174
|
+
case '--no-stats':
|
|
175
|
+
options.showStats = false;
|
|
176
|
+
break;
|
|
177
|
+
|
|
178
|
+
case '--max-connections':
|
|
179
|
+
options.maxConnections = parseInt(args[++i], 10);
|
|
180
|
+
break;
|
|
181
|
+
|
|
164
182
|
case '--help':
|
|
165
183
|
case 'help':
|
|
166
184
|
printHelp();
|
|
@@ -209,7 +227,8 @@ pgserve - Embedded PostgreSQL Server
|
|
|
209
227
|
useRam: options.useRam,
|
|
210
228
|
logLevel: options.logLevel,
|
|
211
229
|
autoProvision: options.autoProvision,
|
|
212
|
-
workers: options.workers
|
|
230
|
+
workers: options.workers,
|
|
231
|
+
maxConnections: options.maxConnections
|
|
213
232
|
});
|
|
214
233
|
|
|
215
234
|
// Only primary process shows full startup message
|
|
@@ -220,7 +239,7 @@ pgserve - Embedded PostgreSQL Server
|
|
|
220
239
|
Cluster started successfully!
|
|
221
240
|
|
|
222
241
|
Endpoint: postgresql://${options.host}:${options.port}/<database>
|
|
223
|
-
Mode: ${memoryMode ? '
|
|
242
|
+
Mode: ${memoryMode ? (options.useRam ? 'RAM (/dev/shm)' : 'Ephemeral (temp)') : 'Persistent'} (Cluster)
|
|
224
243
|
Workers: ${stats.workers} processes
|
|
225
244
|
Data: ${storageType}
|
|
226
245
|
Auto-create: ${options.autoProvision ? 'Enabled' : 'Disabled'}
|
|
@@ -242,7 +261,8 @@ Press Ctrl+C to stop
|
|
|
242
261
|
logLevel: options.logLevel,
|
|
243
262
|
autoProvision: options.autoProvision,
|
|
244
263
|
syncTo: options.syncTo,
|
|
245
|
-
syncDatabases: options.syncDatabases
|
|
264
|
+
syncDatabases: options.syncDatabases,
|
|
265
|
+
maxConnections: options.maxConnections
|
|
246
266
|
});
|
|
247
267
|
|
|
248
268
|
server = router;
|
|
@@ -256,7 +276,7 @@ Press Ctrl+C to stop
|
|
|
256
276
|
Server started successfully!
|
|
257
277
|
|
|
258
278
|
Endpoint: postgresql://${options.host}:${options.port}/<database>
|
|
259
|
-
Mode: ${memoryMode ? '
|
|
279
|
+
Mode: ${memoryMode ? (options.useRam ? 'RAM (/dev/shm)' : 'Ephemeral (temp)') : 'Persistent'}
|
|
260
280
|
Data: ${storageType}
|
|
261
281
|
PostgreSQL: Port ${router.pgPort} (internal)
|
|
262
282
|
Auto-create: ${options.autoProvision ? 'Enabled' : 'Disabled'}
|
|
@@ -270,12 +290,45 @@ Press Ctrl+C to stop
|
|
|
270
290
|
`);
|
|
271
291
|
}
|
|
272
292
|
|
|
293
|
+
// Start stats dashboard if requested (only for primary/single-process)
|
|
294
|
+
let dashboard = null;
|
|
295
|
+
if (options.showStats && !process.env.PGSERVE_WORKER) {
|
|
296
|
+
const { StatsDashboard } = await import('../src/stats-dashboard.js');
|
|
297
|
+
const { StatsCollector } = await import('../src/stats-collector.js');
|
|
298
|
+
|
|
299
|
+
// Create stats collector with appropriate sources
|
|
300
|
+
const collector = new StatsCollector({
|
|
301
|
+
router: options.cluster ? null : server,
|
|
302
|
+
pgManager: server.pgManager,
|
|
303
|
+
clusterStats: options.cluster ? () => server.getStats() : null,
|
|
304
|
+
logger: server.logger,
|
|
305
|
+
port: options.port,
|
|
306
|
+
host: options.host
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
dashboard = new StatsDashboard({
|
|
310
|
+
refreshInterval: 2000, // 2 second refresh for real-time feel
|
|
311
|
+
statsProvider: () => collector.collect()
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
dashboard.start();
|
|
315
|
+
}
|
|
316
|
+
|
|
273
317
|
// Graceful shutdown (only for primary/single-process, workers handle via IPC)
|
|
274
318
|
if (!process.env.PGSERVE_WORKER) {
|
|
275
319
|
const shutdown = async () => {
|
|
320
|
+
// Stop dashboard first to restore cursor
|
|
321
|
+
if (dashboard) {
|
|
322
|
+
dashboard.stop();
|
|
323
|
+
}
|
|
276
324
|
console.log('\nShutting down...');
|
|
277
|
-
|
|
278
|
-
|
|
325
|
+
try {
|
|
326
|
+
await server.stop();
|
|
327
|
+
console.log('Server stopped.');
|
|
328
|
+
} catch (err) {
|
|
329
|
+
console.error('Error during shutdown:', err.message);
|
|
330
|
+
// Still exit - best effort cleanup
|
|
331
|
+
}
|
|
279
332
|
process.exit(0);
|
|
280
333
|
};
|
|
281
334
|
|
package/eslint.config.js
CHANGED