Перейти до основного вмісту
Версія: 2.0

Структурні обмеження

Структурні обмеження встановлюють межі споживання ресурсів одним запуском workflow. Якщо операція перевищила б налаштовану межу, рушій записує типізовану помилку з машинозчитуваною категорією structural_limit та конкретним видом обмеження, а потім завершує запуск із помилкою. Це захищає систему від необмеженого розгалуження, завеликих даних та розростання метаданих.

Види обмежень​

Вид обмеженняТипове значенняЩо обмежує
pending_activity_count2,000Одночасно відкриті виконання activity без кінцевого стану
pending_child_count1,000Одночасно відкриті дочірні workflow без кінцевого стану
pending_timer_count2,000Одночасно відкриті timer в очікуванні
pending_signal_count5,000Одночасно очікувані необроблені signal
pending_update_count500Одночасно очікувані нерозв’язані update
command_batch_size1,000Елементи одного паралельного розгалуження (all())
payload_size_bytes2 MiBСеріалізований розмір одного набору аргументів
memo_size_bytes256 KiBСеріалізований розмір неіндексованих метаданих memo
search_attribute_size_bytes40 KiBСеріалізований розмір індексованих метаданих атрибутів пошуку
history_transaction_size5,000Події історії за одне виконання завдання workflow

Усі обмеження застосовуються під час планування, запису чи прийому команди. Значення 0 вимикає перевірку відповідного виду обмеження.

Попередження про наближення до межі​

Перш ніж жорстка межа завершить запуск чи відхилить команду, рушій може попередити, що ресурс наближається до максимуму. Коли кількісний ресурс (очікувані activity, дочірні workflow, timer, signal, update, розмір пакета команд чи події транзакції історії) перевищує налаштований відсоток жорсткої межі, рушій записує структуроване попередження.

Типовий поріг попередження — 80%. Наприклад, за типового обмеження pending_activity_count у 2,000 попередження записується, коли запуск досягає 1,600 очікуваних activity. Запуск продовжується звичайним чином. Попередження дає операторам час відреагувати: масштабувати worker, запустити continue-as-new чи підвищити межу до того, як жорсткий захист завершить запуск із помилкою.

Налаштовуйте поріг через workflows.v2.structural_limits.warning_threshold_percent:

DW_V2_LIMIT_WARNING_THRESHOLD_PERCENT=80

Задайте 0, щоб повністю вимкнути попередження про наближення до межі.

Записи попереджень містять структурований контекст:

[Durable Workflow] Run 42 approaching structural limit [pending_activity_count]: 1620 / 2000 (81% utilization, warning at 80%).

Структурований контекст журналу містить workflow_run_id, workflow_type, limit_kind, current, limit та utilization_percent для інтеграції із засобами збирання журналів і сповіщень.

Налаштування​

Перевизначайте будь-яку межу через workflows.v2.structural_limits у налаштуваннях або через змінні середовища:

// config/workflows.php
'v2' => [
'structural_limits' => [
'pending_activity_count' => (int) env('DW_V2_LIMIT_PENDING_ACTIVITIES', 2000),
'pending_child_count' => (int) env('DW_V2_LIMIT_PENDING_CHILDREN', 1000),
'pending_timer_count' => (int) env('DW_V2_LIMIT_PENDING_TIMERS', 2000),
'pending_signal_count' => (int) env('DW_V2_LIMIT_PENDING_SIGNALS', 5000),
'pending_update_count' => (int) env('DW_V2_LIMIT_PENDING_UPDATES', 500),
'command_batch_size' => (int) env('DW_V2_LIMIT_COMMAND_BATCH_SIZE', 1000),
'payload_size_bytes' => (int) env('DW_V2_LIMIT_PAYLOAD_SIZE_BYTES', 2097152),
'memo_size_bytes' => (int) env('DW_V2_LIMIT_MEMO_SIZE_BYTES', 262144),
'search_attribute_size_bytes' => (int) env('DW_V2_LIMIT_SEARCH_ATTRIBUTE_SIZE_BYTES', 40960),
'history_transaction_size' => (int) env('DW_V2_LIMIT_HISTORY_TRANSACTION_SIZE', 5000),
'warning_threshold_percent' => (int) env('DW_V2_LIMIT_WARNING_THRESHOLD_PERCENT', 80),
],
],

Точки застосування​

Кількісні межі очікування на боці виконавця​

Перш ніж виконавець планує activity, дочірній workflow чи timer, він рахує поточні елементи цього типу без кінцевого стану в запуску. Якщо кількість уже досягла чи перевищила налаштовану межу, запуск одразу завершується з StructuralLimitExceededException.

Це захищає від шаблонів на кшталт необмежених циклів паралельного розгалуження, які накопичують тисячі очікуваних операцій:

// This will fail if $items exceeds the pending_activity_count limit
$calls = [];
foreach ($items as $item) {
$calls[] = fn () => activity(ProcessItemActivity::class, $item);
}
return all($calls); // Also checked against command_batch_size

Для обробки великих пакетів у межах обмежень обробляйте елементи частинами обмеженого розміру:

foreach (array_chunk($items, 500) as $chunk) {
$calls = [];
foreach ($chunk as $item) {
$calls[] = fn () => activity(ProcessItemActivity::class, $item);
}
all($calls);
}

Кількісні межі очікування на боці прийому​

Коли команда signal чи update надходить через площину керування, webhook або WorkflowStub, рушій до прийняття команди перевіряє кількість необроблених signal (стан received) чи нерозв’язаних update (стан accepted) цільового запуску.

Якщо кількість досягла чи перевищила налаштовану межу, команда відхиляється з причиною structural_limit_exceeded. Відповідь містить машинозчитувані метадані (structural_limit_kind, structural_limit_value, structural_limit_configured), щоб викликачі могли визначити першопричину.

На відміну від меж виконавця, які завершують запуск із помилкою, межі прийому відхиляють окрему команду без завершення workflow. Запуск залишається активним, і викликач може повторити спробу після обробки очікуваних елементів.

// If 5,000 signals are already pending, this will be rejected:
$result = $workflow->attemptSignal('process-item', $data);
if ($result->rejected()) {
// $result->rejectionReason() === 'structural_limit_exceeded'
// back off and retry later
}

Розмір пакета команд​

Функція all() перевіряє загальну кількість кінцевих операцій в одній групі розгалуження щодо command_batch_size. Перевірка відбувається до планування окремих activity чи дочірніх workflow, тож запуск завершується з помилкою цілісно, без часткового планування пакета.

Розмір даних​

Під час планування activity чи дочірнього workflow виконавець серіалізує аргументи й перевіряє довжину в байтах щодо payload_size_bytes. Якщо серіалізовані дані перевищують межу, запуск завершується з помилкою до створення будь-яких рядків цієї операції в базі даних.

Це застосовується до:

  • Аргументів activity: перевірка під час серіалізації аргументів ActivityCall у scheduleActivity.
  • Аргументів дочірнього workflow: перевірка під час серіалізації аргументів запуску дочірнього workflow в scheduleChildWorkflow, до створення рядків дочірнього екземпляра чи запуску.
// A 3 MiB payload will fail with the default 2 MiB limit
activity(ProcessDocumentActivity::class, $threeMegabyteBlob);

Щоб працювати в межах обмеження, увімкніть зовнішнє сховище даних для простору імен, щоб середовище виконання автоматично виносило дані понад поріг до налаштованого об’єктного сховища. Або зберігайте байти самостійно й передавайте посилання на рівні застосунку:

$ref = Storage::put('docs/incoming.pdf', $blob);
activity(ProcessDocumentActivity::class, $ref);

Зовнішнє сховище даних зберігає цілісність відтворення, записуючи в історію оболонку durable-workflow.v2.external-payload-reference.v1 із хешем. Тому обмеження бачить оболонку посилання як дані, а не самі байти.

Розмір memo​

Коли workflow додає чи оновлює записи memo через upsertMemo(), виконавець об’єднує нові записи з наявною мапою memo, кодує результат у JSON і перевіряє довжину в байтах щодо memo_size_bytes. Якщо об’єднане memo перевищує межу, запуск завершується з помилкою до збереження memo.

Розмір транзакції історії​

Кожне виконання завдання workflow (один крок відтворення та поступу вперед) може створювати нові події історії: планування activity, створення timer, запис побічного ефекту, оновлення атрибутів пошуку тощо. history_transaction_size обмежує загальну кількість нових подій, які може створити одне завдання.

Це виявляє неконтрольовані цикли, які створюють необмежену кількість подій в одному завданні без передавання керування:

// If a workflow schedules thousands of operations in one task,
// the history transaction limit prevents the task from growing
// without bound. Process large batches in bounded chunks instead.
foreach (array_chunk($items, 500) as $chunk) {
$calls = [];
foreach ($chunk as $item) {
$calls[] = fn () => activity(ProcessItemActivity::class, $item);
}
all($calls); // Each chunk is a separate task execution
}

Перевірка виконується на початку кожної ітерації головного циклу виконавця. Події під час відтворення (читання наявної історії) не зараховуються до межі. Враховуються лише нові події, записані поточним завданням.

Розмір атрибутів пошуку​

Коли workflow додає чи оновлює атрибути пошуку через upsertSearchAttributes(), виконавець об’єднує нові атрибути з наявним набором, кодує результат у JSON і перевіряє довжину в байтах щодо search_attribute_size_bytes. Якщо об’єднані атрибути перевищують межу, запуск завершується з помилкою до їх збереження.

Класифікація помилок​

Коли структурне обмеження перевищено, рушій записує:

  • Рядок WorkflowFailure із failure_category = structural_limit
  • Подію історії WorkflowFailed із такими полями:
    • failure_category = structural_limit
    • structural_limit_kind — конкретне перевищене обмеження (наприклад, pending_activity_count, command_batch_size)
    • structural_limit_value — поточна кількість чи розмір, що спричинили перевищення
    • structural_limit_configured — налаштована верхня межа

Ці метадані машинозчитувані, тож оператори, Waterline та зовнішні інструменти можуть визначати першопричину без розбору повідомлень довільним текстом.

Перевірка стану​

Поточні налаштування структурних обмежень містяться у знімку перевірки стану v2 під structural_limits, що показує операторам активні верхні межі:

{
"structural_limits": {
"pending_activity_count": 2000,
"pending_child_count": 1000,
"pending_timer_count": 2000,
"pending_signal_count": 5000,
"pending_update_count": 500,
"command_batch_size": 1000,
"payload_size_bytes": 2097152,
"memo_size_bytes": 262144,
"search_attribute_size_bytes": 40960,
"history_transaction_size": 5000,
"warning_threshold_percent": 80
}
}

Обмеження залежно від бекенду​

Знімок можливостей бекенду публікує повний контракт структурних обмежень з урахуванням поточної інфраструктури. Більшість меж є значеннями налаштувань, незалежними від бекенду, але деякі бекенди додають обмеження:

  • Черга SQS: Amazon SQS обмежує відкладену доставку повідомлень 900 секундами, тож знімок містить max_single_timer_delay_seconds: 900. Timer з більшою тривалістю поділяються транспортним рівнем на частини.
  • База даних SQLite: SQLite виконує записи послідовно, тож знімок містить concurrent_write_safety: limited. Високі кількісні межі очікування можуть спричинити конкуренцію за блокування під одночасним навантаженням worker.

Повний контракт доступний у розділі structural_limits відповіді про можливості бекенду:

{
"structural_limits": {
"configured": { "pending_activity_count": 2000, "..." : "..." },
"backend_adjustments": { "max_single_timer_delay_seconds": 900 },
"effective": { "pending_activity_count": 2000, "max_single_timer_delay_seconds": 900, "..." : "..." },
"issues": [
{
"component": "structural_limits",
"severity": "info",
"code": "queue_max_delay_constraint",
"message": "The [sqs] queue driver limits delayed dispatch to 900 seconds; timers exceeding this are chunked by the transport layer."
}
]
}
}

Waterline​

Waterline показує помилки структурних обмежень у таблиці винятків із категорією structural_limit. Деталі помилки на часовій шкалі містять вид обмеження, поточне значення та налаштовану верхню межу.

Обмеження на межі запиту Server​

Під час використання окремого Server інший набір меж застосовується на межі HTTP-запиту до того, як завдання workflow, signal, update чи query потрапляє до площини керування. Ці межі одразу повертають 422 validation_failed або 413 payload_too_large для перевірки всього тіла. Клієнти дізнаються причину відхилення без запису сервером будь-якого рядка до бази даних.

ОбмеженняТипове значенняКлюч налаштуванняЩо обмежує
Розмір тіла2 MiBserver.limits.max_payload_bytesЗагальна кількість байтів тіла HTTP-запиту
Розмір memo256 KiBserver.limits.max_memo_bytesСеріалізоване memo в POST /workflows та POST /schedules
Кількість атрибутів пошуку100server.limits.max_search_attributesЗареєстровані власні атрибути пошуку на простір імен
Довжина ключа атрибута пошуку128 байтівserver.limits.max_search_attribute_key_lengthДовжина одного ключа атрибута пошуку під час запуску
Розмір значення атрибута пошуку2 KiBserver.limits.max_search_attribute_value_bytesКожне рядкове значення та кожен елемент масиву під час запуску
Назва signal / update / query256 байтівserver.limits.max_operation_name_lengthСегмент шляху URL для назв signal/update/query
Довжина workflow_id128 символіввалідатор контролераID workflow в POST /workflows
workflow_type / task_queue / business_key255 символіввалідатор контролераРядкові поля в POST /workflows
request_id255 символіввалідатор контролераТокен усунення дублікатів для signal/update/cancel/terminate
reason1,000 символіввалідатор контролераТекст причини для cancel/terminate/archive

Кожна межа окремо налаштовується через змінні середовища DW_*. Повний контракт наведено в config/dw-contract.php. Значення 0 вимикає перевірку конкретної межі, залишаючи решту чинними.

Поточні значення публікуються під limits у відповіді GET /api/cluster/info, тому клієнти можуть дізнатися їх під час виконання:

{
"limits": {
"max_payload_bytes": 2097152,
"max_memo_bytes": 262144,
"max_search_attributes": 100,
"max_search_attribute_key_length": 128,
"max_search_attribute_value_bytes": 2048,
"max_operation_name_length": 256,
"max_pending_activities": 2000,
"max_pending_children": 2000
}
}

Помилки валідації повертаються в стандартній оболонці помилок площини керування з reason: "validation_failed" та мапою validation_errors, ключами якої є проблемні поля (signal_name, update_name, query_name, search_attributes, memo тощо). Відхилення через розмір даних використовують reason: "payload_too_large" та код стану 413.