Udostępnij przez


Proste funkcje zdefiniowane przez użytkownika w poufnym rejestrze platformy Azure (wersja zapoznawcza)

Proste funkcje zdefiniowane przez użytkownika (UDF) w rejestrze poufnym platformy Azure umożliwiają tworzenie niestandardowych funkcji języka JavaScript, które można wykonywać wewnątrz granicy zaufania rejestru. Ta funkcja została zaprojektowana tak, aby mogła być prosta i łatwa w użyciu, umożliwiając rozszerzanie funkcjonalności interfejsu API rejestru bez konieczności tworzenia złożonych aplikacji.

Za pomocą wbudowanego interfejsu API języka JavaScript można uruchamiać niestandardowy kod w celu wykonywania różnych zadań, takich jak zapytania niestandardowe i obliczenia, kontrole warunkowe, zadania przetwarzania końcowego i nie tylko. Ta funkcja jest odpowiednia w scenariuszach, w których potrzebna jest bezpośrednia integracja z istniejącym interfejsem API rejestru lub uruchamianie uproszczonej logiki niestandardowej w środowisku poufnym.

Aby uzyskać szybki przegląd i pokaz UDF, obejrzyj następujące wideo:

Ważne

Funkcje zdefiniowane przez użytkownika są obecnie dostępne w wersji zapoznawczej w obszarze Wersja 2024-12-09-previewinterfejsu API . Możesz zażądać dostępu do tej wersji zapoznawczej za pośrednictwem tego formularza rejestracji. Zobacz dodatkowe warunki użytkowania dla wersji zapoznawczych platformy Microsoft Azure, aby zapoznać się z postanowieniami prawnymi dotyczącymi funkcji platformy Azure, które są w wersji beta, wersji zapoznawczej lub w inny sposób nie zostały jeszcze wydane w wersji ogólnodostępnej.

Wskazówka

Aby uzyskać bardziej zaawansowane scenariusze, takie jak niestandardowa kontrola dostępu Role-Based (RBAC) lub integracja z zewnętrznymi poufnymi obciążeniami, zobacz zaawansowane funkcje zdefiniowane przez użytkownika w poufnym rejestrze platformy Azure.

Przypadki użycia

Poufne funkcje użytkownika usługi rejestru platformy Azure umożliwiają rozszerzenie funkcjonalności rejestru poprzez uruchamianie logiki niestandardowej. Oto niektóre typowe zastosowania funkcji użytkownika:

  • Niestandardowe obliczenia i zapytania: uruchamiaj autonomiczne funkcje zdefiniowane przez użytkownika, aby odczytywać lub zapisywać dane w dowolnej tabeli aplikacji rejestru zgodnie z logiką biznesową.

  • Walidacja danych i kontrola danych wejściowych: używaj funkcji UDF jako kroków wstępnych, aby uruchamiać procesy wstępnego przetwarzania przed zapisaniem wpisu do rejestru, na przykład aby oczyścić dane wejściowe lub sprawdzić warunki wstępne.

  • Wzbogacanie danych i kontrakty inteligentne: użyj funkcji UDF jako wywołań następczych, aby uruchamiać akcje po zapisaniu wpisu w rejestrze, na przykład w celu dodania niestandardowych metadanych do rejestru lub wyzwalania przepływów pracy po zapisie.

Pisanie funkcji zdefiniowanych przez użytkownika (UDFs)

Poufna funkcja UDF rejestru Azure jest jednostką przechowywaną w rejestrze z unikatowym identyfikatorem i zawiera kod JavaScript, który jest wykonywany, gdy funkcja ta zostaje wywołana. W tej sekcji opisano sposób pisania kodu UDF i używania interfejsu API języka JavaScript do wykonywania różnych zadań.

Struktura funkcji

Kod funkcji zdefiniowanej przez użytkownika wymaga wyeksportowanej funkcji, która jest punktem wejścia skryptu w czasie wykonywania. Podstawowy szablon kodu UDF wygląda następująco:

export function main() {
    // Your JavaScript code here
}

Uwaga / Notatka

Nazwa wyeksportowanej funkcji punktu wejścia, która jest wywoływana podczas wykonywania, może zostać zmodyfikowana za pomocą argumentu exportedFunctionName podczas uruchamiania UDF. Jeśli nie zostanie określona, domyślna nazwa to main.

Uwaga / Notatka

Obsługiwane są funkcje lambda, ale wymagają jawnego zdefiniowania wyeksportowanych nazw funkcji i dopasowania jej do nazwy funkcji punktu wejścia. Przykład:

export const main = () => { 
    // Your JavaScript code here 
};

Argumenty funkcji

Możesz określić dowolne opcjonalne argumenty środowiska uruchomieniowego zaakceptowane przez funkcję zdefiniowanej przez użytkownika. Wartości argumentów można przekazać podczas uruchamiania funkcji zdefiniowanej przez użytkownika w czasie jej wykonywania, przy użyciu parametru arguments.

Argumenty są zawsze przekazywane jako tablica ciągów. Użytkownik jest odpowiedzialny za zapewnienie, że argumenty określone w kodzie UDF są zgodne z argumentami przekazanymi podczas uruchamiania UDF. Użytkownik powinien również upewnić się, że argumenty są prawidłowo parsowane do oczekiwanego typu danych w czasie wykonywania.

export function main(arg1, arg2) {
    // Your JavaScript code here
}

Interfejs API języka JavaScript

Kod JavaScript UDF jest wykonywany w środowisku piaskownicy, która zapewnia ograniczony zestaw interfejsów API.

Można używać wszystkich standardowych funkcji globalnych, obiektów i wartości języka JavaScript . Obiekt globalny o nazwie ccf może służyć do uzyskiwania dostępu do określonych funkcji i narzędzi udostępnianych przez platformę Confidential Consortium Framework (CCF) ( na przykład funkcje pomocnika kryptografii, metody dostępu tabel rejestru itp.). Pełny interfejs API obiektu globalnego ccf jest udokumentowany tutaj.

Dostęp do informacji kontekstowych bieżącego żądania można również uzyskać przy użyciu obiektu globalnego context . Ten obiekt zapewnia dostęp do metadanych żądania, które pochodzą z wykonania funkcji (context.request) i identyfikatora użytkownika obiektu wywołującego funkcję (context.userId). W przypadku hooków transakcji identyfikator kolekcji i zawartość transakcji powiązana z operacją zapisu są również dodawane do context obiektu (context.collectionId i context.contents odpowiednio).

Poniższy fragment kodu przedstawia kilka podstawowych przykładów użycia interfejsu API języka JavaScript:

export function main(args) {
    
    // Basic instructions
    const a = 1 + 1;

    // Basic statements
    if (a > 0) {
        console.log("a is positive");
    } else {
        console.log("a is negative or zero");
    }

    // Parse the string argument as a JSON object
    JSON.parse(args);

    // Logging utilities
    console.log("Hello world");
    
    // Math utilities
    Math.random();
    
    // CCF cryptography utilities
    ccf.crypto.digest("SHA-256", ccf.strToBuf("Hello world"));
    
    // Write to a custom ledger table
    ccf.kv["public:mytable"].set(ccf.strToBuf("myKey"), ccf.strToBuf("myValue"));

    // Read from a custom ledger table
    ccf.bufToStr(ccf.kv["public:mytable"].get(ccf.strToBuf("myKey")));

    // Read from the ledger entry table
    ccf.kv["public:confidentialledger.logs"].get(ccf.strToBuf("subledger:0"));

    // Get the request metadata that originated the function execution
    const requestMetadata = context.request;
    
    // Get the collection ID and transaction content (for transaction hooks only)
    const collectionId = context.collectionId;
    const contents = context.contents;

    // Throw exceptions
    throw new Error("MyCustomError");
}

Wskazówka

Aby uzyskać więcej informacji na temat sposobu użycia map rejestru do przechowywania i pobierania danych, zobacz dokumentację programu CCF dotyczącą interfejsu API magazynu Key-Value.

Uwaga / Notatka

Importowanie modułów nie jest obsługiwane w funkcjach zdefiniowanych przez użytkownika. Kod JavaScript musi być samodzielny i nie może polegać na zewnętrznych bibliotekach ani modułach. Internetowe interfejsy API również nie są obecnie obsługiwane.

Zarządzanie funkcjami zdefiniowanymi przez użytkownika

Aplikacje rejestru poufnego platformy Azure udostępniają dedykowany interfejs API CRUD do tworzenia, odczytywania, aktualizowania i usuwania jednostek UDF. Funkcje zdefiniowane przez użytkownika są bezpiecznie przechowywane w rejestrze i mogą być dostępne wyłącznie dla aplikacji rejestru.

Uwaga / Notatka

Proste funkcje zdefiniowane przez użytkownika i zaawansowane funkcje zdefiniowane przez użytkownika są wzajemnie wykluczające się funkcje. Nie można tworzyć ani uruchamiać prostych UDF, jeśli zdefiniowano zaawansowane UDF, i odwrotnie. Aby przełączyć się między nimi, postępuj zgodnie z instrukcjami na stronie UDF.

Tworzenie lub aktualizowanie funkcji zdefiniowanej przez użytkownika

PUT /app/userDefinedFunctions/myFunction
{
    "code": "export function main() { return "Hello World"; }",
}

Ważne

Do utworzenia lub zaktualizowania UDF wymagana jest rola administratora.

Pobierz funkcję zdefiniowaną przez użytkownika

GET /app/userDefinedFunctions/myFunction

Lista funkcji zdefiniowanych przez użytkownika

GET /app/userDefinedFunctions

Usuń funkcję zdefiniowaną przez użytkownika

DELETE /app/userDefinedFunctions/myFunction

Ważne

Rola administratora jest wymagana do usunięcia funkcji zdefiniowanej przez użytkownika.

Uwaga / Notatka

Usunięcie funkcji zdefiniowanej przez użytkownika (UDF) powoduje, że jednostka zostaje usunięta tylko z aktualnego stanu rejestru. Każda usunięta funkcja zdefiniowana przez użytkownika jest zawsze zachowywana w historii niezmiennej księgi rozrachunkowej (jako każda zatwierdzona transakcja).

Uruchamianie funkcji zdefiniowanych przez użytkownika

Po utworzeniu poufnego rejestru platformy Azure użytkownicy mogą wykonywać funkcję zdefiniowaną przez użytkownika jako funkcję autonomiczną lub jako hak transakcji związany z operacją zapisu. Każde wykonanie funkcji zdefiniowanej przez użytkownika (UDF) jest uruchamiane w oddzielnym środowisku uruchomieniowym i sandboxie, co oznacza, że wykonanie UDF jest odizolowane od innych UDF-ów lub innych operacji rejestru.

Wykonywanie UDF można kontrolować przy użyciu opcjonalnych właściwości, które można określić wewnątrz żądania. Obecnie obsługiwane właściwości to:

  • arguments: tablica ciągów znaków, które reprezentują argumenty przekazywane do funkcji zdefiniowanej przez użytkownika (UDF). Argumenty są przekazywane w tej samej kolejności, w której są zdefiniowane w kodzie UDF. Wartość domyślna to pusta tablica.

  • exportedFunctionName: nazwa wyeksportowanej funkcji, która ma być wywoływana podczas wykonywania. Jeśli nie zostanie określony, wartość domyślna to main.

  • runtimeOptions: obiekt, który określa opcje wykonania dla UDF. Dostępne są następujące opcje:

    • max_heap_bytes: maksymalny rozmiar sterty w bajtach. Wartość domyślna to 10 485 760 (10 MB).

    • max_stack_bytes: maksymalny rozmiar stosu mierzony w bajtach. Wartość domyślna to 1 048 576 (1 MB).

    • max_execution_time_ms: maksymalny czas wykonywania w milisekundach. Wartość domyślna to 1000 (1 sekunda).

    • log_exception_details: wartość logiczna określająca, czy ma być rejestrowane szczegóły wyjątku. Wartość domyślna to true.

    • return_exception_details: wartość logiczna określająca, czy zwracać szczegóły wyjątku w odpowiedzi. Wartość domyślna to true.

Funkcje autonomiczne

Funkcja UDF może być wykonywana bezpośrednio przy użyciu interfejsu POST /app/userDefinedFunctions/{functionId}:execute API.

POST /app/userDefinedFunctions/myFunction:execute
{}

Treść żądania może służyć do określania opcjonalnych parametrów wykonywania, takich jak argumenty funkcji i właściwości środowiska uruchomieniowego języka JavaScript.

POST /app/userDefinedFunctions/myFunction:execute
{
    "arguments": ["arg1", "arg2"],
    "exportedFunctionName": "myMainFunction",
    "runtimeOptions": {
        "max_heap_bytes": 5,
        "max_stack_bytes": 1024,
        "max_execution_time_ms": 5000,
        "log_exception_details": true,
        "return_exception_details": true
    }
}

Odpowiedź wskazuje rezultat wykonania funkcji UDF (powiodło się lub nie powiodło się). Jeśli funkcja UDF powiodła się, odpowiedź zawiera wartość zwróconą przez funkcję w formacie tekstowym (jeśli istnieje).

{
    "result": 
        {
            "returnValue": "MyReturnValue"
        }, 
    "status": "Succeeded"
}

Jeśli funkcja zdefiniowana przez użytkownika nie powiodła się, odpowiedź zawiera komunikat o błędzie ze szczegółowym śladem stosu.

{
    "error": {
        "message": "Error while executing function myFunction: Error: MyCustomError\n    at myMainFunction (myFunction)\n"
    }, 
    "status": "Failed"
}

Ważne

Rola współautora jest wymagana do wykonania funkcji zdefiniowanej przez użytkownika.

Zaczepy transakcji

Funkcja UDF może być również wykonywana jako hak przedwstępny (pre-hook) lub jako hak końcowy (post-hook) po zapisaniu wpisu w księdze w ramach API zapisu księgi (POST /app/transactions). Hooki są uruchamiane w tym samym kontekście co operacja zapisu, co oznacza, że wszystkie dane zapisane w rejestrze przez hooki są automatycznie uwzględniane w tej samej transakcji zapisu.

Treść żądania zapisu może służyć do określenia dowolnych identyfikatorów UDF, które mają być wykonywane jako haki przednie i końcowe.

POST /app/transactions?collectionId=myCollection
{
  "contents": "myValue", 
  "preHooks": [ 
    { 
        "functionId": "myPreHook"
    } 
  ], 
  "postHooks": [ 
    { 
        "functionId": "myPostHook" 
    }
  ] 
} 

Ważne

Hooki muszą być jawnie zdefiniowane w treści żądania dla operacji zapisu. Ogólnie rzecz biorąc, funkcje zdefiniowane przez użytkownika nie mogą być uruchamiane automatycznie dla każdej operacji zapisu po utworzeniu.

Dla każdego haka można określić dowolne opcjonalne właściwości wykonywania. Przykład:

POST /app/transactions?collectionId=myCollection
{
  "contents": "myValue", 
  "preHooks": [ 
    { 
        "functionId": "myPreHook", 
        "properties": { 
            "arguments": [ 
                "arg1",
                "arg2"
            ], 
            "exportedFunctionName": "myMainFunction", 
            "runtimeOptions": { 
                "max_heap_bytes": 5,
                "max_stack_bytes": 1024,
                "max_execution_time_ms": 5000,
                "log_exception_details": true,
                "return_exception_details": true
            } 
        } 
    } 
  ], 
  "postHooks": [ 
    { 
        "functionId": "myPostHook", 
        "properties": { 
            "arguments": [ 
                "arg1"
            ], 
            "exportedFunctionName": "myMainFunction", 
            "runtimeOptions": { 
                "max_heap_bytes": 5,
                "max_stack_bytes": 1024,
                "max_execution_time_ms": 5000,
                "log_exception_details": true,
                "return_exception_details": true
            } 
        } 
    }
  ] 
} 

W treści żądania można określić maksymalnie 5 haków wstępnych i haków końcowych w dowolnej kombinacji. Hooki są zawsze wykonywane w kolejności, w jakiej są podane w treści żądania.

W przypadku niepowodzenia przygotowania wstępnego lub kroku końcowego, cała transakcja zostanie przerwana. W takim przypadku odpowiedź zawiera komunikat o błędzie z przyczyną błędu:

{
    "error": {
        "code": "InternalError",
        "message": "Error while executing function myPreHook: Error: MyCustomError\n    at myMainFunction (myPreHook)\n",
    }
}

Uwaga / Notatka

Nawet jeśli wielokrotne hooki się powiodą, transakcja nadal może zakończyć się niepowodzeniem, jeśli którykolwiek ze zdefiniowanych pre-hooków lub post-hooków nie zostanie pomyślnie uruchomiony do ukończenia.

Wskazówka

Funkcja UDF może być ponownie użyta jako element wstępny i punkt zaczepienia w tym samym żądaniu i wywoływana wiele razy.

Przykłady

W tej sekcji przedstawiono kilka praktycznych przykładów używania funkcji zdefiniowanych przez użytkownika (UDF) w usłudze Azure Confidential Ledger. W poniższych przykładowych scenariuszach przyjęto założenie, że używamy poufnego rejestru platformy Azure do przechowywania transakcji bankowych dla różnych użytkowników banków.

Kontekst

Aby przechować transakcję bankową użytkownika, można użyć istniejącego interfejsu API do zapisu rejestru: wartość transakcji stanowi zawartość wpisu rejestru, a identyfikator użytkownika może być kolekcją lub kluczem, gdzie zawartość jest zapisywana.

POST /app/transactions?collectionId=John
{
    "contents": "10"
}

HTTP/1.1 200 OK

Ponieważ nie ma walidacji zawartości wejściowej, można zapisać wartość nieliczbową jako zawartość. Na przykład to żądanie powiedzie się, nawet jeśli wartość zawartości nie jest liczbą:

POST /app/transactions?collectionId=Mark
{
    "contents": "This is not a number"
}

HTTP/1.1 200 OK

Procedury wstępne na potrzeby walidacji danych

Aby upewnić się, że zawartość transakcji jest zawsze liczbą, można utworzyć funkcję zdefiniowaną przez użytkownika w celu sprawdzenia zawartości wejściowej. Poniższe prehook sprawdza, czy wartość jest liczbą i zgłasza błąd, jeśli nie.

PUT /app/userDefinedFunctions/validateTransaction
{
    "code": "export function main() { if (isNaN(context.contents)) { throw new Error('Contents is not a number'); } }"
}

HTTP/1.1 201 CREATED

Za pomocą wstępnego haka w żądaniu zapisu można wymusić, że dane wejściowe odpowiadają oczekiwanemu formatowi. Poprzednie żądanie kończy się teraz niepowodzeniem zgodnie z oczekiwaniami:

POST /app/transactions?collectionId=Mark
{
    "contents": "This is not a number",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ]
}

HTTP/1.1 500 INTERNAL_SERVER_ERROR
{
  "error": {
    "code": "InternalError",
    "message": "Error while executing function validateTransaction: Error: Contents is not a number\n    at main (validateTransaction)\n"
  }
}

Prawidłowe żądania zawierające wartości liczbowe zakończą się sukcesem zgodnie z oczekiwaniami.

POST /app/transactions?collectionId=Mark
{
    "contents": "30",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ]
}

HTTP/1.1 200 OK

Post-hooks na potrzeby wzbogacania danych

Gdy użytkownicy wykonują nowe transakcje bankowe, chcemy rejestrować, gdy transakcja jest wyższa niż określony próg ze względów inspekcji. Element post-hook może służyć do zapisywania niestandardowych metadanych w rejestrze po operacji zapisu w celu wskazania, czy transakcja była wyższa niż określony próg.

Na przykład można utworzyć funkcję użytkownika w celu sprawdzenia wartości transakcji i zapisania fikcyjnego komunikatu ("Alert" dla wysokich wartości, "Normalny" w przeciwnym razie) przez użytkownika wprowadzającego do niestandardowej tabeli rejestru (payment_metadata), jeśli wartość jest większa niż 50.

PUT /app/userDefinedFunctions/detectHighTransaction
{
    "code": "export function main() { let value = 'Normal'; if (context.contents > 50) { value = 'Alert' } ccf.kv['public:payment_metadata'].set(ccf.strToBuf(context.collectionId), ccf.strToBuf(value)); }"
}

HTTP/1.1 201 CREATED

Po pomyślnym utworzeniu funkcji zdefiniowanej przez użytkownika można go użyć w nowych żądaniach zapisu:

POST /app/transactions?collectionId=Mark
{
    "contents": "100",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ],
    "postHooks": [
        {
            "functionId": "detectHighTransaction"
        }
    ]
}

HTTP/1.1 200 OK
POST /app/transactions?collectionId=John
{
    "contents": "20",
    "preHooks": [
        {
            "functionId": "validateTransaction"
        }
    ],
    "postHooks": [
        {
            "functionId": "detectHighTransaction"
        }
    ]
}

HTTP/1.1 200 OK

Samodzielne funkcje zdefiniowane przez użytkownika dla niestandardowych kwerend

Aby sprawdzić najnowsze wartości zapisane w niestandardowej tabeli payment_metadata przy użyciu procedury post-hook, można utworzyć funkcję zdefiniowaną przez użytkownika w celu odczytania wartości z tabeli przy użyciu wejściowego identyfikatora użytkownika.

PUT /app/userDefinedFunctions/checkPaymentMetadataTable
{
    "code": "export function main(user) { const value = ccf.kv['public:payment_metadata'].get(ccf.strToBuf(user)); if (value === undefined) { throw new Error('UnknownUser'); } return ccf.bufToStr(value); }"
}

HTTP/1.1 201 CREATED

Dzięki bezpośredniemu uruchomieniu zdefiniowanej przez użytkownika funkcji (UDF) można sprawdzić najnowszą wartość zarejestrowaną w niestandardowej tabeli metadanych dla danego użytkownika.

W przypadku użytkowników z niedawną wysoką transakcją funkcja UDF zwraca wartość "Alert", jak oczekiwano.

POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
    "arguments": [
        "Mark"
    ]
}

HTTP/1.1 200 OK
{
  "result": {
    "returnValue": "Alert"
  },
  "status": "Succeeded"
}

W przypadku użytkowników z ostatnimi niskimi transakcjami funkcja UDF zwraca wartość "Normal" zamiast tego.

POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
    "arguments": [
        "John"
    ]
}

HTTP/1.1 200 OK
{
  "result": {
    "returnValue": "Normal"
  },
  "status": "Succeeded"
}

W przypadku użytkowników, którzy nie mają żadnego wpisu w tabeli niestandardowej, funkcja UDF zgłasza błąd zdefiniowany w kodzie UDF.

POST /app/userDefinedFunctions/checkPaymentMetadataTable:execute
{
    "arguments": [
        "Jane"
    ]
}

HTTP/1.1 200 OK
{
  "error": {
    "message": "Error while executing function checkPaymentMetadataTable: Error: UnknownUser\n    at main (checkPaymentMetadataTable)\n"
  },
  "status": "Failed"
}

Rozważania

  • Transakcyjne haki są obecnie obsługiwane tylko dla API POST /app/transactions podczas dodawania nowego wpisu do rejestru.

  • Funkcje zdefiniowane przez użytkownika i haki są zawsze wykonywane w podstawowej repliki rejestru, aby zapewnić kolejność transakcji i silną spójność.

  • Wykonywanie kodu UDF jest zawsze opakowane w jedną transakcję atomową. Jeśli logika języka JavaScript w UDF ukończy się bez żadnych wyjątków, wszystkie operacje w ramach UDF są zatwierdzane w rejestrze. Jeśli zostanie zgłoszony jakikolwiek wyjątek, wszystkie transakcje zostaną wycofane. Podobnie, pre-hooks i post-hooks są wykonywane w tym samym kontekście operacji zapisu, do której są zarejestrowane. Jeśli wstępny lub końcowy punkt kontroli zakończy się niepowodzeniem, cała transakcja zostanie przerwana i do rejestru nie zostanie dodany żaden wpis.

  • Funkcje UDF mogą uzyskiwać dostęp tylko do tabel aplikacji CCF i nie mają dostępu do wewnętrznych tabel rejestru i zarządzania ani innych tabel wbudowanych ze względów bezpieczeństwa. Tabele rejestru, w których wpisy są zapisywane (public:confidentialledger.logs dla rejestrów publicznych i private:confidentialledger.logs dla rejestrów prywatnych) są tylko do odczytu.

  • Maksymalna liczba haków wstępnych i punktów zaczepienia, które można zarejestrować dla jednej transakcji zapisu, wynosi 5.

  • Wykonywanie funkcji UDF i hooka jest ograniczone do 5 sekund. Jeśli wykonanie funkcji trwa dłużej niż 5 sekund, operacja zostanie przerwana i zostanie zwrócony błąd.