@caelo-cms/provisioning 0.2.48 → 0.2.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caelo-cms/provisioning",
3
- "version": "0.2.48",
3
+ "version": "0.2.50",
4
4
  "private": false,
5
5
  "license": "MPL-2.0",
6
6
  "description": "Pulumi-driven provisioning for Caelo CMS — `bunx @caelo-cms/provisioning --provider <self-hosted|gcp|aws|azure>` brings up a TLS-served install with Postgres + storage + secrets manager + the admin + the API gateway.",
@@ -38,7 +38,7 @@
38
38
  "prepublishOnly": "tsc -b --force"
39
39
  },
40
40
  "dependencies": {
41
- "@caelo-cms/shared": "^0.2.48",
41
+ "@caelo-cms/shared": "^0.2.50",
42
42
  "@clack/prompts": "1.3.0",
43
43
  "@pulumi/aws": "^7",
44
44
  "@pulumi/awsx": "^3",
@@ -54,6 +54,22 @@ config:
54
54
  type: string
55
55
  default: "0"
56
56
  description: Admin Cloud Run min instances. 0 = scale to zero. 1 = no cold start (+~$15/mo).
57
+ caelo-gcp:adminMaxInstances:
58
+ type: string
59
+ default: "5"
60
+ description: |
61
+ Admin Cloud Run max instances. Default 5 caps the connection-pool
62
+ footprint at ~100 Postgres conns (Bun SQL pool max=10, two pools per
63
+ admin instance). Bump alongside maxConnections + cloudSqlTier when
64
+ sizing for heavy editorial traffic.
65
+ caelo-gcp:maxConnections:
66
+ type: string
67
+ default: "100"
68
+ description: |
69
+ Postgres max_connections database flag. Default 100 matches the safe
70
+ ceiling for adminMaxInstances=5 + light gateway traffic. Cloud SQL's
71
+ db-f1-micro defaults to 25 — too low for any sustained admin load.
72
+ Tune up alongside cloudSqlTier when scaling out.
57
73
  caelo-gcp:gatewayMinInstances:
58
74
  type: string
59
75
  default: "0"
@@ -26,6 +26,12 @@ pulumi config set caelo-gcp:ownerEmail me@example.com
26
26
  pulumi config set caelo-gcp:project my-gcp-project
27
27
  pulumi config set caelo-gcp:region us-central1
28
28
 
29
+ # Optional: scale up beyond the safe-by-default ceiling. The defaults
30
+ # size for a small editorial install (admin maxScale 5 × Bun SQL pool
31
+ # max 10 × 2 pools = 100 conns, matching Postgres max_connections=100).
32
+ # pulumi config set caelo-gcp:adminMaxInstances 10
33
+ # pulumi config set caelo-gcp:maxConnections 200
34
+
29
35
  # Build + push images. Operator does this once + on every release.
30
36
  gcloud auth configure-docker us-central1-docker.pkg.dev
31
37
  gcloud builds submit ... --tag us-central1-docker.pkg.dev/$PROJECT/caelo/admin:latest
@@ -42,7 +42,26 @@ const cloudSqlEdition = cfg.get("cloudSqlEdition") ?? "ENTERPRISE";
42
42
  // wants to `caelo-cms destroy` shouldn't hit a wall).
43
43
  const deletionProtection = cfg.getBoolean("deletionProtection") ?? false;
44
44
  const adminMinInstances = Number.parseInt(cfg.get("adminMinInstances") ?? "0", 10);
45
+ // adminMaxInstances default is intentionally low. Each admin process holds
46
+ // TWO Bun SQL pools (admin_role on cms_admin + admin_role on cms_public),
47
+ // each with default pool-max=10 → up to ~20 connections per instance under
48
+ // sustained load. With db-f1-micro's tunable max_connections, even 5 admin
49
+ // instances saturate the pool (5 × 20 = 100 = our default ceiling below).
50
+ // Operators on larger SQL tiers should bump maxConnections AND
51
+ // adminMaxInstances together.
52
+ //
53
+ // Why 5, not 10 (Cloud Run's default)? An incident on production showed
54
+ // admin scaling to 10 under bursty editor traffic, demanding 200 slots
55
+ // against the original 25-slot Postgres default — connection exhaustion
56
+ // took the admin offline. v0.2.49 bakes the post-incident shape so fresh
57
+ // installs don't trip the same wall.
58
+ const adminMaxInstances = Number.parseInt(cfg.get("adminMaxInstances") ?? "5", 10);
45
59
  const gatewayMinInstances = Number.parseInt(cfg.get("gatewayMinInstances") ?? "0", 10);
60
+ // Postgres connection slot ceiling. Cloud SQL's db-f1-micro defaults to 25,
61
+ // which 1 admin pool exhausts under load (Bun SQL pool-max=10, two pools per
62
+ // admin instance, plus gateway + migrations + backup-agent overhead). 100 is
63
+ // the safe floor for a small editorial install — tune up alongside SQL tier.
64
+ const maxConnections = Number.parseInt(cfg.get("maxConnections") ?? "100", 10);
46
65
  const wafAdaptiveProtection = cfg.getBoolean("wafAdaptiveProtection") ?? false;
47
66
  const backupRetentionDays = Number.parseInt(cfg.get("backupRetentionDays") ?? "7", 10);
48
67
  // IAP allowlist: comma-list of "user:<email>" or "group:<group@domain>".
@@ -188,6 +207,12 @@ const sqlInstance = new gcp.sql.DatabaseInstance(
188
207
  diskSize: 20,
189
208
  diskType: "PD_SSD",
190
209
  diskAutoresize: true,
210
+ // db-f1-micro defaults max_connections=25, which a single
211
+ // admin Cloud Run instance can exhaust under load (Bun SQL
212
+ // pool-max=10, two pools per admin process). Override at
213
+ // instance level so the cap matches the Cloud Run scale-out
214
+ // headroom defined by adminMaxInstances + gatewayMaxInstances.
215
+ databaseFlags: [{ name: "max_connections", value: maxConnections.toString() }],
191
216
  backupConfiguration: {
192
217
  enabled: true,
193
218
  pointInTimeRecoveryEnabled: cloudSqlHa,
@@ -490,7 +515,7 @@ function cloudRunService(args: CloudRunArgs): gcp.cloudrunv2.Service {
490
515
  const adminSvc = cloudRunService({
491
516
  serviceName: "admin",
492
517
  minInstances: adminMinInstances,
493
- maxInstances: 10,
518
+ maxInstances: adminMaxInstances,
494
519
  memory: "1Gi",
495
520
  // Admin's second pool: admin_role on cms_public for cross-DB reads
496
521
  // + DDL + migrations. NOT public_role (write-limited).