Coverage Report

Created: 2026-08-06 13:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exec/spill/spill_file_manager.h
Line
Count
Source
1
2
// Licensed to the Apache Software Foundation (ASF) under one
3
// or more contributor license agreements.  See the NOTICE file
4
// distributed with this work for additional information
5
// regarding copyright ownership.  The ASF licenses this file
6
// to you under the Apache License, Version 2.0 (the
7
// "License"); you may not use this file except in compliance
8
// with the License.  You may obtain a copy of the License at
9
//
10
//   http://www.apache.org/licenses/LICENSE-2.0
11
//
12
// Unless required by applicable law or agreed to in writing,
13
// software distributed under the License is distributed on an
14
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
// KIND, either express or implied.  See the License for the
16
// specific language governing permissions and limitations
17
// under the License.
18
19
#pragma once
20
#include <atomic>
21
#include <memory>
22
#include <mutex>
23
#include <string>
24
#include <unordered_map>
25
#include <vector>
26
27
#include "common/metrics/metrics.h"
28
#include "common/status.h"
29
#include "exec/spill/spill_file.h"
30
#include "storage/options.h"
31
#include "util/threadpool.h"
32
33
namespace doris {
34
class RuntimeProfile;
35
template <typename T>
36
class AtomicCounter;
37
using IntAtomicCounter = AtomicCounter<int64_t>;
38
template <typename T>
39
class AtomicGauge;
40
using UIntGauge = AtomicGauge<uint64_t>;
41
class MetricEntity;
42
struct MetricPrototype;
43
44
class SpillFileManager;
45
class SpillDataDir {
46
public:
47
    SpillDataDir(std::string path, int64_t capacity_bytes,
48
                 TStorageMedium::type storage_medium = TStorageMedium::HDD);
49
50
    Status init();
51
52
208
    const std::string& path() const { return _path; }
53
54
    std::string get_spill_data_path(const std::string& query_id = "") const;
55
56
    std::string get_spill_data_gc_path(const std::string& sub_dir_name = "") const;
57
58
654
    TStorageMedium::type storage_medium() const { return _storage_medium; }
59
60
    // check if the capacity reach the limit after adding the incoming data
61
    // return true if limit reached, otherwise, return false.
62
    bool reach_capacity_limit(int64_t incoming_data_size);
63
64
    Status update_capacity();
65
66
1.20k
    void update_spill_data_usage(int64_t incoming_data_size) {
67
1.20k
        std::lock_guard<std::mutex> l(_mutex);
68
1.20k
        _spill_data_bytes += incoming_data_size;
69
1.20k
        spill_disk_data_size->set_value(_spill_data_bytes);
70
1.20k
    }
71
72
2
    int64_t get_spill_data_bytes() {
73
2
        std::lock_guard<std::mutex> l(_mutex);
74
2
        return _spill_data_bytes;
75
2
    }
76
77
0
    int64_t get_spill_data_limit() {
78
0
        std::lock_guard<std::mutex> l(_mutex);
79
0
        return _spill_data_limit_bytes;
80
0
    }
81
82
    std::string debug_string();
83
84
private:
85
    bool _reach_disk_capacity_limit(int64_t incoming_data_size);
86
1.10k
    double _get_disk_usage(int64_t incoming_data_size) const {
87
1.10k
        return _disk_capacity_bytes == 0
88
1.10k
                       ? 0
89
1.10k
                       : (double)(_disk_capacity_bytes - _available_bytes + incoming_data_size) /
90
1.10k
                                 (double)_disk_capacity_bytes;
91
1.10k
    }
92
93
    friend class SpillFileManager;
94
    std::string _path;
95
96
    // protect _disk_capacity_bytes, _available_bytes, _spill_data_limit_bytes, _spill_data_bytes
97
    std::mutex _mutex;
98
    // the actual capacity of the disk of this data dir
99
    size_t _disk_capacity_bytes;
100
    int64_t _spill_data_limit_bytes = 0;
101
    // the actual available capacity of the disk of this data dir
102
    size_t _available_bytes = 0;
103
    int64_t _spill_data_bytes = 0;
104
    TStorageMedium::type _storage_medium;
105
106
    std::shared_ptr<MetricEntity> spill_data_dir_metric_entity;
107
    IntGauge* spill_disk_capacity = nullptr;
108
    IntGauge* spill_disk_limit = nullptr;
109
    IntGauge* spill_disk_avail_capacity = nullptr;
110
    IntGauge* spill_disk_data_size = nullptr;
111
    // for test
112
    IntGauge* spill_disk_has_spill_data = nullptr;
113
    IntGauge* spill_disk_has_spill_gc_data = nullptr;
114
};
115
class SpillFileManager {
116
public:
117
    ~SpillFileManager();
118
    SpillFileManager(
119
            std::unordered_map<std::string, std::unique_ptr<SpillDataDir>>&& spill_store_map);
120
121
    Status init();
122
123
    void stop();
124
125
    // Create SpillFile and register it
126
    // @param relative_path  Operator-formatted path under the spill root,
127
    //                       e.g. "query_id/sort-node_id-task_id-unique_id"
128
    Status create_spill_file(const std::string& relative_path, SpillFileSPtr& spill_file);
129
130
    /// Get a unique ID for constructing spill file paths.
131
293
    uint64_t next_id() { return id_++; }
132
133
    // Delete SpillFile data synchronously.
134
    void delete_spill_file(SpillFileSPtr spill_file);
135
136
    // Recursively delete a per-query spill directory during query teardown. Failed deletions are
137
    // retained by the manager and retried by its GC and shutdown paths.
138
    void delete_query_spill_directory(const std::string& query_id, SpillDataDir* data_dir);
139
140
    void gc(int32_t max_work_time_ms);
141
142
722
    void update_spill_write_bytes(int64_t bytes) { _spill_write_bytes_counter->increment(bytes); }
143
144
331
    void update_spill_read_bytes(int64_t bytes) { _spill_read_bytes_counter->increment(bytes); }
145
146
private:
147
    struct PendingQuerySpillDirectory {
148
        int failed_count {0};
149
        std::string query_dir;
150
    };
151
152
    void _init_metrics();
153
    Status _init_spill_store_map();
154
    void _spill_gc_thread_callback();
155
    Status _try_delete_query_spill_directory(const PendingQuerySpillDirectory& pending_directory);
156
    void _retry_pending_query_spill_directories();
157
    std::vector<SpillDataDir*> _get_stores_for_spill(TStorageMedium::type storage_medium);
158
159
    std::unordered_map<std::string, std::unique_ptr<SpillDataDir>> _spill_store_map;
160
161
    CountDownLatch _stop_background_threads_latch;
162
    std::shared_ptr<Thread> _spill_gc_thread;
163
164
    std::mutex _pending_query_spill_directories_mutex;
165
    std::vector<PendingQuerySpillDirectory> _pending_query_spill_directories;
166
167
    std::atomic_uint64_t id_ = 0;
168
169
    std::shared_ptr<MetricEntity> _entity {nullptr};
170
171
    std::unique_ptr<doris::MetricPrototype> _spill_write_bytes_metric {nullptr};
172
    std::unique_ptr<doris::MetricPrototype> _spill_read_bytes_metric {nullptr};
173
174
    IntAtomicCounter* _spill_write_bytes_counter {nullptr};
175
    IntAtomicCounter* _spill_read_bytes_counter {nullptr};
176
};
177
} // namespace doris