morningman commented on PR #68713:
URL: https://github.com/apache/doris/pull/68713#issuecomment-6080975144

   @Gabriel39 Thanks — confirmed, and it is reachable earlier than either of us 
described. Fixed in cddb40bec0b: no thread waits at the gate any more.
   
   **Where it bites.** The binding limit is not the pool's thread cap 
(`max(512, 10 x cores)`, which my reply to the bot leaned on) but the context's 
admission: once a workload group's remote scan scheduler has 
`min_active_file_scan_threads` (8 x cores by default) tasks active or queued — 
and a thread blocked in `acquire()` counts as active — each context is held to 
`_min_scan_concurrency` (1 for file scans) in flight 
(`ScannerContext::_get_margin`, `can_admit_scan_task`). A holder whose block 
the operator consumed then goes back to `_pending_tasks` while its context 
still has a waiter in flight, and nothing reconsiders it until that waiter 
completes, which needs the holder's share. On a 16-core BE, nine concurrent 
`SELECT *` over a fluss primary-key table of 16 declaring buckets get there; 
the waits then run out together and every waiter opens above the budget.
   
   **What changed.**
   - `JniScanHeapGate::request(bytes, stop_waiting)` returns an `Admission` at 
once: admitted when the reader fits, stopped when its scan already stopped, 
otherwise waiting with a `SharedListenableFuture<Void>`. The gate has a thread 
of its own that completes those futures — never inline on the thread that gave 
a share back, which may hold its context's lock (`stop_scanners()` closing 
scanners) — and every 100 ms polls stopped scans (`IOContext::should_stop`, set 
by `try_stop()`), the wait limit and a changed budget.
   - A JNI reader that has to wait opens nothing and reports it through 
`waiting_for()` (`TableReader::waiting_for()`, forwarded by the hybrid readers; 
V1 `JniReader` likewise). `FileScannerV2` / `FileScanner` end the turn without 
a block, and `_scanner_scan` parks the task: `ScanTask::State::PARKED`, out of 
`_in_flight_tasks_num`, so it holds neither a worker nor a slot, and the 
context schedules again since that slot may now admit a pending holder. When 
the future is done the task is scheduled like a task whose block was just 
consumed (`schedule_scan_task(ctx, task, lock)`), on either scheduler, and the 
reader then opens its Java scanner — or ends its split if the scan stopped 
first.
   
   **Tests.**
   - The scheduler interaction you asked for: 
`ScannerContextTest.{thread_pool,task_executor}_heap_waiters_park_and_leave_the_worker_to_the_holder`
 run three scanners that each declare the whole budget on a one-worker pool, 
and `thread_pool_heap_waiters_leave_the_busy_pools_slot_to_the_holder` holds 
the context to one task in flight after the waiters have parked. Every scanner 
finishes, with `admitted_after_wait_limit() == 0` and the wait limit at 10 
minutes; with a blocking wait each of them would have stalled until it. 
`thread_pool_stopping_a_context_ends_its_parked_tasks` covers the other way 
out: a context stopped while its scanners wait sees them leave the line without 
a share, and nothing runs again.
   - The burst: 
`JniScanHeapGateTest.ReadersThatWaitedTooLongTogetherAreAllAdmittedAboveTheBudget`
 pins it down (four waiters past the limit are admitted together, 1300 MB on a 
500 MB budget), and `FuturesAreCompletedOnTheGatesOwnThread` the threading rule 
above.
   - 269 BE tests in 31 suites pass (scan scheduling, both file scanners, the 
JNI readers, the paimon/fluss/hudi readers, the task executor); the new cases 
also 20 times, shuffled. A `JniTableReaderTest` pair checks the reader side: a 
split that does not fit returns from `prepare_split()` without touching the 
JVM, and one whose scan stops while it waits opens nothing.
   
   **Why the wait limit stays.** Parking removes the dependency on workers and 
on the context's slot, but not one on a consumer: in a join whose two sides 
both declare, the probe side's scanners take shares, produce a block each and 
keep the shares while the probe waits for the build, and the build side's later 
splits queue behind them. Only the limit ends that, so "without timeout 
admission" holds for the worker/slot case your test describes, not for this 
one. The config now says the limit is per split, and that the wait holds no 
thread.
   
   I'm also reproducing it end to end on a local BE - the old and the new build 
with `min_active_file_scan_threads` lowered - and will add the numbers here.
   
   The parked state is new to the scan scheduler, so I'd value your eyes on 
`ScannerContext::park_scan_task` / `_resume_parked_task` in particular.
   


-- 
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