Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka:
Azure AI Vyhledávač je k dispozici prostřednictvím portálu Azure, rozhraní REST API a Sady Azure SDK. Podporuje také Foundry IQ, spravovanou znalostní vrstvu, která transformuje podnikový obsah na opakovaně použitelné znalostní báze s podporou oprávnění pro agenty na portálu Microsoft Foundry.
Při správě Azure AI Vyhledávač prostředků, jako jsou indexy a zdroje dat, je důležité bezpečně aktualizovat prostředky, zejména pokud se k prostředkům přistupuje souběžně různými komponentami vaší aplikace.
- Operace aktualizace prostředků se nemusí dokončit okamžitě. Například dokončení aktualizace indexu nebo indexeru může trvat několik sekund. Aktualizace prostředků jsou serializovány, což znamená, že více operací aktualizace nemusí běžet současně na stejném prostředku.
- Pokud dva klienti současně aktualizují prostředek bez koordinace, je možná závodní podmínka. Jeden klient může spustit operaci aktualizace, zatímco druhý klient vyvolá konfliktní chybu. Pokud tomu chcete zabránit, Azure AI Vyhledávač podporuje optimistický model souběžnosti. Prostředek nemá žádné zámky. Místo toho existuje značka ETag pro každý prostředek, který identifikuje verzi prostředku, abyste mohli formulovat požadavky, které zabrání náhodnému přepsání.
Jak to funguje
Optimistická souběžnost je implementována prostřednictvím kontrol podmínek přístupu během volání API pro zápis do indexů, indexerů, zdrojů dat, sad dovedností, znalostních bází a prostředků synonymMap.
Všechny prostředky mají značku entity (ETag), která poskytuje informace o verzi objektu. Nejprve kontrolou značky ETag se můžete vyhnout souběžným aktualizacím v typickém pracovním postupu (načíst, upravit lokálně, aktualizovat) tím, že zajistíte, aby značka ETag prostředku odpovídala vaší místní kopii.
Rozhraní REST API používá v hlavičce požadavku ETag.
Sada Azure SDK pro .NET nastaví ETag prostřednictvím třídy accessCondition a nastaví If-Match | If-Match-None header on the resource. Objekty, které používají značky ETag, například SynonymMap.ETag a SearchIndex.ETag, mají třídu accessCondition.
Při každé aktualizaci prostředku se jeho značka ETag automaticky změní. Když implementujete konkurenční řízení, jde o to, že žádost o aktualizaci musí vyhovovat podmínce, aby vzdálený prostředek měl stejnou značku ETag jako kopie prostředku, kterou jste upravili na straně klienta. Pokud jiný proces změní vzdálený prostředek, značka ETag neodpovídá předpokladu a požadavek selže s HTTP 412 nebo 409. Pokud používáte sadu .NET SDK, zobrazí se tato chyba jako výjimka, kdy metoda rozšíření IsAccessConditionFailed() vrátí hodnotu true.
Poznámka:
Existuje pouze jeden mechanismus pro souběžnost. Vždy se používá bez ohledu na to, které rozhraní API nebo sada SDK se používají pro aktualizace prostředků. Od 18. července 2025 začal Azure AI Vyhledávač vynucovat serializaci pro vytváření a aktualizace indexů, aby se zajistila konzistence a spolehlivost.
Příklad
Následující kód ukazuje optimistickou souběžnost operace aktualizace. Druhá aktualizace selže, protože ETag objektu byl změněn předchozí aktualizací. Konkrétně platí, že pokud se značka ETag v hlavičce požadavku již neshoduje se značkou ETag objektu, vyhledávací služba vrátí stavový kód 400 (chybný požadavek) a aktualizace selže.
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 } });
}
}
Návrhový vzor
Vzor návrhu pro implementaci optimistické souběžnosti by měl zahrnovat cyklus, který opakuje kontrolu přístupové podmínky, test přístupové podmínky a volitelně načte aktualizovaný prostředek, než se pokusíte změny znovu použít.
Tento fragment kódu znázorňuje přidání synonymMap k indexu, který již existuje.
Fragment kódu získá index ukázek hotelů, zkontroluje verzi objektu v operaci aktualizace, vyvolá výjimku, pokud podmínka selže, a pak opakuje operaci (až třikrát), počínaje načtením indexu ze serveru získat nejnovější verzi.
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;
}