github-actions[bot] commented on code in PR #66230:
URL: https://github.com/apache/doris/pull/66230#discussion_r3674229055


##########
be/src/load/group_commit/wal/wal_reader.cpp:
##########
@@ -99,13 +99,42 @@ Status WalReader::_do_get_next_block(Block* block, size_t* 
read_rows, bool* eof)
             return Status::InternalError("read wal {} fail, pos {}, columns 
size {}", _wal_path,
                                          pos, src_block.columns());
         }
-        ColumnPtr column_ptr = src_block.get_by_position(pos).column;
-        if (!column_ptr && slot_desc->is_nullable()) {
-            column_ptr = make_nullable(column_ptr);
+        const auto& source_column = src_block.get_by_position(pos);
+        const auto& target_column = output_block_columns[index];
+        auto source_status = source_column.check_type_and_column_match();
+        if (!source_status.ok()) {
+            return Status::InternalError(
+                    "Invalid WAL column while replaying WAL {}: slot={} 
(unique_id={}), "
+                    "source_position={}, error={}",
+                    _wal_path, slot_desc->col_name(), 
slot_desc->col_unique_id(), pos,

Review Comment:
   [P1] Roll back the replay transaction before requeueing this error
   
   These new mismatch returns propagate through the fragment future, but 
`WalTable::_handle_stream_load` calls 
`RETURN_IF_ERROR(ctx->load_status_future.get())` before its explicit 
`rollback_txn`. This native-insert path gets `txn_id` from the FE plan and 
leaves `need_rollback` false, so neither the callback nor `StreamLoadContext`'s 
destructor rolls the failed transaction back. Normal mode only attempts an 
abort at the start of the next retry; with 
`group_commit_wait_replay_wal_finish=true` that abort is skipped entirely, and 
the same label can exhaust its retries before the WAL is moved to `tmp` and the 
waiter is notified without a failure value. Please propagate the future failure 
into both the returned status and `ctx->status`, restore internal 
token/auth-code authentication, and complete rollback before requeueing it.



##########
be/src/load/group_commit/wal/wal_reader.cpp:
##########
@@ -99,13 +99,42 @@ Status WalReader::_do_get_next_block(Block* block, size_t* 
read_rows, bool* eof)
             return Status::InternalError("read wal {} fail, pos {}, columns 
size {}", _wal_path,
                                          pos, src_block.columns());
         }
-        ColumnPtr column_ptr = src_block.get_by_position(pos).column;
-        if (!column_ptr && slot_desc->is_nullable()) {
-            column_ptr = make_nullable(column_ptr);
+        const auto& source_column = src_block.get_by_position(pos);
+        const auto& target_column = output_block_columns[index];
+        auto source_status = source_column.check_type_and_column_match();
+        if (!source_status.ok()) {
+            return Status::InternalError(
+                    "Invalid WAL column while replaying WAL {}: slot={} 
(unique_id={}), "
+                    "source_position={}, error={}",
+                    _wal_path, slot_desc->col_name(), 
slot_desc->col_unique_id(), pos,
+                    source_status.to_string());
         }
-        dst_block.insert(index, ColumnWithTypeAndName(std::move(column_ptr),
-                                                      
output_block_columns[index].type,
-                                                      
output_block_columns[index].name));
+
+        ColumnWithTypeAndName replay_column(source_column.column, 
target_column.type,
+                                            target_column.name);
+        auto replay_status = replay_column.check_type_and_column_match();

Review Comment:
   [P1] Preserve the serialized source type through compatible schema changes
   
   This target-type validation rejects retained-ID schema changes before the 
load scanner can cast them. For example, a light `ARRAY<INT> -> ARRAY<BIGINT>` 
change can leave an older WAL whose `ColumnArray` contains `Int32`; relabeling 
it as the current `ARRAY<BIGINT>` fails here even though 
`FileScanner::_cast_to_input_block` has the element-wise conversion. Heavy 
scalar changes are normally fenced by the WAL drain, but its timeout or an 
unavailable BE can likewise leave an old `INT` WAL after the current type 
becomes `BIGINT/STRING`, and retry will never make it match. Please retain 
`source_column.type` and apply a schema-compatibility decision before the 
existing CAST for supported same-shape conversions; shape evolution such as 
appended STRUCT fields also needs explicit field/default mapping because the 
legacy struct CAST requires equal arity.



##########
be/src/load/group_commit/wal/wal_reader.cpp:
##########
@@ -99,13 +99,42 @@ Status WalReader::_do_get_next_block(Block* block, size_t* 
read_rows, bool* eof)
             return Status::InternalError("read wal {} fail, pos {}, columns 
size {}", _wal_path,
                                          pos, src_block.columns());
         }
-        ColumnPtr column_ptr = src_block.get_by_position(pos).column;
-        if (!column_ptr && slot_desc->is_nullable()) {
-            column_ptr = make_nullable(column_ptr);
+        const auto& source_column = src_block.get_by_position(pos);
+        const auto& target_column = output_block_columns[index];
+        auto source_status = source_column.check_type_and_column_match();
+        if (!source_status.ok()) {
+            return Status::InternalError(
+                    "Invalid WAL column while replaying WAL {}: slot={} 
(unique_id={}), "

Review Comment:
   [P2] Add executable coverage for the mismatch-and-retry path
   
   This changes a kernel replay path to replace a fatal projection cast with 
compatibility checks and retryable statuses, but the PR adds no test for that 
behavior. The helper test only checks hand-built columns, the WAL writer/reader 
test round-trips matching types, and the only legacy scanner tests are 
disabled/count-only. Please add enabled serialized-WAL coverage that 
distinguishes a supported promotion (which should reach the existing CAST) from 
a genuinely incompatible mapping and `AGG_STATE` signature/layout (which should 
return a contextual error), and verify authenticated rollback, WAL 
retention/retry, and wait-mode exhaustion signaling after a fragment failure.



##########
be/src/load/group_commit/wal/wal_reader.cpp:
##########
@@ -99,13 +99,42 @@ Status WalReader::_do_get_next_block(Block* block, size_t* 
read_rows, bool* eof)
             return Status::InternalError("read wal {} fail, pos {}, columns 
size {}", _wal_path,
                                          pos, src_block.columns());
         }
-        ColumnPtr column_ptr = src_block.get_by_position(pos).column;
-        if (!column_ptr && slot_desc->is_nullable()) {
-            column_ptr = make_nullable(column_ptr);
+        const auto& source_column = src_block.get_by_position(pos);
+        const auto& target_column = output_block_columns[index];
+        auto source_status = source_column.check_type_and_column_match();
+        if (!source_status.ok()) {
+            return Status::InternalError(
+                    "Invalid WAL column while replaying WAL {}: slot={} 
(unique_id={}), "
+                    "source_position={}, error={}",
+                    _wal_path, slot_desc->col_name(), 
slot_desc->col_unique_id(), pos,
+                    source_status.to_string());
         }
-        dst_block.insert(index, ColumnWithTypeAndName(std::move(column_ptr),
-                                                      
output_block_columns[index].type,
-                                                      
output_block_columns[index].name));
+
+        ColumnWithTypeAndName replay_column(source_column.column, 
target_column.type,
+                                            target_column.name);
+        auto replay_status = replay_column.check_type_and_column_match();

Review Comment:
   [P1] Validate the complete AGG_STATE signature and storage layout
   
   This compatibility check is too weak for `AGG_STATE`. If an old `sum(INT)` 
WAL survives the bounded schema-change drain after the column becomes 
`avg(BIGINT)`, FE has accepted a changed AGG_STATE type, but BE 
`DataTypeAggState::equals` still compares only the dynamic type and its 
`DataTypeFixedLengthObject` checker ignores `item_size`. The source check, this 
replay check, and the later equality can therefore accept payloads with 
different sizes and meanings. The downstream CAST also reduces both states to 
`DataTypeFixedLengthObject` and selects the identity path, so sum bytes can be 
interpreted as an avg state. Please compare the full 
function/argument/nullability/version signature and validate the serialized 
layout (including fixed item size) before relabeling the column.



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