17#define dprintf(...) printf(__VA_ARGS__)
19#define dprintf(...) (void(0))
27 const char *m_traceID =
"ResourceMonitor";
48 m_dir_scan_mutex.Lock();
49 if (m_dir_scan_in_progress) {
50 bool dir_checked =
false;
51 m_dir_scan_open_requests.push_back({lfn, cond, dir_checked});
53 m_dir_scan_mutex.UnLock();
54 while ( ! dir_checked)
58 m_dir_scan_mutex.UnLock();
62void ResourceMonitor::process_inter_dir_scan_open_requests(
FsTraversal &fst)
64 m_dir_scan_mutex.
Lock();
65 while ( ! m_dir_scan_open_requests.empty())
67 LfnCondRecord &lcr = m_dir_scan_open_requests.front();
70 cross_check_or_process_oob_lfn(lcr.f_lfn, fst);
76 m_dir_scan_mutex.
Lock();
77 m_dir_scan_open_requests.pop_front();
82void ResourceMonitor::cross_check_or_process_oob_lfn(
const std::string &lfn,
FsTraversal &fst)
86 static const char *trc_pfx =
"cross_check_or_process_oob_lfn() ";
91 DirState *last_existing_ds =
nullptr;
92 DirState *ds = m_fs_state.find_dirstate_for_lfn(lfn, &last_existing_ds);
96 size_t pos = lfn.find_last_of(
"/");
97 std::string dir = (pos == std::string::npos) ?
"" : lfn.substr(0, pos);
99 XrdOssDF *dhp = m_oss.newDir(trc_pfx);
109 if (it->second.has_data && it->second.has_cinfo) {
110 here.
m_StBlocks += it->second.stat_data.st_blocks;
123 dprintf(
"In scan_dir_and_recurse for '%s', size of dir_vec = %d, file_stat_map = %d\n",
134 dprintf(
"would be doing something with %s ... has_data=%d, has_cinfo=%d\n",
135 it->first.c_str(), it->second.has_data, it->second.has_cinfo);
143 if (it->second.has_data && it->second.has_cinfo) {
144 here.
m_StBlocks += it->second.stat_data.st_blocks;
152 std::vector<std::string> dirs;
155 if (++m_dir_scan_check_counter >= 100)
157 process_inter_dir_scan_open_requests(fst);
158 m_dir_scan_check_counter = 0;
163 for (
auto &dname : dirs)
181 DirState *root_ds = m_fs_state.get_root();
200 m_dir_scan_in_progress =
false;
201 m_dir_scan_check_counter = 0;
204 while ( ! m_dir_scan_open_requests.empty())
206 LfnCondRecord &lcr = m_dir_scan_open_requests.front();
208 lcr.f_checked =
true;
212 m_dir_scan_open_requests.pop_front();
230 static const char *trc_pfx =
"process_queues() ";
241 n_records += m_file_open_q.swap_queues();
242 n_records += m_file_update_stats_q.swap_queues();
243 n_records += m_file_close_q.swap_queues();
244 n_records += m_file_purge_q1.swap_queues();
245 n_records += m_file_purge_q2.swap_queues();
246 n_records += m_file_purge_q3.swap_queues();
250 for (
auto &i : m_file_open_q.read_queue())
253 AccessToken &at =
token(i.id);
254 dprintf(
"process file open for token %d, time %ld -- %s\n",
255 i.id, i.record.m_open_time, at.m_filename.c_str());
260 DirState *last_existing_ds =
nullptr;
261 DirState *ds = m_fs_state.find_dirstate_for_lfn(at.m_filename, &last_existing_ds);
268 for (
auto &i : m_file_update_stats_q.read_queue())
271 AccessToken &at =
token(i.id);
274 dprintf(
"process file update for token %d, %p -- %s\n",
275 i.id, ds, at.m_filename.c_str());
278 m_current_usage_in_st_blocks += i.record.m_StBlocksAdded;
281 for (
auto &i : m_file_close_q.read_queue())
284 AccessToken &at =
token(i.id);
285 dprintf(
"process file close for token %d, time %ld -- %s\n",
286 i.id, i.record.m_close_time, at.m_filename.c_str());
295 for (
auto &i : m_file_close_q.read_queue())
296 m_access_tokens.release(i.id);
299 for (
auto &i : m_file_purge_q1.read_queue())
305 m_current_usage_in_st_blocks -= i.record.m_size_in_st_blocks;
307 for (
auto &i : m_file_purge_q2.read_queue())
312 TRACE(
Error, trc_pfx <<
"DirState not found for directory path '" << i.id <<
"'.");
318 m_current_usage_in_st_blocks -= i.record.m_size_in_st_blocks;
320 for (
auto &i : m_file_purge_q3.read_queue())
325 TRACE(
Error, trc_pfx <<
"DirState not found for LFN path '" << i.id <<
"'.");
330 m_current_usage_in_st_blocks -= i.record;
345 static const char *tpfx =
"heart_beat() ";
349 const int s_queue_proc_interval = 10;
352 const int s_purge_check_interval = 60;
358 time_t now = time(0);
359 time_t next_queue_proc_time = now + s_queue_proc_interval;
360 time_t next_sshot_report_time = (now / 60) * 60 + 60;
361 time_t next_purge_check_time = now + s_purge_check_interval;
362 time_t next_purge_report_time = now + s_purge_report_interval;
363 time_t next_purge_cold_files_time = now + s_purge_cold_files_interval;
365 long long n_reported_token_failures = 0;
369 time_t start = time(0);
370 time_t next_event = std::min({ next_queue_proc_time, next_sshot_report_time,
371 next_purge_check_time, next_purge_report_time, next_purge_cold_files_time });
373 if (next_event > start)
375 unsigned int t_sleep = next_event - start;
376 TRACE(Dump, tpfx <<
"sleeping for " << t_sleep <<
" seconds until the next beat.");
391 time_t queue_swap_time = time(0);
395 next_queue_proc_time = queue_swap_time + s_queue_proc_interval;
396 TRACE(Dump, tpfx <<
"process_queues -- n_records=" << n_processed);
401 if (taf > n_reported_token_failures) {
402 TRACE(
Error, tpfx <<
"access-token space exhausted "
403 << taf - n_reported_token_failures <<
" time(s) (total " << taf
404 <<
"); those files are open but not accounted for. Raise MaxChunks "
405 "on ResourceMonitor::m_access_tokens.");
406 n_reported_token_failures = taf;
417 bool do_sshot_report = next_sshot_report_time <= now;
418 bool do_purge_check = next_purge_check_time <= now;
419 bool do_purge_report = next_purge_report_time <= now;
420 bool do_purge_cold_files = next_purge_cold_files_time <= now;
423 if (do_sshot_report || do_purge_check || do_purge_report || do_purge_cold_files)
425 unlink_func unlink_foo = [&](
const std::string &dp)->
int {
426 int ret = m_oss.Unlink(dp.c_str());
428 TRACE(
Info, tpfx <<
"Empty dir unlink error: " << ret <<
" at " << dp);
430 TRACE(
Debug, tpfx <<
"Empty dir unlink success: " << dp);
437 m_fs_state.update_stats_and_usages(queue_swap_time, purge_leaf_dirs, unlink_foo);
445 const DirState &root_ds = *m_fs_state.get_root();
449 m_fs_state.dump_recursively(store_depth);
452 m_fs_state.reset_stats(queue_swap_time);
458 next_sshot_report_time = ((now + 1) / s_sshot_report_interval) * s_sshot_report_interval + s_sshot_report_interval;
463 DataFsSnapshot ss(m_fs_state, m_fs_state.m_sshot_stats_reset_time);
464 const DirState &root_ds = *m_fs_state.get_root();
474 const char* dumpfile =
"/pfc-stats/DirStat.json";
476 m_fs_state.reset_sshot_stats(queue_swap_time);
479 if (do_purge_check || do_purge_report || do_purge_cold_files)
483 next_purge_check_time = now + s_purge_check_interval;
484 if (do_purge_report) next_purge_report_time = now + s_purge_report_interval;
485 if (do_purge_cold_files) next_purge_cold_files_time = now + s_purge_cold_files_interval;
497 std::vector<DirStateElement> &vec,
500 const int pos0 = (int) vec.size();
501 const int n_children = (int) parent_ds.
m_subdirs.size();
504 if (parent_ds.
m_depth >= max_depth)
507 if (n_children == 0) {
520 if (n_children == 0)
return;
537 std::vector<DirPurgeElement> &vec,
540 const int pos0 = (int) vec.size();
541 const int n_children = (int) parent_ds.
m_subdirs.size();
544 if (parent_ds.
m_depth >= max_depth)
547 if (n_children == 0) {
560 if (n_children == 0)
return;
581 static const char *trc_pfx =
"update_vs_and_file_usage_info() ";
587 if (m_oss.StatVS(&vsi, conf.m_data_space.c_str(), 1) < 0) {
588 TRACE(
Error, trc_pfx <<
"can't get StatVS for oss space '" << conf.m_data_space <<
"'. This is a fatal error.");
591 m_fs_state.m_disk_total = vsi.
Total;
592 m_fs_state.m_disk_used = vsi.
Total - vsi.
Free;
593 m_fs_state.m_file_usage = 512ll * m_current_usage_in_st_blocks;
594 if (m_oss.StatVS(&vsi, conf.m_meta_space.c_str(), 1) < 0) {
595 TRACE(
Error, trc_pfx <<
"can't get StatVS for oss space '" << conf.m_meta_space <<
"'. This is a fatal error.");
598 m_fs_state.m_meta_total = vsi.
Total;
599 m_fs_state.m_meta_used = vsi.
Total - vsi.
Free;
602long long ResourceMonitor::get_file_usage_bytes_to_remove(
const DataFsPurgeshot &ps,
long long write_estimate,
int tl)
618 long long delta = write_estimate;
619 TRACE_INT(tl,
"file usage increased since the previous purge interval in bytes: " << delta );
621 long long bytes_to_remove = 0;
624 auto clamp = [&x, &bytes_to_remove](
long long lowval,
long long highval)
627 long long newval = val - bytes_to_remove;
636 if (newval > highval)
638 return val - highval;
641 return bytes_to_remove;
652 float frac_u =
static_cast<float>(u - w2) / (T - w2);
653 float frac_x =
static_cast<float>(x - f0) / (f1 - f0);
657 bytes_to_remove = u -w1;
664 bytes_to_remove = (frac_x - frac_u) * (f1 - f0);
665 bytes_to_remove += delta;
666 bytes_to_remove = clamp(f0, f1);
671 bytes_to_remove = clamp(f0, f2);
673 return bytes_to_remove;
679 if (u > w1 && x > f1)
681 float frac_u =
static_cast<float>(u - w1) / (w2 - w1);
682 float frac_x =
static_cast<float>(x - f1) / (f2 - f1);
685 TRACE_INT(tl,
"Disproportional file quota usage comapared to disc usage (frac_x/frac_u) = " << frac_x <<
"/"<< frac_u);
686 bytes_to_remove = (frac_x - frac_u) * (f2 - f1);
687 bytes_to_remove += delta;
691 bytes_to_remove = clamp(f0, f2);
692 return bytes_to_remove;
702 TRACE_INT(tl,
"File usage exceeds maxim file usage. Total disk usage is under lowWatermark. Clearing to low file usage.");
703 long long f2delta = std::max(f2 - delta, f0);
704 bytes_to_remove = clamp(f0, f2delta);
705 return bytes_to_remove;
708 return bytes_to_remove;
713 static const char *trc_pfx =
"perform_purge_check() ";
716 std::unique_ptr<DataFsPurgeshot> psp(
new DataFsPurgeshot(m_fs_state) );
724 TRACE_INT(tl, trc_pfx <<
"Purge check:");
758 TRACE(
Info, trc_pfx <<
"purge not required.");
763 TRACE(Warning, trc_pfx <<
"purge required but previous purge task is still active!");
767 TRACE(
Info, trc_pfx <<
"scheduling purge task.");
779 const DirState &root_ds = *m_fs_state.get_root();
787 dprintf(
"purge dir count recursive=%d vs from_usage=%d\n", n_pshot_dirs, n_calc_dirs);
788 const int discrepancy = n_calc_dirs - n_pshot_dirs;
789 if (discrepancy != m_dir_count_discrepancy)
791 if (discrepancy != 0) {
792 TRACE(
Error, trc_pfx <<
"directory count mismatch: usage counters " << n_calc_dirs
793 <<
", tree " << n_pshot_dirs <<
", previous difference " << m_dir_count_discrepancy);
795 TRACE(
Info, trc_pfx <<
"directory count consistent again: " << n_pshot_dirs);
797 m_dir_count_discrepancy = discrepancy;
806 struct PurgeDriverJob :
public XrdJob
811 XrdJob(
"XrdPfc::ResourceMonitor::PurgeDriver"),
812 m_purge_shot_ptr(psp)
820 delete m_purge_shot_ptr;
825 Cache::schedP->Schedule(
new PurgeDriverJob(psp.release()) );
874 m_dir_scan_in_progress =
true;
879 const char *tpfx =
"main_thread_function ";
881 time_t is_start = time(0);
882 m_fs_state.init_stat_reset_times(is_start);
883 TRACE(
Info, tpfx <<
"Stating initial directory scan.");
886 TRACE(
Error, tpfx <<
"Initial directory scan has failed. This is a terminal error, aborting.")
891 time_t is_duration = time(0) - is_start;
892 TRACE(
Info, tpfx <<
"Initial directory scan complete, duration=" << is_duration <<
"s");
896 TRACE(
Info, tpfx <<
"First process_queues finished, n_records=" << n_proc_is);
899 if (is_duration > 30 || n_proc_is > 3000)
901 m_file_open_q.shrink_read_queue();
902 m_file_update_stats_q.shrink_read_queue();
903 m_file_close_q.shrink_read_queue();
904 m_file_purge_q1.shrink_read_queue();
905 m_file_purge_q2.shrink_read_queue();
906 m_file_purge_q3.shrink_read_queue();
952 time_t heartbeat_start = time(0);
996 int heartbeat_duration = time(0) - heartbeat_start;
1001 int sleep_time = 60 - heartbeat_duration;
int DoIt(int argpnt, int argc, char **argv, bool singleshot)
void Proto_ResourceMonitorHeartBeat()
#define TRACE_INT(act, x)
virtual int Opendir(const char *path, XrdOucEnv &env)
struct XrdOucCacheStats::CacheStats X
XrdOucCacheStats Statistics
static const Configuration & Conf()
static ResourceMonitor & ResMon()
void ClearPurgeProtectedSet()
static Cache & GetInstance()
Singleton access.
XrdSysTrace * GetTrace() const
static XrdScheduler * schedP
long long WritesSinceLastCall()
PurgePin * GetPurgePin() const
void AddUp(const DirStats &s)
long long m_StBlocksRemoved
std::vector< std::string > m_current_dirs
std::string m_current_path
void slurp_dir_ll(XrdOssDF &dh, int dir_level, const char *path, const char *trc_pfx)
bool begin_traversal(DirState *root, const char *root_path)
std::set< std::string > m_protected_top_dirs
bool cd_down(const std::string &dir_name)
XrdOucEnv & default_env()
std::map< std::string, FilePairStat > m_current_files
Status of cached file. Can be read from and written into a binary file.
virtual bool CallPeriodically()
bool m_purge_task_complete
AccessToken & token(int i)
void CrossCheckIfScanIsInProgress(const std::string &lfn, XrdSysCondVar &cond)
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)
ResourceMonitor(XrdOss &oss)
void perform_purge_task(DataFsPurgeshot &ps)
void scan_dir_and_recurse(FsTraversal &fst)
void fill_pshot_vec_children(const DirState &parent_ds, int parent_idx, std::vector< DirPurgeElement > &vec, int max_depth)
XrdSysCondVar m_purge_task_cond
void perform_purge_task_cleanup()
void update_vs_and_file_usage_info()
bool perform_initial_scan()
time_t m_purge_task_start
void main_thread_function()
std::function< int(const std::string &)> unlink_func
void OldStylePurgeDriver(DataFsPurgeshot &ps)
void note_file_open(DirState *node, DirState *last_existing, bool existing_file)
Contains parameters configurable from the xrootd config file.
long long m_RamAbsAvailable
available from configuration
long long m_diskTotalSpace
total disk space on configured partition or oss space
long long m_fileUsageMax
cache purge - files usage maximum
long long m_fileUsageBaseline
cache purge - files usage baseline
int m_dirStatsStoreDepth
maximum depth for statistics write out
long long m_diskUsageHWM
cache purge - disk usage high water mark
bool are_file_usage_limits_set() const
long long m_fileUsageNominal
cache purge - files usage nominal
int m_purgeAgeBasedPeriod
peform cold file / uvkeep purge every this many purge cycles
long long m_diskUsageLWM
cache purge - disk usage low water mark
int m_dirStatsInterval
time between resource monitor statistics dump in seconds
bool is_age_based_purge_in_effect() const
int m_purgeInterval
sleep interval between cache purges
bool is_dir_stat_reporting_on() const
std::vector< DirPurgeElement > m_dir_vec
long long m_bytes_to_remove
long long m_estimated_writes_from_writeq
void write_json_file(const std::string &fname, XrdOss &oss, bool include_preamble)
std::vector< DirStateElement > m_dir_states
DirUsage m_recursive_subdir_usage
int count_dirs_to_level(int max_depth) const
DirState * find_path(const std::string &path, int max_depth, bool parse_as_lfn, bool create_subdirs, DirState **last_existing_dir=nullptr)
void upward_propagate_initial_scan_usages()