Опитування зовнішнього стану
Опитуйте зовнішнє завдання в activity, а не мережевими запитами в коді workflow. Спершу визначте, де має відбуватися очікування, а тоді обирайте інтервал.
Оберіть межу очікування
| Шаблон | Використання worker | Довговічна історія та керування |
|---|---|---|
| Повторювати одну activity до готовності | Слот activity під час кожної спроби; звільняється на час затримки між спробами | Кожна спроба й кожне заплановане повторення додають історію. Код workflow не відновлюється між спробами, тому в цей момент не може перевірити бюджет історії чи виконати continue-as-new. |
| Одна тривала activity із heartbeat | Займає слот activity протягом усього очікування | Кожен прийнятий heartbeat додає подію історії. Задайте скінченні дедлайни activity та обмежений тайм-аут запиту до зовнішньої системи; heartbeat не продовжує дедлайн усього виконання. |
| Перевірка в activity, а потім довговічний timer | Звільняє слоти activity між перевірками | Кожна перевірка додає події activity, а кожен інтервал — події timer. Між перевірками код workflow може вирішити, чи зупинитися, виконати компенсацію або continue-as-new. |
| Окремий дочірній workflow для опитування | Дочірній workflow використовує слоти відповідно до обраного шаблону | Переносить історію опитування до дочірнього workflow; додає батькові події запуску та завершення дочірнього виклику. Історія та виконання дочірнього workflow не стають безкоштовними. Явно оберіть політику закриття батька. |
Використовуйте короткі перевірки, які безпечно повторювати. Обробник, заблокований зовнішнім запитом, не стане чутливим до скасування лише через наявність timer чи політики скасування у workflow.
Обмежуйте спроби та історію
Повторні спроби потрібно вмикати явно: для PHP activity типово дозволена одна спроба. Наприклад, задайте maxAttempts: 3, backoff: [5, 30], startToCloseTimeout: 10 і scheduleToCloseTimeout: 90 для трьох віддалених спроб зі скінченними затримками та початковим загальним часом. Відповідна політика Python — ActivityRetryPolicy(max_attempts=3, backoff_seconds=[5, 30]), з окремими параметрами тайм-аутів спроби й усієї activity. Успішне повернення «ще не готово» не є помилкою й не запускає повторну спробу. Див. помилки та відновлення.
Повторні спроби доречні, коли неготовність — це явно визначений результат, що допускає повторення, а коду workflow не потрібно виконуватися між перевірками. Не перетворюйте необмежені повтори на прихований цикл опитування. Узгодженість затримок локальних activity відстежується в задачі про контракт повторних спроб; тут приклади використовують віддалені activity.
Явно переходьте до нового запуску
Цей виконуваний приклад робить одну перевірку за запуск, очікує та передає скінченний лічильник решти перевірок у наступний запуск. Замініть лише синтетичний пошук стану в activity справжнім запитом з обмеженим тайм-аутом. Приклад повертає останній стан навіть після вичерпання перевірок; викликач має перевіряти ready.
Вбудований PHP
use Workflow\V2\Activity;
use Workflow\V2\Support\ActivityOptions;
use Workflow\V2\Workflow;
use function Workflow\V2\activity;
use function Workflow\V2\timer;
use function Workflow\V2\continueAsNew;
final class CheckDemoStatus extends Activity
{
public function handle(int $remaining): array
{
return ['ready' => $remaining <= 1];
}
}
final class PollDemo extends Workflow
{
public function handle(int $remaining = 2, int $intervalSeconds = 30): array
{
$status = activity(CheckDemoStatus::class, new ActivityOptions(
maxAttempts: 1, startToCloseTimeout: 10, scheduleToCloseTimeout: 15,
), $remaining);
if ($status['ready'] || $remaining <= 1) {
return $status;
}
timer($intervalSeconds);
return continueAsNew($remaining - 1, $intervalSeconds);
}
}
Запускайте його через звичайне налаштування вбудованого workflow, задавши скінченні тайм-аути запуску та всього виконання.
Сервісний worker Python
from durable_workflow import activity, workflow
from durable_workflow.workflow import ActivityRetryPolicy
@activity.defn(name="poll-demo.status")
async def check_status(remaining):
return {"ready": remaining <= 1}
@workflow.defn(name="poll-demo")
class PollDemo:
def run(self, ctx, remaining=2, interval_seconds=30):
result = yield ctx.schedule_activity(
"poll-demo.status", [remaining],
retry_policy=ActivityRetryPolicy(max_attempts=1, backoff_seconds=[1]),
start_to_close_timeout=10, schedule_to_close_timeout=15,
)
if result["ready"] or remaining <= 1:
return result
yield ctx.sleep(interval_seconds)
return ctx.continue_as_new(remaining - 1, interval_seconds)
Зареєструйте PollDemo і check_status в одному worker Python, а тоді запустіть з аргументами [2, 30] і скінченними тайм-аутами виконання та запуску. Щоб відстежувати наступника, спостерігайте за поточним екземпляром workflow; дескриптор конкретного запуску й надалі вибирає початковий запуск.
Обидва приклади виконано на опублікованих Server 2.9.7 / Core 2.7.2, зі звичайно встановленим Python 2.6.1 та тестовим входом [2, 1]. Обидва виконали дві справжні перевірки у двох запусках. Continue-as-new починає нову історію запуску, додає витрати на запуск і зв'язок та зберігає початковий дедлайн виконання. Це не дає необмеженого часу життя.
Тут використовуйте відносні довговічні очікування. Просування годинника Python і стабільне під час відтворення читання бюджету історії залишаються окремими задачами про годинник та бюджет. Цей рецепт не розгалужується за ctx.now() чи повторно обчисленою довжиною історії. Не вважайте DW_MAX_HISTORY_EVENTS примусово застосовуваним автоматичним переходом: Server #504 відстежує цю прогалину.
Переривайте та звіряйте наслідки
Для зупинки через signal змагайте signal із поточним очікуванням через select та явно скасуйте операцію, яка програла, відповідно до підтримуваної політики скасування. Сам select не скасовує решту операцій. Запит зупинити activity не доводить відсутності зовнішнього побічного ефекту: використовуйте ключі ідемпотентності та звіряйте стан перед компенсацією або повторним запуском. Негайне скасування, terminate і жорсткий тайм-аут workflow не виконують компенсацію з коду workflow; якщо потрібне очищення, використовуйте підтримуване кооперативне скасування.
Перевіряйте фактичну історію
Наведені вимірювання сервісного режиму Python зафіксували:
| Завершений сценарій | Події канонічної історії |
|---|---|
| Одна віддалена activity успішна з третьої спроби | Усього 10: 1 планування, 3 початки, 2 планування повтору, 1 завершення та 3 події життєвого циклу запуску |
| Одна activity із двома прийнятими heartbeat | Усього 8: 3 події activity, 2 події heartbeat та 3 події життєвого циклу |
| Дві успішні перевірки з одним timer між ними | Усього 11: 6 подій activity, 2 події timer та 3 події життєвого циклу |
Приклад переходу записав 8 + 5 подій у запусках Python та 8 + 6 у PHP; PHP також записує StartAccepted для наступника. Це виміряні витрати операцій і запусків, а не універсальні підсумки подій, одиниці оплати чи результати продуктивності. Інші команди, помилки, скасування та метадані прийняття в конкретному runtime можуть додавати події. Перевіряйте історію кожного запуску, збережену політику спроб і початкові дедлайни, а не оцінюйте зростання лише за кількістю ітерацій. Точні спостереження та невдалі записи перевірочної програми збережені в підтвердженні кваліфікації.