This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new ad06c797216 [fix](storage) Fix linker error for
SegmentWriter::_is_mow() with -O1/-O3 (#63928)
ad06c797216 is described below
commit ad06c7972164b2bcb34b2732b77025bdd3ed3877
Author: heguanhui <[email protected]>
AuthorDate: Tue Jun 9 09:54:35 2026 +0800
[fix](storage) Fix linker error for SegmentWriter::_is_mow() with -O1/-O3
(#63928)
### What problem does this PR solve?
Issue Number: close #63927
Problem Summary: Building BE unit tests with TSAN (`-O1`) or RELEASE
(`-O3`) causes undefined reference linker errors for
`SegmentWriter::_is_mow()`, `SegmentWriter::_is_mow_with_cluster_key()`,
and their `VerticalSegmentWriter` counterparts.
**Root cause**: Commit `74aa0ca63a` defined these methods with the
`inline` keyword in `.cpp` files (`segment_writer.cpp` and
`vertical_segment_writer.cpp`). At `-O0` (ASAN), the compiler ignores
`inline` and exports the symbol, so linking succeeds. At `-O1`/`-O3`,
the compiler inlines the function and does NOT export the symbol. When
commit `4616202fc4` introduced `TestSegmentWriter` (a friend subclass)
in `test_segment_writer.h` that calls these private methods from a
different translation unit, the linker cannot find the symbols.
**Fix**: Move the `inline` definitions from `.cpp` files into the header
files as class-internal inline definitions, so they are visible to all
translation units.
**Error example** (TSAN build):
```
undefined reference to 'doris::segment_v2::SegmentWriter::_is_mow()'
undefined reference to
'doris::segment_v2::SegmentWriter::_is_mow_with_cluster_key()'
undefined reference to 'doris::segment_v2::VerticalSegmentWriter::_is_mow()'
undefined reference to
'doris::segment_v2::VerticalSegmentWriter::_is_mow_with_cluster_key()'
```
### Release note
None
### Check List (For Author)
- Test
- [x] Unit Test: Added `segment_writer_mow_check_test.cpp` with 7 test
cases covering all logic branches for both `SegmentWriter` and
`VerticalSegmentWriter`
- [ ] Regression test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason <!-- Add your reason? -->
- Behavior changed:
- [x] No.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [x] No.
- [ ] Yes. <!-- Add document PR link here. eg:
https://github.com/apache/doris-website/pull/1214 -->
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label
Co-authored-by: root <root@DESKTOP-3AF37B>
---
be/src/storage/segment/segment_writer.cpp | 8 -
be/src/storage/segment/segment_writer.h | 8 +-
be/src/storage/segment/vertical_segment_writer.cpp | 8 -
be/src/storage/segment/vertical_segment_writer.h | 8 +-
.../segment/segment_writer_mow_check_test.cpp | 200 +++++++++++++++++++++
5 files changed, 212 insertions(+), 20 deletions(-)
diff --git a/be/src/storage/segment/segment_writer.cpp
b/be/src/storage/segment/segment_writer.cpp
index 00db2362b63..f5f63d923f7 100644
--- a/be/src/storage/segment/segment_writer.cpp
+++ b/be/src/storage/segment/segment_writer.cpp
@@ -1244,13 +1244,5 @@ Status
SegmentWriter::_generate_short_key_index(std::vector<IOlapColumnDataAcces
return Status::OK();
}
-bool SegmentWriter::_is_mow() {
- return _tablet_schema->keys_type() == UNIQUE_KEYS &&
_opts.enable_unique_key_merge_on_write;
-}
-
-bool SegmentWriter::_is_mow_with_cluster_key() {
- return _is_mow() && !_tablet_schema->cluster_key_uids().empty();
-}
-
} // namespace segment_v2
} // namespace doris
diff --git a/be/src/storage/segment/segment_writer.h
b/be/src/storage/segment/segment_writer.h
index c98a6cead20..e6bba442bd4 100644
--- a/be/src/storage/segment/segment_writer.h
+++ b/be/src/storage/segment/segment_writer.h
@@ -189,8 +189,12 @@ private:
IOlapColumnDataAccessor* seq_column, size_t num_rows, bool
need_sort);
Status _generate_short_key_index(std::vector<IOlapColumnDataAccessor*>&
key_columns,
size_t num_rows, const
std::vector<size_t>& short_key_pos);
- bool _is_mow();
- bool _is_mow_with_cluster_key();
+ bool _is_mow() {
+ return _tablet_schema->keys_type() == UNIQUE_KEYS &&
_opts.enable_unique_key_merge_on_write;
+ }
+ bool _is_mow_with_cluster_key() {
+ return _is_mow() && !_tablet_schema->cluster_key_uids().empty();
+ }
protected:
// Build key index for derived writers that override append_block.
diff --git a/be/src/storage/segment/vertical_segment_writer.cpp
b/be/src/storage/segment/vertical_segment_writer.cpp
index fed06831fbd..6cf4cf1a1c6 100644
--- a/be/src/storage/segment/vertical_segment_writer.cpp
+++ b/be/src/storage/segment/vertical_segment_writer.cpp
@@ -1516,12 +1516,4 @@ void VerticalSegmentWriter::_set_max_key(const Slice&
key) {
_max_key.append(key.get_data(), key.get_size());
}
-inline bool VerticalSegmentWriter::_is_mow() {
- return _tablet_schema->keys_type() == UNIQUE_KEYS &&
_opts.enable_unique_key_merge_on_write;
-}
-
-inline bool VerticalSegmentWriter::_is_mow_with_cluster_key() {
- return _is_mow() && !_tablet_schema->cluster_key_uids().empty();
-}
-
} // namespace doris::segment_v2
diff --git a/be/src/storage/segment/vertical_segment_writer.h
b/be/src/storage/segment/vertical_segment_writer.h
index e6ee4933dd6..1a0645efa92 100644
--- a/be/src/storage/segment/vertical_segment_writer.h
+++ b/be/src/storage/segment/vertical_segment_writer.h
@@ -199,8 +199,12 @@ private:
Status _check_column_writer_disk_capacity(size_t cid);
Status _finalize_column_writer_and_update_meta(size_t cid);
- bool _is_mow();
- bool _is_mow_with_cluster_key();
+ bool _is_mow() {
+ return _tablet_schema->keys_type() == UNIQUE_KEYS &&
_opts.enable_unique_key_merge_on_write;
+ }
+ bool _is_mow_with_cluster_key() {
+ return _is_mow() && !_tablet_schema->cluster_key_uids().empty();
+ }
private:
friend class ::doris::BlockAggregator;
diff --git a/be/test/storage/segment/segment_writer_mow_check_test.cpp
b/be/test/storage/segment/segment_writer_mow_check_test.cpp
new file mode 100644
index 00000000000..e8be43a7fa4
--- /dev/null
+++ b/be/test/storage/segment/segment_writer_mow_check_test.cpp
@@ -0,0 +1,200 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+
+#include "io/fs/local_file_system.h"
+#include "storage/olap_common.h"
+#include "storage/rowset/rowset_id_generator.h"
+#include "storage/segment/segment_writer.h"
+#include "storage/segment/vertical_segment_writer.h"
+#include "storage/tablet/tablet_schema.h"
+
+namespace doris::segment_v2 {
+
+// Subclass in a different translation unit that calls _is_mow() and
_is_mow_with_cluster_key().
+// This test verifies that these inline private methods are visible across
translation units.
+// Previously they were defined with `inline` in .cpp files, which caused
linker errors
+// when compiled with -O1 or higher (TSAN/RELEASE), because the compiler
inlined them
+// and did not export the symbols.
+class TestSegmentWriterMowCheck : public SegmentWriter {
+public:
+ using SegmentWriter::SegmentWriter;
+
+ bool check_is_mow() { return _is_mow(); }
+ bool check_is_mow_with_cluster_key() { return _is_mow_with_cluster_key(); }
+};
+
+class TestVerticalSegmentWriterMowCheck : public VerticalSegmentWriter {
+public:
+ using VerticalSegmentWriter::VerticalSegmentWriter;
+
+ bool check_is_mow() { return _is_mow(); }
+ bool check_is_mow_with_cluster_key() { return _is_mow_with_cluster_key(); }
+};
+
+static const std::string kSegmentDir =
"./ut_dir/segment_writer_mow_check_test";
+
+TabletColumnPtr create_int_key(int32_t id) {
+ auto column = std::make_shared<TabletColumn>();
+ column->_unique_id = id;
+ column->_col_name = std::to_string(id);
+ column->_type = FieldType::OLAP_FIELD_TYPE_INT;
+ column->_is_key = true;
+ column->_is_nullable = false;
+ column->_length = 4;
+ column->_index_length = 4;
+ return column;
+}
+
+TabletColumnPtr create_int_value(int32_t id) {
+ auto column = std::make_shared<TabletColumn>();
+ column->_unique_id = id;
+ column->_col_name = std::to_string(id);
+ column->_type = FieldType::OLAP_FIELD_TYPE_INT;
+ column->_is_key = false;
+ column->_is_nullable = true;
+ column->_length = 4;
+ column->_index_length = 4;
+ return column;
+}
+
+TabletSchemaSPtr create_unique_key_schema() {
+ TabletSchemaSPtr schema = std::make_shared<TabletSchema>();
+ schema->append_column(*create_int_key(0));
+ schema->append_column(*create_int_value(1));
+ schema->_keys_type = UNIQUE_KEYS;
+ return schema;
+}
+
+TabletSchemaSPtr create_dup_key_schema() {
+ TabletSchemaSPtr schema = std::make_shared<TabletSchema>();
+ schema->append_column(*create_int_key(0));
+ schema->append_column(*create_int_value(1));
+ schema->_keys_type = DUP_KEYS;
+ return schema;
+}
+
+TabletSchemaSPtr create_unique_key_schema_with_cluster_key() {
+ auto schema = create_unique_key_schema();
+ schema->_cluster_key_uids = {1};
+ return schema;
+}
+
+class SegmentWriterMowCheckTest : public testing::Test {
+public:
+ void SetUp() override {
+ auto fs = io::global_local_filesystem();
+ auto st = fs->delete_directory(kSegmentDir);
+ ASSERT_TRUE(st.ok() || st.is<ErrorCode::NOT_FOUND>()) << st;
+ st = fs->create_directory(kSegmentDir);
+ ASSERT_TRUE(st.ok()) << st;
+ }
+
+ void TearDown() override {
+
EXPECT_TRUE(io::global_local_filesystem()->delete_directory(kSegmentDir).ok());
+ }
+
+ io::FileWriterPtr create_file_writer(size_t segment_id) {
+ RowsetId rowset_id;
+ rowset_id.init(1);
+ std::string filename = fmt::format("{}_{}.dat", rowset_id.to_string(),
segment_id);
+ std::string path = fmt::format("{}/{}", kSegmentDir, filename);
+ io::FileWriterPtr file_writer;
+ auto st = io::global_local_filesystem()->create_file(path,
&file_writer);
+ EXPECT_TRUE(st.ok()) << st;
+ return file_writer;
+ }
+};
+
+TEST_F(SegmentWriterMowCheckTest, segment_writer_is_mow_false_for_dup_key) {
+ auto schema = create_dup_key_schema();
+ SegmentWriterOptions opts;
+ opts.enable_unique_key_merge_on_write = true;
+ auto file_writer = create_file_writer(0);
+ TestSegmentWriterMowCheck writer(file_writer.get(), 0, schema, nullptr,
nullptr, opts, nullptr);
+ EXPECT_FALSE(writer.check_is_mow());
+ EXPECT_FALSE(writer.check_is_mow_with_cluster_key());
+}
+
+TEST_F(SegmentWriterMowCheckTest, segment_writer_is_mow_true_for_unique_mow) {
+ auto schema = create_unique_key_schema();
+ SegmentWriterOptions opts;
+ opts.enable_unique_key_merge_on_write = true;
+ auto file_writer = create_file_writer(1);
+ TestSegmentWriterMowCheck writer(file_writer.get(), 1, schema, nullptr,
nullptr, opts, nullptr);
+ EXPECT_TRUE(writer.check_is_mow());
+ EXPECT_FALSE(writer.check_is_mow_with_cluster_key());
+}
+
+TEST_F(SegmentWriterMowCheckTest,
segment_writer_is_mow_false_when_mow_disabled) {
+ auto schema = create_unique_key_schema();
+ SegmentWriterOptions opts;
+ opts.enable_unique_key_merge_on_write = false;
+ auto file_writer = create_file_writer(2);
+ TestSegmentWriterMowCheck writer(file_writer.get(), 2, schema, nullptr,
nullptr, opts, nullptr);
+ EXPECT_FALSE(writer.check_is_mow());
+ EXPECT_FALSE(writer.check_is_mow_with_cluster_key());
+}
+
+TEST_F(SegmentWriterMowCheckTest, segment_writer_is_mow_with_cluster_key) {
+ auto schema = create_unique_key_schema_with_cluster_key();
+ SegmentWriterOptions opts;
+ opts.enable_unique_key_merge_on_write = true;
+ auto file_writer = create_file_writer(3);
+ TestSegmentWriterMowCheck writer(file_writer.get(), 3, schema, nullptr,
nullptr, opts, nullptr);
+ EXPECT_TRUE(writer.check_is_mow());
+ EXPECT_TRUE(writer.check_is_mow_with_cluster_key());
+}
+
+TEST_F(SegmentWriterMowCheckTest,
vertical_segment_writer_is_mow_false_for_dup_key) {
+ auto schema = create_dup_key_schema();
+ VerticalSegmentWriterOptions opts;
+ opts.enable_unique_key_merge_on_write = true;
+ auto file_writer = create_file_writer(4);
+ TestVerticalSegmentWriterMowCheck writer(file_writer.get(), 4, schema,
nullptr, nullptr, opts,
+ nullptr);
+ EXPECT_FALSE(writer.check_is_mow());
+ EXPECT_FALSE(writer.check_is_mow_with_cluster_key());
+}
+
+TEST_F(SegmentWriterMowCheckTest,
vertical_segment_writer_is_mow_true_for_unique_mow) {
+ auto schema = create_unique_key_schema();
+ VerticalSegmentWriterOptions opts;
+ opts.enable_unique_key_merge_on_write = true;
+ auto file_writer = create_file_writer(5);
+ TestVerticalSegmentWriterMowCheck writer(file_writer.get(), 5, schema,
nullptr, nullptr, opts,
+ nullptr);
+ EXPECT_TRUE(writer.check_is_mow());
+ EXPECT_FALSE(writer.check_is_mow_with_cluster_key());
+}
+
+TEST_F(SegmentWriterMowCheckTest,
vertical_segment_writer_is_mow_with_cluster_key) {
+ auto schema = create_unique_key_schema_with_cluster_key();
+ VerticalSegmentWriterOptions opts;
+ opts.enable_unique_key_merge_on_write = true;
+ auto file_writer = create_file_writer(6);
+ TestVerticalSegmentWriterMowCheck writer(file_writer.get(), 6, schema,
nullptr, nullptr, opts,
+ nullptr);
+ EXPECT_TRUE(writer.check_is_mow());
+ EXPECT_TRUE(writer.check_is_mow_with_cluster_key());
+}
+
+} // namespace doris::segment_v2
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]