Skip to content

[BUG] Timestamp column nullable type mismatch causes coredump in RowBinlogSegmentWriter::_fill_binlog_columns #64494

Open

Description

@heguanhui

Search before asking

  • I had searched in the issues and found no similar issues.

Version

master

What's Wrong?

Issue 标题
text
[BUG] Timestamp column nullable type mismatch causes coredump in RowBinlogSegmentWriter::_fill_binlog_columns
Issue 正文
markdown

Problem Description

After fixing the binlog column index issue (PR #xxxxx), another coredump occurs when running GroupRowsetWriterTest.sub_writer_rollback:
[ RUN ] GroupRowsetWriterTest.sub_writer_rollback
F20260614 02:56:27.868108 376 status.h:472] Bad cast from type:doris::ColumnVector<(doris::PrimitiveType)6>* to doris::ColumnNullable*
*** Check failure stack trace: ***
@ 0x5566859035cf google::LogMessage::SendToLog()
@ 0x5566858f9be0 google::LogMessage::Flush()
@ 0x5566858fd2d9 google::LogMessageFatal::~LogMessageFatal()
@ 0x5566640864af doris::Status::FatalError<>()
@ 0x55666d9a69b7 ZZ11assert_castIPN5doris14ColumnNullableEL18TypeCheckOnRelease1ERPNS0_7IColumnEET_OT1_ENKUlOS7_E_clIS6_EES2_SA
@ 0x55666d9a5e75 assert_cast<>()
@ 0x55668225e916 doris::segment_v2::RowBinlogSegmentWriter::_fill_binlog_columns()
@ 0x55668225a39a doris::segment_v2::RowBinlogSegmentWriter::append_block()
@ 0x556681d53a74 doris::SegmentFlusher::_add_rows()
@ 0x556681d4e445 doris::SegmentFlusher::flush_single_block()
@ 0x556681d5742a doris::SegmentCreator::flush_single_block()
@ 0x556681c9950c doris::SegmentCreator::flush_single_block()
@ 0x556681c59c5f doris::BaseBetaRowsetWriter::flush_single_block()
@ 0x556681cae1bb doris::GroupRowsetWriter::flush_single_block()
@ 0x55666c293f62 doris::GroupRowsetWriterTest_sub_writer_rollback_Test::TestBody()

text

Steps to Reproduce

cd be/ut_build_dir
./run-be-ut.sh --run GroupRowsetWriterTest.sub_writer_rollback
Root Cause
In RowBinlogSegmentWriter::_fill_binlog_columns(), the timestamp column (__DORIS_BINLOG_TIMESTAMP__) is cast to ColumnNullable* using assert_cast:

cpp
IColumn* ts_col_ptr = binlog_prefix_columns[2].get();
auto* ts_nullable_column = assert_cast<ColumnNullable*>(ts_col_ptr);
ts_nullable_column->insert_many_defaults(num_rows);
However, in the unit test environment, the timestamp column is not wrapped as ColumnNullable (it's a plain ColumnVector), causing the assert_cast to fail.

Expected Behavior
The timestamp column should always be nullable by design. But in the current unit test setup, it is not. The code should handle both cases gracefully or the test environment should be fixed to match production behavior.

Proposed Fix
Use check_and_get_column to safely handle both nullable and non-nullable cases:

cpp
IColumn* ts_col_ptr = binlog_prefix_columns[2].get();
auto* ts_nullable_column = check_and_get_column<ColumnNullable>(ts_col_ptr);
if (ts_nullable_column != nullptr) {
    ts_nullable_column->insert_many_defaults(num_rows);
} else {
    ts_col_ptr->insert_many_defaults(num_rows);
}
Additional Context
This issue was discovered while testing PR #xxxxx (binlog column index fix)

The timestamp column is defined as nullable in the schema

The problem only occurs in unit tests, not in production

Environment
Doris version: master branch

Build with ASAN enabled
### Labels
type/bug

component/be

feature/binlog

UT

### What You Expected?

ut executed successfully

### How to Reproduce?

_No response_

### Anything Else?

_No response_

### Are you willing to submit PR?

- [x] Yes I am willing to submit a PR!

### Code of Conduct

- [x] I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct)

Activity

  1. zclllyybb commented on Jun 15, 2026

    @zclllyybb
    Contributor

    Breakwater-GitHub-Analysis-Slot: slot_498e1c5a18e8

    Initial analysis: the reported failure is credible on current upstream/master and the stack matches a row-binlog schema construction mismatch in the BE unit-test path. I checked refreshed upstream/master at 1316d4bd633.

    What the code shows:

    • RowBinlogSegmentWriter::_fill_binlog_columns() builds a block for the row-binlog prefix columns and unconditionally casts the timestamp column to ColumnNullable before inserting the NULL placeholder.
    • TabletSchema::create_block_by_cids() creates that column from DataTypeFactory::create_data_type(TabletColumn), so the runtime column is nullable only if the tablet schema column has is_nullable = true.
    • The production FE row-binlog schema construction makes __DORIS_BINLOG_OP__ and __DORIS_BINLOG_TIMESTAMP__ nullable.
    • The failing UT does not go through that FE schema path. GroupRowsetWriterTest uses testutil::enable_row_binlog(), and the current test helper's CreateTabletRequestColumnDef has no nullable field; create_tablet_column() does not set TColumn.is_allow_null. Later TabletMeta::init_column_from_tcolumn() copies tcolumn.is_allow_null into ColumnPB.is_nullable, so the test-created timestamp column becomes a plain non-null BIGINT vector. That explains the fatal cast from ColumnVector<TYPE_BIGINT> to ColumnNullable.

    Conclusion:

    • This looks like a real master UT bug, but the strongest evidence points to the BE test schema helper not matching the intended row-binlog schema semantics, rather than proving that production FE-created row-binlog schemas have a non-null timestamp column.
    • The crash was probably exposed after the previous row-binlog special-index fix because the writer can now reach _fill_binlog_columns().

    Recommended next steps:

    • Prefer fixing the test/schema construction path so the row-binlog timestamp column is nullable, for example by adding nullable support to CreateTabletRequestColumnDef / create_tablet_column() or explicitly setting is_allow_null for __DORIS_BINLOG_OP__ and __DORIS_BINLOG_TIMESTAMP__ in testutil::enable_row_binlog().
    • Add regression assertions in GroupRowsetWriterTest or the row-binlog metadata test that the row-binlog schema has valid LSN/timestamp special indexes and that __DORIS_BINLOG_TIMESTAMP__ is nullable, not only that the column name exists.
    • A defensive writer-side fallback using check_and_get_column<ColumnNullable>() would likely unblock this exact UT because the reader later replaces the TSO placeholder with commit TSO and already handles nullable/non-nullable output. However, I would not make that the only fix unless maintainers intentionally want to tolerate older or malformed row-binlog schemas; otherwise it can hide a schema invariant violation that the writer comments currently assume.

    Missing information to fully close this:

    • The exact PR/commit for the prior "binlog column index" fix mentioned in the issue.
    • Whether this is reproducible only through GroupRowsetWriterTest.sub_writer_rollback, or also with a real FE-created row-binlog table. If it reproduces outside BE UT, please provide the exact commit SHA, table DDL/binlog properties, tablet meta/schema dump for the row-binlog tablet, and the full BE fatal log/core stack.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions