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:
- Wereldwijde distributie - onder de motorkap
- Schrijfbewerkingen in meerdere regio's configureren in uw toepassingen
- Clients configureren voor multihoming
- Regio's toevoegen aan of verwijderen uit uw Azure Cosmos DB-account
- Schrijfbewerkingen in uw toepassingen configureren in meerdere regio's.
- Partitionering en gegevensdistributie
- Indexering in Azure Cosmos DB