# 🔍 AUDIT DES TRANSACTIONS DB — PROJET VEZA **Date** : 2025-01-27 **Objectif** : Identifier toutes les opĂ©rations multi-Ă©tapes non transactionnelles qui peuvent laisser la DB dans un Ă©tat incohĂ©rent **Phase** : Hardening — Élimination des risques d'incohĂ©rence de donnĂ©es --- ## 📋 TABLE DES MATIÈRES 1. [RĂ©sumĂ© ExĂ©cutif](#1-rĂ©sumĂ©-exĂ©cutif) 2. [Backend Go](#2-backend-go) 3. [Stream Server (Rust)](#3-stream-server-rust) 4. [Chat Server (Rust)](#4-chat-server-rust) 5. [Table RĂ©capitulative](#5-table-rĂ©capitulative) 6. [Liste P0 Prioritaire](#6-liste-p0-prioritaire) --- ## 1. RÉSUMÉ EXÉCUTIF ### Statistiques Globales - **Total opĂ©rations multi-Ă©tapes identifiĂ©es** : 18 - **OpĂ©rations transactionnelles** : 8 (44%) - **OpĂ©rations non transactionnelles** : 10 (56%) - **P0 (Critique)** : 5 opĂ©rations - **P1 (Important)** : 5 opĂ©rations ### Risques Principaux 1. **Marketplace** : Commandes partiellement créées (items sans order, licenses sans order) 2. **Playlists** : Duplication incomplĂšte, collaborateurs sans playlist 3. **Social** : Compteurs de likes/comments dĂ©synchronisĂ©s 4. **Stream** : Segments orphelins sans job, jobs sans segments 5. **RBAC** : Assignations de rĂŽles partiellement appliquĂ©es --- ## 2. BACKEND GO ### 2.1 Marketplace Service #### ✅ **CreateOrder** — **TRANSACTIONNEL** **Localisation** : `internal/core/marketplace/service.go:136-215` **Flow actuel** : ```go s.db.Transaction(func(tx *gorm.DB) error { 1. Valider produits + calculer total 2. CREATE order (PENDING) 3. UPDATE order (COMPLETED) + PaymentIntent 4. CREATE order_items (pour chaque produit) 5. CREATE licenses (pour chaque track) }) ``` **État** : ✅ **Transactionnel** — Toutes les Ă©critures sont dans une transaction GORM **Risques** : Aucun — En cas d'erreur, rollback complet --- #### ✅ **CreateProduct** — **TRANSACTIONNEL** **Localisation** : `internal/core/marketplace/service.go:69-99` **Flow actuel** : ```go s.db.Transaction(func(tx *gorm.DB) error { 1. Valider track existence + ownership 2. CREATE product }) ``` **État** : ✅ **Transactionnel** — Validation + crĂ©ation dans une transaction **Risques** : Aucun --- ### 2.2 Playlist Services #### ✅ **AddTrack** — **TRANSACTIONNEL** **Localisation** : `internal/repositories/playlist_track_repository.go:41-124` **Flow actuel** : ```go r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 1. CREATE playlist_track 2. UPDATE playlists.track_count (+1) }) ``` **État** : ✅ **Transactionnel** — CrĂ©ation + mise Ă  jour du compteur dans une transaction **Risques** : Aucun --- #### ✅ **RemoveTrack** — **TRANSACTIONNEL** **Localisation** : `internal/repositories/playlist_track_repository.go:127-162` **Flow actuel** : ```go r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 1. DELETE playlist_track 2. UPDATE playlist_tracks.position (dĂ©calage) 3. UPDATE playlists.track_count (-1) }) ``` **État** : ✅ **Transactionnel** — Suppression + dĂ©calage positions + compteur dans une transaction **Risques** : Aucun --- #### ✅ **ReorderTracks** — **TRANSACTIONNEL** **Localisation** : `internal/repositories/playlist_track_repository.go:165-198` **Flow actuel** : ```go r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { 1. UPDATE playlist_tracks.position (pour chaque track) }) ``` **État** : ✅ **Transactionnel** — Toutes les mises Ă  jour de positions dans une transaction **Risques** : Aucun --- #### ❌ **DuplicatePlaylist** — **NON TRANSACTIONNEL** — **P0** **Localisation** : `internal/services/playlist_duplicate_service.go:41-131` **Flow actuel** : ```go 1. GET original playlist 2. CREATE new playlist (via CreatePlaylist) 3. FOR each track: 4. AddTrackToPlaylist (chaque appel est transactionnel, mais pas l'ensemble) ``` **État** : ❌ **NON Transactionnel** — La duplication complĂšte n'est pas dans une transaction **Risques concrets** : - Si crash aprĂšs crĂ©ation de la playlist mais avant fin de l'ajout des tracks → **Playlist vide créée** - Si crash au milieu de l'ajout des tracks → **Playlist partiellement dupliquĂ©e** (certains tracks manquants) - Si `AddTrackToPlaylist` Ă©choue pour un track, on continue avec les autres (ligne 117) → **Playlist incomplĂšte** **Impact mĂ©tier** : **ÉLEVÉ** — Playlists dupliquĂ©es incomplĂštes, confusion utilisateur **Recommandation** : Wrapper toute la duplication dans une transaction : ```go return s.playlistService.db.Transaction(func(tx *gorm.DB) error { // CrĂ©er playlist // Ajouter tous les tracks // Si erreur → rollback complet }) ``` --- #### ❌ **AddCollaborator** — **NON TRANSACTIONNEL** — **P1** **Localisation** : `internal/services/playlist_service.go:611-665` **Flow actuel** : ```go 1. GET playlist (vĂ©rification ownership) 2. GET user (vĂ©rification existence) 3. CREATE playlist_collaborator (via repository) ``` **État** : ❌ **NON Transactionnel** — VĂ©rifications + crĂ©ation sĂ©parĂ©es **Risques concrets** : - Si crash entre vĂ©rification et crĂ©ation → **Pas de collaborateur créé** (acceptable, mais incohĂ©rent si d'autres opĂ©rations dĂ©pendent) - Si playlist supprimĂ©e entre vĂ©rification et crĂ©ation → **Collaborateur créé pour playlist inexistante** (contrainte FK devrait bloquer, mais pas garanti) **Impact mĂ©tier** : **MOYEN** — Risque faible mais possible **Recommandation** : Wrapper dans une transaction si on veut garantir l'atomicitĂ© des vĂ©rifications + crĂ©ation --- ### 2.3 Social Services #### ❌ **ToggleLike** — **NON TRANSACTIONNEL** — **P1** **Localisation** : `internal/core/social/service.go:131-167` **Flow actuel** : ```go // Cas 1: Unlike 1. DELETE like 2. UPDATE post.like_count (-1) // ⚠ Pas dans la mĂȘme transaction // Cas 2: Like 1. CREATE like 2. UPDATE post.like_count (+1) // ⚠ Pas dans la mĂȘme transaction ``` **État** : ❌ **NON Transactionnel** — Create/Delete + Update compteur sĂ©parĂ©s **Risques concrets** : - Si crash aprĂšs DELETE like mais avant UPDATE compteur → **Like supprimĂ© mais compteur non dĂ©crĂ©mentĂ©** → **Compteur dĂ©synchronisĂ©** - Si crash aprĂšs CREATE like mais avant UPDATE compteur → **Like créé mais compteur non incrĂ©mentĂ©** → **Compteur dĂ©synchronisĂ©** **Impact mĂ©tier** : **MOYEN** — Compteurs dĂ©synchronisĂ©s, mais donnĂ©es principales (like) cohĂ©rentes **Recommandation** : Wrapper dans une transaction : ```go return s.db.Transaction(func(tx *gorm.DB) error { // DELETE ou CREATE like // UPDATE post.like_count }) ``` --- #### ❌ **AddComment** — **NON TRANSACTIONNEL** — **P1** **Localisation** : `internal/core/social/service.go:169-188` **Flow actuel** : ```go 1. CREATE comment 2. UPDATE post.comment_count (+1) // ⚠ Pas dans la mĂȘme transaction ``` **État** : ❌ **NON Transactionnel** — CrĂ©ation commentaire + mise Ă  jour compteur sĂ©parĂ©s **Risques concrets** : - Si crash aprĂšs CREATE comment mais avant UPDATE compteur → **Commentaire créé mais compteur non incrĂ©mentĂ©** → **Compteur dĂ©synchronisĂ©** **Impact mĂ©tier** : **MOYEN** — Compteurs dĂ©synchronisĂ©s, mais commentaire créé **Recommandation** : Wrapper dans une transaction --- ### 2.4 RBAC Services #### ❌ **AssignRoleToUser (RBACService)** — **NON TRANSACTIONNEL** — **P0** **Localisation** : `internal/services/rbac_service.go:168-210` **Flow actuel** : ```go 1. SELECT COUNT(*) FROM users WHERE id = $1 // VĂ©rification existence 2. SELECT COUNT(*) FROM roles WHERE id = $1 // VĂ©rification existence 3. SELECT COUNT(*) FROM user_roles WHERE ... // VĂ©rification doublon 4. INSERT INTO user_roles ... // Assignation ``` **État** : ❌ **NON Transactionnel** — 4 queries sĂ©parĂ©es, pas de transaction **Risques concrets** : - Si crash entre vĂ©rifications et INSERT → **Pas d'assignation créée** (acceptable) - Si user/role supprimĂ© entre vĂ©rification et INSERT → **Assignation créée pour user/role inexistant** (contrainte FK devrait bloquer, mais pas garanti si suppression soft) - Si race condition : 2 requĂȘtes simultanĂ©es peuvent toutes deux passer les vĂ©rifications et crĂ©er 2 assignations → **Doublon** (contrainte UNIQUE devrait bloquer, mais erreur non gĂ©rĂ©e proprement) **Impact mĂ©tier** : **ÉLEVÉ** — Assignations de rĂŽles incohĂ©rentes, sĂ©curitĂ© compromise **Recommandation** : Wrapper dans une transaction avec isolation level appropriĂ© : ```go return s.db.Transaction(func(tx *gorm.DB) error { // VĂ©rifications + INSERT dans la mĂȘme transaction }) ``` --- #### ❌ **AssignRoleToUser (RoleService)** — **NON TRANSACTIONNEL** — **P1** **Localisation** : `internal/services/role_service.go:86-99` **Flow actuel** : ```go 1. CREATE user_role ``` **État** : ❌ **NON Transactionnel** — Simple CREATE, mais devrait vĂ©rifier existence user/role avant **Risques concrets** : - Si user/role n'existe pas → **Erreur FK** (gĂ©rĂ©e par DB, mais pas de validation prĂ©alable) - Pas de vĂ©rification de doublon avant crĂ©ation **Impact mĂ©tier** : **MOYEN** — Erreurs DB non gĂ©rĂ©es proprement **Recommandation** : Ajouter vĂ©rifications + wrapper dans transaction --- ### 2.5 HLS Queue Service #### ✅ **CreateJob** — **TRANSACTIONNEL** **Localisation** : `internal/services/hls_queue_service.go:77` **Flow actuel** : ```go s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { // CrĂ©ation job + initialisation }) ``` **État** : ✅ **Transactionnel** **Risques** : Aucun --- ### 2.6 Refresh Token Service #### ✅ **RotateToken** — **TRANSACTIONNEL** **Localisation** : `internal/services/refresh_token_service.go:70` **Flow actuel** : ```go s.db.Transaction(func(tx *gorm.DB) error { // Invalider ancien token + crĂ©er nouveau }) ``` **État** : ✅ **Transactionnel** **Risques** : Aucun --- ## 3. STREAM SERVER (RUST) ### 3.1 Segment Tracker #### ❌ **persist_segment** — **NON TRANSACTIONNEL** — **P0** **Localisation** : `src/core/processing/segment_tracker.rs:82-106` **Flow actuel** : ```rust async fn persist_segment(&self, segment: &SegmentInfo) -> Result<(), AppError> { 1. INSERT INTO stream_segments (...) // Insert segment 2. self.update_current_duration().await?; // UPDATE stream_jobs.updated_at } ``` **État** : ❌ **NON Transactionnel** — INSERT segment + UPDATE job sĂ©parĂ©s **Risques concrets** : - Si crash aprĂšs INSERT segment mais avant UPDATE job → **Segment créé mais job non mis Ă  jour** → **Segments orphelins** - Si crash aprĂšs UPDATE job mais avant INSERT segment → **Job mis Ă  jour mais segment non créé** → **IncohĂ©rence durĂ©e** **Impact mĂ©tier** : **ÉLEVÉ** — Segments HLS orphelins, jobs avec mĂ©tadonnĂ©es incorrectes, streaming cassĂ© **Recommandation** : Utiliser une transaction SQLx : ```rust let mut tx = self.db.begin().await?; sqlx::query!("INSERT INTO stream_segments ...").execute(&mut *tx).await?; sqlx::query!("UPDATE stream_jobs ...").execute(&mut *tx).await?; tx.commit().await?; ``` --- #### ❌ **EncodingPool (insert_segments_from_playlist)** — **NON TRANSACTIONNEL** — **P1** **Localisation** : `src/core/encoding_pool.rs:300-349` **Flow actuel** : ```rust for line in lines { if segment_path.exists() { sqlx::query!("INSERT INTO stream_segments ...") .execute(&self.db_pool) // ⚠ Pas de transaction .await?; segment_index += 1; } } ``` **État** : ❌ **NON Transactionnel** — Insertions de segments multiples sans transaction **Risques concrets** : - Si crash au milieu de la boucle → **Segments partiellement insĂ©rĂ©s** → **Playlist HLS incomplĂšte** **Impact mĂ©tier** : **MOYEN** — Playlist HLS partiellement gĂ©nĂ©rĂ©e **Recommandation** : Wrapper toutes les insertions dans une transaction : ```rust let mut tx = self.db_pool.begin().await?; for segment in segments { sqlx::query!("INSERT ...").execute(&mut *tx).await?; } tx.commit().await?; ``` --- ### 3.2 Stream Jobs #### ❌ **Job Creation + Segment Persistence** — **NON TRANSACTIONNEL** — **P0** **Localisation** : `src/core/processing/processor.rs` + `segment_tracker.rs` **Flow actuel** : ```rust // Dans processor.rs 1. CREATE stream_job (status: processing) 2. Spawn FFmpeg 3. Segments dĂ©tectĂ©s → persist_segment() (appelĂ© plusieurs fois) 4. UPDATE stream_job (status: completed) ``` **État** : ❌ **NON Transactionnel** — Job créé, puis segments persistĂ©s individuellement, puis job mis Ă  jour **Risques concrets** : - Si crash aprĂšs crĂ©ation job mais avant segments → **Job créé sans segments** → **Job orphelin** - Si crash pendant persistance segments → **Segments partiellement créés** → **Job incomplet** - Si crash aprĂšs segments mais avant UPDATE job → **Segments créés mais job non finalisĂ©** → **Job bloquĂ© en "processing"** **Impact mĂ©tier** : **ÉLEVÉ** — Jobs de transcodage incomplets, streaming cassĂ© **Recommandation** : - **Option 1** : Persister segments en batch Ă  la fin (dĂ©jĂ  fait dans `persist_all()`, mais pas utilisĂ© systĂ©matiquement) - **Option 2** : Utiliser un pattern "two-phase" : job créé en "pending", segments persistĂ©s en batch, puis job finalisĂ© en "completed" dans une transaction --- ## 4. CHAT SERVER (RUST) ### 4.1 Message Operations #### ✅ **send_room_message** — **TRANSACTIONNEL** **Localisation** : `src/hub/channels.rs:301-388` **Flow actuel** : ```rust let mut tx = hub.db.begin().await?; 1. VĂ©rifier membership 2. INSERT INTO messages 3. UPDATE messages.thread_count (si parent) 4. process_mentions() (INSERT mentions) tx.commit().await?; ``` **État** : ✅ **Transactionnel** — Toutes les Ă©critures dans une transaction SQLx **Risques** : Aucun --- #### ✅ **send_dm_message** — **TRANSACTIONNEL** **Localisation** : `src/hub/direct_messages.rs:278-336` **Flow actuel** : ```rust let mut tx = hub.db.begin().await?; 1. INSERT INTO messages 2. UPDATE messages.thread_count (si parent) 3. process_dm_mentions() 4. UPDATE dm_conversations.updated_at tx.commit().await?; ``` **État** : ✅ **Transactionnel** — Toutes les Ă©critures dans une transaction **Risques** : Aucun --- #### ✅ **process_batch (OptimizedPersistence)** — **TRANSACTIONNEL** **Localisation** : `src/optimized_persistence.rs:663-699` **Flow actuel** : ```rust let mut tx = self.pg_pool.begin().await?; for message in &messages { sqlx::query("INSERT INTO messages ...").execute(&mut *tx).await?; } tx.commit().await?; ``` **État** : ✅ **Transactionnel** — Toutes les insertions en batch dans une transaction **Risques** : Aucun --- ## 5. TABLE RÉCAPITULATIVE | Service | OpĂ©ration | État | PrioritĂ© | Risque en cas de crash | |---------|-----------|------|----------|------------------------| | **Backend Go** | | Marketplace | `CreateOrder` | ✅ Transactionnel | - | Aucun | | Marketplace | `CreateProduct` | ✅ Transactionnel | - | Aucun | | Playlist | `AddTrack` | ✅ Transactionnel | - | Aucun | | Playlist | `RemoveTrack` | ✅ Transactionnel | - | Aucun | | Playlist | `ReorderTracks` | ✅ Transactionnel | - | Aucun | | Playlist | `DuplicatePlaylist` | ❌ **NON** | **P0** | Playlist vide ou incomplĂšte | | Playlist | `AddCollaborator` | ❌ **NON** | P1 | Collaborateur sans playlist | | Social | `ToggleLike` | ❌ **NON** | P1 | Compteur dĂ©synchronisĂ© | | Social | `AddComment` | ❌ **NON** | P1 | Compteur dĂ©synchronisĂ© | | RBAC | `AssignRoleToUser` (RBACService) | ❌ **NON** | **P0** | Assignation incohĂ©rente | | RBAC | `AssignRoleToUser` (RoleService) | ❌ **NON** | P1 | Erreurs non gĂ©rĂ©es | | HLS | `CreateJob` | ✅ Transactionnel | - | Aucun | | Auth | `RotateToken` | ✅ Transactionnel | - | Aucun | | **Stream Server** | | SegmentTracker | `persist_segment` | ❌ **NON** | **P0** | Segments orphelins | | EncodingPool | `insert_segments_from_playlist` | ❌ **NON** | P1 | Playlist HLS incomplĂšte | | Processor | Job + Segments | ❌ **NON** | **P0** | Jobs incomplets | | **Chat Server** | | Channels | `send_room_message` | ✅ Transactionnel | - | Aucun | | DirectMessages | `send_dm_message` | ✅ Transactionnel | - | Aucun | | Persistence | `process_batch` | ✅ Transactionnel | - | Aucun | --- ## 6. LISTE P0 PRIORITAIRE ### 🔮 P0 — Must-Fix avant dĂ©ploiement 1. **`PlaylistDuplicateService.DuplicatePlaylist`** (Backend Go) - **Risque** : Playlists dupliquĂ©es incomplĂštes - **Impact** : Confusion utilisateur, donnĂ©es corrompues - **Fix** : Wrapper crĂ©ation playlist + ajout tracks dans une transaction 2. **`RBACService.AssignRoleToUser`** (Backend Go) - **Risque** : Assignations de rĂŽles incohĂ©rentes, sĂ©curitĂ© compromise - **Impact** : Permissions incorrectes, accĂšs non autorisĂ©s - **Fix** : Wrapper toutes les vĂ©rifications + INSERT dans une transaction 3. **`SegmentTracker.persist_segment`** (Stream Server) - **Risque** : Segments HLS orphelins, jobs avec mĂ©tadonnĂ©es incorrectes - **Impact** : Streaming cassĂ©, playlists HLS incomplĂštes - **Fix** : Utiliser transaction SQLx pour INSERT segment + UPDATE job 4. **`StreamProcessor` (Job + Segments)** (Stream Server) - **Risque** : Jobs de transcodage incomplets, segments partiellement créés - **Impact** : Streaming cassĂ©, jobs bloquĂ©s - **Fix** : Pattern "two-phase" ou persistance batch Ă  la fin 5. **`SocialService.ToggleLike` / `AddComment`** (Backend Go) — **P0 si compteurs critiques** - **Risque** : Compteurs dĂ©synchronisĂ©s - **Impact** : MĂ©triques incorrectes (si critiques pour business) - **Fix** : Wrapper dans transaction --- ## 7. RECOMMANDATIONS GÉNÉRALES ### Pattern Transactionnel Standard (Backend Go) CrĂ©er un helper dans `internal/database/` ou utiliser directement GORM : ```go // Pattern recommandĂ© func (s *Service) OperationMultiSteps(ctx context.Context, ...) error { return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { // 1. Validations // 2. Écritures multiples // 3. Retour erreur si problĂšme → rollback automatique return nil }) } ``` ### Pattern Transactionnel Standard (Rust - SQLx) ```rust // Pattern recommandĂ© async fn operation_multi_steps(&self, ...) -> Result<()> { let mut tx = self.db.begin().await?; sqlx::query!("INSERT ...").execute(&mut *tx).await?; sqlx::query!("UPDATE ...").execute(&mut *tx).await?; tx.commit().await?; Ok(()) } ``` ### RĂšgles de Gestion d'Erreur 1. **Toute erreur dans la transaction → rollback automatique** 2. **Wrapper des erreurs avec contexte** : `fmt.Errorf("OperationName: %w", err)` 3. **Pas d'Ă©critures "post-transaction"** qui pourraient rĂ©introduire de l'incohĂ©rence 4. **Logs structurĂ©s au niveau transaction**, pas dans chaque sous-Ă©tape --- ## 8. PROCHAINES ÉTAPES 1. ✅ **Phase 1 : Audit** — **COMPLÉTÉ** (ce document) 2. ⏳ **Phase 2 : Design** — CrĂ©er `docs/DB_TRANSACTION_PLAN.md` avec plan d'implĂ©mentation 3. ⏳ **Phase 3 : ImplĂ©mentation** — Corriger les P0 identifiĂ©s 4. ⏳ **Phase 4 : Tests** — Tests ciblĂ©s pour vĂ©rifier rollback en cas d'erreur 5. ⏳ **Phase 5 : Documentation** — Mettre Ă  jour `TRIAGE.md` et `AUDIT_STABILITY.md` --- **Date de crĂ©ation** : 2025-01-27 **DerniĂšre mise Ă  jour** : 2025-01-27 **Statut** : ✅ Audit complet — En attente feu vert pour Phase 2 (Design)