IBM Cloud Docs
Dokumentversionssteuerung und MVCC

Dokumentversionssteuerung und MVCC

Multi-Version Concurrency Control (MVCC) stellen IBM® Cloudant® for IBM Cloud®-Datenbanken sicher, dass alle Knoten in einem Datenbankcluster nur die neueste Version eines Dokuments enthalten.

Da IBM Cloudant-Datenbanken sukzessive konsistent sind, ist dies notwendig, um Inkonsistenzen zwischen Knoten infolge einer Synchronisierung veralteter Dokumente zu vermeiden.

Multiversion Concurrency Control (MVCC) ermöglicht gleichzeitigen Lese- und Schreibzugriff auf eine IBM Cloudant-Datenbank. MVCC ist eine Form des optimistischen gemeinsamen Zugriffs. Es beschleunigt sowohl Lese- als auch Schreiboperationen in IBM Cloudant-Datenbanken, weil es keinen Grund für Datenbanksperren für Lese- oder Schreibvorgänge gibt. MVCC ermöglicht auch eine Synchronisierung zwischen IBM Cloudant-Datenbankknoten.

Revisionen

Jedes Dokument in einer IBM Cloudant-Datenbank hat ein Feld _rev, in dem die zugehörige Revisionsnummer angegeben ist.

Eine Revisionsnummer wird Ihren Dokumenten vom Server hinzugefügt, wenn Sie diese einfügen oder ändern. Die Nummer ist in der Serverantwort enthalten, wenn Sie Änderungen vornehmen oder ein Dokument lesen. Der Wert _rev setzt sich aus einer Kombination eines einzelnen Zählers und eines Hashwerts des Dokuments zusammen.

Die beiden Hauptverwendungen der Revisionsnummer sind in den folgenden Fällen hilfreich:

  1. Bestimmen, welche Dokumente zwischen Servern repliziert werden müssen.
  2. Bestätigen, dass ein Client versucht, die aktuelle Version eines Dokuments zu ändern.

Sie müssen die vorherige _rev angeben, wenn Sie ein Dokument aktualisieren , da Ihre Anforderung andernfalls fehlschlägt und den Fehler 409zurückgibt.

_rev darf nicht verwendet werden, um ein System zur Versionssteuerung zu erstellen, da es sich um einen internen Wert handelt, der vom Server verwendet wird. Deshalb sind ältere Revisionen eines Dokuments transient und werden daher regelmäßig gelöscht.

Sie können jedoch eine bestimmte Überarbeitung abfragen, indem Sie die zugehörige _revverwenden. Ältere Überarbeitungen werden jedoch regelmäßig von einem Prozess gelöscht, der als komprimierung. Sie können eine bestimmte Dokumentrevision unter Angabe von _rev abfragen, um den Verlauf der Revisionen Ihres Dokuments abzurufen. Eine Folge der Verdichtung ist jedoch, dass Sie sich nicht auf eine erfolgreiche Antwort verlassen können. Wenn Sie einen Versionsverlauf Ihrer Dokumente benötigen, sollten Sie für jede Revision ein neues Dokument erstellen.

Verteilte Datenbanken und Konflikte

Verteilte Datenbanken arbeiten ohne konstante Verbindung zur Hauptdatenbank unter IBM Cloudant, die selbst ebenfalls verteilt ist, deshalb können Aktualisierungen, die auf derselben früheren Version basieren, trotzdem in Konflikt zueinander stehen.

Um Konflikte zu finden, fügen Sie beim Abrufen eines Dokuments den Abfrageparameter conflicts=true hinzu. Die Antwort enthält ein Array _conflicts mit allen in Konflikt stehenden Revisionen.

Schreiben Sie eine Ansicht, um Konflikte für mehrere Dokumente in einer Datenbank zu finden.

Die folgende 'map'-Funktion ist ein Beispiel, das alle in Konflikt stehenden Revisionen für jedes Dokument mit einem Konflikt ausgibt.

Beispiel für eine 'map'-Funktion zum Suchen von Dokumenten mit einem Konflikt:

function (doc) {
    if (doc._conflicts) {
        emit(null, [doc._rev].concat(doc._conflicts));
    }
}

Sie können diese Ansicht regelmäßig abfragen und Konflikte bei Bedarf beheben, oder Sie können die Ansicht nach jeder Replikation abfragen.

Vorgehensweise zum Lösen von Konflikten

Sobald Sie einen Konflikt gefunden haben, können Sie ihn in vier Schritten ('get', 'merge', 'upload' und 'delete') beheben, wie weiter unten gezeigt.

Sehen wir uns ein Beispiel für das Lösen eines Konflikts an. Angenommen, Sie haben eine Datenbank mit Produkten für einen Onlineshop. Die erste Version eines Dokuments kann dem folgenden Beispiel ähneln:

{
    "_id": "74b2be56045bed0c8c9d24b939000dbe",
    "_rev": "1-7438df87b632b312c53a08361a7c3299",
    "name": "Samsung Galaxy S4",
    "description": "",
    "price": 650
}

Da das Dokument noch keine Beschreibung aufweist, kann ein Benutzer eine Beschreibung hinzufügen.

Zweite Version des Dokuments, erstellt durch Hinzufügen einer Beschreibung:

{
    "_id": "74b2be56045bed0c8c9d24b939000dbe",
    "_rev": "2-61ae00e029d4f5edd2981841243ded13",
    "name": "Samsung Galaxy S4",
    "description": "Latest smartphone from Samsung",
    "price": 650
}

Zur gleichen Zeit setzt ein anderer Benutzer, der mit einer replizierten Datenbank arbeitet, den Preis herab.

Eine andere Revision, die mit der vorherigen aufgrund von unterschiedlichen price-Werten in Konflikt steht:

{
    "_id": "74b2be56045bed0c8c9d24b939000dbe",
    "_rev": "2-f796915a291b37254f6df8f6f3389121",
    "name": "Samsung Galaxy S4",
    "description": "",
    "price": 600
}

Die beiden Datenbanken werden dann repliziert. Die Abweichungen in den Dokumentversionen führen zu einem Konflikt.

Konfliktrevisionen abrufen

Sie können Dokumente mit Konflikten mithilfe der Option conflicts=true ermitteln.

Beispieldokument für die Suche nach Dokumenten mit Konflikten:

https://$ACCOUNT.cloudant.com/products/$_ID?conflicts=true

Beispielantwort auf den Dokumentenabruf, die zeigt, wie sich Konfliktrevisionen auf Dokumente auswirken:

{
    "_id":"74b2be56045bed0c8c9d24b939000dbe",
    "_rev":"2-f796915a291b37254f6df8f6f3389121",
    "name":"Samsung Galaxy S4",
    "description":"",
    "price":600,
    "_conflicts":["2-61ae00e029d4f5edd2981841243ded13"]
}

Die Version mit dem geänderten Preis wurde willkürlich als aktuelle Version des Dokuments ausgewählt. Auf den Konflikt mit einer anderen Version wird hingewiesen, indem die ID dieser anderen Version im Array _conflicts angegeben wird. In den meisten Fällen hat dieses Array nur ein Element, aber es kann viele Konfliktrevisionen geben.

Änderungen zusammenführen

Um mithilfe eines Vergleichs der Revisionen zu ermitteln, was geändert wurde, ruft Ihre Anwendung alle Versionen aus der Datenbank ab.

Beispielbefehle zum Abrufen aller Versionen eines Dokuments aus der Datenbank:

https://$ACCOUNT.cloudant.com/products/$_ID
https://$ACCOUNT.cloudant.com/products/$_ID?rev=2-61ae00e029d4f5edd2981841243ded13
https://$ACCOUNT.cloudant.com/products/$_ID?rev=1-7438df87b632b312c53a08361a7c3299

Da sich die in Konflikt stehenden Änderungen auf verschiedene Felder des Dokuments beziehen, können sie einfach zusammengeführt werden.

Für komplexere Konflikte sind unter Umständen andere Lösungsstrategien erforderlich:

  • Zeitbasiert - verwenden Sie die erste oder die letzte Bearbeitung.
  • Benutzereingriff - berichten Sie Konflikte an Benutzer und lassen Sie sie entscheiden, welche Lösung die passende ist.
  • Hoch entwickelte Algorithmen - beispielsweise 3-Wege-Zusammenführungen von Textfeldern.

Ein praktisches Beispiel für die Implementierung einer Zusammenführung von Änderungen finden Sie in diesem Projekt mit Beispielcode.

Neue Revision hochladen

Der nächste Schritt besteht darin, ein Dokument zu erstellen, in dem die Konflikte gelöst sind, und die Datenbank damit zu aktualisieren.

Beispieldokument, das Änderungen aus den beiden Konfliktrevisionen zusammenführt

{
    "_id": "74b2be56045bed0c8c9d24b939000dbe",
    "_rev": "3-daaecd7213301a1ad5493186d6916755",
    "name": "Samsung Galaxy S4",
    "description": "Latest smartphone from Samsung",
    "price": 600
}

Alte Revisionen löschen

Schließlich löschen Sie die alten Revisionen, indem Sie eine DELETE-Anforderung an die URLs mit der zu löschenden Revision senden.

Beispielanforderung zum Löschen einer alten Dokumentrevision unter Verwendung von HTTP:

DELETE https://$ACCOUNT.cloudant.com/products/$_ID?rev=2-61ae00e029d4f5edd2981841243ded13

Beispielanforderung zum Löschen einer alten Dokumentrevision:

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))

Das vorherige Go-Beispiel erfordert den folgenden Importblock:

import (
   "encoding/json"
   "fmt"
   "github.com/IBM/cloudant-go-sdk/cloudantv1"
)

Für alle Go-Beispiele muss das Objekt service initialisiert sein. Weitere Informationen finden Sie in den Beispielen im Abschnitt 'Authentifizierung' in der API-Dokumentation.

Jetzt werden Konflikte, die sich auf das Dokument auswirken, aufgelöst. Sie können den Status überprüfen, indem Sie das Dokument erneut mit GET abrufen, wobei der Parameter conflicts auf true gesetzt ist.