Ce qui se passe
L'optimiseur de Postgres choisit un plan à partir de statistiques et d'un modèle de coût. Sur les requêtes à jointures nombreuses, il se trompe régulièrement — et le problème est structurel : l'ordonnancement des jointures est NP-difficile, et Leis et al. ont posé la question en 2015 puis de nouveau dix ans plus tard pour conclure que rien n'avait fondamentalement changé.
Rohan Bansal a entraîné un modèle de 4 milliards de paramètres à produire de meilleurs plans, et a publié le code. C'est un billet de blog, pas un article relu par des pairs.
Pourquoi ça marche
L'intuition tient en une phrase : produire un bon plan est difficile, vérifier qu'un plan est bon ne l'est pas. Il n'y a qu'un axe — le temps d'exécution de la requête — donc une récompense vérifiable, donc un terrain naturel pour l'apprentissage par renforcement.
Le modèle ne réécrit pas l'optimiseur : il produit des hints pour l'extension
pg_hint_plan, qui se posent en
commentaire au-dessus de la requête.
/*+ HashJoin(a b) SeqScan(a) */
SELECT * FROM pgbench_accounts a JOIN pgbench_branches b ON a.bid = b.bid
ORDER BY a.aid;Le montage
- Modèle de base :
empero-ai/Qwen3.8-4B-Distill, une distillation de Qwen 3.5 4B par le petit laboratoire allemand Empero, avec Qwen 3.8 2.4T comme professeur. 4,66 milliards de poids, 9,32 Go enbf16. - Phase 1, SFT : 100 trajectoires de GPT-6 Astra (sur 120 générées,
20 en validation), rendues au format Qwen avec la bibliothèque
renderersde Prime Intellect, dépliées en 382 lignes d'entraînement, une seule époque, taille de lot de 1, viaprime-rl. - Phase 2, RL agentique : 1 200 mises à jour, variante maison de GRPO pour scorer dans un environnement bruité, vLLM et l'entraîneur sur un nœud loué 2× H100, et quatre conteneurs Postgres sur le bureau de l'auteur.
- Le LoRA entraîné : 42,5 Mo, 21,2 millions de paramètres.
Le banc de mesure mérite d'être noté : chaque candidat est exécuté en trois paires (candidat, défaut) entrelacées, les médianes comparées, et un écart de moins de 5 % compte comme une égalité. C'est ce qui empêche le bruit du cache de pages Linux de fabriquer des gains.
Les résultats
Sur JOB — 113 requêtes à jointures nombreuses sur le jeu de données IMDb — avec le point de contrôle final, trois rollouts par requête :
| Sélection | Gains | Régressions | Speedup géo. |
|---|---|---|---|
| Choix propre du modèle, par trajectoire | 119 | 7 | 1,40× |
| Meilleur retour dans chaque trajectoire | 129 | 1 | 1,44× |
| Meilleur retour sur les trois | 68 | 0 | 1,81× |
La dernière ligne — une réponse par requête, choisie parmi 15 candidats — donne 1,81× d'accélération géométrique moyenne, 1,81× sur le workload total par coïncidence, et −44,7 % de latence cumulée.
Le point de départ vaut d'être rappelé : avant entraînement, le modèle 4B ne produisait aucun plan valide pour 99 des 113 requêtes.
Ce que le modèle a appris
Sur 1 347 actions : 1 141 hints de scan, 917 arbres Leading — ceux qui
pilotent l'ordre des jointures — et 572 hints Parallel. Les corrections
Rows ne sortent que 146 fois. Il force régulièrement les boucles imbriquées
plutôt que les jointures de hachage, préfère les index aux scans séquentiels, et
utilise volontiers enable_sort=off et random_page_cost=1.1.
Sur job-01d (90× de gain), son raisonnement note qu'un scan séquentiel
applique un filtre coûteux sur 575 000 lignes et force un parcours par bitmap.
Le coût
1 200 $ au total : ~800 $ pour un nœud 2× H100 SXM loué chez Lambda pendant ~95 heures, et ~400 $ d'API OpenAI pour générer les démonstrations Astra.
À retenir
Le cadre d'usage annoncé est précis : des charges analytiques où la même requête tourne des milliers de fois. Entraîner coûte des dizaines à des centaines d'exécutions en amont, mais le coût s'amortit.
Trois réserves, à garder ensemble : ce n'est pas relu par des pairs, les
poids ne sont pas publiés — seulement le code, sur
polyphilz/qorl — et JOB est un banc
d'essai académique, pas votre charge de production.