Added three new integration test scenarios: - Test 08: Config Hot-Reload (dynamic configuration updates) - Test 09: Module Dependencies (dependency injection & cascade reload) - Test 10: Multi-Version Coexistence (canary deployment & progressive migration) Fixes: - Fixed CTest working directory for all tests (add WORKING_DIRECTORY) - Fixed module paths to use relative paths (./ prefix) - Fixed IModule.h comments for clarity New test modules: - ConfigurableModule (for config reload testing) - BaseModule, DependentModule, IndependentModule (for dependency testing) - GameLogicModuleV1/V2/V3 (for multi-version testing) Test coverage now includes 10 comprehensive integration scenarios covering hot-reload, chaos testing, stress testing, race conditions, memory leaks, error recovery, limits, config reload, dependencies, and multi-versioning. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
857 lines
26 KiB
Markdown
857 lines
26 KiB
Markdown
# Scénario 10: Multi-Version Module Coexistence
|
|
|
|
**Priorité**: ⭐⭐ HIGH
|
|
**Phase**: 3 (ADVANCED)
|
|
**Durée estimée**: ~2-3 minutes
|
|
**Effort implémentation**: ~6-8 heures
|
|
|
|
---
|
|
|
|
## 🎯 Objectif
|
|
|
|
Valider que le système peut gérer plusieurs versions d'un même module chargées simultanément en production, avec:
|
|
- Chargement de multiples versions (v1, v2, v3) du même module en parallèle
|
|
- Canary deployment (routage progressif du traffic vers nouvelle version)
|
|
- Migration progressive du state entre versions
|
|
- Rollback instantané en cas de problème
|
|
- Isolation mémoire complète entre versions (pas de corruption croisée)
|
|
- Garbage collection automatique des versions inutilisées
|
|
|
|
---
|
|
|
|
## 📋 Description
|
|
|
|
### Cas d'Usage Réel
|
|
|
|
Dans un jeu en production, ce scénario permet de:
|
|
- **Zero-downtime deployments**: Déployer nouveau système de combat sans redémarrer serveurs
|
|
- **Canary releases**: Tester optimisation sur 10% des joueurs avant rollout complet
|
|
- **A/B testing**: Comparer performance v1 vs v2 en production avec métriques
|
|
- **Instant rollback**: Revenir à version stable en < 1s si bugs critiques
|
|
- **Blue/Green deployment**: Basculer instantanément entre versions
|
|
|
|
### Architecture des Modules de Test
|
|
|
|
**GameLogicModule v1**: Version baseline
|
|
- Logic simple: position update (x += vx * dt, y += vy * dt)
|
|
- 100 entités actives
|
|
- Pas de collision detection
|
|
- Version = 1
|
|
|
|
**GameLogicModule v2**: Version avec nouvelle feature
|
|
- Logic v1 + collision detection (nouvelle feature)
|
|
- Compatibilité state avec v1
|
|
- Version = 2
|
|
|
|
**GameLogicModule v3**: Version optimisée
|
|
- Physics system optimisé (SIMD, spatial hashing)
|
|
- Collision detection améliorée
|
|
- Version = 3
|
|
|
|
### Setup Initial (Phase 0)
|
|
**Durée**: 10 secondes
|
|
|
|
1. Charger **GameLogicModule v1**
|
|
- 100 entités actives (position, velocity)
|
|
- State baseline enregistré
|
|
2. Vérifier stabilité:
|
|
- ✅ v1 traite 100% des entités
|
|
- ✅ Aucune erreur
|
|
- ✅ Performance baseline établie
|
|
|
|
### Phase 1: Canary Deployment v2
|
|
**Durée**: 30 secondes
|
|
|
|
**Objectif**: Tester v2 sur 10% du traffic (canary)
|
|
|
|
1. À t=10s, charger **GameLogicModule v2** en parallèle de v1
|
|
2. Configurer traffic split:
|
|
- 90% → v1 (90 entités)
|
|
- 10% → v2 (10 entités, canary)
|
|
3. Vérifier isolation:
|
|
- ✅ v1 traite exactement 90 entités
|
|
- ✅ v2 traite exactement 10 entités
|
|
- ✅ Total = 100 entités (aucune perdue)
|
|
- ✅ State complètement isolé (pas de corruption)
|
|
- ✅ Canary accuracy: 10% ±2%
|
|
|
|
**Métriques**:
|
|
- `version_load_time_ms`: Temps de chargement de v2 (< 200ms)
|
|
- `canary_accuracy`: Précision du routage (10% ±2%)
|
|
- `isolation_check`: Aucune corruption mémoire croisée
|
|
|
|
### Phase 2: Progressive Migration v1 → v2
|
|
**Durée**: 40 secondes
|
|
|
|
**Objectif**: Migration graduelle du traffic vers v2
|
|
|
|
1. Migration progressive du traffic:
|
|
- t=50s: **30% v2, 70% v1** (30 entités → v2)
|
|
- t=60s: **50% v2, 50% v1** (50 entités → v2)
|
|
- t=70s: **80% v2, 20% v1** (80 entités → v2)
|
|
- t=80s: **100% v2, 0% v1** (migration complète)
|
|
|
|
2. Vérifier migration:
|
|
- ✅ State migré progressivement (pas de perte)
|
|
- ✅ Chaque étape: total = 100 entités
|
|
- ✅ Performance stable durant migration
|
|
- ✅ Aucune corruption de données
|
|
|
|
**Métriques**:
|
|
- `state_migration_time_ms`: Temps pour migrer state v1→v2 (< 500ms)
|
|
- `migration_data_loss`: Aucune entité perdue (0)
|
|
- `traffic_accuracy`: Précision du routage à chaque étape (±2%)
|
|
|
|
### Phase 3: Garbage Collection v1
|
|
**Durée**: 10 secondes
|
|
|
|
**Objectif**: Décharger automatiquement v1 (plus utilisée)
|
|
|
|
1. À t=90s, déclencher auto GC:
|
|
- v1 inutilisée depuis 10s (0% traffic)
|
|
- Seuil GC atteint
|
|
2. Vérifier déchargement:
|
|
- ✅ v1 déchargée automatiquement
|
|
- ✅ v2 continue sans interruption (100 entités)
|
|
- ✅ Mémoire libérée (> 95% de la mémoire v1)
|
|
|
|
**Métriques**:
|
|
- `gc_trigger_time_s`: Temps avant GC après inutilisation (10s)
|
|
- `gc_efficiency`: % mémoire libérée (> 95%)
|
|
- `gc_time_ms`: Temps pour GC complète (< 100ms)
|
|
|
|
### Phase 4: Emergency Rollback v2 → v1
|
|
**Durée**: 20 secondes
|
|
|
|
**Objectif**: Rollback instantané si v2 plante
|
|
|
|
1. À t=100s, simuler bug critique dans v2:
|
|
- Exception dans collision detection
|
|
- Ou: performance dégradée (lag)
|
|
- Ou: corruption de state
|
|
2. Déclencher rollback automatique:
|
|
- Recharger v1 en urgence
|
|
- Restaurer state depuis backup
|
|
3. Vérifier rollback:
|
|
- ✅ v1 rechargée en < 200ms
|
|
- ✅ State restauré depuis backup (aucune perte)
|
|
- ✅ Service continuity (downtime < 1s)
|
|
- ✅ 100 entités actives
|
|
|
|
**Métriques**:
|
|
- `rollback_time_ms`: Temps total de rollback (< 200ms)
|
|
- `rollback_downtime_ms`: Temps sans service (< 1000ms)
|
|
- `state_recovery`: State restauré complètement (100%)
|
|
|
|
### Phase 5: Three-Way Coexistence (v1, v2, v3)
|
|
**Durée**: 30 secondes
|
|
|
|
**Objectif**: 3 versions en parallèle (A/B/C testing)
|
|
|
|
1. À t=120s, charger v3 (version optimisée)
|
|
2. Configurer traffic split à 3 voies:
|
|
- 20% → v1 (20 entités, baseline)
|
|
- 30% → v2 (30 entités, feature test)
|
|
- 50% → v3 (50 entités, optimized test)
|
|
3. Vérifier coexistence:
|
|
- ✅ 3 versions actives simultanément
|
|
- ✅ Total = 100 entités (aucune perdue)
|
|
- ✅ Isolation mémoire stricte (3 heaps séparés)
|
|
- ✅ Routage correct du traffic (±2%)
|
|
- ✅ Métriques séparées par version
|
|
|
|
**Métriques**:
|
|
- `multi_version_count`: Nombre de versions actives (3)
|
|
- `traffic_split_accuracy`: Précision du routage 3-voies (±2%)
|
|
- `memory_isolation`: Aucune corruption croisée (100%)
|
|
- `performance_comparison`: Métriques v1 vs v2 vs v3
|
|
|
|
---
|
|
|
|
## 🏗️ Implémentation
|
|
|
|
### Extension de IModule.h
|
|
|
|
```cpp
|
|
// include/grove/IModule.h
|
|
class IModule {
|
|
public:
|
|
// Méthodes existantes
|
|
virtual void initialize(std::shared_ptr<IDataNode> config) = 0;
|
|
virtual void process(float deltaTime) = 0;
|
|
virtual std::shared_ptr<IDataNode> getState() const = 0;
|
|
virtual void setState(std::shared_ptr<IDataNode> state) = 0;
|
|
virtual bool isIdle() const = 0;
|
|
|
|
// Scenario 8: Config hot-reload
|
|
virtual bool updateConfig(std::shared_ptr<IDataNode> newConfig) {
|
|
return false;
|
|
}
|
|
|
|
// Scenario 9: Dependencies
|
|
virtual std::vector<std::string> getDependencies() const {
|
|
return {};
|
|
}
|
|
|
|
// Scenario 9-10: Version tracking
|
|
virtual int getVersion() const {
|
|
return 1;
|
|
}
|
|
|
|
// NOUVELLE: Scenario 10 - State migration
|
|
virtual bool migrateStateFrom(int fromVersion,
|
|
std::shared_ptr<IDataNode> oldState) {
|
|
// Default: simple copy if same version
|
|
if (fromVersion == getVersion()) {
|
|
setState(oldState);
|
|
return true;
|
|
}
|
|
return false; // Override for custom migration
|
|
}
|
|
|
|
virtual ~IModule() = default;
|
|
};
|
|
```
|
|
|
|
### GameLogicModule v1
|
|
|
|
```cpp
|
|
// tests/modules/GameLogicModuleV1.h
|
|
#pragma once
|
|
#include "grove/IModule.h"
|
|
#include <vector>
|
|
#include <memory>
|
|
|
|
struct Entity {
|
|
float x, y; // Position
|
|
float vx, vy; // Velocity
|
|
int id;
|
|
};
|
|
|
|
class GameLogicModuleV1 : public IModule {
|
|
public:
|
|
void initialize(std::shared_ptr<IDataNode> config) override;
|
|
void process(float deltaTime) override;
|
|
std::shared_ptr<IDataNode> getState() const override;
|
|
void setState(std::shared_ptr<IDataNode> state) override;
|
|
bool isIdle() const override { return true; }
|
|
|
|
int getVersion() const override { return 1; }
|
|
|
|
// v1: Simple movement only
|
|
void updateEntities(float dt) {
|
|
for (auto& e : entities_) {
|
|
e.x += e.vx * dt;
|
|
e.y += e.vy * dt;
|
|
}
|
|
}
|
|
|
|
size_t getEntityCount() const { return entities_.size(); }
|
|
|
|
private:
|
|
std::vector<Entity> entities_;
|
|
int processCount_ = 0;
|
|
};
|
|
|
|
extern "C" IModule* createModule() {
|
|
return new GameLogicModuleV1();
|
|
}
|
|
|
|
extern "C" void destroyModule(IModule* module) {
|
|
delete module;
|
|
}
|
|
```
|
|
|
|
### GameLogicModule v2
|
|
|
|
```cpp
|
|
// tests/modules/GameLogicModuleV2.h
|
|
#pragma once
|
|
#include "grove/IModule.h"
|
|
#include <vector>
|
|
#include <memory>
|
|
|
|
struct Entity {
|
|
float x, y;
|
|
float vx, vy;
|
|
int id;
|
|
bool collided; // NEW in v2
|
|
};
|
|
|
|
class GameLogicModuleV2 : public IModule {
|
|
public:
|
|
void initialize(std::shared_ptr<IDataNode> config) override;
|
|
void process(float deltaTime) override;
|
|
std::shared_ptr<IDataNode> getState() const override;
|
|
void setState(std::shared_ptr<IDataNode> state) override;
|
|
bool isIdle() const override { return true; }
|
|
|
|
int getVersion() const override { return 2; }
|
|
|
|
// v2: Movement + collision detection
|
|
void updateEntities(float dt) {
|
|
for (auto& e : entities_) {
|
|
e.x += e.vx * dt;
|
|
e.y += e.vy * dt;
|
|
checkCollisions(e); // NEW feature in v2
|
|
}
|
|
}
|
|
|
|
// State migration from v1
|
|
bool migrateStateFrom(int fromVersion,
|
|
std::shared_ptr<IDataNode> oldState) override {
|
|
if (fromVersion == 1) {
|
|
// Migrate v1 → v2: add collided flag
|
|
setState(oldState);
|
|
for (auto& e : entities_) {
|
|
e.collided = false; // Initialize new field
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t getEntityCount() const { return entities_.size(); }
|
|
|
|
private:
|
|
std::vector<Entity> entities_;
|
|
int processCount_ = 0;
|
|
|
|
void checkCollisions(Entity& e) {
|
|
// Simple collision detection
|
|
e.collided = (e.x < 0 || e.x > 1000 || e.y < 0 || e.y > 1000);
|
|
}
|
|
};
|
|
|
|
extern "C" IModule* createModule() {
|
|
return new GameLogicModuleV2();
|
|
}
|
|
|
|
extern "C" void destroyModule(IModule* module) {
|
|
delete module;
|
|
}
|
|
```
|
|
|
|
### GameLogicModule v3
|
|
|
|
```cpp
|
|
// tests/modules/GameLogicModuleV3.h
|
|
#pragma once
|
|
#include "grove/IModule.h"
|
|
#include <vector>
|
|
#include <memory>
|
|
|
|
struct Entity {
|
|
float x, y;
|
|
float vx, vy;
|
|
int id;
|
|
bool collided;
|
|
float mass; // NEW in v3 for advanced physics
|
|
};
|
|
|
|
class GameLogicModuleV3 : public IModule {
|
|
public:
|
|
void initialize(std::shared_ptr<IDataNode> config) override;
|
|
void process(float deltaTime) override;
|
|
std::shared_ptr<IDataNode> getState() const override;
|
|
void setState(std::shared_ptr<IDataNode> state) override;
|
|
bool isIdle() const override { return true; }
|
|
|
|
int getVersion() const override { return 3; }
|
|
|
|
// v3: Optimized physics + advanced collision
|
|
void updateEntities(float dt) {
|
|
for (auto& e : entities_) {
|
|
applyPhysics(e, dt); // OPTIMIZED in v3
|
|
checkCollisions(e);
|
|
}
|
|
}
|
|
|
|
// State migration from v2
|
|
bool migrateStateFrom(int fromVersion,
|
|
std::shared_ptr<IDataNode> oldState) override {
|
|
if (fromVersion == 2 || fromVersion == 1) {
|
|
setState(oldState);
|
|
for (auto& e : entities_) {
|
|
e.mass = 1.0f; // Initialize new field
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t getEntityCount() const { return entities_.size(); }
|
|
|
|
private:
|
|
std::vector<Entity> entities_;
|
|
int processCount_ = 0;
|
|
|
|
void applyPhysics(Entity& e, float dt) {
|
|
// Advanced physics (SIMD optimized in real impl)
|
|
float ax = 0.0f, ay = 9.8f; // Gravity
|
|
e.vx += ax * dt;
|
|
e.vy += ay * dt;
|
|
e.x += e.vx * dt;
|
|
e.y += e.vy * dt;
|
|
}
|
|
|
|
void checkCollisions(Entity& e) {
|
|
e.collided = (e.x < 0 || e.x > 1000 || e.y < 0 || e.y > 1000);
|
|
}
|
|
};
|
|
|
|
extern "C" IModule* createModule() {
|
|
return new GameLogicModuleV3();
|
|
}
|
|
|
|
extern "C" void destroyModule(IModule* module) {
|
|
delete module;
|
|
}
|
|
```
|
|
|
|
### MultiVersionEngine API
|
|
|
|
Extension du système pour gérer plusieurs versions simultanément:
|
|
|
|
```cpp
|
|
// include/grove/MultiVersionEngine.h
|
|
#pragma once
|
|
#include <string>
|
|
#include <map>
|
|
#include <memory>
|
|
|
|
class MultiVersionEngine {
|
|
public:
|
|
// Load specific version of a module
|
|
bool loadModuleVersion(const std::string& moduleName,
|
|
int version,
|
|
const std::string& libraryPath);
|
|
|
|
// Unload specific version
|
|
bool unloadModuleVersion(const std::string& moduleName, int version);
|
|
|
|
// Traffic routing (canary deployment)
|
|
// Example: setTrafficSplit("GameLogic", {{1, 0.9}, {2, 0.1}})
|
|
// → 90% traffic to v1, 10% to v2
|
|
bool setTrafficSplit(const std::string& moduleName,
|
|
const std::map<int, float>& versionWeights);
|
|
|
|
// Get current traffic split
|
|
std::map<int, float> getTrafficSplit(const std::string& moduleName) const;
|
|
|
|
// State migration between versions
|
|
bool migrateState(const std::string& moduleName,
|
|
int fromVersion,
|
|
int toVersion);
|
|
|
|
// Emergency rollback to specific version
|
|
bool rollback(const std::string& moduleName,
|
|
int targetVersion,
|
|
float maxDowntimeMs = 1000.0f);
|
|
|
|
// Automatic garbage collection of unused versions
|
|
void enableAutoGC(const std::string& moduleName,
|
|
float unusedThresholdSeconds = 60.0f);
|
|
|
|
void disableAutoGC(const std::string& moduleName);
|
|
|
|
// Get all loaded versions for a module
|
|
std::vector<int> getLoadedVersions(const std::string& moduleName) const;
|
|
|
|
// Check if version is loaded
|
|
bool isVersionLoaded(const std::string& moduleName, int version) const;
|
|
|
|
// Get metrics for specific version
|
|
struct VersionMetrics {
|
|
int version;
|
|
float trafficPercent;
|
|
size_t processedEntities;
|
|
float avgProcessTimeMs;
|
|
size_t memoryUsageBytes;
|
|
float uptimeSeconds;
|
|
};
|
|
|
|
VersionMetrics getVersionMetrics(const std::string& moduleName,
|
|
int version) const;
|
|
};
|
|
```
|
|
|
|
### Traffic Router Implementation
|
|
|
|
```cpp
|
|
// src/MultiVersionEngine.cpp (excerpt)
|
|
class TrafficRouter {
|
|
public:
|
|
// Distribute entities across versions based on weights
|
|
std::map<int, std::vector<Entity>> routeTraffic(
|
|
const std::vector<Entity>& entities,
|
|
const std::map<int, float>& weights) {
|
|
|
|
std::map<int, std::vector<Entity>> routed;
|
|
|
|
// Validate weights sum to 1.0
|
|
float sum = 0.0f;
|
|
for (auto& [v, w] : weights) sum += w;
|
|
assert(std::abs(sum - 1.0f) < 0.01f);
|
|
|
|
// Deterministic routing based on entity ID
|
|
for (const auto& e : entities) {
|
|
float hash = (e.id % 100) / 100.0f; // [0, 1)
|
|
float cumulative = 0.0f;
|
|
|
|
for (auto& [version, weight] : weights) {
|
|
cumulative += weight;
|
|
if (hash < cumulative) {
|
|
routed[version].push_back(e);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return routed;
|
|
}
|
|
};
|
|
```
|
|
|
|
### State Migration Helper
|
|
|
|
```cpp
|
|
// Helper for progressive state migration
|
|
class StateMigrator {
|
|
public:
|
|
// Migrate state progressively from v1 to v2
|
|
bool migrate(IModule* fromModule, IModule* toModule) {
|
|
// Extract state from old version
|
|
auto oldState = fromModule->getState();
|
|
|
|
// Attempt migration
|
|
bool success = toModule->migrateStateFrom(
|
|
fromModule->getVersion(),
|
|
oldState
|
|
);
|
|
|
|
if (success) {
|
|
recordMigration(fromModule->getVersion(),
|
|
toModule->getVersion());
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
private:
|
|
void recordMigration(int from, int to) {
|
|
migrationHistory_.push_back({from, to, std::chrono::steady_clock::now()});
|
|
}
|
|
|
|
struct MigrationRecord {
|
|
int fromVersion;
|
|
int toVersion;
|
|
std::chrono::steady_clock::time_point timestamp;
|
|
};
|
|
|
|
std::vector<MigrationRecord> migrationHistory_;
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Métriques Collectées
|
|
|
|
| Métrique | Description | Seuil |
|
|
|----------|-------------|-------|
|
|
| **version_load_time_ms** | Temps de chargement d'une nouvelle version | < 200ms |
|
|
| **canary_accuracy** | Précision du routage canary (10% = 10.0% ±2%) | ±2% |
|
|
| **traffic_split_accuracy** | Précision du routage multi-version | ±2% |
|
|
| **state_migration_time_ms** | Temps de migration de state entre versions | < 500ms |
|
|
| **rollback_time_ms** | Temps de rollback complet | < 200ms |
|
|
| **rollback_downtime_ms** | Downtime durant rollback | < 1000ms |
|
|
| **memory_isolation** | Isolation mémoire entre versions (corruption) | 100% |
|
|
| **gc_trigger_time_s** | Temps avant GC après inutilisation | 10s |
|
|
| **gc_efficiency** | % mémoire libérée après GC | > 95% |
|
|
| **multi_version_count** | Nombre max de versions simultanées | 3 |
|
|
| **total_entities** | Total entités (doit rester constant) | 100 |
|
|
|
|
---
|
|
|
|
## ✅ Critères de Succès
|
|
|
|
### MUST PASS
|
|
1. ✅ Chargement de 3 versions du même module simultanément
|
|
2. ✅ Traffic routing précis (±2% de la cible) pour canary et 3-way split
|
|
3. ✅ Migration de state progressive sans perte de données
|
|
4. ✅ Rollback en < 200ms avec state restauré
|
|
5. ✅ Isolation mémoire complète (aucune corruption croisée)
|
|
6. ✅ GC automatique des versions inutilisées après seuil
|
|
7. ✅ Total entités constant (100) durant toutes les phases
|
|
8. ✅ Aucun crash durant tout le test
|
|
|
|
### NICE TO HAVE
|
|
1. ✅ Hot-patch d'une version sans full reload (micro-update)
|
|
2. ✅ A/B testing metrics (compare perf v1 vs v2 vs v3)
|
|
3. ✅ Version pinning (certaines entités forcées sur version spécifique)
|
|
4. ✅ Circuit breaker (auto-rollback si error rate > 5%)
|
|
5. ✅ Canary promotion automatique si metrics OK après 30s
|
|
|
|
---
|
|
|
|
## 🔧 API Nécessaires dans DebugEngine
|
|
|
|
### Nouvelles méthodes
|
|
|
|
```cpp
|
|
class DebugEngine {
|
|
public:
|
|
// Méthodes existantes...
|
|
|
|
// NOUVELLES pour Scenario 10
|
|
bool loadModuleVersion(const std::string& moduleName,
|
|
int version,
|
|
const std::string& path);
|
|
|
|
bool unloadModuleVersion(const std::string& moduleName, int version);
|
|
|
|
bool setTrafficSplit(const std::string& moduleName,
|
|
const std::map<int, float>& weights);
|
|
|
|
std::map<int, float> getTrafficSplit(const std::string& moduleName);
|
|
|
|
bool migrateState(const std::string& moduleName,
|
|
int fromVersion,
|
|
int toVersion);
|
|
|
|
bool rollback(const std::string& moduleName, int targetVersion);
|
|
|
|
void enableAutoGC(const std::string& moduleName, float thresholdSec);
|
|
|
|
std::vector<int> getLoadedVersions(const std::string& moduleName);
|
|
|
|
MultiVersionEngine::VersionMetrics getVersionMetrics(
|
|
const std::string& moduleName,
|
|
int version);
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 🐛 Cas d'Erreur Attendus
|
|
|
|
| Erreur | Cause | Action |
|
|
|--------|-------|--------|
|
|
| Traffic routing imprécis (15% au lieu de 10%) | Mauvais algorithme de distribution | FAIL - fix router |
|
|
| Entités perdues (95 au lieu de 100) | Migration incomplète | FAIL - fix migration |
|
|
| Corruption mémoire entre versions | Heap partagé | FAIL - isoler complètement |
|
|
| Rollback > 1s | Rechargement lent | FAIL - optimiser load |
|
|
| v1 pas GC après 60s inutilisée | Auto GC non implémenté | FAIL - implémenter GC |
|
|
| Crash lors de 3-way split | Race condition | FAIL - fix synchronisation |
|
|
| State migration v1→v2 perd données | Migration partielle | FAIL - vérifier migration |
|
|
|
|
---
|
|
|
|
## 📝 Output Attendu
|
|
|
|
```
|
|
================================================================================
|
|
TEST: Multi-Version Module Coexistence
|
|
================================================================================
|
|
|
|
=== Phase 0: Setup Baseline (10s) ===
|
|
✓ GameLogicModule v1 loaded
|
|
✓ 100 entities initialized
|
|
✓ Baseline performance: 0.5ms/frame
|
|
✓ Baseline memory: 8.2MB
|
|
|
|
=== Phase 1: Canary Deployment v2 (30s) ===
|
|
Loading GameLogicModule v2...
|
|
✓ v2 loaded in 145ms
|
|
Configuring canary: 10% v2, 90% v1
|
|
|
|
Traffic distribution:
|
|
v1: 90 entities (90.0%) ✓
|
|
v2: 10 entities (10.0%) ✓
|
|
Total: 100 entities ✓
|
|
|
|
✓ Canary accuracy: 10.0% (±2%)
|
|
✓ Memory isolation: 100% (no corruption)
|
|
✓ Both versions stable
|
|
|
|
Metrics:
|
|
Version load time: 145ms ✓ (< 200ms)
|
|
Canary accuracy: 10.0% ✓ (±2%)
|
|
Memory isolation: 100% ✓
|
|
|
|
=== Phase 2: Progressive Migration v1 → v2 (40s) ===
|
|
t=50s: Traffic split → 30% v2, 70% v1
|
|
v1: 70 entities, v2: 30 entities ✓
|
|
|
|
t=60s: Traffic split → 50% v2, 50% v1
|
|
v1: 50 entities, v2: 50 entities ✓
|
|
|
|
t=70s: Traffic split → 80% v2, 20% v1
|
|
v1: 20 entities, v2: 80 entities ✓
|
|
|
|
t=80s: Traffic split → 100% v2, 0% v1 (migration complete)
|
|
v1: 0 entities, v2: 100 entities ✓
|
|
|
|
✓ State migrated progressively (no data loss)
|
|
✓ Total entities constant: 100
|
|
✓ Performance stable
|
|
|
|
Metrics:
|
|
State migration time: 385ms ✓ (< 500ms)
|
|
Data loss: 0% ✓
|
|
Migration steps: 4 ✓
|
|
|
|
=== Phase 3: Garbage Collection v1 (10s) ===
|
|
v1 unused for 10s → triggering auto GC...
|
|
✓ v1 unloaded automatically
|
|
✓ v2 continues without interruption (100 entities)
|
|
✓ Memory freed: 7.8MB (95.1%)
|
|
|
|
Metrics:
|
|
GC trigger time: 10.0s ✓
|
|
GC efficiency: 95.1% ✓ (> 95%)
|
|
GC time: 78ms ✓ (< 100ms)
|
|
|
|
=== Phase 4: Emergency Rollback v2 → v1 (20s) ===
|
|
Simulating critical bug in v2 (collision detection crash)...
|
|
⚠️ v2 error detected → triggering emergency rollback
|
|
|
|
Rollback sequence:
|
|
1. State backup captured
|
|
2. Reloading v1...
|
|
3. State restored from backup
|
|
4. Traffic redirected to v1
|
|
|
|
✓ v1 reloaded in 178ms
|
|
✓ State restored (100 entities)
|
|
✓ Downtime: 892ms (< 1s)
|
|
✓ Service continuity maintained
|
|
|
|
Metrics:
|
|
Rollback time: 178ms ✓ (< 200ms)
|
|
Rollback downtime: 892ms ✓ (< 1000ms)
|
|
State recovery: 100% ✓
|
|
|
|
=== Phase 5: Three-Way Coexistence (v1, v2, v3) (30s) ===
|
|
Loading GameLogicModule v3 (optimized)...
|
|
✓ v3 loaded in 152ms
|
|
|
|
Configuring 3-way traffic split: 20% v1, 30% v2, 50% v3
|
|
|
|
Traffic distribution:
|
|
v1: 20 entities (20.0%) ✓
|
|
v2: 30 entities (30.0%) ✓
|
|
v3: 50 entities (50.0%) ✓
|
|
Total: 100 entities ✓
|
|
|
|
✓ 3 versions coexisting
|
|
✓ Memory isolation: 100%
|
|
✓ Traffic routing accurate (±2%)
|
|
|
|
Performance comparison:
|
|
v1: 0.50ms/frame (baseline)
|
|
v2: 0.68ms/frame (+36% due to collision)
|
|
v3: 0.42ms/frame (-16% optimized!)
|
|
|
|
Metrics:
|
|
Multi-version count: 3 ✓
|
|
Traffic accuracy: ±1.5% ✓ (< ±2%)
|
|
Memory isolation: 100% ✓
|
|
Total entities: 100 ✓
|
|
|
|
================================================================================
|
|
METRICS SUMMARY
|
|
================================================================================
|
|
Version load time: 152ms (threshold: < 200ms) ✓
|
|
Canary accuracy: 10.0% (target: 10% ±2%) ✓
|
|
Traffic split accuracy: ±1.5% (threshold: ±2%) ✓
|
|
State migration time: 385ms (threshold: < 500ms) ✓
|
|
Rollback time: 178ms (threshold: < 200ms) ✓
|
|
Rollback downtime: 892ms (threshold: < 1000ms) ✓
|
|
Memory isolation: 100% (threshold: 100%) ✓
|
|
GC efficiency: 95.1% (threshold: > 95%) ✓
|
|
Multi-version count: 3 (target: 3) ✓
|
|
Total entities: 100 (constant) ✓
|
|
|
|
================================================================================
|
|
ASSERTIONS
|
|
================================================================================
|
|
✓ multi_version_loading (3 versions loaded)
|
|
✓ canary_deployment_accurate
|
|
✓ progressive_migration_no_loss
|
|
✓ rollback_fast_and_safe
|
|
✓ memory_isolation_complete
|
|
✓ auto_gc_triggered
|
|
✓ three_way_coexistence
|
|
✓ traffic_routing_precise
|
|
✓ total_entities_constant
|
|
✓ no_crashes
|
|
|
|
Result: ✅ PASSED
|
|
|
|
================================================================================
|
|
```
|
|
|
|
---
|
|
|
|
## 📅 Planning
|
|
|
|
**Jour 13 (3h):**
|
|
- Étendre IModule avec `migrateStateFrom()`
|
|
- Implémenter GameLogicModuleV1, V2, V3
|
|
- Créer Entity struct et logic de base
|
|
|
|
**Jour 14 (3h):**
|
|
- Implémenter MultiVersionEngine class
|
|
- Implémenter TrafficRouter (canary, multi-way split)
|
|
- Implémenter StateMigrator
|
|
|
|
**Jour 15 (2h):**
|
|
- Implémenter Auto GC system
|
|
- Implémenter Emergency Rollback
|
|
- Ajouter métriques par version
|
|
|
|
**Jour 16 (2h):**
|
|
- Implémenter test_10_multiversion_coexistence.cpp
|
|
- Debug + validation
|
|
- Intégration CMake
|
|
|
|
---
|
|
|
|
## 🎯 Valeur Ajoutée
|
|
|
|
Ce test valide une fonctionnalité **critique pour la production** :
|
|
|
|
### Zero-Downtime Deployments
|
|
- Déployer nouveau système de combat sans arrêter serveurs
|
|
- Migration progressive pour minimiser risques
|
|
- Rollback instantané si problème
|
|
|
|
### Canary Releases
|
|
- Tester nouvelle version sur 5-10% des joueurs d'abord
|
|
- Comparer métriques (perf, bugs) avant rollout complet
|
|
- Réduire blast radius si bugs critiques
|
|
|
|
### A/B Testing en Production
|
|
- Comparer performance v1 vs v2 vs v3 avec vraies données
|
|
- Décisions data-driven pour optimisations
|
|
- Validation de nouvelles features avant déploiement global
|
|
|
|
### Business Value
|
|
- **Réduction des downtimes** : 0s au lieu de 5-10min par déploiement
|
|
- **Réduction des risques** : Canary détecte bugs avant rollout complet
|
|
- **Time-to-market** : Déploiements plus fréquents et plus sûrs
|
|
- **Player experience** : Aucune interruption de service
|
|
|
|
### Exemple Réel
|
|
Dans un MMO avec 100k joueurs connectés:
|
|
1. Deploy nouveau système PvP sur 5% (5k joueurs)
|
|
2. Monitor crash rate, lag, feedback
|
|
3. Si OK → gradual rollout 10% → 30% → 100%
|
|
4. Si bug critique → instant rollback en < 1s
|
|
5. Zero downtime, minimal risk
|
|
|
|
---
|
|
|
|
**Prochaine étape**: Implémentation
|