Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert guide for diagnosing and fixing Odoo performance issues: slow queries, worker configuration, memory limits, PostgreSQL tuning, and profiling tools.
.claude/skills/odoo-performance-tuner/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-02 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✓ | ▲ Improved | — | — |
| case-21 | ✓→✓ | = Same ✓ | — | — |
| case-20 | ✓→✓ | = Same ✓ | — | — |
This skill helps diagnose and resolve Odoo performance problems — from slow page loads and database bottlenecks to worker misconfiguration and memory bloat. It covers PostgreSQL query tuning, Odoo worker settings, and built-in profiling tools.
MemoryError or Worker timeout errors in logs.odoo.conf for a specific server spec.@odoo-performance-tuner and describe your performance issue.ini# odoo.conf — tuned for a 4-core, 8GB RAM server workers = 9 # (CPU_cores × 2) + 1 — never set to 0 in production max_cron_threads = 2 # background cron jobs; keep ≤ 2 to preserve user-facing capacity limit_memory_soft = 1610612736 # 1.5 GB — worker is recycled gracefully after this limit_memory_hard = 2147483648 # 2.0 GB — worker is killed immediately; prevents OOM crashes limit_time_cpu = 600 # max CPU seconds per request limit_time_real = 1200 # max wall-clock seconds per request limit_request = 8192 # max requests before worker recycles (prevents memory leaks)
sql-- Step 1: Enable pg_stat_statements extension (run once as postgres superuser) CREATE EXTENSION IF NOT EXISTS pg_stat_statements; -- Step 2: Also add to postgresql.conf and reload: -- shared_preload_libraries = 'pg_stat_statements' -- log_min_duration_statement = 1000 -- log queries taking > 1 second -- Step 3: Find the top 10 slowest average queries SELECT LEFT(query, 100) AS query_snippet, round(mean_exec_time::numeric, 2) AS avg_ms, calls, round(total_exec_time::numeric, 2) AS total_ms FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10; -- Step 4: Check for missing indexes causing full table scans SELECT schemaname, tablename, attname, n_distinct, correlation FROM pg_stats WHERE tablename = 'sale_order_line' AND correlation < 0.5 -- low correlation = poor index efficiency ORDER BY n_distinct DESC;
textPrerequisites: Run Odoo with ?debug=1 in the URL to enable debug mode. Menu: Settings → Technical → Profiling Steps: 1. Click "Enable Profiling" — set a duration (e.g., 60 seconds) 2. Navigate to and reproduce the slow action 3. Return to Settings → Technical → Profiling → View Results What to look for: - Total SQL queries > 100 on a single page → N+1 query problem - Single queries taking > 100ms → missing DB index - Same query repeated many times → missing cache, use @ormcache - Python time high but SQL low → compute field inefficiency
mapped(), filtered(), and sorted() on in-memory recordsets — they don't trigger additional SQL.partner_id, state, date_order).@tools.ormcache decorator on methods pulled repeatedly with the same arguments.workers = 0 in production — single-threaded mode serializes all requests and blocks all users on any slow operation.limit_memory_soft — workers exceeding it are recycled between requests; without the limit they grow unbounded and crash.prefetch_ids on recordsets — rely on Odoo's automatic batch prefetching, which activates by default.shared_buffers, work_mem, effective_cache_size) is highly server-specific and not covered in depth here — use PGTune as a starting baseline.odoo.conf access — some tuning options are unavailable.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.