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