XRootD
Loading...
Searching...
No Matches
XrdPfcResourceMonitor.hh
Go to the documentation of this file.
1#ifndef __XRDPFC_RESOURCEMONITOR_HH__
2#define __XRDPFC_RESOURCEMONITOR_HH__
3
4#include "XrdPfcStats.hh"
7
8#include <sys/stat.h>
9
11
12#include <list>
13#include <set>
14#include <string>
15#include <vector>
16
17class XrdOss;
18
19namespace XrdPfc {
20
21struct DataFsState;
22struct DirState;
23struct DirStateElement;
24struct DataFsSnapshot;
25struct DirPurgeElement;
26struct DataFsPurgeshot;
27class FsTraversal;
28
29//==============================================================================
30// ResourceMonitor
31//==============================================================================
32
33// Encapsulates local variables used withing the previous mega-function Purge().
34//
35// This will be used within the continuously/periodically ran heart-beat / breath
36// function ... and then parts of it will be passed to invoked FS scan and purge
37// jobs (which will be controlled throught this as well).
38
39// Andy: XRDADMINPATH Is the directory for administrative files (i.e. all.adminpath)
40// Also: XrdOucEnv::Export("XRDLOGDIR", logParms.logfn); (in XrdOucLogging::configLog)
41
43{
44 template<typename ID, typename RECORD>
45 class Queue {
46 public:
47 struct Entry {
49 RECORD record;
50 };
51 using queue_type = std::vector<Entry>;
52 using iterator = typename queue_type::iterator;
53
54 Queue() = default;
55
56 int write_queue_size() const { return m_write_queue.size(); }
57 bool read_queue_empty() const { return m_read_queue.empty(); }
58 int read_queue_size() const { return m_read_queue.size(); }
59
60 // Writer / producer access
61 void push(ID id, RECORD stat) { m_write_queue.push_back({ id, stat }); }
62 // Existing entry access for updating Stats
63 RECORD& write_record(int pos) { return m_write_queue[pos].record; }
64
65 // Reader / consumer access
66 int swap_queues() { m_read_queue.clear(); m_write_queue.swap(m_read_queue); return read_queue_size(); }
67 const queue_type& read_queue() const { return m_read_queue; }
68 iterator begin() const { return m_read_queue.begin(); }
69 iterator end() const { return m_read_queue.end(); }
70
71 // Shrinkage of overgrown queues
72 void shrink_read_queue() { m_read_queue.clear(); m_read_queue.shrink_to_fit(); }
73
74 private:
75 queue_type m_write_queue, m_read_queue;
76 };
77
78 struct AccessToken {
79 std::string m_filename;
80 unsigned int m_last_queue_swap_u1 = 0xffffffff;
81 int m_last_write_queue_pos = -1;
82 DirState *m_dir_state = nullptr;
83
84 void clear() {
85 m_filename.clear();
86 m_last_queue_swap_u1 = 0xffffffff;
87 m_last_write_queue_pos = -1;
88 m_dir_state = nullptr;
89 }
90 };
91 // Stable addresses: process_queues() uses AccessToken& without m_queue_mutex.
92 // 4096 * 1024 tokens, one per open file. acquire() and release() run under
93 // m_queue_mutex, in the same critical section as the queue push.
94 StableChunkPool<AccessToken, 12, 1024> m_access_tokens;
95
96 struct OpenRecord {
97 time_t m_open_time;
98 bool m_existing_file;
99 };
100
101 struct CloseRecord {
102 time_t m_close_time;
103 Stats m_full_stats;
104 };
105
106 struct PurgeRecord {
107 long long m_size_in_st_blocks;
108 int m_n_files;
109 };
110
111 Queue<int, OpenRecord> m_file_open_q;
112 Queue<int, Stats> m_file_update_stats_q;
113 Queue<int, CloseRecord> m_file_close_q;
114 Queue<DirState*, PurgeRecord> m_file_purge_q1;
115 Queue<std::string, PurgeRecord> m_file_purge_q2;
116 Queue<std::string, long long> m_file_purge_q3;
117 // DirPurge queue -- not needed? But we do need last-change timestamp in DirState.
118
119 long long m_current_usage_in_st_blocks = 0; // aggregate disk usage by files
120 int m_dir_count_discrepancy = 0; // last reported usage-vs-tree mismatch
121 long long m_token_alloc_failures = 0; // token space exhausted, under m_queue_mutex
122
123 XrdSysMutex m_queue_mutex; // mutex shared between queues
124 unsigned int m_queue_swap_u1 = 0u; // identifier of current swap cycle
125
126 DataFsState &m_fs_state;
127 XrdOss &m_oss;
128
129 // Requests for File opens during name-space scans. Such LFNs are processed
130 // with some priority
131 struct LfnCondRecord
132 {
133 const std::string &f_lfn;
134 XrdSysCondVar &f_cond;
135 bool &f_checked;
136 };
137
138 // Top-level directories the traversal does not enter, so they have no DirState.
139 std::set<std::string> m_protected_top_dirs;
140
141 bool is_in_protected_top_dir(const std::string &path) const {
142 size_t b = path.find_first_not_of('/');
143 if (b == std::string::npos) return false;
144 size_t e = path.find('/', b);
145 return m_protected_top_dirs.find(
146 path.substr(b, e == std::string::npos ? std::string::npos : e - b))
147 != m_protected_top_dirs.end();
148 }
149
150 XrdSysMutex m_dir_scan_mutex;
151 std::list<LfnCondRecord> m_dir_scan_open_requests;
152 int m_dir_scan_check_counter = 0;
153 bool m_dir_scan_in_progress = true;
154
156 static void seed_last_times_from_cinfo(DirUsage &u, const struct stat &cinfo_stat) {
157 if (cinfo_stat.st_mtime > u.m_LastOpenTime) u.m_LastOpenTime = cinfo_stat.st_mtime;
158 if (cinfo_stat.st_mtime > u.m_LastCloseTime) u.m_LastCloseTime = cinfo_stat.st_mtime;
159 }
160
161 void process_inter_dir_scan_open_requests(FsTraversal &fst);
162 void cross_check_or_process_oob_lfn(const std::string &lfn, FsTraversal &fst);
163 long long get_file_usage_bytes_to_remove(const DataFsPurgeshot &ps, long long previous_file_usage, int logLeve);
164
165public:
166 ResourceMonitor(XrdOss& oss);
168
169 // --- Initial scan, building of DirState tree
170
171 void scan_dir_and_recurse(FsTraversal &fst);
173
174 // --- Event registration
175
176 int register_file_open(const std::string& filename, time_t open_timestamp, bool existing_file) {
177 // Simply return a token, we will resolve it in the actual processing of the queue.
178 XrdSysMutexHelper _lock(&m_queue_mutex);
179 int token_id = m_access_tokens.acquire();
180 if (token_id < 0) {
181 // Unreachable in practice; heart_beat() reports the count.
182 ++m_token_alloc_failures;
183 return -1;
184 }
185 AccessToken &at = m_access_tokens[token_id];
186 at.m_filename = filename;
187 at.m_last_queue_swap_u1 = m_queue_swap_u1 - 1;
188
189 m_file_open_q.push(token_id, {open_timestamp, existing_file});
190 return token_id;
191 }
192
193 void register_file_update_stats(int token_id, const Stats& stats) {
194 if (token_id < 0) return; // register_file_open() could not get a token
195 XrdSysMutexHelper _lock(&m_queue_mutex);
196 AccessToken &at = token(token_id);
197 // Check if this is the first update within this queue swap cycle.
198 if (at.m_last_queue_swap_u1 != m_queue_swap_u1) {
199 m_file_update_stats_q.push(token_id, stats);
200 at.m_last_queue_swap_u1 = m_queue_swap_u1;
201 at.m_last_write_queue_pos = m_file_update_stats_q.write_queue_size() - 1;
202 } else {
203 Stats &existing_stats = m_file_update_stats_q.write_record(at.m_last_write_queue_pos);
204 existing_stats.AddUp(stats);
205 }
206 // Optionally, one could return "scaler" to moodify stat-reporting
207 // frequency in the file ... if it comes too often or too rarely.
208 // See also the logic for determining reporting interval (in N_bytes_read)
209 // in File::Open().
210 }
211
212 void register_file_close(int token_id, time_t close_timestamp, const Stats& full_stats) {
213 if (token_id < 0) return; // register_file_open() could not get a token
214 XrdSysMutexHelper _lock(&m_queue_mutex);
215 m_file_close_q.push(token_id, {close_timestamp, full_stats});
216 }
217
218 // deletions can come from purge and from direct requests (Cache::UnlinkFile), the latter
219 // also covering the emergency shutdown of a file.
220 void register_file_purge(DirState* target, long long size_in_st_blocks) {
221 XrdSysMutexHelper _lock(&m_queue_mutex);
222 m_file_purge_q1.push(target, {size_in_st_blocks, 1});
223 }
224 void register_multi_file_purge(DirState* target, long long size_in_st_blocks, int n_files) {
225 XrdSysMutexHelper _lock(&m_queue_mutex);
226 m_file_purge_q1.push(target, {size_in_st_blocks, n_files});
227 }
228 void register_multi_file_purge(const std::string& target, long long size_in_st_blocks, int n_files) {
229 XrdSysMutexHelper _lock(&m_queue_mutex);
230 m_file_purge_q2.push(target, {size_in_st_blocks, n_files});
231 }
232 void register_file_purge(const std::string& filename, long long size_in_st_blocks) {
233 XrdSysMutexHelper _lock(&m_queue_mutex);
234 m_file_purge_q3.push(filename, size_in_st_blocks);
235 }
236
237 // void register_dir_purge(DirState* target);
238 // target assumed to be empty at this point, triggered by a file_purge removing the last file in it.
239 // hmmh, this is actually tricky ... who will purge the dirs? we should now at export-to-vector time
240 // and can prune leaf directories. This might fail if a file has been created in there in the meantime, which is ok.
241 // However, is there a race condition between rmdir and creation of a new file in that dir? Ask Andy.
242
243 // --- Helpers for event processing and actions
244
245 AccessToken& token(int i) { return m_access_tokens[i]; }
246
247 // --- Actions
248
249 int process_queues();
250
251 void heart_beat();
252
253 // --- Helpers for export of DirState vector snapshot.
254
255 void fill_sshot_vec_children(const DirState &parent_ds,
256 int parent_idx,
257 std::vector<DirStateElement> &vec,
258 int max_depth);
259
260 void fill_pshot_vec_children(const DirState &parent_ds,
261 int parent_idx,
262 std::vector<DirPurgeElement> &vec,
263 int max_depth);
264
265 // Interface to other part of XCache -- note the CamelCase() notation.
266 void CrossCheckIfScanIsInProgress(const std::string &lfn, XrdSysCondVar &cond);
267
268 // main function, steers startup then enters heart_beat. does not die.
269 void init_before_main(); // called from startup thread / configuration processing
270 void main_thread_function(); // run in dedicated thread
271
273 // The following variables are set under the above lock, purge task signals to heart_beat.
276 bool m_purge_task_active {false}; // from the perspective of heart-beat, set only in heartbeat
277 bool m_purge_task_complete {false}; // from the perspective of the task, reset in heartbeat, set in task
278 // When m_purge_task_active == true at the start of a heart_beat(), DirState entries
279 // are not removed from the tree in it, to allow a purge thread to report cleared
280 // files directly via DirState ptr (not used yet: the purge shot is a copy).
281 // Note, DirState removal happens at the end of heart_beat().
282
283 // Purge helpers etc.
285 void perform_purge_check(bool purge_cold_files, int tl);
286
289};
290
291}
292
293#endif
#define stat(a, b)
Definition XrdPosix.hh:105
#define ID
void CrossCheckIfScanIsInProgress(const std::string &lfn, XrdSysCondVar &cond)
int register_file_open(const std::string &filename, time_t open_timestamp, bool existing_file)
void perform_purge_check(bool purge_cold_files, int tl)
void fill_sshot_vec_children(const DirState &parent_ds, int parent_idx, std::vector< DirStateElement > &vec, int max_depth)
void perform_purge_task(DataFsPurgeshot &ps)
void register_file_purge(const std::string &filename, long long size_in_st_blocks)
void scan_dir_and_recurse(FsTraversal &fst)
void register_file_purge(DirState *target, long long size_in_st_blocks)
void fill_pshot_vec_children(const DirState &parent_ds, int parent_idx, std::vector< DirPurgeElement > &vec, int max_depth)
void register_multi_file_purge(DirState *target, long long size_in_st_blocks, int n_files)
void register_file_update_stats(int token_id, const Stats &stats)
void register_multi_file_purge(const std::string &target, long long size_in_st_blocks, int n_files)
void register_file_close(int token_id, time_t close_timestamp, const Stats &full_stats)
Statistics of cache utilisation by a File object.
void AddUp(const Stats &s)
XrdPosixStats Stats