Controllo della versione del documento e MVCC
MVCC (Multi - version concurrency control) è come i database IBM® Cloudant® for IBM Cloud® assicurano che tutti i nodi in un cluster di database contengano solo la versione più recente di un documento.
Poiché i database IBM Cloudant sono eventualmente congruenti, è necessario evitare che sorgano incongruenze tra i nodi come risultato della sincronizzazione tra documenti obsoleti.
Il controllo della concorrenza multiversione (MVCC, Multi-Version Concurrency Control) consente l'accesso simultaneo in lettura e scrittura a un database IBM Cloudant. MVCC è una forma di simultaneità ottimistica. Rende più rapide le operazioni di lettura e scrittura sui database IBM Cloudant perché i blocchi del database sulle operazioni di lettura o scrittura non sono necessari. MVCC abilita inoltre la sincronizzazione tra i nodi di database IBM Cloudant.
Revisioni
Ogni documento in un database IBM Cloudant ha un campo _rev
che indica il numero di revisione.
Quando inserisci o modifichi i documenti, il server aggiunge a questi un numero di revisione. Il numero viene incluso nella risposta del server quando apporti modifiche o leggi un documento. Il valore _rev
viene creato utilizzando
una combinazione di un contatore semplice e un hash del documento.
I due usi principali del numero di revisione sono per aiutare nei seguenti casi:
- Determinare i documenti da replicare tra i server.
- Confermare che un client sta tentando di modificare la versione più recente di un documento.
Devi specificare il precedente _rev
quando aggiorni un documento altrimenti la tua richiesta non riesce e restituisce un errore 409.
_rev
non deve essere utilizzato per creare un sistema di controllo versione in quanto è un valore interno utilizzato dal server. Pertanto, le revisioni più vecchie di un documento sono transitorie e vengono rimosse regolarmente.
Tuttavia, è possibile interrogare una particolare revisione utilizzando il suo _rev
, ma le revisioni più vecchie vengono regolarmente eliminate da un processo denominato
compattazione. È possibile interrogare una particolare revisione del documento utilizzando il relativo _rev
per ottenere una cronologia
delle revisioni del documento. Tuttavia, una conseguenza della compattazione è che non è possibile fare affidamento su una risposta corretta. Se hai bisogno di una cronologia delle versione dei tuoi documenti, una soluzione è quella di
creare un nuovo documento per ogni revisione.
Database distribuiti e conflitti
I database distribuiti funzionano senza una connessione costante al database principale su IBM Cloudant, che è a sua volta distribuito, pertanto gli aggiornamenti basati sulla stessa versione precedente possono ancora essere in conflitto.
Per individuare i conflitti, aggiungere il parametro di interrogazione conflicts=true
quando si richiama un documento. La risposta contiene un array _conflicts
con tutte le revisioni in conflitto.
Per trovare conflitti per più documenti in un database, scrivi una vista.
La seguente funzione mappa è un esempio che emette tutte le revisioni in conflitto per ogni documento che ha un conflitto.
Consultare il seguente esempio di una funzione di associazione per trovare i documenti con un conflitto:
function (doc) {
if (doc._conflicts) {
emit(null, [doc._rev].concat(doc._conflicts));
}
}
È possibile interrogare regolarmente questa vista e risolvere i conflitti, se necessario, oppure interrogare la vista dopo ogni replica.
Passi per risolvere i conflitti
Una volta trovato un conflitto, è possibile risolverlo in quattro passi: ottenere, unire, caricare ed eliminare, come mostrato in seguito.
Consideriamo un esempio di come risolvere un conflitto. Supponiamo di avere un database di prodotti per un negozio online. La prima versione di un documento potrebbe essere simile al seguente esempio:
{
"_id": "74b2be56045bed0c8c9d24b939000dbe",
"_rev": "1-7438df87b632b312c53a08361a7c3299",
"name": "Samsung Galaxy S4",
"description": "",
"price": 650
}
Poiché il documento non ha ancora una descrizione, qualcuno potrebbe aggiungerne una.
Vedere la seconda versione del documento, creata aggiungendo una descrizione:
{
"_id": "74b2be56045bed0c8c9d24b939000dbe",
"_rev": "2-61ae00e029d4f5edd2981841243ded13",
"name": "Samsung Galaxy S4",
"description": "Latest smartphone from Samsung",
"price": 650
}
Allo stesso tempo, qualcun altro - lavorando con un database replicato - riduce il prezzo.
Visualizzare una revisione differente, in conflitto con quella precedente a causa di un diverso valore price
:
{
"_id": "74b2be56045bed0c8c9d24b939000dbe",
"_rev": "2-f796915a291b37254f6df8f6f3389121",
"name": "Samsung Galaxy S4",
"description": "",
"price": 600
}
I due database vengono poi replicati. La differenza nelle versioni del documento provoca un conflitto.
Ottieni le revisioni in conflitto
I documenti con conflitti vengono identificati utilizzando l'opzione conflicts=true
.
Consultare il seguente esempio di ricerca di documenti con conflitti:
https://$ACCOUNT.cloudant.com/products/$_ID?conflicts=true
Vedi la seguente risposta di esempio che mostra le revisioni in conflitto che influenzano i documenti:
{
"_id":"74b2be56045bed0c8c9d24b939000dbe",
"_rev":"2-f796915a291b37254f6df8f6f3389121",
"name":"Samsung Galaxy S4",
"description":"",
"price":600,
"_conflicts":["2-61ae00e029d4f5edd2981841243ded13"]
}
La versione con il prezzo modificato è stata scelta arbitrariamente come ultima versione del documento. Il conflitto con un'altra versione viene notato fornendo l'ID di tale altra versione nell'array _conflicts
. Nella maggior
parte dei casi, questo array ha un solo elemento, ma potrebbero esistere molte revisioni in conflitto.
Unisci le modifiche
Per confrontare le revisioni per vedere cosa è stato modificato, la tua applicazione ottiene tutte le versioni dal database.
Per richiamare tutte le versioni di un documento dal database, fare riferimento ai seguenti comandi di esempio:
https://$ACCOUNT.cloudant.com/products/$_ID
https://$ACCOUNT.cloudant.com/products/$_ID?rev=2-61ae00e029d4f5edd2981841243ded13
https://$ACCOUNT.cloudant.com/products/$_ID?rev=1-7438df87b632b312c53a08361a7c3299
Poiché le modifiche in conflitto riguardano i diversi campi del documento, è facile unirle.
Per i conflitti più complessi, potrebbero essere necessarie altre strategie di risoluzione:
- Basato sull'ora - utilizzare la prima o l'ultima modifica.
- Intervento dell'utente - segnala i conflitti agli utenti e lascia che siano loro a decidere la migliore risoluzione.
- Algoritmi sofisticati - ad esempio, unioni a 3 vie di campi di testo.
Per un esempio pratico di come implementare un'unione di modifiche, vedi questo progetto con codice di esempio.
Carica la nuova revisione
Il passo successivo è quello di creare un documento che risolva i conflitti e di aggiornare il database con tale documento.
Consultare il seguente documento di esempio che unisce le modifiche delle due revisioni in conflitto:
{
"_id": "74b2be56045bed0c8c9d24b939000dbe",
"_rev": "3-daaecd7213301a1ad5493186d6916755",
"name": "Samsung Galaxy S4",
"description": "Latest smartphone from Samsung",
"price": 600
}
Elimina le vecchie revisioni
Infine, si eliminano le vecchie revisioni inviando una richiesta DELETE
agli URL con la revisione che si desidera eliminare.
Vedi la seguente richiesta di esempio per eliminare una vecchia versione del documento utilizzando HTTP:
DELETE https://$ACCOUNT.cloudant.com/products/$_ID?rev=2-61ae00e029d4f5edd2981841243ded13
Vedi la seguente richiesta di esempio per eliminare una vecchia revisione del documento:
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))
L'esempio Go precedente richiede il seguente blocco di importazione:
import (
"encoding/json"
"fmt"
"github.com/IBM/cloudant-go-sdk/cloudantv1"
)
Tutti gli esempi Go richiedono l'inizializzazione dell'oggetto service
. Per ulteriori informazioni, vedi la sezione Autenticazione della documentazione API per degli esempi.
Ora, i conflitti che riguardano il documento vengono risolti. È possibile verificare lo stato eseguendo di nuovo GET
sul documento con il parametro conflicts
impostato su true
.