Creación de versiones de documentos y MVCC
Control de simultaneidad de varias versiones (MVCC) es el método que utilizan las bases de datos de IBM® Cloudant® for IBM Cloud® para garantizar que todos los nodos de un clúster de bases de datos contienen solo la versión más reciente de un documento.
Puesto que las bases de datos de IBM Cloudant son finalmente coherentes, se necesita este sistema para evitar que se produzcan incoherencias entre nodos como resultado de la sincronización entre documentos obsoletos.
El control de simultaneidad de varias versiones (MVCC) permite el acceso simultáneo de lectura y escritura a una base de datos de IBM Cloudant. MVCC es una forma de simultaneidad optimista. Agiliza las operaciones de lectura y escritura en bases de datos de IBM Cloudant, ya que no son necesarios los bloqueos de base de datos en las operaciones de lectura ni escritura. MVCC también permite la sincronización entre los nodos de las bases de datos de IBM Cloudant.
Revisiones
Cada documento de una base de datos de IBM Cloudant tiene un campo _rev
que indica su número de revisión.
El servidor añade un número de revisión a los documentos cuando el usuario los inserta o modifica. El número se incluye en la respuesta del servidor cuando se realizan cambios o se lee un documento. El valor _rev
se crea utilizando
una combinación de un contador simple y un hash del documento.
Los dos usos principales del número de revisión son de ayuda en los siguientes casos:
- Determine los documentos de los que se debe hacer réplica entre servidores.
- Confirmar que un cliente está intentando modificar la última versión de un documento.
Debe especificar el _rev
anterior cuando actualice un documento o, de lo contrario, la solicitud fallará y devolverá un error 409.
No se debe utilizar _rev
para crear un sistema de control de versiones porque es un valor interno que utiliza el servidor. Por lo tanto, las versiones anteriores de un documento son transitorias y se eliminan con regularidad.
Sin embargo, puede consultar una revisión determinada utilizando su _rev
, pero las revisiones más antiguas se suprimen regularmente mediante un proceso denominado
compactación. Puede consultar una revisión de documento determinada mediante el _rev
correspondiente para obtener un historial de revisiones
del documento. Sin embargo, una consecuencia de la compactación es que no puede contar con obtener una respuesta correcta. Si necesita un historial de versiones de sus documentos, una solución es crear un nuevo documento para cada revisión.
Bases de datos distribuidas y conflictos
Las bases de datos distribuidas funcionan sin una conexión constante con la base de datos principal de IBM Cloudant, que a su vez es distribuida, de manera que actualizaciones basadas en la misma versión anterior pueden ser conflictivas.
Para buscar conflictos, añada el parámetro de consulta conflicts=true
al recuperar un documento. La respuesta contiene una matriz _conflicts
con todas
las revisiones conflictivas.
Para buscar los conflictos correspondientes a varios documentos de una base de datos, escriba una vista.
La siguiente función de correlación es un ejemplo que emite todas las revisiones conflictivas para cada documento que tiene un conflicto.
Consulte el ejemplo siguiente de una función de correlación si desea encontrar los documentos con un conflicto:
function (doc) {
if (doc._conflicts) {
emit(null, [doc._rev].concat(doc._conflicts));
}
}
Puede consultar esta vista regularmente y resolver los conflictos necesarios o consultar la vista después de cada réplica.
Pasos para resolver conflictos
Cuando encuentre un conflicto, puede resolverlo en cuatro pasos: obtener, fusionar, cargar y suprimir, tal como se muestra a continuación.
Consideremos un ejemplo de cómo resolver un conflicto. Supongamos que tiene una base de datos de productos para una tienda en línea. La primera versión de un documento se parece al del siguiente ejemplo:
{
"_id": "74b2be56045bed0c8c9d24b939000dbe",
"_rev": "1-7438df87b632b312c53a08361a7c3299",
"name": "Samsung Galaxy S4",
"description": "",
"price": 650
}
Como el documento aún no tiene una descripción, alguien puede añadir una.
Consulte la segunda versión del documento, que se crea añadiendo una descripción:
{
"_id": "74b2be56045bed0c8c9d24b939000dbe",
"_rev": "2-61ae00e029d4f5edd2981841243ded13",
"name": "Samsung Galaxy S4",
"description": "Latest smartphone from Samsung",
"price": 650
}
Paralelamente, otra persona que trabaja con una base de datos replicada reduce el precio.
Consulte una revisión distinta, que entra en conflicto con la anterior debido a un valor de price
distinto:
{
"_id": "74b2be56045bed0c8c9d24b939000dbe",
"_rev": "2-f796915a291b37254f6df8f6f3389121",
"name": "Samsung Galaxy S4",
"description": "",
"price": 600
}
Luego se realiza una réplica de ambas bases de datos. La diferencia entre las versiones del documento da lugar a un conflicto.
Obtenga las revisiones conflictivas
Puede identificar los documentos con conflictos mediante la opción conflicts=true
.
Consulte el ejemplo siguiente sobre cómo hallar documentos con conflictos:
https://$ACCOUNT.cloudant.com/products/$_ID?conflicts=true
Consulte la respuesta del ejemplo siguiente que muestra las revisiones conflictivas que afectan a los documentos:
{
"_id":"74b2be56045bed0c8c9d24b939000dbe",
"_rev":"2-f796915a291b37254f6df8f6f3389121",
"name":"Samsung Galaxy S4",
"description":"",
"price":600,
"_conflicts":["2-61ae00e029d4f5edd2981841243ded13"]
}
La versión con el precio cambiado se ha elegido aleatoriamente como la última versión del documento. El conflicto con la otra versión se indica mediante la aparición del ID de la otra versión en la matriz _conflicts
. En la mayoría
de los casos, esta matriz solo tiene un elemento, pero podría haber varias revisiones conflictivas.
Fusiones los cambios
Para comparar las revisiones a fin de ver lo que ha cambiado, la aplicación obtiene todas las versiones de la base de datos.
Consulte los mandatos de ejemplo siguientes para recuperar todas las versiones de un documento de la base de datos:
https://$ACCOUNT.cloudant.com/products/$_ID
https://$ACCOUNT.cloudant.com/products/$_ID?rev=2-61ae00e029d4f5edd2981841243ded13
https://$ACCOUNT.cloudant.com/products/$_ID?rev=1-7438df87b632b312c53a08361a7c3299
Puesto que los cambios conflictivos corresponden a distintos campos del documento, es fácil fusionarlos.
En el caso de conflictos más complejos pueden ser necesarias otras estrategias de resolución:
- En función del tiempo: utilizar la primera o la última edición.
- Intervención del usuario: notificar los conflictos a los usuarios y dejar que decidan la mejor resolución.
- Algoritmos sofisticados: por ejemplo, funciones de 3 vías de campos de texto.
Para obtener un ejemplo práctico de cómo implementar una fusión de cambios, consulte este proyecto con el código de ejemplo.
Cargue la nueva revisión
El siguiente paso consiste en crear un documento que resuelva los conflictos y actualizar la base de datos con el mismo.
Consulte el siguiente documento de ejemplo que fusiona los cambios de las dos revisiones en conflicto:
{
"_id": "74b2be56045bed0c8c9d24b939000dbe",
"_rev": "3-daaecd7213301a1ad5493186d6916755",
"name": "Samsung Galaxy S4",
"description": "Latest smartphone from Samsung",
"price": 600
}
Suprima las revisiones antiguas
Finalmente, suprima las versiones antiguas enviado una solicitud DELETE
a los URL con la revisión que desea suprimir.
Consulte la solicitud del ejemplo siguiente para suprimir una revisión antigua del documento utilizando HTTP:
DELETE https://$ACCOUNT.cloudant.com/products/$_ID?rev=2-61ae00e029d4f5edd2981841243ded13
Consulte la siguiente solicitud de ejemplo para suprimir una revisión de documento antigua:
curl -H "Authorization: Bearer $API_BEARER_TOKEN" -X DELETE "$SERVICE_URL/events/0007241142412418284?rev=2-9a0d1cd9f40472509e9aac6461837367"
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.DeleteDocumentOptions;
import com.ibm.cloud.cloudant.v1.model.DocumentResult;
Cloudant service = Cloudant.newInstance();
DeleteDocumentOptions documentOptions =
new DeleteDocumentOptions.Builder()
.db("events")
.docId("0007241142412418284")
.rev("2-9a0d1cd9f40472509e9aac6461837367")
.build();
DocumentResult response =
service.deleteDocument(documentOptions).execute()
.getResult();
System.out.println(response);
const { CloudantV1 } = require('@ibm-cloud/cloudant');
const service = CloudantV1.newInstance({});
service.deleteDocument({
db: 'events',
docId: '0007241142412418284',
rev: '2-9a0d1cd9f40472509e9aac6461837367'
}).then(response => {
console.log(response.result);
});
from ibmcloudant.cloudant_v1 import CloudantV1
service = CloudantV1.new_instance()
response = service.delete_document(
db='events',
doc_id='0007241142412418284',
rev='2-9a0d1cd9f40472509e9aac6461837367'
).get_result()
print(response)
deleteDocumentOptions := service.NewDeleteDocumentOptions(
"events",
"0007241142412418284",
)
deleteDocumentOptions.SetRev("2-9a0d1cd9f40472509e9aac6461837367")
documentResult, response, err := service.DeleteDocument(deleteDocumentOptions)
if err != nil {
panic(err)
}
b, _ := json.MarshalIndent(documentResult, "", " ")
fmt.Println(string(b))
El ejemplo Go anterior requiere el siguiente bloque de importación:
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
)
Todos los ejemplos de Go requieren que se inicialice el objeto service
. Para obtener más información, consulte los ejemplos de la Sección de autenticación de la documentación de la API.
Ahora se han resuelto los conflictos que afectaban al documento. Puede verificar el estado volviendo a ejecutar GET
para el documento con el parámetro conflicts
establecido en true
.