Skip to content

Document polling patterns for waiting on external state and their history cost #283

Closed

Description

@rmcdaniel

Workflows often wait hours for external state, such as a vendor file or a downstream job. No guide compares the ways to poll, and the history cost differs from other engines. In Durable Workflow every activity attempt and every heartbeat is a history event.

Current behavior

Docs a7229d5d25c65c665f292e5e7e70fa769891cd0a:

  • Each activity attempt records ActivityStarted, and each retry records ActivityRetryScheduled with the failure (failures and recovery). The guide documents the events but not what they cost in a long poll.
  • Each heartbeat appends a history event (heartbeats).
  • Workflow code is not resumed between activity attempts, so it cannot check its history budget or continue as new while one activity keeps retrying.
  • The continue-as-new PollingWorkflow examples poll once and return rather than loop.
  • The building blocks are documented separately: select() and handle cancellation in the concurrency guide, sagas, child workflows and timeouts.

Expected

One guide lets a developer choose and implement a polling pattern for a multi-hour wait. For each pattern it states history events per poll, worker slot use, which timeout bounds the whole wait, and how to interrupt or restart it.

Acceptance

  • Cover retry-driven polling, a long heartbeating activity, a timer loop with continue-as-new, and a dedicated child workflow. For each, list the events per iteration, whether workflow code can check its budget while waiting, slot occupancy, and what ends the wait.
  • Show interrupting a wait with select() on a signal and cancelling the losing handle, then compensating or restarting.
  • Recommend short, fast-failing attempts with bounded downstream timeouts as guidance, not a guarantee.
  • Account for replay-stable budget reads tracked in workflow#859 and history-limit enforcement in server#504. SQS delay chunking is already delivered by workflow#851; do not describe it as an open limitation on supported releases. Do not recommend in-run budget checks until #859 is resolved.
  • Include embedded PHP and Python service examples checked against named published versions.

Activity

  1. added
    documentationImprovements or additions to documentation
    kind:featureA public product capability or experience is requested
    priority:P3Lower-priority product improvement
    status:readyReady for implementation
    on Oct 20, 2026
  2. rmcdaniel commented on Oct 20, 2026

    @rmcdaniel
    MemberAuthor

    Product review:

    Keep as a comparative guide using existing supported polling choices. Product fixes are already tracked in Workflow #859 (replay-stable budget reads), Server #504 (history-limit enforcement) and shared #215 (advancing workflow clocks). The SQS chunking limitation in Workflow #851 is already delivered and must no longer be presented as open on the supported baseline. Do not publish an unsafe budget loop as a workaround for an unresolved runtime defect.

  3. rmcdaniel commented on Oct 20, 2026

    @rmcdaniel
    MemberAuthor

    Taking the genuine comparative guide in a separate source branch: new polyglot/polling-patterns only, all seven translated copies and their reviewed hash keys. Shared navigation is coordinated separately. The guide will compare retry-driven polling, a heartbeating activity, a relative timer loop and a dedicated child workflow, with history/slot/deadline/cancellation costs kept distinct. It will use finite explicit retry/backoff and fixed per-run rollover, not an unsafe replay-varying budget loop or an advancing Python now claim. Existing Workflow859, Server504 and shared215/219 limitations remain explicit; SQS chunking851 is already delivered. A fresh isolated published Server2.9.7/Core2.7.2 plus normal Python2.6.1 cohort measures actual retry, heartbeat and timer-loop history. Separate embedded example verification will be stated only if it runs. No native/distributed/mode parity is inferred. All task resources retire by10:30UTC, immediately after audit; shared images are preserved. No runtime or SDK implementation is included.

  4. rmcdaniel commented on Oct 20, 2026

    @rmcdaniel
    MemberAuthor

    Published-artifact qualification for this guide is complete. This is documentation evidence, with no product change or broader runtime acceptance.

    • Exact published Server 2.9.7 / Core 2.7.2: source 150f3fbc / 64efa176, immutable image sha256:40e969d0b6b240b835dcdb2fc531507266216ec34a83f59f561674f41e35f461; Python 2.6.1 installed normally from its published package.
    • Three separate real Python service-mode originals, original 30-second observers and unchanged 120-second run / 180-second execution bounds: a remote activity succeeding on attempt three, one activity with two explicit heartbeats, and two successful activity polls separated by a real one-second durable timer. All return their exact expected values; observed completion times were 6.79 / 5.20 / 8.76 seconds, with no performance claim.
    • Stopped, read-only canonical histories contain respectively 10 / 8 / 11 events. The retry case has one ActivityScheduled, three ActivityStarted, two ActivityRetryScheduled and one ActivityCompleted. The heartbeat case adds exactly two ActivityHeartbeatRecorded events. The two-check timer loop has two each of ActivityScheduled/Started/Completed and one each of TimerScheduled/Fired. StartAccepted, WorkflowStarted and WorkflowCompleted account for the three common lifecycle events. These are measured histories for this tuple, not universal billing units or a cross-runtime event-count promise.
    • The proposed finite rollover example is exercised as both a normal Python worker and actual PHP embedded queue handlers, with input (remaining=2, interval=1) rather than its guide's default interval of 30. Each executes two real activities and one durable timer, then completes the successor with ready=true; no activity mock, history repair or fake successful result. Original execution deadlines remain byte-identical across the two runs. Python records 8+5 events, PHP 8+6; the PHP successor includes StartAccepted, so run counts and runtime admission metadata are explicitly separated from per-poll operation costs.
    • Exact fresh Python first/final runs: 01m5ccc5p6v3vxdpqytbc01by2 / 01m5ccc8dgv42c9vreez3wqmze. PHP: 01m5ccbsg9ye0xr67rk2dkk4nf / 01m5ccbvqxg0kyr3by7sn64yaa. Across all original and corrected-observer business executions the stopped audit finds nine completed run rows, 39 completed tasks, zero task repairs and 69 history events.

    Unsuccessful fixture observations remain explicit: the first PHP fixture omitted creation/migration of its isolated database; the first Python observer read the continued predecessor's null output rather than the current successor. The first read-only audit assumed an absent task attempts column; a subsequent audit needed immutable mode only for the stopped, sidecar-free PHP database while retaining WAL-aware reads for service data. Corrected readers leave all database/WAL/SHM SHA-256 values unchanged. None is a Server or SDK defect or a rescued original.

    The standalone PHP HTTP/timer roles needed SIGKILL after their five-second SIGTERM allowance (137, no OOM); graceful role shutdown is not qualified. Python workers deregistered and exited normally. All business results completed before teardown.

    No native, multi-database, distributed fairness, billing, capacity or SDK clock advancement claim follows. Relative sleeps and an explicit finite remaining-check counter avoid relying on the unresolved replay-clock/history-budget behavior. All task resources are being retired immediately; the final cleanup checkpoint follows separately.

  5. rmcdaniel commented on Oct 20, 2026

    @rmcdaniel
    MemberAuthor

    Actual final qualification-phase cleanup: 10:08:05 UTC, October 20, ahead of the 10:30 boundary. Every owned application/timer/SDK container, network, installed dependencies, SQLite/WAL/SHM database, probe/observer/audit and raw output is absent; no image was deleted. Read-only checks used ephemeral UID1000 containers and also exited/removed normally. Only the separate guide source remains.

    Draft #312 now contains the complete eight-language guide at 1fa0377fa91886881e78c61e58c43d5fda5b85d9 / tree 2623f1346ebb6ad7f3c644510ebe028fe078c931, with no product edit or qualification transferred from older heads. Full seven-locale inventory/hash/code and targeted anchor/link/bounded-policy checks pass. Normal exact-head CI/Sonar, shared navigation integration and actual publication remain ahead; this issue stays open.

  6. rmcdaniel commented on Oct 20, 2026

    @rmcdaniel
    MemberAuthor

    Source delivery checkpoint: #312 merged normally at 7c478fb, whole tree 51b1b8f24fa1baeedbc2bb2080b6d7d7c2deb1ce, identical to fully qualified candidate 09616420c5. Remote head retirement is independently verified. Scope remains the eight new guides, seven reviewed-source hash keys and one navigation entry.

    All six candidate checks and all seven independent fresh-main checks now pass, including executable documentation 39411584489, source deployment 39411584502, protocol, public boundary and reviewed Sonar. The intentional single-line declarative-sidebar duplication and unchanged gate settings remain explicitly reviewed in the Sonar disposition; passing is not presented as zero duplication or an empty main issue inventory.

    Actual native Pages publication 39412448279 is still queued. This issue remains open until real HTTP acceptance of the new guide in all eight locales, including translated titles/language/canonical links, all eight explicit anchors, finite retry/rollover examples and measured operation-versus-run history totals. The same readback will verify the retained retry and clock guides. No new application, SDK, database or raw-output resources were started; the published-artifact proof and 10:08 cleanup remain final.

  7. rmcdaniel commented on Oct 20, 2026

    @rmcdaniel
    MemberAuthor

    Delivered through #312 and independently accepted on the public site at 2026-10-20 11:14:15 UTC.

    Exact source: merge 7c478fb97b444ba275dd4c4e4eead37cfd233557, whole tree 51b1b8f24fa1baeedbc2bb2080b6d7d7c2deb1ce, identical to reviewed/tested candidate 09616420c5. All six candidate checks and all seven independent fresh-main checks pass, including executable documentation 39411584489, source deployment 39411584502, protocol, boundary and reviewed Sonar. Actual Pages publication 39412448279 passes; generated commit 76eb5acab05ca40c61bf7ef87448198c0647be09 identifies that exact source. Remote head retirement is verified.

    Actual live acceptance: all eight new guide routes return HTTP 200 with the source-matching translated title, correct document language, canonical URL, rendered navigation link and all seven navigable section IDs. Each contains both runnable examples, explicit finite retry/deadline settings, the named published versions and the measured 8 + 5 versus 8 + 6 run-history distinction.

    Locale Published polling guide
    English Polling external state
    Українська Опитування зовнішнього стану
    Español Sondeo del estado externo
    Português brasileiro Consulta periódica de estado externo
    Deutsch Externen Status abfragen
    Français Interroger un état externe
    日本語 外部状態のポーリング
    简体中文 轮询外部状态

    The same readback checks all eight retained #301 failures guides (default one attempt, finite [5, 30] backoff, original 120-second total deadline and repair boundary) and all eight #307 Python guides (WaitWithoutClockLoop and both clock anchors). 24 actual routes pass, preserving their language metadata and canonical URLs.

    One unsuccessful live observer is retained explicitly: it demanded a rendered id on the H1 title, counting all eight source heading markers as navigable anchors. The pinned Docusaurus Heading component deliberately removes H1 IDs because titles are outside the table of contents. Production English/Spanish/Ukrainian readback confirms identical behavior and all seven real section anchors. Only that reader expectation was corrected; the published source, timeouts, language checks and remaining assertions were unchanged. This is not a site or translation defect or a rescued original.

    The published-artifact runtime proof, its failed setup/observer records and 10:08:05 cleanup remain final. The Sonar review preserves zero PR issues alongside the intentional sidebar-data duplication metric; no profile or threshold was changed. Clock, replay-budget, maximum-history enforcement and retry-delay product gaps remain linked and open. No native, distributed, billing, performance or automatic-cleanup feature is claimed.

    All live-check containers exited and removed themselves; no apps, compiler/dependencies, database or raw output remain. Closing as published and verified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationkind:featureA public product capability or experience is requestedpriority:P3Lower-priority product improvementstatus:readyReady for implementation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions