Az egyidejűség kezelése az Azure AI Keresésben

Megjegyzés

Azure AI Keresés a Azure portálon, REST API-kon és Azure SDK-k keresztül érhető el. Emellett a Foundry IQ-t is támogatja, amely a vállalati tartalmakat újrafelhasználható, engedélytudatos tudásbázisokká alakítja át az Microsoft Foundry portálon található ügynökök számára.

Az Azure AI Keresés erőforrások, például indexek és adatforrások kezelésekor fontos az erőforrások biztonságos frissítése, különösen akkor, ha az erőforrásokhoz az alkalmazás különböző összetevői egyszerre férnek hozzá.

  • Előfordulhat, hogy az erőforrás-frissítési műveletek nem fejeződnek be azonnal. Egy index vagy indexelőfrissítése például több másodpercet is igénybe vehet. Az erőforrásfrissítések szerializáltak, ami azt jelenti, hogy több frissítési művelet nem fut egyszerre ugyanazon az erőforráson.
  • Ha két ügyfél egyidejűleg összehangolás nélkül frissít egy erőforrást, lehetséges a versenyfeltétel . Az egyik ügyfél elindíthat egy frissítési műveletet, míg a másik ügyfél ütközési hibát jelez. Ennek elkerülése érdekében Azure AI Keresés támogatja a optimista egyidejűségi modellt. Nincsenek zárolások az erőforráson. Ehelyett minden erőforráshoz elérhető egy ETag, amely azonosítja az erőforrás verzióját, így olyan kéréseket hozhat létre, amelyek elkerülik a véletlen felülírásokat.

Hogyan működik?

Az optimista egyidejűség a hozzáférési feltételek ellenőrzése révén valósul meg az API-hívások során, amelyek indexek, indexelők, adatforrások, képességkészletek, tudásbázisok és szinonimaMappa erőforrások írására szolgálnak.

Minden erőforrás rendelkezik egy entitáscímkével (ETag), amely az objektum verzióadatait tartalmazza. Az ETag első ellenőrzésével elkerülheti az egyidejű frissítéseket egy tipikus munkafolyamatban (lekérés, helyi módosítás, frissítés) úgy, hogy az erőforrás ETagje megegyezik a helyi példányával.

Minden alkalommal, amikor frissít egy erőforrást, az ETag automatikusan megváltozik. Az egyidejűség-kezelés megvalósításakor mindössze egy előfeltételt kell alkalmaznia a frissítési kérelemre, amely megköveteli, hogy a távoli erőforrás ugyanazzal az ETagtel rendelkezzen, mint az ügyfélen módosított erőforrás másolata. Ha egy másik folyamat módosítja a távoli erőforrást, az ETag nem felel meg az előfeltételnek, és a kérés HTTP 412-vel vagy 409-gyel meghiúsul. Ha a .NET SDK-t használja, ez a hiba kivételként jelenik meg, ahol a IsAccessConditionFailed() kiterjesztési metódus igaz értéket ad vissza.

Megjegyzés

Az egyidejűségnek csak egy mechanizmusa van. Mindig azt használják, függetlenül attól, hogy melyik API-t vagy SDK-t használják az erőforrás-frissítésekhez. 2025. július 18-tól Azure AI Keresés megkezdte az indexek létrehozásának és frissítésének szerializálását a konzisztencia és a megbízhatóság biztosítása érdekében.

Példa

Az alábbi kód egy frissítési művelet optimista egyidejűségét mutatja be. A második frissítés meghiúsul, mert az objektum ETagjét egy korábbi frissítés módosította. Pontosabban, ha a kérelem fejlécében lévő ETag már nem felel meg az objektum ETagjének, a search service egy 400-os állapotkódot ad vissza (hibás kérés), és a frissítés meghiúsul.

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

Tervminta

Az optimista egyidejűség megvalósításának tervezési mintájának tartalmaznia kell egy ciklust, amely újrapróbálja a hozzáférési feltétel ellenőrzését, a hozzáférési feltétel tesztelését, és opcionálisan lekéri a frissített erőforrást, mielőtt megkíséreli újra alkalmazni a módosításokat.

Ez a kódrészlet egy szinonimatérkép hozzáadását mutatja be egy már létező indexhez.

A kódrészlet lekéri a hotels-mintaindexet, ellenőrzi az objektumverziót egy frissítési műveleten, kivételt okoz, ha a feltétel meghiúsul, majd újrapróbálkozza a műveletet (legfeljebb háromszor), kezdve az index lekérésével a kiszolgálóról a legújabb verzió beszerzéséhez.

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

Lásd még