@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.
Files changed (133) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +73 -47
  3. package/package.json +4 -4
  4. package/skills/supabase-advanced-troubleshooting/SKILL.md +404 -200
  5. package/skills/supabase-advanced-troubleshooting/references/errors.md +11 -0
  6. package/skills/supabase-advanced-troubleshooting/references/evidence-collection-framework.md +34 -0
  7. package/skills/supabase-advanced-troubleshooting/references/examples.md +11 -0
  8. package/skills/supabase-advanced-troubleshooting/references/rls-edge-functions-realtime.md +363 -0
  9. package/skills/supabase-advanced-troubleshooting/references/systematic-isolation.md +56 -0
  10. package/skills/supabase-advanced-troubleshooting/references/timing-analysis.md +35 -0
  11. package/skills/supabase-architecture-variants/SKILL.md +395 -216
  12. package/skills/supabase-architecture-variants/references/errors.md +11 -0
  13. package/skills/supabase-architecture-variants/references/examples.md +12 -0
  14. package/skills/supabase-architecture-variants/references/serverless-and-multi-tenant.md +251 -0
  15. package/skills/supabase-architecture-variants/references/variant-a-monolith-(simple).md +44 -0
  16. package/skills/supabase-architecture-variants/references/variant-b-service-layer-(moderate).md +72 -0
  17. package/skills/supabase-architecture-variants/references/variant-c-microservice-(complex).md +81 -0
  18. package/skills/supabase-auth-storage-realtime-core/SKILL.md +471 -37
  19. package/skills/supabase-ci-integration/SKILL.md +315 -67
  20. package/skills/supabase-ci-integration/references/errors.md +10 -0
  21. package/skills/supabase-ci-integration/references/examples.md +36 -0
  22. package/skills/supabase-ci-integration/references/implementation.md +54 -0
  23. package/skills/supabase-common-errors/SKILL.md +320 -62
  24. package/skills/supabase-common-errors/references/errors.md +53 -0
  25. package/skills/supabase-common-errors/references/examples.md +23 -0
  26. package/skills/supabase-cost-tuning/SKILL.md +365 -131
  27. package/skills/supabase-cost-tuning/references/cost-estimation.md +34 -0
  28. package/skills/supabase-cost-tuning/references/cost-reduction-strategies.md +40 -0
  29. package/skills/supabase-cost-tuning/references/errors.md +11 -0
  30. package/skills/supabase-cost-tuning/references/examples.md +15 -0
  31. package/skills/supabase-data-handling/SKILL.md +378 -145
  32. package/skills/supabase-data-handling/references/errors.md +11 -0
  33. package/skills/supabase-data-handling/references/examples.md +27 -0
  34. package/skills/supabase-data-handling/references/implementation.md +223 -0
  35. package/skills/supabase-data-handling/references/retention-and-backup.md +221 -0
  36. package/skills/supabase-debug-bundle/SKILL.md +267 -73
  37. package/skills/supabase-debug-bundle/references/errors.md +12 -0
  38. package/skills/supabase-debug-bundle/references/examples.md +24 -0
  39. package/skills/supabase-debug-bundle/references/implementation.md +54 -0
  40. package/skills/supabase-deploy-integration/SKILL.md +258 -147
  41. package/skills/supabase-deploy-integration/references/errors.md +11 -0
  42. package/skills/supabase-deploy-integration/references/examples.md +21 -0
  43. package/skills/supabase-deploy-integration/references/google-cloud-run.md +36 -0
  44. package/skills/supabase-deploy-integration/references/vercel-deployment.md +35 -0
  45. package/skills/supabase-enterprise-rbac/SKILL.md +327 -160
  46. package/skills/supabase-enterprise-rbac/references/api-scoping-and-enforcement.md +255 -0
  47. package/skills/supabase-enterprise-rbac/references/errors.md +11 -0
  48. package/skills/supabase-enterprise-rbac/references/examples.md +12 -0
  49. package/skills/supabase-enterprise-rbac/references/role-implementation.md +33 -0
  50. package/skills/supabase-enterprise-rbac/references/sso-integration.md +35 -0
  51. package/skills/supabase-hello-world/SKILL.md +160 -54
  52. package/skills/supabase-incident-runbook/SKILL.md +453 -131
  53. package/skills/supabase-incident-runbook/references/errors.md +11 -0
  54. package/skills/supabase-incident-runbook/references/examples.md +10 -0
  55. package/skills/supabase-incident-runbook/references/immediate-actions-by-error-type.md +41 -0
  56. package/skills/supabase-install-auth/SKILL.md +186 -50
  57. package/skills/supabase-install-auth/references/examples.md +102 -0
  58. package/skills/supabase-known-pitfalls/SKILL.md +411 -241
  59. package/skills/supabase-known-pitfalls/references/errors.md +11 -0
  60. package/skills/supabase-known-pitfalls/references/examples.md +12 -0
  61. package/skills/supabase-load-scale/SKILL.md +346 -217
  62. package/skills/supabase-load-scale/references/capacity-planning.md +47 -0
  63. package/skills/supabase-load-scale/references/errors.md +11 -0
  64. package/skills/supabase-load-scale/references/examples.md +26 -0
  65. package/skills/supabase-load-scale/references/load-testing-with-k6.md +59 -0
  66. package/skills/supabase-load-scale/references/scaling-patterns.md +65 -0
  67. package/skills/supabase-load-scale/references/table-partitioning.md +263 -0
  68. package/skills/supabase-local-dev-loop/SKILL.md +272 -73
  69. package/skills/supabase-local-dev-loop/references/errors.md +11 -0
  70. package/skills/supabase-local-dev-loop/references/examples.md +21 -0
  71. package/skills/supabase-local-dev-loop/references/implementation.md +60 -0
  72. package/skills/supabase-migration-deep-dive/SKILL.md +338 -177
  73. package/skills/supabase-migration-deep-dive/references/backfill-versioning-rollback.md +258 -0
  74. package/skills/supabase-migration-deep-dive/references/errors.md +11 -0
  75. package/skills/supabase-migration-deep-dive/references/examples.md +12 -0
  76. package/skills/supabase-migration-deep-dive/references/implementation-plan.md +80 -0
  77. package/skills/supabase-migration-deep-dive/references/pre-migration-assessment.md +39 -0
  78. package/skills/supabase-multi-env-setup/SKILL.md +393 -152
  79. package/skills/supabase-multi-env-setup/references/configuration-structure.md +59 -0
  80. package/skills/supabase-multi-env-setup/references/errors.md +11 -0
  81. package/skills/supabase-multi-env-setup/references/examples.md +11 -0
  82. package/skills/supabase-observability/SKILL.md +318 -196
  83. package/skills/supabase-observability/references/alert-configuration.md +40 -0
  84. package/skills/supabase-observability/references/errors.md +11 -0
  85. package/skills/supabase-observability/references/examples.md +13 -0
  86. package/skills/supabase-observability/references/metrics-collection.md +65 -0
  87. package/skills/supabase-performance-tuning/SKILL.md +304 -160
  88. package/skills/supabase-performance-tuning/references/caching-strategy.md +49 -0
  89. package/skills/supabase-performance-tuning/references/errors.md +11 -0
  90. package/skills/supabase-performance-tuning/references/examples.md +13 -0
  91. package/skills/supabase-policy-guardrails/SKILL.md +248 -221
  92. package/skills/supabase-policy-guardrails/references/ci-cost-security.md +484 -0
  93. package/skills/supabase-policy-guardrails/references/errors.md +11 -0
  94. package/skills/supabase-policy-guardrails/references/eslint-rules.md +46 -0
  95. package/skills/supabase-policy-guardrails/references/examples.md +10 -0
  96. package/skills/supabase-prod-checklist/SKILL.md +474 -84
  97. package/skills/supabase-prod-checklist/references/errors.md +63 -0
  98. package/skills/supabase-prod-checklist/references/examples.md +153 -0
  99. package/skills/supabase-prod-checklist/references/implementation.md +113 -0
  100. package/skills/supabase-rate-limits/SKILL.md +311 -98
  101. package/skills/supabase-rate-limits/references/errors.md +11 -0
  102. package/skills/supabase-rate-limits/references/examples.md +46 -0
  103. package/skills/supabase-rate-limits/references/implementation.md +66 -0
  104. package/skills/supabase-reference-architecture/SKILL.md +249 -182
  105. package/skills/supabase-reference-architecture/references/errors.md +29 -0
  106. package/skills/supabase-reference-architecture/references/examples.md +116 -0
  107. package/skills/supabase-reference-architecture/references/key-components.md +244 -0
  108. package/skills/supabase-reference-architecture/references/project-structure.md +109 -0
  109. package/skills/supabase-reliability-patterns/SKILL.md +229 -234
  110. package/skills/supabase-reliability-patterns/references/circuit-breaker.md +36 -0
  111. package/skills/supabase-reliability-patterns/references/dead-letter-queue.md +48 -0
  112. package/skills/supabase-reliability-patterns/references/errors.md +11 -0
  113. package/skills/supabase-reliability-patterns/references/examples.md +11 -0
  114. package/skills/supabase-reliability-patterns/references/idempotency-keys.md +36 -0
  115. package/skills/supabase-reliability-patterns/references/offline-degradation-health-dualwrite.md +489 -0
  116. package/skills/supabase-schema-from-requirements/SKILL.md +373 -34
  117. package/skills/supabase-sdk-patterns/SKILL.md +388 -99
  118. package/skills/supabase-sdk-patterns/references/errors.md +11 -0
  119. package/skills/supabase-sdk-patterns/references/examples.md +45 -0
  120. package/skills/supabase-sdk-patterns/references/implementation.md +67 -0
  121. package/skills/supabase-security-basics/SKILL.md +282 -102
  122. package/skills/supabase-security-basics/references/errors.md +10 -0
  123. package/skills/supabase-security-basics/references/examples.md +70 -0
  124. package/skills/supabase-security-basics/references/implementation.md +39 -0
  125. package/skills/supabase-upgrade-migration/SKILL.md +248 -66
  126. package/skills/supabase-upgrade-migration/references/errors.md +10 -0
  127. package/skills/supabase-upgrade-migration/references/examples.md +51 -0
  128. package/skills/supabase-upgrade-migration/references/implementation.md +29 -0
  129. package/skills/supabase-webhooks-events/SKILL.md +412 -138
  130. package/skills/supabase-webhooks-events/references/errors.md +55 -0
  131. package/skills/supabase-webhooks-events/references/event-handler-pattern.md +106 -0
  132. package/skills/supabase-webhooks-events/references/examples.md +133 -0
  133. package/skills/supabase-webhooks-events/references/signature-verification.md +165 -0
@@ -0,0 +1,47 @@
1
+ # Capacity Planning
2
+
3
+ ## Capacity Planning
4
+
5
+ ### Metrics to Monitor
6
+
7
+ | Metric | Warning | Critical |
8
+ |--------|---------|----------|
9
+ | CPU Utilization | > 70% | > 85% |
10
+ | Memory Usage | > 75% | > 90% |
11
+ | Request Queue Depth | > 100 | > 500 |
12
+ | Error Rate | > 1% | > 5% |
13
+ | P95 Latency | > 500ms | > 2000ms |
14
+
15
+ ### Capacity Calculation
16
+
17
+ ```typescript
18
+ interface CapacityEstimate {
19
+ currentRPS: number;
20
+ maxRPS: number;
21
+ headroom: number;
22
+ scaleRecommendation: string;
23
+ }
24
+
25
+ function estimateSupabaseCapacity(
26
+ metrics: SystemMetrics
27
+ ): CapacityEstimate {
28
+ const currentRPS = metrics.requestsPerSecond;
29
+ const avgLatency = metrics.p50Latency;
30
+ const cpuUtilization = metrics.cpuPercent;
31
+
32
+ // Estimate max RPS based on current performance
33
+ const maxRPS = currentRPS / (cpuUtilization / 100) * 0.7; // 70% target
34
+ const headroom = ((maxRPS - currentRPS) / currentRPS) * 100;
35
+
36
+ return {
37
+ currentRPS,
38
+ maxRPS: Math.floor(maxRPS),
39
+ headroom: Math.round(headroom),
40
+ scaleRecommendation: headroom < 30
41
+ ? 'Scale up soon'
42
+ : headroom < 50
43
+ ? 'Monitor closely'
44
+ : 'Adequate capacity',
45
+ };
46
+ }
47
+ ```
@@ -0,0 +1,11 @@
1
+ # Error Handling Reference
2
+
3
+ | Issue | Cause | Solution |
4
+ |-------|-------|----------|
5
+ | k6 timeout | Rate limited | Reduce RPS |
6
+ | HPA not scaling | Wrong metrics | Verify metric name |
7
+ | Connection refused | Pool exhausted | Increase pool size |
8
+ | Inconsistent results | Warm-up needed | Add ramp-up phase |
9
+
10
+ ---
11
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
@@ -0,0 +1,26 @@
1
+ ## Examples
2
+
3
+ ### Quick k6 Test
4
+
5
+ ```bash
6
+ k6 run --vus 10 --duration 30s supabase-load-test.js
7
+ ```
8
+
9
+ ### Check Current Capacity
10
+
11
+ ```typescript
12
+ const metrics = await getSystemMetrics();
13
+ const capacity = estimateSupabaseCapacity(metrics);
14
+ console.log('Headroom:', capacity.headroom + '%');
15
+ console.log('Recommendation:', capacity.scaleRecommendation);
16
+ ```
17
+
18
+ ### Scale HPA Manually
19
+
20
+ ```bash
21
+ kubectl scale deployment supabase-integration --replicas=5
22
+ kubectl get hpa supabase-integration-hpa
23
+ ```
24
+
25
+ ---
26
+ *[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
@@ -0,0 +1,59 @@
1
+ # Load Testing With K6
2
+
3
+ ## Load Testing with k6
4
+
5
+ ### Basic Load Test
6
+
7
+ ```javascript
8
+ // supabase-load-test.js
9
+ import http from 'k6/http';
10
+ import { check, sleep } from 'k6';
11
+
12
+ export const options = {
13
+ stages: [
14
+ { duration: '2m', target: 10 }, // Ramp up
15
+ { duration: '5m', target: 10 }, // Steady state
16
+ { duration: '2m', target: 50 }, // Ramp to peak
17
+ { duration: '5m', target: 50 }, // Stress test
18
+ { duration: '2m', target: 0 }, // Ramp down
19
+ ],
20
+ thresholds: {
21
+ http_req_duration: ['p(95)<200'],
22
+ http_req_failed: ['rate<0.01'],
23
+ },
24
+ };
25
+
26
+ export default function () {
27
+ const response = http.post(
28
+ 'https://api.supabase.com/v1/resource',
29
+ JSON.stringify({ test: true }),
30
+ {
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ 'Authorization': `Bearer ${__ENV.SUPABASE_API_KEY}`,
34
+ },
35
+ }
36
+ );
37
+
38
+ check(response, {
39
+ 'status is 200': (r) => r.status === 200,
40
+ 'latency < 200ms': (r) => r.timings.duration < 200,
41
+ });
42
+
43
+ sleep(1);
44
+ }
45
+ ```
46
+
47
+ ### Run Load Test
48
+
49
+ ```bash
50
+ # Install k6
51
+ brew install k6 # macOS
52
+ # or: sudo apt install k6 # Linux
53
+
54
+ # Run test
55
+ k6 run --env SUPABASE_API_KEY=${SUPABASE_API_KEY} supabase-load-test.js
56
+
57
+ # Run with output to InfluxDB
58
+ k6 run --out influxdb=http://localhost:8086/k6 supabase-load-test.js
59
+ ```
@@ -0,0 +1,65 @@
1
+ # Scaling Patterns
2
+
3
+ ## Scaling Patterns
4
+
5
+ ### Horizontal Scaling
6
+
7
+ ```yaml
8
+ # kubernetes HPA
9
+ apiVersion: autoscaling/v2
10
+ kind: HorizontalPodAutoscaler
11
+ metadata:
12
+ name: supabase-integration-hpa
13
+ spec:
14
+ scaleTargetRef:
15
+ apiVersion: apps/v1
16
+ kind: Deployment
17
+ name: supabase-integration
18
+ minReplicas: 2
19
+ maxReplicas: 20
20
+ metrics:
21
+ - type: Resource
22
+ resource:
23
+ name: cpu
24
+ target:
25
+ type: Utilization
26
+ averageUtilization: 70
27
+ - type: Pods
28
+ pods:
29
+ metric:
30
+ name: supabase_queue_depth
31
+ target:
32
+ type: AverageValue
33
+ averageValue: 100
34
+ ```
35
+
36
+ ### Connection Pooling
37
+
38
+ ```typescript
39
+ import { Pool } from 'generic-pool';
40
+
41
+ const supabasePool = Pool.create({
42
+ create: async () => {
43
+ return new SupabaseClient({
44
+ apiKey: process.env.SUPABASE_API_KEY!,
45
+ });
46
+ },
47
+ destroy: async (client) => {
48
+ await client.close();
49
+ },
50
+ max: 20,
51
+ min: 5,
52
+ idleTimeoutMillis: 30000,
53
+ });
54
+
55
+ async function withSupabaseClient<T>(
56
+ fn: (client: SupabaseClient) => Promise<T>
57
+ ): Promise<T> {
58
+ const client = await supabasePool.acquire();
59
+ try {
60
+ return await fn(client);
61
+ } finally {
62
+ supabasePool.release(client);
63
+ }
64
+ }
65
+ ```
@@ -0,0 +1,263 @@
1
+ ## Database Table Partitioning
2
+
3
+ For tables with millions/billions of rows, partitioning splits data into smaller physical chunks. Supabase supports PostgreSQL native partitioning (range, list, hash). Queries that include the partition key only scan relevant partitions.
4
+
5
+ ### Range Partitioning by Date (Most Common)
6
+
7
+ ```sql
8
+ -- Create the partitioned parent table
9
+ CREATE TABLE public.events (
10
+ id bigint GENERATED ALWAYS AS IDENTITY,
11
+ org_id uuid NOT NULL REFERENCES public.organizations(id),
12
+ event_type text NOT NULL,
13
+ payload jsonb,
14
+ created_at timestamptz NOT NULL DEFAULT now(),
15
+ PRIMARY KEY (id, created_at) -- partition key must be in PK
16
+ ) PARTITION BY RANGE (created_at);
17
+
18
+ -- Create monthly partitions
19
+ CREATE TABLE public.events_2025_01 PARTITION OF public.events
20
+ FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
21
+ CREATE TABLE public.events_2025_02 PARTITION OF public.events
22
+ FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');
23
+ CREATE TABLE public.events_2025_03 PARTITION OF public.events
24
+ FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');
25
+ -- ... create partitions for each month
26
+
27
+ -- Default partition catches anything that doesn't match
28
+ CREATE TABLE public.events_default PARTITION OF public.events DEFAULT;
29
+
30
+ -- Index each partition (PostgreSQL auto-creates on child tables from parent index)
31
+ CREATE INDEX idx_events_org_created ON public.events (org_id, created_at);
32
+ CREATE INDEX idx_events_type ON public.events (event_type);
33
+
34
+ -- Enable RLS on the parent table (policies apply to all partitions)
35
+ ALTER TABLE public.events ENABLE ROW LEVEL SECURITY;
36
+
37
+ CREATE POLICY "Users read own org events" ON public.events
38
+ FOR SELECT USING (
39
+ org_id IN (
40
+ SELECT org_id FROM public.org_members WHERE user_id = auth.uid()
41
+ )
42
+ );
43
+ ```
44
+
45
+ ### Automate Partition Creation
46
+
47
+ ```sql
48
+ -- Function to auto-create next month's partition
49
+ CREATE OR REPLACE FUNCTION public.create_monthly_partition()
50
+ RETURNS void AS $$
51
+ DECLARE
52
+ next_month date := date_trunc('month', now() + interval '1 month');
53
+ partition_name text;
54
+ start_date text;
55
+ end_date text;
56
+ BEGIN
57
+ partition_name := 'events_' || to_char(next_month, 'YYYY_MM');
58
+ start_date := to_char(next_month, 'YYYY-MM-DD');
59
+ end_date := to_char(next_month + interval '1 month', 'YYYY-MM-DD');
60
+
61
+ -- Skip if partition already exists
62
+ IF NOT EXISTS (
63
+ SELECT 1 FROM pg_class WHERE relname = partition_name
64
+ ) THEN
65
+ EXECUTE format(
66
+ 'CREATE TABLE public.%I PARTITION OF public.events FOR VALUES FROM (%L) TO (%L)',
67
+ partition_name, start_date, end_date
68
+ );
69
+ RAISE NOTICE 'Created partition: %', partition_name;
70
+ END IF;
71
+ END;
72
+ $$ LANGUAGE plpgsql;
73
+
74
+ -- Schedule via pg_cron (runs on the 25th of each month)
75
+ SELECT cron.schedule(
76
+ 'create-events-partition',
77
+ '0 0 25 * *',
78
+ $$SELECT public.create_monthly_partition()$$
79
+ );
80
+ ```
81
+
82
+ ### Query Partitioned Tables from the SDK
83
+
84
+ ```typescript
85
+ import { createClient } from '@supabase/supabase-js'
86
+ import type { Database } from './database.types'
87
+
88
+ const supabase = createClient<Database>(
89
+ process.env.SUPABASE_URL!,
90
+ process.env.SUPABASE_ANON_KEY!
91
+ )
92
+
93
+ // IMPORTANT: Always include the partition key in your WHERE clause
94
+ // Without it, Postgres scans ALL partitions (defeats the purpose)
95
+
96
+ // GOOD: partition key (created_at) in the filter — scans only 1 partition
97
+ async function getRecentEvents(orgId: string) {
98
+ const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
99
+
100
+ const { data, error } = await supabase
101
+ .from('events')
102
+ .select('id, event_type, payload, created_at')
103
+ .eq('org_id', orgId)
104
+ .gte('created_at', thirtyDaysAgo) // <-- partition key filter
105
+ .order('created_at', { ascending: false })
106
+ .limit(100)
107
+
108
+ if (error) throw new Error(`Events query failed: ${error.message}`)
109
+ return data
110
+ }
111
+
112
+ // BAD: no partition key — scans every partition
113
+ async function getAllEvents(orgId: string) {
114
+ const { data, error } = await supabase
115
+ .from('events')
116
+ .select('id, event_type, payload, created_at')
117
+ .eq('org_id', orgId) // no created_at filter = full table scan across all partitions
118
+ // This query gets slower as more partitions are added
119
+ }
120
+
121
+ // Verify partition pruning with EXPLAIN
122
+ // Run in SQL Editor:
123
+ // EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM events WHERE org_id = '...' AND created_at > '2025-03-01';
124
+ // Look for "Partitions removed: N" in the output
125
+ ```
126
+
127
+ ### Drop Old Partitions (Data Retention)
128
+
129
+ ```sql
130
+ -- Drop partitions older than 12 months (instant, no vacuum needed)
131
+ CREATE OR REPLACE FUNCTION public.drop_old_partitions(retention_months int DEFAULT 12)
132
+ RETURNS void AS $$
133
+ DECLARE
134
+ cutoff date := date_trunc('month', now() - (retention_months || ' months')::interval);
135
+ partition record;
136
+ BEGIN
137
+ FOR partition IN
138
+ SELECT inhrelid::regclass::text AS name
139
+ FROM pg_inherits
140
+ WHERE inhparent = 'public.events'::regclass
141
+ AND inhrelid::regclass::text ~ 'events_\d{4}_\d{2}'
142
+ LOOP
143
+ -- Extract date from partition name and compare
144
+ IF substring(partition.name FROM 'events_(\d{4}_\d{2})')::date < cutoff THEN
145
+ EXECUTE format('DROP TABLE %s', partition.name);
146
+ RAISE NOTICE 'Dropped old partition: %', partition.name;
147
+ END IF;
148
+ END LOOP;
149
+ END;
150
+ $$ LANGUAGE plpgsql;
151
+
152
+ -- Schedule monthly cleanup
153
+ SELECT cron.schedule('drop-old-partitions', '0 2 1 * *',
154
+ $$SELECT public.drop_old_partitions(12)$$
155
+ );
156
+ ```
157
+
158
+ ## Output
159
+
160
+ - Read replica client configured for analytics/dashboard queries, primary for writes
161
+ - Connection pooling mode selected (transaction vs session) with correct port
162
+ - Compute tier matched to traffic requirements
163
+ - Storage uploads optimized with CDN cache headers and image transforms
164
+ - Edge Functions deployed to the region closest to users
165
+ - Large tables partitioned by date range with automated partition management
166
+ - Data retention policy via partition drops
167
+
168
+ ## Error Handling
169
+
170
+ | Issue | Cause | Solution |
171
+ |-------|-------|----------|
172
+ | `too many connections for role` | Exceeded Supavisor pool limit | Use transaction mode (port 6543), reduce idle connections, upgrade compute |
173
+ | Read replica returns stale data | Replication lag (typically <100ms) | Do not read-after-write on replica; use primary for consistency-critical reads |
174
+ | `no partition of relation "events" found for row` | Insert date outside any partition range | Create a DEFAULT partition or pre-create future partitions |
175
+ | Storage CDN returns old file | Cached at edge | Use content-addressed paths (`hash.ext`) or set shorter `cacheControl` |
176
+ | Edge Function cold start | First request to a region | Use `keep-alive` cron ping or accept ~200ms cold start |
177
+ | `prepared statement already exists` | Transaction mode doesn't support prepared statements | Switch to session mode (port 5432) or disable prepared statements in your ORM |
178
+
179
+ ## Examples
180
+
181
+ ### Quick Connection Pool Check
182
+
183
+ ```bash
184
+ # Check how many connections are in use right now
185
+ psql "$DATABASE_URL" -c "SELECT count(*) AS active_connections FROM pg_stat_activity WHERE state = 'active';"
186
+ ```
187
+
188
+ ### Switch an Existing Table to Partitioned
189
+
190
+ ```sql
191
+ -- You cannot ALTER an existing table to be partitioned.
192
+ -- Instead: create partitioned table, migrate data, swap names.
193
+
194
+ -- 1. Create new partitioned table
195
+ CREATE TABLE public.events_partitioned (LIKE public.events INCLUDING ALL)
196
+ PARTITION BY RANGE (created_at);
197
+
198
+ -- 2. Create partitions for existing data range
199
+ CREATE TABLE public.events_p_2025_01 PARTITION OF public.events_partitioned
200
+ FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
201
+ -- ... more partitions
202
+
203
+ -- 3. Copy data (do this during low traffic)
204
+ INSERT INTO public.events_partitioned SELECT * FROM public.events;
205
+
206
+ -- 4. Swap tables in a transaction
207
+ BEGIN;
208
+ ALTER TABLE public.events RENAME TO events_old;
209
+ ALTER TABLE public.events_partitioned RENAME TO events;
210
+ COMMIT;
211
+
212
+ -- 5. Verify, then drop old table
213
+ DROP TABLE public.events_old;
214
+ ```
215
+
216
+ ### Test Read Replica Lag
217
+
218
+ ```typescript
219
+ import { supabase, supabaseReadOnly } from '../lib/supabase'
220
+
221
+ async function measureReplicaLag() {
222
+ // Write to primary
223
+ const { data: written } = await supabase
224
+ .from('health_checks')
225
+ .insert({ timestamp: new Date().toISOString() })
226
+ .select('id, timestamp')
227
+ .single()
228
+
229
+ // Immediately read from replica
230
+ const start = Date.now()
231
+ let found = false
232
+
233
+ while (!found && Date.now() - start < 5000) {
234
+ const { data } = await supabaseReadOnly
235
+ .from('health_checks')
236
+ .select('id')
237
+ .eq('id', written!.id)
238
+ .maybeSingle()
239
+
240
+ if (data) {
241
+ found = true
242
+ console.log(`Replica lag: ${Date.now() - start}ms`)
243
+ } else {
244
+ await new Promise(r => setTimeout(r, 10))
245
+ }
246
+ }
247
+
248
+ if (!found) console.warn('Replica lag exceeds 5 seconds')
249
+ }
250
+ ```
251
+
252
+ ## Resources
253
+
254
+ - [Supabase Read Replicas](https://supabase.com/docs/guides/platform/read-replicas)
255
+ - [Supabase Connection Pooling (Supavisor)](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler)
256
+ - [Supabase Compute Add-ons](https://supabase.com/docs/guides/platform/compute-add-ons)
257
+ - [Supabase Storage CDN](https://supabase.com/docs/guides/storage/cdn/fundamentals)
258
+ - [Supabase Edge Functions](https://supabase.com/docs/guides/functions)
259
+ - [PostgreSQL Table Partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html)
260
+
261
+ ## Next Steps
262
+
263
+ For reliability patterns (circuit breakers, offline queues, graceful degradation), see `supabase-reliability-patterns`.