This is an automated email from the ASF dual-hosted git repository.

gavinchou 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 3a83360e4f8 [fix](cloud) Get packed slice location through the 
FileWriter interface (#68106)
3a83360e4f8 is described below

commit 3a83360e4f829cf2910a89108ca57ee290daf87e
Author: Xin Liao <[email protected]>
AuthorDate: Thu Oct 15 20:48:43 2026 +0800

    [fix](cloud) Get packed slice location through the FileWriter interface 
(#68106)
    
    #67347 changed `CloudRowsetWriter::_collect_packed_slice_location()` to
    read the slice
    location from the writer instead of `PackedFileManager`:
    
    ```cpp
    if (!file_writer->is_in_packed_file()) {
        return Status::OK();
    }
    ...
    
static_cast<io::PackedFileWriter*>(file_writer)->get_packed_slice_location(&index);
    ```
    
    `is_in_packed_file()` is a virtual method of `FileWriter`, and a writer
    that wraps a
    `PackedFileWriter` (for example an encryption writer layered on top of
    `PackedFileSystem`)
    has to forward it so that callers still see that the data went into a
    packed file. For
    such a writer the check passes but the object is not a
    `PackedFileWriter`, so the
    `static_cast` is undefined behavior:
    
    ```
    cloud_rowset_writer.cpp:218:5: runtime error: downcast of address 
0x7c8be5b61100 which does not point to an object of type 'io::PackedFileWriter'
    note: object is of type 'doris::io::EncryptedFileWriter'
    F packed_file_writer.cpp:236] Check failed: _state == State::CLOSED
    ```
    
    Debug/ASAN builds abort on the `DCHECK`. Release builds read unrelated
    fields as
    `_is_direct_write` / `_packed_slice_handle`, which may crash or record a
    bogus packed slice
    location in the rowset meta.
    
    ### What is changed and how it works?
    
    `get_packed_slice_location()` becomes a virtual method of `FileWriter`
    and replaces
    `is_in_packed_file()`:
    
    - The two methods described the same fact and had the same "after close"
    precondition, so a
    wrapper had to forward both. Forwarding only one of them either failed
    or, worse, made the
    caller skip the file silently. Now there is a single method, and an
    empty
      `packed_file_path` means the file is not in a packed file.
    - The default implementation returns an empty location.
    `PackedFileWriter` overrides it with
    its existing implementation (empty for direct write), and a wrapper
    forwards it to its
      inner writer.
    - `CloudRowsetWriter` and `DeleteBitmapFileWriter` call it through
    `FileWriter` instead of
    downcasting. The lifetime fix of #67347 is kept: the location is still
    taken from the
      writer's own slice handle, not looked up by path.
    - `PackedSliceLocation` moves to `io/fs/packed_slice_location.h` so
    `file_writer.h` does not
      need to include `packed_file_manager.h`.
---
 be/src/cloud/cloud_rowset_writer.cpp       | 11 ++-------
 be/src/cloud/delete_bitmap_file_writer.cpp |  3 +--
 be/src/io/fs/file_writer.h                 | 12 ++++++---
 be/src/io/fs/packed_file_manager.h         | 13 +---------
 be/src/io/fs/packed_file_writer.h          |  5 +---
 be/src/io/fs/packed_slice_location.h       | 39 ++++++++++++++++++++++++++++++
 be/test/io/fs/packed_file_system_test.cpp  | 13 +++++-----
 be/test/io/fs/packed_file_writer_test.cpp  | 23 ++++++++++++++++++
 8 files changed, 83 insertions(+), 36 deletions(-)

diff --git a/be/src/cloud/cloud_rowset_writer.cpp 
b/be/src/cloud/cloud_rowset_writer.cpp
index 20aa151a341..03fc3ca6682 100644
--- a/be/src/cloud/cloud_rowset_writer.cpp
+++ b/be/src/cloud/cloud_rowset_writer.cpp
@@ -21,7 +21,6 @@
 #include "common/status.h"
 #include "io/cache/block_file_cache_factory.h"
 #include "io/fs/packed_file_manager.h"
-#include "io/fs/packed_file_writer.h"
 #include "storage/rowset/rowset_factory.h"
 
 namespace doris {
@@ -219,18 +218,12 @@ Status 
CloudRowsetWriter::_collect_packed_slice_location(io::FileWriter* file_wr
         return Status::OK();
     }
 
-    // Check if file is actually in packed file (not direct write for large 
files)
-    if (!file_writer->is_in_packed_file()) {
-        return Status::OK();
-    }
-
     // Ask the writer, which holds a reference to its own slice location. 
Looking it up by
     // path in PackedFileManager would race with the retention based cleanup 
of the index.
     io::PackedSliceLocation index;
-    RETURN_IF_ERROR(
-            
static_cast<io::PackedFileWriter*>(file_writer)->get_packed_slice_location(&index));
+    RETURN_IF_ERROR(file_writer->get_packed_slice_location(&index));
     if (index.packed_file_path.empty()) {
-        return Status::OK(); // File not in packed file, skip
+        return Status::OK(); // File not in packed file (e.g. direct write for 
large files), skip
     }
 
     rowset_meta->add_packed_slice_location(file_path, index.packed_file_path, 
index.offset,
diff --git a/be/src/cloud/delete_bitmap_file_writer.cpp 
b/be/src/cloud/delete_bitmap_file_writer.cpp
index d137da5efec..81714ce5559 100644
--- a/be/src/cloud/delete_bitmap_file_writer.cpp
+++ b/be/src/cloud/delete_bitmap_file_writer.cpp
@@ -97,9 +97,8 @@ Status DeleteBitmapFileWriter::close() {
 
     // Check if file was written to packed file
     if (_enable_packed_file) {
-        auto* packed_writer = 
static_cast<io::PackedFileWriter*>(_file_writer.get());
         io::PackedSliceLocation loc;
-        st = packed_writer->get_packed_slice_location(&loc);
+        st = _file_writer->get_packed_slice_location(&loc);
         if (!st.ok()) {
             LOG(WARNING) << "failed to get packed slice location for delete 
bitmap file=" << _path
                          << ", st=" << st.to_string();
diff --git a/be/src/io/fs/file_writer.h b/be/src/io/fs/file_writer.h
index 92e4475d637..1ab0d14745b 100644
--- a/be/src/io/fs/file_writer.h
+++ b/be/src/io/fs/file_writer.h
@@ -25,6 +25,7 @@
 #include "io/cache/block_file_cache_factory.h"
 #include "io/cache/file_cache_common.h"
 #include "io/fs/file_reader_writer_fwd.h"
+#include "io/fs/packed_slice_location.h"
 #include "io/fs/path.h"
 #include "util/slice.h"
 
@@ -93,9 +94,14 @@ public:
 
     virtual State state() const = 0;
 
-    // Returns true if this file's data was written to a packed file.
-    // Used to determine whether to collect packed slice location from 
PackedFileManager.
-    virtual bool is_in_packed_file() const { return false; }
+    // Gets the location of this file's slice in the packed file. Must be 
called after the writer
+    // is closed. An empty packed_file_path means the file is not in a packed 
file. Writers that
+    // wrap another writer must forward this call, so callers never need to 
downcast to
+    // PackedFileWriter.
+    virtual Status get_packed_slice_location(PackedSliceLocation* location) 
const {
+        *location = PackedSliceLocation {};
+        return Status::OK();
+    }
 
     FileCacheAllocatorBuilder* cache_builder() const {
         return _cache_builder == nullptr ? nullptr : _cache_builder.get();
diff --git a/be/src/io/fs/packed_file_manager.h 
b/be/src/io/fs/packed_file_manager.h
index 9cc76eb803e..de9ebf89df7 100644
--- a/be/src/io/fs/packed_file_manager.h
+++ b/be/src/io/fs/packed_file_manager.h
@@ -36,23 +36,12 @@
 #include "common/status.h"
 #include "io/fs/file_system.h"
 #include "io/fs/file_writer.h"
+#include "io/fs/packed_slice_location.h"
 #include "io/fs/path.h"
 #include "util/slice.h"
 
 namespace doris::io {
 
-struct PackedSliceLocation {
-    std::string packed_file_path;
-    int64_t offset;
-    int64_t size;
-    int64_t create_time = 0;
-    int64_t tablet_id = 0;
-    std::string rowset_id;
-    std::string resource_id;
-    int64_t txn_id = 0;
-    int64_t packed_file_size = -1; // Total size of the packed file, -1 means 
not set
-};
-
 // Upload state of the packed file a slice belongs to
 enum class PackedSliceUploadState : uint8_t {
     PENDING = 0,
diff --git a/be/src/io/fs/packed_file_writer.h 
b/be/src/io/fs/packed_file_writer.h
index a53d412e667..f571b188acb 100644
--- a/be/src/io/fs/packed_file_writer.h
+++ b/be/src/io/fs/packed_file_writer.h
@@ -55,10 +55,7 @@ public:
     // Get merge file segment index information
     // This method should be called after close(true) to get the index 
information
     // Returns empty index if file is not in merge file
-    Status get_packed_slice_location(PackedSliceLocation* location) const;
-
-    // Returns true if this file's data was written to a packed file (not 
direct write)
-    bool is_in_packed_file() const override { return !_is_direct_write; }
+    Status get_packed_slice_location(PackedSliceLocation* location) const 
override;
 
 #ifdef BE_TEST
     size_t buffer_capacity_for_test() const { return _buffer.capacity(); }
diff --git a/be/src/io/fs/packed_slice_location.h 
b/be/src/io/fs/packed_slice_location.h
new file mode 100644
index 00000000000..a6d9e48a49e
--- /dev/null
+++ b/be/src/io/fs/packed_slice_location.h
@@ -0,0 +1,39 @@
+// 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.
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+
+namespace doris::io {
+
+// Location of a small file's slice inside a packed file.
+// An empty packed_file_path means the file is not in a packed file.
+struct PackedSliceLocation {
+    std::string packed_file_path;
+    int64_t offset;
+    int64_t size;
+    int64_t create_time = 0;
+    int64_t tablet_id = 0;
+    std::string rowset_id;
+    std::string resource_id;
+    int64_t txn_id = 0;
+    int64_t packed_file_size = -1; // Total size of the packed file, -1 means 
not set
+};
+
+} // namespace doris::io
diff --git a/be/test/io/fs/packed_file_system_test.cpp 
b/be/test/io/fs/packed_file_system_test.cpp
index 3ead07b5610..b2007941ab7 100644
--- a/be/test/io/fs/packed_file_system_test.cpp
+++ b/be/test/io/fs/packed_file_system_test.cpp
@@ -30,6 +30,7 @@
 #include "io/fs/file_reader.h"
 #include "io/fs/file_system.h"
 #include "io/fs/file_writer.h"
+#include "io/fs/packed_file_writer.h"
 #include "io/fs/path.h"
 #include "util/slice.h"
 
@@ -247,7 +248,7 @@ TEST_F(PackedFileSystemTest, 
FirstSegmentDataFileUsesPackedWriter) {
 
     ASSERT_NE(_inner_fs->last_writer(), nullptr);
     EXPECT_EQ(_inner_fs->last_writer()->bytes_appended(), 0);
-    EXPECT_TRUE(writer->is_in_packed_file());
+    EXPECT_NE(dynamic_cast<PackedFileWriter*>(writer.get()), nullptr);
 }
 
 TEST_F(PackedFileSystemTest, LaterSegmentDataFileUsesDirectWriter) {
@@ -264,7 +265,7 @@ TEST_F(PackedFileSystemTest, 
LaterSegmentDataFileUsesDirectWriter) {
 
     ASSERT_NE(_inner_fs->last_writer(), nullptr);
     EXPECT_EQ(_inner_fs->last_writer()->bytes_appended(), data.size());
-    EXPECT_FALSE(writer->is_in_packed_file());
+    EXPECT_EQ(dynamic_cast<PackedFileWriter*>(writer.get()), nullptr);
 }
 
 TEST_F(PackedFileSystemTest, LaterSegmentIndexFileUsesDirectWriter) {
@@ -281,7 +282,7 @@ TEST_F(PackedFileSystemTest, 
LaterSegmentIndexFileUsesDirectWriter) {
 
     ASSERT_NE(_inner_fs->last_writer(), nullptr);
     EXPECT_EQ(_inner_fs->last_writer()->bytes_appended(), data.size());
-    EXPECT_FALSE(writer->is_in_packed_file());
+    EXPECT_EQ(dynamic_cast<PackedFileWriter*>(writer.get()), nullptr);
 }
 
 TEST_F(PackedFileSystemTest, NonzeroFirstSegmentUsesPackedWriter) {
@@ -298,7 +299,7 @@ TEST_F(PackedFileSystemTest, 
NonzeroFirstSegmentUsesPackedWriter) {
     ASSERT_TRUE(first_segment_writer->appendv(&data_slice, 1).ok());
     ASSERT_NE(_inner_fs->last_writer(), nullptr);
     EXPECT_EQ(_inner_fs->last_writer()->bytes_appended(), 0);
-    EXPECT_TRUE(first_segment_writer->is_in_packed_file());
+    EXPECT_NE(dynamic_cast<PackedFileWriter*>(first_segment_writer.get()), 
nullptr);
 
     Path segment_zero_path("rowset_1_0.dat");
     FileWriterPtr segment_zero_writer;
@@ -307,7 +308,7 @@ TEST_F(PackedFileSystemTest, 
NonzeroFirstSegmentUsesPackedWriter) {
     ASSERT_TRUE(segment_zero_writer->appendv(&data_slice, 1).ok());
     ASSERT_NE(_inner_fs->last_writer(), nullptr);
     EXPECT_EQ(_inner_fs->last_writer()->bytes_appended(), data.size());
-    EXPECT_FALSE(segment_zero_writer->is_in_packed_file());
+    EXPECT_EQ(dynamic_cast<PackedFileWriter*>(segment_zero_writer.get()), 
nullptr);
 
     Path first_segment_index_path("rowset_1_10.idx");
     FileWriterPtr first_segment_index_writer;
@@ -320,7 +321,7 @@ TEST_F(PackedFileSystemTest, 
NonzeroFirstSegmentUsesPackedWriter) {
     ASSERT_TRUE(first_segment_index_writer->appendv(&index_slice, 1).ok());
     ASSERT_NE(_inner_fs->last_writer(), nullptr);
     EXPECT_EQ(_inner_fs->last_writer()->bytes_appended(), 0);
-    EXPECT_TRUE(first_segment_index_writer->is_in_packed_file());
+    
EXPECT_NE(dynamic_cast<PackedFileWriter*>(first_segment_index_writer.get()), 
nullptr);
 }
 
 TEST_F(PackedFileSystemTest, OpenFileNotInMergeFile) {
diff --git a/be/test/io/fs/packed_file_writer_test.cpp 
b/be/test/io/fs/packed_file_writer_test.cpp
index e26a5ec4743..d5b7dc2da79 100644
--- a/be/test/io/fs/packed_file_writer_test.cpp
+++ b/be/test/io/fs/packed_file_writer_test.cpp
@@ -275,4 +275,27 @@ TEST_F(PackedFileWriterTest, EmptyAppend) {
     EXPECT_EQ(writer.bytes_appended(), 0);
 }
 
+TEST_F(PackedFileWriterTest, GetPackedSliceLocationThroughFileWriterInterface) 
{
+    // Callers only hold a FileWriter, which may be a wrapper around 
PackedFileWriter,
+    // so the location must be reachable through the virtual interface.
+    MockFileWriterForMerge plain_writer("plain_file");
+    ASSERT_TRUE(plain_writer.close(false).ok());
+    FileWriter* plain = &plain_writer;
+    PackedSliceLocation location;
+    location.packed_file_path = "stale";
+    ASSERT_TRUE(plain->get_packed_slice_location(&location).ok());
+    EXPECT_TRUE(location.packed_file_path.empty());
+
+    PackedFileWriter packed_writer(std::move(_inner_writer), 
Path("large_file"), _append_info);
+    FileWriter* writer = &packed_writer;
+    std::string data(150, 'x');
+    ASSERT_TRUE(writer->append(Slice(data)).ok());
+    ASSERT_TRUE(writer->close(false).ok());
+    ASSERT_EQ(writer->state(), FileWriter::State::CLOSED);
+
+    location.packed_file_path = "stale";
+    ASSERT_TRUE(writer->get_packed_slice_location(&location).ok());
+    EXPECT_TRUE(location.packed_file_path.empty());
+}
+
 } // namespace doris::io


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to