Conflictoplossingsbeleid beheren in Azure Cosmos DB

VAN TOEPASSING OP: NoSQL

Bij schrijfbewerkingen in meerdere regio's kunnen conflicten optreden wanneer meerdere clients naar hetzelfde item schrijven. Wanneer er een conflict optreedt, kunt u het conflict oplossen met behulp van verschillende beleidsregels voor conflictoplossing. In dit artikel wordt beschreven hoe u beleidsregels voor conflictoplossing beheert.

Tip

Conflictoplossingsbeleid kan alleen worden opgegeven tijdens het maken van containers en kan niet worden gewijzigd na het maken van de container.

Het conflictoplossingsbeleid 'last writer wins' maken

Deze voorbeelden laten zien hoe u een container kunt instellen met het conflictoplossingsbeleid 'last writer wins'. Het standaardpad voor last-writer-wins is het tijdstempelveld of de _ts eigenschap. Voor API voor NoSQL kan dit ook worden ingesteld op een door de gebruiker gedefinieerd pad met een numeriek type. In een conflict wint de hoogste waarde. Als het pad niet is ingesteld of ongeldig is, wordt het standaard ingesteld _ts. Conflicten die zijn opgelost met dit beleid, worden niet weergegeven in de conflictfeed. Dit beleid kan door alle API's worden gebruikt.

.NET SDK

DocumentCollection lwwCollection = await createClient.CreateDocumentCollectionIfNotExistsAsync(
  UriFactory.CreateDatabaseUri(this.databaseName), new DocumentCollection
  {
      Id = this.lwwCollectionName,
      ConflictResolutionPolicy = new ConflictResolutionPolicy
      {
          Mode = ConflictResolutionMode.LastWriterWins,
          ConflictResolutionPath = "/myCustomId",
      },
  });

Java V4 SDK

Java SDK V4 (Maven com.azure::azure-cosmos) Async API


ConflictResolutionPolicy policy = ConflictResolutionPolicy.createLastWriterWinsPolicy("/myCustomId");

CosmosContainerProperties containerProperties = new CosmosContainerProperties(container_id, partition_key);
containerProperties.setConflictResolutionPolicy(policy);
/* ...other container config... */
database.createContainerIfNotExists(containerProperties).block();

Java V2 SDK's

Async Java V2 SDK (Maven com.microsoft.azure::azure-cosmosdb)

DocumentCollection collection = new DocumentCollection();
collection.setId(id);
ConflictResolutionPolicy policy = ConflictResolutionPolicy.createLastWriterWinsPolicy("/myCustomId");
collection.setConflictResolutionPolicy(policy);
DocumentCollection createdCollection = client.createCollection(databaseUri, collection, null).toBlocking().value();

Node.js/JavaScript/TypeScript SDK

const database = client.database(this.databaseName);
const { container: lwwContainer } = await database.containers.createIfNotExists(
  {
    id: this.lwwContainerName,
    conflictResolutionPolicy: {
      mode: "LastWriterWins",
      conflictResolutionPath: "/myCustomId"
    }
  }
);

Python SDK

database = client.get_database_client(database=database_id)
lww_conflict_resolution_policy = {'mode': 'LastWriterWins', 'conflictResolutionPath': '/regionId'}
lww_container = database.create_container(id=lww_container_id, partition_key=PartitionKey(path="/id"), 
    conflict_resolution_policy=lww_conflict_resolution_policy)

Een aangepast conflictoplossingsbeleid maken met behulp van een opgeslagen procedure

Deze voorbeelden laten zien hoe u een container kunt instellen met aangepast conflictoplossingsbeleid. Dit beleid maakt gebruik van de logica in een opgeslagen procedure om het conflict op te lossen. Als een opgeslagen procedure is aangewezen om conflicten op te lossen, worden conflicten niet weergegeven in de conflictfeed, tenzij er een fout optreedt in de aangewezen opgeslagen procedure.

Nadat het beleid is gemaakt met de container, moet u de opgeslagen procedure maken. In het onderstaande .NET SDK-voorbeeld ziet u een voorbeeld van deze werkstroom. Dit beleid wordt alleen ondersteund in de API voor NoSQL.

Voorbeeld van een opgeslagen procedure voor conflictoplossing

Opgeslagen procedures voor aangepaste conflictoplossing moeten worden geïmplementeerd met behulp van de onderstaande functiehandtekening. De functienaam hoeft niet overeen te komen met de naam die wordt gebruikt bij het registreren van de opgeslagen procedure bij de container, maar het vereenvoudigt de naamgeving. Hier volgt een beschrijving van de parameters die moeten worden geïmplementeerd voor deze opgeslagen procedure.

  • incomingItem: het item dat wordt ingevoegd of bijgewerkt in de doorvoering die de conflicten genereert. Is null voor verwijderbewerkingen.
  • existingItem: het momenteel vastgelegde item. Deze waarde is niet null in een update en null voor een invoeg- of verwijderbewerking.
  • isTombstone: Booleaanse waarde die aangeeft of de incomingItem conflicteert met een eerder verwijderd item. Indien waar, is existingItem ook null.
  • conflictingItems: matrix van de vastgelegde versie van alle items in de container die conflicteren met incomingItem op id of andere unieke indexeigenschappen.

Belangrijk

Net als bij elke opgeslagen procedure heeft een aangepaste procedure voor conflictoplossing toegang tot alle gegevens met dezelfde partitiesleutel en kan elke invoeg-, update- of verwijderbewerking worden uitgevoerd om conflicten op te lossen.

Met deze opgeslagen voorbeeldprocedure worden conflicten opgelost door de laagste waarde in het /myCustomId pad te selecteren.

function resolver(incomingItem, existingItem, isTombstone, conflictingItems) {
  var collection = getContext().getCollection();

  if (!incomingItem) {
      if (existingItem) {

          collection.deleteDocument(existingItem._self, {}, function (err, responseOptions) {
              if (err) throw err;
          });
      }
  } else if (isTombstone) {
      // delete always wins.
  } else {
      if (existingItem) {
          if (incomingItem.myCustomId > existingItem.myCustomId) {
              return; // existing item wins
          }
      }

      var i;
      for (i = 0; i < conflictingItems.length; i++) {
          if (incomingItem.myCustomId > conflictingItems[i].myCustomId) {
              return; // existing conflict item wins
          }
      }

      // incoming item wins - clear conflicts and replace existing with incoming.
      tryDelete(conflictingItems, incomingItem, existingItem);
  }

  function tryDelete(documents, incoming, existing) {
      if (documents.length > 0) {
          collection.deleteDocument(documents[0]._self, {}, function (err, responseOptions) {
              if (err) throw err;

              documents.shift();
              tryDelete(documents, incoming, existing);
          });
      } else if (existing) {
          collection.replaceDocument(existing._self, incoming,
              function (err, documentCreated) {
                  if (err) throw err;
              });
      } else {
          collection.createDocument(collection.getSelfLink(), incoming,
              function (err, documentCreated) {
                  if (err) throw err;
              });
      }
  }
}

.NET SDK

DocumentCollection udpCollection = await createClient.CreateDocumentCollectionIfNotExistsAsync(
  UriFactory.CreateDatabaseUri(this.databaseName), new DocumentCollection
  {
      Id = this.udpCollectionName,
      ConflictResolutionPolicy = new ConflictResolutionPolicy
      {
          Mode = ConflictResolutionMode.Custom,
          ConflictResolutionProcedure = string.Format("dbs/{0}/colls/{1}/sprocs/{2}", this.databaseName, this.udpCollectionName, "resolver"),
      },
  });

//Create the stored procedure
await clients[0].CreateStoredProcedureAsync(
UriFactory.CreateStoredProcedureUri(this.databaseName, this.udpCollectionName, "resolver"), new StoredProcedure
{
    Id = "resolver",
    Body = File.ReadAllText(@"resolver.js")
});

Java V4 SDK

Java SDK V4 (Maven com.azure::azure-cosmos) Async API


ConflictResolutionPolicy policy = ConflictResolutionPolicy.createCustomPolicy("resolver");

CosmosContainerProperties containerProperties = new CosmosContainerProperties(container_id, partition_key);
containerProperties.setConflictResolutionPolicy(policy);
/* ...other container config... */
database.createContainerIfNotExists(containerProperties).block();

Java V2 SDK's

Async Java V2 SDK (Maven com.microsoft.azure::azure-cosmosdb)

DocumentCollection collection = new DocumentCollection();
collection.setId(id);
ConflictResolutionPolicy policy = ConflictResolutionPolicy.createCustomPolicy("resolver");
collection.setConflictResolutionPolicy(policy);
DocumentCollection createdCollection = client.createCollection(databaseUri, collection, null).toBlocking().value();

Nadat de container is gemaakt, moet u de opgeslagen procedure resolver maken.

Node.js/JavaScript/TypeScript SDK

const database = client.database(this.databaseName);
const { container: udpContainer } = await database.containers.createIfNotExists(
  {
    id: this.udpContainerName,
    conflictResolutionPolicy: {
      mode: "Custom",
      conflictResolutionProcedure: `dbs/${this.databaseName}/colls/${
        this.udpContainerName
      }/sprocs/resolver`
    }
  }
);

Nadat de container is gemaakt, moet u de opgeslagen procedure resolver maken.

Python SDK

database = client.get_database_client(database=database_id)
udp_custom_resolution_policy = {'mode': 'Custom' }
udp_container = database.create_container(id=udp_container_id, partition_key=PartitionKey(path="/id"),
    conflict_resolution_policy=udp_custom_resolution_policy)

Nadat de container is gemaakt, moet u de opgeslagen procedure resolver maken.

Aangepast conflictoplossingsbeleid maken

Deze voorbeelden laten zien hoe u een container kunt instellen met aangepast conflictoplossingsbeleid. Met deze implementatie wordt elk conflict weergegeven in de conflictfeed. Het is aan u om de conflicten afzonderlijk van de conflictfeed af te handelen.

.NET SDK

DocumentCollection manualCollection = await createClient.CreateDocumentCollectionIfNotExistsAsync(
  UriFactory.CreateDatabaseUri(this.databaseName), new DocumentCollection
  {
      Id = this.manualCollectionName,
      ConflictResolutionPolicy = new ConflictResolutionPolicy
      {
          Mode = ConflictResolutionMode.Custom,
      },
  });

Java V4 SDK

Java SDK V4 (Maven com.azure::azure-cosmos) Async API


ConflictResolutionPolicy policy = ConflictResolutionPolicy.createCustomPolicy();

CosmosContainerProperties containerProperties = new CosmosContainerProperties(container_id, partition_key);
containerProperties.setConflictResolutionPolicy(policy);
/* ...other container config... */
database.createContainerIfNotExists(containerProperties).block();

Java V2 SDK's

Async Java V2 SDK (Maven com.microsoft.azure::azure-cosmosdb)

DocumentCollection collection = new DocumentCollection();
collection.setId(id);
ConflictResolutionPolicy policy = ConflictResolutionPolicy.createCustomPolicy();
collection.setConflictResolutionPolicy(policy);
DocumentCollection createdCollection = client.createCollection(databaseUri, collection, null).toBlocking().value();

Node.js/JavaScript/TypeScript SDK

const database = client.database(this.databaseName);
const {
  container: manualContainer
} = await database.containers.createIfNotExists({
  id: this.manualContainerName,
  conflictResolutionPolicy: {
    mode: "Custom"
  }
});

Python SDK

database = client.get_database_client(database=database_id)
manual_resolution_policy = {'mode': 'Custom'}
manual_container = database.create_container(id=manual_container_id, partition_key=PartitionKey(path="/id"), 
    conflict_resolution_policy=manual_resolution_policy)

Lezen uit conflictfeed

Deze voorbeelden laten zien hoe u kunt lezen uit de conflictfeed van een container. Conflicten kunnen slechts enkele redenen in de conflictfeed worden weergegeven:

  • Het conflict is niet automatisch opgelost
  • Het conflict heeft een fout veroorzaakt met de aangewezen opgeslagen procedure
  • Het conflictoplossingsbeleid is ingesteld op aangepast en wijst geen opgeslagen procedure aan voor het afhandelen van conflicten

.NET SDK

FeedResponse<Conflict> conflicts = await delClient.ReadConflictFeedAsync(this.collectionUri);

Java-SDK's

Java V4 SDK (Maven com.azure::azure-cosmos)

int requestPageSize = 3;
CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();

CosmosPagedFlux<CosmosConflictProperties> conflictReadFeedFlux = container.readAllConflicts(options);

conflictReadFeedFlux.byPage(requestPageSize).toIterable().forEach(page -> {

    int expectedNumberOfConflicts = 0;
    int numberOfResults = 0;
    Iterator<CosmosConflictProperties> pageIt = page.getElements().iterator();

    while (pageIt.hasNext()) {
        CosmosConflictProperties conflictProperties = pageIt.next();

        // Read the conflict and committed item
        CosmosAsyncConflict conflict = container.getConflict(conflictProperties.getId());
        CosmosConflictResponse response = conflict.read(new CosmosConflictRequestOptions()).block();

        // response.
    }
});

Node.js/JavaScript/TypeScript SDK

const container = client
  .database(this.databaseName)
  .container(this.lwwContainerName);

const { result: conflicts } = await container.conflicts.readAll().toArray();

Python

conflicts_iterator = iter(container.list_conflicts())
conflict = next(conflicts_iterator, None)
while conflict:
    # Do something with conflict
    conflict = next(conflicts_iterator, None)

Volgende stappen

Meer informatie over de volgende Azure Cosmos DB-concepten: