ワンクリックで
analyze-performance
Query performance analysis and optimization (explain plans, hotpath detection, interactive optimization)
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Query performance analysis and optimization (explain plans, hotpath detection, interactive optimization)
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
Execute story development with selectable automation modes to accommodate different developer preferences, skill levels, and story complexity.
Set up a Kord-aligned documentation baseline (no legacy frameworks)
Create Next Story Task methodology and workflow
Validate Next Story Task methodology and workflow
advanced-elicitation methodology and workflow
No checklists needed - this task facilitates brainstorming sessions, validation is through user interaction methodology and workflow
SOC 職業分類に基づく
| name | analyze-performance |
| description | Query performance analysis and optimization (explain plans, hotpath detection, interactive optimization) |
| agent | architect |
| subtask | false |
Query performance analysis and optimization (explain plans, hotpath detection, interactive optimization)
Parameter:* mode (optional, default: interactive)
Purpose:* Definitive pass/fail criteria for task completion
Checklist:*
acceptance-criteria:
- [ ] Analysis accurate; all targets covered; report complete
type: acceptance-criterion
blocker: true
validation: |
Assert analysis accurate; all targets covered; report complete
error_message: "Acceptance criterion not met: Analysis accurate; all targets covered; report complete"
Strategy:* fallback
Common Errors:*
Error:* Target Not Accessible
Error:* Analysis Timeout
Error:* Memory Limit Exceeded
Prompt user to select analysis type:*
Select performance analysis type:
1. *query** - Analyze specific query execution plan
2. *hotpaths** - Detect performance bottlenecks across system
3. *interactive** - Interactive query optimization session
Which type? [query/hotpaths/interactive]:
Capture:* {type}
If type=query, also prompt:*
Enter SQL query to analyze (or file path):
Capture:* {query}
When:* User selects query
Purpose:* Analyze execution plan for specific query
# Check if query is a file path
if [[ -f "$QUERY" ]]; then
QUERY_SQL=$(cat "$QUERY")
else
QUERY_SQL="$QUERY"
fi
# Validate SQL syntax (basic)
echo "$QUERY_SQL" | grep -iE '^(SELECT|WITH|EXPLAIN)' || {
echo "❌ Error: Query must start with SELECT, WITH, or EXPLAIN"
exit 1
}
psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 <<SQL
\echo '=== Query Performance Analysis ==='
\echo ''
\echo 'Query:'
\echo '$QUERY_SQL'
\echo ''
\echo '=== Execution Plan (EXPLAIN ANALYZE) ==='
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, FORMAT TEXT)
$QUERY_SQL;
\echo ''
\echo '=== JSON Format (for tools) ==='
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
$QUERY_SQL;
SQL
psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 <<'SQL'
\echo ''
\echo '=== Performance Recommendations ==='
-- Check for sequential scans on large tables
SELECT
schemaname,
tablename,
seq_scan,
seq_tup_read,
idx_scan,
CASE
WHEN seq_scan > idx_scan THEN '⚠️ Consider adding index'
WHEN seq_tup_read > 10000 THEN '⚠️ Large sequential scan detected'
ELSE '✓ Looks good'
END AS recommendation
FROM pg_stat_user_tables
WHERE schemaname = 'public'
AND (seq_scan > idx_scan OR seq_tup_read > 10000)
ORDER BY seq_tup_read DESC
LIMIT 10;
SQL
When:* User selects hotpaths
Purpose:* Detect performance bottlenecks across entire system
psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 <<'SQL'
\echo '=== Performance Hotpaths Analysis ==='
\echo ''
-- 1. Slowest Queries (requires pg_stat_statements extension)
\echo '1. Top 10 Slowest Queries:'
SELECT
LEFT(query, 80) AS query_preview,
calls,
ROUND(total_exec_time::numeric / 1000, 2) AS total_seconds,
ROUND(mean_exec_time::numeric, 2) AS avg_ms,
ROUND((100 * total_exec_time / SUM(total_exec_time) OVER ())::numeric, 2) AS percent_total
FROM pg_stat_statements
WHERE query NOT LIKE '%pg_stat_statements%'
ORDER BY total_exec_time DESC
LIMIT 10;
\echo ''
\echo '2. Most Frequent Queries:'
SELECT
LEFT(query, 80) AS query_preview,
calls,
ROUND(mean_exec_time::numeric, 2) AS avg_ms,
ROUND(total_exec_time::numeric / 1000, 2) AS total_seconds
FROM pg_stat_statements
WHERE query NOT LIKE '%pg_stat_statements%'
ORDER BY calls DESC
LIMIT 10;
\echo ''
\echo '3. Tables with Most Sequential Scans:'
SELECT
schemaname,
tablename,
seq_scan,
seq_tup_read,
idx_scan,
n_live_tup AS approx_rows,
ROUND((seq_tup_read::numeric / NULLIF(seq_scan, 0)), 0) AS avg_rows_per_scan
FROM pg_stat_user_tables
WHERE schemaname = 'public'
AND seq_scan > 0
ORDER BY seq_tup_read DESC
LIMIT 10;
\echo ''
\echo '4. Tables with Bloat (Dead Tuples):'
SELECT
schemaname,
tablename,
n_live_tup,
n_dead_tup,
ROUND((n_dead_tup::numeric / NULLIF(n_live_tup, 0) * 100), 2) AS dead_tuple_percent,
last_vacuum,
last_autovacuum
FROM pg_stat_user_tables
WHERE schemaname = 'public'
AND n_dead_tup > 100
ORDER BY n_dead_tup DESC
LIMIT 10;
\echo ''
\echo '5. Missing Indexes (Foreign Keys without indexes):'
SELECT
t.tablename,
c.column_name,
pg_size_pretty(pg_relation_size(t.tablename::regclass)) AS table_size,
'CREATE INDEX idx_' || t.tablename || '_' || c.column_name || ' ON ' || t.tablename || '(' || c.column_name || ');' AS suggested_index
FROM pg_tables t
JOIN information_schema.columns c ON c.table_name = t.tablename
LEFT JOIN pg_indexes i ON i.tablename = t.tablename
AND i.indexdef LIKE '%' || c.column_name || '%'
WHERE t.schemaname = 'public'
AND c.table_schema = 'public'
AND c.column_name LIKE '%_id'
AND c.column_name != 'id'
AND i.indexname IS NULL
ORDER BY pg_relation_size(t.tablename::regclass) DESC
LIMIT 10;
\echo ''
\echo '6. Index Usage Statistics:'
SELECT
schemaname,
tablename,
indexname,
idx_scan,
idx_tup_read,
idx_tup_fetch,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
CASE
WHEN idx_scan = 0 THEN '❌ Unused - consider dropping'
WHEN idx_scan < 100 THEN '⚠️ Low usage'
ELSE '✓ Active'
END AS usage_status
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
ORDER BY idx_scan ASC, pg_relation_size(indexrelid) DESC
LIMIT 15;
\echo ''
\echo '7. Cache Hit Ratio (should be > 99%):'
SELECT
'Index Hit Rate' AS metric,
ROUND((SUM(idx_blks_hit) / NULLIF(SUM(idx_blks_hit + idx_blks_read), 0) * 100)::numeric, 2) AS percentage
FROM pg_statio_user_indexes
UNION ALL
SELECT
'Table Hit Rate' AS metric,
ROUND((SUM(heap_blks_hit) / NULLIF(SUM(heap_blks_hit + heap_blks_read), 0) * 100)::numeric, 2) AS percentage
FROM pg_statio_user_tables;
\echo ''
\echo '8. Connection Pool Status:'
SELECT
COUNT(*) AS total_connections,
COUNT(*) FILTER (WHERE state = 'active') AS active,
COUNT(*) FILTER (WHERE state = 'idle') AS idle,
COUNT(*) FILTER (WHERE state = 'idle in transaction') AS idle_in_transaction,
MAX(EXTRACT(EPOCH FROM (NOW() - query_start))) AS longest_query_seconds
FROM pg_stat_activity
WHERE datname = current_database();
SQL
When:* User selects interactive
Purpose:* Guided query optimization session
\echo '=== Interactive Query Optimization Session ==='
\echo ''
\echo 'This will guide you through optimizing a slow query.'
\echo ''
# Prompt for query
read -p "Paste your slow query: " SLOW_QUERY
# Step 1: Current performance
psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 <<SQL
\echo ''
\echo 'Step 1: Current Performance Baseline'
\echo ''
\timing on
EXPLAIN (ANALYZE, BUFFERS)
$SLOW_QUERY;
\timing off
SQL
# Step 2: Analyze table statistics
psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 <<'SQL'
\echo ''
\echo 'Step 2: Table Statistics'
\echo ''
-- Extract table names from query (basic regex)
-- This is simplified - actual implementation would parse query
SELECT
schemaname,
tablename,
n_live_tup AS row_count,
seq_scan,
idx_scan,
n_tup_ins,
n_tup_upd,
n_tup_del,
last_vacuum,
last_analyze
FROM pg_stat_user_tables
WHERE schemaname = 'public'
ORDER BY n_live_tup DESC;
SQL
# Step 3: Suggest indexes
\echo ''
\echo 'Step 3: Index Suggestions'
\echo ''
\echo 'Based on your query, consider these indexes:'
\echo ''
\echo ' 1. Check WHERE clause columns - add index'
\echo ' 2. Check JOIN columns - add composite index'
\echo ' 3. Check ORDER BY columns - add index'
\echo ''
read -p "Would you like to see existing indexes? (y/n): " SHOW_INDEXES
if [[ "$SHOW_INDEXES" == "y" ]]; then
psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 <<'SQL'
SELECT
schemaname,
tablename,
indexname,
indexdef
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY tablename, indexname;
SQL
fi
# Step 4: Optimization recommendations
\echo ''
\echo 'Step 4: General Optimization Tips'
\echo ''
\echo ' ✓ Use EXPLAIN ANALYZE to understand execution'
\echo ' ✓ Add indexes on WHERE/JOIN/ORDER BY columns'
\echo ' ✓ Avoid SELECT * - specify only needed columns'
\echo ' ✓ Use LIMIT for large result sets'
\echo ' ✓ Consider materialized views for complex aggregations'
\echo ' ✓ Use connection pooling (Supabase Pooler)'
\echo ' ✓ Run VACUUM ANALYZE periodically'
\echo ''
read -p "Create index now? (y/n): " CREATE_INDEX
if [[ "$CREATE_INDEX" == "y" ]]; then
read -p "Enter index SQL: " INDEX_SQL
psql "$SUPABASE_DB_URL" -v ON_ERROR_STOP=1 <<SQL
$INDEX_SQL;
\echo 'Index created. Re-run EXPLAIN to see improvement.'
SQL
fi
=== Query Performance Analysis ===
Query:
SELECT u.*, COUNT(p.id) FROM users u LEFT JOIN posts p ON p.user_id = u.id GROUP BY u.id;
=== Execution Plan (EXPLAIN ANALYZE) ===
HashAggregate (cost=1234.56..1234.78 rows=22 width=520) (actual time=12.345..12.456 rows=22 loops=1)
-> Hash Left Join (cost=45.67..890.12 rows=34567 width=512) (actual time=2.345..10.123 rows=34567 loops=1)
Hash Cond: (p.user_id = u.id)
-> Seq Scan on posts p (cost=0.00..678.90 rows=34567 width=8) (actual time=0.012..5.678 rows=34567 loops=1)
-> Hash (cost=23.45..23.45 rows=22 width=504) (actual time=0.234..0.234 rows=22 loops=1)
-> Seq Scan on users u (cost=0.00..23.45 rows=22 width=504) (actual time=0.012..0.123 rows=22 loops=1)
Planning Time: 1.234 ms
Execution Time: 12.567 ms
=== Performance Hotpaths Analysis ===
1. Top 10 Slowest Queries:
query_preview | calls | total_seconds | avg_ms | percent_total
--------------------------------------------------+-------+---------------+--------+---------------
SELECT * FROM large_table WHERE complex_cond... | 1234 | 123.45 | 100.04 | 45.67
UPDATE users SET last_seen = NOW() WHERE... | 5678 | 67.89 | 11.95 | 25.12
... (additional output)
security-audit - Check for missing indexes on FKsverify-order {migration} - Validate index creation ordercreate-migration-plan - Plan index additionsexplain {query} - Legacy command (deprecated, use analyze-performance query)Prerequisites:*
pg_stat_statements extension enabled for hotpaths analysis:
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
Note:* This consolidated task replaces db-explain.md and db-analyze-hotpaths.md (deprecated in v3.0)