Udostępnij za pośrednictwem


Powiązanie wyzwalacza usługi Azure Database for MySQL dla usługi Azure Functions (wersja zapoznawcza)

Uwaga

Mimo że powiązania danych wejściowych i wyjściowych są obsługiwane we wszystkich planach, powiązanie wyzwalacza usługi Azure Database for MySQL jest dostępne tylko w planach dedykowanych i premium w okresie obowiązywania wersji zapoznawczej.

Powiązania wyzwalacza usługi Azure Database for MySQL monitorują tabelę użytkowników pod kątem zmian (wstawień i aktualizacji) i wywołują funkcję ze zaktualizowanymi danymi wierszy.

Powiązania wyzwalacza usługi Azure Database for MySQL używają danych kolumn i służą az_func_updated_at do monitorowania tabeli użytkowników pod kątem zmian. W związku z tym należy zmienić strukturę tabeli, aby umożliwić śledzenie zmian w tabeli MySQL przed użyciem obsługi wyzwalacza. Śledzenie zmian w tabeli można włączyć za pomocą następującego zapytania. Na przykład włącz ją w Products tabeli:

ALTER TABLE Products
ADD az_func_updated_at TIMESTAMP DEFAULT 
CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

Tabela dzierżaw zawiera wszystkie kolumny odpowiadające kluczowi podstawowemu z tabeli użytkownika i trzy kolejne kolumny: az_func_AttemptCount, az_func_LeaseExpirationTimei az_func_SyncCompletedTime. Jeśli którakolwiek z kolumn klucza podstawowego ma taką samą nazwę, wynik jest komunikatem o błędzie, który wyświetla konflikty. W takim przypadku należy zmienić nazwę wymienionych kolumn klucza podstawowego, aby wyzwalacz działał.

Omówienie funkcji

Po uruchomieniu funkcji wyzwalacza inicjuje dwie oddzielne pętle: pętlę sondowania zmian i pętlę odnawiania dzierżawy. Pętle te są uruchamiane w sposób ciągły do momentu zatrzymania funkcji.

Powiązanie wyzwalacza usługi Azure Database for MySQL używa pętli sondowania do sprawdzania zmian. Pętla sondowania wyzwala funkcję użytkownika po wykryciu zmian. Na wysokim poziomie pętla wygląda następująco:

while (true) {
    1. Get list of changes on table - up to a maximum number controlled by the MySql_Trigger_MaxBatchSize setting
    2. Trigger function with list of changes
    3. Wait for delay controlled by MySql_Trigger_PollingIntervalMs setting
}

Zmiany są przetwarzane w kolejności ich wprowadzania. Najstarsze zmiany są najpierw przetwarzane. Rozważ następujące kwestie dotyczące przetwarzania zmian:

  • Jeśli zmiany wystąpią w wielu wierszach jednocześnie, dokładna kolejność, w jakiej są wysyłane do funkcji, jest oparta na kolejności rosnącej az_func_updated_at kolumny i kolumn klucza podstawowego.
  • Zmiany są wsadowe dla wiersza. Jeśli wiele zmian wystąpi w wierszu między każdą iterację pętli, uwzględniany jest tylko najnowszy wpis zmiany, który istnieje dla tego wiersza.

Uwaga

Obecnie tożsamości zarządzane nie są obsługiwane w przypadku połączeń między usługami Azure Functions i Azure Database for MySQL.

Przykładowe użycie

Więcej przykładów dla wyzwalacza usługi Azure Database for MySQL jest dostępnych w repozytorium GitHub.

W przykładzie Product odwołuje się do klasy i odpowiedniej tabeli bazy danych:

namespace AzureMySqlSamples.Common
{
    public class Product
    {
        public int? ProductId { get; set; }

        public string Name { get; set; }

        public int Cost { get; set; }

        public override bool Equals(object obj)
        {
            if (obj is Product)
            {
                var that = obj as Product;
                return this.ProductId == that.ProductId && this.Name == that.Name && this.Cost == that.Cost;
            }
            return false;
        }
    }
DROP TABLE IF EXISTS Products;

CREATE TABLE Products (
  ProductId int PRIMARY KEY,
  Name varchar(100) NULL,
  Cost int NULL
);

Śledzenie zmian w bazie danych można włączyć, dodając jedną kolumnę do tabeli:

ALTER TABLE <table name>  
ADD COLUMN az_func_updated_at TIMESTAMP 
DEFAULT CURRENT_TIMESTAMP 
ON UPDATE CURRENT_TIMESTAMP;

Wyzwalacz usługi Azure Database for MySQL wiąże się z IReadOnlyList<MySqlChange<T>>elementem , który zawiera listę MySqlChange obiektów. Każdy obiekt ma dwie właściwości:

  • Item: element, który został zmieniony. Typ elementu powinien być zgodny ze schematem tabeli, jak pokazano ToDoItem w klasie.
  • Operation: wartość z MySqlChangeOperation wyliczenia. Możliwa wartość dotyczy Update zarówno wstawiania, jak i aktualizacji.

W poniższym przykładzie pokazano funkcję języka C# , która jest wywoływana w przypadku wystąpienia zmian w Product tabeli:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Extensions.MySql;
using Microsoft.Extensions.Logging;
using AzureMySqlSamples.Common;

namespace AzureMySqlSamples.TriggerBindingSamples
{
        private static readonly Action<ILogger, string, Exception> _loggerMessage = LoggerMessage.Define<string>(LogLevel.Information, eventId: new EventId(0, "INFO"), formatString: "{Message}");

        [Function(nameof(ProductsTrigger))]
        public static void Run(
            [MySqlTrigger("Products", "MySqlConnectionString")]
            IReadOnlyList<MySqlChange<Product>> changes, FunctionContext context)
        {
            ILogger logger = context.GetLogger("ProductsTrigger");
            // The output is used to inspect the trigger binding parameter in test methods.
            foreach (MySqlChange<Product> change in changes)
            {
                Product product = change.Item;
                _loggerMessage(logger, $"Change operation: {change.Operation}", null);
                _loggerMessage(logger, $"Product Id: {product.ProductId}, Name: {product.Name}, Cost: {product.Cost}", null);
            }
        }
}

Przykładowe użycie

Więcej przykładów dla wyzwalacza usługi Azure Database for MySQL jest dostępnych w repozytorium GitHub.

W przykładzie Product odwołuje się do klasy, MySqlChangeProduct klasy, MySqlChangeOperation wyliczenia i odpowiedniej tabeli bazy danych.

W osobnym pliku o nazwie Product.java:

package com.function.Common;

import com.fasterxml.jackson.annotation.JsonProperty;

public class Product {
    @JsonProperty("ProductId")
    private int ProductId;
    @JsonProperty("Name")
    private String Name;
    @JsonProperty("Cost")
    private int Cost;

    public Product() {
    }

    public Product(int productId, String name, int cost) {
        ProductId = productId;
        Name = name;
        Cost = cost;
    }
}

W osobnym pliku o nazwie MySqlChangeProduct.java:

package com.function.Common;

public class MySqlChangeProduct {
    private MySqlChangeOperation Operation;
    private Product Item;

    public MySqlChangeProduct() {
    }

    public MySqlChangeProduct(MySqlChangeOperation operation, Product item) {
        this.Operation = operation;
        this.Item = item;
    }
}

W osobnym pliku o nazwie MySqlChangeOperation.java:

package com.function.Common;

import com.google.gson.annotations.SerializedName;

public enum MySqlChangeOperation {
    @SerializedName("0")
    Update
}
DROP TABLE IF EXISTS Products;

CREATE TABLE Products (
  ProductId int PRIMARY KEY,
  Name varchar(100) NULL,
  Cost int NULL
);

Śledzenie zmian w bazie danych można włączyć, dodając następującą kolumnę do tabeli:

ALTER TABLE <table name>  
ADD COLUMN az_func_updated_at TIMESTAMP 
DEFAULT CURRENT_TIMESTAMP 
ON UPDATE CURRENT_TIMESTAMP;

Wyzwalacz usługi Azure Database for MySQL wiąże się z MySqlChangeProduct[]elementem , który jest tablicą MySqlChangeProduct obiektów. Każdy obiekt ma dwie właściwości:

  • item: element, który został zmieniony. Typ elementu powinien być zgodny ze schematem tabeli, jak pokazano Product w klasie.
  • operation: wartość z MySqlChangeOperation wyliczenia. Możliwa wartość dotyczy Update zarówno wstawiania, jak i aktualizacji.

W poniższym przykładzie przedstawiono funkcję Języka Java wywoływaną w przypadku wystąpienia zmian w Product tabeli:

/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 */

package com.function;

import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.mysql.annotation.MySqlTrigger;
import com.function.Common.MySqlChangeProduct;
import com.google.gson.Gson;

import java.util.logging.Level;

public class ProductsTrigger {
    @FunctionName("ProductsTrigger")
    public void run(
            @MySqlTrigger(
                name = "changes",
                tableName = "Products",
                connectionStringSetting = "MySqlConnectionString")
                MySqlChangeProduct[] changes,
            ExecutionContext context) {

        context.getLogger().log(Level.INFO, "MySql Changes: " + new Gson().toJson(changes));
    }
}

Przykładowe użycie

Więcej przykładów dla wyzwalacza usługi Azure Database for MySQL jest dostępnych w repozytorium GitHub.

W przykładzie Product odwołuje się do tabeli bazy danych:

DROP TABLE IF EXISTS Products;

CREATE TABLE Products (
  ProductId int PRIMARY KEY,
  Name varchar(100) NULL,
  Cost int NULL
);

Śledzenie zmian w bazie danych można włączyć, dodając jedną kolumnę do tabeli:

ALTER TABLE <table name>  
ADD COLUMN az_func_updated_at TIMESTAMP 
DEFAULT CURRENT_TIMESTAMP 
ON UPDATE CURRENT_TIMESTAMP;

Wyzwalacz usługi Azure Database for MySQL wiąże się z Productelementem , który zawiera listę obiektów. Każdy obiekt ma dwie właściwości:

  • item: element, który został zmieniony. Struktura elementu jest zgodna ze schematem tabeli.
  • operation: Możliwa wartość jest Update zarówno dla wstawień, jak i aktualizacji.

W poniższym przykładzie przedstawiono funkcję programu PowerShell wywoływaną w przypadku wystąpienia zmian w Product tabeli.

W poniższym przykładzie przedstawiono powiązanie danych w pliku function.json:

{
    "bindings": [
      {
        "name": "changes",
        "type": "mysqlTrigger",
        "direction": "in",
        "tableName": "Products",
        "connectionStringSetting": "MySqlConnectionString"
      }
    ],
    "disabled": false
  }

W sekcji Konfiguracja opisano te właściwości.

Poniższy przykład to przykładowy kod programu PowerShell dla funkcji w pliku run.ps1:

using namespace System.Net

param($changes)
# The output is used to inspect the trigger binding parameter in test methods.
# Use -Compress to remove new lines and spaces for testing purposes.
$changesJson = $changes | ConvertTo-Json -Compress
Write-Host "MySql Changes: $changesJson"

Przykładowe użycie

Więcej przykładów dla wyzwalacza usługi Azure Database for MySQL jest dostępnych w repozytorium GitHub.

W przykładzie Product odwołuje się do tabeli bazy danych:

DROP TABLE IF EXISTS Products;

CREATE TABLE Products (
  ProductId int PRIMARY KEY,
  Name varchar(100) NULL,
  Cost int NULL
);

Śledzenie zmian w bazie danych można włączyć, dodając jedną kolumnę do tabeli:

ALTER TABLE <table name>  
ADD COLUMN az_func_updated_at TIMESTAMP 
DEFAULT CURRENT_TIMESTAMP 
ON UPDATE CURRENT_TIMESTAMP;

Wyzwalacz usługi Azure Database for MySQL wiąże się z Changeselementem , który jest tablicą obiektów. Każdy obiekt ma dwie właściwości:

  • item: element, który został zmieniony. Struktura elementu jest zgodna ze schematem tabeli.
  • operation: Możliwa wartość jest Update zarówno dla wstawień, jak i aktualizacji.

W poniższym przykładzie przedstawiono funkcję języka JavaScript wywoływaną w przypadku wystąpienia zmian w Product tabeli.

W poniższym przykładzie przedstawiono powiązanie danych w pliku function.json:

{
    "bindings": [
      {
        "name": "changes",
        "type": "mysqlTrigger",
        "direction": "in",
        "tableName": "Products",
        "connectionStringSetting": "MySqlConnectionString",
      }
    ],
    "disabled": false
  }

W sekcji Konfiguracja opisano te właściwości.

Poniższy przykład to przykładowy kod JavaScript dla funkcji w index.js pliku :

module.exports = async function (context, changes) {
    context.log(`MySql Changes: ${JSON.stringify(changes)}`)
}

Przykładowe użycie

Więcej przykładów dla wyzwalacza usługi Azure Database for MySQL jest dostępnych w repozytorium GitHub.

W przykładzie Product odwołuje się do tabeli bazy danych:

DROP TABLE IF EXISTS Products;

CREATE TABLE Products (
  ProductId int PRIMARY KEY,
  Name varchar(100) NULL,
  Cost int NULL
);

Śledzenie zmian w bazie danych można włączyć, dodając jedną kolumnę do tabeli:

ALTER TABLE <table name>  
ADD COLUMN az_func_updated_at TIMESTAMP 
DEFAULT CURRENT_TIMESTAMP 
ON UPDATE CURRENT_TIMESTAMP;

Uwaga

Musisz użyć usługi Azure Functions w wersji 1.22.0b4 dla języka Python.

Wyzwalacz usługi Azure Database for MySQL wiąże się ze zmienną o nazwie Product, która wyświetla listę obiektów. Każdy obiekt ma dwie właściwości:

  • item: element, który został zmieniony. Struktura elementu jest zgodna ze schematem tabeli.
  • operation: Możliwa wartość jest Update zarówno dla wstawień, jak i aktualizacji.

W poniższym przykładzie przedstawiono funkcję języka Python wywoływaną w przypadku wystąpienia zmian w Product tabeli.

Poniższy przykład to przykładowy kod języka Python dla pliku function_app.py:

import json
import logging
import azure.functions as func

app = func.FunctionApp()

# The function is triggered when a change (insert, update)
# is made to the Products table.
@app.function_name(name="ProductsTrigger")
@app.mysql_trigger(arg_name="products",
table_name="Products",
connection_string_setting="MySqlConnectionString")

def products_trigger(products: str) -> None:
logging.info("MySQL Changes: %s", json.loads(products))

Atrybuty

Właściwość atrybutu opis
TableName Wymagany. Nazwa tabeli monitora wyzwalacza.
ConnectionStringSetting Wymagany. Nazwa ustawienia aplikacji zawierającego parametry połączenia dla bazy danych zawierającej tabelę monitorowaną pod kątem zmian. Nazwa ustawienia parametrów połączenia odpowiada ustawieniu aplikacji (w local.settings.json programowania lokalnego), które zawiera parametry połączenia z usługą Azure Database for MySQL.
LeasesTableName Opcjonalny. Nazwa tabeli do przechowywania dzierżaw. Jeśli nie zostanie określony, nazwa to Leases_{FunctionId}_{TableId}.

Adnotacje

W bibliotece środowiska uruchomieniowego funkcji Języka Java użyj @MySQLTrigger adnotacji dotyczącej parametrów, których wartości pochodzą z usługi Azure Database for MySQL. Ta adnotacja obsługuje następujące elementy:

Składnik opis
name Wymagany. Nazwa parametru powiązanego z wyzwalaczem.
tableName Wymagany. Nazwa tabeli monitora wyzwalacza.
connectionStringSetting Wymagany. Nazwa ustawienia aplikacji zawierającego parametry połączenia dla bazy danych zawierającej tabelę monitorowaną pod kątem zmian. Nazwa ustawienia parametrów połączenia odpowiada ustawieniu aplikacji (w local.settings.json programowania lokalnego), które zawiera parametry połączenia z usługą Azure Database for MySQL.
LeasesTableName Opcjonalny. Nazwa tabeli do przechowywania dzierżaw. Jeśli nie zostanie określony, nazwa to Leases_{FunctionId}_{TableId}.

Konfigurowanie

W poniższej tabeli opisano właściwości konfiguracji powiązania ustawione w pliku function.json:

Majątek opis
name Wymagany. Nazwa parametru powiązanego z wyzwalaczem.
type Wymagany. Musi być ustawiona wartość MysqlTrigger.
direction Wymagany. Musi być ustawiona wartość in.
tableName Wymagany. Nazwa tabeli monitora wyzwalacza.
connectionStringSetting Wymagany. Nazwa ustawienia aplikacji zawierającego parametry połączenia dla bazy danych zawierającej tabelę monitorowaną pod kątem zmian. Nazwa ustawienia parametrów połączenia odpowiada ustawieniu aplikacji (w local.settings.json programowania lokalnego), które zawiera parametry połączenia z usługą Azure Database for MySQL.
LeasesTableName Opcjonalny. Nazwa tabeli do przechowywania dzierżaw. Jeśli nie zostanie określony, nazwa to Leases_{FunctionId}_{TableId}.

Opcjonalna konfiguracja

Możesz skonfigurować następujące opcjonalne ustawienia wyzwalacza usługi Azure Database for MySQL na potrzeby lokalnego programowania lub wdrożeń w chmurze.

host.json

W tej sekcji opisano ustawienia konfiguracji dostępne dla tego powiązania w wersji 2.x lub nowszej. Ustawienia w pliku host.json mają zastosowanie do wszystkich funkcji w wystąpieniu aplikacji funkcji. Aby uzyskać więcej informacji na temat ustawień konfiguracji aplikacji funkcji, zobacz host.json dokumentacja usługi Azure Functions.

Ustawienie Domyślny opis
MaxBatchSize 100 Maksymalna liczba zmian przetworzonych przy każdej iteracji pętli wyzwalacza przed wysłaniem ich do wyzwalanej funkcji.
PollingIntervalMs 1000 Opóźnienie w milisekundach między przetwarzaniem każdej partii zmian. (1000 ms to 1 sekunda).
MaxChangesPerWorker 1000 Górny limit liczby oczekujących zmian w tabeli użytkowników dozwolonych dla procesu roboczego aplikacji. Jeśli liczba zmian przekroczy ten limit, może to spowodować zwiększenie skali w poziomie. To ustawienie dotyczy tylko aplikacji funkcji platformy Azure z włączonym skalowaniem opartym na środowisku uruchomieniowym.

Przykładowy plik host.json

Oto przykładowy plik host.json z opcjonalnymi ustawieniami:

{
  "version": "2.0",
  "extensions": {
      "MySql": {
        "MaxBatchSize": 300,
        "PollingIntervalMs": 1000,
        "MaxChangesPerWorker": 100
      }
  },
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    },
    "logLevel": {
      "default": "Trace"
    }
  }
}

local.setting.json

Plik local.settings.json przechowuje ustawienia i ustawienia aplikacji używane przez lokalne narzędzia programistyczne. Ustawienia w pliku local.settings.json są używane tylko wtedy, gdy uruchamiasz projekt lokalnie. Podczas publikowania projektu na platformie Azure należy również dodać wszystkie wymagane ustawienia do ustawień aplikacji dla aplikacji funkcji.

Ważne

Ponieważ plik local.settings.json może zawierać wpisy tajne, takie jak parametry połączenia, nigdy nie należy przechowywać go w repozytorium zdalnym. Narzędzia, które obsługują usługę Azure Functions, zapewniają sposoby synchronizowania ustawień w pliku local.settings.json z ustawieniami aplikacji w aplikacji funkcji, do której wdrożono projekt.

Ustawienie Domyślny opis
MySql_Trigger_BatchSize 100 Maksymalna liczba zmian przetworzonych przy każdej iteracji pętli wyzwalacza przed wysłaniem ich do wyzwalanej funkcji.
MySql_Trigger_PollingIntervalMs 1000 Opóźnienie w milisekundach między przetwarzaniem każdej partii zmian. (1000 ms to 1 sekunda).
MySql_Trigger_MaxChangesPerWorker 1000 Górny limit liczby oczekujących zmian w tabeli użytkowników dozwolonych dla procesu roboczego aplikacji. Jeśli liczba zmian przekroczy ten limit, może to spowodować zwiększenie skali w poziomie. To ustawienie dotyczy tylko aplikacji funkcji platformy Azure z włączonym skalowaniem opartym na środowisku uruchomieniowym.

Przykładowy plik local.settings.json

Oto przykładowy plik local.settings.json z opcjonalnymi ustawieniami:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "MySqlConnectionString": "",
    "MySql_Trigger_MaxBatchSize": 300,
    "MySql_Trigger_PollingIntervalMs": 1000,
    "MySql_Trigger_MaxChangesPerWorker": 100
  }
}

Konfigurowanie śledzenia zmian (wymagane)

Skonfigurowanie śledzenia zmian do użycia z wyzwalaczem usługi Azure Database for MySQL wymaga dodania kolumny w tabeli przy użyciu funkcji. Te kroki można wykonać za pomocą dowolnego narzędzia MySQL, które obsługuje uruchamianie zapytań, w tym programu Visual Studio Code lub narzędzia Azure Data Studio.

Powiązania wyzwalacza usługi Azure Database for MySQL używają danych kolumn i służą az_func_updated_at do monitorowania tabeli użytkowników pod kątem zmian. W związku z tym należy zmienić strukturę tabeli, aby umożliwić śledzenie zmian w tabeli MySQL przed użyciem obsługi wyzwalacza. Śledzenie zmian w tabeli można włączyć za pomocą następującego zapytania. Na przykład włącz ją w Products tabeli:

ALTER TABLE Products;
ADD az_func_updated_at 
TIMESTAMP DEFAULT CURRENT_TIMESTAMP 
ON UPDATE CURRENT_TIMESTAMP;

Tabela dzierżaw zawiera wszystkie kolumny odpowiadające kluczowi podstawowemu z tabeli użytkownika i dwie kolejne kolumny: az_func_AttemptCount i az_func_LeaseExpirationTime. Jeśli którakolwiek z kolumn klucza podstawowego ma taką samą nazwę, wynik jest komunikatem o błędzie, który wyświetla konflikty. W takim przypadku należy zmienić nazwę wymienionych kolumn klucza podstawowego, aby wyzwalacz działał.

Włączanie skalowania opartego na środowisku uruchomieniowym

Opcjonalnie funkcje mogą być skalowane automatycznie na podstawie liczby zmian oczekujących na przetworzenie w tabeli użytkowników. Aby umożliwić prawidłowe skalowanie funkcji w planie Premium podczas korzystania z wyzwalaczy usługi Azure Database for MySQL, należy włączyć monitorowanie skalowania w czasie wykonywania.

  1. W witrynie Azure Portal w aplikacji funkcji wybierz pozycję Konfiguracja.

  2. Na karcie Ustawienia środowiska uruchomieniowego funkcji w obszarze Monitorowanie skalowania środowiska uruchomieniowego wybierz pozycję Włączone.

    Zrzut ekranu przedstawiający obszar witryny Azure Portal umożliwiający skalowanie środowiska uruchomieniowego.

Pomoc techniczna dotycząca ponawiania prób

Ponowne próby uruchamiania

Jeśli podczas uruchamiania wystąpi wyjątek, środowisko uruchomieniowe hosta automatycznie podejmie próbę ponownego uruchomienia odbiornika wyzwalacza ze strategią wycofywania wykładniczego. Te próby będą kontynuowane do momentu pomyślnego uruchomienia odbiornika lub anulowania uruchamiania.

Ponowne próby wyjątku funkcji

Jeśli wystąpi wyjątek w funkcji użytkownika podczas przetwarzania zmian, partia obecnie przetwarzanych wierszy zostanie ponowiona w ciągu 60 sekund. Inne zmiany są przetwarzane normalnie w tym czasie, ale wiersze w partii, które spowodowały wyjątek, są ignorowane do czasu upływu limitu czasu.

Jeśli wykonanie funkcji zakończy się niepowodzeniem pięć kolejnych razy dla określonego wiersza, ten wiersz zostanie zignorowany dla wszystkich przyszłych zmian. Ponieważ wiersze w partii nie są deterministyczne, wiersze w partii zakończone niepowodzeniem mogą skończyć się w różnych partiach w kolejnych wywołaniach. To zachowanie oznacza, że nie wszystkie wiersze w partii, które zakończyły się niepowodzeniem, muszą być ignorowane. Jeśli inne wiersze w partii spowodowały wyjątek, wiersze "dobre" mogą skończyć się w innej partii, która nie zakończy się niepowodzeniem w przyszłych wywołaniach.