# 🔍 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)

