Mengelola kebijakan resolusi konflik di Azure Cosmos DB

BERLAKU UNTUK: NoSQL

Dengan penulisan multi-wilayah, ketika beberapa klien menulis ke item yang sama, konflik bisa saja terjadi. Anda dapat menyelesaikan konflik dengan menggunakan kebijakan penyelesaian konflik yang berbeda saat konflik terjadi. Artikel ini menjelaskan cara mengelola kebijakan resolusi konflik.

Tip

Kebijakan penyelesaian konflik hanya dapat ditentukan pada waktu pembuatan kontainer dan tidak dapat diubah setelah pembuatan kontainer.

Membuat kebijakan resolusi konflik dengan metode penulis-terakhir-menang

Sampel ini menunjukkan cara menyiapkan kontainer dengan kebijakan resolusi konflik penulis-terakhir-menang. Jalur default untuk lpenulis-terakhir-menang adalah bidang tanda waktu atau properti _ts. Untuk API untuk NoSQL, ini juga dapat diatur ke jalur yang ditentukan pengguna dengan jenis numerik. Dalam konflik, nilai tertinggi menang. Jika jalur tidak diatur atau tidak valid, jalur tersebut default ke_ts. Konflik yang diselesaikan dengan kebijakan ini tidak muncul di umpan konflik. Kebijakan ini dapat digunakan oleh semua API.

.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) API Asinkron


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

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)

Membuat kebijakan resolusi konflik kustom menggunakan prosedur tersimpan

Sampel ini menunjukkan cara menyiapkan kontainer dengan kebijakan resolusi konflik penulis-terakhir-menang. Kebijakan ini menggunakan logika dalam prosedur tersimpan untuk mengatasi konflik. Jika prosedur tersimpan ditunjuk untuk mengatasi konflik, konflik tidak akan muncul di umpan konflik kecuali ada kesalahan dalam prosedur tersimpan yang ditunjuk.

Setelah kebijakan dibuat dengan kontainer, Anda perlu membuat prosedur tersimpan. Sampel .NET SDK di bawah ini menunjukkan contoh alur kerja ini. Kebijakan ini hanya didukung di API untuk NoSQL.

Contoh prosedur tersimpan untuk resolusi konflik kustom

Resolusi konflik kustom dengan prosedur tersimpan harus diimplementasikan menggunakan tanda tangan fungsi yang ditunjukkan di bawah ini. Nama fungsi tidak perlu cocok dengan nama yang digunakan saat mendaftarkan prosedur tersimpan dengan kontainer, tetapi memang menyederhanakan penamaan. Berikut adalah deskripsi parameter yang harus diimplementasikan untuk prosedur tersimpan ini.

  • incomingItem: Item yang dimasukkan atau diperbarui dalam penerapan yang menghasilkan konflik. Null untuk menghapus operasi.
  • existingItem: Item yang saat ini diterapkan. Nilai ini non-null dalam pembaruan dan null untuk sisipan atau menghapus.
  • isTombstone: Boolean menunjukkan apakah incomingItem bertentangan dengan item yang dihapus sebelumnya. Jika benar, existingItem juga null.
  • conflictingItems: Array versi penerapan dari semua item dalam kontainer yang bertentangan dengan incomingItem pada ID atau properti indeks unik lainnya.

Penting

Sama seperti prosedur tersimpan, prosedur penyelesaian konflik kustom dapat mengakses data apa pun dengan kunci partisi yang sama dan dapat melakukan operasi sisipkan, perbarui, atau hapus untuk mengatasi konflik.

Contoh prosedur tersimpan ini menyelesaikan konflik dengan memilih nilai terendah dari jalur /myCustomId.

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


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

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

Setelah kontainer Anda dibuat, Anda harus membuat prosedur tersimpan resolver.

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`
    }
  }
);

Setelah kontainer Anda dibuat, Anda harus membuat prosedur tersimpan resolver.

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)

Setelah kontainer Anda dibuat, Anda harus membuat prosedur tersimpan resolver.

Membuat kebijakan resolusi konflik kustom

Sampel ini menunjukkan cara menyiapkan kontainer dengan kebijakan resolusi konflik penulis-terakhir-menang. Dengan implementasi ini, setiap konflik akan muncul di umpan konflik. Terserah Anda untuk menangani konflik satu per satu dari umpan konflik.

.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) API Asinkron


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

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)

Baca dari umpan konflik

Sampel ini menunjukkan cara membaca dari umpan konflik kontainer. Konflik mungkin muncul di umpan konflik hanya karena beberapa alasan:

  • Konflik tidak diselesaikan secara otomatis
  • Konflik menyebabkan kesalahan dengan prosedur tersimpan yang ditunjuk
  • Kebijakan resolusi konflik diatur ke kustom dan tidak menunjuk prosedur tersimpan untuk menangani konflik

.NET SDK

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

Java SDK

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)

Langkah berikutnya

Mempelajari tentang konsep Azure Cosmos DB berikut ini: