liaoxin01 commented on PR #65584:
URL: https://github.com/apache/doris/pull/65584#issuecomment-5302697310

   Reviewed the whole close path against master. The direction is right — 
pulling "register" and "all-received check" into one lock scope does eliminate 
the orphaned stream, and moving `brpc::StreamClose` out of `_lock` onto a local 
vector is a real improvement on its own (master iterates the *member* 
`_closing_stream_ids` while holding `_lock`, and `on_closed` -> `clear_load` 
deletes `this`). A few things I'd like addressed before merge.
   
   **1. [Medium] The `_all_close_load_received` latch is unnecessary, and it 
can bypass the fence.**
   
   Re-evaluating `_close_load_cnt >= _total_streams` inside 
`mark_eos_sent_and_collect()` is already sufficient, because registration and 
the check now share one lock:
   - T1 marks first (cnt < N, parks A, no drain) -> T2 marks (cnt == N, parks 
B, drains A+B). OK
   - T2 marks first (cnt == N, drains to B) -> T1 marks (parks A, re-checks cnt 
>= N, drains A). OK
   
   The latch, however, is write-once and never reset, while `_total_streams` is 
*not* constant: for a LoadStream created by an incremental open 
(`_is_incremental = (_total_streams == 0)`), `add_source()` keeps incrementing 
it (load_stream.h:131-137). So:
   - source A opens 1 incremental stream -> `_total_streams = 1`; A's 
CLOSE_LOAD -> cnt=1 >= 1 -> **latch set**
   - sources B and C open later -> `_total_streams = 3`
   - B's CLOSE_LOAD -> cnt=2 < 3. master would park B and wait for C; with the 
sticky latch B is `StreamClose`d immediately, skipping the #56120 fence.
   
   Suggest dropping `_all_close_load_received` entirely and just doing:
   
   ```cpp
   if (_close_load_cnt >= _total_streams) {
       to_close.insert(to_close.end(), _eos_sent_stream_ids.begin(), 
_eos_sent_stream_ids.end());
       _eos_sent_stream_ids.clear();
   }
   ```
   
   **2. [Medium] `is_incremental` is a misnomer, and the new comment built on 
it is inverted.**
   
   `hdr.num_incremental_streams()` means "how many incremental destinations 
*this source* opened", not "this stream is incremental". 
`LoadStreamMap::close_load()` passes `_num_incremental_streams` for **both** 
groups (load_stream_map_pool.cpp:125), and vtablet_writer_v2.cpp:737-743 states 
explicitly that a source's *non-incremental* CLOSE_LOAD also carries 
`num_incremental_streams > 0` so the destination defers StreamClose.
   
   So this comment is the opposite of what happens:
   
   > `// A non-incremental stream is closed as soon as its own CLOSE_LOAD (and 
EOS) is handled -- ... not subject to fencing.`
   
   Behavior matches master (no bug introduced), but the comment will mislead 
the next person touching this code. Please rename to something like 
`sender_has_incremental_streams` / `needs_close_fence` and rewrite the comment.
   
   **3. [Medium] The PR description's "second hazard" (close-before-EOS) does 
not exist on master.**
   
   master already does `push_back(id)` *after* `_report_result(id, ...)`, so 
every drained id had its EOS sent; this PR keeps the exact same ordering. The 
`Stream closed without EOS` reports are more likely the *tail* of the same bug: 
the orphaned stream eventually hits `on_idle_timeout`, which calls 
`brpc::StreamClose` without EOS (load_stream.cpp:795). Worth correcting in the 
description so the backports aren't evaluated against a hazard that isn't there.
   
   **Minor**
   
   - `mark_eos_sent_and_collect` is declared `public` but only `_dispatch` uses 
it (no test calls it). Should be private, and per BE convention `_`-prefixed. 
Also the name doesn't quite fit — nothing is "marked", it registers + collects.
   - `_eos_sent_stream_ids`: every stream reaching this function has had its 
EOS sent, including the ones returned directly. The vector actually holds 
fence-deferred ids — `_deferred_close_stream_ids` reads better.
   - `close()`'s return value is now consumed *only* by the debug point 
(load_stream.cpp:799 / :812). Either move the condition inside the 
`DBUG_EXECUTE_IF` block or add a note, otherwise it looks like it still carries 
business meaning.
   - UT name typo: `incremental_close_race_orhpans_streams` -> `orphans`.
   - In the UT, `config::enable_debug_points` / debug point cleanup isn't RAII; 
the `ASSERT_TRUE(debug_points->is_enable(...))` in the middle returns early on 
failure and leaves the global config set to `true` for the rest of the test 
binary. A `Defer` would fix it.
   - The debug point hardcodes `bthread_usleep(3000000)`. Doris convention is a 
parameterized sleep, e.g. `auto sleep_sec = dp->param<int32_t>("sleep_sec", 
1);` (vtablet_writer_v2.cpp:674). As written the UT always costs ~7s.
   - The docker suite only covers `cloudMode = false`; the receiver-side path 
is identical in cloud mode. Either cover both or note why not.
   - `deadline = 60000` for "create 50 partitions on a 3-BE docker cluster with 
a 3s sleep per incremental stream" may be tight on a loaded CI. Consider 
raising it, or attaching load state to the timeout message so a flake is 
diagnosable.
   - Optional observability: log the drain (`to_close.size()` and remaining 
parked count). Orphaned-stream issues are very hard to diagnose from logs today.
   
   **Compatibility**: no issues found — BE-process-local state only, no 
RPC/thrift/protobuf/metadata/config changes, and the debug point is inert 
unless `enable_debug_points=true`.
   
   **Backport note**: all three labelled branches contain 
`_closing_stream_ids`, so the fix is needed on all of them, but on branch-3.1 / 
branch-4.0 the file lives at `be/src/runtime/load_stream.cpp` (only 4.1 and 
master use `be/src/load/channel/`), so the cherry-pick will conflict.
   


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