villebro opened a new pull request, #43734:
URL: https://github.com/apache/superset/pull/43734

   ### SUMMARY
   
   Addresses the latest review of the GTF async chart-data work (findings
   P1a/P1b/P1c and the DAG-scheduling concern P2a), and adds transition/deferral
   metrics. Targets the `gaq-to-gtf` umbrella branch.
   
   **P1c — non-abortable timeout no longer strands a task `IN_PROGRESS`.** A 
task
   with no registered abort handlers can't be cancelled, so its timeout firing 
is
   now a logged no-op. Previously it set `_timeout_triggered`, which blocks the
   executor's `IN_PROGRESS → SUCCESS` transition (gated on no abort in flight),
   while the finalize only moves `TIMED_OUT` from `ABORTING` — a transition that
   never happens for a handler-less task — leaving it stuck `IN_PROGRESS`. It 
now
   runs to natural completion.
   
   **P2a — the DAG dependency gate is non-blocking.** A dependent picked up 
before
   its prerequisites are terminal is deferred with a Celery 
`self.retry(countdown=…,
   max_retries=None)` instead of parking a worker slot in a blocking
   `wait_for_completion`. The backoff grows and is jittered (1s, 3s, 5s… capped 
at
   30s, keyed off `self.request.retries`, so no persisted state). The gate
   (`_unmet_prerequisite`) trusts a prerequisite already terminal in the loaded
   snapshot and re-reads only the non-terminal ones; the abort-before-claim 
check
   and the failed-prerequisite `FAILURE` cascade moved into `execute_task`, 
ahead of
   the heartbeat/claim, so a merely-deferred task carries no heartbeat and 
can't be
   mistaken for abandoned work by the reaper.
   
   **P1b — a failed Celery enqueue no longer poisons the dedup key.** If
   `execute_task.delay(...)` throws, the just-created task is transitioned to
   `FAILURE` (and a completion published) instead of being left `PENDING`, 
where it
   would dedup future identical submits into a task that never runs. The 
original
   enqueue error is re-raised; cleanup failures are swallowed and logged (the 
orphan
   reaper is the backstop).
   
   **Observability.** Every status transition emits 
`gtf.task.transition.<status>`,
   and each DAG defer emits `gtf.task.dag_deferred`.
   
   **P1a — forced async chart-data no longer double-executes.** A forced 
refresh in
   the async flow is issued twice — the async submit, then a synchronous 
read-back —
   and both previously carried `force=true`, so the read-back recomputed the
   identical query instead of reading the result the task just cached. A 
per-refresh
   idempotency nonce (`force_nonce`) now dedups this **server-side**:
   
   - The first execution to run for a `(nonce, cache_key)` recomputes with 
`force`
     and, *after* its result is cached, records a marker 
`gtf-force-nonce:{nonce}:{cache_key}`.
   - Any later request carrying the same nonce sees the marker and resolves
     `force=false`, reading the freshly-warmed result.
   - The marker is a **separate** cache key, never folded into the result
     `cache_key`, so the fresh value lands under the normal key and ordinary
     (non-forced) loads stay warm.
   - A request whose `cache_key` differs from the one the forced compute cached
     against (e.g. templated `{{ current_timestamp }}` / time-relative keys that
     shift between submit and read-back) finds no marker and correctly 
recomputes,
     rather than mis-reading another key's result. The composite key also makes 
each
     query object in a multi-query request independently idempotent under one
     request nonce.
   
   The nonce is minted once per user force refresh on the frontend
   (`requestChartDataResolved`) and carried on both requests; it is threaded 
through
   query serialization so the async task and the read-back share it. Because the
   guard is server-side, it also covers retries, double-clicks, and second-tab
   refreshes — not just the re-issue.
   
   Because the design is nonce-based (not "clear `force` on the re-issue"), the
   frontend keeps `force=true` on both requests; the server, not the client, is
   what prevents the second compute.
   
   ### TESTING INSTRUCTIONS
   
   - Backend: `pytest tests/unit_tests/tasks/ tests/unit_tests/commands/tasks/ 
tests/unit_tests/common/`
     (DAG gate/defer, enqueue-failure cleanup, timeout no-op, transition metric,
     nonce gate + marker, serialization round-trip).
   - Frontend: `npm run test -- chartActions buildQueryContext exploreUtils`
     (one nonce minted per refresh and carried on both submit and re-issue; both
     keep `force`; the re-issue is synchronous).
   - Manual (async chart-data deployment, `GLOBAL_ASYNC_QUERIES` on): 
force-refresh
     a chart; confirm the query runs **once** (one task, no second compute on 
the
     read-back) via query logs/metrics; a fresh force refresh recomputes.
   
   ### ADDITIONAL INFORMATION
   
   - [ ] Has associated issue:
   - [ ] Required feature flags:
   - [ ] Changes UI
   - [ ] Includes DB Migration
   - [x] Introduces new feature or API (adds optional `force_nonce` to the
         chart-data query context; ignored when `force` is false)
   - [ ] Removes existing feature or API
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to