Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Примечание.
Поиск с использованием ИИ Azure доступна через портал Azure, REST API и Azure SDKs. Он также лежит в основе Foundry IQ — управляемого слоя знаний, который преобразует корпоративный контент в многократно используемые базы знаний с учетом разрешений доступа для агентов на портале Microsoft Foundry.
При управлении Поиск с использованием ИИ Azure ресурсами, такими как индексы и источники данных, важно безопасно обновлять ресурсы, особенно если ресурсы доступны одновременно различными компонентами приложения.
- Операции обновления ресурсов могут не завершиться немедленно. Например, обновление индекса или индексатора может занять несколько секунд. Обновления ресурсов сериализуются, что означает, что несколько операций обновления могут не выполняться одновременно в одном ресурсе.
- Когда два клиента одновременно обновляют ресурс без координации, возможна ситуация, которую называют состояние гонки. Один клиент может запустить операцию обновления, а другой клиент получает конфликтную ошибку. Чтобы предотвратить это, Поиск с использованием ИИ Azure поддерживает модель параллелизма оптимистической конкуренции. При этом в отношении ресурса нет никаких блокировок. Вместо этого существует ETag для каждого ресурса, который определяет версию ресурса, чтобы можно было сформулировать запросы, которые избегают случайной перезаписи.
Принцип работы
Оптимистичная параллельность реализуется с помощью проверок условий доступа при вызовах API, которые записывают индексы, индексаторы, источники данных, наборы навыков, базы знаний и карты синонимов.
Все ресурсы имеют тег сущности (ETag), который предоставляет сведения о версии объекта. Если сначала проверить ETag, то можно избежать параллельных обновлений в стандартном рабочем процессе (получение, локальное изменение, обновление), убедившись, что ETag ресурса соответствует локальной копии.
REST API использует ETag в заголовке запроса.
Пакет SDK Azure для .NET задает ETag через класс AccessCondition, задав заголовок If-Match | If-None-Match для ресурса. Объекты, использующие ETag, такие как SynonymMap.ETag и SearchIndex.ETag, имеют класс accessCondition.
Каждый раз при обновлении ресурса его ETag изменяется автоматически. При реализации управления параллелизмом все, что необходимо сделать, это поместить предварительное условие в запрос на обновление. Этим условием должно быть требование, чтобы удаленный ресурс имел такой же ETag, как и копия ресурса, измененного вами на клиенте. Если другой процесс изменяет удаленный ресурс, ETag не соответствует предварительным условию, и запрос завершается ошибкой HTTP 412 или 409. Если вы используете пакет SDK .NET, этот сбой манифестирует как исключение, в котором метод расширения IsAccessConditionFailed() возвращает значение true.
Примечание.
Для параллелизма существует только один механизм. Он всегда используется независимо от того, какой API или пакет SDK используется для обновлений ресурсов. Начиная с 18 июля 2025 г. Поиск с использованием ИИ Azure начал применять сериализацию для операций создания и обновления индекса, чтобы обеспечить согласованность и надежность.
Пример
Следующий код демонстрирует оптимистическое параллелизм для операции обновления. Второе обновление завершается сбоем, так как предыдущим обновлением изменяется ETag объекта. В частности, если ETag в заголовке запроса больше не соответствует ETag объекта, служба поиска возвращает код 400 (неправильный запрос), и обновление завершается неудачей.
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 } });
}
}
Конструктивный шаблон
Шаблон проектирования для реализации оптимистического параллелизма должен содержать цикл, который повторяет проверку условия доступа, тест для условия доступа и при необходимости извлекает обновленный ресурс перед попыткой повторного применения изменений.
В этом фрагменте кода показано добавление synonymMap в индекс, который уже существует.
Фрагмент кода получает индекс с примером отелей, проверяет версию объекта в операции обновления, вызывает исключение, если условие завершается сбоем, а затем повторяет операцию (до трех раз), начиная с извлечения индекса с сервера, чтобы получить последнюю версию.
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;
}