Coverage Report

Created: 2026-10-29 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format/jni/jni_reader.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 "jni_reader.h"
19
20
#include <glog/logging.h>
21
22
#include <atomic>
23
#include <map>
24
#include <ostream>
25
#include <tuple>
26
#include <unordered_map>
27
#include <utility>
28
29
#include "core/block/block.h"
30
#include "core/types.h"
31
#include "format/jni/jni_data_bridge.h"
32
#include "format/table/partition_column_filler.h"
33
#include "io/io_common.h"
34
#include "runtime/descriptors.h"
35
#include "runtime/runtime_state.h"
36
#include "util/defer_op.h"
37
#include "util/jni-util.h"
38
39
namespace doris {
40
class RuntimeProfile;
41
class RuntimeState;
42
43
class Block;
44
} // namespace doris
45
46
namespace doris {
47
48
const std::vector<SlotDescriptor*> JniReader::_s_empty_slot_descs;
49
50
// =========================================================================
51
// JniReader constructors
52
// =========================================================================
53
54
JniReader::JniReader(const std::vector<SlotDescriptor*>& file_slot_descs, RuntimeState* state,
55
                     RuntimeProfile* profile, Jni::PluginRef plugin_ref,
56
                     std::map<std::string, std::string> scanner_params,
57
                     std::vector<std::string> column_names, int64_t self_split_weight,
58
                     int64_t jni_heap_bytes)
59
2
        : _file_slot_descs(file_slot_descs),
60
2
          _state(state),
61
2
          _profile(profile),
62
2
          _plugin_ref(plugin_ref),
63
2
          _connector_name(plugin_ref.plugin),
64
2
          _scanner_params(std::move(scanner_params)),
65
2
          _column_names(std::move(column_names)),
66
2
          _self_split_weight(static_cast<int32_t>(self_split_weight)),
67
2
          _jni_heap_bytes(jni_heap_bytes) {}
68
69
JniReader::JniReader(Jni::PluginRef plugin_ref, std::map<std::string, std::string> scanner_params)
70
3
        : _file_slot_descs(_s_empty_slot_descs),
71
3
          _plugin_ref(plugin_ref),
72
3
          _connector_name(plugin_ref.plugin),
73
3
          _scanner_params(std::move(scanner_params)) {
74
3
    _is_table_schema = true;
75
3
}
76
77
0
Status JniReader::on_before_init_reader(ReaderInitContext* ctx) {
78
0
    _column_descs = ctx->column_descs;
79
0
    _io_ctx = ctx->io_ctx;
80
0
    if (_col_name_to_block_idx == nullptr) {
81
0
        _col_name_to_block_idx = ctx->col_name_to_block_idx;
82
0
    }
83
0
    _partition_values.clear();
84
0
    _partition_value_is_null.clear();
85
0
    if (ctx->range == nullptr || ctx->tuple_descriptor == nullptr ||
86
0
        !ctx->range->__isset.columns_from_path_keys) {
87
0
        return Status::OK();
88
0
    }
89
90
0
    DORIS_CHECK(ctx->range->__isset.columns_from_path);
91
0
    DORIS_CHECK(ctx->range->columns_from_path.size() == ctx->range->columns_from_path_keys.size());
92
0
    const bool has_null_flags = ctx->range->__isset.columns_from_path_is_null;
93
0
    if (has_null_flags) {
94
0
        DORIS_CHECK(ctx->range->columns_from_path_is_null.size() ==
95
0
                    ctx->range->columns_from_path_keys.size());
96
0
    }
97
98
0
    std::unordered_map<std::string, const SlotDescriptor*> name_to_slot;
99
0
    for (auto* slot : ctx->tuple_descriptor->slots()) {
100
0
        name_to_slot.emplace(slot->col_name(), slot);
101
0
    }
102
0
    for (size_t i = 0; i < ctx->range->columns_from_path_keys.size(); ++i) {
103
0
        const auto& key = ctx->range->columns_from_path_keys[i];
104
0
        auto slot_it = name_to_slot.find(key);
105
0
        if (slot_it == name_to_slot.end()) {
106
0
            continue;
107
0
        }
108
0
        _partition_values.emplace(
109
0
                key, std::make_tuple(ctx->range->columns_from_path[i], slot_it->second));
110
0
        _partition_value_is_null.emplace(
111
0
                key, has_null_flags ? ctx->range->columns_from_path_is_null[i] : false);
112
0
    }
113
0
    return Status::OK();
114
0
}
115
116
0
Status JniReader::on_after_read_block(Block* block, size_t* read_rows) {
117
0
    if (_column_descs == nullptr || _partition_values.empty() || *read_rows == 0 ||
118
0
        _push_down_agg_type == TPushAggOp::type::COUNT) {
119
0
        return Status::OK();
120
0
    }
121
0
    return _fill_partition_columns(block, *read_rows);
122
0
}
123
124
// =========================================================================
125
// JniReader::open  (merged from JniConnector::open)
126
// =========================================================================
127
128
3
Status JniReader::open(RuntimeState* state, RuntimeProfile* profile) {
129
3
    _state = state;
130
3
    _profile = profile;
131
3
    if (_profile) {
132
0
        ADD_TIMER(_profile, _connector_name.c_str());
133
0
        _open_scanner_time = ADD_CHILD_TIMER(_profile, "OpenScannerTime", _connector_name.c_str());
134
0
        _java_scan_time = ADD_CHILD_TIMER(_profile, "JavaScanTime", _connector_name.c_str());
135
0
        _java_append_data_time =
136
0
                ADD_CHILD_TIMER(_profile, "JavaAppendDataTime", _connector_name.c_str());
137
0
        _java_create_vector_table_time =
138
0
                ADD_CHILD_TIMER(_profile, "JavaCreateVectorTableTime", _connector_name.c_str());
139
0
        _fill_block_time = ADD_CHILD_TIMER(_profile, "FillBlockTime", _connector_name.c_str());
140
0
        _jvm_heap_wait_time = ADD_CHILD_TIMER(_profile, "JvmHeapWaitTime", _connector_name.c_str());
141
0
        _jvm_heap_declared_bytes = ADD_CHILD_COUNTER(_profile, "JvmHeapDeclaredBytes", TUnit::BYTES,
142
0
                                                     _connector_name.c_str());
143
0
        _max_time_split_weight_counter = _profile->add_conditition_counter(
144
0
                "MaxTimeSplitWeight", TUnit::UNIT, [](int64_t _c, int64_t c) { return c > _c; },
145
0
                _connector_name.c_str());
146
0
    }
147
3
    _java_scan_watcher = 0;
148
149
3
    int batch_size = 0;
150
3
    if (!_is_table_schema && _state) {
151
0
        batch_size = _state->batch_size();
152
0
    }
153
3
    _batch_size = batch_size;
154
    // Only a reader whose range declared the heap it will hold asks for it; see the constructor.
155
3
    if (_jni_heap_bytes > 0) {
156
        // FileScanner's try_stop() marks its IOContext: a cancelled query, a satisfied limit, a
157
        // closing scan. The gate asks on a thread of its own, so the check owns what it reads, and
158
        // reads it atomically, as try_stop() writes it.
159
0
        DORIS_CHECK(_io_ctx != nullptr);
160
0
        _heap_admission =
161
0
                JniScanHeapGate::instance()->request(_jni_heap_bytes, [io_ctx = _io_ctx]() {
162
0
                    return std::atomic_ref<bool>(io_ctx->should_stop)
163
0
                            .load(std::memory_order_relaxed);
164
0
                });
165
0
        if (_profile != nullptr) {
166
0
            COUNTER_UPDATE(_jvm_heap_declared_bytes, _jni_heap_bytes);
167
0
        }
168
0
        if (_heap_admission->waiting()) {
169
            // The first read opens it once the gate admits it. Until then FileScanner parks on
170
            // waiting_for() instead of keeping a worker that admitted readers need to finish.
171
0
            return Status::OK();
172
0
        }
173
0
    }
174
3
    return _open_admitted_java_scanner();
175
3
}
176
177
0
std::optional<SharedListenableFuture<Void>> JniReader::waiting_for() const {
178
0
    if (_heap_admission != nullptr && _heap_admission->waiting()) {
179
0
        return _heap_admission->future();
180
0
    }
181
0
    return std::nullopt;
182
0
}
183
184
3
Status JniReader::_open_admitted_java_scanner() {
185
3
    if (_heap_admission != nullptr) {
186
0
        DORIS_CHECK(!_heap_admission->waiting());
187
0
        if (_profile != nullptr) {
188
0
            COUNTER_UPDATE(_jvm_heap_wait_time, _heap_admission->wait_ns());
189
0
        }
190
0
        if (!_heap_admission->admitted()) {
191
            // The scan stopped before this reader's turn: it opens no Java scanner and reads
192
            // nothing. Opening it would not be harmless - FileScanner reads the first block in the
193
            // same pass, and a merge read loads the row groups of all its files for that block.
194
0
            return Status::OK();
195
0
        }
196
0
    }
197
    // The admission covers the Java scanner: close() gives it back, and here it goes if the
198
    // scanner did not open - attaching to the JVM can fail too.
199
3
    Defer release_unless_opened {[this]() {
200
3
        if (!_scanner_opened) {
201
0
            _heap_admission.reset();
202
0
        }
203
3
    }};
204
    // Only now does the reader attach to the JVM.
205
3
    JNIEnv* env = nullptr;
206
3
    RETURN_IF_ERROR(Jni::Env::Get(&env));
207
3
    SCOPED_RAW_TIMER(&_jni_scanner_open_watcher);
208
3
    if (_state) {
209
0
        _scanner_params.emplace("time_zone", _state->timezone());
210
0
    }
211
3
    RETURN_IF_ERROR(_init_jni_scanner(env, static_cast<int>(_batch_size)));
212
    // Call org.apache.doris.jni.spi.JniScanner#open
213
3
    RETURN_IF_ERROR(_jni_scanner_obj.call_void_method(env, _scanner_api->open).call());
214
215
3
    RETURN_ERROR_IF_EXC(env);
216
3
    _scanner_opened = true;
217
3
    return Status::OK();
218
3
}
219
220
// =========================================================================
221
// JniReader::_do_get_next_block  (merged from JniConnector::get_next_block)
222
// =========================================================================
223
224
0
Status JniReader::_do_get_next_block(Block* block, size_t* read_rows, bool* eof) {
225
0
    if (!_scanner_opened) {
226
        // Only a reader that open() left waiting for its share of the JVM heap is unopened here,
227
        // and FileScanner reads it only once waiting_for() says the wait is over.
228
0
        DORIS_CHECK(_heap_admission != nullptr);
229
0
        RETURN_IF_ERROR(_open_admitted_java_scanner());
230
0
        if (!_scanner_opened) {
231
            // Its scan stopped first.
232
0
            *read_rows = 0;
233
0
            *eof = true;
234
0
            return Status::OK();
235
0
        }
236
0
    }
237
0
    JNIEnv* env = nullptr;
238
0
    RETURN_IF_ERROR(Jni::Env::Get(&env));
239
0
    long meta_address = 0;
240
0
    {
241
0
        SCOPED_RAW_TIMER(&_java_scan_watcher);
242
0
        RETURN_IF_ERROR(_jni_scanner_obj.call_long_method(env, _scanner_api->get_next_batch_meta)
243
0
                                .call(&meta_address));
244
0
    }
245
0
    if (meta_address == 0) {
246
0
        *read_rows = 0;
247
0
        *eof = true;
248
0
        return Status::OK();
249
0
    }
250
0
    _set_meta(meta_address);
251
0
    long num_rows = _table_meta.next_meta_as_long();
252
0
    if (num_rows == 0) {
253
0
        *read_rows = 0;
254
0
        *eof = true;
255
0
        return Status::OK();
256
0
    }
257
0
    RETURN_IF_ERROR(_fill_block(block, num_rows));
258
0
    *read_rows = num_rows;
259
0
    *eof = false;
260
0
    RETURN_IF_ERROR(_jni_scanner_obj.call_void_method(env, _scanner_api->release_table).call());
261
0
    _has_read += num_rows;
262
0
    return Status::OK();
263
0
}
264
265
// =========================================================================
266
// JniReader::close  (merged from JniConnector::close)
267
// =========================================================================
268
269
3
Status JniReader::close() {
270
3
    if (_closed) {
271
0
        return Status::OK();
272
0
    }
273
3
    if (!_scanner_opened) {
274
        // Leaves the gate's line if it was still waiting.
275
0
        _heap_admission.reset();
276
0
        _closed = true;
277
0
        return Status::OK();
278
0
    }
279
280
3
    JNIEnv* env = nullptr;
281
3
    RETURN_IF_ERROR(Jni::Env::Get(&env));
282
283
    // _fill_block may fail before releasing the current Java table. JniScanner::releaseTable()
284
    // is idempotent, so close always retries it. Java close must still run when that release
285
    // fails, otherwise connector resources such as Paimon's static table-cache lease can leak.
286
3
    auto close_status = _jni_scanner_obj.call_void_method(env, _scanner_api->release_table).call();
287
3
    auto java_close_status = _jni_scanner_obj.call_void_method(env, _scanner_api->close).call();
288
3
    if (close_status.ok() && !java_close_status.ok()) {
289
0
        close_status = std::move(java_close_status);
290
0
    }
291
3
    if (close_status.ok()) {
292
3
        _heap_admission.reset();
293
3
        _scanner_opened = false;
294
3
        _closed = true;
295
3
    }
296
3
    return close_status;
297
3
}
298
299
// =========================================================================
300
// JniReader::set_batch_size
301
// =========================================================================
302
303
0
void JniReader::set_batch_size(size_t batch_size) {
304
0
    DCHECK_GT(batch_size, 0);
305
0
    if (_batch_size == batch_size) {
306
0
        return;
307
0
    }
308
0
    _batch_size = batch_size;
309
0
    if (_scanner_opened) {
310
0
        JNIEnv* env = nullptr;
311
0
        Status st = Jni::Env::Get(&env);
312
0
        if (!st) {
313
0
            LOG(WARNING) << "failed to get jni env when set_batch_size: " << st;
314
0
            return;
315
0
        }
316
0
        st = _jni_scanner_obj.call_void_method(env, _scanner_api->set_batch_size)
317
0
                     .with_arg(static_cast<int>(_batch_size))
318
0
                     .call();
319
0
        if (!st) {
320
0
            LOG(WARNING) << "failed to call setBatchSize: " << st;
321
0
        }
322
0
    }
323
0
}
324
325
// =========================================================================
326
// JniReader::_init_jni_scanner  (merged from JniConnector::_init_jni_scanner)
327
// =========================================================================
328
329
3
Status JniReader::_init_jni_scanner(JNIEnv* env, int batch_size) {
330
3
    return Jni::PluginRegistry::create_scanner(env, _plugin_ref, batch_size, _scanner_params,
331
3
                                               &_jni_scanner_obj, &_scanner_api);
332
3
}
333
334
// =========================================================================
335
// JniReader::_fill_block  (merged from JniConnector::_fill_block)
336
// =========================================================================
337
338
0
Status JniReader::_fill_block(Block* block, size_t num_rows) {
339
0
    SCOPED_RAW_TIMER(&_fill_block_watcher);
340
0
    JNIEnv* env = nullptr;
341
0
    RETURN_IF_ERROR(Jni::Env::Get(&env));
342
    // Fallback: if _col_name_to_block_idx was not set by the caller (e.g. JdbcScanner),
343
    // build the name-to-position map from the block itself.
344
0
    std::unordered_map<std::string, uint32_t> local_name_to_idx;
345
0
    const std::unordered_map<std::string, uint32_t>* col_map = _col_name_to_block_idx;
346
0
    if (col_map == nullptr) {
347
0
        local_name_to_idx = block->get_name_to_pos_map();
348
0
        col_map = &local_name_to_idx;
349
0
    }
350
0
    for (int i = 0; i < _column_names.size(); ++i) {
351
0
        auto& column_with_type_and_name = block->get_by_position(col_map->at(_column_names[i]));
352
0
        auto& column_ptr = column_with_type_and_name.column;
353
0
        auto& column_type = column_with_type_and_name.type;
354
0
        RETURN_IF_ERROR(JniDataBridge::fill_column(_table_meta, column_ptr, column_type, num_rows));
355
        // Column is not released when fill_column failed. It will be released when releasing table.
356
0
        RETURN_IF_ERROR(_jni_scanner_obj.call_void_method(env, _scanner_api->release_column)
357
0
                                .with_arg(i)
358
0
                                .call());
359
0
        RETURN_ERROR_IF_EXC(env);
360
0
    }
361
0
    return Status::OK();
362
0
}
363
364
0
Status JniReader::_fill_partition_columns(Block* block, size_t num_rows) {
365
0
    std::unordered_map<std::string, uint32_t> local_name_to_idx;
366
0
    const std::unordered_map<std::string, uint32_t>* col_map = _col_name_to_block_idx;
367
0
    if (col_map == nullptr) {
368
0
        local_name_to_idx = block->get_name_to_pos_map();
369
0
        col_map = &local_name_to_idx;
370
0
    }
371
372
0
    for (const auto& desc : *_column_descs) {
373
0
        if (desc.category != ColumnCategory::PARTITION_KEY) {
374
0
            continue;
375
0
        }
376
0
        auto value_it = _partition_values.find(desc.name);
377
0
        if (value_it == _partition_values.end()) {
378
0
            continue;
379
0
        }
380
0
        auto col_it = col_map->find(desc.name);
381
0
        if (col_it == col_map->end()) {
382
0
            return Status::InternalError("Missing partition column {} in block {}", desc.name,
383
0
                                         block->dump_structure());
384
0
        }
385
386
0
        auto& column_with_type_and_name = block->get_by_position(col_it->second);
387
0
        auto mutable_column = std::move(*column_with_type_and_name.column).mutate();
388
0
        const auto& [value, slot_desc] = value_it->second;
389
0
        auto null_it = _partition_value_is_null.find(desc.name);
390
0
        DORIS_CHECK(null_it != _partition_value_is_null.end());
391
0
        RETURN_IF_ERROR(fill_partition_column_from_path_value(*mutable_column, *slot_desc, value,
392
0
                                                              num_rows, null_it->second));
393
0
        column_with_type_and_name.column = std::move(mutable_column);
394
0
    }
395
0
    return Status::OK();
396
0
}
397
398
// =========================================================================
399
// JniReader::_get_statistics  (merged from JniConnector::get_statistics)
400
// =========================================================================
401
402
0
Status JniReader::_get_statistics(JNIEnv* env, std::map<std::string, std::string>* result) {
403
0
    result->clear();
404
0
    Jni::LocalObject metrics;
405
0
    RETURN_IF_ERROR(
406
0
            _jni_scanner_obj.call_object_method(env, _scanner_api->get_statistics).call(&metrics));
407
408
0
    RETURN_IF_ERROR(Jni::Util::convert_to_cpp_map(env, metrics, result));
409
0
    return Status::OK();
410
0
}
411
412
// =========================================================================
413
// JniReader::_collect_profile_before_close
414
// (merged from JniConnector::_collect_profile_before_close)
415
// =========================================================================
416
417
0
void JniReader::_collect_profile_before_close() {
418
0
    if (_scanner_opened && _profile != nullptr) {
419
0
        JNIEnv* env = nullptr;
420
0
        Status st = Jni::Env::Get(&env);
421
0
        if (!st) {
422
0
            LOG(WARNING) << "failed to get jni env when collect profile: " << st;
423
0
            return;
424
0
        }
425
0
        COUNTER_UPDATE(_open_scanner_time, _jni_scanner_open_watcher);
426
0
        COUNTER_UPDATE(_fill_block_time, _fill_block_watcher);
427
428
0
        jlong append_data_time = 0;
429
0
        auto append_time_status =
430
0
                _jni_scanner_obj.call_long_method(env, _scanner_api->get_append_data_time)
431
0
                        .call(&append_data_time);
432
0
        jlong create_vector_table_time = 0;
433
0
        auto create_table_time_status =
434
0
                _jni_scanner_obj.call_long_method(env, _scanner_api->get_create_vector_table_time)
435
0
                        .call(&create_vector_table_time);
436
0
        if (!append_time_status.ok()) {
437
0
            LOG(WARNING) << "failed to collect JNI append-data time before close: "
438
0
                         << append_time_status;
439
0
        }
440
0
        if (!create_table_time_status.ok()) {
441
0
            LOG(WARNING) << "failed to collect JNI vector-table time before close: "
442
0
                         << create_table_time_status;
443
0
        }
444
0
        if (append_time_status.ok() && create_table_time_status.ok()) {
445
0
            COUNTER_UPDATE(_java_append_data_time, append_data_time);
446
0
            COUNTER_UPDATE(_java_create_vector_table_time, create_vector_table_time);
447
0
            COUNTER_UPDATE(_java_scan_time,
448
0
                           _java_scan_watcher - append_data_time - create_vector_table_time);
449
0
            _max_time_split_weight_counter->conditional_update(
450
0
                    _jni_scanner_open_watcher + _fill_block_watcher + _java_scan_watcher,
451
0
                    _self_split_weight);
452
0
        }
453
454
        // update scanner metrics
455
0
        std::map<std::string, std::string> statistics_result;
456
0
        st = _get_statistics(env, &statistics_result);
457
0
        if (!st) {
458
0
            LOG(WARNING) << "failed to get_statistics when collect profile: " << st;
459
0
            return;
460
0
        }
461
462
0
        const auto update_peak = [](int64_t previous, int64_t current) {
463
0
            return current > previous;
464
0
        };
465
0
        for (const auto& metric : statistics_result) {
466
0
            std::vector<std::string> type_and_name = split(metric.first, ":");
467
0
            if (type_and_name.size() != 2) {
468
0
                LOG(WARNING) << "Name of JNI Scanner metric should be pattern like "
469
0
                             << "'metricType:metricName'";
470
0
                continue;
471
0
            }
472
0
            int64_t metric_value = std::stoll(metric.second);
473
0
            RuntimeProfile::Counter* scanner_counter;
474
0
            if (type_and_name[0] == "timer") {
475
0
                scanner_counter =
476
0
                        ADD_CHILD_TIMER(_profile, type_and_name[1], _connector_name.c_str());
477
0
                COUNTER_UPDATE(scanner_counter, metric_value);
478
0
            } else if (type_and_name[0] == "counter") {
479
0
                scanner_counter = ADD_CHILD_COUNTER(_profile, type_and_name[1], TUnit::UNIT,
480
0
                                                    _connector_name.c_str());
481
0
                COUNTER_UPDATE(scanner_counter, metric_value);
482
0
            } else if (type_and_name[0] == "bytes") {
483
0
                scanner_counter = ADD_CHILD_COUNTER(_profile, type_and_name[1], TUnit::BYTES,
484
0
                                                    _connector_name.c_str());
485
0
                COUNTER_UPDATE(scanner_counter, metric_value);
486
0
            } else if (type_and_name[0] == "timer_gauge") {
487
0
                scanner_counter =
488
0
                        ADD_CHILD_TIMER(_profile, type_and_name[1], _connector_name.c_str());
489
0
                COUNTER_SET(scanner_counter, metric_value);
490
0
            } else if (type_and_name[0] == "gauge") {
491
0
                scanner_counter = ADD_CHILD_COUNTER(_profile, type_and_name[1], TUnit::UNIT,
492
0
                                                    _connector_name.c_str());
493
0
                COUNTER_SET(scanner_counter, metric_value);
494
0
            } else if (type_and_name[0] == "bytes_gauge") {
495
0
                scanner_counter = ADD_CHILD_COUNTER(_profile, type_and_name[1], TUnit::BYTES,
496
0
                                                    _connector_name.c_str());
497
0
                COUNTER_SET(scanner_counter, metric_value);
498
0
            } else if (type_and_name[0] == "timer_peak") {
499
0
                auto* scanner_peak_counter = _profile->add_conditition_counter(
500
0
                        type_and_name[1], TUnit::TIME_NS, update_peak, _connector_name.c_str());
501
0
                scanner_peak_counter->conditional_update(metric_value, metric_value);
502
0
            } else if (type_and_name[0] == "peak") {
503
0
                auto* scanner_peak_counter = _profile->add_conditition_counter(
504
0
                        type_and_name[1], TUnit::UNIT, update_peak, _connector_name.c_str());
505
0
                scanner_peak_counter->conditional_update(metric_value, metric_value);
506
0
            } else if (type_and_name[0] == "bytes_peak") {
507
0
                auto* scanner_peak_counter = _profile->add_conditition_counter(
508
0
                        type_and_name[1], TUnit::BYTES, update_peak, _connector_name.c_str());
509
0
                scanner_peak_counter->conditional_update(metric_value, metric_value);
510
0
            } else {
511
                LOG(WARNING) << "Type of JNI Scanner metric should be timer, counter, bytes, "
512
0
                             << "timer_gauge, gauge, bytes_gauge, timer_peak, peak or bytes_peak";
513
0
                continue;
514
0
            }
515
0
        }
516
0
    }
517
0
}
518
519
} // namespace doris