From 30399b45597af99db77675b473be9c00c645820d Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Thu, 27 Aug 2026 15:39:24 -0400 Subject: [PATCH] =?UTF-8?q?[FIX]=20imbrication=20:=20le=20co=C3=BBt=20d'un?= =?UTF-8?q?=20vCPU=20d=C3=A9pend=20de=20la=20profondeur=20de=20l'=C3=A9tag?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amorçage du quatrième étage, mesuré sur deux descentes complètes : 1 664 s à 2 vCPU, 15 608 s à 3. Un seul vCPU de plus, ×9,4. Aux étages 2 et 3 le même vCPU ne coûte RIEN — ssh en 37 s et 93 s, comme à deux. Le « gel » observé à 8 et 12 vCPU n'est probablement pas autre chose que cette courbe poussée assez loin : 1 664 × 9,4 par vCPU dépasse vite toute patience, et un RIP immobile à cinq minutes d'intervalle ne s'en distingue pas. D'où un SEUIL de profondeur au lieu d'une largeur uniforme. Le troisième vCPU reste aux étages 2 et 3, où il est gratuit et où il enlève le surengagement qui affamait l'installation de l'étage 4 — celle-ci ne finissait pas avec un parent à 2 vCPU, elle progresse avec un parent à 3. À partir du quatrième étage, le strict minimum. La combinaison ainsi obtenue — parent 3, enfant 2 — n'a jamais été mesurée : les deux essais étaient (2, 2) et (3, 3). --- EN --- Fourth-level boot, measured on two full descents: 1,664 s at 2 vCPU, 15,608 s at 3. One more vCPU, ×9.4. At levels 2 and 3 that same vCPU costs NOTHING — ssh in 37 s and 93 s, same as at two. The "freeze" seen at 8 and 12 vCPU is most likely nothing but this curve taken far enough: 1,664 × 9.4 per vCPU quickly exceeds any patience, and a static RIP five minutes apart is indistinguishable from it. Hence a depth THRESHOLD instead of a uniform width. The third vCPU stays at levels 2 and 3, where it is free and where it removes the overcommit that starved level 4's install — which never finished with a 2-vCPU parent and does progress with a 3-vCPU one. From the fourth level down, the strict minimum. The resulting combination — parent 3, child 2 — has never been measured: the two attempts were (2, 2) and (3, 3). Assisted-by: claude-opus-5 (cherry picked from commit 9a5583a4b9461a087d161775764c560c82e0609e) --- script/proxmox/nesting.py | 36 ++++++++++++++++++++++++--------- test/test_proxmox_nesting.py | 39 ++++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/script/proxmox/nesting.py b/script/proxmox/nesting.py index f73296c..3acd18c 100644 --- a/script/proxmox/nesting.py +++ b/script/proxmox/nesting.py @@ -101,14 +101,25 @@ DISQUE_MIN_GO = 15 # Le plus profond : deux, le seul chiffre dont on ait la preuve qu'il démarre # au quatrième étage. VCPU_IMBRIQUE = 2 -# Les étages imbriqués intermédiaires : un de plus, pour héberger leur enfant +# Les étages imbriqués PEU PROFONDS : un de plus, pour héberger leur enfant # sans être aussi étroits que lui. Deux hébergeant deux, c'est cent pour cent # de surengagement — et l'installation de l'étage 4 dépassait alors 2 h 50 # contre 793 s pour l'étage 3. # -# Trois est une HYPOTHÈSE : on a la preuve que deux démarre au quatrième étage -# et que huit gèle, rien entre les deux. C'est la descente qui tranchera. +# « Peu profonds », et c'est la mesure qui l'impose. Ce troisième vCPU ne coûte +# RIEN aux étages 2 et 3 — leur ssh répond en 37 s et 93 s, comme à deux vCPU — +# et il coûte tout au quatrième : 15 608 s, soit 4 h 20, contre 1 664 s à deux +# vCPU. Un seul vCPU de plus, l'amorçage multiplié par 9,4. VCPU_INTERMEDIAIRE = 3 +# Le premier étage qui doit rester au strict minimum. +# +# Amorçage du quatrième étage, mesuré : 1 664 s à 2 vCPU, 15 608 s à 3, jamais +# à 8 ni à 12 — même RIP à cinq minutes d'intervalle. Le « gel » observé à 8 et +# 12 n'est probablement pas autre chose que cette courbe poussée assez loin : +# 1 664 × 9,4 par vCPU supplémentaire dépasse vite toute patience. +# +# Aux étages 2 et 3, la même largeur ne coûte rien. Le seuil est donc là. +SEUIL_ETROIT = 4 # Le premier étage tourne sur le MÉTAL : aucun risque de gel, et son amorçage # est rapide — onze vCPU y ont démarré en 42 s. Il n'a pourtant qu'un enfant à # trois vCPU à servir ; quatre suffisent, et laissent la machine physique aux @@ -178,8 +189,9 @@ def nesting_plan( étages tienne : la cible du bas, plus un surcoût par étage au-dessus. Le processeur n'en fait PAS partie de la même façon : il ne croît - pas avec la profondeur, puisque tout étage imbriqué reste étroit. Le - premier étage demande VCPU_METAL, quelle que soit la profondeur. + pas avec la profondeur — il DÉCROÎT, et se stabilise à + VCPU_IMBRIQUE dès SEUIL_ETROIT. Le premier étage demande VCPU_METAL, + quelle que soit la profondeur. """ return ( PVE_RAM_CIBLE_MO + (d - 1) * PVE_RAM_MO, @@ -212,15 +224,21 @@ def nesting_plan( niveaux = [ { "niveau": niveau, - # Trois largeurs, et aucune ne dépend de la profondeur : le - # métal en premier, le fond au plus étroit, les intermédiaires - # juste assez larges pour héberger leur enfant. + # Le métal peut être large ; un étage imbriqué peu profond + # gagne son troisième vCPU pour héberger son enfant sans + # surengagement ; à partir de SEUIL_ETROIT, le strict minimum, + # parce que là ce troisième vCPU multiplie l'amorçage par 9,4. + # + # L'étage juste AU-DESSUS du seuil garde donc trois quand son + # enfant en a deux : c'est le seul endroit de la descente où le + # surengagement disparaît, et c'est celui qui compte, puisque + # l'étage 4 est le premier dont l'installation s'effondrait. "vcpu": ( VCPU_METAL if niveau == 1 else ( VCPU_IMBRIQUE - if niveau == atteignable + if niveau >= SEUIL_ETROIT else VCPU_INTERMEDIAIRE ) ), diff --git a/test/test_proxmox_nesting.py b/test/test_proxmox_nesting.py index 36db064..d03079d 100644 --- a/test/test_proxmox_nesting.py +++ b/test/test_proxmox_nesting.py @@ -83,14 +83,41 @@ class TestLePlanDesEtages(unittest.TestCase): ) def test_the_three_widths_are_where_they_belong(self): + """Le coût d'un vCPU dépend de la PROFONDEUR de l'étage, pas d'une + largeur absolue. + + Mesuré : le troisième vCPU ne coûte rien aux étages 2 et 3 — ssh en + 37 s et 93 s, comme à deux vCPU — et coûte 4 h 20 au quatrième, contre + 1 664 s à deux. Un seul vCPU de plus, l'amorçage ×9,4.""" niveaux = nesting.nesting_plan(6, **self.HOTE)["niveaux"] + largeurs = {n["niveau"]: n["vcpu"] for n in niveaux} # Le métal : aucun risque de gel, onze vCPU y ont démarré en 42 s. - self.assertEqual(niveaux[0]["vcpu"], nesting.VCPU_METAL) - # Le fond : le seul chiffre dont on ait la preuve qu'il démarre au - # quatrième étage. - self.assertEqual(niveaux[-1]["vcpu"], nesting.VCPU_IMBRIQUE) - for n in niveaux[1:-1]: - self.assertEqual(n["vcpu"], nesting.VCPU_INTERMEDIAIRE) + self.assertEqual(largeurs[1], nesting.VCPU_METAL) + # Peu profonds : le troisième vCPU est gratuit, et il enlève le + # surengagement là où l'installation s'effondrait. + for niveau in range(2, nesting.SEUIL_ETROIT): + self.assertEqual( + largeurs[niveau], nesting.VCPU_INTERMEDIAIRE, f"étage {niveau}" + ) + # À partir du seuil : le strict minimum, sans exception. + for niveau in range(nesting.SEUIL_ETROIT, 7): + self.assertEqual( + largeurs[niveau], nesting.VCPU_IMBRIQUE, f"étage {niveau}" + ) + + def test_the_level_above_the_threshold_keeps_its_headroom(self): + """C'est le seul endroit de la descente où le surengagement disparaît, + et c'est celui qui compte : l'étage 4 est le premier dont + l'installation s'effondrait, faute d'un parent plus large que lui. + + Les deux combinaisons mesurées étaient (parent 2, enfant 2) — démarre + en 1 664 s puis l'installation ne finit pas — et (parent 3, enfant 3) — + démarre en 15 608 s. Celle-ci est (parent 3, enfant 2).""" + niveaux = nesting.nesting_plan(6, **self.HOTE)["niveaux"] + largeurs = {n["niveau"]: n["vcpu"] for n in niveaux} + parent = largeurs[nesting.SEUIL_ETROIT - 1] + enfant = largeurs[nesting.SEUIL_ETROIT] + self.assertGreater(parent, enfant) def test_a_single_level_descent_runs_on_metal(self): niveaux = nesting.nesting_plan(1, **self.HOTE)["niveaux"]