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

yiguolei pushed a commit to branch branch-1.2-lts
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-1.2-lts by this push:
     new 1d26fea38e [fix](vec)crashing caused by parallel output file (#17384) 
(#17477)
1d26fea38e is described below

commit 1d26fea38e330caff452cf8d3ef653f2a3287679
Author: Jerry Hu <[email protected]>
AuthorDate: Tue Mar 7 08:46:56 2023 +0800

    [fix](vec)crashing caused by parallel output file (#17384) (#17477)
    
    cherry-pick #17384
---
 be/src/vec/sink/vdata_stream_sender.cpp            | 19 ++++++-
 be/src/vec/sink/vdata_stream_sender.h              |  1 +
 be/src/vec/sink/vresult_file_sink.cpp              | 61 +++++++---------------
 be/src/vec/sink/vresult_file_sink.h                |  7 ++-
 .../suites/export_p0/test_outfile.groovy           | 34 ++++++++++++
 5 files changed, 77 insertions(+), 45 deletions(-)

diff --git a/be/src/vec/sink/vdata_stream_sender.cpp 
b/be/src/vec/sink/vdata_stream_sender.cpp
index 00dcb68756..9768f61244 100644
--- a/be/src/vec/sink/vdata_stream_sender.cpp
+++ b/be/src/vec/sink/vdata_stream_sender.cpp
@@ -303,7 +303,6 @@ VDataStreamSender::VDataStreamSender(ObjectPool* pool, int 
sender_id, const RowD
            sink.output_partition.type == 
TPartitionType::BUCKET_SHFFULE_HASH_PARTITIONED);
     //
     std::map<int64_t, int64_t> fragment_id_to_channel_index;
-
     for (int i = 0; i < destinations.size(); ++i) {
         // Select first dest as transfer chain.
         bool is_transfer_chain = (i == 0);
@@ -326,6 +325,7 @@ VDataStreamSender::VDataStreamSender(ObjectPool* pool, int 
sender_id, const RowD
 }
 
 VDataStreamSender::VDataStreamSender(ObjectPool* pool, int sender_id, const 
RowDescriptor& row_desc,
+                                     PlanNodeId dest_node_id,
                                      const 
std::vector<TPlanFragmentDestination>& destinations,
                                      int per_channel_buffer_size,
                                      bool 
send_query_statistics_with_every_batch)
@@ -333,6 +333,7 @@ VDataStreamSender::VDataStreamSender(ObjectPool* pool, int 
sender_id, const RowD
           _pool(pool),
           _row_desc(row_desc),
           _current_channel_idx(0),
+          _part_type(TPartitionType::UNPARTITIONED),
           _ignore_not_found(true),
           _cur_pb_block(&_pb_block1),
           _profile(nullptr),
@@ -346,8 +347,22 @@ VDataStreamSender::VDataStreamSender(ObjectPool* pool, int 
sender_id, const RowD
           _split_block_distribute_by_channel_timer(nullptr),
           _blocks_sent_counter(nullptr),
           _local_bytes_send_counter(nullptr),
-          _dest_node_id(0) {
+          _dest_node_id(dest_node_id) {
     _name = "VDataStreamSender";
+    std::map<int64_t, int64_t> fragment_id_to_channel_index;
+    for (int i = 0; i < destinations.size(); ++i) {
+        const auto& fragment_instance_id = 
destinations[i].fragment_instance_id;
+        if (fragment_id_to_channel_index.find(fragment_instance_id.lo) ==
+            fragment_id_to_channel_index.end()) {
+            _channel_shared_ptrs.emplace_back(
+                    new Channel(this, row_desc, destinations[i].brpc_server, 
fragment_instance_id,
+                                _dest_node_id, per_channel_buffer_size, false,
+                                send_query_statistics_with_every_batch));
+        }
+        fragment_id_to_channel_index.emplace(fragment_instance_id.lo,
+                                             _channel_shared_ptrs.size() - 1);
+        _channels.push_back(_channel_shared_ptrs.back().get());
+    }
 }
 
 VDataStreamSender::VDataStreamSender(ObjectPool* pool, const RowDescriptor& 
row_desc,
diff --git a/be/src/vec/sink/vdata_stream_sender.h 
b/be/src/vec/sink/vdata_stream_sender.h
index efa5a5bad1..3e17170367 100644
--- a/be/src/vec/sink/vdata_stream_sender.h
+++ b/be/src/vec/sink/vdata_stream_sender.h
@@ -50,6 +50,7 @@ public:
                       int per_channel_buffer_size, bool 
send_query_statistics_with_every_batch);
 
     VDataStreamSender(ObjectPool* pool, int sender_id, const RowDescriptor& 
row_desc,
+                      PlanNodeId dest_node_id,
                       const std::vector<TPlanFragmentDestination>& 
destinations,
                       int per_channel_buffer_size, bool 
send_query_statistics_with_every_batch);
 
diff --git a/be/src/vec/sink/vresult_file_sink.cpp 
b/be/src/vec/sink/vresult_file_sink.cpp
index d1d2cfaa72..bceaa910f5 100644
--- a/be/src/vec/sink/vresult_file_sink.cpp
+++ b/be/src/vec/sink/vresult_file_sink.cpp
@@ -34,9 +34,7 @@ VResultFileSink::VResultFileSink(ObjectPool* pool, const 
RowDescriptor& row_desc
                                  const TResultFileSink& sink, int 
per_channel_buffer_size,
                                  bool send_query_statistics_with_every_batch,
                                  const std::vector<TExpr>& t_output_expr)
-        : VDataStreamSender(pool, row_desc, per_channel_buffer_size,
-                            send_query_statistics_with_every_batch),
-          _t_output_expr(t_output_expr) {
+        : _t_output_expr(t_output_expr), _row_desc(row_desc) {
     CHECK(sink.__isset.file_options);
     _file_opts.reset(new ResultFileOptions(sink.file_options));
     CHECK(sink.__isset.storage_backend_type);
@@ -55,20 +53,18 @@ VResultFileSink::VResultFileSink(ObjectPool* pool, int 
sender_id, const RowDescr
                                  int per_channel_buffer_size,
                                  bool send_query_statistics_with_every_batch,
                                  const std::vector<TExpr>& t_output_expr, 
DescriptorTbl& descs)
-        : VDataStreamSender(pool, sender_id, row_desc, destinations, 
per_channel_buffer_size,
-                            send_query_statistics_with_every_batch),
-          _t_output_expr(t_output_expr),
-          
_output_row_descriptor(descs.get_tuple_descriptor(sink.output_tuple_id), false) 
{
+        : _t_output_expr(t_output_expr),
+          
_output_row_descriptor(descs.get_tuple_descriptor(sink.output_tuple_id), false),
+          _row_desc(row_desc) {
     CHECK(sink.__isset.file_options);
     _file_opts.reset(new ResultFileOptions(sink.file_options));
     CHECK(sink.__isset.storage_backend_type);
     _storage_type = sink.storage_backend_type;
     _is_top_sink = false;
-    DCHECK_EQ(destinations.size(), 1);
-    _channel_shared_ptrs.emplace_back(new Channel(
-            this, _output_row_descriptor, destinations[0].brpc_server,
-            destinations[0].fragment_instance_id, sink.dest_node_id, 
_buf_size, true, true));
-    _channels.push_back(_channel_shared_ptrs.back().get());
+    CHECK_EQ(destinations.size(), 1);
+    _stream_sender.reset(new VDataStreamSender(pool, sender_id, row_desc, 
sink.dest_node_id,
+                                               destinations, 
per_channel_buffer_size,
+                                               
send_query_statistics_with_every_batch));
 
     _name = "VResultFileSink";
     //for impl csv_with_name and csv_with_names_and_types
@@ -77,6 +73,9 @@ VResultFileSink::VResultFileSink(ObjectPool* pool, int 
sender_id, const RowDescr
 }
 
 Status VResultFileSink::init(const TDataSink& tsink) {
+    if (!_is_top_sink) {
+        RETURN_IF_ERROR(_stream_sender->init(tsink));
+    }
     return Status::OK();
 }
 
@@ -111,30 +110,24 @@ Status VResultFileSink::prepare(RuntimeState* state) {
                 _output_row_descriptor));
     } else {
         // init channel
-        _profile = _pool->add(new RuntimeProfile(title.str()));
-        _state = state;
-        _serialize_batch_timer = ADD_TIMER(profile(), "SerializeBatchTime");
-        _bytes_sent_counter = ADD_COUNTER(profile(), "BytesSent", 
TUnit::BYTES);
-        _local_bytes_send_counter = ADD_COUNTER(profile(), "LocalBytesSent", 
TUnit::BYTES);
-        _uncompressed_bytes_counter =
-                ADD_COUNTER(profile(), "UncompressedRowBatchSize", 
TUnit::BYTES);
-        // create writer
         _output_block.reset(new 
Block(_output_row_descriptor.tuple_descriptors()[0]->slots(), 1));
         _writer.reset(new (std::nothrow) VFileResultWriter(
                 _file_opts.get(), _storage_type, 
state->fragment_instance_id(), _output_vexpr_ctxs,
                 _profile, nullptr, _output_block.get(), 
state->return_object_data_as_binary(),
                 _output_row_descriptor));
+        RETURN_IF_ERROR(_stream_sender->prepare(state));
+        _profile->add_child(_stream_sender->profile(), true, nullptr);
     }
     _writer->set_header_info(_header_type, _header);
     RETURN_IF_ERROR(_writer->init(state));
-    for (int i = 0; i < _channels.size(); ++i) {
-        RETURN_IF_ERROR(_channels[i]->init(state));
-    }
     return Status::OK();
 }
 
 Status VResultFileSink::open(RuntimeState* state) {
     START_AND_SCOPE_SPAN(state->get_tracer(), span, "VResultFileSink::open");
+    if (!_is_top_sink) {
+        RETURN_IF_ERROR(_stream_sender->open(state));
+    }
     return VExpr::open(_output_vexpr_ctxs, state);
 }
 
@@ -174,25 +167,9 @@ Status VResultFileSink::close(RuntimeState* state, Status 
exec_status) {
                 state->fragment_instance_id());
     } else {
         if (final_status.ok()) {
-            RETURN_IF_ERROR(serialize_block(_output_block.get(), 
_cur_pb_block, _channels.size()));
-            for (auto channel : _channels) {
-                RETURN_IF_ERROR(channel->send_block(_cur_pb_block));
-            }
-        }
-        Status final_st = Status::OK();
-        for (int i = 0; i < _channels.size(); ++i) {
-            Status st = _channels[i]->close(state);
-            if (!st.ok() && final_st.ok()) {
-                final_st = st;
-            }
-        }
-        // wait all channels to finish
-        for (int i = 0; i < _channels.size(); ++i) {
-            Status st = _channels[i]->close_wait(state);
-            if (!st.ok() && final_st.ok()) {
-                final_st = st;
-            }
+            RETURN_IF_ERROR(_stream_sender->send(state, _output_block.get()));
         }
+        RETURN_IF_ERROR(_stream_sender->close(state, final_status));
         _output_block->clear();
     }
 
@@ -206,7 +183,7 @@ void 
VResultFileSink::set_query_statistics(std::shared_ptr<QueryStatistics> stat
     if (_is_top_sink) {
         _sender->set_query_statistics(statistics);
     } else {
-        _query_statistics = statistics;
+        _stream_sender->set_query_statistics(statistics);
     }
 }
 
diff --git a/be/src/vec/sink/vresult_file_sink.h 
b/be/src/vec/sink/vresult_file_sink.h
index c5c509caf7..be3c2926bb 100644
--- a/be/src/vec/sink/vresult_file_sink.h
+++ b/be/src/vec/sink/vresult_file_sink.h
@@ -24,7 +24,7 @@ namespace doris {
 namespace vectorized {
 class VResultWriter;
 
-class VResultFileSink : public VDataStreamSender {
+class VResultFileSink : public DataSink {
 public:
     VResultFileSink(ObjectPool* pool, const RowDescriptor& row_desc, const 
TResultFileSink& sink,
                     int per_channel_buffer_size, bool 
send_query_statistics_with_every_batch,
@@ -48,6 +48,7 @@ public:
     RuntimeProfile* profile() override { return _profile; }
 
     void set_query_statistics(std::shared_ptr<QueryStatistics> statistics) 
override;
+    const RowDescriptor& row_desc() const { return _row_desc; }
 
 private:
     Status prepare_exprs(RuntimeState* state);
@@ -62,11 +63,15 @@ private:
 
     std::unique_ptr<Block> _output_block = nullptr;
     std::shared_ptr<BufferControlBlock> _sender;
+    std::unique_ptr<VDataStreamSender> _stream_sender;
     std::shared_ptr<VResultWriter> _writer;
     int _buf_size = 1024; // Allocated from _pool
     bool _is_top_sink = true;
     std::string _header;
     std::string _header_type;
+
+    RowDescriptor _row_desc;
+    RuntimeProfile* _profile;
 };
 } // namespace vectorized
 } // namespace doris
diff --git a/regression-test/suites/export_p0/test_outfile.groovy 
b/regression-test/suites/export_p0/test_outfile.groovy
index 4fa3350268..d2ceb46c5e 100644
--- a/regression-test/suites/export_p0/test_outfile.groovy
+++ b/regression-test/suites/export_p0/test_outfile.groovy
@@ -186,4 +186,38 @@ suite("test_outfile") {
             path.delete();
         }
     }
+
+    // test parallel output
+    try {
+        File path = new File(outFilePath)
+        if (!path.exists()) {
+            assert path.mkdirs()
+        } else {
+            throw new IllegalStateException("""${outFilePath} already exists! 
""")
+        }
+        sql """drop table if exists select_into_file"""
+        sql """CREATE TABLE `select_into_file` (
+                    `id` int,
+                     `name` varchar(30)
+                 ) ENGINE=OLAP
+                   DUPLICATE KEY(`id`)
+                   DISTRIBUTED BY HASH(`id`) BUCKETS 2
+                   PROPERTIES (
+                   "replication_allocation" = "tag.location.default: 1"
+                   );"""
+        sql """insert into select_into_file values(1, "b"),(2, "z"),(3, "a"),
+                    (4, "c"), (5, "睿"), (6, "多"), (7, "丝"), (8, "test"),
+                    (100, "aa"), (111, "bb"), (123, "cc"), (222, "dd");"""
+        sql "set enable_parallel_outfile = true;"
+        sql """select * from select_into_file into outfile 
"file://${outFilePath}/" properties("success_file_name" = "SUCCESS");"""
+    } finally {
+        try_sql("DROP TABLE IF EXISTS select_into_file")
+        File path = new File(outFilePath)
+        if (path.exists()) {
+            for (File f: path.listFiles()) {
+                f.delete();
+            }
+            path.delete();
+        }
+    }
 }


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

Reply via email to