Documentation Migration MC1 vers MC2
Présentation générale
Objectif
Cette procédure migre le système de gestion des assets média (DAM - Digital Asset Management) depuis une architecture MediaCloud 1 (MC1) vers MediaCloud 2 (MC2). Cette migration transforme un stockage principalement local (SAN) vers une architecture cloud native basée sur AWS S3 avec traitement asynchrone et ingestion automatique.
Points d'entrée
Ecran admin:
Classe principale :
MigrateProcess(war/src/main/java/com/wedia/dam/mediacloud/api/migrationmc2/MigrateProcess.java)Contrôleur :
MigrateController(war/src/main/java/com/wedia/dam/mediacloud/api/migrationmc2/MigrateController.java)
Pré-requis
Moteur en 2026.2.1 au minimum
Configuration MC2 effectuée sur tous les noeuds du cluster
Configuration de ImagingServer pour faire des preview en 10.000x10.000
Analyse des presets existants
Analyse du code applicatif
Architecture et flux de données
Composants impliqués
1. Base de données
Rôle : Stockage des métadonnées des assets et structure des objets
Lecture :
Structure des objets (
CTObjectStructure)Instances d'objets avec propriétés
fileouimageConfiguration EVP (vidéos) via
OPV2Database
Écriture (Phase 3) :
Modification du type de champ
file/image→blobMise à jour de la structure persistante
2. Stockage SAN (Storage Area Network)
Rôle : Stockage local historique des fichiers sources et cache
Lecture :
Fichiers sources originaux (
file/objecttype/filename)Cache de variations pré-calculées (thumbnails, presets)
Fichiers temporaires de workcopy
Écriture : Aucune pendant la migration
Suppression (Phase 5) : Nettoyage des fichiers locaux après migration réussie
3. Imaging Server
Rôle : Service de traitement d'images en mémoire
Opérations :
Génération de thumbnails (différentes résolutions)
Création de workcopy (10000×10000 pixels)
Conversion de formats (PNG, JPG, WebP)
Génération d'images animées (GIF, WebP)
Calcul de hashimage pour recherche visuelle
Accès : Via
ImagingFactory.getImaging()
4. MC2 / AWS S3
Rôle :
Stockage des fichiers sources originaux en cloud
Stockage des variations et résultats de traitement
Opérations :
PUT: Upload des sources (Phase 1, Phase 2)GET metadata: Vérification existence/tailleDELETE: Suppression des anciens chemins MC1.5 (Phase 4)
5. AWS DynamoDB - MigrateLog
Rôle : Journal des modifications pendant la migration
Table :
{clientAppName}.migratelog-tableCycle de vie :
Activé au début de Phase 1
Alimenté par les hooks CRUD pendant Phases 1-2
Lu et vidé pendant Phase 2 et Phase 4
Désactivé en Phase 3
6. AWS DynamoDB - MigrateState
Rôle : État de progression par asset
Table :
{clientAppName}.migratestate-table
7. Azure Blob Storage (OPV2)
Rôle : Stockage historique des vidéos HLS et sous-titres
Accès : Via
OPV2Database.GetSecretConfig()etAzureStorageLecture seule :
Fichiers HLS (
.m3u8,.ts)Sous-titres (
.vtt)
Utilisé dans : Phase 1 pour
copyVideoHls()etcopyVideoSubtitle()
8. Thread Pool Migration
Rôle : Parallélisation des traitements
Configuration :
ThreadPoolExecutorWrappernommé "WXM_DAMDY.MigrateMc2"Taille : Configurable via
config.nbrThread(défaut:nbrCPU/2)Utilisation :
Phase 1 : Traitement parallèle des objets
Phase 2 : Traitement parallèle des logs
Phase 5 : Suppression parallèle des fichiers locaux
Diagrammes d'architecture réseau
Vue d'ensemble des échanges réseau
┌──────────────────────────────────────────────────┐
│ Serveur Tomcat (WEDIAG) │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ MigrateProcess │ │
│ │ (war/WEB-INF/classes) │ │
│ └──────────┬──────────────────────────┘ │
│ │ │
│ ├─────────────────────┐ │
│ │ │ │
│ ┌──────────▼──────────┐ ┌─────▼──────────┐ │
│ │ AwsCloudService │ │ ImagingFactory│ │
│ │ (HTTP Client) │ │ │ │
│ └──────────┬──────────┘ └───────┬────────┘ │
└─────────────┼──────────────────────┼─────────────┘
│ │
│ │
┌─────────────────────────┼──────────────────────┼──────────────────┐
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
┌────────────────┐ ┌──────────────────┐ ┌──────────────┐ ┌────────────────┐
│ Base SQL │ │ AWS Cloud │ │ Imaging │ │ Azure Blob │
│ (MySQL/ │ │ │ │ Server │ │ Storage │
│ PostgreSQL) │ │ ┌────────────┐ │ │ (Docker) │ │ (OPV2) │
│ │ │ │ S3 Sources │ │ │ │ │ │
│ - Metadata │ │ │ Bucket │ │ │ - Thumbnail │ │ - HLS (.m3u8) │
│ - Structures │ │ └────────────┘ │ │ - Workcopy │ │ - Segments │
│ - Config EVP │ │ │ │ - Convert │ │ (.ts) │
│ │ │ ┌────────────┐ │ │ - HashImage │ │ - Subtitles │
└────────────────┘ │ │ S3 Results │ │ │ │ │ (.vtt) │
│ │ Bucket │ │ └──────────────┘ └────────────────┘
│ └────────────┘ │
│ │
│ ┌────────────┐ │
│ │ DynamoDB │ │
│ │ │ │
│ │ - Migrate │ │
│ │ Log │ │
│ │ - Migrate │ │
│ │ State │ │
│ └────────────┘ │
└──────────────────┘
┌────────────────────────────────────────────────────────────────────────┐
│ SAN (Storage Area Network) - Accès local filesystem │
│ │
│ /san/file/{objecttype}/{filename} ← Lecture sources │
│ /san/cache/{hash}/thumbnail_*.{format} ← Lecture cache variations │
│ │
│ Accès : NFS / CIFS / Direct mount │
└────────────────────────────────────────────────────────────────────────┘
Flux réseau par phase de migration
Phase 1 : Copie initiale
┌─────────────┐
│ Tomcat │
│ (Thread 1) │
└──────┬──────┘
│
│ 1. SELECT structure, instances
├─────────────────────────────────────► ┌──────────────┐
│ │ MySQL/ │
│◄───────────────────────────────────────┤ PostgreSQL │
│ ResultSet (metadata) └──────────────┘
│
│ 2. Read file source
├─────────────────────────────────────► ┌──────────────┐
│ │ SAN (local) │
│◄───────────────────────────────────────┤ │
│ Binary data (File InputStream) └──────────────┘
│
│ 3. POST getThumbnail(params)
├─────────────────────────────────────► ┌──────────────┐
│ │ Imaging │
│◄───────────────────────────────────────┤ Server │
│ Response: path to generated file │ (HTTP API) │
│ └──────────────┘
│
│ 4. Read generated workcopy from cache
├─────────────────────────────────────► ┌──────────────┐
│ │ SAN (cache) │
│◄───────────────────────────────────────┤ │
│ Binary data (workcopy PNG/JPG) └──────────────┘
│
│ 5. PUT DynamoDB (MigrateState started=true)
├─────────────────────────────────────► ┌──────────────┐
│ │ DynamoDB │
│◄───────────────────────────────────────┤ MigrateState│
│ 200 OK └──────────────┘
│
│ 6. PUT S3 Results (workcopy, variations, hash, ...)
├─────────────────────────────────────► ┌──────────────┐
│ Multipart upload (chunked) │ S3 Results │
│◄───────────────────────────────────────┤ Bucket │
│ 200 OK + ETag └──────────────┘
│
│ 7. Si vidéo: GET Azure (HLS, subtitles)
├─────────────────────────────────────► ┌──────────────┐
│ │ Azure Blob │
│◄───────────────────────────────────────┤ Storage │
│ Binary data (m3u8, ts, vtt) │ (OPV2) │
│ └──────────────┘
│ └─> Puis PUT vers S3 Results
│
│ 8. PUT S3 Sources (fichier source original)
├────────────────────────────────────► ┌──────────────┐
│ Multipart upload │ S3 Sources │
│◄──────────────────────────────────────┤ Bucket │
│ 200 OK + ETag └──────────────┘
│
│ 9. PUT DynamoDB (MigrateState done=true, steps_done)
├────────────────────────────────────► ┌──────────────┐
│ │ DynamoDB │
│◄──────────────────────────────────────┤ MigrateState│
│ 200 OK └──────────────┘
│
│ 10. Pendant ce temps, hooks BDD actifs:
│ INSERT/UPDATE/DELETE → PUT DynamoDB MigrateLog
│
└──────────────────────────────────────────────────────────────────
Note: Étapes 1-9 répétées en parallèle pour N threads (config.nbrThread)
Phase 2 : Traitement insertions
┌─────────────┐
│ Tomcat │
│ (Main Loop) │
└──────┬──────┘
│
│ 1. SCAN DynamoDB (Action=INSERTED)
├─────────────────────────────────────► ┌──────────────┐
│ │ DynamoDB │
│◄───────────────────────────────────────┤ MigrateLog │
│ List<MigrateLog> (paginated) └──────────────┘
│
│ 2. Pour chaque log → Thread parallèle:
│
│ a. DELETE DynamoDB (MigrateState)
│ ├──────────────────────────────────► ┌──────────────┐
│ │ │ DynamoDB │
│ │◄────────────────────────────────────┤ MigrateState│
│ │ 200 OK └──────────────┘
│ │
│ b. SELECT instance by ID
│ ├──────────────────────────────────► ┌──────────────┐
│ │ │ MySQL/ │
│ │◄────────────────────────────────────┤ PostgreSQL │
│ │ ResultSet (1 row) └──────────────┘
│ │
│ c. Rejouer migrateObjectField()
│ │ (identique à Phase 1 : SAN + Imaging + S3)
│ │
│ d. DELETE DynamoDB (MigrateLog)
│ └──────────────────────────────────► ┌──────────────┐
│ │ DynamoDB │
│ ◄─────────────────────────────────────┤ MigrateLog │
│ 200 OK └──────────────┘
│
│ 3. Si nbr_done > 0 : RETRY (boucle)
│ Sinon : FIN Phase 2
│
└──────────────────────────────────────────────────────────────────
Phase 3 : Bascule (maintenance)
┌─────────────┐
│ Tomcat │
│ (Single │
│ Thread) │
└──────┬──────┘
│
│ 1. Broadcast Cluster (ClusterEnableMaintenance)
├────────────────────────────────────► ┌──────────────┐
│ │ Autres │
│◄──────────────────────────────────────┤ nœuds │
│ ACK │ Tomcat │
│ └──────────────┘
│
│ 2. Rejouer Phase 2 (résidus)
│ (voir diagramme Phase 2)
│
│ 3. BEGIN TRANSACTION
│ ┌────────────────────────────────► ┌──────────────┐
│ │ ALTER TABLE ... MODIFY COLUMN │ DB │
│ └◄──────────────────────────────────┤ │
│ 200 OK └──────────────┘
│
│ 4. Broadcast Cluster (ClusterDisableMigrationLog)
├─────────────────────────────────────► ┌──────────────┐
│ │ Autres │
│◄───────────────────────────────────────┤ nœuds │
│ ACK └──────────────┘
│
│ 5. Shutdown Application
│ └─> Fermeture connexions (BDD, S3, DynamoDB, HTTP pools)
│
│ 6. Startup Application
│ └─> Rechargement structures (type blob maintenant)
│
└──────────────────────────────────────────────────────────────────
─
Phase 4 : Nettoyage suppressions
┌─────────────┐
│ Tomcat │
│ (Main Loop) │
└──────┬──────┘
│
│ 1. SCAN DynamoDB (Action=DELETED)
├────────────────────────────────────► ┌──────────────┐
│ │ DynamoDB │
│◄──────────────────────────────────────┤ MigrateLog │
│ List<MigrateLog> (DELETED entries) └──────────────┘
│
│ 2. Pour chaque log:
│
│ a. SELECT instance (vérif sécurité)
│ ├──────────────────────────────────► ┌──────────────┐
│ │ │ MySQL/ │
│ │◄────────────────────────────────────┤ PostgreSQL │
│ │ ResultSet (peut être vide) └──────────────┘
│ │
│ b. Si vraiment à supprimer:
│ │ DELETE S3 (blobPath)
│ ├──────────────────────────────────► ┌──────────────┐
│ │ │ S3 Sources │
│ │◄────────────────────────────────────┤ Bucket │
│ │ 204 No Content └──────────────┘
│ │
│ c. DELETE DynamoDB (MigrateLog)
│ └──────────────────────────────────► ┌──────────────┐
│ │ DynamoDB │
│ ◄─────────────────────────────────────┤ MigrateLog │
│ 200 OK └──────────────┘
│
│ 3. Si nbr_done > 0 : RETRY (boucle)
│ Sinon : FIN Phase 4
│
└──────────────────────────────────────────────────────────────────
Phase 5 : Nettoyage local (SAN)
┌─────────────┐
│ Tomcat │
│ (Thread N) │
└──────┬──────┘
│
│ 1. SELECT instances WHERE blob IS NOT NULL
├────────────────────────────────────► ┌──────────────┐
│ │ MySQL/ │
│◄──────────────────────────────────────┤ PostgreSQL │
│ ResultSet (instances avec blob) └──────────────┘
│
│ 2. Pour chaque instance (parallèle):
│
│ Check existence + DELETE file
│ └──────────────────────────────────► ┌──────────────┐
│ │ SAN (local) │
│ File.delete() │ │
│ └──────────────┘
│
│ Aucun échange réseau externe (local filesystem only)
│
└──────────────────────────────────────────────────────────────────
Protocoles et APIs utilisés
1. Base de données SQL
Opérations :
Lecture structures, instances
Modification structure (Phase 3)
2. AWS S3
Opérations
Upload fichiers (sources, results)
Lecture des fichiers existants
Suppression fichiers obsolètes
3. AWS DynamoDB
Opérations :
Écriture MigrateLog, MigrateState
Lecture MigrateState
Suppression après traitement
Parcours complet (avec FilterExpression)
4. Imaging Server
Opérations :
Génération thumbnail
Exécution preset
5. Azure Blob Storage (OPV2)
APIs :
Liste fichiers HLS/subtitles
Téléchargement local temporaire
Utilisation : Lecture seule (migration depuis legacy)
Bande passante et volumétrie
Points de contention réseau
Upload S3 (Phase 1)
Goulot principal :
Bande passante sortante serveur Tomcat
Lecture des fichiers dans la SAN (iops)
Imaging Server (Phase 1)
Goulot :
CPU Imaging Server (génération thumbnails)
Lecture des fichiers dans la SAN (iops)
DynamoDB (Phases 2, 4)
Goulot : Bande passante sortante serveur Tomcat
Base SQL (Phase 3)
Goulot : Lock tables pendant ALTER TABLE
Durée : Quelques secondes à minutes (dépend taille table)
Impact : Site indisponible pendant ce temps
Les 5 phases de migration
Phase 1 : Copie initiale (phaseOne)
Objectif
Copier l'état actuel de tous les assets de MC1 vers MC2 sans interruption de service.
Flux détaillé
┌─────────────────────────────────────────────────────────────────────────────┐
│ PHASE 1 : COPIE INITIALE │
└─────────────────────────────────────────────────────────────────────────────┘
1. Configuration initiale
└─> Activer MigrateLog (DynamoDB + Cluster)
└─> Créer ThreadPool (nbrThread workers)
2. Pour chaque ObjectType (wxm_dam_asset, wxm_dam_document, ...)
├─> Identifier les champs file/image
├─> Compter les instances (estimation)
└─> Pour chaque instance ayant un fichier
└─> [PARALLÈLE] Soumettre au ThreadPool
│
├─> Lire état MigrateState (DynamoDB)
│ └─> Si done=true && !forceCheck: SKIP
│
├─> Marquer started=true (DynamoDB)
│
├─> Lire fichier source (SAN)
│ └─> Binary.getServerPath()
│
├─> [ÉTAPE] Gestion MC1.5 (si applicable)
│ ├─> Tester existence S3: s3://sources/{mc15_path}
│ └─> Si trouvé:
│ ├─> Copier S3→S3 (sources + results)
│ ├─> Marquer pour suppression (MigrateLog.DELETED)
│ └─> TERMINÉ (c'est un MC1.5)
│
├─> [ÉTAPE] copyWorkcopy
│ ├─> Lire cache SAN ou générer (ImagingServer)
│ ├─> Générer 10000×10000 (PNG/JPG selon source)
│ ├─> Upload S3 Results: {blobPath}.d/thumbnail_10000x10000.{format}
│ └─> Marquer étape done (DynamoDB)
│
├─> [ÉTAPE] copyWorkcopyAnimated
│ ├─> Chercher cache animated WebP/PNG
│ ├─> Générer 10000×10000 animé (ImagingServer)
│ ├─> Upload S3 Results: {blobPath}.d/animated_10000x10000.webp
│ └─> Marquer étape done
│
├─> [ÉTAPE SI VIDEO/AUDIO] copyVideo
│ ├─> Charger variations depuis VariationsProvider
│ ├─> Pour chaque résolution (FHD, HD, MD, SD)
│ │ ├─> Extraire variation en fichier temp
│ │ └─> Upload S3 Results: {blobPath}.d/mediaconvert/files/output_{res}.mp4
│ ├─> Générer manifest JSON
│ ├─> Upload S3 Results: {blobPath}.d/mediaconvert/job/output.json
│ └─> Marquer étape done
│
├─> [ÉTAPE SI VIDEO] copyVideoHls
│ ├─> Requête OPV2Database.getMediaFilesHLS()
│ ├─> Connexion Azure Blob Storage
│ ├─> Pour chaque fichier .m3u8/.ts
│ │ ├─> Download depuis Azure
│ │ └─> Upload S3 Results: {blobPath}.d/mediaconvert/hls/{file}
│ └─> Marquer étape done
│
├─> [ÉTAPE SI VIDEO] copyVideoSubtitle
│ ├─> Requête OPV2Database.getMediaFilesSubtitles()
│ ├─> Connexion Azure Blob Storage
│ ├─> Pour chaque sous-titre
│ │ ├─> Download .vtt depuis Azure
│ │ └─> Upload S3 Results: {blobPath}.d/subtitles/sources/{lang}.vtt
│ ├─> Générer subtitles.json
│ ├─> Upload S3 Results: {blobPath}.d/subtitles/sources/subtitles.json
│ └─> Marquer étape done
│
├─> [ÉTAPE] copyVariations
│ ├─> Pour prefix (Thumbnail, Animated)
│ │ └─> Pour dimension (Tiny=100, Small=200, Big=600, Screen=2000)
│ │ └─> Pour format (png, jpg, webp, gif)
│ │ ├─> Chercher cache SAN
│ │ ├─> Si trouvé:
│ │ │ ├─> Upload S3 Results: {preset_name}/{variation}
│ │ │ └─> Appel MC2 API: savePresetOutputPath()
│ │ └─> Sinon: SKIP
│ │
│ └─> Pour preset custom (circle_png, square_2000, square_png)
│ ├─> Vérifier preset existe (PresetManager)
│ ├─> Si valide:
│ │ ├─> Exécuter preset (ImagingServer)
│ │ ├─> Upload S3 Results
│ │ └─> Appel MC2 API: savePresetOutputPath()
│ └─> Sinon: WARNING log
│
├─> [ÉTAPE SI PDF/OFFICE] copyPdf
│ ├─> Charger variation "pdf"
│ ├─> Extraire en fichier temp
│ ├─> Upload S3 Results: {blobPath}.d/pdf.d/base.pdf
│ └─> Marquer étape done
│
├─> [ÉTAPE SI PDF/OFFICE] copyExtractedText
│ ├─> ImagingFactory.getImaging().getExtractedText()
│ ├─> Lire texte en UTF-8
│ ├─> Upload S3 Results: {blobPath}.d/extract.text.d/base.txt
│ └─> Marquer étape done
│
├─> [ÉTAPE] copyHashImage
│ ├─> Chercher cache hashimage.png
│ ├─> Si trouvé:
│ │ └─> Upload S3 Results: {blobPath}.d/hashimage.d/hashimage.png
│ └─> Marquer étape done
│
├─> [ÉTAPE SI doCopySource] copySource
│ ├─> Tester existence S3 Sources: {blobPath}
│ ├─> Si absent OU taille différente:
│ │ ├─> Copier fichier source en temp
│ │ ├─> Upload S3 Sources: {blobPath}
│ │ └─> Si manualIngestion: créer IngestionConfiguration.json
│ └─> Marquer étape done
│
└─> Marquer done=true (DynamoDB)
3. Pendant toute la phase 1
└─> Hooks actifs sur BDD:
├─> INSERT → MigrateLog.INSERTED (DynamoDB)
├─> UPDATE → MigrateLog.DELETED (ancien) + INSERTED (nouveau)
└─> DELETE → MigrateLog.DELETED (DynamoDB)
Points de reprise
Chaque étape est atomique et marquée dans DynamoDB
En cas d'arrêt : reprend au dernier état enregistré
Option
forceCheck: refait les étapes manquantesOption
forceRedo: ignore l'état et refait tout
Phase 2 : Traitement des insertions (phaseTwo)
Objectif
Traiter les fichiers créés ou modifiés pendant la Phase 1 grâce au journal d'événements.
Flux détaillé
┌─────────────────────────────────────────────────────────────────────────────┐
│ PHASE 2 : TRAITEMENT DES INSERTIONS │
└─────────────────────────────────────────────────────────────────────────────┘
BOUCLE WHILE (tant qu'il reste des entrées):
1. Thread d'estimation (arrière-plan)
├─> Scan DynamoDB MigrateLog
├─> Filtre: Action=INSERTED
├─> Compte total → Logger.phaseTwoNbrTodo()
└─> Continue en parallèle pendant 5 secondes
2. Thread principal (traitement)
└─> Scan DynamoDB MigrateLog (Action=INSERTED)
└─> Pour chaque entrée MigrateLog
│
├─> Vérifier ObjectType dans liste migration
│ └─> Si hors scope: SKIP
│
├─> [PARALLÈLE] Soumettre au ThreadPool
│ │
│ ├─> Supprimer MigrateState pour cette propriété
│ │ └─> DELETE DynamoDB MigrateState
│ │ (force retraitement car fichier a changé)
│ │
│ ├─> Charger instance depuis BDD
│ │ └─> CTObjects.loadObjectById()
│ │
│ ├─> Si instance existe:
│ │ ├─> Récupérer IObjectField
│ │ └─> Appeler migrateObjectField()
│ │ (identique à Phase 1 - toutes les étapes)
│ │
│ ├─> Logger.phaseTwoNbrDone++
│ │
│ └─> Supprimer entrée MigrateLog
│ └─> DELETE DynamoDB MigrateLog
│ (traité, on nettoie)
│
└─> Logger progression
3. Synchronisation
└─> Attendre tous les ThreadPool tasks
└─> Compter nbr_done
4. Condition de sortie
└─> SI nbr_done == 0: FIN (plus rien à traiter)
└─> SINON: RETRY (boucle suivante)
Cas d'usage typiques
Création asset : Utilisateur upload un nouveau fichier → INSERTED
Modification asset : Utilisateur remplace un fichier → DELETED + INSERTED
Suppression asset : Utilisateur supprime un asset → DELETED (traité en Phase 4)
Gestion des conflits
La suppression du
MigrateStateforce le retraitement completSi l'instance n'existe plus en BDD : log DELETED sera traité en Phase 4
Évite les incohérences entre cache DynamoDB et état réel
Phase 3 : Bascule ⚠️ (phaseThree)
Objectif
Point de non-retour : Basculer définitivement vers MC2 en modifiant la structure de la base de données.
Flux détaillé
┌─────────────────────────────────────────────────────────────────────────────┐
│ PHASE 3 : BASCULE (POINT DE NON-RETOUR) │
└─────────────────────────────────────────────────────────────────────────────┘
1. Activation mode maintenance
├─> MigrateService.enableMaintenance()
├─> Bloque les opérations CREATE/UPDATE/DELETE
└─> Broadcast Cluster: ClusterEnableMaintenance
(tous les serveurs passent en maintenance)
2. Traitement des derniers résidus
└─> Appel phaseTwo()
(traiter tout ce qui a été créé entre-temps)
3. Désactivation ElasticSearch
└─> ServiceManager.requestService(ElasticSearchService)
└─> service.disable()
(évite l'indexation pendant modification structure)
4. Modification structure BDD
├─> Connexion BDD: CTDatabaseFactory.getOrCreateDefaultDriver()
│
└─> Pour chaque ObjectType à migrer
│
├─> Charger structure: CTObjectStructure.load(objectname)
│
└─> Pour chaque champ file/image
│
├─> Récupérer CTObjectFieldWritable
├─> Modifier type: field.setType("blob")
├─> Sauvegarder structure
│ └─> structure.Update(driver, null, false, true)
└─> Logger.phaseThreeObjectnameDone()
5. Désactivation logs migration
└─> MigrateService.disableMigrationLog()
└─> Broadcast Cluster: ClusterDisableMigrationLog
(plus d'enregistrement des modifications)
6. Redémarrage moteur
├─> Startup.stop()
│ └─> Fermeture connexions, threadpools, services
│
└─> Startup.start()
└─> Réinitialisation complète
├─> Recharge structures (maintenant en blob)
├─> Réactive services (ElasticSearch, etc.)
└─> Recharge configuration MC2
⚠️ Avertissements critiques
Interruption de service
Le site est en maintenance (utilisateurs bloqués)
Durée : Variable selon nombre d'ObjectTypes (quelques secondes à minutes)
Point de non-retour
Une fois les champs convertis en
blob, retour en arrière nécessite :Backup BDD à restaurer
Pré-requis absolus
Phase 1 terminée à 100%
Phase 2 stable (plus d'entrées ou très peu)
Backup BDD à jour
Risques
Échec redémarrage moteur → site indisponible
Cluster non synchronisé → nœuds en état différent
Phase 4 : Nettoyage des suppressions (phaseFour)
Objectif
Supprimer de S3 les fichiers obsolètes marqués DELETED dans le journal.
Flux détaillé
┌─────────────────────────────────────────────────────────────────────────────┐
│ PHASE 4 : NETTOYAGE DES SUPPRESSIONS │
└─────────────────────────────────────────────────────────────────────────────┘
BOUCLE WHILE (tant qu'il reste des entrées):
1. Thread d'estimation (arrière-plan)
├─> Scan DynamoDB MigrateLog
├─> Filtre: Action=DELETED
├─> Compte total → Logger.phaseFourNbrTodo()
└─> Continue en parallèle
2. Thread principal (traitement)
└─> Scan DynamoDB MigrateLog (Action=DELETED)
└─> Pour chaque entrée MigrateLog
│
├─> Extraire PropertyValue (blobPath à supprimer)
│
├─> Vérification sécurité (fichier vraiment inutilisé ?)
│ ├─> Charger instance depuis BDD
│ │ └─> CTObjects.loadObjectById()
│ │
│ ├─> Si instance existe:
│ │ ├─> Lire valeur actuelle du champ
│ │ └─> Si valeur == PropertyValue:
│ │ ├─> WARNING: "Fichier marqué à supprimer mais encore utilisé !"
│ │ └─> remove = false (ne pas supprimer)
│ │
│ └─> Sinon: remove = true
│
├─> Si remove == true:
│ └─> Supprimer de S3 Sources
│ └─> awsService.getSourcesStorage().delete(PropertyValue)
│
├─> Logger.phaseFourNbrDone++
│
└─> Supprimer entrée MigrateLog
└─> DELETE DynamoDB MigrateLog
3. Synchronisation
└─> Attendre traitement complet
4. Condition de sortie
└─> SI nbr_done == 0: FIN
└─> SINON: RETRY
Cas traités
Fichiers MC1.5 : Anciens chemins S3 migrés depuis MC1.5
Fichiers remplacés : Assets dont le fichier a changé en Phase 1/2
Assets supprimés : Instances supprimées pendant la migration
Sécurité
Vérification double : le fichier est-il vraiment inutilisé ?
Log WARNING si détection usage inattendu
Pas de suppression en mode
simulate
Phase 5 : Nettoyage local (phaseFive)
Objectif
Supprimer les fichiers locaux du SAN maintenant que tout est en S3.
Flux détaillé
┌─────────────────────────────────────────────────────────────────────────────┐
│ PHASE 5 : NETTOYAGE LOCAL (SAN) │
└─────────────────────────────────────────────────────────────────────────────┘
1. Identification des ObjectTypes
└─> _findObjectnamesToRemovePj()
├─> Pour chaque ObjectType
│ └─> Identifier champs type "blob"
│ (conversion faite en Phase 3)
└─> Retourne Set<ObjectType>
2. Estimation
└─> Pour chaque ObjectType
├─> Compter instances avec blob non vide
└─> Logger.removePjAddObjectsTodo()
3. Traitement parallèle
└─> Pour chaque ObjectType
└─> Pour chaque instance avec blob
│
├─> [PARALLÈLE] Soumettre au ThreadPool
│ │
│ └─> Pour chaque champ blob
│ │
│ ├─> Extraire PropertyValue (blobPath)
│ │
│ ├─> Construire chemin local SAN
│ │ └─> file/{objecttype}/{blobPath}
│ │ Exemple: file/wxm_dam_asset/assets/2024/asset123.jpg
│ │
│ ├─> Vérifier existence fichier local
│ │ └─> Startup.getSanPathOf(filePath).toFile()
│ │
│ ├─> Si fichier existe:
│ │ ├─> Logger: "Suppression {size} bytes"
│ │ └─> CTFile.delete(file)
│ │ (suppression physique SAN)
│ │
│ └─> Logger.removePjObjectFieldDone++
│
└─> Attendre ThreadPool tasks
4. Résultat
└─> Espace disque SAN libéré
└─> Fichiers uniquement en S3
Conditions de suppression
Le champ est de type
blob(Phase 3 effectuée)Le fichier existe physiquement en local
Pas de vérification S3 (on fait confiance à Phase 1)
Espace libéré
Fichiers sources (
file/)Pas de suppression du cache (géré séparément par CacheCleaner)
Configuration et paramètres
MigrateConfig
MigrateController.MigrateConfig config = new MigrateConfig();
// Mode simulation (aucun changement persisté)
config.simulate = false; // false = vraie migration
// Copie des sources
config.doCopySource = true; // true = upload sources vers S3
// Stratégies de reprise
config.forceCheck = false; // true = refaire étapes non complétées
config.forceRedo = false; // true = tout refaire depuis zéro
// Parallélisation
config.nbrThread = 4; // Nombre de workers (0 = auto: CPU/2)
// Redémarrage automatique
config.enableRestart = true; // true = redémarre après Phase 3
// false = s'arrête et attend relance manuelle
Monitoring et observabilité
Statistiques temps réel
L'écran administratif permet de suivre l'évolution.
Il présente également des estimations de temps par phase.
Logs (Log4j)
# Configuration logger
Nom: migration.log
Remarques
Le principe de la migration est de ne tolérer aucune erreur.
Donc à la moindre erreur, la procédure s’arrête en indiquant dans l'écran administratif l’erreur.
Cette dernière est aussi logguée dans le log.
Il faudra résoudre l’erreur puis relancer la migration.
Les erreurs classiques:
un bug dans l’EVP fait qu’il existe plusieurs points d’entrée pour les HLS: la procédure ne peut pas savoir lequel est le bon. Il faudra interroger la base de données et marquer comme supprimés les points d’entrée obsolète.
un propriété file est renseignée dans la base de donnée, mais on ne trouve pas le fichier dans la SAN. Il faudra soit ajouter le fichier dans la SAN, soit vider la propriété concernée.
Glossaire
Terme | Description |
|---|---|
MC1 | MediaCloud 1 - Architecture historique avec stockage local (SAN) |
MC2 | MediaCloud 2 - Architecture cloud avec S3 et traitement asynchrone |
MC1.5 | État intermédiaire avec sources déjà en S3 mais structure BDD en |
BlobPath | Chemin du fichier dans S3 Sources (ex: |
Workcopy | Version haute résolution (10000×10000) pour traitement |
Variation | Version transformée (thumbnail, preset) d'un asset |
Preset | Transformation nommée (ex: |
Ingestion | Processus MC2 d'analyse et génération variations asynchrone |
SAN | Storage Area Network - Stockage local haute performance |
OPV2 | Online Video Platform v2 - Système de gestion vidéos (Azure) |
HLS | HTTP Live Streaming - Format vidéo adaptatif |