Aller au contenu

PD-43 — Rétrospective

1. Contexte

Champ Valeur
Story ID PD-43
Titre Service OVH S3 Multipart Upload
Domaine storage
Projet infra
Date complétion 2025-12-29
Verdict ACCEPTÉ

2. Métriques

Métrique Valeur
Durée totale ~20h (3 jours)
Fichiers modifiés 39 (30 production, 9 tests)
Coverage 80.5% branches, 98.45% statements
Écarts bloquants corrigés 2 (E-01 streaming, E-02 intégrité)

3. Learnings clés

  • Pattern streaming-first avec Transform streams : Concevoir dès le départ avec Transform streams évite des refactorisations coûteuses. stream.pipe(transform) garantit mémoire constante quelle que soit la taille.

  • Double vérification d'intégrité sans overhead : Calcul parallèle MD5 et SHA3-256 dans le même pipeline streaming n'ajoute pas de latence (traitement en transit).

  • Synchronisation explicite des événements asynchrones : Les événements finish/end des streams doivent être gérés via Promise pour éviter race conditions dans calculs asynchrones.

  • Importance de la couverture des chemins d'erreur : Les chemins d'erreur souvent oubliés mais critiques. Utiliser jest --coverage --coverageReporters=text pour identifier branches non couvertes.

  • Spécification avec invariants numérotés facilite l'acceptabilité : INV-1 à INV-11 permettent vérification systématique et traçabilité claire des écarts.

4. Patterns applicables

Nouveau pattern : Streaming pipeline avec double hash

// Pipeline streaming sans bufferisation
const hashTransform = this.hashService.createHashTransform();  // SHA3-256
const md5Transform = createMd5Transform();                      // MD5 pour S3

const pipeline = stream
  .pipe(hashTransform)
  .pipe(md5Transform);

const s3Result = await this.s3Adapter.uploadPart({
  body: pipeline,  // Direct streaming vers S3
  contentLength: undefined,  // Chunked transfer
});

// Attendre fin explicite des transforms
await Promise.all([
  new Promise(resolve => hashTransform.on('finish', resolve)),
  new Promise(resolve => md5Transform.on('finish', resolve)),
]);

const sha3Hash = hashTransform.getHash();
const md5Hash = md5Transform.getHash();

Nouveau pattern : Transform stream avec Promise sync

// Éviter race condition sur calcul async
const md5Promise = new Promise<void>((resolve) => {
  md5Transform.on('finish', resolve);
});

const response = await this.s3Client.send(command);

// Attendre explicitement AVANT vérification
await md5Promise;

if (md5Digest && s3Etag !== md5Digest) {
  throw new Error('Integrity check failed');
}

5. Signal CLAUDE.md

Priorité haute : Pattern streaming Transform avec Promise sync.

### Node.js — Transform streams et race conditions (2026-02-XX)

**PIÈGE** : Les événements `finish`/`end` des Transform streams sont asynchrones.

**Symptôme** : Le hash calculé dans `flush()` n'est pas disponible immédiatement après `await send()`.

**Solution** : Promise explicite sur l'événement `finish`.

```typescript
const hashPromise = new Promise<void>((resolve) => {
  transform.on('finish', resolve);
});

const response = await client.send(command);

await hashPromise;  // Attendre AVANT utilisation

const hash = transform.getHash();  // Maintenant disponible

Applicable à : tout calcul dans callback flush() d'un Transform stream. ```

6. Conclusion

PD-43 a livré le service multipart upload OVH S3 avec streaming sans bufferisation (INV-2) et double vérification d'intégrité MD5+SHA3-256 (INV-10). Les 2 écarts bloquants initiaux (E-01 bufferisation, E-02 intégrité stub) ont été corrigés grâce aux invariants numérotés. Le pattern streaming pipeline avec Promise sync est réutilisable pour tout traitement de flux volumineux.


Rétrospective générée 2026-02-19 (Étape 10 batch storage)