@intentsolutionsio/supabase-pack 1.0.0 → 1.0.3
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/LICENSE +1 -1
- package/README.md +73 -47
- package/package.json +4 -4
- package/skills/supabase-advanced-troubleshooting/SKILL.md +404 -200
- package/skills/supabase-advanced-troubleshooting/references/errors.md +11 -0
- package/skills/supabase-advanced-troubleshooting/references/evidence-collection-framework.md +34 -0
- package/skills/supabase-advanced-troubleshooting/references/examples.md +11 -0
- package/skills/supabase-advanced-troubleshooting/references/rls-edge-functions-realtime.md +363 -0
- package/skills/supabase-advanced-troubleshooting/references/systematic-isolation.md +56 -0
- package/skills/supabase-advanced-troubleshooting/references/timing-analysis.md +35 -0
- package/skills/supabase-architecture-variants/SKILL.md +395 -216
- package/skills/supabase-architecture-variants/references/errors.md +11 -0
- package/skills/supabase-architecture-variants/references/examples.md +12 -0
- package/skills/supabase-architecture-variants/references/serverless-and-multi-tenant.md +251 -0
- package/skills/supabase-architecture-variants/references/variant-a-monolith-(simple).md +44 -0
- package/skills/supabase-architecture-variants/references/variant-b-service-layer-(moderate).md +72 -0
- package/skills/supabase-architecture-variants/references/variant-c-microservice-(complex).md +81 -0
- package/skills/supabase-auth-storage-realtime-core/SKILL.md +471 -37
- package/skills/supabase-ci-integration/SKILL.md +315 -67
- package/skills/supabase-ci-integration/references/errors.md +10 -0
- package/skills/supabase-ci-integration/references/examples.md +36 -0
- package/skills/supabase-ci-integration/references/implementation.md +54 -0
- package/skills/supabase-common-errors/SKILL.md +320 -62
- package/skills/supabase-common-errors/references/errors.md +53 -0
- package/skills/supabase-common-errors/references/examples.md +23 -0
- package/skills/supabase-cost-tuning/SKILL.md +365 -131
- package/skills/supabase-cost-tuning/references/cost-estimation.md +34 -0
- package/skills/supabase-cost-tuning/references/cost-reduction-strategies.md +40 -0
- package/skills/supabase-cost-tuning/references/errors.md +11 -0
- package/skills/supabase-cost-tuning/references/examples.md +15 -0
- package/skills/supabase-data-handling/SKILL.md +378 -145
- package/skills/supabase-data-handling/references/errors.md +11 -0
- package/skills/supabase-data-handling/references/examples.md +27 -0
- package/skills/supabase-data-handling/references/implementation.md +223 -0
- package/skills/supabase-data-handling/references/retention-and-backup.md +221 -0
- package/skills/supabase-debug-bundle/SKILL.md +267 -73
- package/skills/supabase-debug-bundle/references/errors.md +12 -0
- package/skills/supabase-debug-bundle/references/examples.md +24 -0
- package/skills/supabase-debug-bundle/references/implementation.md +54 -0
- package/skills/supabase-deploy-integration/SKILL.md +258 -147
- package/skills/supabase-deploy-integration/references/errors.md +11 -0
- package/skills/supabase-deploy-integration/references/examples.md +21 -0
- package/skills/supabase-deploy-integration/references/google-cloud-run.md +36 -0
- package/skills/supabase-deploy-integration/references/vercel-deployment.md +35 -0
- package/skills/supabase-enterprise-rbac/SKILL.md +327 -160
- package/skills/supabase-enterprise-rbac/references/api-scoping-and-enforcement.md +255 -0
- package/skills/supabase-enterprise-rbac/references/errors.md +11 -0
- package/skills/supabase-enterprise-rbac/references/examples.md +12 -0
- package/skills/supabase-enterprise-rbac/references/role-implementation.md +33 -0
- package/skills/supabase-enterprise-rbac/references/sso-integration.md +35 -0
- package/skills/supabase-hello-world/SKILL.md +160 -54
- package/skills/supabase-incident-runbook/SKILL.md +453 -131
- package/skills/supabase-incident-runbook/references/errors.md +11 -0
- package/skills/supabase-incident-runbook/references/examples.md +10 -0
- package/skills/supabase-incident-runbook/references/immediate-actions-by-error-type.md +41 -0
- package/skills/supabase-install-auth/SKILL.md +186 -50
- package/skills/supabase-install-auth/references/examples.md +102 -0
- package/skills/supabase-known-pitfalls/SKILL.md +411 -241
- package/skills/supabase-known-pitfalls/references/errors.md +11 -0
- package/skills/supabase-known-pitfalls/references/examples.md +12 -0
- package/skills/supabase-load-scale/SKILL.md +346 -217
- package/skills/supabase-load-scale/references/capacity-planning.md +47 -0
- package/skills/supabase-load-scale/references/errors.md +11 -0
- package/skills/supabase-load-scale/references/examples.md +26 -0
- package/skills/supabase-load-scale/references/load-testing-with-k6.md +59 -0
- package/skills/supabase-load-scale/references/scaling-patterns.md +65 -0
- package/skills/supabase-load-scale/references/table-partitioning.md +263 -0
- package/skills/supabase-local-dev-loop/SKILL.md +272 -73
- package/skills/supabase-local-dev-loop/references/errors.md +11 -0
- package/skills/supabase-local-dev-loop/references/examples.md +21 -0
- package/skills/supabase-local-dev-loop/references/implementation.md +60 -0
- package/skills/supabase-migration-deep-dive/SKILL.md +338 -177
- package/skills/supabase-migration-deep-dive/references/backfill-versioning-rollback.md +258 -0
- package/skills/supabase-migration-deep-dive/references/errors.md +11 -0
- package/skills/supabase-migration-deep-dive/references/examples.md +12 -0
- package/skills/supabase-migration-deep-dive/references/implementation-plan.md +80 -0
- package/skills/supabase-migration-deep-dive/references/pre-migration-assessment.md +39 -0
- package/skills/supabase-multi-env-setup/SKILL.md +393 -152
- package/skills/supabase-multi-env-setup/references/configuration-structure.md +59 -0
- package/skills/supabase-multi-env-setup/references/errors.md +11 -0
- package/skills/supabase-multi-env-setup/references/examples.md +11 -0
- package/skills/supabase-observability/SKILL.md +318 -196
- package/skills/supabase-observability/references/alert-configuration.md +40 -0
- package/skills/supabase-observability/references/errors.md +11 -0
- package/skills/supabase-observability/references/examples.md +13 -0
- package/skills/supabase-observability/references/metrics-collection.md +65 -0
- package/skills/supabase-performance-tuning/SKILL.md +304 -160
- package/skills/supabase-performance-tuning/references/caching-strategy.md +49 -0
- package/skills/supabase-performance-tuning/references/errors.md +11 -0
- package/skills/supabase-performance-tuning/references/examples.md +13 -0
- package/skills/supabase-policy-guardrails/SKILL.md +248 -221
- package/skills/supabase-policy-guardrails/references/ci-cost-security.md +484 -0
- package/skills/supabase-policy-guardrails/references/errors.md +11 -0
- package/skills/supabase-policy-guardrails/references/eslint-rules.md +46 -0
- package/skills/supabase-policy-guardrails/references/examples.md +10 -0
- package/skills/supabase-prod-checklist/SKILL.md +474 -84
- package/skills/supabase-prod-checklist/references/errors.md +63 -0
- package/skills/supabase-prod-checklist/references/examples.md +153 -0
- package/skills/supabase-prod-checklist/references/implementation.md +113 -0
- package/skills/supabase-rate-limits/SKILL.md +311 -98
- package/skills/supabase-rate-limits/references/errors.md +11 -0
- package/skills/supabase-rate-limits/references/examples.md +46 -0
- package/skills/supabase-rate-limits/references/implementation.md +66 -0
- package/skills/supabase-reference-architecture/SKILL.md +249 -182
- package/skills/supabase-reference-architecture/references/errors.md +29 -0
- package/skills/supabase-reference-architecture/references/examples.md +116 -0
- package/skills/supabase-reference-architecture/references/key-components.md +244 -0
- package/skills/supabase-reference-architecture/references/project-structure.md +109 -0
- package/skills/supabase-reliability-patterns/SKILL.md +229 -234
- package/skills/supabase-reliability-patterns/references/circuit-breaker.md +36 -0
- package/skills/supabase-reliability-patterns/references/dead-letter-queue.md +48 -0
- package/skills/supabase-reliability-patterns/references/errors.md +11 -0
- package/skills/supabase-reliability-patterns/references/examples.md +11 -0
- package/skills/supabase-reliability-patterns/references/idempotency-keys.md +36 -0
- package/skills/supabase-reliability-patterns/references/offline-degradation-health-dualwrite.md +489 -0
- package/skills/supabase-schema-from-requirements/SKILL.md +373 -34
- package/skills/supabase-sdk-patterns/SKILL.md +388 -99
- package/skills/supabase-sdk-patterns/references/errors.md +11 -0
- package/skills/supabase-sdk-patterns/references/examples.md +45 -0
- package/skills/supabase-sdk-patterns/references/implementation.md +67 -0
- package/skills/supabase-security-basics/SKILL.md +282 -102
- package/skills/supabase-security-basics/references/errors.md +10 -0
- package/skills/supabase-security-basics/references/examples.md +70 -0
- package/skills/supabase-security-basics/references/implementation.md +39 -0
- package/skills/supabase-upgrade-migration/SKILL.md +248 -66
- package/skills/supabase-upgrade-migration/references/errors.md +10 -0
- package/skills/supabase-upgrade-migration/references/examples.md +51 -0
- package/skills/supabase-upgrade-migration/references/implementation.md +29 -0
- package/skills/supabase-webhooks-events/SKILL.md +412 -138
- package/skills/supabase-webhooks-events/references/errors.md +55 -0
- package/skills/supabase-webhooks-events/references/event-handler-pattern.md +106 -0
- package/skills/supabase-webhooks-events/references/examples.md +133 -0
- package/skills/supabase-webhooks-events/references/signature-verification.md +165 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Error Handling Reference
|
|
2
|
+
|
|
3
|
+
| Issue | Cause | Solution |
|
|
4
|
+
|-------|-------|----------|
|
|
5
|
+
| Too many findings | Legacy codebase | Prioritize security first |
|
|
6
|
+
| Pattern not detected | Complex code | Manual review |
|
|
7
|
+
| False positive | Similar code | Whitelist exceptions |
|
|
8
|
+
| Fix breaks tests | Behavior change | Update tests |
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## Examples
|
|
2
|
+
|
|
3
|
+
### Quick Pitfall Scan
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Check for common pitfalls
|
|
7
|
+
grep -r "sk_live_" --include="*.ts" src/ # Key leakage
|
|
8
|
+
grep -r "console.log" --include="*.ts" src/ # Potential PII logging
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
|
|
@@ -1,274 +1,403 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: supabase-load-scale
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
description: 'Scale Supabase projects for production load: read replicas, connection
|
|
4
|
+
pooling
|
|
5
|
+
|
|
6
|
+
tuning via Supavisor, compute size upgrades, CDN caching for Storage,
|
|
7
|
+
|
|
8
|
+
Edge Function regional deployment, and database table partitioning.
|
|
9
|
+
|
|
10
|
+
Use when preparing for traffic spikes, optimizing connection limits,
|
|
11
|
+
|
|
12
|
+
setting up read replicas for analytics queries, or partitioning large tables.
|
|
13
|
+
|
|
14
|
+
Trigger with phrases like "supabase scale", "supabase read replica",
|
|
15
|
+
|
|
16
|
+
"supabase connection pooling", "supabase compute upgrade",
|
|
17
|
+
|
|
18
|
+
"supabase CDN storage", "supabase edge function regions",
|
|
19
|
+
|
|
20
|
+
"supabase partitioning", "supavisor", "supabase pool mode".
|
|
21
|
+
|
|
22
|
+
'
|
|
23
|
+
allowed-tools: Read, Write, Edit, Bash(supabase:*), Bash(psql:*), Bash(curl:*), Grep
|
|
10
24
|
version: 1.0.0
|
|
11
25
|
license: MIT
|
|
12
26
|
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
27
|
+
tags:
|
|
28
|
+
- saas
|
|
29
|
+
- supabase
|
|
30
|
+
- scaling
|
|
31
|
+
- performance
|
|
32
|
+
- connection-pooling
|
|
33
|
+
- read-replicas
|
|
34
|
+
- partitioning
|
|
35
|
+
compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
|
|
13
36
|
---
|
|
14
|
-
|
|
15
37
|
# Supabase Load & Scale
|
|
16
38
|
|
|
17
39
|
## Overview
|
|
18
|
-
|
|
40
|
+
|
|
41
|
+
Supabase scaling operates at six layers: **read replicas** (offload analytics and reporting queries), **connection pooling** (Supavisor pgBouncer replacement with transaction/session modes), **compute upgrades** (vCPU/RAM tiers), **CDN for Storage** (cache public bucket assets at the edge), **Edge Function regions** (deploy functions closer to users), and **table partitioning** (split billion-row tables for query performance). This skill covers each layer with real `createClient` configuration, SQL, and CLI commands.
|
|
19
42
|
|
|
20
43
|
## Prerequisites
|
|
21
|
-
- k6 load testing tool installed
|
|
22
|
-
- Kubernetes cluster with HPA configured
|
|
23
|
-
- Prometheus for metrics collection
|
|
24
|
-
- Test environment API keys
|
|
25
|
-
|
|
26
|
-
## Load Testing with k6
|
|
27
|
-
|
|
28
|
-
### Basic Load Test
|
|
29
|
-
```javascript
|
|
30
|
-
// supabase-load-test.js
|
|
31
|
-
import http from 'k6/http';
|
|
32
|
-
import { check, sleep } from 'k6';
|
|
33
|
-
|
|
34
|
-
export const options = {
|
|
35
|
-
stages: [
|
|
36
|
-
{ duration: '2m', target: 10 }, // Ramp up
|
|
37
|
-
{ duration: '5m', target: 10 }, // Steady state
|
|
38
|
-
{ duration: '2m', target: 50 }, // Ramp to peak
|
|
39
|
-
{ duration: '5m', target: 50 }, // Stress test
|
|
40
|
-
{ duration: '2m', target: 0 }, // Ramp down
|
|
41
|
-
],
|
|
42
|
-
thresholds: {
|
|
43
|
-
http_req_duration: ['p(95)<200'],
|
|
44
|
-
http_req_failed: ['rate<0.01'],
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export default function () {
|
|
49
|
-
const response = http.post(
|
|
50
|
-
'https://api.supabase.com/v1/resource',
|
|
51
|
-
JSON.stringify({ test: true }),
|
|
52
|
-
{
|
|
53
|
-
headers: {
|
|
54
|
-
'Content-Type': 'application/json',
|
|
55
|
-
'Authorization': `Bearer ${__ENV.SUPABASE_API_KEY}`,
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
);
|
|
59
44
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
- Supabase project on a Pro plan or higher (read replicas require Pro+)
|
|
46
|
+
- `@supabase/supabase-js` v2+ installed
|
|
47
|
+
- `supabase` CLI installed and linked to your project
|
|
48
|
+
- Database access via `psql` or Supabase SQL Editor
|
|
49
|
+
- TypeScript project with generated database types
|
|
64
50
|
|
|
65
|
-
|
|
66
|
-
|
|
51
|
+
## Step 1 — Read Replicas and Connection Pooling
|
|
52
|
+
|
|
53
|
+
Read replicas let you route read-heavy queries (dashboards, reports, search) to replica databases while keeping writes on the primary. Supabase uses **Supavisor** (their pgBouncer replacement) for connection pooling with two modes: **transaction** (default, shares connections between requests) and **session** (holds a connection per client session, needed for prepared statements).
|
|
54
|
+
|
|
55
|
+
### Configure the Read Replica Client
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// lib/supabase.ts
|
|
59
|
+
import { createClient } from '@supabase/supabase-js'
|
|
60
|
+
import type { Database } from './database.types'
|
|
61
|
+
|
|
62
|
+
// Primary client — handles all writes and real-time subscriptions
|
|
63
|
+
export const supabase = createClient<Database>(
|
|
64
|
+
process.env.SUPABASE_URL!,
|
|
65
|
+
process.env.SUPABASE_ANON_KEY!
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
// Read replica client — use for analytics, dashboards, search
|
|
69
|
+
// The read replica URL is available in Dashboard > Settings > Database
|
|
70
|
+
export const supabaseReadOnly = createClient<Database>(
|
|
71
|
+
process.env.SUPABASE_READ_REPLICA_URL!, // e.g., https://<project-ref>-ro.supabase.co
|
|
72
|
+
process.env.SUPABASE_ANON_KEY!, // same anon key works for replicas
|
|
73
|
+
{
|
|
74
|
+
db: { schema: 'public' },
|
|
75
|
+
// Replicas may have slight lag (typically <100ms)
|
|
76
|
+
// Do NOT use for reads-after-writes in the same request
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
// Server-side admin client with connection pooling via Supavisor
|
|
81
|
+
// Use the pooled connection string (port 6543) instead of direct (port 5432)
|
|
82
|
+
export const supabaseAdmin = createClient<Database>(
|
|
83
|
+
process.env.SUPABASE_URL!,
|
|
84
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY!,
|
|
85
|
+
{
|
|
86
|
+
auth: { autoRefreshToken: false, persistSession: false },
|
|
87
|
+
db: { schema: 'public' },
|
|
88
|
+
}
|
|
89
|
+
)
|
|
67
90
|
```
|
|
68
91
|
|
|
69
|
-
###
|
|
92
|
+
### Direct Postgres Connections via Supavisor Pooling
|
|
93
|
+
|
|
70
94
|
```bash
|
|
71
|
-
#
|
|
72
|
-
|
|
73
|
-
|
|
95
|
+
# Transaction mode (default, port 6543) — best for serverless/short-lived connections
|
|
96
|
+
# Shares connections across clients. Cannot use prepared statements.
|
|
97
|
+
psql "postgresql://postgres.[project-ref]:[password]@aws-0-us-east-1.pooler.supabase.com:6543/postgres"
|
|
74
98
|
|
|
75
|
-
#
|
|
76
|
-
|
|
99
|
+
# Session mode (port 5432 via pooler) — needed for prepared statements, LISTEN/NOTIFY
|
|
100
|
+
# One-to-one connection mapping per client.
|
|
101
|
+
psql "postgresql://postgres.[project-ref]:[password]@aws-0-us-east-1.pooler.supabase.com:5432/postgres"
|
|
77
102
|
|
|
78
|
-
#
|
|
79
|
-
|
|
103
|
+
# Direct connection (no pooling) — for migrations and admin tasks only
|
|
104
|
+
psql "postgresql://postgres:[password]@db.[project-ref].supabase.co:5432/postgres"
|
|
80
105
|
```
|
|
81
106
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
### Horizontal Scaling
|
|
85
|
-
```yaml
|
|
86
|
-
# kubernetes HPA
|
|
87
|
-
apiVersion: autoscaling/v2
|
|
88
|
-
kind: HorizontalPodAutoscaler
|
|
89
|
-
metadata:
|
|
90
|
-
name: supabase-integration-hpa
|
|
91
|
-
spec:
|
|
92
|
-
scaleTargetRef:
|
|
93
|
-
apiVersion: apps/v1
|
|
94
|
-
kind: Deployment
|
|
95
|
-
name: supabase-integration
|
|
96
|
-
minReplicas: 2
|
|
97
|
-
maxReplicas: 20
|
|
98
|
-
metrics:
|
|
99
|
-
- type: Resource
|
|
100
|
-
resource:
|
|
101
|
-
name: cpu
|
|
102
|
-
target:
|
|
103
|
-
type: Utilization
|
|
104
|
-
averageUtilization: 70
|
|
105
|
-
- type: Pods
|
|
106
|
-
pods:
|
|
107
|
-
metric:
|
|
108
|
-
name: supabase_queue_depth
|
|
109
|
-
target:
|
|
110
|
-
type: AverageValue
|
|
111
|
-
averageValue: 100
|
|
112
|
-
```
|
|
107
|
+
### Route Queries to the Right Target
|
|
113
108
|
|
|
114
|
-
### Connection Pooling
|
|
115
109
|
```typescript
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
110
|
+
// services/analytics.ts
|
|
111
|
+
import { supabaseReadOnly } from '../lib/supabase'
|
|
112
|
+
|
|
113
|
+
// Heavy analytics queries go to the read replica
|
|
114
|
+
export async function getDashboardMetrics(orgId: string) {
|
|
115
|
+
const { data, error } = await supabaseReadOnly
|
|
116
|
+
.from('events')
|
|
117
|
+
.select('event_type, count:id.count()')
|
|
118
|
+
.eq('org_id', orgId)
|
|
119
|
+
.gte('created_at', new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString())
|
|
120
|
+
|
|
121
|
+
if (error) throw new Error(`Dashboard query failed: ${error.message}`)
|
|
122
|
+
return data
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// services/orders.ts
|
|
126
|
+
import { supabase } from '../lib/supabase'
|
|
127
|
+
|
|
128
|
+
// Writes always go to the primary
|
|
129
|
+
export async function createOrder(order: OrderInsert) {
|
|
130
|
+
const { data, error } = await supabase
|
|
131
|
+
.from('orders')
|
|
132
|
+
.insert(order)
|
|
133
|
+
.select('id, status, total, created_at')
|
|
134
|
+
.single()
|
|
135
|
+
|
|
136
|
+
if (error) throw new Error(`Order creation failed: ${error.message}`)
|
|
137
|
+
return data
|
|
141
138
|
}
|
|
142
139
|
```
|
|
143
140
|
|
|
144
|
-
|
|
141
|
+
### Monitor Connection Pool Usage
|
|
142
|
+
|
|
143
|
+
```sql
|
|
144
|
+
-- Check active connections by source (run in SQL Editor)
|
|
145
|
+
SELECT
|
|
146
|
+
usename,
|
|
147
|
+
application_name,
|
|
148
|
+
client_addr,
|
|
149
|
+
state,
|
|
150
|
+
count(*) AS connections
|
|
151
|
+
FROM pg_stat_activity
|
|
152
|
+
WHERE datname = 'postgres'
|
|
153
|
+
GROUP BY usename, application_name, client_addr, state
|
|
154
|
+
ORDER BY connections DESC;
|
|
155
|
+
|
|
156
|
+
-- Check connection limits for your compute tier
|
|
157
|
+
SHOW max_connections;
|
|
158
|
+
-- Micro: 60, Small: 90, Medium: 120, Large: 160, XL: 240, 2XL: 380, 4XL: 480
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Step 2 — Compute Upgrades, CDN for Storage, and Edge Function Regions
|
|
162
|
+
|
|
163
|
+
### Compute Size Selection Guide
|
|
164
|
+
|
|
165
|
+
| Tier | vCPU | RAM | Max Connections | Best For |
|
|
166
|
+
|------|------|-----|----------------|----------|
|
|
167
|
+
| Micro (Free) | 2 shared | 1 GB | 60 | Development, prototypes |
|
|
168
|
+
| Small (Pro) | 2 dedicated | 2 GB | 90 | Low-traffic production |
|
|
169
|
+
| Medium | 2 dedicated | 4 GB | 120 | Growing apps, moderate traffic |
|
|
170
|
+
| Large | 4 dedicated | 8 GB | 160 | High-traffic, complex queries |
|
|
171
|
+
| XL | 8 dedicated | 16 GB | 240 | Large datasets, concurrent users |
|
|
172
|
+
| 2XL | 16 dedicated | 32 GB | 380 | Enterprise, heavy analytics |
|
|
173
|
+
| 4XL | 32 dedicated | 64 GB | 480 | Mission-critical, max throughput |
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Upgrade compute via CLI (requires Pro plan)
|
|
177
|
+
supabase projects update --experimental --compute-size small # or medium, large, xl, 2xl, 4xl
|
|
178
|
+
|
|
179
|
+
# Check current compute size
|
|
180
|
+
supabase projects list
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### CDN Caching for Storage Buckets
|
|
145
184
|
|
|
146
|
-
|
|
147
|
-
| Metric | Warning | Critical |
|
|
148
|
-
|--------|---------|----------|
|
|
149
|
-
| CPU Utilization | > 70% | > 85% |
|
|
150
|
-
| Memory Usage | > 75% | > 90% |
|
|
151
|
-
| Request Queue Depth | > 100 | > 500 |
|
|
152
|
-
| Error Rate | > 1% | > 5% |
|
|
153
|
-
| P95 Latency | > 500ms | > 2000ms |
|
|
185
|
+
Public buckets are automatically served through Supabase's CDN. Optimize cache behavior with proper headers and transforms.
|
|
154
186
|
|
|
155
|
-
### Capacity Calculation
|
|
156
187
|
```typescript
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
188
|
+
import { createClient } from '@supabase/supabase-js'
|
|
189
|
+
|
|
190
|
+
const supabase = createClient(
|
|
191
|
+
process.env.SUPABASE_URL!,
|
|
192
|
+
process.env.SUPABASE_ANON_KEY!
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
// Upload with cache-control headers for CDN optimization
|
|
196
|
+
async function uploadPublicAsset(
|
|
197
|
+
bucket: string,
|
|
198
|
+
path: string,
|
|
199
|
+
file: File
|
|
200
|
+
) {
|
|
201
|
+
const { data, error } = await supabase.storage
|
|
202
|
+
.from(bucket)
|
|
203
|
+
.upload(path, file, {
|
|
204
|
+
cacheControl: '31536000', // 1 year cache for immutable assets
|
|
205
|
+
upsert: false, // prevent accidental overwrites
|
|
206
|
+
contentType: file.type,
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
if (error) throw new Error(`Upload failed: ${error.message}`)
|
|
210
|
+
|
|
211
|
+
// Get the CDN-cached public URL
|
|
212
|
+
const { data: { publicUrl } } = supabase.storage
|
|
213
|
+
.from(bucket)
|
|
214
|
+
.getPublicUrl(path, {
|
|
215
|
+
transform: {
|
|
216
|
+
width: 800, // Image transforms are cached at the CDN edge
|
|
217
|
+
quality: 80,
|
|
218
|
+
format: 'webp',
|
|
219
|
+
},
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
return { path: data.path, publicUrl }
|
|
162
223
|
}
|
|
163
224
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
: 'Adequate capacity',
|
|
184
|
-
};
|
|
225
|
+
// Bust cache by uploading to a new path (content-addressed)
|
|
226
|
+
import { createHash } from 'crypto'
|
|
227
|
+
|
|
228
|
+
async function uploadVersionedAsset(bucket: string, file: Buffer, ext: string) {
|
|
229
|
+
const hash = createHash('sha256').update(file).digest('hex').slice(0, 12)
|
|
230
|
+
const path = `assets/${hash}.${ext}`
|
|
231
|
+
|
|
232
|
+
const { error } = await supabase.storage
|
|
233
|
+
.from(bucket)
|
|
234
|
+
.upload(path, file, {
|
|
235
|
+
cacheControl: '31536000',
|
|
236
|
+
upsert: false,
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
if (error && error.message !== 'The resource already exists') {
|
|
240
|
+
throw new Error(`Versioned upload failed: ${error.message}`)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return supabase.storage.from(bucket).getPublicUrl(path).data.publicUrl
|
|
185
244
|
}
|
|
186
245
|
```
|
|
187
246
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
```markdown
|
|
191
|
-
## Supabase Performance Benchmark
|
|
192
|
-
**Date:** YYYY-MM-DD
|
|
193
|
-
**Environment:** [staging/production]
|
|
194
|
-
**SDK Version:** X.Y.Z
|
|
195
|
-
|
|
196
|
-
### Test Configuration
|
|
197
|
-
- Duration: 10 minutes
|
|
198
|
-
- Ramp: 10 → 100 → 10 VUs
|
|
199
|
-
- Target endpoint: /v1/resource
|
|
200
|
-
|
|
201
|
-
### Results
|
|
202
|
-
| Metric | Value |
|
|
203
|
-
|--------|-------|
|
|
204
|
-
| Total Requests | 50,000 |
|
|
205
|
-
| Success Rate | 99.9% |
|
|
206
|
-
| P50 Latency | 120ms |
|
|
207
|
-
| P95 Latency | 350ms |
|
|
208
|
-
| P99 Latency | 800ms |
|
|
209
|
-
| Max RPS Achieved | 150 |
|
|
210
|
-
|
|
211
|
-
### Observations
|
|
212
|
-
- [Key finding 1]
|
|
213
|
-
- [Key finding 2]
|
|
214
|
-
|
|
215
|
-
### Recommendations
|
|
216
|
-
- [Scaling recommendation]
|
|
217
|
-
```
|
|
247
|
+
### Edge Function Regional Deployment
|
|
218
248
|
|
|
219
|
-
|
|
249
|
+
```bash
|
|
250
|
+
# Deploy to a specific region (closer to your users)
|
|
251
|
+
supabase functions deploy my-function --region us-east-1
|
|
252
|
+
supabase functions deploy my-function --region eu-west-1
|
|
253
|
+
supabase functions deploy my-function --region ap-southeast-1
|
|
220
254
|
|
|
221
|
-
|
|
222
|
-
|
|
255
|
+
# List available regions
|
|
256
|
+
supabase functions list
|
|
223
257
|
|
|
224
|
-
|
|
225
|
-
|
|
258
|
+
# Deploy all functions to a region
|
|
259
|
+
supabase functions deploy --region us-east-1
|
|
260
|
+
```
|
|
226
261
|
|
|
227
|
-
|
|
228
|
-
|
|
262
|
+
```typescript
|
|
263
|
+
// supabase/functions/geo-router/index.ts
|
|
264
|
+
// Edge Function that runs in the region closest to the user
|
|
265
|
+
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
|
|
266
|
+
|
|
267
|
+
Deno.serve(async (req) => {
|
|
268
|
+
const supabase = createClient(
|
|
269
|
+
Deno.env.get('SUPABASE_URL')!,
|
|
270
|
+
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
// Edge Functions automatically run in the nearest region
|
|
274
|
+
// Use this for latency-sensitive operations
|
|
275
|
+
const { data, error } = await supabase
|
|
276
|
+
.from('products')
|
|
277
|
+
.select('id, name, price')
|
|
278
|
+
.eq('region', req.headers.get('x-region') ?? 'us')
|
|
279
|
+
.limit(20)
|
|
280
|
+
|
|
281
|
+
if (error) {
|
|
282
|
+
return new Response(JSON.stringify({ error: error.message }), { status: 500 })
|
|
283
|
+
}
|
|
229
284
|
|
|
230
|
-
|
|
231
|
-
|
|
285
|
+
return new Response(JSON.stringify(data), {
|
|
286
|
+
headers: {
|
|
287
|
+
'Content-Type': 'application/json',
|
|
288
|
+
'Cache-Control': 'public, max-age=60', // CDN caches the response
|
|
289
|
+
},
|
|
290
|
+
})
|
|
291
|
+
})
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Step 3 — Database Table Partitioning
|
|
295
|
+
|
|
296
|
+
See [table partitioning patterns](references/table-partitioning.md) for range partitioning by date, automated partition creation via `pg_cron`, SDK query patterns with partition key filters, and partition drop for data retention.
|
|
232
297
|
|
|
233
298
|
## Output
|
|
234
|
-
|
|
235
|
-
-
|
|
236
|
-
-
|
|
237
|
-
-
|
|
299
|
+
|
|
300
|
+
- Read replica client configured for analytics/dashboard queries, primary for writes
|
|
301
|
+
- Connection pooling mode selected (transaction vs session) with correct port
|
|
302
|
+
- Compute tier matched to traffic requirements
|
|
303
|
+
- Storage uploads optimized with CDN cache headers and image transforms
|
|
304
|
+
- Edge Functions deployed to the region closest to users
|
|
305
|
+
- Large tables partitioned by date range with automated partition management
|
|
306
|
+
- Data retention policy via partition drops
|
|
238
307
|
|
|
239
308
|
## Error Handling
|
|
309
|
+
|
|
240
310
|
| Issue | Cause | Solution |
|
|
241
311
|
|-------|-------|----------|
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
|
|
|
245
|
-
|
|
|
312
|
+
| `too many connections for role` | Exceeded Supavisor pool limit | Use transaction mode (port 6543), reduce idle connections, upgrade compute |
|
|
313
|
+
| Read replica returns stale data | Replication lag (typically <100ms) | Do not read-after-write on replica; use primary for consistency-critical reads |
|
|
314
|
+
| `no partition of relation "events" found for row` | Insert date outside any partition range | Create a DEFAULT partition or pre-create future partitions |
|
|
315
|
+
| Storage CDN returns old file | Cached at edge | Use content-addressed paths (`hash.ext`) or set shorter `cacheControl` |
|
|
316
|
+
| Edge Function cold start | First request to a region | Use `keep-alive` cron ping or accept ~200ms cold start |
|
|
317
|
+
| `prepared statement already exists` | Transaction mode doesn't support prepared statements | Switch to session mode (port 5432) or disable prepared statements in your ORM |
|
|
246
318
|
|
|
247
319
|
## Examples
|
|
248
320
|
|
|
249
|
-
### Quick
|
|
321
|
+
### Quick Connection Pool Check
|
|
322
|
+
|
|
250
323
|
```bash
|
|
251
|
-
|
|
324
|
+
# Check how many connections are in use right now
|
|
325
|
+
psql "$DATABASE_URL" -c "SELECT count(*) AS active_connections FROM pg_stat_activity WHERE state = 'active';"
|
|
252
326
|
```
|
|
253
327
|
|
|
254
|
-
###
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
328
|
+
### Switch an Existing Table to Partitioned
|
|
329
|
+
|
|
330
|
+
```sql
|
|
331
|
+
-- You cannot ALTER an existing table to be partitioned.
|
|
332
|
+
-- Instead: create partitioned table, migrate data, swap names.
|
|
333
|
+
|
|
334
|
+
-- 1. Create new partitioned table
|
|
335
|
+
CREATE TABLE public.events_partitioned (LIKE public.events INCLUDING ALL)
|
|
336
|
+
PARTITION BY RANGE (created_at);
|
|
337
|
+
|
|
338
|
+
-- 2. Create partitions for existing data range
|
|
339
|
+
CREATE TABLE public.events_p_2025_01 PARTITION OF public.events_partitioned
|
|
340
|
+
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
|
|
341
|
+
-- ... more partitions
|
|
342
|
+
|
|
343
|
+
-- 3. Copy data (do this during low traffic)
|
|
344
|
+
INSERT INTO public.events_partitioned SELECT * FROM public.events;
|
|
345
|
+
|
|
346
|
+
-- 4. Swap tables in a transaction
|
|
347
|
+
BEGIN;
|
|
348
|
+
ALTER TABLE public.events RENAME TO events_old;
|
|
349
|
+
ALTER TABLE public.events_partitioned RENAME TO events;
|
|
350
|
+
COMMIT;
|
|
351
|
+
|
|
352
|
+
-- 5. Verify, then drop old table
|
|
353
|
+
DROP TABLE public.events_old;
|
|
260
354
|
```
|
|
261
355
|
|
|
262
|
-
###
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
356
|
+
### Test Read Replica Lag
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { supabase, supabaseReadOnly } from '../lib/supabase'
|
|
360
|
+
|
|
361
|
+
async function measureReplicaLag() {
|
|
362
|
+
// Write to primary
|
|
363
|
+
const { data: written } = await supabase
|
|
364
|
+
.from('health_checks')
|
|
365
|
+
.insert({ timestamp: new Date().toISOString() })
|
|
366
|
+
.select('id, timestamp')
|
|
367
|
+
.single()
|
|
368
|
+
|
|
369
|
+
// Immediately read from replica
|
|
370
|
+
const start = Date.now()
|
|
371
|
+
let found = false
|
|
372
|
+
|
|
373
|
+
while (!found && Date.now() - start < 5000) {
|
|
374
|
+
const { data } = await supabaseReadOnly
|
|
375
|
+
.from('health_checks')
|
|
376
|
+
.select('id')
|
|
377
|
+
.eq('id', written!.id)
|
|
378
|
+
.maybeSingle()
|
|
379
|
+
|
|
380
|
+
if (data) {
|
|
381
|
+
found = true
|
|
382
|
+
console.log(`Replica lag: ${Date.now() - start}ms`)
|
|
383
|
+
} else {
|
|
384
|
+
await new Promise(r => setTimeout(r, 10))
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (!found) console.warn('Replica lag exceeds 5 seconds')
|
|
389
|
+
}
|
|
266
390
|
```
|
|
267
391
|
|
|
268
392
|
## Resources
|
|
269
|
-
|
|
270
|
-
- [
|
|
271
|
-
- [Supabase
|
|
393
|
+
|
|
394
|
+
- [Supabase Read Replicas](https://supabase.com/docs/guides/platform/read-replicas)
|
|
395
|
+
- [Supabase Connection Pooling (Supavisor)](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler)
|
|
396
|
+
- [Supabase Compute Add-ons](https://supabase.com/docs/guides/platform/compute-add-ons)
|
|
397
|
+
- [Supabase Storage CDN](https://supabase.com/docs/guides/storage/cdn/fundamentals)
|
|
398
|
+
- [Supabase Edge Functions](https://supabase.com/docs/guides/functions)
|
|
399
|
+
- [PostgreSQL Table Partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html)
|
|
272
400
|
|
|
273
401
|
## Next Steps
|
|
274
|
-
|
|
402
|
+
|
|
403
|
+
For reliability patterns (circuit breakers, offline queues, graceful degradation), see `supabase-reliability-patterns`.
|