|
| 1 | +# Cooperative external scan scheduling |
| 2 | + |
| 3 | +A statement obtains its stage schedulers through |
| 4 | +`ConnectorSession.getTaskScheduler`. The engine owns the task groups and closes |
| 5 | +them with the statement. Connectors submit neutral `Runnable` tasks through this |
| 6 | +SPI; they do not import FE executor classes. |
| 7 | + |
| 8 | +The two stages have independent workers: |
| 9 | + |
| 10 | +- `SPLIT` advances scan producers and assigns ranges to the BE split queues. |
| 11 | +- `METADATA` performs independent operations such as cold Manifest reads. |
| 12 | + |
| 13 | +A producer may need metadata tasks to finish. Keeping those tasks out of the |
| 14 | +producer pool avoids a pool full of parents waiting for children queued behind |
| 15 | +them. A task must not block waiting for children submitted to its own executor. |
| 16 | + |
| 17 | +## Admission and continuation |
| 18 | + |
| 19 | +Each executor has a concurrent FIFO ready queue containing task groups. A |
| 20 | +worker claims a task from the next group, republishes that group if it still |
| 21 | +has pending work, and then executes the task outside queue synchronization. |
| 22 | +This allows several workers to serve one query and gives other ready queries |
| 23 | +an opportunity at subsequent task boundaries. It does not preempt a running |
| 24 | +task or guarantee equal CPU time. |
| 25 | + |
| 26 | +`tryExecute` never waits for space. A full group queue registers the supplied |
| 27 | +`onSpace` callback and returns `false`; the caller retains the task until it |
| 28 | +can retry. Reuse the same callback identity and remove it when the task is |
| 29 | +cancelled or no longer needs admission. Failures are delivered through the |
| 30 | +failure callback rather than silently dropping work. |
| 31 | + |
| 32 | +A serial producer can use `tryExecuteContinuation` for bounded further work. |
| 33 | +The group still returns behind other ready groups, but a worker need not wake |
| 34 | +another worker just to execute its own serial continuation. Initial tasks, |
| 35 | +external admission and recovery from backpressure retain the normal wakeup |
| 36 | +protocol. Split production yields after at most 16 source batches or after |
| 37 | +observing 1 ms elapsed between batches. One blocking batch can exceed that |
| 38 | +interval; the bound is cooperative. |
| 39 | + |
| 40 | +## Iceberg Manifest reads |
| 41 | + |
| 42 | +Cold Manifest prefetch uses a bounded window and a statement-wide read budget. |
| 43 | +Within that window, completed reads can be consumed before a slower earlier |
| 44 | +Manifest. First-split production does not wait for speculative prefetch when |
| 45 | +one Manifest already supplies output; subsequent BE demand enables further |
| 46 | +prefetch. Filtered or rewrite-scoped scans must still advance while no output |
| 47 | +has been found. |
| 48 | + |
| 49 | +Closing a scan stops new submissions and wakes completion waiters. Load failures |
| 50 | +propagate through the corresponding future. A running shared cache load is not |
| 51 | +interrupted just because one waiting scan stops. Authentication and plugin |
| 52 | +thread-context class loading remain scoped around each actual metadata read. |
| 53 | +Cached partition values stay immutable: adapt a file task to the pinned scan |
| 54 | +schema at the scan boundary. Select the format from each file range rather |
| 55 | +than planning files merely to infer a table-wide format. |
| 56 | + |
| 57 | +The captured scheduler exposes its closed state even after query cleanup removes |
| 58 | +the statement registry. A Manifest reader waiting for another scan's read permit |
| 59 | +checks this state at most 100 ms apart and rechecks after admission. Cancellation |
| 60 | +therefore stops that waiter without releasing another scan's permit or starting |
| 61 | +another remote read. This does not interrupt an already running shared cache load. |
| 62 | +Hive checks the same captured state between directory requests, including when |
| 63 | +empty partitions are skipped within one iterator call. |
| 64 | + |
| 65 | +## Configuration and limits |
| 66 | + |
| 67 | +| Setting | Default | Meaning | |
| 68 | +|---|---:|---| |
| 69 | +| FE `external_scan_split_threads` | 64 | Worker threads in the producer scheduler | |
| 70 | +| FE `external_scan_metadata_threads` | 64 | Worker threads in the metadata scheduler | |
| 71 | +| FE `external_scan_task_queue_capacity` | 128 | Pending tasks per query in each stage | |
| 72 | +| Session `iceberg_manifest_load_concurrency` | 8 | Manifest read budget per query and window bound per scan; 1 uses sequential loading | |
| 73 | + |
| 74 | +These task queues are distinct from `split_queue_capacity`, which bounds the |
| 75 | +produced Split buffer. Queue capacity does not reserve that many running |
| 76 | +threads. Worker counts limit the respective pools, and the query read budget |
| 77 | +also covers caller-assisted Manifest loading. |
| 78 | + |
| 79 | +Workers are ordinary threads. Synchronous remote calls occupy workers until |
| 80 | +they return; FIFO admission cannot recover a worker blocked inside an SDK. |
| 81 | +Effective remote timeouts and dependency fault isolation are separate concerns. |
| 82 | + |
| 83 | +Planning-only attempts also run their query-finish callbacks when statement |
| 84 | +execution returns. This releases batch Split producers and metadata leases for |
| 85 | +EXPLAIN and planning failures even when no coordinator was registered. Normal |
| 86 | +coordinator unregistration remains idempotent. Deferred Arrow Flight queries keep |
| 87 | +those callbacks until their retained executor finalizes the query after consumption. |
| 88 | + |
| 89 | +Backend assignment has its own policy-state lock. First-split readiness and stop |
| 90 | +notifications use a separate short-held lock, so an already converted range can |
| 91 | +wake the planner while another lane is still assigning its batch. The policy's |
| 92 | +mutable load-balancing state remains serialized. |
| 93 | + |
| 94 | +The engine captures task-group cancellation on the statement executor before |
| 95 | +asynchronous work starts. KILL and timeout close those groups even before a |
| 96 | +coordinator exists; a group captured after cancellation is immediately closed. |
| 97 | +Cancellation listeners run outside their registry operation and are claimed once. |
| 98 | +This stops queued work and wakes future waiters without interrupting a running |
| 99 | +shared cache loader. Normal completion retains the separate read-lease cleanup. |
| 100 | + |
| 101 | +### Consumer completion notification |
| 102 | + |
| 103 | +Each backend has a bounded split queue and its own data-ready wait channel. A consumer registers before checking queue contents and terminal state, then waits only when neither is available. Successful enqueue, producer completion, failure and cancellation signal that channel. The empty queue is not periodically polled to discover EOF: otherwise finishing just after a poll begins adds 100 ms despite completed production. Producers avoid the channel monitor when no consumer waits; callbacks that resume producers run after releasing the consumer channel monitor. Cancellation signals consumers before closing SDK resources. |
| 104 | + |
| 105 | +### Startup demand |
| 106 | + |
| 107 | +With several partition producers, each publishes one batch before waiting for the first BE fetch. This supplies parallel scanning work without filling the entire configured buffer before dispatch. Registration and the first fetch share the queue-space lock; the first consumer resumes all startup waiters outside that lock. Producers then use the full configured capacity and normal cooperative scheduling. A single streaming producer, including Iceberg Manifest planning, does not wait at this boundary. Cancellation closes waiting producers through their existing query-owned resources; failures wake startup waiters explicitly. No new file queue or concurrency limit is introduced. |
0 commit comments