@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
|
+
| Can't reproduce | Race condition | Add timing analysis |
|
|
6
|
+
| Intermittent failure | Timing-dependent | Increase sample size |
|
|
7
|
+
| No useful logs | Missing instrumentation | Add debug logging |
|
|
8
|
+
| Memory growth | Resource leak | Use heap profiling |
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Evidence Collection Framework
|
|
2
|
+
|
|
3
|
+
## Evidence Collection Framework
|
|
4
|
+
|
|
5
|
+
### Comprehensive Debug Bundle
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
#!/bin/bash
|
|
9
|
+
# advanced-supabase-debug.sh
|
|
10
|
+
|
|
11
|
+
BUNDLE="supabase-advanced-debug-$(date +%Y%m%d-%H%M%S)"
|
|
12
|
+
mkdir -p "$BUNDLE"/{logs,metrics,network,config,traces}
|
|
13
|
+
|
|
14
|
+
# 1. Extended logs (1 hour window)
|
|
15
|
+
kubectl logs -l app=supabase-integration --since=1h > "$BUNDLE/logs/pods.log"
|
|
16
|
+
journalctl -u supabase-service --since "1 hour ago" > "$BUNDLE/logs/system.log"
|
|
17
|
+
|
|
18
|
+
# 2. Metrics dump
|
|
19
|
+
curl -s localhost:9090/api/v1/query?query=supabase_requests_total > "$BUNDLE/metrics/requests.json"
|
|
20
|
+
curl -s localhost:9090/api/v1/query?query=supabase_errors_total > "$BUNDLE/metrics/errors.json"
|
|
21
|
+
|
|
22
|
+
# 3. Network capture (30 seconds)
|
|
23
|
+
timeout 30 tcpdump -i any port 443 -w "$BUNDLE/network/capture.pcap" &
|
|
24
|
+
|
|
25
|
+
# 4. Distributed traces
|
|
26
|
+
curl -s localhost:16686/api/traces?service=supabase > "$BUNDLE/traces/jaeger.json"
|
|
27
|
+
|
|
28
|
+
# 5. Configuration state
|
|
29
|
+
kubectl get cm supabase-config -o yaml > "$BUNDLE/config/configmap.yaml"
|
|
30
|
+
kubectl get secret supabase-secrets -o yaml > "$BUNDLE/config/secrets-redacted.yaml"
|
|
31
|
+
|
|
32
|
+
tar -czf "$BUNDLE.tar.gz" "$BUNDLE"
|
|
33
|
+
echo "Advanced debug bundle: $BUNDLE.tar.gz"
|
|
34
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Examples
|
|
2
|
+
|
|
3
|
+
### Quick Layer Test
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Test each layer in sequence
|
|
7
|
+
curl -v https://api.supabase.com/health 2>&1 | grep -E "(Connected|TLS|HTTP)"
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# RLS Conflicts, Edge Function Cold Starts, and Realtime Drops
|
|
2
|
+
|
|
3
|
+
Diagnose RLS policy conflicts that cause unexpected access patterns, profile Edge Function cold starts, and investigate Realtime connection drops.
|
|
4
|
+
|
|
5
|
+
**RLS policy conflict analysis:**
|
|
6
|
+
|
|
7
|
+
```sql
|
|
8
|
+
-- List ALL policies on a table to find conflicts
|
|
9
|
+
SELECT
|
|
10
|
+
pol.polname AS policy_name,
|
|
11
|
+
CASE pol.polcmd
|
|
12
|
+
WHEN 'r' THEN 'SELECT'
|
|
13
|
+
WHEN 'a' THEN 'INSERT'
|
|
14
|
+
WHEN 'w' THEN 'UPDATE'
|
|
15
|
+
WHEN 'd' THEN 'DELETE'
|
|
16
|
+
WHEN '*' THEN 'ALL'
|
|
17
|
+
END AS command,
|
|
18
|
+
CASE pol.polpermissive
|
|
19
|
+
WHEN true THEN 'PERMISSIVE'
|
|
20
|
+
ELSE 'RESTRICTIVE'
|
|
21
|
+
END AS type,
|
|
22
|
+
pg_get_expr(pol.polqual, pol.polrelid) AS using_clause,
|
|
23
|
+
pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check_clause,
|
|
24
|
+
ARRAY(SELECT rolname FROM pg_roles WHERE oid = ANY(pol.polroles)) AS applies_to_roles
|
|
25
|
+
FROM pg_policy pol
|
|
26
|
+
JOIN pg_class cls ON cls.oid = pol.polrelid
|
|
27
|
+
WHERE cls.relname = 'your_table_name'
|
|
28
|
+
ORDER BY pol.polcmd, pol.polpermissive DESC;
|
|
29
|
+
|
|
30
|
+
-- Common conflict: PERMISSIVE policies are OR'd together
|
|
31
|
+
-- If you have two SELECT policies, a row is visible if EITHER matches
|
|
32
|
+
-- RESTRICTIVE policies are AND'd — all must pass
|
|
33
|
+
|
|
34
|
+
-- Test a specific user's access
|
|
35
|
+
SET request.jwt.claim.sub = 'user-uuid';
|
|
36
|
+
SET request.jwt.claim.role = 'authenticated';
|
|
37
|
+
SET request.jwt.claims = '{"sub": "user-uuid", "role": "authenticated", "app_metadata": {"role": "editor", "org_id": "org-123"}}';
|
|
38
|
+
|
|
39
|
+
-- Check what they can see
|
|
40
|
+
SELECT count(*) FROM your_table_name;
|
|
41
|
+
|
|
42
|
+
-- Check the auth functions
|
|
43
|
+
SELECT auth.uid(), auth.jwt(), auth.role();
|
|
44
|
+
|
|
45
|
+
RESET ALL;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**RLS conflict debugging from the SDK:**
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { createClient } from '@supabase/supabase-js';
|
|
52
|
+
|
|
53
|
+
// Compare results across permission levels
|
|
54
|
+
async function debugRLSConflict(table: string, filters: Record<string, string>) {
|
|
55
|
+
const anonClient = createClient(url, anonKey);
|
|
56
|
+
const authedClient = createClient(url, anonKey);
|
|
57
|
+
const adminClient = createClient(url, serviceRoleKey, {
|
|
58
|
+
auth: { autoRefreshToken: false, persistSession: false },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Sign in as a test user
|
|
62
|
+
await authedClient.auth.signInWithPassword({
|
|
63
|
+
email: 'test@example.com',
|
|
64
|
+
password: 'test-password',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
let query = (client: any) => {
|
|
68
|
+
let q = client.from(table).select('*', { count: 'exact' });
|
|
69
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
70
|
+
q = q.eq(key, value);
|
|
71
|
+
}
|
|
72
|
+
return q;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const [anonResult, authedResult, adminResult] = await Promise.all([
|
|
76
|
+
query(anonClient),
|
|
77
|
+
query(authedClient),
|
|
78
|
+
query(adminClient),
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
console.log(`RLS debug for "${table}":`, {
|
|
82
|
+
anon: { count: anonResult.count, error: anonResult.error?.message },
|
|
83
|
+
authenticated: { count: authedResult.count, error: authedResult.error?.message },
|
|
84
|
+
admin: { count: adminResult.count, error: adminResult.error?.message },
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (adminResult.count !== authedResult.count) {
|
|
88
|
+
console.warn(
|
|
89
|
+
`RLS filtering: admin sees ${adminResult.count} rows, user sees ${authedResult.count}`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Edge Function cold start profiling:**
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { createClient } from '@supabase/supabase-js';
|
|
99
|
+
|
|
100
|
+
const supabase = createClient(url, anonKey);
|
|
101
|
+
|
|
102
|
+
// Measure cold start vs warm invocation times
|
|
103
|
+
async function profileEdgeFunction(
|
|
104
|
+
functionName: string,
|
|
105
|
+
iterations = 5,
|
|
106
|
+
coldStartDelayMs = 60000
|
|
107
|
+
) {
|
|
108
|
+
const results: { iteration: number; durationMs: number; isColdStart: boolean }[] = [];
|
|
109
|
+
|
|
110
|
+
for (let i = 0; i < iterations; i++) {
|
|
111
|
+
// Wait before first call to ensure cold start
|
|
112
|
+
if (i === 0) {
|
|
113
|
+
console.log(`Waiting ${coldStartDelayMs / 1000}s for cold start...`);
|
|
114
|
+
await new Promise((r) => setTimeout(r, coldStartDelayMs));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const start = performance.now();
|
|
118
|
+
const { data, error } = await supabase.functions.invoke(functionName, {
|
|
119
|
+
body: { action: 'ping', timestamp: Date.now() },
|
|
120
|
+
});
|
|
121
|
+
const duration = Math.round(performance.now() - start);
|
|
122
|
+
|
|
123
|
+
results.push({
|
|
124
|
+
iteration: i + 1,
|
|
125
|
+
durationMs: duration,
|
|
126
|
+
isColdStart: i === 0 || duration > 1000,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
console.log(`Invocation ${i + 1}: ${duration}ms ${i === 0 ? '(cold start)' : '(warm)'}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const coldStarts = results.filter((r) => r.isColdStart);
|
|
133
|
+
const warmStarts = results.filter((r) => !r.isColdStart);
|
|
134
|
+
|
|
135
|
+
console.log('Summary:', {
|
|
136
|
+
coldStartAvgMs: coldStarts.length > 0
|
|
137
|
+
? Math.round(coldStarts.reduce((s, r) => s + r.durationMs, 0) / coldStarts.length)
|
|
138
|
+
: 'N/A',
|
|
139
|
+
warmStartAvgMs: warmStarts.length > 0
|
|
140
|
+
? Math.round(warmStarts.reduce((s, r) => s + r.durationMs, 0) / warmStarts.length)
|
|
141
|
+
: 'N/A',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Check Edge Function logs for cold start indicators
|
|
148
|
+
npx supabase functions logs my-function --project-ref <ref> 2>&1 | head -50
|
|
149
|
+
|
|
150
|
+
# Look for patterns:
|
|
151
|
+
# - First invocation after deploy: high latency
|
|
152
|
+
# - "Worker booted" or "Isolate created" messages
|
|
153
|
+
# - Memory/CPU spikes on first request
|
|
154
|
+
|
|
155
|
+
# Reduce cold starts:
|
|
156
|
+
# 1. Minimize imports (lazy-load heavy dependencies)
|
|
157
|
+
# 2. Keep function payload small
|
|
158
|
+
# 3. Use scheduled warm-up pings via pg_cron
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Realtime connection drop investigation:**
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { createClient } from '@supabase/supabase-js';
|
|
165
|
+
|
|
166
|
+
const supabase = createClient(url, anonKey);
|
|
167
|
+
|
|
168
|
+
// Debug Realtime connection stability
|
|
169
|
+
function monitorRealtimeChannel(table: string) {
|
|
170
|
+
const channel = supabase
|
|
171
|
+
.channel(`debug-${table}`)
|
|
172
|
+
.on('system', {}, (payload) => {
|
|
173
|
+
console.log(`[SYSTEM] ${new Date().toISOString()}:`, payload);
|
|
174
|
+
// Watch for: CHANNEL_ERROR, TIMED_OUT, TOKEN_EXPIRED
|
|
175
|
+
})
|
|
176
|
+
.on(
|
|
177
|
+
'postgres_changes',
|
|
178
|
+
{ event: '*', schema: 'public', table },
|
|
179
|
+
(payload) => {
|
|
180
|
+
console.log(`[CHANGE] ${payload.eventType}:`, payload.new);
|
|
181
|
+
}
|
|
182
|
+
)
|
|
183
|
+
.subscribe((status, err) => {
|
|
184
|
+
console.log(`[STATUS] ${new Date().toISOString()}: ${status}`, err ?? '');
|
|
185
|
+
|
|
186
|
+
if (status === 'CHANNEL_ERROR') {
|
|
187
|
+
console.error('Channel error — will auto-reconnect');
|
|
188
|
+
}
|
|
189
|
+
if (status === 'TIMED_OUT') {
|
|
190
|
+
console.error('Connection timed out — check network/firewall');
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Monitor connection health periodically
|
|
195
|
+
const healthInterval = setInterval(() => {
|
|
196
|
+
const state = channel.state;
|
|
197
|
+
console.log(`[HEALTH] Channel state: ${state}`);
|
|
198
|
+
if (state !== 'joined') {
|
|
199
|
+
console.warn(`[HEALTH] Channel not joined, current state: ${state}`);
|
|
200
|
+
}
|
|
201
|
+
}, 30000);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
channel,
|
|
205
|
+
stop: () => {
|
|
206
|
+
clearInterval(healthInterval);
|
|
207
|
+
channel.unsubscribe();
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
```sql
|
|
214
|
+
-- Verify table is in the Realtime publication
|
|
215
|
+
SELECT schemaname, tablename
|
|
216
|
+
FROM pg_publication_tables
|
|
217
|
+
WHERE pubname = 'supabase_realtime'
|
|
218
|
+
ORDER BY tablename;
|
|
219
|
+
|
|
220
|
+
-- Add a missing table to the publication
|
|
221
|
+
ALTER PUBLICATION supabase_realtime ADD TABLE public.your_table;
|
|
222
|
+
|
|
223
|
+
-- Check Realtime connection limits
|
|
224
|
+
-- Supabase free plan: 200 concurrent connections
|
|
225
|
+
-- Pro plan: 500 concurrent connections
|
|
226
|
+
-- You can check current count in the Supabase dashboard → Realtime → Connections
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Output
|
|
230
|
+
|
|
231
|
+
After completing this skill, you will have:
|
|
232
|
+
|
|
233
|
+
- **Slow query identification** — `pg_stat_statements` queries ranking by total time, frequency, and cache hit ratio
|
|
234
|
+
- **EXPLAIN ANALYZE proficiency** — reading execution plans and creating targeted indexes
|
|
235
|
+
- **Lock contention diagnosis** — blocked/blocking query pairs with lock modes
|
|
236
|
+
- **Connection leak detection** — idle and idle-in-transaction connections with kill commands
|
|
237
|
+
- **Connection pool monitoring** — SDK-based health check RPC with utilization alerts
|
|
238
|
+
- **RLS conflict analysis** — policy listing with permissive/restrictive classification and multi-level comparison
|
|
239
|
+
- **Edge Function profiling** — cold start vs warm invocation measurement
|
|
240
|
+
- **Realtime debugging** — channel state monitoring with system event logging
|
|
241
|
+
|
|
242
|
+
## Error Handling
|
|
243
|
+
|
|
244
|
+
| Error | Cause | Solution |
|
|
245
|
+
|-------|-------|----------|
|
|
246
|
+
| `pg_stat_statements` not available | Extension not enabled | Run `CREATE EXTENSION pg_stat_statements;` |
|
|
247
|
+
| Seq Scan on large table | Missing index on filter column | Create index with `CREATE INDEX CONCURRENTLY` |
|
|
248
|
+
| `deadlock detected` | Circular lock dependency | Ensure consistent lock ordering across transactions |
|
|
249
|
+
| All connections in `idle in transaction` | Application not closing transactions | Add connection timeout; review ORM connection pool settings |
|
|
250
|
+
| RLS returns empty for authenticated user | JWT claims don't match policy | Check `auth.jwt()` output; verify `app_metadata` is set |
|
|
251
|
+
| Edge Function > 2s cold start | Large dependency bundle | Lazy-import heavy modules; reduce function size |
|
|
252
|
+
| Realtime `TIMED_OUT` | Network/firewall blocking WebSocket | Check port 443 is open; verify no proxy strips `Upgrade` header |
|
|
253
|
+
| `CHANNEL_ERROR` on subscribe | Table not in Realtime publication | Run `ALTER PUBLICATION supabase_realtime ADD TABLE ...` |
|
|
254
|
+
|
|
255
|
+
## Examples
|
|
256
|
+
|
|
257
|
+
**Example 1 — Quick performance audit:**
|
|
258
|
+
|
|
259
|
+
```sql
|
|
260
|
+
-- Run this query to get a snapshot of database health
|
|
261
|
+
SELECT
|
|
262
|
+
'Connections' AS metric,
|
|
263
|
+
count(*)::text AS value
|
|
264
|
+
FROM pg_stat_activity WHERE datname = current_database()
|
|
265
|
+
UNION ALL
|
|
266
|
+
SELECT 'Cache hit ratio',
|
|
267
|
+
round(100.0 * sum(heap_blks_hit) / nullif(sum(heap_blks_hit + heap_blks_read), 0), 2)::text || '%'
|
|
268
|
+
FROM pg_statio_user_tables
|
|
269
|
+
UNION ALL
|
|
270
|
+
SELECT 'Table bloat (dead tuples)',
|
|
271
|
+
sum(n_dead_tup)::text
|
|
272
|
+
FROM pg_stat_user_tables
|
|
273
|
+
UNION ALL
|
|
274
|
+
SELECT 'Longest running query',
|
|
275
|
+
coalesce(max(age(now(), query_start))::text, 'none')
|
|
276
|
+
FROM pg_stat_activity WHERE state = 'active' AND query NOT LIKE '%pg_stat%';
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Example 2 — Build a diagnostic bundle for support:**
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { createClient } from '@supabase/supabase-js';
|
|
283
|
+
|
|
284
|
+
const supabase = createClient(url, serviceRoleKey, {
|
|
285
|
+
auth: { autoRefreshToken: false, persistSession: false },
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
async function buildDiagnosticBundle() {
|
|
289
|
+
const bundle: Record<string, any> = {
|
|
290
|
+
timestamp: new Date().toISOString(),
|
|
291
|
+
projectRef: process.env.SUPABASE_PROJECT_REF,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
// Connection stats
|
|
295
|
+
const { data: connHealth } = await supabase.rpc('get_connection_health');
|
|
296
|
+
bundle.connections = connHealth;
|
|
297
|
+
|
|
298
|
+
// Table sizes
|
|
299
|
+
const { data: tableSizes } = await supabase.rpc('get_table_sizes');
|
|
300
|
+
bundle.tableSizes = tableSizes;
|
|
301
|
+
|
|
302
|
+
// Recent errors from application logs
|
|
303
|
+
const { data: recentErrors } = await supabase
|
|
304
|
+
.from('error_logs')
|
|
305
|
+
.select('message, count, last_seen')
|
|
306
|
+
.order('last_seen', { ascending: false })
|
|
307
|
+
.limit(10);
|
|
308
|
+
bundle.recentErrors = recentErrors;
|
|
309
|
+
|
|
310
|
+
console.log(JSON.stringify(bundle, null, 2));
|
|
311
|
+
// Submit with your support ticket at https://supabase.com/dashboard/support
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Example 3 — Automated slow query alert:**
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { createClient } from '@supabase/supabase-js';
|
|
319
|
+
|
|
320
|
+
const supabase = createClient(url, serviceRoleKey, {
|
|
321
|
+
auth: { autoRefreshToken: false, persistSession: false },
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
async function checkSlowQueries(thresholdMs = 1000) {
|
|
325
|
+
const { data: slowQueries } = await supabase.rpc('get_slow_queries', {
|
|
326
|
+
threshold_ms: thresholdMs,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (slowQueries && slowQueries.length > 0) {
|
|
330
|
+
console.warn(`Found ${slowQueries.length} queries averaging > ${thresholdMs}ms`);
|
|
331
|
+
for (const q of slowQueries) {
|
|
332
|
+
console.warn(` [${q.avg_ms}ms avg, ${q.calls} calls] ${q.query_preview}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Database function:
|
|
338
|
+
// CREATE OR REPLACE FUNCTION get_slow_queries(threshold_ms numeric DEFAULT 1000)
|
|
339
|
+
// RETURNS TABLE(queryid bigint, avg_ms numeric, calls bigint, query_preview text) AS $$
|
|
340
|
+
// SELECT queryid, round(mean_exec_time::numeric, 2), calls, left(query, 150)
|
|
341
|
+
// FROM pg_stat_statements
|
|
342
|
+
// WHERE mean_exec_time > threshold_ms AND calls > 10
|
|
343
|
+
// ORDER BY mean_exec_time DESC LIMIT 10;
|
|
344
|
+
// $$ LANGUAGE sql SECURITY DEFINER;
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## Resources
|
|
348
|
+
|
|
349
|
+
- [pg_stat_statements — PostgreSQL Docs](https://www.postgresql.org/docs/current/pgstatstatements.html)
|
|
350
|
+
- [EXPLAIN ANALYZE — PostgreSQL Docs](https://www.postgresql.org/docs/current/sql-explain.html)
|
|
351
|
+
- [Supabase Performance Advisor](https://supabase.com/docs/guides/database/inspect)
|
|
352
|
+
- [RLS Debugging — Supabase Docs](https://supabase.com/docs/guides/troubleshooting/rls-simplified-BJTcS8)
|
|
353
|
+
- [Edge Functions Logging — Supabase Docs](https://supabase.com/docs/guides/functions/logging)
|
|
354
|
+
- Realtime Debugging — Supabase Docs
|
|
355
|
+
- [pg_locks — PostgreSQL Docs](https://www.postgresql.org/docs/current/view-pg-locks.html)
|
|
356
|
+
- [Connection Pooling with Supavisor](https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler)
|
|
357
|
+
|
|
358
|
+
## Next Steps
|
|
359
|
+
|
|
360
|
+
- For load testing and scaling patterns, see `supabase-load-scale`
|
|
361
|
+
- For incident response procedures, see `supabase-incident-runbook`
|
|
362
|
+
- For performance tuning and index optimization, see `supabase-performance-tuning`
|
|
363
|
+
- For common error patterns and quick fixes, see `supabase-common-errors`
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Systematic Isolation
|
|
2
|
+
|
|
3
|
+
## Systematic Isolation
|
|
4
|
+
|
|
5
|
+
### Layer-by-Layer Testing
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// Test each layer independently
|
|
9
|
+
async function diagnoseSupabaseIssue(): Promise<DiagnosisReport> {
|
|
10
|
+
const results: DiagnosisResult[] = [];
|
|
11
|
+
|
|
12
|
+
// Layer 1: Network connectivity
|
|
13
|
+
results.push(await testNetworkConnectivity());
|
|
14
|
+
|
|
15
|
+
// Layer 2: DNS resolution
|
|
16
|
+
results.push(await testDNSResolution('api.supabase.com'));
|
|
17
|
+
|
|
18
|
+
// Layer 3: TLS handshake
|
|
19
|
+
results.push(await testTLSHandshake('api.supabase.com'));
|
|
20
|
+
|
|
21
|
+
// Layer 4: Authentication
|
|
22
|
+
results.push(await testAuthentication());
|
|
23
|
+
|
|
24
|
+
// Layer 5: API response
|
|
25
|
+
results.push(await testAPIResponse());
|
|
26
|
+
|
|
27
|
+
// Layer 6: Response parsing
|
|
28
|
+
results.push(await testResponseParsing());
|
|
29
|
+
|
|
30
|
+
return { results, firstFailure: results.find(r => !r.success) };
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Minimal Reproduction
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Strip down to absolute minimum
|
|
38
|
+
async function minimalRepro(): Promise<void> {
|
|
39
|
+
// 1. Fresh client, no customization
|
|
40
|
+
const client = new SupabaseClient({
|
|
41
|
+
apiKey: process.env.SUPABASE_API_KEY!,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 2. Simplest possible call
|
|
45
|
+
try {
|
|
46
|
+
const result = await client.ping();
|
|
47
|
+
console.log('Ping successful:', result);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Ping failed:', {
|
|
50
|
+
message: error.message,
|
|
51
|
+
code: error.code,
|
|
52
|
+
stack: error.stack,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Timing Analysis
|
|
2
|
+
|
|
3
|
+
## Timing Analysis
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
class TimingAnalyzer {
|
|
7
|
+
private timings: Map<string, number[]> = new Map();
|
|
8
|
+
|
|
9
|
+
async measure<T>(label: string, fn: () => Promise<T>): Promise<T> {
|
|
10
|
+
const start = performance.now();
|
|
11
|
+
try {
|
|
12
|
+
return await fn();
|
|
13
|
+
} finally {
|
|
14
|
+
const duration = performance.now() - start;
|
|
15
|
+
const existing = this.timings.get(label) || [];
|
|
16
|
+
existing.push(duration);
|
|
17
|
+
this.timings.set(label, existing);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
report(): TimingReport {
|
|
22
|
+
const report: TimingReport = {};
|
|
23
|
+
for (const [label, times] of this.timings) {
|
|
24
|
+
report[label] = {
|
|
25
|
+
count: times.length,
|
|
26
|
+
min: Math.min(...times),
|
|
27
|
+
max: Math.max(...times),
|
|
28
|
+
avg: times.reduce((a, b) => a + b, 0) / times.length,
|
|
29
|
+
p95: this.percentile(times, 95),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return report;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|