fix(backend): decoupe l'insertion des recommandations en lots et remet les docs a jour
Backend / Tests exigeant une base (push) Failing after 34s
Backend / Lint, typage et tests (push) Successful in 1m24s
Backend / Audit des dépendances (push) Successful in 57s
SonarQube / build-back (push) Successful in 1m5s
SonarQube / build-front (push) Successful in 9m39s
SonarQube / test-back (push) Failing after 51s
SonarQube / test-front (push) Failing after 5m6s
SonarQube / SonarQube (push) Skipped
Backend / Tests exigeant une base (push) Failing after 34s
Backend / Lint, typage et tests (push) Successful in 1m24s
Backend / Audit des dépendances (push) Successful in 57s
SonarQube / build-back (push) Successful in 1m5s
SonarQube / build-front (push) Successful in 9m39s
SonarQube / test-back (push) Failing after 51s
SonarQube / test-front (push) Failing after 5m6s
SonarQube / SonarQube (push) Skipped
`create_missing()` construisait un seul `INSERT ... VALUES` pour la totalite des propositions. Avec quatre colonnes par ligne et le plafond asyncpg de 32 767 parametres, la route echouait au-dela de 8 191 recommandations par appel, cas devenu realiste maintenant que la detection interne (#104) alimente `alert` en continu. L'insertion passe par des lots de `TAILLE_DE_LOT` lignes, sur le patron de `app/etl/historical_import.py`. L'ADR 0006, `20-backend.md` et la description de la PR annoncaient qu'aucune source n'alimentait `alert` et que #104 n'etait pas commencee. #104 est livree sur `dev` depuis la #113 : les phrases sont corrigees plutot que laissees a vieillir dans un ADR.
This commit is contained in:
@@ -16,6 +16,9 @@ class NouvelleRecommandation:
|
||||
rule_reference: str
|
||||
|
||||
|
||||
TAILLE_DE_LOT = 1000
|
||||
|
||||
|
||||
class RecommendationRepository:
|
||||
def __init__(self, session: AsyncSession) -> None:
|
||||
self._session = session
|
||||
@@ -34,14 +37,15 @@ class RecommendationRepository:
|
||||
# Pourquoi : l'idempotence est déléguée à `uq_recommendation_alert_rule` plutôt qu'à une
|
||||
# lecture préalable, qui laisserait une fenêtre entre le contrôle et l'insertion.
|
||||
async def create_missing(self, nouvelles: Sequence[NouvelleRecommandation]) -> int:
|
||||
if not nouvelles:
|
||||
return 0
|
||||
|
||||
requete = (
|
||||
insert(Recommendation)
|
||||
.values([asdict(nouvelle) for nouvelle in nouvelles])
|
||||
.on_conflict_do_nothing(constraint="uq_recommendation_alert_rule")
|
||||
.returning(Recommendation.recommendation_id)
|
||||
)
|
||||
creees = (await self._session.scalars(requete)).all()
|
||||
return len(creees)
|
||||
creees = 0
|
||||
# Piège : asyncpg plafonne une requête à 32 767 paramètres, soit 8 191 lignes de quatre
|
||||
# colonnes. Au-delà de ce seuil un `INSERT` d'un seul tenant échouerait.
|
||||
for debut in range(0, len(nouvelles), TAILLE_DE_LOT):
|
||||
requete = (
|
||||
insert(Recommendation)
|
||||
.values([asdict(nouvelle) for nouvelle in nouvelles[debut : debut + TAILLE_DE_LOT]])
|
||||
.on_conflict_do_nothing(constraint="uq_recommendation_alert_rule")
|
||||
.returning(Recommendation.recommendation_id)
|
||||
)
|
||||
creees += len((await self._session.scalars(requete)).all())
|
||||
return creees
|
||||
|
||||
Reference in New Issue
Block a user