Coverage Report

Created: 2026-08-06 08:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/service/http/action/http_stream.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "service/http/action/http_stream.h"
19
20
#include <algorithm>
21
#include <cstddef>
22
#include <future>
23
#include <sstream>
24
25
// use string iequal
26
#include <event2/buffer.h>
27
#include <event2/bufferevent.h>
28
#include <event2/http.h>
29
#include <gen_cpp/FrontendService.h>
30
#include <gen_cpp/FrontendService_types.h>
31
#include <gen_cpp/HeartbeatService_types.h>
32
#include <rapidjson/prettywriter.h>
33
#include <thrift/protocol/TDebugProtocol.h>
34
35
#include "cloud/config.h"
36
#include "common/config.h"
37
#include "common/logging.h"
38
#include "common/metrics/doris_metrics.h"
39
#include "common/metrics/metrics.h"
40
#include "common/status.h"
41
#include "common/utils.h"
42
#include "io/fs/stream_load_pipe.h"
43
#include "load/group_commit/group_commit_mgr.h"
44
#include "load/load_path_mgr.h"
45
#include "load/stream_load/new_load_stream_mgr.h"
46
#include "load/stream_load/stream_load_context.h"
47
#include "load/stream_load/stream_load_executor.h"
48
#include "load/stream_load/stream_load_recorder.h"
49
#include "runtime/cluster_info.h"
50
#include "runtime/exec_env.h"
51
#include "runtime/fragment_mgr.h"
52
#include "service/http/http_channel.h"
53
#include "service/http/http_common.h"
54
#include "service/http/http_headers.h"
55
#include "service/http/http_request.h"
56
#include "service/http/utils.h"
57
#include "storage/storage_engine.h"
58
#include "util/byte_buffer.h"
59
#include "util/client_cache.h"
60
#include "util/load_util.h"
61
#include "util/string_util.h"
62
#include "util/thrift_rpc_helper.h"
63
#include "util/time.h"
64
#include "util/uid_util.h"
65
66
namespace doris {
67
using namespace ErrorCode;
68
69
namespace {
70
71
constexpr size_t MEBIBYTE = 1024 * 1024;
72
73
10
bool is_compressed_file_scan(const TPipelineFragmentParams& params) {
74
10
    if (!params.__isset.file_scan_params) {
75
0
        return false;
76
0
    }
77
10
    return std::ranges::any_of(params.file_scan_params, [](const auto& file_scan_param) {
78
10
        const auto& file_scan_params = file_scan_param.second;
79
10
        TFileCompressType::type compress_type = file_scan_params.__isset.compress_type
80
10
                                                        ? file_scan_params.compress_type
81
10
                                                        : TFileCompressType::UNKNOWN;
82
10
        TFileFormatType::type format_type = file_scan_params.__isset.format_type
83
10
                                                    ? file_scan_params.format_type
84
10
                                                    : TFileFormatType::FORMAT_UNKNOWN;
85
10
        return LoadUtil::is_compressed_load(compress_type, format_type);
86
10
    });
87
10
}
88
89
} // namespace
90
91
DEFINE_COUNTER_METRIC_PROTOTYPE_2ARG(http_stream_requests_total, MetricUnit::REQUESTS);
92
DEFINE_COUNTER_METRIC_PROTOTYPE_2ARG(http_stream_duration_ms, MetricUnit::MILLISECONDS);
93
DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(http_stream_current_processing, MetricUnit::REQUESTS);
94
95
HttpStreamAction::HttpStreamAction(ExecEnv* exec_env)
96
9
        : HttpHandlerWithAuth(exec_env, TPrivilegeHier::GLOBAL, TPrivilegeType::LOAD) {
97
    // Use LOAD privilege type: requires LOAD permission
98
    // Note: _exec_env is set by parent class HttpHandlerWithAuth
99
9
    _http_stream_entity =
100
9
            DorisMetrics::instance()->metric_registry()->register_entity("http_stream");
101
9
    INT_COUNTER_METRIC_REGISTER(_http_stream_entity, http_stream_requests_total);
102
9
    INT_COUNTER_METRIC_REGISTER(_http_stream_entity, http_stream_duration_ms);
103
9
    INT_GAUGE_METRIC_REGISTER(_http_stream_entity, http_stream_current_processing);
104
9
}
105
106
5
HttpStreamAction::~HttpStreamAction() {
107
5
    DorisMetrics::instance()->metric_registry()->deregister_entity(_http_stream_entity);
108
5
}
109
110
135
void HttpStreamAction::handle(HttpRequest* req) {
111
135
    std::shared_ptr<StreamLoadContext> ctx =
112
135
            std::static_pointer_cast<StreamLoadContext>(req->handler_ctx());
113
135
    if (ctx == nullptr) {
114
0
        return;
115
0
    }
116
117
    // status already set to fail
118
135
    if (ctx->status.ok()) {
119
114
        ctx->status = _handle(req, ctx);
120
114
        if (!ctx->status.ok() && !ctx->status.is<PUBLISH_TIMEOUT>()) {
121
3
            LOG(WARNING) << "handle streaming load failed, id=" << ctx->id
122
3
                         << ", errmsg=" << ctx->status;
123
3
        }
124
114
    }
125
135
    ctx->load_cost_millis = UnixMillis() - ctx->start_millis;
126
127
135
    if (!ctx->status.ok() && !ctx->status.is<PUBLISH_TIMEOUT>()) {
128
24
        if (ctx->body_sink != nullptr) {
129
24
            ctx->body_sink->cancel(ctx->status.to_string());
130
24
        }
131
24
    }
132
133
135
    if (!ctx->status.ok()) {
134
24
        auto str = std::string(ctx->to_json());
135
        // add new line at end
136
24
        str = str + '\n';
137
24
        HttpChannel::send_reply(req, str);
138
24
        return;
139
24
    }
140
111
    auto str = std::string(ctx->to_json());
141
    // add new line at end
142
111
    str = str + '\n';
143
111
    HttpChannel::send_reply(req, str);
144
111
    if (config::enable_stream_load_record || config::enable_stream_load_record_to_audit_log_table) {
145
111
        if (req->header(HTTP_SKIP_RECORD_TO_AUDIT_LOG_TABLE).empty()) {
146
111
            str = ctx->prepare_stream_load_record(str);
147
111
            _save_stream_load_record(ctx, str);
148
111
        }
149
111
    }
150
    // update statistics
151
111
    http_stream_requests_total->increment(1);
152
111
    http_stream_duration_ms->increment(ctx->load_cost_millis);
153
111
}
154
155
114
Status HttpStreamAction::_handle(HttpRequest* http_req, std::shared_ptr<StreamLoadContext> ctx) {
156
114
    if (ctx->body_bytes > 0 && ctx->receive_bytes != ctx->body_bytes) {
157
0
        LOG(WARNING) << "recevie body don't equal with body bytes, body_bytes=" << ctx->body_bytes
158
0
                     << ", receive_bytes=" << ctx->receive_bytes << ", id=" << ctx->id;
159
0
        return Status::Error<ErrorCode::NETWORK_ERROR>("receive body don't equal with body bytes");
160
0
    }
161
114
    RETURN_IF_ERROR(ctx->body_sink->finish());
162
163
    // wait stream load finish
164
114
    RETURN_IF_ERROR(ctx->load_status_future.get());
165
166
111
    if (ctx->group_commit) {
167
13
        LOG(INFO) << "skip commit because this is group commit, pipe_id=" << ctx->id.to_string();
168
13
        return Status::OK();
169
13
    }
170
171
98
    if (ctx->two_phase_commit) {
172
1
        int64_t pre_commit_start_time = MonotonicNanos();
173
1
        RETURN_IF_ERROR(_exec_env->stream_load_executor()->pre_commit_txn(ctx.get()));
174
1
        ctx->pre_commit_txn_cost_nanos = MonotonicNanos() - pre_commit_start_time;
175
97
    } else {
176
        // If put file success we need commit this load
177
97
        int64_t commit_and_publish_start_time = MonotonicNanos();
178
97
        RETURN_IF_ERROR(_exec_env->stream_load_executor()->commit_txn(ctx.get()));
179
97
        ctx->commit_and_publish_txn_cost_nanos = MonotonicNanos() - commit_and_publish_start_time;
180
97
    }
181
98
    return Status::OK();
182
98
}
183
184
135
int HttpStreamAction::on_header(HttpRequest* req) {
185
    // Call parent's auth check first
186
135
    int ret = HttpHandlerWithAuth::on_header(req);
187
135
    if (ret != 0) {
188
0
        return ret; // Auth failed, return error
189
0
    }
190
191
135
    http_stream_current_processing->increment(1);
192
193
135
    std::shared_ptr<StreamLoadContext> ctx = std::make_shared<StreamLoadContext>(_exec_env);
194
135
    req->set_handler_ctx(ctx);
195
196
135
    ctx->load_type = TLoadType::MANUL_LOAD;
197
135
    ctx->load_src_type = TLoadSourceType::RAW;
198
135
    ctx->two_phase_commit = req->header(HTTP_TWO_PHASE_COMMIT) == "true";
199
135
    Status st = _handle_group_commit(req, ctx);
200
201
135
    LOG(INFO) << "new income streaming load request." << ctx->brief()
202
135
              << " sql : " << req->header(HTTP_SQL) << ", group_commit=" << ctx->group_commit;
203
135
    if (st.ok()) {
204
135
        st = _on_header(req, ctx);
205
135
    }
206
135
    if (!st.ok()) {
207
0
        ctx->status = std::move(st);
208
0
        if (ctx->body_sink != nullptr) {
209
0
            ctx->body_sink->cancel(ctx->status.to_string());
210
0
        }
211
0
        auto str = ctx->to_json();
212
        // add new line at end
213
0
        str = str + '\n';
214
0
        HttpChannel::send_reply(req, str);
215
0
        if (config::enable_stream_load_record ||
216
0
            config::enable_stream_load_record_to_audit_log_table) {
217
0
            if (req->header(HTTP_SKIP_RECORD_TO_AUDIT_LOG_TABLE).empty()) {
218
0
                str = ctx->prepare_stream_load_record(str);
219
0
                _save_stream_load_record(ctx, str);
220
0
            }
221
0
        }
222
0
        return -1;
223
0
    }
224
135
    return 0;
225
135
}
226
227
136
Status HttpStreamAction::_on_header(HttpRequest* http_req, std::shared_ptr<StreamLoadContext> ctx) {
228
    // auth information
229
136
    if (!parse_basic_auth(*http_req, &ctx->auth)) {
230
0
        LOG(WARNING) << "parse basic authorization failed." << ctx->brief();
231
0
        return Status::NotAuthorized("no valid Basic authorization");
232
0
    }
233
234
    // TODO(zs) : need Need to request an FE to obtain information such as format
235
    // check content length
236
136
    ctx->body_bytes = 0;
237
136
    const auto csv_max_body_mb = config::streaming_load_max_mb;
238
136
    size_t csv_max_body_bytes = csv_max_body_mb * MEBIBYTE;
239
136
    if (!http_req->header(HttpHeaders::CONTENT_LENGTH).empty()) {
240
133
        try {
241
133
            ctx->body_bytes = std::stol(http_req->header(HttpHeaders::CONTENT_LENGTH));
242
133
        } catch (const std::exception& e) {
243
0
            return Status::InvalidArgument("invalid HTTP header CONTENT_LENGTH={}: {}",
244
0
                                           http_req->header(HttpHeaders::CONTENT_LENGTH), e.what());
245
0
        }
246
        // csv max body size
247
133
        if (ctx->body_bytes > csv_max_body_bytes) {
248
1
            LOG(WARNING) << "body exceed max size." << ctx->brief();
249
1
            return Status::Error<ErrorCode::EXCEEDED_LIMIT>(
250
1
                    "body size {} bytes ({:.2f} MiB) exceeds the limit of {} bytes ({} MiB) set "
251
1
                    "by BE config `streaming_load_max_mb`. Increase it if you are sure this load "
252
1
                    "is reasonable",
253
1
                    ctx->body_bytes, static_cast<double>(ctx->body_bytes) / MEBIBYTE,
254
1
                    csv_max_body_bytes, csv_max_body_mb);
255
1
        }
256
133
    }
257
258
135
    auto pipe = std::make_shared<io::StreamLoadPipe>(
259
135
            io::kMaxPipeBufferedBytes /* max_buffered_bytes */, 64 * 1024 /* min_chunk_size */,
260
135
            ctx->body_bytes /* total_length */);
261
135
    ctx->body_sink = pipe;
262
135
    ctx->pipe = pipe;
263
264
135
    RETURN_IF_ERROR(_exec_env->new_load_stream_mgr()->put(ctx->id, ctx));
265
266
    // Here, transactions are set from fe's NativeInsertStmt.
267
    // TODO(zs) : How to support two_phase_commit
268
269
135
    return Status::OK();
270
135
}
271
272
9.61k
void HttpStreamAction::on_chunk_data(HttpRequest* req) {
273
9.61k
    std::shared_ptr<StreamLoadContext> ctx =
274
9.61k
            std::static_pointer_cast<StreamLoadContext>(req->handler_ctx());
275
9.61k
    if (ctx == nullptr || !ctx->status.ok()) {
276
8
        return;
277
8
    }
278
9.61k
    if (!req->header(HTTP_WAL_ID_KY).empty()) {
279
0
        ctx->wal_id = std::stoll(req->header(HTTP_WAL_ID_KY));
280
0
    }
281
9.61k
    struct evhttp_request* ev_req = req->get_evhttp_request();
282
9.61k
    auto evbuf = evhttp_request_get_input_buffer(ev_req);
283
284
    // In HttpStreamAction::on_chunk_data
285
    //      -> process_put
286
    //      -> StreamLoadExecutor::execute_plan_fragment
287
    //      -> exec_plan_fragment
288
    // , SCOPED_ATTACH_TASK will be called.
289
9.61k
    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(ExecEnv::GetInstance()->stream_load_pipe_tracker());
290
291
9.61k
    int64_t start_read_data_time = MonotonicNanos();
292
9.61k
    Status st = ctx->allocate_schema_buffer();
293
9.61k
    if (!st.ok()) {
294
0
        ctx->status = st;
295
0
        return;
296
0
    }
297
19.2k
    while (evbuffer_get_length(evbuf) > 0) {
298
9.61k
        ByteBufferPtr bb;
299
9.61k
        st = ByteBuffer::allocate(128 * 1024, &bb);
300
9.61k
        if (!st.ok()) {
301
0
            ctx->status = st;
302
0
            return;
303
0
        }
304
9.61k
        auto remove_bytes = evbuffer_remove(evbuf, bb->ptr, bb->capacity);
305
9.61k
        bb->pos = remove_bytes;
306
9.61k
        bb->flip();
307
9.61k
        st = ctx->body_sink->append(bb);
308
        // schema_buffer stores 1M of data for parsing column information
309
        // need to determine whether to cache for the first time
310
9.61k
        if (ctx->is_read_schema) {
311
135
            if (ctx->schema_buffer()->pos + remove_bytes < config::stream_tvf_buffer_size) {
312
135
                ctx->schema_buffer()->put_bytes(bb->ptr, remove_bytes);
313
135
            } else {
314
0
                LOG(INFO) << "use a portion of data to request fe to obtain column information";
315
0
                ctx->is_read_schema = false;
316
0
                ctx->status = process_put(req, ctx);
317
0
            }
318
135
        }
319
9.61k
        if (!st.ok()) {
320
0
            LOG(WARNING) << "append body content failed. errmsg=" << st << ", " << ctx->brief();
321
0
            ctx->status = st;
322
0
            return;
323
0
        }
324
9.61k
        ctx->receive_bytes += remove_bytes;
325
9.61k
    }
326
    // after all the data has been read and it has not reached 1M, it will execute here
327
9.61k
    if (ctx->is_read_schema) {
328
135
        LOG(INFO) << "after all the data has been read and it has not reached 1M, it will execute "
329
135
                  << "here";
330
135
        ctx->is_read_schema = false;
331
135
        ctx->status = process_put(req, ctx);
332
135
    }
333
9.61k
    ctx->read_data_cost_nanos += (MonotonicNanos() - start_read_data_time);
334
9.61k
}
335
336
135
void HttpStreamAction::free_handler_ctx(std::shared_ptr<void> param) {
337
135
    std::shared_ptr<StreamLoadContext> ctx = std::static_pointer_cast<StreamLoadContext>(param);
338
135
    if (ctx == nullptr) {
339
0
        return;
340
0
    }
341
    // sender is gone, make receiver know it
342
135
    if (ctx->body_sink != nullptr) {
343
135
        ctx->body_sink->cancel("sender is gone");
344
135
    }
345
    // remove stream load context from stream load manager and the resource will be released
346
135
    ctx->exec_env()->new_load_stream_mgr()->remove(ctx->id);
347
135
    http_stream_current_processing->increment(-1);
348
135
}
349
350
Status HttpStreamAction::process_put(HttpRequest* http_req,
351
135
                                     std::shared_ptr<StreamLoadContext> ctx) {
352
135
    TStreamLoadPutRequest request;
353
135
    if (http_req != nullptr) {
354
135
        request.__set_load_sql(http_req->header(HTTP_SQL));
355
135
        if (!http_req->header(HTTP_MEMTABLE_ON_SINKNODE).empty()) {
356
0
            bool value = iequal(http_req->header(HTTP_MEMTABLE_ON_SINKNODE), "true");
357
0
            request.__set_memtable_on_sink_node(value);
358
0
        }
359
135
    } else {
360
0
        request.__set_token(ctx->auth.token);
361
0
        request.__set_load_sql(ctx->sql_str);
362
0
        ctx->auth.token = "";
363
0
    }
364
135
    set_request_auth(&request, ctx->auth);
365
135
    request.__set_loadId(ctx->id.to_thrift());
366
135
    request.__set_label(ctx->label);
367
135
    if (ctx->group_commit) {
368
14
        if (!http_req->header(HTTP_GROUP_COMMIT).empty()) {
369
14
            request.__set_group_commit_mode(http_req->header(HTTP_GROUP_COMMIT));
370
14
        } else {
371
            // used for wait_internal_group_commit_finish
372
0
            request.__set_group_commit_mode("sync_mode");
373
0
        }
374
14
    }
375
135
    if (_exec_env->cluster_info()->backend_id != 0) {
376
135
        request.__set_backend_id(_exec_env->cluster_info()->backend_id);
377
135
    } else {
378
0
        LOG(WARNING) << "_exec_env->cluster_info not set backend_id";
379
0
    }
380
135
    if (ctx->wal_id > 0) {
381
0
        request.__set_partial_update(false);
382
0
    }
383
384
    // plan this load
385
135
    TNetworkAddress master_addr = _exec_env->cluster_info()->master_fe_addr;
386
135
    int64_t stream_load_put_start_time = MonotonicNanos();
387
135
    RETURN_IF_ERROR(ThriftRpcHelper::rpc<FrontendServiceClient>(
388
135
            master_addr.hostname, master_addr.port,
389
135
            [&request, ctx](FrontendServiceConnection& client) {
390
135
                client->streamLoadPut(ctx->put_result, request);
391
135
            }));
392
135
    ctx->put_result.pipeline_params.query_options.__set_enable_strict_cast(false);
393
135
    ctx->stream_load_put_cost_nanos = MonotonicNanos() - stream_load_put_start_time;
394
135
    Status plan_status(Status::create(ctx->put_result.status));
395
135
    if (!plan_status.ok()) {
396
20
        LOG(WARNING) << "plan streaming load failed. errmsg=" << plan_status << ctx->brief();
397
20
        return plan_status;
398
20
    }
399
115
    if (config::is_cloud_mode() && ctx->two_phase_commit && ctx->is_mow_table()) {
400
1
        return Status::NotSupported("http stream 2pc is unsupported for mow table");
401
1
    }
402
114
    ctx->db = ctx->put_result.pipeline_params.db_name;
403
114
    ctx->table = ctx->put_result.pipeline_params.table_name;
404
114
    ctx->txn_id = ctx->put_result.pipeline_params.txn_conf.txn_id;
405
114
    ctx->label = ctx->put_result.pipeline_params.import_label;
406
114
    ctx->put_result.pipeline_params.__set_wal_id(ctx->wal_id);
407
114
    if (http_req != nullptr && http_req->header(HTTP_GROUP_COMMIT) == "async_mode") {
408
        // FIXME find a way to avoid chunked stream load write large WALs
409
12
        size_t content_length = 0;
410
12
        if (!http_req->header(HttpHeaders::CONTENT_LENGTH).empty()) {
411
10
            try {
412
10
                content_length = std::stol(http_req->header(HttpHeaders::CONTENT_LENGTH));
413
10
            } catch (const std::exception& e) {
414
0
                return Status::InvalidArgument("invalid HTTP header CONTENT_LENGTH={}: {}",
415
0
                                               http_req->header(HttpHeaders::CONTENT_LENGTH),
416
0
                                               e.what());
417
0
            }
418
10
            if (is_compressed_file_scan(ctx->put_result.pipeline_params)) {
419
5
                content_length *= 3;
420
5
            }
421
10
        }
422
12
        ctx->put_result.pipeline_params.__set_content_length(content_length);
423
12
    }
424
114
    TPipelineFragmentParamsList mocked;
425
114
    return _exec_env->stream_load_executor()->execute_plan_fragment(ctx, mocked);
426
114
}
427
428
void HttpStreamAction::_save_stream_load_record(std::shared_ptr<StreamLoadContext> ctx,
429
111
                                                const std::string& str) {
430
111
    std::shared_ptr<StreamLoadRecorder> stream_load_recorder =
431
111
            ExecEnv::GetInstance()->storage_engine().get_stream_load_recorder();
432
433
111
    if (stream_load_recorder != nullptr) {
434
111
        std::string key =
435
111
                std::to_string(ctx->start_millis + ctx->load_cost_millis) + "_" + ctx->label;
436
111
        auto st = stream_load_recorder->put(key, str);
437
111
        if (st.ok()) {
438
111
            LOG(INFO) << "put stream_load_record rocksdb successfully. label: " << ctx->label
439
111
                      << ", key: " << key;
440
111
        }
441
111
    } else {
442
0
        LOG(WARNING) << "put stream_load_record rocksdb failed. stream_load_recorder is null.";
443
0
    }
444
111
}
445
446
Status HttpStreamAction::_handle_group_commit(HttpRequest* req,
447
135
                                              std::shared_ptr<StreamLoadContext> ctx) {
448
135
    std::string group_commit_mode = req->header(HTTP_GROUP_COMMIT);
449
135
    if (!group_commit_mode.empty() && !iequal(group_commit_mode, "sync_mode") &&
450
135
        !iequal(group_commit_mode, "async_mode") && !iequal(group_commit_mode, "off_mode")) {
451
0
        return Status::InvalidArgument(
452
0
                "group_commit can only be [async_mode, sync_mode, off_mode]");
453
0
    }
454
135
    if (config::wait_internal_group_commit_finish) {
455
0
        group_commit_mode = "sync_mode";
456
0
    }
457
135
    int64_t content_length = req->header(HttpHeaders::CONTENT_LENGTH).empty()
458
135
                                     ? 0
459
135
                                     : std::stoll(req->header(HttpHeaders::CONTENT_LENGTH));
460
135
    if (content_length < 0) {
461
0
        std::stringstream ss;
462
0
        ss << "This http load content length <0 (" << content_length
463
0
           << "), please check your content length.";
464
0
        LOG(WARNING) << ss.str();
465
0
        return Status::InvalidArgument(ss.str());
466
0
    }
467
    // allow chunked stream load in flink
468
135
    auto is_chunk =
469
135
            !req->header(HttpHeaders::TRANSFER_ENCODING).empty() &&
470
135
            req->header(HttpHeaders::TRANSFER_ENCODING).find("chunked") != std::string::npos;
471
135
    if (group_commit_mode.empty() || iequal(group_commit_mode, "off_mode") ||
472
135
        (content_length == 0 && !is_chunk)) {
473
        // off_mode and empty
474
121
        ctx->group_commit = false;
475
121
        return Status::OK();
476
121
    }
477
14
    if (is_chunk) {
478
2
        ctx->label = "";
479
2
    }
480
481
14
    auto partial_columns = !req->header(HTTP_PARTIAL_COLUMNS).empty() &&
482
14
                           iequal(req->header(HTTP_PARTIAL_COLUMNS), "true");
483
14
    auto temp_partitions = !req->header(HTTP_TEMP_PARTITIONS).empty();
484
14
    auto partitions = !req->header(HTTP_PARTITIONS).empty();
485
14
    if (!partial_columns && !partitions && !temp_partitions && !ctx->two_phase_commit) {
486
14
        if (!config::wait_internal_group_commit_finish && !ctx->label.empty()) {
487
0
            return Status::InvalidArgument("label and group_commit can't be set at the same time");
488
0
        }
489
14
        ctx->group_commit = true;
490
14
        if (iequal(group_commit_mode, "async_mode")) {
491
13
            if (!load_size_smaller_than_wal_limit(content_length)) {
492
0
                std::stringstream ss;
493
0
                ss << "There is no space for group commit http load async WAL. This http load "
494
0
                      "size is "
495
0
                   << content_length << ". WAL dir info: "
496
0
                   << ExecEnv::GetInstance()->wal_mgr()->get_wal_dirs_info_string();
497
0
                LOG(WARNING) << ss.str();
498
0
                return Status::Error<EXCEEDED_LIMIT>(ss.str());
499
0
            }
500
13
        }
501
14
    }
502
14
    return Status::OK();
503
14
}
504
505
} // namespace doris