مشاركة عبر


ربط مشغل Azure Database for MySQL لوظائف Azure

تراقب روابط مشغل Azure Database for MySQL جدول المستخدم للتغييرات (الإدراج والتحديثات) وتستدعي الدالة مع بيانات الصف المحدثة.

تستخدم az_func_updated_at روابط مشغل Azure Database for MySQL وبيانات العمود لمراقبة جدول المستخدم للتغييرات. على هذا النحو، تحتاج إلى تغيير بنية الجدول للسماح بتعقب التغيير على جدول MySQL قبل استخدام دعم المشغل. يمكنك تمكين تعقب التغيير على جدول من خلال الاستعلام التالي. على سبيل المثال، قم بتمكينه على Products الجدول:

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

يحتوي جدول عقود الإيجار على جميع الأعمدة التي تتوافق مع المفتاح الأساسي من جدول المستخدم وثلاثة أعمدة أخرى: az_func_AttemptCountو az_func_LeaseExpirationTimeو az_func_SyncCompletedTime. إذا كان لأي من أعمدة المفتاح الأساسي نفس الاسم، تكون النتيجة رسالة خطأ تسرد التعارضات. في هذه الحالة، يجب إعادة تسمية أعمدة المفتاح الأساسي المدرجة حتى يعمل المشغل.

نظرة عامة على الوظائف

عند بدء تشغيل دالة المشغل، تبدأ حلقتين منفصلتين: حلقة التحقق من التغيير وحلقة تجديد التأجير. تعمل هذه الحلقات بشكل مستمر حتى يتم إيقاف الدالة.

تستخدم قاعدة بيانات Azure لربط مشغل MySQL حلقة الاستقصاء للتحقق من وجود تغييرات. تقوم حلقة الاستقصاء بتشغيل وظيفة المستخدم عند اكتشاف التغييرات. على مستوى عال، تبدو الحلقة مثل هذا المثال:

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
}

تتم معالجة التغييرات بالترتيب الذي يتم إجراؤها. تتم معالجة أقدم التغييرات أولا. ضع في اعتبارك هذه النقاط حول معالجة التغيير:

  • إذا حدثت تغييرات في صفوف متعددة في وقت واحد، فإن الترتيب الدقيق الذي يتم إرسالها به إلى الدالة يستند إلى الترتيب az_func_updated_at التصاعدي للعمود وأعمدة المفتاح الأساسي.
  • يتم تجميع التغييرات في دفعات لصف. إذا حدثت تغييرات متعددة في صف بين كل تكرار للحلقة، يتم النظر فقط في إدخال التغيير الأخير الموجود لهذا الصف.

إشعار

حاليا، الهويات المدارة غير مدعومة للاتصالات بين Azure Functions وقاعدة بيانات Azure ل MySQL.

استخدام المثال

تتوفر المزيد من العينات لمشغل Azure Database for MySQL في مستودع GitHub.

يشير المثال إلى Product فئة وجدول قاعدة بيانات مطابق:

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

يمكنك تمكين تعقب التغييرات على قاعدة البيانات عن طريق إضافة عمود واحد إلى الجدول:

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

يرتبط مشغل Azure Database for MySQL ب IReadOnlyList<MySqlChange<T>>، الذي يسرد MySqlChange العناصر. يحتوي كل كائن على خاصيتين:

  • Item: العنصر الذي تم تغييره. يجب أن يتبع نوع العنصر مخطط الجدول، كما هو ملاحظ في ToDoItem الفئة.
  • Operation: قيمة من MySqlChangeOperation قائمة التعداد. القيمة المحتملة هي Update لكل من الإدراج والتحديثات.

يوضح المثال التالي دالة C#‎ التي يتم استدعاؤها عند حدوث تغييرات في Product الجدول:

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

استخدام المثال

تتوفر المزيد من العينات لمشغل Azure Database for MySQL في مستودع GitHub.

يشير المثال إلى Product فئة وفئة MySqlChangeProductMySqlChangeOperation تعداد وجدول قاعدة بيانات مطابق.

في ملف منفصل يسمى 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;
    }
}

في ملف منفصل يسمى 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;
    }
}

في ملف منفصل يسمى 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
);

يمكنك تمكين تعقب التغييرات على قاعدة البيانات عن طريق إضافة العمود التالي إلى الجدول:

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

يرتبط مشغل Azure Database for MySQL ب MySqlChangeProduct[]، وهو صفيف من MySqlChangeProduct الكائنات. يحتوي كل كائن على خاصيتين:

  • item: العنصر الذي تم تغييره. يجب أن يتبع نوع العنصر مخطط الجدول، كما هو ملاحظ في Product الفئة.
  • operation: قيمة من MySqlChangeOperation قائمة التعداد. القيمة المحتملة هي Update لكل من الإدراج والتحديثات.

يوضح المثال التالي دالة Java التي يتم استدعاؤها عند حدوث تغييرات في Product الجدول:

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

استخدام المثال

تتوفر المزيد من العينات لمشغل Azure Database for MySQL في مستودع GitHub.

يشير المثال إلى Product جدول قاعدة بيانات:

DROP TABLE IF EXISTS Products;

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

يمكنك تمكين تعقب التغييرات على قاعدة البيانات عن طريق إضافة عمود واحد إلى الجدول:

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

يرتبط مشغل Azure Database for MySQL ب Product، الذي يسرد العناصر. يحتوي كل كائن على خاصيتين:

  • item: العنصر الذي تم تغييره. تتبع بنية العنصر مخطط الجدول.
  • operation: القيمة المحتملة هي Update لكل من الإدراج والتحديثات.

يوضح المثال التالي دالة PowerShell التي يتم استدعاؤها عند حدوث تغييرات في Product الجدول.

المثال التالي هو ربط البيانات في ملف function.json:

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

يشرح قسم التكوين هذه الخصائص.

المثال التالي هو نموذج التعليمات البرمجية PowerShell للدالة في ملف 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"

استخدام المثال

تتوفر المزيد من العينات لمشغل Azure Database for MySQL في مستودع GitHub.

يشير المثال إلى Product جدول قاعدة بيانات:

DROP TABLE IF EXISTS Products;

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

يمكنك تمكين تعقب التغييرات على قاعدة البيانات عن طريق إضافة عمود واحد إلى الجدول:

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

يرتبط مشغل Azure Database for MySQL ب Changes، وهو صفيف من الكائنات. يحتوي كل كائن على خاصيتين:

  • item: العنصر الذي تم تغييره. تتبع بنية العنصر مخطط الجدول.
  • operation: القيمة المحتملة هي Update لكل من الإدراج والتحديثات.

يوضح المثال التالي دالة JavaScript التي يتم استدعاؤها عند حدوث تغييرات في Product الجدول.

المثال التالي هو ربط البيانات في ملف function.json:

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

يشرح قسم التكوين هذه الخصائص.

المثال التالي هو نموذج التعليمات البرمجية JavaScript للدالة في index.js الملف:

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

استخدام المثال

تتوفر المزيد من العينات لمشغل Azure Database for MySQL في مستودع GitHub.

يشير المثال إلى Product جدول قاعدة بيانات:

DROP TABLE IF EXISTS Products;

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

يمكنك تمكين تعقب التغييرات على قاعدة البيانات عن طريق إضافة عمود واحد إلى الجدول:

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

إشعار

يجب استخدام Azure Functions الإصدار 1.22.0b4 ل Python.

يرتبط مشغل Azure Database for MySQL بالمتغير المسمى Product، والذي يسرد الكائنات. يحتوي كل كائن على خاصيتين:

  • item: العنصر الذي تم تغييره. تتبع بنية العنصر مخطط الجدول.
  • operation: القيمة المحتملة هي Update لكل من الإدراج والتحديثات.

يوضح المثال التالي دالة Python التي يتم استدعاؤها عند حدوث تغييرات في Product الجدول.

المثال التالي هو نموذج التعليمات البرمجية Python لملف 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))

السمات

خاصية السمة ‏‏الوصف
TableName مطلوب. اسم الجدول الذي يراقبه المشغل.
ConnectionStringSetting مطلوب. اسم إعداد تطبيق يحتوي على سلسلة الاتصال لقاعدة البيانات التي تحتوي على الجدول الذي تمت مراقبته للتغييرات. يتوافق اسم إعداد سلسلة الاتصال مع إعداد التطبيق (في local.settings.json للتطوير المحلي) الذي يحتوي على سلسلة الاتصال بقاعدة بيانات Azure ل MySQL.
LeasesTableName اختياري. اسم الجدول لتخزين عقود الإيجار. إذا لم يتم تحديده، يكون الاسم هو Leases_{FunctionId}_{TableId}.

تعليقات توضيحية

في مكتبة وقت تشغيل وظائف Java، استخدم @MySQLTrigger التعليق التوضيحي على المعلمات التي تأتي قيمها من قاعدة بيانات Azure ل MySQL. يدعم هذا التعليق التوضيحي العناصر التالية:

العنصر ‏‏الوصف
name مطلوب. اسم المعلمة التي يرتبط بها المشغل.
tableName مطلوب. اسم الجدول الذي يراقبه المشغل.
connectionStringSetting مطلوب. اسم إعداد تطبيق يحتوي على سلسلة الاتصال لقاعدة البيانات التي تحتوي على الجدول الذي تمت مراقبته للتغييرات. يتوافق اسم إعداد سلسلة الاتصال مع إعداد التطبيق (في local.settings.json للتطوير المحلي) الذي يحتوي على سلسلة الاتصال بقاعدة بيانات Azure ل MySQL.
LeasesTableName اختياري. اسم الجدول لتخزين عقود الإيجار. إذا لم يتم تحديده، يكون الاسم هو Leases_{FunctionId}_{TableId}.

التكوين

يوضح الجدول التالي خصائص تكوين الربط التي قمت بتعيينها في ملف function.json:

الخاصية ‏‏الوصف
name مطلوب. اسم المعلمة التي يرتبط بها المشغل.
type مطلوب. يجب تعيينه إلى MysqlTrigger.
direction مطلوب. يجب تعيينه إلى in.
tableName مطلوب. اسم الجدول الذي يراقبه المشغل.
connectionStringSetting مطلوب. اسم إعداد تطبيق يحتوي على سلسلة الاتصال لقاعدة البيانات التي تحتوي على الجدول الذي تمت مراقبته للتغييرات. يتوافق اسم إعداد سلسلة الاتصال مع إعداد التطبيق (في local.settings.json للتطوير المحلي) الذي يحتوي على سلسلة الاتصال بقاعدة بيانات Azure ل MySQL.
LeasesTableName اختياري. اسم الجدول لتخزين عقود الإيجار. إذا لم يتم تحديده، يكون الاسم هو Leases_{FunctionId}_{TableId}.

التكوين الاختياري

يمكنك تكوين الإعدادات الاختيارية التالية لمشغل Azure Database for MySQL للتطوير المحلي أو عمليات النشر السحابية.

host.json

يصف هذا القسم إعدادات التكوين المتوفرة لهذا الربط في الإصدار 2.x والإصدارات الأحدث. تنطبق الإعدادات في ملف host.json على جميع الوظائف في مثيل تطبيق الوظائف. لمزيد من المعلومات حول إعدادات تكوين تطبيق الوظائف، راجع مرجعhost.json ل Azure Functions.

الإعدادات افتراضي ‏‏الوصف
MaxBatchSize 100 الحد الأقصى لعدد التغييرات التي تمت معالجتها مع كل تكرار لحلقة المشغل قبل إرسالها إلى الدالة المشغلة.
PollingIntervalMs 1000 التأخير بالمللي ثانية بين معالجة كل دفعة من التغييرات. (1,000 مللي ثانية هي ثانية 1.)
MaxChangesPerWorker 1000 الحد الأعلى لعدد التغييرات المعلقة في جدول المستخدم المسموح بها لكل عامل تطبيق. إذا تجاوز عدد التغييرات هذا الحد، فقد يؤدي ذلك إلى توسيع النطاق. ينطبق الإعداد فقط على تطبيقات وظائف Azure مع تمكين التحجيم المستند إلى وقت التشغيل.

مثال host.json الملف

فيما يلي مثال على ملف host.json مع الإعدادات الاختيارية:

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

local.setting.json

يخزن ملف local.settings.json إعدادات التطبيق وإعداداته التي تستخدمها أدوات التطوير المحلية. يتم استخدام الإعدادات الموجودة في الملف local.settings.json فقط عند تشغيل المشروع محليًا. عند نشر مشروعك إلى Azure، تأكد أيضا من إضافة أي إعدادات مطلوبة إلى إعدادات التطبيق لتطبيق الوظائف.

هام

نظرا لأن ملف local.settings.json قد يحتوي على أسرار، مثل سلاسل الاتصال، يجب عدم تخزينه في مستودع بعيد. توفر الأدوات التي تدعم Azure Functions طرقا لمزامنة الإعدادات في ملف local.settings.json مع إعدادات التطبيق في تطبيق الوظائف الذي يتم نشر مشروعك عليه.

الإعدادات افتراضي ‏‏الوصف
MySql_Trigger_BatchSize 100 الحد الأقصى لعدد التغييرات التي تمت معالجتها مع كل تكرار لحلقة المشغل قبل إرسالها إلى الدالة المشغلة.
MySql_Trigger_PollingIntervalMs 1000 التأخير بالمللي ثانية بين معالجة كل دفعة من التغييرات. (1,000 مللي ثانية هي ثانية 1.)
MySql_Trigger_MaxChangesPerWorker 1000 الحد الأعلى لعدد التغييرات المعلقة في جدول المستخدم المسموح بها لكل عامل تطبيق. إذا تجاوز عدد التغييرات هذا الحد، فقد يؤدي ذلك إلى توسيع النطاق. ينطبق الإعداد فقط على تطبيقات وظائف Azure مع تمكين التحجيم المستند إلى وقت التشغيل.

مثال local.settings.json الملف

فيما يلي مثال local.settings.json الملف مع الإعدادات الاختيارية:

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

إعداد تعقب التغييرات (مطلوب)

يتطلب إعداد تعقب التغييرات للاستخدام مع مشغل Azure Database for MySQL إضافة عمود في جدول باستخدام دالة. يمكنك إكمال هذه الخطوات من أي أداة MySQL تدعم تشغيل الاستعلامات، بما في ذلك Visual Studio Code أو Azure Data Studio.

تستخدم az_func_updated_at روابط مشغل Azure Database for MySQL وبيانات العمود لمراقبة جدول المستخدم للتغييرات. على هذا النحو، تحتاج إلى تغيير بنية الجدول للسماح بتعقب التغيير على جدول MySQL قبل استخدام دعم المشغل. يمكنك تمكين تعقب التغيير على جدول من خلال الاستعلام التالي. على سبيل المثال، قم بتمكينه على Products الجدول:

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

يحتوي جدول عقود الإيجار على جميع الأعمدة التي تتوافق مع المفتاح الأساسي من جدول المستخدم وعمودين كثر: az_func_AttemptCount و az_func_LeaseExpirationTime. إذا كان لأي من أعمدة المفتاح الأساسي نفس الاسم، تكون النتيجة رسالة خطأ تسرد التعارضات. في هذه الحالة، يجب إعادة تسمية أعمدة المفتاح الأساسي المدرجة حتى يعمل المشغل.

تمكين التحجيم المستند إلى وقت التشغيل

اختياريا، يمكن توسيع نطاق الدالات تلقائيا استنادا إلى عدد التغييرات المعلقة التي سيتم معالجتها في جدول المستخدم. للسماح لوظائفك بالتحجيم بشكل صحيح على خطة Premium عند استخدام Azure Database لمشغلات MySQL، تحتاج إلى تمكين مراقبة مقياس وقت التشغيل.

  1. في مدخل Microsoft Azure، في تطبيق الوظائف، حدد Configuration.

  2. في علامة التبويب إعدادات وقت تشغيل الدالة ، لمراقبة مقياس وقت التشغيل، حدد تشغيل.

    لقطة شاشة لمنطقة مدخل Azure لتمكين تحجيم وقت التشغيل.

إعادة محاولة الدعم

إعادة محاولة بدء التشغيل

إذا حدث استثناء أثناء بدء التشغيل، يحاول وقت تشغيل المضيف تلقائيا إعادة تشغيل مستمع المشغل باستخدام استراتيجية التراجع الأسي. تستمر عمليات إعادة المحاولة هذه حتى يتم بدء تشغيل وحدة الاستماع بنجاح أو إلغاء بدء التشغيل.

إعادة محاولة استثناء الدالة

إذا حدث استثناء في وظيفة المستخدم أثناء معالجة التغيير، تتم إعادة محاولة دفعة الصفوف التي تتم معالجتها حاليا مرة أخرى في 60 ثانية. تتم معالجة التغييرات الأخرى كالمعتاد خلال هذا الوقت، ولكن يتم تجاهل الصفوف في الدفعة التي تسببت في الاستثناء حتى تنقضي فترة المهلة.

إذا فشل تنفيذ الدالة خمس مرات متتالية لصف معين، يتم تجاهل هذا الصف لكافة التغييرات المستقبلية. نظرا لأن الصفوف في الدفعة ليست محددة، فقد ينتهي الأمر بالصفوف في دفعة فاشلة في دفعات مختلفة في استدعاءات لاحقة. يعني هذا السلوك أنه لا يتم بالضرورة تجاهل كافة الصفوف في الدفعة الفاشلة. إذا تسببت الصفوف الأخرى في الدفعة في الاستثناء، فقد ينتهي الأمر بالصفوف "الجيدة" في دفعة مختلفة لا تفشل في استدعاءات المستقبل.