Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Catatan
Pencarian Azure AI tersedia melalui portal Azure, REST API, dan Azure SDK. Ini juga mendukung Foundry IQ, lapisan pengetahuan terkelola yang mengubah konten perusahaan menjadi pangkalan pengetahuan yang dapat digunakan kembali dan sadar izin untuk agen di portal Microsoft Foundry.
Saat mengelola sumber daya Pencarian Azure AI seperti indeks dan sumber data, penting untuk memperbarui sumber daya dengan aman, terutama jika sumber daya diakses secara bersamaan oleh berbagai komponen aplikasi Anda.
- Operasi pembaruan sumber daya mungkin tidak segera selesai. Misalnya, memperbarui indeks atau pengindeks mungkin membutuhkan waktu beberapa detik untuk diselesaikan. Pembaruan sumber daya diserialisasikan, yang berarti beberapa operasi pembaruan mungkin tidak berjalan secara bersamaan pada sumber daya yang sama.
- Ketika dua klien secara bersamaan memperbarui sumber daya tanpa koordinasi, kondisi balapan dimungkinkan. Satu klien dapat memulai operasi pembaruan sementara klien lain mengalami kesalahan konflik. Untuk mencegah hal ini, Pencarian Azure AI mendukung model konkurensi optimistik. Tidak ada kunci pada sumber daya. Sebagai gantinya, setiap sumber daya memiliki ETag yang mengidentifikasi versi sumber daya, sehingga Anda dapat menyusun permintaan untuk menghindari perubahan data yang tidak disengaja.
Cara kerjanya
Konkurensi optimis diimplementasikan melalui pemeriksaan kondisi akses dalam penulisan panggilan API ke indeks, pengindeks, sumber data, set keterampilan, basis pengetahuan, dan sumber daya synonym map.
Semua sumber daya memiliki tag entitas (ETag) yang menyediakan informasi versi objek. Dengan memeriksa ETag terlebih dahulu, Anda dapat menghindari pembaruan bersamaan dalam alur kerja biasa (dapatkan, ubah secara lokal, perbarui) dengan memastikan ETag sumber daya cocok dengan salinan lokal Anda.
REST API menggunakan ETag pada header permintaan.
SDK Azure untuk .NET mengatur ETag melalui kelas accessCondition, mengatur If-Match | If-Match-None header pada sumber daya. Objek yang menggunakan ETag, seperti SynonymMap.ETag dan SearchIndex.ETag, memiliki kelas accessCondition.
Setiap kali Anda memperbarui sumber daya, ETag-nya berubah secara otomatis. Saat Anda menerapkan manajemen konkurensi, yang Anda lakukan hanyalah menempatkan prasyarat pada permintaan pembaruan yang memerlukan sumber daya jarak jauh untuk memiliki ETag yang sama dengan salinan sumber daya yang Anda modifikasi pada klien. Jika proses lain mengubah sumber daya jarak jauh, ETag tidak cocok dengan prasyarat dan permintaan gagal dengan HTTP 412 atau 409. Jika Anda menggunakan SDK .NET, kegagalan ini bermanifestasi sebagai pengecualian di mana metode ekstensi IsAccessConditionFailed() mengembalikan true.
Catatan
Hanya ada satu mekanisme untuk konkurensi. Ini selalu digunakan terlepas dari API atau SDK mana yang digunakan untuk pembaruan sumber daya. Mulai 18 Juli 2025, Pencarian Azure AI mulai memberlakukan serialisasi untuk operasi pembuatan dan pembaruan indeks untuk memastikan konsistensi dan keandalan.
Contoh
Kode berikut menunjukkan konkurensi optimistik untuk operasi pemutakhiran. Pembaruan kedua gagal karena ETag objek diubah oleh pembaruan sebelumnya. Lebih khusus lagi, ketika ETag di header permintaan tidak lagi cocok dengan ETag objek, search service mengembalikan kode status 400 (permintaan buruk), dan pembaruan gagal.
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using System;
using System.Net;
using System.Threading.Tasks;
namespace AzureSearch.SDKHowTo
{
class Program
{
// This sample shows how ETags work by performing conditional updates and deletes
// on an Azure Search index.
static void Main(string[] args)
{
string serviceName = "PLACEHOLDER FOR YOUR SEARCH SERVICE NAME";
string apiKey = "PLACEHOLDER FOR YOUR SEARCH SERVICE ADMIN API KEY";
// Create a SearchIndexClient to send create/delete index commands
Uri serviceEndpoint = new Uri($"https://{serviceName}.search.windows.net/");
AzureKeyCredential credential = new AzureKeyCredential(apiKey);
SearchIndexClient adminClient = new SearchIndexClient(serviceEndpoint, credential);
// Delete index if it exists
Console.WriteLine("Check for index and delete if it already exists...\n");
DeleteTestIndexIfExists(adminClient);
// Every top-level resource in Azure Search has an associated ETag that keeps track of which version
// of the resource you're working on. When you first create a resource such as an index, its ETag is
// empty.
SearchIndex index = DefineTestIndex();
Console.WriteLine(
$"Test searchIndex hasn't been created yet, so its ETag should be blank. ETag: '{index.ETag}'");
// Once the resource exists in Azure Search, its ETag is populated. Make sure to use the object
// returned by the SearchIndexClient. Otherwise, you will still have the old object with the
// blank ETag.
Console.WriteLine("Creating index...\n");
index = adminClient.CreateIndex(index);
Console.WriteLine($"Test index created; Its ETag should be populated. ETag: '{index.ETag}'");
// ETags prevent concurrent updates to the same resource. If another
// client tries to update the resource, it will fail as long as all clients are using the right
// access conditions.
SearchIndex indexForClientA = index;
SearchIndex indexForClientB = adminClient.GetIndex("test-idx");
Console.WriteLine("Simulating concurrent update. To start, clients A and B see the same ETag.");
Console.WriteLine($"ClientA ETag: '{indexForClientA.ETag}' ClientB ETag: '{indexForClientB.ETag}'");
// indexForClientA successfully updates the index.
indexForClientA.Fields.Add(new SearchField("a", SearchFieldDataType.Int32));
indexForClientA = adminClient.CreateOrUpdateIndex(indexForClientA);
Console.WriteLine($"Client A updates test-idx by adding a new field. The new ETag for test-idx is: '{indexForClientA.ETag}'");
// indexForClientB tries to update the index, but fails due to the ETag check.
try
{
indexForClientB.Fields.Add(new SearchField("b", SearchFieldDataType.Boolean));
adminClient.CreateOrUpdateIndex(indexForClientB);
Console.WriteLine("Whoops; This shouldn't happen");
Environment.Exit(1);
}
catch (RequestFailedException e) when (e.Status == 400)
{
Console.WriteLine("Client B failed to update the index, as expected.");
}
// Uncomment the next line to remove test-idx
//adminClient.DeleteIndex("test-idx");
Console.WriteLine("Complete. Press any key to end application...\n");
Console.ReadKey();
}
private static void DeleteTestIndexIfExists(SearchIndexClient adminClient)
{
try
{
if (adminClient.GetIndex("test-idx") != null)
{
adminClient.DeleteIndex("test-idx");
}
}
catch (RequestFailedException e) when (e.Status == 404)
{
//if an exception occurred and status is "Not Found", this is working as expected
Console.WriteLine("Failed to find index and this is because it's not there.");
}
}
private static SearchIndex DefineTestIndex() =>
new SearchIndex("test-idx", new[] { new SearchField("id", SearchFieldDataType.String) { IsKey = true } });
}
}
Pola rancangan
Pola desain untuk menerapkan konkurensi optimis harus mencakup loop yang mencoba kembali pemeriksaan kondisi akses, pengujian untuk kondisi akses, dan secara opsional mengambil ulang sumber daya yang diperbarui sebelum mencoba menerapkan kembali perubahan.
Cuplikan kode ini menggambarkan penambahan synonymMap ke indeks yang sudah ada.
Cuplikan mendapatkan indeks sampel hotel, memeriksa versi objek pada operasi pembaruan, melempar pengecualian jika kondisi gagal, dan kemudian mencoba kembali operasi tersebut (hingga tiga kali), dimulai dengan pengambilan indeks dari server untuk mendapatkan versi terbaru.
private static void EnableSynonymsInHotelsIndexSafely(SearchIndexClient indexClient)
{
int MaxNumTries = 3;
for (int i = 0; i < MaxNumTries; ++i)
{
try
{
SearchIndex index = indexClient.GetIndex("hotels-sample");
index = AddSynonymMapsToFields(index);
// The onlyIfUnchangedcondition ensures that the index is updated only if the ETags match.
indexClient.CreateOrUpdateIndex(index, onlyIfUnchanged: true);
Console.WriteLine("Updated the index successfully.\n");
break;
}
catch (RequestFailedException e) when (e.Status == 412)
{
Console.WriteLine($"Index update failed : {e.Message}. Attempt({i}/{MaxNumTries}).\n");
}
}
}
private static SearchIndex AddSynonymMapsToFields(SearchIndex index)
{
index.Fields.First(f => f.Name == "category").SynonymMapNames.Add("desc-synonymmap");
index.Fields.First(f => f.Name == "tags").SynonymMapNames.Add("desc-synonymmap");
return index;
}