rda-python-dsquasar 3.0.21__tar.gz → 3.0.22__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {rda_python_dsquasar-3.0.21/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.22}/PKG-INFO +1 -1
  2. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/pyproject.toml +1 -1
  3. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar/dsquasar.py +39 -8
  4. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar/dsquasar.usg +17 -2
  5. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22/src/rda_python_dsquasar.egg-info}/PKG-INFO +1 -1
  6. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/LICENSE +0 -0
  7. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/MANIFEST.in +0 -0
  8. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/README.md +0 -0
  9. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/setup.cfg +0 -0
  10. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar/__init__.py +0 -0
  11. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar/ds_quasar.py +0 -0
  12. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar/dstacc.py +0 -0
  13. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar/taccrec.py +0 -0
  14. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar/tacctar.py +0 -0
  15. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar.egg-info/SOURCES.txt +0 -0
  16. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar.egg-info/dependency_links.txt +0 -0
  17. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar.egg-info/entry_points.txt +0 -0
  18. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar.egg-info/requires.txt +0 -0
  19. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/src/rda_python_dsquasar.egg-info/top_level.txt +0 -0
  20. {rda_python_dsquasar-3.0.21 → rda_python_dsquasar-3.0.22}/test/test_dsquasar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_dsquasar
3
- Version: 3.0.21
3
+ Version: 3.0.22
4
4
  Summary: RDA Python package to backup and recover RDA data archives to and from GLOBUS Quasar backup server
5
5
  Author-email: Zaihua Ji <zji@ucar.edu>
6
6
  Project-URL: Homepage, https://github.com/NCAR/rda-python-dsquasar
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rda_python_dsquasar"
7
- version = "3.0.21"
7
+ version = "3.0.22"
8
8
  authors = [
9
9
  { name="Zaihua Ji", email="zji@ucar.edu" },
10
10
  ]
@@ -123,6 +123,7 @@ class DsQuasar(PgCMD, PgSplit):
123
123
  'maxcnt' : 10,
124
124
  'dolock' : 1,
125
125
  'doemail' : 0,
126
+ 'locflag' : None, # -L, limit the datasets gathered to one dataset.locflag
126
127
  'starttime' : 0, # wall-clock start of the run, for the PBS walltime guard
127
128
  'einfo' : 0, # set once a progress report is cached into dscheck.einfo
128
129
  'eretry' : 0, # earliest retry time after a progress report failed to cache
@@ -144,7 +145,7 @@ class DsQuasar(PgCMD, PgSplit):
144
145
  argv = sys.argv[1:]
145
146
  for arg in argv:
146
147
  if re.match(r'^-(h|-help)$', arg, re.I): self.show_usage('dsquasar')
147
- ms = re.match(r'^-(a|b|c|d|e|E|l|m|n|t|u|w|A|B|D|W)$', arg)
148
+ ms = re.match(r'^-(a|b|c|d|e|E|l|m|n|t|u|w|A|B|D|L|W)$', arg)
148
149
  if ms:
149
150
  arg = ms.group(1)
150
151
  if arg == 'b':
@@ -152,7 +153,7 @@ class DsQuasar(PgCMD, PgSplit):
152
153
  elif arg in self.sopts:
153
154
  self.sopts[arg] = 1
154
155
  option = None
155
- elif 'AcdlmtwW'.find(arg) > -1:
156
+ elif 'AcdLlmtwW'.find(arg) > -1:
156
157
  option = arg
157
158
  if arg == 'd': self.bopts = []
158
159
  elif 'BD'.find(arg) > -1:
@@ -179,6 +180,10 @@ class DsQuasar(PgCMD, PgSplit):
179
180
  if not (arg == 'Y' or arg == 'N'): self.pglog(arg +": Lock Flag(-l) must be Y or N", self.LGWNEX)
180
181
  self.PGBACK['dolock'] = 1 if arg == 'Y' else 0
181
182
  option = None
183
+ elif option == 'L':
184
+ if not (arg == 'G' or arg == 'O'): self.pglog(arg +": Location Flag(-L) must be G or O", self.LGWNEX)
185
+ self.PGBACK['locflag'] = arg
186
+ option = None
182
187
  elif option == 'W':
183
188
  self.PGBACK['maxworkers'] = int(arg)
184
189
  if self.PGBACK['maxworkers'] < 1: self.pglog(arg +": Maximum Workers(-W) must be > 0", self.LGWNEX)
@@ -918,9 +923,10 @@ class DsQuasar(PgCMD, PgSplit):
918
923
  # even if PBS kills the job at the walltime. the final report follows and drops the
919
924
  # parked one if the run does finish in time. the live email buffers are saved and put
920
925
  # back, so the final report still carries everything logged before the cutoff.
921
- # PBS sends no catchable warning before the kill - SIGTERM is not trapped and SIGKILL
922
- # cannot be - so this parked report is the only thing standing between a killed job and
923
- # a run that is never heard from. every action submitted to PBS is guarded, not just the
926
+ # batch_term_report covers the kill itself, but only the seconds of it: SIGKILL cannot be
927
+ # caught, and a process stuck in uninterruptible I/O may never run the handler. this parked
928
+ # report is what stands between a killed job and a run that is never heard from, so it is
929
+ # kept as the first line of defence. every action submitted to PBS is guarded, not just the
924
930
  # tar and transfer ones: -A 16 and the hidden -A 128 are submitted with the same 24 hour
925
931
  # walltime and used to be excluded, so they died silently.
926
932
  # the tar queue depths are only reported for the actions they describe. both depths are
@@ -1468,13 +1474,29 @@ class DsQuasar(PgCMD, PgSplit):
1468
1474
  fcnt = 0
1469
1475
  # warn per named dataset with no files; stay silent when scanning all datasets
1470
1476
  logact = self.LOGWRN if self.dsids else 0
1477
+ flds = "dsid, backflag, pid, lockhost"
1478
+ # -L splits the work by where the data sits: an Object(O) dataset has to be staged
1479
+ # down from the object store and checksummed before it can be tarred, while a Glade(G)
1480
+ # one is only stat'ed, so the two classes run at wildly different rates and are better
1481
+ # off in separate runs than sharing one walltime.
1482
+ # G is matched as "not O" rather than "= G" on purpose: dataset.locflag is only a
1483
+ # summary of the per-file flags (set_dataset_locflag writes it only when every public
1484
+ # web file agrees, and leaves the old value otherwise), so it can hold a stale value.
1485
+ # as "not O" the two halves stay exhaustive and no dataset falls through both runs
1486
+ lcnd = ''
1487
+ if self.PGBACK['locflag'] == 'O':
1488
+ lcnd = "locflag = 'O'"
1489
+ elif self.PGBACK['locflag']:
1490
+ lcnd = "locflag <> 'O'"
1471
1491
  if self.dsids:
1472
1492
  pgrecs = []
1473
1493
  for dsid in self.dsids:
1474
- pgrec = self.pgget("dataset", "dsid, backflag, pid", "dsid = '{}'".format(dsid), self.LGWNEX)
1494
+ dcnd = "dsid = '{}'".format(dsid)
1495
+ if lcnd: dcnd += " AND " + lcnd
1496
+ pgrec = self.pgget("dataset", flds, dcnd, self.LGWNEX)
1475
1497
  if pgrec: pgrecs.append(pgrec)
1476
1498
  else:
1477
- mrecs = self.pgmget("dataset", "dsid, backflag, pid", self.dsid_order().strip(), self.LGWNEX)
1499
+ mrecs = self.pgmget("dataset", flds, (lcnd + self.dsid_order()).strip(), self.LGWNEX)
1478
1500
  dcnt = len(mrecs['dsid']) if mrecs else 0
1479
1501
  pgrecs = [self.onerecord(mrecs, i) for i in range(dcnt)]
1480
1502
  for pgrec in pgrecs:
@@ -1487,7 +1509,16 @@ class DsQuasar(PgCMD, PgSplit):
1487
1509
  # main runs take new files (bid = 0), so it shares no record with the worker holding
1488
1510
  # the lock and must not skip the dataset for it
1489
1511
  if unlock and pgrec['pid'] and not self.PGBACK['chgdays']:
1490
- if self.lock_dataset(dsid, 0, self.LOGACT) < 1: continue
1512
+ # quiet unlock: a live holder is the normal case for a second worker, and the
1513
+ # library wording for it ("Cannot Unlock") reads like a failure. report the skip
1514
+ # here instead, in terms of the worker that is still on the dataset
1515
+ lstat = self.lock_dataset(dsid, 0, 0)
1516
+ if lstat < 1:
1517
+ # -1 is the live holder; 0 means the record went away or the db errored,
1518
+ # which lock_dataset has already logged as an error
1519
+ if lstat < 0:
1520
+ self.pglog("{}: skipped, still in use by {}/{}".format(dsid, pgrec['pid'], pgrec['lockhost']), self.LOGACT)
1521
+ continue
1491
1522
  fcnt += self.get_dataset_files(dsid, dsfiles, pgrec['backflag'], logact, sizes)
1492
1523
  if dsfiles:
1493
1524
  s = 's' if fcnt > 1 else ''
@@ -8,8 +8,8 @@
8
8
  file tarring and transferring is performed through the utility program dsarch.
9
9
 
10
10
  Usage: dsquasar [-b] [(-a | -t DatasetIDs)] [-c ChangeDays] [-(e|E)] \
11
- [-(B|D)] [-A ActionBits] [-l (Y|N)] [-m ProcessCount] \
12
- [-n] [-u] [-d [HostName] [TryCount]] [-h]
11
+ [-(B|D)] [-L (G|O)] [-A ActionBits] [-l (Y|N)] \
12
+ [-m ProcessCount] [-n] [-u] [-d [HostName] [TryCount]] [-h]
13
13
 
14
14
  Either -a or -t must be given (usage is displayed otherwise). dsquasar runs
15
15
  from the Quasar backup work directory; it changes there automatically.
@@ -24,6 +24,21 @@
24
24
  '%' is allowed and is expanded against the dataset table. Ignored,
25
25
  with a warning, when -a is also given.
26
26
 
27
+ -L (G|O)
28
+ Limit the run by dataset.locflag: O takes the datasets on the object
29
+ store, G takes everything else. Applies to -a and to -t alike. The
30
+ two classes back up at very different rates: an O dataset has every
31
+ file staged down from the object store and checksummed before it can
32
+ be tarred, while a G dataset is only stat'ed. Running them in one job
33
+ lets the slow class eat the whole walltime, so give them separate
34
+ runs, e.g. one submit with -L G and another with -L O. Defaults to
35
+ all datasets.
36
+
37
+ -L G and -L O together always cover every dataset, so a pair of runs
38
+ loses nothing. The split is by dataset, though, and dataset.locflag
39
+ is only a summary of the per-file flags, so a dataset holding files of
40
+ both kinds is backed up entirely by whichever run claims it.
41
+
27
42
 
28
43
  BACKUP SCOPE
29
44
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_dsquasar
3
- Version: 3.0.21
3
+ Version: 3.0.22
4
4
  Summary: RDA Python package to backup and recover RDA data archives to and from GLOBUS Quasar backup server
5
5
  Author-email: Zaihua Ji <zji@ucar.edu>
6
6
  Project-URL: Homepage, https://github.com/NCAR/rda-python-dsquasar