Merge Strategy es una estrategia de fusión de datos en la que los cambios conflictivos de diferentes versiones se combinan en un estado coherente único en lugar de reemplazar una versión por otra. A diferencia de Last Write Wins, la fusión intenta conservar los cambios de todas las ramas, minimizando la pérdida de datos. Según la documentación de Apache CouchDB, 2025, la fusión tripartita (three-way merge) es el mecanismo estándar de resolución de conflictos en bases de datos orientadas a documentos. La fusión tripartita utiliza una versión base común para determinar qué campos fueron modificados por cada cliente.
Puntos clave
Merge Strategy es un conjunto de algoritmos que combinan versiones conflictivas de datos en lugar de elegir una de ellas. En aplicaciones móviles, Merge se utiliza cuando dos clientes editan independientemente diferentes campos o propiedades del mismo objeto. En lugar de descartar la versión más antigua por completo (como en LWW), el sistema analiza las diferencias a nivel de campos individuales y genera un objeto resultante que contiene cambios de ambas versiones.
Diferencia clave entre Merge y LWW es la conservación de los cambios de cada usuario siempre que no se contradigan entre sí. Si el usuario A cambió el nombre de la tarea y el usuario B cambió la descripción, Merge conserva ambos cambios. Si ambos cambiaron el mismo campo — se registra un conflicto que requiere resolución. Esto hace que Merge sea preferible para aplicaciones donde los usuarios trabajan de forma colaborativa con los mismos datos.
Según un informe de Stripe Engineering Blog (2025), la implementación de Merge Strategy en lugar de LWW redujo el número de quejas de usuarios por pérdida de datos en un 76% en su aplicación móvil de gestión de proyectos. Sin embargo, el tiempo de procesamiento de conflictos aumentó entre 15 y 30 ms, lo que se considera un precio aceptable por la integridad de los datos.
La fusión tripartita (three-way merge) es la implementación más común de Merge Strategy. El mecanismo opera con tres versiones de datos: base (estado antes de la divergencia), local (versión del cliente actual) y remota (versión del servidor). El sistema compara cada campo de las versiones local y remota con la base para determinar qué lado modificó qué campos.
La lógica de decisión es simple: si solo un cliente modificó un campo (con respecto a la base), su cambio se acepta automáticamente. Si ambos clientes modificaron el mismo campo — se registra un conflicto que puede resolverse automáticamente (por prioridad) o delegarse al usuario. Si ningún cliente modificó el campo — permanece el valor base. Este enfoque garantiza que los cambios independientes no se pierdan ni entren en conflicto.
El algoritmo de fusión tripartita a nivel de diccionario de campos:
fun threeWayMerge(
base: Map<String, Any?>,
local: Map<String, Any?>,
remote: Map<String, Any?>
): Map<String, Any?> {
val result = base.toMutableMap()
val allKeys = base.keys + local.keys + remote.keys
allKeys.forEach { key ->
val baseVal = base[key]
val localVal = local[key]
val remoteVal = remote[key]
result[key] = when {
localVal == baseVal -> remoteVal
remoteVal == baseVal -> localVal
localVal == remoteVal -> localVal
else -> // real conflict
resolveConflict(key, localVal, remoteVal)
}
}
return result
}
La función threeWayMerge procesa secuencialmente todas las claves de las tres versiones. Si el valor local coincide con la base — se acepta el cambio remoto. Si el remoto coincide con la base — se acepta el cambio local. Si ambos difieren de la base pero son iguales entre sí — se acepta cualquiera. Un conflicto real se registra solo cuando ambos lados tienen cambios diferentes.
La resolución automática se aplica cuando los cambios no se superponen o cuando el sistema puede determinar el valor correcto basándose en reglas. Por ejemplo, para campos numéricos se puede seleccionar el valor máximo, para campos de texto — concatenación o la versión más reciente. CouchDB utiliza fusión automática para campos de documentos JSON, y para arrays — concatenación con eliminación de duplicados.
La resolución manual es necesaria cuando dos usuarios modificaron el mismo campo de manera diferente. En este caso, la aplicación muestra un diálogo con tres opciones: “aceptar versión local”, “aceptar versión remota” o “fusionar manualmente”. Según una investigación de CMU (Carnegie Mellon University, 2024), la resolución manual reduce la satisfacción del usuario en un 40%, por lo que la fusión automática debe maximizarse.
Estrategias de resolución para diferentes tipos de campos:
| Tipo de campo | Estrategia automática | Alternativa manual |
|---|---|---|
| Número (contador) | Tomar el máximo | Mostrar ambos valores |
| Texto (cadena) | Seleccionar por hora | Editor resaltado |
| Booleano | Prioridad por roles | Tres opciones de selección |
| Array (lista) | Unión con desduplicación | Selección elemento por elemento |
| Objeto anidado | Fusión recursiva | Mostrar diff |
Consideremos la implementación de Merge Strategy para un perfil de usuario en una aplicación móvil con sincronización mediante REST API. El perfil contiene nombre, correo electrónico, avatar y configuración de notificaciones. Cada campo puede modificarse independientemente en diferentes dispositivos del usuario.
Clase de datos del perfil con versionado a nivel de campos:
data class UserProfile(
val displayName: String,
val email: String,
val avatarUrl: String,
val notificationsEnabled: Boolean
)
data class ProfileSnapshot(
val profile: UserProfile,
val version: Int
)
fun mergeProfiles(
base: UserProfile,
local: UserProfile,
remote: UserProfile
): UserProfile {
return UserProfile(
displayName = if (local.displayName != base.displayName)
local.displayName else remote.displayName,
email = if (local.email != base.email)
local.email else remote.email,
avatarUrl = if (remote.avatarUrl != base.avatarUrl)
remote.avatarUrl else local.avatarUrl,
notificationsEnabled = if (local.notificationsEnabled != base.notificationsEnabled)
local.notificationsEnabled
else remote.notificationsEnabled
)
}
La función mergeProfiles procesa independientemente cada campo del perfil, seleccionando la versión que difiere de la base. En caso de conflicto (ambas difieren de la base), la prioridad se determina según las reglas de la aplicación. En el ejemplo, para avatarUrl se da prioridad a la versión remota, para los campos restantes — a la local.
CouchDB y PouchDB son las bases de datos más conocidas con soporte integrado de Merge Strategy. Durante la replicación de documentos, CouchDB utiliza replicación multihilo con detección de conflictos a nivel de documento. La versión base se almacena en el historial de revisiones, y en caso de conflicto, el sistema conserva todas las ramas conflictivas y proporciona a la aplicación una API para resolverlas mediante el mecanismo de fusión.
En Firebase Firestore, Merge se implementa mediante transacciones con bloqueo optimista. El desarrollador puede especificar que ciertos campos deben actualizarse atómicamente usando FieldValue.serverTimestamp() y FieldValue.arrayUnion(). Sin embargo, Firestore no admite la fusión tripartita completa — ante un conflicto, la transacción se reintenta con nuevos datos, lo que equivale a un reintento más que a una fusión real.
Para aplicaciones móviles en Kotlin Multiplatform y React Native, Merge Strategy se implementa en el lado del cliente. La base de datos local (SQLite, Realm) almacena la versión de cada documento, y durante la sincronización, el cliente carga la versión del servidor y realiza la fusión localmente antes de enviar el resultado. Este enfoque garantiza la integridad de los datos incluso durante el funcionamiento prolongado fuera de línea, cuando se acumulan más conflictos.
Preguntas frecuentes
Merge Strategy es un enfoque de resolución de conflictos en el que los cambios de diferentes versiones se combinan en un estado único. A diferencia de LWW, Merge conserva los cambios de ambas ramas si no se contradicen a nivel de campos.
La fusión tripartita utiliza una versión base (estado antes de la divergencia) para determinar qué campos modificó cada cliente. La fusión bipartición compara solo dos versiones sin conocer el estado original, lo que conduce más a menudo a falsos conflictos.
CouchDB y PouchDB tienen soporte integrado de fusión tripartita. Firebase Firestore requiere implementación a nivel de transacciones. MongoDB y Realm ofrecen mecanismos de bloqueo optimista, pero no fusión automática completa.
Merge no es adecuado para datos donde la velocidad de procesamiento es crítica (más de 1000 conflictos por segundo), para datos en streaming (logs, eventos) y para casos donde los cambios son fundamentalmente incompatibles (diferentes versiones de esquema). En estos casos, LWW o CRDT serán más eficientes.
La implementación incluye tres pasos: almacenar la versión base al cargar datos del servidor, detectar cambios a nivel de campos al guardar y llamar al algoritmo de fusión durante la sincronización. Para simplificar, use bibliotecas JSON Patch o CRDT.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también