Guía de migración a Lore VCS: cómo solucionar la pesadilla de costes de Git LFS y los conflictos de assets en Unreal
A medida que el proyecto crece, Git LFS se convierte en un desastre. Corregir unas pocas líneas de código es instantáneo, pero cuando empiezan a mezclarse assets artísticos de tamaño gigabyte, el reloj de arena gira durante decenas de segundos por cada commit. Debido a la estructura ineficiente de LFS, que copia y acumula archivos grandes, el almacenamiento del repositorio supera rápidamente el terabyte y las facturas por ancho de banda muestran números aterradores.
Lore, publicado como código abierto por Epic Games, prioriza los archivos binarios. Utiliza el método FastCDC, que divide los archivos en bloques para eliminar duplicados, reduciendo drásticamente tanto el tamaño del paquete como el ancho de banda de transferencia. He recopilado los métodos prácticos de migración y optimización para Lore, que incluso equipos pequeños sin personal especializado en DevOps pueden aplicar de inmediato.
1. Migración progresiva de Git LFS a Lore
No es necesario descartar el historial de commits del código fuente acumulado en el repositorio Git existente. Una estrategia híbrida es lo más realista: dejar el código ligero en Git y separar solo los pesados assets binarios al repositorio de Lore.
El siguiente script identifica la lista de archivos que rastreaba Git LFS, crea una estructura de directorios simétrica y compara los hashes SHA-256 de las copias para realizar la transferencia a Lore sin pérdida de datos.
`bash
#!/usr/bin/env bash
set -euo pipefail
GIT_REPO_DIR="HOME/projects/my−game−git"LOREREPODIR="HOME/projects/my-game-lore"
LORE_SERVER_URL="lore://127.0.0.1:41337/my-project"
echo "[1/5] Inicializando el repositorio Lore y vinculación remota..."
if [ ! -d "LOREREPODIR"];thenmkdir−p"LORE_REPO_DIR"
cd "LOREREPODIR"lorerepositorycreate"LORE_SERVER_URL"
else
cd "$LORE_REPO_DIR"
fi
echo "[2/5] Recopilando lista de archivos rastreados por Git LFS..."
cd "GITREPODIR"LFSFILES=(git lfs ls-files -n | grep -E ".(uasset|umap|fbx|png|psd)$" || true)
if [ -z "$LFS_FILES" ]; then
echo "No hay archivos LFS para migrar."
exit 0
fi
echo "[3/5] Creando manifiesto para copia de archivos y verificación..."
echo "LFSFILES"∣whileread−rfilepath;doif[−f"filepath" ]; then
dest_dir="LOREREPODIR/(dirname "filepath")"mkdir−p"dest_dir"
cp -p "filepath""LORE_REPO_DIR/filepath"sourcehash=(openssl dgst -sha256 "$filepath" | awk '{print 2}')
echo "filepath:sourcehash">>"LORE_REPO_DIR/.lore_migration_manifest"
fi
done
echo "[4/5] Desvinculando Git LFS y preparando Lore..."
cd "GITREPODIR"echo"LFS_FILES" | while read -r filepath; do
git rm --cached "$filepath"
done
sed -i.bak -E 's/filter=lfs diff=lfs merge=lfs//g' .gitattributes
cd "LOREREPODIR"echo"LFS_FILES" | while read -r filepath; do
lore stage "$filepath"
done
lore commit -m "Migration: Transfer heavy assets from Git LFS to Lore"
lore push
echo "[5/5] Verificación de integridad de datos..."
lore repository verify state
lore repository verify fragment
if [ -f ".lore_migration_manifest" ]; then
migration_errors=0
while IFS=":" read -r file sha; do
if [ -f "file"];thencurrenthash=(openssl dgst -sha256 "$file" | awk '{print 2}')
if [ "sha" != "$current_hash" ]; then
echo "Archivo corrupto detectado: file"migrationerrors=((migration_errors + 1))
fi
fi
done < .lore_migration_manifest
if [ $migration_errors -eq 0 ]; then
echo "Migración completada. Todos los datos han sido recuperados con un 100% de integridad."
rm .lore_migration_manifest
else
echo "Error de verificación: existen problemas de integridad en $migration_errors archivos."
exit 1
fi
fi
`
Abre la terminal y guarda el contenido anterior como migrate.sh. Dale permisos de ejecución con chmod +x migrate.sh, ajusta las variables según tu ruta local y ejecútalo.
Una vez terminada esta tarea, el uso de almacenamiento LFS en la nube remota disminuirá instantáneamente. Según los casos de implementación propios de Epic Games, esta tecnología de deduplicación a nivel de bloque permite reducir los costes de mantenimiento de infraestructura en un 50%.
2. Aplicación de hidratación bajo demanda (on-demand hydration) en la pipeline de CI/CD
Una pipeline que descarga cientos de gigabytes de assets de juego completos en cada ciclo de compilación es la causa principal de la saturación de la tarjeta de red y las operaciones de entrada/salida de disco. Lore admite la recuperación retardada (hidratación bajo demanda) basada en un sistema de archivos virtual para reducir los tiempos de compilación.
En la etapa de clonación inicial, solo se replican la cadena de estructura de metadatos y el árbol de directorios como un cascarón, y cuando el compilador lee los assets reales durante el proceso de "cocinado" (cooking), se transmiten dinámicamente solo los fragmentos binarios necesarios para llenar el disco.
`yaml
name: Game Project On-Demand Production Build
on:
push:
branches: [ release-candidate ]
env:
LORE_SERVER: "lore://10.0.1.50:41337/game-depot"
SHARED_STORE_PATH: "/var/lib/lore_shared_chunk_store"
WORKSPACE_PATH: "game_build_workspace"
jobs:
assemble-assets:
runs-on: self-hosted
steps:
- name: Preparar Persistent Shared Store
run: |
if [ ! -d "env.SHAREDSTOREPATH"];thensudomkdir−p"{{ env.SHARED_STORE_PATH }}"
sudo chmod 777 "env.SHAREDSTOREPATH"loreshared−storecreate"{{ env.SHARED_STORE_PATH }}"
fi
- name: Sparse Bare Clone vía Shared Cache
run: |
lore clone \
--bare \
--use-shared-store \
--shared-store-path "${{ env.SHARED_STORE_PATH }}" \
"${{ env.LORE_SERVER }}" \
"${{ env.WORKSPACE_PATH }}"
- name: Excluir fuentes cinemáticas innecesarias mediante filtros de vista
run: |
cd "${{ env.WORKSPACE_PATH }}"
echo "+ /SourceCode/" > .lore/view
echo "+ /Config/" >> .lore/view
echo "+ /Content/Core_Assets/" >> .lore/view
echo "- /Content/Cinematic_RAW/" >> .lore/view
- name: Sincronizar solo los fragmentos necesarios al disco local
run: |
cd "${{ env.WORKSPACE_PATH }}"
lore sync
- name: Ejecutar Unreal Engine Cooker
run: |
cd "${{ env.WORKSPACE_PATH }}"
./RunUAT.sh BuildCookRun \
-project="$(pwd)/GameProject.uproject" \
-platform=Win64 \
-clientconfig=Development \
-cook -stage -pak -archive
`
Si preparas un almacén de caché persistente como /var/lib/lore_shared_chunk_store en el servidor de compilación dedicado, el efecto se maximiza. Tras obtener rápidamente solo la información del índice con el comando lore clone --bare, se dejan solo las rutas necesarias en el archivo de filtro .lore/view y se ejecuta lore sync. Al establecer esta estructura, el tráfico enviado a los nodos de compilación se reduce en más de un 85%. El tiempo total de compilación se reduce en un 30% de media.
3. Prevención de conflictos de assets con el sistema de bloqueo de archivos
Los assets binarios, como los .uasset de Unreal Engine o los archivos de mapas de nivel, no se pueden fusionar automáticamente en la rama principal como el código de texto. Si dos artistas modifican el mismo asset simultáneamente, el que haga el push más tarde sobrescribirá el trabajo del anterior, provocando la pérdida de datos o desajustes en la línea temporal.
Lore ofrece directamente una función de bloqueo (Lock) que realiza un control exclusivo basado en el registro inmutable del servidor remoto.
`bash
Solicitar permiso exclusivo de edición para un archivo de modelo de personaje binario específico
lore lock acquire Content/Characters/HeroMesh.uasset --branch main
Verificar el estado de bloqueo de los recursos bajo una carpeta específica
lore lock status Content/Characters/HeroMesh.uasset
Buscar todos los archivos bloqueados por un trabajador específico en el repositorio remoto
lore lock query --branch main --owner developer_artist_03
Devolver permiso tras completar el trabajo y realizar el push
lore lock release Content/Characters/HeroMesh.uasset --branch main
`
Debes establecer una regla por la cual los artistas deben adquirir el bloqueo con el comando lore lock acquire antes de tocar un archivo. Una vez finalizada la modificación y reflejada correctamente en el repositorio remoto, se libera el bloqueo con lore lock release para pasárselo al siguiente trabajador.
Si a los diseñadores y artistas les resulta incómodo el uso de comandos de terminal, puedes integrar dicha ruta en herramientas de GUI de escritorio como Anchorpoint, de modo que estos comandos de bloqueo se ejecuten simplemente haciendo clic derecho con el ratón.
4. Guía de ajuste de caché según especificaciones de hardware
Es necesario ajustar la configuración del cliente según las especificaciones del entorno de trabajo para evitar cuellos de botella en la entrada y salida de archivos.
Ajuste del cliente local según las especificaciones del PC de trabajo
- Sistema de alto rendimiento (SSD NVMe, 32GB RAM o más, CPU multinúcleo): Se permiten operaciones directas basadas en mapeo de memoria a través de técnicas de mapeo de memoria virtual del SO. Para eliminar retrasos en la sincronización, se amplía el número máximo de hilos de conexión de descarga paralela y se establece un periodo de retención de caché de disco más prolongado.
- Sistema de gama de entrada y artistas (SSD SATA o HDD, 16GB RAM o menos): Se desactiva el método de mapeo de memoria para evitar bloqueos del motor por agotamiento de memoria. Se activan los flags
--direct-file-write y --direct-file-io para escribir directamente en el disco, y se aumenta el intervalo de programación del recolector de basura (garbage collection) para evitar fenómenos de acaparamiento de disco durante el trabajo.
Análisis de rendimiento según la elección del algoritmo de compresión
Consultando los resultados de la reunión de decisión de arquitectura de Lore (ADR-00016), establecer el estándar Zstandard Level 6 como motor de compresión predeterminado es lo que mejor equilibra la eficiencia.
| Tipo de algoritmo de compresión |
Tasa de compresión final (%) |
Velocidad de operación de compresión (MiB/s) |
Velocidad de descompresión (MiB/s) |
Portabilidad y características |
| LZ4 Default |
47.6% |
718.7 |
2494.5 |
Rápido, pero desperdicia mucha capacidad de almacenamiento |
| Zstd Level 1 |
34.5% |
602.0 |
1363.1 |
Buen punto medio entre velocidad y preservación de espacio |
| Zstd Level 6 (Recomendado) |
28.9% |
136.5 |
1284.9 |
Mantiene la mejor eficiencia de procesamiento frente a ahorro de espacio |
| Oodle Kraken 3 (Fast) |
28.2% |
95.6 |
1413.1 |
Requiere biblioteca propietaria Oodle de Epic Games |
| Oodle Kraken 6 (Por defecto anterior) |
23.9% |
2.2 |
1312.6 |
La velocidad se reduce drásticamente al transferir unidades de GB |
Oodle Kraken Level 6, utilizado frecuentemente en el pasado, causaba que la velocidad de procesamiento de compresión cayera a 2.2 MiB por segundo durante el proceso de serialización del backend, convirtiéndose en la causa principal de que los artistas tuvieran que esperar al commit antes de salir del trabajo.
Por el contrario, Zstd Level 6 ofrece una tasa de compresión cercana a Oodle Kraken 6, pero su velocidad de compresión de bloques de fragmentos locales es de 136.5 MiB/s, unas 62 veces más rápida. Si tienes muchas sucursales en el extranjero o trabajadores remotos y existe mucha interferencia en el ancho de banda, considera configurar una topología de servidor de dos capas configurando un nodo de caché de borde (proxy edge cache) dentro de la red local.