Coverage Report

Created: 2026-10-28 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/operator/analytic_spill.h
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
#pragma once
19
20
#include <cstddef>
21
#include <cstdint>
22
#include <memory>
23
#include <vector>
24
25
#include "common/status.h"
26
#include "core/block/block.h"
27
#include "core/custom_allocator.h"
28
#include "exprs/aggregate/aggregate_function.h"
29
30
namespace doris {
31
32
class RuntimeProfile;
33
class RuntimeState;
34
class SpillFile;
35
class SpillFileWriter;
36
37
using SpillFileSPtr = std::shared_ptr<SpillFile>;
38
using SpillFileWriterSPtr = std::shared_ptr<SpillFileWriter>;
39
40
/// An immutable run of consecutive analytic partitions handed from the sink to the source. Rows
41
/// keep the input order. partition_ends[p] is the exclusive end row of partition p inside the batch
42
/// and peer_group_ends are batch-relative. Small partitions are batched together so that the source
43
/// replays ordinary sized Blocks even when every partition contains only a few rows.
44
struct AnalyticSpillBatch {
45
    struct RecordMemory {
46
        size_t serialized_bytes = 0;
47
        size_t decoded_bytes = 0;
48
        size_t footer_bytes = 0;
49
    };
50
51
    int64_t rows = 0;
52
    std::vector<Block> blocks;
53
    SpillFileSPtr data_file;
54
    RecordMemory data_record_memory;
55
56
    DorisVector<int64_t> peer_group_ends;
57
    SpillFileSPtr peer_group_file;
58
    RecordMemory peer_record_memory;
59
60
    std::vector<int64_t> partition_ends;
61
    std::vector<WindowSpillStrategy> strategies;
62
    std::vector<WindowSpillPeerFunction> peer_functions;
63
    // Row p holds the result of partition p for PARTITION_REDUCE functions.
64
    std::vector<ColumnPtr> partition_results;
65
    // One constant bucket count per NTILE function, indexed by function index.
66
    // FE requires NTILE arguments to be positive integer literals, e.g. NTILE(4) uses 4 in every
67
    // partition. Other strategies leave their entries at zero.
68
    DorisVector<int64_t> ntile_bucket_counts;
69
    std::vector<DataTypePtr> result_types;
70
    std::vector<bool> change_to_nullable_flags;
71
};
72
73
/// Owns the input rows of one analytic batch: zero or more finished partitions followed by the
74
/// partition that is still receiving rows. Before the first revoke it retains ordinary Blocks.
75
/// Once spilled, the writer stays open until seal(); later small Blocks are coalesced up to the
76
/// spill buffer size before being written, and large Blocks are written directly.
77
class AnalyticSpillBatchStore {
78
public:
79
    AnalyticSpillBatchStore(RuntimeProfile* profile, int node_id, bool has_peer_groups);
80
    ~AnalyticSpillBatchStore();
81
82
    AnalyticSpillBatchStore(const AnalyticSpillBatchStore&) = delete;
83
    AnalyticSpillBatchStore& operator=(const AnalyticSpillBatchStore&) = delete;
84
85
    Status append_block(RuntimeState* state, Block block);
86
    Status append_peer_group_end(RuntimeState* state, int64_t group_end);
87
    Status spill(RuntimeState* state);
88
    Status seal(RuntimeState* state, AnalyticSpillBatch* batch);
89
90
154k
    [[nodiscard]] int64_t rows() const { return _rows; }
91
    [[nodiscard]] size_t revocable_mem_size() const;
92
11.8k
    [[nodiscard]] bool is_spilled() const { return _data_file != nullptr; }
93
2.16k
    [[nodiscard]] bool has_spilled_peer_groups() const { return _peer_group_file != nullptr; }
94
    [[nodiscard]] size_t pending_write_bytes() const;
95
4.06k
    [[nodiscard]] size_t pending_data_bytes() const { return _write_buffer.allocated_bytes(); }
96
4.06k
    [[nodiscard]] size_t pending_peer_bytes() const {
97
4.06k
        return _peer_group_ends.capacity() * sizeof(int64_t);
98
4.06k
    }
99
1.28k
    [[nodiscard]] int64_t peer_group_metadata_bytes() const {
100
1.28k
        return _peer_group_count * sizeof(int64_t);
101
1.28k
    }
102
103
private:
104
    Status _create_writer(RuntimeState* state, const char* label, SpillFileSPtr& file,
105
                          SpillFileWriterSPtr& writer);
106
    Status _write_data_block(RuntimeState* state, Block block);
107
    Status _flush_write_buffer(RuntimeState* state);
108
    Status _ensure_peer_group_writer(RuntimeState* state);
109
    Status _flush_peer_group_ends(RuntimeState* state);
110
    void _release_blocks();
111
    void _release_peer_group_buffer();
112
113
    RuntimeProfile* _profile = nullptr;
114
    int _node_id = -1;
115
    bool _has_peer_groups = false;
116
    int64_t _rows = 0;
117
    int64_t _peer_group_count = 0;
118
    size_t _blocks_memory_bytes = 0;
119
120
    std::vector<Block> _blocks;
121
    SpillFileSPtr _data_file;
122
    SpillFileWriterSPtr _data_writer;
123
    // Small Blocks appended after the first spill, written once they reach the spill buffer size.
124
    MutableBlock _write_buffer;
125
126
    DorisVector<int64_t> _peer_group_ends;
127
    SpillFileSPtr _peer_group_file;
128
    SpillFileWriterSPtr _peer_group_writer;
129
};
130
131
} // namespace doris