XRootD
XrdPfc::ResourceMonitor Class Reference

#include <XrdPfcResourceMonitor.hh>

+ Collaboration diagram for XrdPfc::ResourceMonitor:

Public Member Functions

 ResourceMonitor (XrdOss &oss)
 
 ~ResourceMonitor ()
 
void CrossCheckIfScanIsInProgress (const std::string &lfn, XrdSysCondVar &cond)
 
void fill_pshot_vec_children (const DirState &parent_ds, int parent_idx, std::vector< DirPurgeElement > &vec, int max_depth)
 
void fill_sshot_vec_children (const DirState &parent_ds, int parent_idx, std::vector< DirStateElement > &vec, int max_depth)
 
void heart_beat ()
 
void init_before_main ()
 
void main_thread_function ()
 
bool perform_initial_scan ()
 
void perform_purge_check (bool purge_cold_files, int tl)
 
void perform_purge_task (DataFsPurgeshot &ps)
 
void perform_purge_task_cleanup ()
 
int process_queues ()
 
void register_file_close (int token_id, time_t close_timestamp, const Stats &full_stats)
 
int register_file_open (const std::string &filename, time_t open_timestamp, bool existing_file)
 
void register_file_purge (const std::string &filename, long long size_in_st_blocks)
 
void register_file_purge (DirState *target, long long size_in_st_blocks)
 
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_multi_file_purge (DirState *target, long long size_in_st_blocks, int n_files)
 
void scan_dir_and_recurse (FsTraversal &fst)
 
AccessToken & token (int i)
 
void update_vs_and_file_usage_info ()
 

Public Attributes

bool m_purge_task_active {false}
 
bool m_purge_task_complete {false}
 
XrdSysCondVar m_purge_task_cond {0}
 
time_t m_purge_task_end {0}
 
time_t m_purge_task_start {0}
 

Detailed Description

Definition at line 38 of file XrdPfcResourceMonitor.hh.

Constructor & Destructor Documentation

◆ ResourceMonitor()

ResourceMonitor::ResourceMonitor ( XrdOss &  oss)

Definition at line 32 of file XrdPfcResourceMonitor.cc.

32  :
33  m_fs_state(* new DataFsState),
34  m_oss(oss)
35 {}

◆ ~ResourceMonitor()

ResourceMonitor::~ResourceMonitor ( )

Definition at line 37 of file XrdPfcResourceMonitor.cc.

38 {
39  delete &m_fs_state;
40 }

Member Function Documentation

◆ CrossCheckIfScanIsInProgress()

void ResourceMonitor::CrossCheckIfScanIsInProgress ( const std::string &  lfn,
XrdSysCondVar &  cond 
)

Definition at line 46 of file XrdPfcResourceMonitor.cc.

47 {
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});
52  cond.Lock();
53  m_dir_scan_mutex.UnLock();
54  while ( ! dir_checked)
55  cond.Wait();
56  cond.UnLock();
57  } else {
58  m_dir_scan_mutex.UnLock();
59  }
60 }

References XrdSysCondVar::Lock(), XrdSysMutex::Lock(), XrdSysCondVar::UnLock(), XrdSysMutex::UnLock(), and XrdSysCondVar::Wait().

+ Here is the call graph for this function:

◆ fill_pshot_vec_children()

void ResourceMonitor::fill_pshot_vec_children ( const DirState &  parent_ds,
int  parent_idx,
std::vector< DirPurgeElement > &  vec,
int  max_depth 
)

Definition at line 535 of file XrdPfcResourceMonitor.cc.

539 {
540  const int pos0 = (int) vec.size();
541  const int n_children = (int) parent_ds.m_subdirs.size();
542 
543  // maxdepth 0 is the root alone: a node at max_depth is emitted, its children are not.
544  if (parent_ds.m_depth >= max_depth)
545  {
546  // Truncated here: keep the daughter range at (-1, -1). No children: empty range.
547  if (n_children == 0) {
548  DirPurgeElement &dse = vec[parent_idx];
549  dse.m_daughters_begin = dse.m_daughters_end = pos0;
550  }
551  return;
552  }
553 
554  { // scoped: the emplace_back loop below can reallocate vec
555  DirPurgeElement &parent_dse = vec[parent_idx];
556  parent_dse.m_daughters_begin = pos0;
557  parent_dse.m_daughters_end = pos0 + n_children;
558  }
559 
560  if (n_children == 0) return;
561 
562  for (auto const & [name, child] : parent_ds.m_subdirs)
563  {
564  vec.emplace_back( DirPurgeElement(child, child.m_here_usage, child.m_recursive_subdir_usage, parent_idx) );
565  }
566 
567  int pos = pos0;
568  for (auto const & [name, child] : parent_ds.m_subdirs)
569  {
570  fill_pshot_vec_children(child, pos, vec, max_depth);
571  ++pos;
572  }
573 }
static void child()
void fill_pshot_vec_children(const DirState &parent_ds, int parent_idx, std::vector< DirPurgeElement > &vec, int max_depth)

References child(), XrdPfc::DirPurgeElement::m_daughters_begin, XrdPfc::DirPurgeElement::m_daughters_end, XrdPfc::DirState::m_depth, and XrdPfc::DirState::m_subdirs.

Referenced by perform_purge_check().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ fill_sshot_vec_children()

void ResourceMonitor::fill_sshot_vec_children ( const DirState &  parent_ds,
int  parent_idx,
std::vector< DirStateElement > &  vec,
int  max_depth 
)

Definition at line 495 of file XrdPfcResourceMonitor.cc.

499 {
500  const int pos0 = (int) vec.size();
501  const int n_children = (int) parent_ds.m_subdirs.size();
502 
503  // maxdepth 0 is the root alone: a node at max_depth is emitted, its children are not.
504  if (parent_ds.m_depth >= max_depth)
505  {
506  // Truncated here: keep the daughter range at (-1, -1). No children: empty range.
507  if (n_children == 0) {
508  DirStateElement &dse = vec[parent_idx];
509  dse.m_daughters_begin = dse.m_daughters_end = pos0;
510  }
511  return;
512  }
513 
514  { // scoped: the emplace_back loop below can reallocate vec
515  DirStateElement &parent_dse = vec[parent_idx];
516  parent_dse.m_daughters_begin = pos0;
517  parent_dse.m_daughters_end = pos0 + n_children;
518  }
519 
520  if (n_children == 0) return;
521 
522  for (auto const & [name, child] : parent_ds.m_subdirs)
523  {
524  vec.emplace_back( DirStateElement(child, parent_idx) );
525  }
526 
527  int pos = pos0;
528  for (auto const & [name, child] : parent_ds.m_subdirs)
529  {
530  fill_sshot_vec_children(child, pos, vec, max_depth);
531  ++pos;
532  }
533 }
void fill_sshot_vec_children(const DirState &parent_ds, int parent_idx, std::vector< DirStateElement > &vec, int max_depth)

References child(), XrdPfc::DirStateElement::m_daughters_begin, XrdPfc::DirStateElement::m_daughters_end, XrdPfc::DirState::m_depth, and XrdPfc::DirState::m_subdirs.

Referenced by heart_beat().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ heart_beat()

void ResourceMonitor::heart_beat ( )

Definition at line 343 of file XrdPfcResourceMonitor.cc.

344 {
345  static const char *tpfx = "heart_beat() ";
346 
347  const Configuration &conf = Cache::Conf();
348 
349  const int s_queue_proc_interval = 10;
350  const int s_sshot_report_interval = conf.m_dirStatsInterval; // one of 60, 120, 300,
351  // 600, 900, 1200, 1800, 3600 s
352  const int s_purge_check_interval = 60;
353  const int s_purge_report_interval = conf.m_purgeInterval;
354  const int s_purge_cold_files_interval = conf.m_purgeInterval * conf.m_purgeAgeBasedPeriod;
355 
356  // initial scan performed as part of config
357 
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; // at next full minute
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;
364 
365  long long n_reported_token_failures = 0;
366 
367  while (true)
368  {
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 });
372 
373  if (next_event > start)
374  {
375  unsigned int t_sleep = next_event - start;
376  TRACE(Dump, tpfx << "sleeping for " << t_sleep << " seconds until the next beat.");
377  sleep(t_sleep);
378  }
379 
380  // Check if purge has been running and has completed yet.
381  // For now this is only used to prevent removal of empty leaf directories
382  // during stat propagation so we do not need to wait for the condition in
383  // the above sleep.
384  if (m_purge_task_active) {
386  if (m_purge_task_complete) {
388  }
389  }
390 
391  time_t queue_swap_time = time(0);
392 
393  // Always process the queues.
394  int n_processed = process_queues();
395  next_queue_proc_time = queue_swap_time + s_queue_proc_interval;
396  TRACE(Dump, tpfx << "process_queues -- n_records=" << n_processed);
397 
398  { // Access-token space exhausted -- should be unreachable, so say so loudly.
399  long long taf;
400  { XrdSysMutexHelper _lock(&m_queue_mutex); taf = m_token_alloc_failures; }
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;
407  }
408  }
409 
410  // Always update basic info on m_fs_state (space, usage, file_usage).
412 
413  now = time(0);
414 
415  // Make planning for fs_state_update, sshot dump and purge task.
416  // Second two require the first, so figure out what is going to happen.
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;
421 
422  // Update stats in usages if any secondary activity will happen.
423  if (do_sshot_report || do_purge_check || do_purge_report || do_purge_cold_files)
424  {
425  unlink_func unlink_foo = [&](const std::string &dp)->int {
426  int ret = m_oss.Unlink(dp.c_str());
427  if (ret != 0) {
428  TRACE(Info, tpfx << "Empty dir unlink error: " << ret << " at " << dp);
429  } else {
430  TRACE(Debug, tpfx << "Empty dir unlink success: " << dp);
431  }
432  return ret;
433  };
434 
435  // Potentially prune the empty leaf dirs even less frequently, once per hour, maybe?
436  bool purge_leaf_dirs = do_sshot_report && ! m_purge_task_active;
437  m_fs_state.update_stats_and_usages(queue_swap_time, purge_leaf_dirs, unlink_foo);
438 
439  // This reporting into log/stdout is to be removed.
440  // Meaning of conf.is_dir_stat_reporting_on() etc is to be clarified / improved.
441  if (do_sshot_report && conf.is_dir_stat_reporting_on())
442  {
443  const int store_depth = conf.m_dirStatsStoreDepth;
444  #ifdef RM_DEBUG
445  const DirState &root_ds = *m_fs_state.get_root();
446  dprintf("Snapshot n_dirs=%d, total n_dirs=%d\n", root_ds.count_dirs_to_level(store_depth),
448  #endif
449  m_fs_state.dump_recursively(store_depth);
450  }
451 
452  m_fs_state.reset_stats(queue_swap_time);
453  }
454 
455  if (do_sshot_report)
456  {
457  // Sshot reports are equidistant, at "full" reporting interval.
458  next_sshot_report_time = ((now + 1) / s_sshot_report_interval) * s_sshot_report_interval + s_sshot_report_interval;
459 
460  // This should dump out binary snapshot into /pfc-stats/, if so configured.
461 
462  // json dump to std::out for debug purpose
463  DataFsSnapshot ss(m_fs_state, m_fs_state.m_sshot_stats_reset_time);
464  const DirState &root_ds = *m_fs_state.get_root();
465  const int store_depth = conf.m_dirStatsStoreDepth;
466  const int n_sshot_dirs = root_ds.count_dirs_to_level(store_depth);
467  ss.m_dir_states.reserve(n_sshot_dirs);
468  ss.m_dir_states.emplace_back( DirStateElement(root_ds, -1) );
469  fill_sshot_vec_children(root_ds, 0, ss.m_dir_states, store_depth);
470 
471  // This should really be export to a file (preferably binary, but then bin->json command is needed, too).
472  // ss.dump();
473 
474  const char* dumpfile = "/pfc-stats/DirStat.json";
475  ss.write_json_file(dumpfile, m_oss, false);
476  m_fs_state.reset_sshot_stats(queue_swap_time);
477  }
478 
479  if (do_purge_check || do_purge_report || do_purge_cold_files)
480  {
481  perform_purge_check(do_purge_cold_files, do_purge_report ? TRACE_Info : TRACE_Debug);
482 
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;
486  }
487 
488  } // end while forever
489 }
#define TRACE_Debug
Definition: XrdCmsTrace.hh:37
#define TRACE_Info
#define dprintf(...)
bool Debug
@ Error
#define TRACE(act, x)
Definition: XrdTrace.hh:63
virtual int Unlink(const char *path, int Opts=0, XrdOucEnv *envP=0)=0
static const Configuration & Conf()
Definition: XrdPfc.cc:134
Status of cached file. Can be read from and written into a binary file.
Definition: XrdPfcInfo.hh:41
void perform_purge_check(bool purge_cold_files, int tl)
std::function< int(const std::string &)> unlink_func
Contains parameters configurable from the xrootd config file.
Definition: XrdPfc.hh:64
int m_dirStatsStoreDepth
maximum depth for statistics write out
Definition: XrdPfc.hh:105
int m_purgeAgeBasedPeriod
peform cold file / uvkeep purge every this many purge cycles
Definition: XrdPfc.hh:99
int m_dirStatsInterval
time between resource monitor statistics dump in seconds
Definition: XrdPfc.hh:104
int m_purgeInterval
sleep interval between cache purges
Definition: XrdPfc.hh:97
bool is_dir_stat_reporting_on() const
Definition: XrdPfc.hh:70
void reset_stats(time_t last_update)
void dump_recursively(int max_depth) const
void update_stats_and_usages(time_t last_update, bool purge_empty_dirs, unlink_func unlink_foo)
void reset_sshot_stats(time_t last_update)
DirUsage m_recursive_subdir_usage
int count_dirs_to_level(int max_depth) const

References XrdPfc::Cache::Conf(), XrdPfc::DirState::count_dirs_to_level(), Debug, dprintf, XrdPfc::DataFsState::dump_recursively(), Error, fill_sshot_vec_children(), XrdPfc::DataFsState::get_root(), XrdPfc::Configuration::is_dir_stat_reporting_on(), XrdPfc::DataFsSnapshot::m_dir_states, XrdPfc::Configuration::m_dirStatsInterval, XrdPfc::Configuration::m_dirStatsStoreDepth, XrdPfc::DirState::m_here_usage, XrdPfc::DirUsage::m_NDirectories, m_purge_task_active, m_purge_task_complete, m_purge_task_cond, XrdPfc::Configuration::m_purgeAgeBasedPeriod, XrdPfc::Configuration::m_purgeInterval, XrdPfc::DirState::m_recursive_subdir_usage, XrdPfc::DataFsState::m_sshot_stats_reset_time, perform_purge_check(), process_queues(), XrdPfc::DataFsState::reset_sshot_stats(), XrdPfc::DataFsState::reset_stats(), TRACE, TRACE_Debug, TRACE_Info, XrdOss::Unlink(), XrdPfc::DataFsState::update_stats_and_usages(), update_vs_and_file_usage_info(), and XrdPfc::DataFsSnapshot::write_json_file().

Referenced by main_thread_function().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ init_before_main()

void ResourceMonitor::init_before_main ( )

Definition at line 870 of file XrdPfcResourceMonitor.cc.

871 {
872  // setup for in-scan -- this is called from initial setup.
873  MutexHolder _lck(m_dir_scan_mutex);
874  m_dir_scan_in_progress = true;
875 }

Referenced by XrdPfc::Cache::Config().

+ Here is the caller graph for this function:

◆ main_thread_function()

void ResourceMonitor::main_thread_function ( )

Definition at line 877 of file XrdPfcResourceMonitor.cc.

878 {
879  const char *tpfx = "main_thread_function ";
880  {
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.");
884 
885  if ( ! perform_initial_scan()) {
886  TRACE(Error, tpfx << "Initial directory scan has failed. This is a terminal error, aborting.")
887  _exit(1);
888  }
889  // Reset of m_dir_scan_in_progress is done in perform_initial_scan()
890 
891  time_t is_duration = time(0) - is_start;
892  TRACE(Info, tpfx << "Initial directory scan complete, duration=" << is_duration <<"s");
893 
894  // run first process queues
895  int n_proc_is = process_queues();
896  TRACE(Info, tpfx << "First process_queues finished, n_records=" << n_proc_is);
897 
898  // shrink queues if scan time was longer than 30s.
899  if (is_duration > 30 || n_proc_is > 3000)
900  {
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();
907  }
908  }
909  heart_beat();
910 }
if(Avsz)
void init_stat_reset_times(time_t t)

References Error, heart_beat(), XrdPfc::DataFsState::init_stat_reset_times(), perform_initial_scan(), process_queues(), and TRACE.

Referenced by ResourceMonitorThread().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ perform_initial_scan()

bool ResourceMonitor::perform_initial_scan ( )

Definition at line 174 of file XrdPfcResourceMonitor.cc.

175 {
176  // Called after PFC configuration is complete, but before full startup of the daemon.
177  // Base line usages are accumulated as part of the file-system, traversal.
178 
180 
181  DirState *root_ds = m_fs_state.get_root();
182  FsTraversal fst(m_oss);
183  fst.m_protected_top_dirs.insert("pfc-stats"); // XXXX This should come from config. Also: N2N?
184 
185  if ( ! fst.begin_traversal(root_ds, "/"))
186  return false;
187 
188  // The following are initialized in ResourceMonitor.hh to avoid a race at startup:
189  // m_dir_scan_in_progress = true;
190  // m_dir_scan_check_counter = 0;
191 
193 
194  fst.end_traversal();
195 
196  // We have all directories scanned, available in DirState tree, let all remaining files go
197  // and then we shall do the upward propagation of usages.
198  {
199  XrdSysMutexHelper _lock(m_dir_scan_mutex);
200  m_dir_scan_in_progress = false;
201  m_dir_scan_check_counter = 0;
202  }
203  // m_dir_scan_open_requests should now be final, ie, no new entries will be added.
204  while ( ! m_dir_scan_open_requests.empty())
205  {
206  LfnCondRecord &lcr = m_dir_scan_open_requests.front();
207  lcr.f_cond.Lock();
208  lcr.f_checked = true;
209  lcr.f_cond.Signal();
210  lcr.f_cond.UnLock();
211 
212  m_dir_scan_open_requests.pop_front();
213  }
214 
215  // Do upward propagation of usages.
217  m_current_usage_in_st_blocks = root_ds->m_here_usage.m_StBlocks +
220 
221  return true;
222 }
void scan_dir_and_recurse(FsTraversal &fst)
void upward_propagate_initial_scan_usages()

References XrdPfc::FsTraversal::begin_traversal(), XrdPfc::FsTraversal::end_traversal(), XrdPfc::DataFsState::get_root(), XrdPfc::DirState::m_here_usage, XrdPfc::FsTraversal::m_protected_top_dirs, XrdPfc::DirState::m_recursive_subdir_usage, XrdPfc::DirUsage::m_StBlocks, scan_dir_and_recurse(), update_vs_and_file_usage_info(), and XrdPfc::DirState::upward_propagate_initial_scan_usages().

Referenced by main_thread_function().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ perform_purge_check()

void ResourceMonitor::perform_purge_check ( bool  purge_cold_files,
int  tl 
)

Definition at line 711 of file XrdPfcResourceMonitor.cc.

712 {
713  static const char *trc_pfx = "perform_purge_check() ";
714  const Configuration &conf = Cache::Conf();
715 
716  std::unique_ptr<DataFsPurgeshot> psp( new DataFsPurgeshot(m_fs_state) );
717  DataFsPurgeshot &ps = *psp;
718 
719  ps.m_file_usage = 512ll * m_current_usage_in_st_blocks;
720  // These are potentially wrong as cache might be writing over preallocated byte ranges.
722  // Can have another estimate based on eiter writes or st-blocks from purge-stats, once we have them.
723 
724  TRACE_INT(tl, trc_pfx << "Purge check:");
725 
726  ps.m_bytes_to_remove = 0;
727  if (conf.are_file_usage_limits_set())
728  {
729  ps.m_bytes_to_remove = get_file_usage_bytes_to_remove(ps, ps.m_estimated_writes_from_writeq, tl);
730  }
731  else
732  {
733  if (ps.m_disk_used > conf.m_diskUsageHWM)
734  {
735  TRACE_INT(tl, "Disk usage: " << ps.m_disk_used << " exceed highWatermark.");
737  }
738  }
739 
740  ps.m_space_based_purge = ps.m_bytes_to_remove ? 1 : 0;
741 
742  // Purge precheck -- check if age-based purge is required
743  // We ignore uvkeep time, it requires reading of cinfo files and it is enforced in File::Open() anyway.
744 
745  if (purge_cold_files && conf.is_age_based_purge_in_effect()) // || conf.is_uvkeep_purge_in_effect())
746  {
747  ps.m_age_based_purge = true;
748  }
749 
750  TRACE_INT(tl, "\tbytes_to_remove = " << ps.m_bytes_to_remove << " B");
751  TRACE_INT(tl, "\tspace_based_purge = " << ps.m_space_based_purge);
752  TRACE_INT(tl, "\tage_based_purge = " << ps.m_age_based_purge);
753 
754  bool periodic = Cache::GetInstance().GetPurgePin() ?
756 
757  if ( ! ps.m_space_based_purge && ! ps.m_age_based_purge && !periodic ) {
758  TRACE(Info, trc_pfx << "purge not required.");
760  return;
761  }
762  if (m_purge_task_active) {
763  TRACE(Warning, trc_pfx << "purge required but previous purge task is still active!");
764  return;
765  }
766 
767  TRACE(Info, trc_pfx << "scheduling purge task.");
768 
769  // At this point we have all the information: report, decide on action.
770  // There is still some missing infrastructure, especially as regards to purge-plugin:
771  // - at what point do we start bugging the pu-pin to start coughing up purge lists?
772  // - have a new parameter or just do it "one cycle before full"?
773  // - what if it doesn't -- when do we do the old-stlye scan & purge?
774  // - how do we do age-based purge and uvkeep purge?
775  // - they are really quite different -- and could run separately, registering
776  // files into a purge-candidate list. This has to be rechecked before the actual
777  // deletion -- eg, by comparing stat time of cinfo + doing the is-active / is-purge-protected.
778 
779  const DirState &root_ds = *m_fs_state.get_root();
780 
781  // Size from the tree itself; the usage counters can be off (#2808).
782  const int n_pshot_dirs = root_ds.count_dirs_to_level(9999);
783 
784  // Report a counters-vs-tree mismatch when it appears or changes.
785  const int n_calc_dirs = 1 + root_ds.m_here_usage.m_NDirectories
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)
790  {
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);
794  } else {
795  TRACE(Info, trc_pfx << "directory count consistent again: " << n_pshot_dirs);
796  }
797  m_dir_count_discrepancy = discrepancy;
798  }
799 
800  ps.m_dir_vec.reserve(n_pshot_dirs);
801  ps.m_dir_vec.emplace_back( DirPurgeElement(root_ds, root_ds.m_here_usage, root_ds.m_recursive_subdir_usage, -1) );
802  fill_pshot_vec_children(root_ds, 0, ps.m_dir_vec, 9999);
803 
804  m_purge_task_active = true;
805 
806  struct PurgeDriverJob : public XrdJob
807  {
808  DataFsPurgeshot *m_purge_shot_ptr;
809 
810  PurgeDriverJob(DataFsPurgeshot *psp) :
811  XrdJob("XrdPfc::ResourceMonitor::PurgeDriver"),
812  m_purge_shot_ptr(psp)
813  {}
814 
815  void DoIt() override
816  {
817  Cache::ResMon().perform_purge_task(*m_purge_shot_ptr);
819 
820  delete m_purge_shot_ptr;
821  delete this;
822  }
823  };
824 
825  Cache::schedP->Schedule( new PurgeDriverJob(psp.release()) );
826 }
int DoIt(int argpnt, int argc, char **argv, bool singleshot)
Definition: XrdAccTest.cc:262
#define TRACE_INT(act, x)
Definition: XrdPfcTrace.hh:52
Definition: XrdJob.hh:43
PurgePin * GetPurgePin() const
Definition: XrdPfc.hh:284
static ResourceMonitor & ResMon()
Definition: XrdPfc.cc:135
void ClearPurgeProtectedSet()
Definition: XrdPfc.cc:687
static Cache & GetInstance()
Singleton access.
Definition: XrdPfc.cc:132
static XrdScheduler * schedP
Definition: XrdPfc.hh:302
long long WritesSinceLastCall()
Definition: XrdPfc.cc:320
virtual bool CallPeriodically()
void perform_purge_task(DataFsPurgeshot &ps)
void Schedule(XrdJob *jp)
@ Warning
long long m_diskUsageHWM
cache purge - disk usage high water mark
Definition: XrdPfc.hh:93
bool are_file_usage_limits_set() const
Definition: XrdPfc.hh:67
long long m_diskUsageLWM
cache purge - disk usage low water mark
Definition: XrdPfc.hh:92
bool is_age_based_purge_in_effect() const
Definition: XrdPfc.hh:68
std::vector< DirPurgeElement > m_dir_vec

References XrdPfc::Configuration::are_file_usage_limits_set(), XrdPfc::PurgePin::CallPeriodically(), XrdPfc::Cache::ClearPurgeProtectedSet(), XrdPfc::Cache::Conf(), XrdPfc::DirState::count_dirs_to_level(), DoIt(), dprintf, Error, fill_pshot_vec_children(), XrdPfc::DataFsState::get_root(), XrdPfc::Cache::GetInstance(), XrdPfc::Cache::GetPurgePin(), XrdPfc::Configuration::is_age_based_purge_in_effect(), XrdPfc::DataFsPurgeshot::m_age_based_purge, XrdPfc::DataFsPurgeshot::m_bytes_to_remove, XrdPfc::DataFsPurgeshot::m_dir_vec, XrdPfc::DataFsStateBase::m_disk_used, XrdPfc::Configuration::m_diskUsageHWM, XrdPfc::Configuration::m_diskUsageLWM, XrdPfc::DataFsPurgeshot::m_estimated_writes_from_writeq, XrdPfc::DataFsStateBase::m_file_usage, XrdPfc::DirState::m_here_usage, XrdPfc::DirUsage::m_NDirectories, m_purge_task_active, XrdPfc::DirState::m_recursive_subdir_usage, XrdPfc::DataFsPurgeshot::m_space_based_purge, perform_purge_task(), perform_purge_task_cleanup(), XrdPfc::Cache::ResMon(), XrdPfc::Cache::schedP, XrdScheduler::Schedule(), TRACE, TRACE_INT, TPC::Warning, and XrdPfc::Cache::WritesSinceLastCall().

Referenced by heart_beat().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ perform_purge_task()

void ResourceMonitor::perform_purge_task ( DataFsPurgeshot &  ps)

Definition at line 833 of file XrdPfcResourceMonitor.cc.

834 {
835  // BEWARE: Runs in a dedicated thread - is only to communicate back to the
836  // hear_beat() / data structs via the purge queues and condition variable.
837 
838  // const char *tpfx = "perform_purge_task ";
839 
840  {
842  m_purge_task_start = time(0);
843  }
844 
845  // For now, fall back to the old purge ... to be improved with:
846  // - new scan, following the DataFsPurgeshot;
847  // - usage of cinfo stat mtime for time of last access (touch already done at output);
848  // - use DirState* to report back purged files.
849  // Already changed to report back purged files --- but using the string / path variant.
850  OldStylePurgeDriver(ps); // In XrdPfcPurge.cc
851 }
void OldStylePurgeDriver(DataFsPurgeshot &ps)
Definition: XrdPfcPurge.cc:99

References m_purge_task_cond, m_purge_task_start, and XrdPfc::OldStylePurgeDriver().

Referenced by perform_purge_check().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ perform_purge_task_cleanup()

void ResourceMonitor::perform_purge_task_cleanup ( )

Definition at line 853 of file XrdPfcResourceMonitor.cc.

854 {
855  // Separated out so the purge_task can exit without post-checks.
856 
857  {
859  m_purge_task_end = time(0);
860  m_purge_task_complete = true;
862  }
864 }

References XrdPfc::Cache::ClearPurgeProtectedSet(), XrdPfc::Cache::GetInstance(), m_purge_task_complete, m_purge_task_cond, m_purge_task_end, and XrdSysCondVar::Signal().

Referenced by perform_purge_check().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ process_queues()

int ResourceMonitor::process_queues ( )

Definition at line 228 of file XrdPfcResourceMonitor.cc.

229 {
230  static const char *trc_pfx = "process_queues() ";
231 
232  // Assure that we pick up only entries that are present now.
233  // We really want all open records to be processed before file-stats updates
234  // and all those before the close records.
235  // Purges are sort of tangential as they really just modify bytes / number
236  // of files in a directory and do not deal with any persistent file id tokens.
237 
238  int n_records = 0;
239  {
240  XrdSysMutexHelper _lock(&m_queue_mutex);
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();
247  ++m_queue_swap_u1;
248  }
249 
250  for (auto &i : m_file_open_q.read_queue())
251  {
252  // i.id: LFN, i.record: OpenRecord
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());
256 
257  // Resolve fname into DirState.
258  // We could clear the filename after this ... or keep it, should we need it later on.
259  // For now it is just used for debug printouts.
260  DirState *last_existing_ds = nullptr;
261  DirState *ds = m_fs_state.find_dirstate_for_lfn(at.m_filename, &last_existing_ds);
262  at.m_dir_state = ds;
263  note_file_open(ds, last_existing_ds, i.record.m_existing_file);
264 
265  ds->m_here_usage.m_LastOpenTime = i.record.m_open_time;
266  }
267 
268  for (auto &i : m_file_update_stats_q.read_queue())
269  {
270  // i.id: token, i.record: Stats
271  AccessToken &at = token(i.id);
272  // Stats
273  DirState *ds = at.m_dir_state;
274  dprintf("process file update for token %d, %p -- %s\n",
275  i.id, ds, at.m_filename.c_str());
276 
277  ds->m_here_stats.AddUp(i.record);
278  m_current_usage_in_st_blocks += i.record.m_StBlocksAdded;
279  }
280 
281  for (auto &i : m_file_close_q.read_queue())
282  {
283  // i.id: token, i.record: CloseRecord
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());
287 
288  DirState *ds = at.m_dir_state;
289  ds->m_here_stats.m_NFilesClosed += 1;
290  ds->m_here_usage.m_LastCloseTime = i.record.m_close_time;
291 
292  }
293  { // Under the lock: clear() writes fields register_file_update_stats() uses.
294  XrdSysMutexHelper _lock(&m_queue_mutex);
295  for (auto &i : m_file_close_q.read_queue())
296  m_access_tokens.release(i.id);
297  }
298 
299  for (auto &i : m_file_purge_q1.read_queue())
300  {
301  // i.id: DirState*, i.record: PurgeRecord
302  DirState *ds = i.id;
303  ds->m_here_stats.m_StBlocksRemoved += i.record.m_size_in_st_blocks;
304  ds->m_here_stats.m_NFilesRemoved += i.record.m_n_files;
305  m_current_usage_in_st_blocks -= i.record.m_size_in_st_blocks;
306  }
307  for (auto &i : m_file_purge_q2.read_queue())
308  {
309  // i.id: directory-path, i.record: PurgeRecord
310  DirState *ds = m_fs_state.get_root()->find_path(i.id, -1, false, false);
311  if ( ! ds) {
312  TRACE(Error, trc_pfx << "DirState not found for directory path '" << i.id << "'.");
313  // find_path can return the last dir found ... but this clearly isn't a valid purge record.
314  continue;
315  }
316  ds->m_here_stats.m_StBlocksRemoved += i.record.m_size_in_st_blocks;
317  ds->m_here_stats.m_NFilesRemoved += i.record.m_n_files;
318  m_current_usage_in_st_blocks -= i.record.m_size_in_st_blocks;
319  }
320  for (auto &i : m_file_purge_q3.read_queue())
321  {
322  // i.id: LFN, i.record: size of file in st_blocks
323  DirState *ds = m_fs_state.get_root()->find_path(i.id, -1, true, false);
324  if ( ! ds) {
325  TRACE(Error, trc_pfx << "DirState not found for LFN path '" << i.id << "'.");
326  continue;
327  }
328  ds->m_here_stats.m_StBlocksRemoved += i.record;
329  ds->m_here_stats.m_NFilesRemoved += 1;
330  m_current_usage_in_st_blocks -= i.record;
331  }
332 
333  // Read queues / vectors are cleared at swap time.
334  // We might consider reducing their capacity by half if, say, their usage is below 25%.
335 
336  return n_records;
337 }
void AddUp(const DirStats &s)
Definition: XrdPfcStats.hh:192
long long m_StBlocksRemoved
Definition: XrdPfcStats.hh:149
void release(int i)
Clear the slot and return it for reuse. Caller must hold its mutex.
void note_file_open(DirState *node, DirState *last_existing, bool existing_file)
DirState * find_dirstate_for_lfn(const std::string &lfn, DirState **last_existing_dir=nullptr)
DirState * find_path(const std::string &path, int max_depth, bool parse_as_lfn, bool create_subdirs, DirState **last_existing_dir=nullptr)

References XrdPfc::DirStats::AddUp(), dprintf, Error, XrdPfc::DataFsState::find_dirstate_for_lfn(), XrdPfc::DirState::find_path(), XrdPfc::DataFsState::get_root(), XrdPfc::DirState::m_here_stats, XrdPfc::DirState::m_here_usage, XrdPfc::DirUsage::m_LastCloseTime, XrdPfc::DirUsage::m_LastOpenTime, XrdPfc::DirStats::m_NFilesClosed, XrdPfc::DirStats::m_NFilesRemoved, XrdPfc::DirStats::m_StBlocksRemoved, XrdPfc::note_file_open(), XrdPfc::StableChunkPool< T, InnerShift, MaxChunks >::release(), token(), and TRACE.

Referenced by heart_beat(), and main_thread_function().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ register_file_close()

void XrdPfc::ResourceMonitor::register_file_close ( int  token_id,
time_t  close_timestamp,
const Stats &  full_stats 
)
inline

Definition at line 190 of file XrdPfcResourceMonitor.hh.

190  {
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  }

Referenced by XrdPfc::Cache::ExecuteCommandUrl().

+ Here is the caller graph for this function:

◆ register_file_open()

int XrdPfc::ResourceMonitor::register_file_open ( const std::string &  filename,
time_t  open_timestamp,
bool  existing_file 
)
inline

Definition at line 154 of file XrdPfcResourceMonitor.hh.

154  {
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  }

Referenced by XrdPfc::Cache::ExecuteCommandUrl().

+ Here is the caller graph for this function:

◆ register_file_purge() [1/2]

void XrdPfc::ResourceMonitor::register_file_purge ( const std::string &  filename,
long long  size_in_st_blocks 
)
inline

Definition at line 210 of file XrdPfcResourceMonitor.hh.

210  {
211  XrdSysMutexHelper _lock(&m_queue_mutex);
212  m_file_purge_q3.push(filename, size_in_st_blocks);
213  }

◆ register_file_purge() [2/2]

void XrdPfc::ResourceMonitor::register_file_purge ( DirState *  target,
long long  size_in_st_blocks 
)
inline

Definition at line 198 of file XrdPfcResourceMonitor.hh.

198  {
199  XrdSysMutexHelper _lock(&m_queue_mutex);
200  m_file_purge_q1.push(target, {size_in_st_blocks, 1});
201  }

Referenced by XrdPfc::Cache::UnlinkFile().

+ Here is the caller graph for this function:

◆ register_file_update_stats()

void XrdPfc::ResourceMonitor::register_file_update_stats ( int  token_id,
const Stats &  stats 
)
inline

Definition at line 171 of file XrdPfcResourceMonitor.hh.

171  {
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  }
XrdPosixStats Stats
Definition: XrdPosixFile.cc:64

References XrdPfc::Stats::AddUp(), and token().

Referenced by XrdPfc::Cache::ExecuteCommandUrl().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ register_multi_file_purge() [1/2]

void XrdPfc::ResourceMonitor::register_multi_file_purge ( const std::string &  target,
long long  size_in_st_blocks,
int  n_files 
)
inline

Definition at line 206 of file XrdPfcResourceMonitor.hh.

206  {
207  XrdSysMutexHelper _lock(&m_queue_mutex);
208  m_file_purge_q2.push(target, {size_in_st_blocks, n_files});
209  }

◆ register_multi_file_purge() [2/2]

void XrdPfc::ResourceMonitor::register_multi_file_purge ( DirState *  target,
long long  size_in_st_blocks,
int  n_files 
)
inline

Definition at line 202 of file XrdPfcResourceMonitor.hh.

202  {
203  XrdSysMutexHelper _lock(&m_queue_mutex);
204  m_file_purge_q1.push(target, {size_in_st_blocks, n_files});
205  }

◆ scan_dir_and_recurse()

void ResourceMonitor::scan_dir_and_recurse ( FsTraversal &  fst)

Definition at line 121 of file XrdPfcResourceMonitor.cc.

122 {
123  dprintf("In scan_dir_and_recurse for '%s', size of dir_vec = %d, file_stat_map = %d\n",
124  fst.m_current_path.c_str(),
125  (int)fst.m_current_dirs.size(), (int)fst.m_current_files.size());
126 
127  // Breadth first, accumulate into "here", unless it was already scanned via an
128  // OOB open file request.
129  if ( ! fst.m_dir_state->m_scanned)
130  {
131  DirUsage &here = fst.m_dir_state->m_here_usage;
132  for (auto it = fst.m_current_files.begin(); it != fst.m_current_files.end(); ++it)
133  {
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);
136 
137  // XXX Make some of these optional?
138  // Remove files that do not have both cinfo and data?
139  // Remove empty directories before even descending?
140  // Leave this for some consistency pass?
141  // Note that FsTraversal supports ignored paths ... some details (config, N2N) to be clarified.
142 
143  if (it->second.has_data && it->second.has_cinfo) {
144  here.m_StBlocks += it->second.stat_data.st_blocks;
145  here.m_NFiles += 1;
146  }
147  }
148  fst.m_dir_state->m_scanned = true;
149  }
150 
151  // Swap-out directories as inter_dir_scan can use the FsTraversal.
152  std::vector<std::string> dirs;
153  dirs.swap(fst.m_current_dirs);
154 
155  if (++m_dir_scan_check_counter >= 100)
156  {
157  process_inter_dir_scan_open_requests(fst);
158  m_dir_scan_check_counter = 0;
159  }
160 
161  // Descend into sub-dirs, do not accumulate into recursive_subdir_usage yet. This is done
162  // in a separate pass to allow for proper accounting of files being opened during the initial scan.
163  for (auto &dname : dirs)
164  {
165  if (fst.cd_down(dname))
166  {
168  fst.cd_up();
169  }
170  // XXX else try to remove it?
171  }
172 }
std::vector< std::string > m_current_dirs
bool cd_down(const std::string &dir_name)
std::map< std::string, FilePairStat > m_current_files

References XrdPfc::FsTraversal::cd_down(), XrdPfc::FsTraversal::cd_up(), dprintf, XrdPfc::FsTraversal::m_current_dirs, XrdPfc::FsTraversal::m_current_files, XrdPfc::FsTraversal::m_current_path, XrdPfc::FsTraversal::m_dir_state, XrdPfc::DirState::m_here_usage, XrdPfc::DirUsage::m_NFiles, XrdPfc::DirState::m_scanned, and XrdPfc::DirUsage::m_StBlocks.

Referenced by perform_initial_scan().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ token()

AccessToken& XrdPfc::ResourceMonitor::token ( int  i)
inline

Definition at line 223 of file XrdPfcResourceMonitor.hh.

223 { return m_access_tokens[i]; }

Referenced by process_queues(), and register_file_update_stats().

+ Here is the caller graph for this function:

◆ update_vs_and_file_usage_info()

void ResourceMonitor::update_vs_and_file_usage_info ( )

Definition at line 579 of file XrdPfcResourceMonitor.cc.

580 {
581  static const char *trc_pfx = "update_vs_and_file_usage_info() ";
582 
583  const auto &conf = Cache::Conf();
584  XrdOssVSInfo vsi;
585 
586  // StatVS error (after it succeeded in config) implies a memory corruption (according to Mr. H).
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.");
589  _exit(1);
590  }
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.");
596  _exit(1);
597  }
598  m_fs_state.m_meta_total = vsi.Total;
599  m_fs_state.m_meta_used = vsi.Total - vsi.Free;
600 }
long long Total
Definition: XrdOssVS.hh:90
long long Free
Definition: XrdOssVS.hh:91
virtual int StatVS(XrdOssVSInfo *vsP, const char *sname=0, int updt=0)
Definition: XrdOss.cc:117

References XrdPfc::Cache::Conf(), Error, XrdOssVSInfo::Free, XrdPfc::DataFsStateBase::m_disk_total, XrdPfc::DataFsStateBase::m_disk_used, XrdPfc::DataFsStateBase::m_file_usage, XrdPfc::DataFsStateBase::m_meta_total, XrdPfc::DataFsStateBase::m_meta_used, XrdOss::StatVS(), XrdOssVSInfo::Total, and TRACE.

Referenced by heart_beat(), and perform_initial_scan().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Member Data Documentation

◆ m_purge_task_active

bool XrdPfc::ResourceMonitor::m_purge_task_active {false}

Definition at line 254 of file XrdPfcResourceMonitor.hh.

Referenced by heart_beat(), and perform_purge_check().

◆ m_purge_task_complete

bool XrdPfc::ResourceMonitor::m_purge_task_complete {false}

Definition at line 255 of file XrdPfcResourceMonitor.hh.

Referenced by heart_beat(), and perform_purge_task_cleanup().

◆ m_purge_task_cond

XrdSysCondVar XrdPfc::ResourceMonitor::m_purge_task_cond {0}

◆ m_purge_task_end

time_t XrdPfc::ResourceMonitor::m_purge_task_end {0}

Definition at line 253 of file XrdPfcResourceMonitor.hh.

Referenced by perform_purge_task_cleanup().

◆ m_purge_task_start

time_t XrdPfc::ResourceMonitor::m_purge_task_start {0}

Definition at line 252 of file XrdPfcResourceMonitor.hh.

Referenced by perform_purge_task().


The documentation for this class was generated from the following files: