SQL Server w logistyce

Idempotencja wysyłki do systemu zewnętrznego w T-SQL: blokada aplikacyjna i indeks filtrowany

Dwa kliknięcia w odstępie sekundy nie powinny wywołać odbiorcy dwa razy. Procedura z blokadą sp_getapplock i odczytem wpisu pilnuje wywołania, a indeks filtrowany pilnuje tabeli wpisów.

sql.server.net.pl/sql-server/
Szafy serwerowe z pomarańczową tarczą i symbolem kłódki
Szafy serwerowe z pomarańczową tarczą i symbolem kłódki
W skrócie
  • Problem: dwie sesje, które wołają procedurę dla tego samego dokumentu, mogą wywołać system zewnętrzny dwa razy, zanim pierwszy zapis będzie widoczny.
  • Wzorzec: sesyjna blokada sp_getapplock z czasem oczekiwania 0, a po niej odczyt wpisu dokumentu: system zewnętrzny jest wołany wyłącznie wtedy, gdy wpisu brak.
  • Pułapka: sam indeks filtrowany odrzuca dopiero zapis po wywołaniu, a wynik poniżej zera nie zawsze oznacza zajęty zasób.
  • Kontrola: dwie sesje w odstępie sekundy: pierwsza dostaje kod 1, druga od razu kod 3, a w systemie zewnętrznym jest jedno wywołanie.

Idempotencja wysyłki do systemu zewnętrznego w T-SQL zaczyna się od prostej sytuacji: dwie osoby klikają wysyłkę tego samego dokumentu w odstępie sekundy, a wywołanie odbiorcy trwa kilka sekund. Gdy procedura nie pilnuje wejścia, odbiorca dostaje ten sam dokument dwa razy.

Procedura bierze sesyjną blokadę aplikacyjną z czasem oczekiwania zero na zasobie zbudowanym z numeru dokumentu. Dopiero po odczycie istniejącego wpisu wywołuje system zewnętrzny. Druga sesja dostaje odpowiedź, że zasób jest zajęty, i niczego nie wywołuje. Unikatowy indeks filtrowany na wpisach w toku pilnuje tabeli, a nie samego wywołania.

To wzorzec z nazwami przykładowymi, a nie opis działania żadnego systemu. Sprawdziliśmy go na SQL Server 2022 przy poziomach zgodności 130 i 160 i za każdym razem dostaliśmy ten sam wynik. Użyte konstrukcje działają od SQL Server 2012 (według dokumentacji).

Dlaczego dwa kliknięcia mogą wysłać dokument dwa razy

Weźmy procedurę, która najpierw czyta wpis dokumentu, a potem wywołuje odbiorcę zewnętrznego. Wpis ze stanem zapisuje dopiero po wywołaniu. Pierwsza sesja czyta brak wpisu i rusza z wywołaniem, które trwa kilka sekund. Druga sesja startuje sekundę później, także czyta brak wpisu, bo pierwsza jeszcze niczego nie zapisała, i wywołuje odbiorcę po raz drugi.

W przykładzie odbiorcę zastępuje druga tabela, w której każde wywołanie zostawia wiersz. Test liczy więc rzeczywiste wywołania, a nie wpisy w tabeli śledzenia. Czas wywołania odtwarza polecenie WAITFOR na pięć sekund. Procedura odpowiada kodami przykładu, które opisuje tabela.

Kody odpowiedzi procedury z przykładu
KodStan wpisu w odpowiedziZnaczenie
1NOWY albo W_TOKUprzyjęto do wysyłki
2GOTOWEdokument już wysłany
3ZAJETEzasób zajęty, bez wywołania

Przykład: dwie osoby otwierają w dwóch kartach ten sam dokument 26-3001 i klikają wysyłkę w odstępie sekundy. Procedura z pierwszego kliknięcia bierze blokadę i wywołuje odbiorcę zewnętrznego, a drugie kliknięcie od razu dostaje odpowiedź, że dokument jest w toku w innym oknie. Po chwili odpytanie kolejki odbiorcy zamyka wpis, a trzecie kliknięcie pokazuje, że dokument został już wysłany, bez nowego wywołania. Administrator sprawdza zapytaniem kontrolnym, że w systemie zewnętrznym jest jedno wywołanie tego dokumentu i jeden wpis w toku.

Tabela wpisów i unikatowy indeks filtrowany

Tabela wpisów łączy numer dokumentu ze statusem, a identyfikator zlecenia u odbiorcy zapisuje w osobnej kolumnie. Status przyjmuje jedną z trzech wartości z tabeli poniżej, więc po awarii wiadomo, na jakim etapie jest dokument. Druga tabela, SystemZewnetrzny, udaje odbiorcę.

Wartości statusu wpisu
StatusZnaczenie
W_TOKUwpis w toku
GOTOWEwpis zakończony
BLAD_ZEWbłąd po stronie odbiorcy

Przykład ilustracyjny. Nazwy statusów są nazwami przykładu.

CREATE TABLE dbo.WysylkaDokumentu (
    Id         int IDENTITY(1,1) PRIMARY KEY,
    DokumentId int         NOT NULL,
    Status     varchar(20) NOT NULL,
    ZlecenieId int         NULL
);
CREATE UNIQUE INDEX UX_WysylkaDokumentu_WToku
    ON dbo.WysylkaDokumentu (DokumentId) WHERE Status = 'W_TOKU';
CREATE TABLE dbo.SystemZewnetrzny (
    ZlecenieId int IDENTITY(1000,1) PRIMARY KEY,
    DokumentId int NOT NULL
);

Indeks filtrowany obejmuje tylko wiersze z warunkiem WHERE Status = 'W_TOKU'. Dokument może mieć więc najwyżej jeden wpis w toku, ale wiele wpisów GOTOWE. Ten sam mechanizm w innym zastosowaniu opisuje artykuł o indeksie filtrowanym przy miękkim usuwaniu.

Próby zapisu do tabeli z indeksem filtrowanym
PróbaWynik
drugi wpis W_TOKU dla tego samego dokumentubłąd 2601
dwa wpisy GOTOWE dla tego samego dokumentuprzeszły
nowy W_TOKU po zamknięciu wpisu w tokuprzyjęty

Wynik prób na przykładowych danych.

Sam indeks nie wystarcza, bo nie widzi wywołania. Wariant bez blokady czyta wpis i wywołuje odbiorcę, a wpis zapisuje dopiero potem. Przy dwóch sesjach obie wywołały system zewnętrzny, więc są w nim dwa wiersze, a indeks odrzucił dopiero drugi zapis błędem 2601, już po wywołaniu.

CREATE PROCEDURE dbo.WyslijBezBlokady @DokumentId int
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @status varchar(20), @zlecenie int;
    SELECT TOP (1) @status = Status, @zlecenie = ZlecenieId
    FROM dbo.WysylkaDokumentu
    WHERE DokumentId = @DokumentId AND Status IN ('GOTOWE', 'W_TOKU')
    ORDER BY CASE Status WHEN 'GOTOWE' THEN 0 ELSE 1 END, Id DESC;
    IF @status IS NULL
    BEGIN
        WAITFOR DELAY '00:00:05';
        INSERT dbo.SystemZewnetrzny (DokumentId) VALUES (@DokumentId);
        SET @zlecenie = SCOPE_IDENTITY();
        INSERT dbo.WysylkaDokumentu (DokumentId, Status, ZlecenieId) VALUES (@DokumentId, 'W_TOKU', @zlecenie);
    END;
    SELECT CASE @status WHEN 'GOTOWE' THEN 2 ELSE 1 END AS Kod,
           ISNULL(@status, 'NOWY') AS StanWpisu, @zlecenie AS ZlecenieId;
END;

Błąd 2601 przerywa tylko polecenie INSERT. Procedura bez TRY...CATCH i bez SET XACT_ABORT ON poszła dalej i zwróciła drugiej sesji kod 1 ze stanem NOWY oraz identyfikatorem drugiego wywołania, choć zapis się nie udał. Wołający widzi więc sukces tam, gdzie odbiorca dostał dokument po raz drugi.

Blokada chroni wywołanie zewnętrzne, a indeks filtrowany chroni tabelę wpisów.

Blokada sesyjna z czasem oczekiwania zero

Procedura bierze blokadę poleceniem sp_getapplock w trybie Exclusive. Właścicielem blokady jest sesja (Session), a czas oczekiwania wynosi 0. Zasób powstaje z przedrostka Wysylka_ i numeru dokumentu, więc każdy dokument ma własną blokadę, a wysyłka jednego dokumentu nie wstrzymuje innych.

Wynik -1 oznacza przekroczenie czasu oczekiwania, czyli zasób zajęty. Przykład odmawia przy każdym wyniku poniżej zera: odpowiada kodem 3 i stanem ZAJETE, bez żadnego wywołania. Wynik -999 oznacza natomiast błąd wywołania, a nie zajęty zasób, i wrócimy do niego w pułapkach.

CREATE PROCEDURE dbo.WyslijDokument @DokumentId int, @Awaria bit = 0
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @zasob nvarchar(255) = N'Wysylka_' + CAST(@DokumentId AS nvarchar(20)),
            @blokada int, @status varchar(20), @zlecenie int;
    EXEC @blokada = sp_getapplock @zasob, 'Exclusive', 'Session', 0;
    IF @blokada < 0 BEGIN SELECT 3 AS Kod, 'ZAJETE' AS StanWpisu, CAST(NULL AS int) AS ZlecenieId; RETURN; END;
    BEGIN TRY
        SELECT TOP (1) @status = Status, @zlecenie = ZlecenieId
        FROM dbo.WysylkaDokumentu
        WHERE DokumentId = @DokumentId AND Status IN ('GOTOWE', 'W_TOKU')
        ORDER BY CASE Status WHEN 'GOTOWE' THEN 0 ELSE 1 END, Id DESC;
        IF @status IS NULL
        BEGIN
            WAITFOR DELAY '00:00:05';
            IF @Awaria = 1 THROW 50001, N'Awaria wywolania', 1;
            INSERT dbo.SystemZewnetrzny (DokumentId) VALUES (@DokumentId);
            SET @zlecenie = SCOPE_IDENTITY();
            INSERT dbo.WysylkaDokumentu (DokumentId, Status, ZlecenieId) VALUES (@DokumentId, 'W_TOKU', @zlecenie);
        END;
        SELECT CASE @status WHEN 'GOTOWE' THEN 2 ELSE 1 END AS Kod,
               ISNULL(@status, 'NOWY') AS StanWpisu, @zlecenie AS ZlecenieId;
    END TRY
    BEGIN CATCH
        EXEC sp_releaseapplock @zasob, 'Session';
        THROW;
    END CATCH;
    EXEC sp_releaseapplock @zasob, 'Session';
END;

Czas zero oznacza, że druga sesja nie ustawia się w kolejce za pierwszą. Dostaje odmowę od razu, więc wołający może natychmiast pokazać komunikat o dokumencie w toku.

W teście dwóch sesji dla jednego dokumentu pierwsza sesja wykonała wywołanie symulowane poleceniem WAITFOR i dostała kod 1 ze stanem NOWY. Druga sesja, uruchomiona sekundę później, wróciła natychmiast z odpowiedzią 3 ZAJETE. W systemie zewnętrznym jest jedno wywołanie, a w tabeli wpisów jeden wpis W_TOKU.

Dwie sesje: pierwsza ma blokadę i wywołuje system, druga dostaje kod 3; bez blokady indeks odrzuca zapis po wywołaniu Schemat pokazuje, że sesja z blokadą wywołuje system zewnętrzny, druga sesja dostaje kod 3, a w wariancie bez blokady indeks odrzuca zapis dopiero po drugim wywołaniu. Blokada chroni wywołanie, a indeks filtrowany chroni tabelę wpisów Dwie sesje wołają procedurę dla tego samego dokumentu; druga startuje po sekundzie. Czas (poglądowo) 0 s 1 s 5 s Sesja A Sesja B (start po 1 s) Wariant bez blokady, sesja B sp_getapplock, czas 0 przyznana odczyt wpisu: brak wywołanie zewnętrzne (5 s) zapis wpisu W_TOKU zwolnienie blokady sp_getapplock, czas 0 wynik -1: zasób zajęty odpowiedź kod 3 ZAJETE bez wywołania odczyt wpisu: brak wywołanie zewnętrzne (5 s) drugie wywołanie zapis W_TOKU błąd 2601 Tabela wpisów indeks filtrowany WHERE Status = 'W_TOKU' odrzucony przyznana odmowa lub błąd wywołanie zewnętrzne
Schemat pokazuje, że sesja z blokadą wywołuje system zewnętrzny, druga sesja dostaje kod 3, a w wariancie bez blokady indeks odrzuca zapis dopiero po drugim wywołaniu.
Wyniki dwóch sesji dla dokumentu 2
SesjaKodStanWpisuZlecenieId
okno 1 (odpowiedź po 5 s)1NOWY1001
okno 2 (od razu)3ZAJETENULL

Identyfikator 1001 dotyczy bazy, w której wcześniej wykonano test sekwencyjny z następnej sekcji; w świeżej bazie będzie 1000.

Zapytanie kontrolne liczy wywołania w odbiorcy i wpisy w toku: dokument 2 przeszedł przez procedurę z blokadą, a dokument 3 przez wariant z samym indeksem.

SELECT DokumentId,
       (SELECT COUNT(*) FROM dbo.SystemZewnetrzny s WHERE s.DokumentId = d.DokumentId) AS WywolaniaZewnetrzne,
       (SELECT COUNT(*) FROM dbo.WysylkaDokumentu w WHERE w.DokumentId = d.DokumentId AND w.Status = 'W_TOKU') AS WpisyWToku
FROM (VALUES (2), (3)) AS d(DokumentId);
Wynik zapytania kontrolnego
DokumentIdWywolaniaZewnetrzneWpisyWToku
211
321

Dokument 2: procedura z blokadą. Dokument 3: wariant z samym indeksem filtrowanym.

Porównanie dwóch wariantów procedury
Wariant proceduryDruga sesja po sekundzieWywołania w systemie zewnętrznymWpisy W_TOKU
blokada z czasem 0 i odczyt wpisuod razu kod 3 ZAJETE, bez wywołania11
sam unikatowy indeks filtrowanybłąd 2601 przy zapisie, już po wywołaniu21

Wynik testu dwóch sesji w odstępie sekundy.

Siatka wyników: sesja pierwsza kod 1 NOWY, druga kod 3 ZAJETE oraz kontrola liczby wywołań zewnętrznych Makieta poglądowa, dane przykładowe. Siatki wyników obu sesji i zapytania kontrolnego, w którym wariant bez blokady ma dwa wywołania zewnętrzne. Wyniki Sesja 1, po 5 s Kod StanWpisu ZlecenieId 1 NOWY 1001 Sesja 2, od razu Kod StanWpisu ZlecenieId 3 ZAJETE NULL Kontrola DokumentId WywolaniaZewnetrzne WpisyWToku 2 1 1 3 2 1 wariant bez blokady Makieta poglądowa, dane przykładowe
Makieta poglądowa, dane przykładowe. Siatki wyników obu sesji i zapytania kontrolnego, w którym wariant bez blokady ma dwa wywołania zewnętrzne.

Odczyt istniejącego wpisu przed wywołaniem

Po zdobyciu blokady procedura czyta wpis dokumentu, przy czym GOTOWE ma pierwszeństwo przed W_TOKU. Brak wpisu oznacza wywołanie odbiorcy. Wpis W_TOKU daje kod 1 z tym samym identyfikatorem zlecenia, bez wywołania. Wpis GOTOWE daje kod 2, także bez wywołania.

Test sekwencyjny jednego dokumentu pokazuje trzy stany po kolei. Trzecie wywołanie następuje po zamknięciu wpisu, które zwykle robi odpytanie kolejki odbiorcy, a w teście robi je polecenie UPDATE. Liczba wywołań w systemie zewnętrznym wynosi przez cały test jeden.

EXEC dbo.WyslijDokument @DokumentId = 1;
EXEC dbo.WyslijDokument @DokumentId = 1;
UPDATE dbo.WysylkaDokumentu SET Status = 'GOTOWE' WHERE ZlecenieId = 1000 AND Status = 'W_TOKU';
EXEC dbo.WyslijDokument @DokumentId = 1;
Wyniki testu sekwencyjnego dla dokumentu 1
WywołanieKodStanWpisuZlecenieId
pierwsze1NOWY1000
drugie1W_TOKU1000
trzecie, po zamknięciu wpisu2GOTOWE1000

Wynik testu sekwencyjnego; w świeżej bazie identyfikator wynosi 1000.

Od strony użytkownika wysyłkę przez kolejkę z licznikiem na ekranie opisuje strona o wysyłce dokumentu do ERP przez kolejkę. Kolejkę dogania proces opisany na stronie o monitorze kolejki ERP. Ten artykuł zostaje przy samej procedurze.

Zwolnienie blokady w każdej ścieżce, także po błędzie

Blokada sesyjna trwa do jawnego zwolnienia albo do końca połączenia. Procedura zwalnia ją więc na końcu i w bloku CATCH przed THROW. W teście awarii wywołania w środku bloku TRY wołający dostał błąd 50001, funkcja APPLOCK_MODE zwróciła NoLock, a wpisów w tabeli było zero.

BEGIN TRY EXEC dbo.WyslijDokument @DokumentId = 4, @Awaria = 1; END TRY
BEGIN CATCH SELECT ERROR_NUMBER() AS Blad; END CATCH;
SELECT APPLOCK_MODE('public', N'Wysylka_4', 'Session') AS TrybBlokady;

Zapis do dziennika i kolejność w bloku CATCH opisuje artykuł o TRY CATCH i THROW po ROLLBACK, a bufor błędów, który przeżywa wycofanie transakcji, artykuł o zmiennej tabelowej a ROLLBACK. Granice samego zwolnienia blokady zebraliśmy w tabeli, bo zależą od kontekstu wołającego.

Pułapki sprawdzone uruchomieniem
SytuacjaCo się dzieje
właściciel Transaction (domyślny) poza transakcjąwynik -999 i komunikat o braku aktywnej transakcji, a nie błąd; procedura odpowiedziałaby kodem 3, choć nikt nie trzyma zasobu
błąd przerywa procedurę bez własnego CATCH, a wołający ma TRY...CATCH albo XACT_ABORT ONblokada zostaje (APPLOCK_MODE pokazuje Exclusive) do ręcznego sp_releaseapplock
błąd przerywa tylko polecenie, bo wołający nie ma ani TRY...CATCH, ani XACT_ABORT ONprocedura idzie dalej i sama zwalnia blokadę (NoLock)
ta sama sesja bierze blokadę dwa razyoba wywołania zwracają 0; po pierwszym zwolnieniu tryb nadal Exclusive, NoLock dopiero po drugim
procedura utworzona w sesji z QUOTED_IDENTIFIER OFFpóźniejszy INSERT do tabeli z indeksem filtrowanym kończy się błędem 1934

Przykład ilustracyjny. Zachowanie zależy od kontekstu wołającego, więc nie jest regułą ogólną.

Ostatnia pułapka dotyczy narzędzia, nie kodu. Indeks filtrowany wymaga prawidłowych opcji sesji przy zapisie, a procedura zapamiętuje ustawienie QUOTED_IDENTIFIER z chwili utworzenia. Przykład uruchomiliśmy z tym ustawieniem włączonym, a program sqlcmd potrzebuje do tego przełącznika -I. Procedurę utworzoną bez niego i wywołaną później z sesji z ustawieniem włączonym zatrzymał błąd 1934. Mechanizm opisuje artykuł o procedurze z QUOTED_IDENTIFIER OFF oraz dokumentacja SET QUOTED_IDENTIFIER.

Blokada na grupę dokumentów i porównanie z UPDATE z OUTPUT

Zasób blokady nie musi dotyczyć jednego dokumentu. Blokada na grupę, na przykład z numerem zamówienia w nazwie zasobu, może czekać zamiast odmawiać. W teście sesja A trzymała zasób cztery sekundy, a sesja B z czasem 10000 ms czekała około trzech sekund i dostała wynik 1, czyli przyznanie po oczekiwaniu. Z czasem 0 ta sama sesja dostała -1 od razu. Wynik 0 lub wyższy oznacza przyznanie blokady.

Porównajmy wzorzec z jednym poleceniem UPDATE z OUTPUT. Dwa dokumenty oznaczone jako wysłane jednym poleceniem po awarii wywołania pozostały oznaczone, więc do ponowienia nie zostało nic (liczba 0). To semantyka co najwyżej raz, bez stanów pośrednich, którą opisuje artykuł o znaczniku wysłano w jednym UPDATE. Wpis ze stanami pozwala odróżnić dokument w toku od zakończonego i od błędu.

Blokadę całego przebiegu cyklicznego z tabelą kontrolną zdarzeń pokazuje artykuł o jednym mailu na zdarzenie, a ponowienia z limitem prób artykuł o klasyfikacji błędów i ponowieniach wysyłki. Opisany tu wzorzec sam niczego nie ponawia.

Granica wzorca leży po wywołaniu. Gdy błąd pojawi się po wywołaniu, a przed zapisem wpisu, procedura nie wie, czy dokument powstał u odbiorcy. W teście wariantu bez blokady wywołanie poszło, a wpisu brak. Ten przypadek opisuje strona o stanie nierozstrzygniętym po wysłaniu dokumentu do ERP. Wszystkie artykuły sekcji zebraliśmy w spisie artykułów o SQL Server.

Cztery pytania przed budową własnej procedury wysyłki

Zanim przeniesiesz wzorzec do własnej bazy, odpowiedz sobie na cztery pytania.

  • Równoległość: co robi procedura wysyłająca dokument, gdy dwie sesje wywołają ją dla tego samego dokumentu w tej samej sekundzie.
  • Kolejność zapisu: czy wiersz śledzenia powstaje przed wywołaniem zewnętrznym, czy po nim, i co wtedy chroni unikatowy indeks.
  • Zwolnienie blokady: kto zwalnia blokadę, gdy procedura kończy się błędem, i jak to sprawdzić w tej samej sesji.
  • Liczba wywołań: skąd wiadomo, ile razy system zewnętrzny naprawdę odebrał dany dokument.
FAQ

Najczęstsze pytania o idempotencję wysyłki w T-SQL

01

Czy sam unikatowy indeks filtrowany wystarczy, żeby nie wysłać dokumentu dwa razy?

Nie. Indeks chroni tabelę wpisów, ale odrzuca dopiero zapis, który następuje po wywołaniu: w wariancie bez blokady obie sesje wywołały system zewnętrzny, a indeks odrzucił drugi zapis błędem 2601. Procedura bez TRY...CATCH i bez SET XACT_ABORT ON poszła dalej i zwróciła drugiej sesji kod 1 mimo nieudanego zapisu.

02

Dlaczego blokada ma czas oczekiwania zero?

Z czasem zero druga sesja dostaje wynik -1 od razu i odpowiada kodem 3 ze stanem ZAJETE, bez żadnego wywołania. W teście druga sesja, uruchomiona sekundę po pierwszej, wróciła natychmiast, a w systemie zewnętrznym było jedno wywołanie.

03

Co się dzieje z blokadą, gdy w procedurze wystąpi błąd?

Blokada sesyjna trwa do jawnego zwolnienia albo do końca połączenia, więc procedura zwalnia ją w CATCH przed THROW. Gdy błąd przerwie procedurę bez własnego CATCH, a wołający ma TRY...CATCH albo XACT_ABORT ON, blokada zostaje do ręcznego sp_releaseapplock. Blokada jest też liczona: dwa wzięcia wymagają dwóch zwolnień.

04

Kiedy blokada na grupę dokumentów powinna czekać zamiast odmawiać?

Gdy zasób dotyczy grupy, na przykład numeru zamówienia, czas oczekiwania może być dodatni. W teście sesja z czasem 10000 ms czekała około trzech sekund i dostała wynik 1, a z czasem 0 dostała -1 od razu. Wynik 0 lub wyższy oznacza przyznanie blokady.

05

Czym ten wzorzec różni się od jednego polecenia UPDATE z OUTPUT?

UPDATE z OUTPUT oznacza dokument jednym poleceniem, więc po awarii wywołania dokument zostaje oznaczony i do ponowienia nie zostaje nic. Wzorzec z wpisem ma stany W_TOKU, GOTOWE i BLAD_ZEW, więc po awarii wiadomo, co poszło. Przypadek, gdy wywołanie poszło, a zapis wpisu zawiódł, pozostaje poza wzorcem.

Słownik pojęć

Słownik pojęć

Pojęcia użyte we wzorcu idempotentnej wysyłki.

IIdempotencja
Własność operacji, której powtórzenie daje ten sam skutek co jedno wykonanie. We wzorcu dwa wywołania procedury dla tego samego dokumentu kończą się jednym wywołaniem systemu zewnętrznego.
Ssp_getapplock
Procedura systemowa, która zakłada blokadę na zasobie o dowolnej nazwie. We wzorcu bierze ją w trybie Exclusive, z właścicielem Session i czasem oczekiwania 0, na zasobie z numerem dokumentu.
BBlokada sesyjna
Blokada aplikacyjna z właścicielem Session, która trwa do jawnego zwolnienia albo do końca połączenia. We wzorcu procedura zwalnia ją na końcu i w bloku CATCH przed THROW.
IIndeks filtrowany
Indeks obejmujący tylko wiersze spełniające warunek po WHERE. We wzorcu unikatowy indeks z warunkiem Status = W_TOKU dopuszcza najwyżej jeden wpis w toku na dokument.
AAPPLOCK_MODE
Funkcja zwracająca tryb blokady aplikacyjnej na zasobie, na przykład Exclusive albo NoLock. We wzorcu potwierdza, że po awarii blokada została zwolniona.
KKolejka odbiorcy
Kolejka po stronie systemu zewnętrznego, której odpytanie zamyka wpis dokumentu. We wzorcu zamknięcie zmienia status W_TOKU na GOTOWE, a kolejne wywołanie dostaje kod 2.
BBlokada liczona
Cecha blokady sesyjnej: ta sama sesja może wziąć ją wielokrotnie, a każde wzięcie wymaga osobnego zwolnienia. W teście tryb Exclusive zniknął dopiero po drugim zwolnieniu.
QQUOTED_IDENTIFIER
Opcja sesji zapamiętywana razem z procedurą. Tabela z indeksem filtrowanym wymaga jej włączenia, a procedura utworzona z wyłączoną opcją kończy zapis błędem 1934.

Zobacz to w praktyce

Sprawdź, jak systemy SoftwareStudio oparte na MS SQL Server wspierają ten obszar Twojej firmy.