FrankMormino commented on issue #66365:
URL: https://github.com/apache/doris/issues/66365#issuecomment-6152084604

   We hit what looks like the same bug on **4.1.3** (release build) and have a 
CPU profile that points to a root cause: an infinite loop in the RLE decoder 
for BOOLEAN columns during the lazy-materialization row fetch. The relevant 
code is unchanged in 4.1.4.1 and master.
   
   ### Symptom
   Scan threads of the workload group's remote-scan pool (`rs_normal`, 16 
threads) get stuck one at a time at 100% CPU and never return. The query is 
cancelled at `query_timeout` (900 s), but the thread keeps spinning. Over about 
12 days all 16 threads were stuck and the BE sat at ~1560% CPU. Only a BE 
restart frees them. The tables have many BOOLEAN / `tinyint(1)` columns (Unique 
Key MoW, Kafka-sink loaded); the trigger is ad hoc `SELECT ... ORDER BY ... 
LIMIT n` (n <= 1024).
   
   ### Stack (gperftools `/pprof/profile`, 10 s; every hot sample is in these 
frames)
   ```
   BitReader::GetVlqInt
   RleDecoder<bool>::ReadHeader
   RleDecoder<bool>::Skip
   RlePageDecoder<FieldType::OLAP_FIELD_TYPE_BOOL>::read_by_rowids
   FileColumnIterator::read_by_rowids
   RowIdStorageReader::read_by_rowids
   PInternalService::multiget_data_v2
   ```
   
   ### Why it never ends
   `be/src/util/rle_encoding.h`, `RleDecoder<T>::Skip`:
   ```cpp
   while (to_skip > 0) {
       bool result = ReadHeader();
       DCHECK(result);
       ...
   }
   ```
   Once the bit reader is exhausted, `ReadHeader()` returns false and leaves 
`repeat_count_` / `literal_count_` at 0, so `to_skip` never decreases. `DCHECK` 
is a no-op in release builds, and there is no cancellation check, so the loop 
spins forever.
   
   Likely entry: `be/src/storage/segment/rle_page.h`, 
`RlePageDecoder::read_by_rowids`:
   ```cpp
   ordinal_t ord = rowids[i] - page_first_ordinal;
   ...
   _rle_decoder.Skip(ord - _cur_index);
   ```
   If row ids reach the page out of order or duplicated (`ord < _cur_index`), 
the unsigned `ord - _cur_index` underflows to a huge value and `Skip` runs past 
the end of the page. In `rowid_fetcher.cpp` the internal-table path groups row 
ids by consecutive file id but does not sort or dedup them within a segment; 
the external-table path does both.
   
   ### Workaround (confirmed)
   `SET GLOBAL topn_lazy_materialization_threshold = -1;` (or `0`, as in this 
issue), then restart the BE to free the threads that are already stuck. 4.1.4.1 
alone does not help.
   
   ### Suggested fix
   1. `RleDecoder::Skip` / `Get`: stop (return 0 / false) when `ReadHeader()` 
fails instead of looping.
   2. `RlePageDecoder::read_by_rowids`: handle `ord < _cur_index` (re-seek the 
decoder) instead of skipping by an underflowed delta.
   3. Sort and dedup row ids per segment in the internal multiget path, as the 
external path does.
   4. Add a cancellation check to the multiget read loop so a stuck fetch 
cannot outlive its query.
   


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