rda-python-dsquasar 3.0.24__tar.gz → 3.0.26__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.
- {rda_python_dsquasar-3.0.24/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.26}/PKG-INFO +1 -1
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/pyproject.toml +1 -1
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/dsquasar.py +149 -41
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/dsquasar.usg +15 -12
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26/src/rda_python_dsquasar.egg-info}/PKG-INFO +1 -1
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/LICENSE +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/MANIFEST.in +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/README.md +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/setup.cfg +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/__init__.py +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/ds_quasar.py +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/dstacc.py +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/taccrec.py +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/tacctar.py +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar.egg-info/SOURCES.txt +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar.egg-info/dependency_links.txt +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar.egg-info/entry_points.txt +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar.egg-info/requires.txt +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar.egg-info/top_level.txt +0 -0
- {rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/test/test_dsquasar.py +0 -0
{rda_python_dsquasar-3.0.24/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.26}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_dsquasar
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.26
|
|
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
|
{rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/dsquasar.py
RENAMED
|
@@ -89,7 +89,12 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
89
89
|
self.SUBLMTS = 2000 # file count limit for a sub-group
|
|
90
90
|
# tarring (TARACT) forks one child per tar file, so its parallel unit is the tar
|
|
91
91
|
# file: use a small per-process limit and a high cap to spread the work widely.
|
|
92
|
-
self.MPTLIMIT = 200 # tar file count per batch process for tarring
|
|
92
|
+
self.MPTLIMIT = 200 # tar file count per batch process for tarring on Glade
|
|
93
|
+
# a tar file of an object store dataset has every member staged down from Boreas and
|
|
94
|
+
# checksummed first, so it takes many times longer than a Glade one: give it a
|
|
95
|
+
# process per far fewer tar files. this is the only way to speed up a backlog held
|
|
96
|
+
# in one dataset, since a second worker cannot share the dataset lock.
|
|
97
|
+
self.MPOLIMIT = 20 # tar file count per batch process for tarring on the object store
|
|
93
98
|
self.MPTMAX = 12 # maximum number of batch processes for tarring
|
|
94
99
|
# uploading (BCKACT) forks one child per BCKSIZE transfer batch (~BCKSIZE/TARSIZE
|
|
95
100
|
# tar files each), so far fewer parallel units than tar files: use a large
|
|
@@ -107,8 +112,9 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
107
112
|
# dscheck work done, which is action independent because dcount counts GDEX files
|
|
108
113
|
# for -A 3 but tar files for -A 2/-A 4.
|
|
109
114
|
self.MAXWORKERS = 2 # default maximum concurrent workers per command (-W)
|
|
110
|
-
self.WORKGRACE =
|
|
111
|
-
self.
|
|
115
|
+
self.WORKGRACE = 3600 # 1 hour before a running job is judged on progress
|
|
116
|
+
self.GATHGRACE = 2*3600 # 2 hours before a job with nothing done yet counts as stalled
|
|
117
|
+
self.MINWPROJ = 1.5 # projected done fraction at the walltime that counts as finishing
|
|
112
118
|
self.PGBACK = {
|
|
113
119
|
'workdir' : "{}/{}/quasar_backup".format(self.PGLOG['GDEXWORK'], self.PGLOG['COMMONUSER']),
|
|
114
120
|
'mproc' : 1,
|
|
@@ -202,6 +208,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
202
208
|
# an even worker slot walks the datasets from the other end so that it and the odd
|
|
203
209
|
# slot before it meet in the middle instead of fighting over the same dataset locks
|
|
204
210
|
if self.reverse_dsids(): self.dsids.reverse()
|
|
211
|
+
self.order_dsids()
|
|
205
212
|
self.PGBACK['cmd'] = "dsquasar {}".format(' '.join(argv))
|
|
206
213
|
|
|
207
214
|
# do quasar backup process
|
|
@@ -275,6 +282,18 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
275
282
|
dsid = self.format_dataset_id(dsid)
|
|
276
283
|
if dsid not in self.dsids: self.dsids.append(dsid)
|
|
277
284
|
|
|
285
|
+
# put the -t datasets on the object store first for an odd worker slot and the ones on
|
|
286
|
+
# Glade first for an even one, keeping the given (or worker-reversed) order within each
|
|
287
|
+
# class; see dsid_order for why
|
|
288
|
+
def order_dsids(self):
|
|
289
|
+
if len(self.dsids) < 2: return
|
|
290
|
+
dcnd = "locflag = 'O' AND dsid IN ('{}')".format("', '".join(self.dsids))
|
|
291
|
+
pgrecs = self.pgmget('dataset', 'dsid', dcnd, self.LGWNEX)
|
|
292
|
+
if not pgrecs: return
|
|
293
|
+
odsids = set(pgrecs['dsid'])
|
|
294
|
+
reverse = self.reverse_dsids()
|
|
295
|
+
self.dsids.sort(key = lambda dsid: (dsid in odsids) == reverse)
|
|
296
|
+
|
|
278
297
|
# Check backup and file information and dump the backup statistics#
|
|
279
298
|
def dump_statistics_action(self):
|
|
280
299
|
self.start_dsquasar_none_daemon(self.STTACT)
|
|
@@ -375,7 +394,8 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
375
394
|
# file (one child per tar): new infiles CINACT will create this run (estimated from
|
|
376
395
|
# the total bytes of GDEX files ready to back up, ~one tar per TARSIZE) plus existing
|
|
377
396
|
# status 'N' infile records to build (TARACT), scaled one process per MPTLIMIT tar
|
|
378
|
-
# files
|
|
397
|
+
# files on Glade or one per MPOLIMIT tar files on the object store, the two summed and
|
|
398
|
+
# capped at MPTMAX. uploading's unit is the BCKSIZE transfer batch (one child
|
|
379
399
|
# per ~BCKSIZE/TARSIZE tar files): existing status 'T' tar records to transfer
|
|
380
400
|
# (BCKACT), scaled one process per MPBLIMIT tar files, capped at MPBMAX. the phases
|
|
381
401
|
# run sequentially in one run and share a single reserved ncpus, so return the max of
|
|
@@ -383,16 +403,34 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
383
403
|
# never reaches here and stays single process.
|
|
384
404
|
def batch_process_count(self, acts):
|
|
385
405
|
mproc = 1
|
|
386
|
-
|
|
406
|
+
gcnt = ocnt = 0 # tar files on Glade and on the object store
|
|
407
|
+
# an extra worker cannot touch the datasets another live process holds, typically the
|
|
408
|
+
# big object store dataset worker 1 is on, so it leaves them out of its sizing as its
|
|
409
|
+
# real gather will; counting them reserved worker 1's cpus again for the Glade work
|
|
410
|
+
# left. the gather's unlock pass skips a live holder and frees a dead one's lock.
|
|
411
|
+
# worker 1 keeps counting everything: a held dataset is usually released well within
|
|
412
|
+
# its 24 hours, and checking each holder costs an rdaps call per dataset
|
|
413
|
+
extra = self.PGBACK['worker'] > 1
|
|
387
414
|
if acts&self.CINACT:
|
|
388
|
-
sizes = [0]
|
|
389
|
-
if self.gather_dataset_files(None,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
415
|
+
sizes = [0, 0] # bytes on Glade and on the object store
|
|
416
|
+
if self.gather_dataset_files(None, extra, sizes):
|
|
417
|
+
gcnt += (sizes[0] + self.TARSIZE - 1)//self.TARSIZE
|
|
418
|
+
ocnt += (sizes[1] + self.TARSIZE - 1)//self.TARSIZE
|
|
419
|
+
if not (gcnt or ocnt): gcnt = 1
|
|
420
|
+
if acts&self.TARACT:
|
|
421
|
+
# a dataset lock left by a dead process also drops out here, though the gather
|
|
422
|
+
# would free it; that undercounts by a stale lock's infiles at most
|
|
423
|
+
xcnd = " AND dsid NOT IN (SELECT dsid FROM dataset WHERE pid > 0)" if extra else ''
|
|
424
|
+
ncnt = self.batch_tar_count('N', None, xcnd)
|
|
425
|
+
oncnt = self.batch_tar_count('N', 'O', xcnd) if ncnt else 0
|
|
426
|
+
gcnt += ncnt - oncnt
|
|
427
|
+
ocnt += oncnt
|
|
428
|
+
# ceil(gcnt/MPTLIMIT + ocnt/MPOLIMIT): one process per MPTLIMIT Glade tar files plus
|
|
429
|
+
# one per MPOLIMIT object store tar files, rounded up once over the sum
|
|
430
|
+
tunit = self.MPTLIMIT*self.MPOLIMIT
|
|
431
|
+
tproc = (gcnt*self.MPOLIMIT + ocnt*self.MPTLIMIT + tunit - 1)//tunit
|
|
432
|
+
if tproc > self.MPTMAX: tproc = self.MPTMAX
|
|
433
|
+
if tproc > mproc: mproc = tproc
|
|
396
434
|
if acts&self.BCKACT:
|
|
397
435
|
bcnt = self.batch_tar_count('T')
|
|
398
436
|
if bcnt > self.MPBLIMIT:
|
|
@@ -401,10 +439,25 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
401
439
|
if bproc > mproc: mproc = bproc
|
|
402
440
|
return mproc
|
|
403
441
|
|
|
404
|
-
#
|
|
405
|
-
|
|
442
|
+
# the -L scope of a bfile condition, or the scope of the given locflag. bfile has no
|
|
443
|
+
# locflag, so a record is matched through its dataset with the same "O" / "not O" split
|
|
444
|
+
# gather_dataset_files uses, keeping the two halves exhaustive. without it an -L O and
|
|
445
|
+
# an -L G run that overlap each tar, transfer and report the tar records of both halves
|
|
446
|
+
def bfile_locflag_condition(self, locflag = None):
|
|
447
|
+
if not locflag: locflag = self.PGBACK['locflag']
|
|
448
|
+
if not locflag: return ''
|
|
449
|
+
lnot = '' if locflag == 'O' else 'NOT '
|
|
450
|
+
return " AND dsid {}IN (SELECT dsid FROM dataset WHERE locflag = 'O')".format(lnot)
|
|
451
|
+
|
|
452
|
+
# count the bfile tar records of a given status honoring backflag/dataset/locflag scope;
|
|
453
|
+
# a locflag given narrows the count further to that half, inside any -L scope, and xcnd
|
|
454
|
+
# is any extra condition to AND on
|
|
455
|
+
def batch_tar_count(self, status, locflag = None, xcnd = ''):
|
|
406
456
|
bcnd = "status = '{}'".format(status)
|
|
407
457
|
if self.PGBACK['backflag']: bcnd += " AND type = '{}'".format(self.PGBACK['backflag'])
|
|
458
|
+
bcnd += self.bfile_locflag_condition()
|
|
459
|
+
if locflag: bcnd += self.bfile_locflag_condition(locflag)
|
|
460
|
+
bcnd += xcnd
|
|
408
461
|
if self.dsids:
|
|
409
462
|
tcnt = 0
|
|
410
463
|
for dsid in self.dsids:
|
|
@@ -433,11 +486,16 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
433
486
|
dargv = dargv[0:100]
|
|
434
487
|
return self.get_dscheck("dsquasar", dargv, self.PGBACK['workdir'], self.PGLOG['CURUID'], dargx, self.LOGWRN)
|
|
435
488
|
|
|
436
|
-
# a running batch job counts as stalled once it has run longer than WORKGRACE
|
|
437
|
-
#
|
|
438
|
-
#
|
|
439
|
-
# such a job cannot finish this attempt however long it is left alone, so an
|
|
440
|
-
# worker is what helps it, not more retries.
|
|
489
|
+
# a running batch job counts as stalled once it has run longer than WORKGRACE and its
|
|
490
|
+
# progress so far projects to less than MINWPROJ of its recorded work by the walltime,
|
|
491
|
+
# or once it has run longer than GATHGRACE with nothing recorded done at all:
|
|
492
|
+
# such a job cannot finish this attempt comfortably however long it is left alone, so an
|
|
493
|
+
# extra worker is what helps it, not more retries.
|
|
494
|
+
# MINWPROJ is above 1 on purpose: the first worker starts on the slow object store
|
|
495
|
+
# datasets and an extra one on the fast Glade ones (see dsid_order), so a second worker
|
|
496
|
+
# is wanted whenever the first is not well on its way to finishing, not only when it is
|
|
497
|
+
# sure to miss the walltime. 1.5 leaves alone only a job on pace to finish within about
|
|
498
|
+
# two thirds of the walltime.
|
|
441
499
|
# the test is on the RATE, not on an absolute done fraction as it first was. a fixed
|
|
442
500
|
# 1% threshold only catches a job that is nearly frozen, and reads a job that needs
|
|
443
501
|
# weeks as progressing: the real case was 2% done after 13H35M of a 24 hour walltime,
|
|
@@ -450,15 +508,19 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
450
508
|
# runs this and set_walltime_deadline reads the granted walltime back in the batch job.
|
|
451
509
|
# when the daemon caps it to a shorter queue limit the projection is optimistic, which
|
|
452
510
|
# errs towards leaving the job alone.
|
|
453
|
-
# a job that has recorded nothing done
|
|
454
|
-
# zero rate
|
|
455
|
-
#
|
|
456
|
-
#
|
|
511
|
+
# a job that has recorded nothing done may still be in its gather phases, which read as
|
|
512
|
+
# a zero rate, so it gets the longer GATHGRACE before it is judged. but a job that has
|
|
513
|
+
# recorded nothing after that long, typically one downloading its first object store
|
|
514
|
+
# dataset (dcount is only flushed every pstep files), is the slowest case of all and
|
|
515
|
+
# must not be left alone forever. the graces stay short because an extra worker can
|
|
516
|
+
# only be added by a cron submit, every 4 hours: a 6 hour grace kept it out of the first
|
|
517
|
+
# 6 to 10 hours of every attempt.
|
|
457
518
|
def worker_progress_stalled(self, pgrec):
|
|
458
519
|
if not (pgrec['pid'] and pgrec['stttime']): return False
|
|
459
520
|
if self.check_host_pid(pgrec['lockhost'], pgrec['pid']) <= 0: return False
|
|
460
521
|
elapsed = tm() - pgrec['stttime']
|
|
461
|
-
if elapsed < self.WORKGRACE
|
|
522
|
+
if elapsed < self.WORKGRACE: return False
|
|
523
|
+
if not pgrec['dcount']: return elapsed >= self.GATHGRACE
|
|
462
524
|
done = pgrec['dcount']/pgrec['fcount'] if pgrec['fcount'] else 0
|
|
463
525
|
return (done*self.WALLTIME/elapsed) < self.MINWPROJ
|
|
464
526
|
|
|
@@ -609,7 +671,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
609
671
|
self.check_batch_deadline()
|
|
610
672
|
if len(qinfo['dsids']) == self.DSCNT: self.process_one_backup_file(qinfo, True, False)
|
|
611
673
|
if self.PGBACK['dolock'] and dsid not in qinfo['dslocks']:
|
|
612
|
-
if self.
|
|
674
|
+
if self.lock_work_dataset(dsid) < 1: continue
|
|
613
675
|
qinfo['dslocks'].append(dsid)
|
|
614
676
|
if not self.regather_dataset_files(bfiles, dsid, backflag):
|
|
615
677
|
self.lock_dataset(dsid, 0, self.LGEREX)
|
|
@@ -675,12 +737,27 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
675
737
|
# run, including the primary one, are left alone. returns the datasets locked here, to be
|
|
676
738
|
# unlocked once the tar file is dispatched, or None if one of them is held by another
|
|
677
739
|
# worker, in which case nothing new is left locked and the tar file must wait for a later run
|
|
740
|
+
# lock a dataset before working on it. a dataset held by another live process is the
|
|
741
|
+
# normal case once a second worker or the other backup action runs, and the library
|
|
742
|
+
# wording for it ("Locked by N/PBS: Cannot Relock") was logged as an ERROR, which counted
|
|
743
|
+
# in the error count and the error email although the dataset is just skipped. lock
|
|
744
|
+
# quietly and report the skip as information instead, naming the holder; a database
|
|
745
|
+
# error is still logged as an error by lock_dataset. returns what lock_dataset returns:
|
|
746
|
+
# 1 locked, -1 held by another process, 0 no dataset record or a database error
|
|
747
|
+
def lock_work_dataset(self, dsid, logact = None):
|
|
748
|
+
lstat = self.lock_dataset(dsid, 1, 0)
|
|
749
|
+
if lstat < 0:
|
|
750
|
+
pgrec = self.pgget('dataset', 'pid, lockhost', "dsid = '{}'".format(dsid), self.LOGERR)
|
|
751
|
+
holder = "{}/{}".format(pgrec['pid'], pgrec['lockhost']) if pgrec else 'another process'
|
|
752
|
+
self.pglog("{}: skipped, locked by {}".format(dsid, holder), self.LOGACT if logact is None else logact)
|
|
753
|
+
return lstat
|
|
754
|
+
|
|
678
755
|
def lock_backup_datasets(self, qinfo, dsids):
|
|
679
756
|
dslocks = []
|
|
680
757
|
if not self.PGBACK['dolock']: return dslocks
|
|
681
758
|
for dsid in dsids:
|
|
682
759
|
if dsid in qinfo['dslocks']: continue
|
|
683
|
-
if self.
|
|
760
|
+
if self.lock_work_dataset(dsid, self.DTLACT) < 1:
|
|
684
761
|
self.unlock_backup_datasets(qinfo, dslocks)
|
|
685
762
|
return None
|
|
686
763
|
qinfo['dslocks'].append(dsid)
|
|
@@ -720,7 +797,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
720
797
|
'qsize' : 0, 'qcnt' : 0}
|
|
721
798
|
for dsid in bfiles:
|
|
722
799
|
if self.PGBACK['dolock']:
|
|
723
|
-
if self.
|
|
800
|
+
if self.lock_work_dataset(dsid) < 1: continue
|
|
724
801
|
qinfo['dslocks'].append(dsid)
|
|
725
802
|
for bid in bfiles[dsid]:
|
|
726
803
|
self.check_batch_deadline()
|
|
@@ -1258,6 +1335,21 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1258
1335
|
s = 's' if mcnt > 1 else ''
|
|
1259
1336
|
ftype = 'Saved' if fcate == 's' else 'Web'
|
|
1260
1337
|
self.pglog("{}: {} of {} {} file{} missing from storage, not backed up".format(dsid, mcnt, fcnt, ftype, s), self.LOGACT)
|
|
1338
|
+
if self.PGBACK['pstep']: self.drop_dscheck_files(mcnt)
|
|
1339
|
+
|
|
1340
|
+
# take files found missing from storage out of the dscheck file count. they are counted
|
|
1341
|
+
# in at start, before any file is checked, but are never tarred and so never reach
|
|
1342
|
+
# dcount: left in, they cap the done fraction below 100% (8,308 of 21,620 files once,
|
|
1343
|
+
# 38%), understate the progress report and make the stall test add a worker the run
|
|
1344
|
+
# does not need. with an upload action every file was counted twice, once to tar and
|
|
1345
|
+
# once to upload, so twice as many come out
|
|
1346
|
+
def drop_dscheck_files(self, mcnt):
|
|
1347
|
+
fcnt = self.DSCHK['fcount'] if 'fcount' in self.DSCHK else 0
|
|
1348
|
+
if not fcnt: return
|
|
1349
|
+
if self.PGBACK['action']&self.BCKACT: mcnt *= 2
|
|
1350
|
+
fcnt -= mcnt
|
|
1351
|
+
if fcnt < self.PGBACK['dcnt']: fcnt = self.PGBACK['dcnt']
|
|
1352
|
+
self.set_dscheck_fcount(fcnt, self.LGEREX)
|
|
1261
1353
|
|
|
1262
1354
|
# initialize dataset backup dict structure
|
|
1263
1355
|
@staticmethod
|
|
@@ -1497,11 +1589,20 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1497
1589
|
if self.pgget_wfile(dsid, 'wid', "bid " + bcnd, self.LGWNEX): fopt |= self.WOPT
|
|
1498
1590
|
return fopt
|
|
1499
1591
|
|
|
1500
|
-
# dataset ordering for gathering work
|
|
1501
|
-
#
|
|
1502
|
-
#
|
|
1503
|
-
|
|
1504
|
-
|
|
1592
|
+
# dataset ordering for gathering work. an odd worker slot starts on the datasets on the
|
|
1593
|
+
# object store and an even one on the datasets on Glade: an Object(O) dataset has every
|
|
1594
|
+
# file staged down from Boreas and checksummed before it can be tarred, while a Glade
|
|
1595
|
+
# one is only stat'ed, so the slow downloads get the most time from the first worker
|
|
1596
|
+
# while an added second worker clears the fast Glade work. an even slot walks the exact
|
|
1597
|
+
# reverse of an odd one (Glade from d999999 down, then the object store from d999999
|
|
1598
|
+
# down), so that a pair of workers meets in the middle instead of fighting over the same
|
|
1599
|
+
# dataset locks. dataset.locflag is matched as "O" / "not O" (a NULL locflag counts as
|
|
1600
|
+
# Glade), the same split -L uses. bfile has no locflag, so its records are matched
|
|
1601
|
+
# through their dataset
|
|
1602
|
+
def dsid_order(self, bfile = False):
|
|
1603
|
+
ocnd = "dsid IN (SELECT dsid FROM dataset WHERE locflag = 'O')" if bfile else "locflag = 'O'"
|
|
1604
|
+
dord = " DESC" if self.reverse_dsids() else ""
|
|
1605
|
+
return " ORDER BY CASE WHEN {} THEN 0 ELSE 1 END{}, dsid{}".format(ocnd, dord, dord)
|
|
1505
1606
|
|
|
1506
1607
|
# whether this worker slot walks the datasets from the high end
|
|
1507
1608
|
def reverse_dsids(self):
|
|
@@ -1512,11 +1613,9 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1512
1613
|
fcnt = 0
|
|
1513
1614
|
# warn per named dataset with no files; stay silent when scanning all datasets
|
|
1514
1615
|
logact = self.LOGWRN if self.dsids else 0
|
|
1515
|
-
flds = "dsid, backflag, pid, lockhost"
|
|
1516
|
-
# -L
|
|
1517
|
-
#
|
|
1518
|
-
# one is only stat'ed, so the two classes run at wildly different rates and are better
|
|
1519
|
-
# off in separate runs than sharing one walltime.
|
|
1616
|
+
flds = "dsid, backflag, locflag, pid, lockhost"
|
|
1617
|
+
# -L limits the run to where the data sits. it is optional: without it an odd worker
|
|
1618
|
+
# slot starts on the object store datasets and an even one on Glade (see dsid_order).
|
|
1520
1619
|
# G is matched as "not O" rather than "= G" on purpose: dataset.locflag is only a
|
|
1521
1620
|
# summary of the per-file flags (set_dataset_locflag writes it only when every public
|
|
1522
1621
|
# web file agrees, and leaves the old value otherwise), so it can hold a stale value.
|
|
@@ -1557,7 +1656,14 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1557
1656
|
if lstat < 0:
|
|
1558
1657
|
self.pglog("{}: skipped, still in use by {}/{}".format(dsid, pgrec['pid'], pgrec['lockhost']), self.LOGACT)
|
|
1559
1658
|
continue
|
|
1560
|
-
|
|
1659
|
+
if sizes is None:
|
|
1660
|
+
fcnt += self.get_dataset_files(dsid, dsfiles, pgrec['backflag'], logact)
|
|
1661
|
+
else:
|
|
1662
|
+
# sizes holds the bytes ready on Glade and on the object store apart, since a
|
|
1663
|
+
# tar file of each takes a very different time to build (see MPOLIMIT)
|
|
1664
|
+
dsizes = [0]
|
|
1665
|
+
fcnt += self.get_dataset_files(dsid, dsfiles, pgrec['backflag'], logact, dsizes)
|
|
1666
|
+
sizes[1 if pgrec['locflag'] == 'O' else 0] += dsizes[0]
|
|
1561
1667
|
if dsfiles:
|
|
1562
1668
|
s = 's' if fcnt > 1 else ''
|
|
1563
1669
|
bmsg = self.BACKMSG[self.PGBACK['backflag']] if self.PGBACK['backflag'] else 'backup'
|
|
@@ -1571,10 +1677,11 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1571
1677
|
bcnd = "status = 'N'"
|
|
1572
1678
|
flds = "bid, dsid, bfile, type, data_size size, (scount + wcount) fcnt, dsids, note"
|
|
1573
1679
|
if self.PGBACK['backflag']: bcnd += " AND type = '{}'".format(self.PGBACK['backflag'])
|
|
1680
|
+
bcnd += self.bfile_locflag_condition()
|
|
1574
1681
|
if self.dsids:
|
|
1575
1682
|
cnds = ["dsid = '{}' AND {}".format(dsid, bcnd) for dsid in self.dsids]
|
|
1576
1683
|
else:
|
|
1577
|
-
cnds = [bcnd + self.dsid_order()]
|
|
1684
|
+
cnds = [bcnd + self.dsid_order(True)]
|
|
1578
1685
|
for dcnd in cnds:
|
|
1579
1686
|
pgrecs = self.pgmget("bfile", flds, dcnd, self.LGWNEX)
|
|
1580
1687
|
bcnt = len(pgrecs['bid']) if pgrecs else 0
|
|
@@ -1597,10 +1704,11 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1597
1704
|
bcnd = "status = 'T'"
|
|
1598
1705
|
flds = "bid, dsid, bfile, type, data_size size, (scount + wcount) fcnt, note"
|
|
1599
1706
|
if self.PGBACK['backflag']: bcnd += " AND type = '{}'".format(self.PGBACK['backflag'])
|
|
1707
|
+
bcnd += self.bfile_locflag_condition()
|
|
1600
1708
|
if self.dsids:
|
|
1601
1709
|
cnds = ["dsid = '{}' AND {}".format(dsid, bcnd) for dsid in self.dsids]
|
|
1602
1710
|
else:
|
|
1603
|
-
cnds = [bcnd + self.dsid_order()]
|
|
1711
|
+
cnds = [bcnd + self.dsid_order(True)]
|
|
1604
1712
|
for dcnd in cnds:
|
|
1605
1713
|
pgrecs = self.pgmget("bfile", flds, dcnd, self.LGWNEX)
|
|
1606
1714
|
bcnt = len(pgrecs['bid']) if pgrecs else 0
|
{rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/dsquasar.usg
RENAMED
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
store, G takes everything else. Applies to -a and to -t alike. The
|
|
30
30
|
two classes back up at very different rates: an O dataset has every
|
|
31
31
|
file staged down from the object store and checksummed before it can
|
|
32
|
-
be tarred, while a G dataset is only stat'ed.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
all datasets.
|
|
32
|
+
be tarred, while a G dataset is only stat'ed. -L is optional: without
|
|
33
|
+
it the first worker (and a run without -w) starts on the O datasets,
|
|
34
|
+
so the slow downloads get the most time, and a second worker (-w 2)
|
|
35
|
+
starts on the G datasets. Defaults to all datasets.
|
|
36
36
|
|
|
37
37
|
-L G and -L O together always cover every dataset, so a pair of runs
|
|
38
38
|
loses nothing. The split is by dataset, though, and dataset.locflag
|
|
@@ -97,9 +97,11 @@
|
|
|
97
97
|
-m ProcessCount
|
|
98
98
|
Number of concurrent child processes. Defaults to 1. When -m is not
|
|
99
99
|
given, a delayed backup run sizes the count from the number of tar
|
|
100
|
-
files it expects to handle
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
files it expects to handle (one process per 200 tar files on Glade
|
|
101
|
+
and per 20 on the object store, whose files are downloaded first, up
|
|
102
|
+
to 12) and reserves matching PBS cpus, so in delay mode the process
|
|
103
|
+
count normally travels through the reserved cpus rather than the
|
|
104
|
+
command line. An explicit -m overrides that sizing,
|
|
103
105
|
and -m 1 forces a single process.
|
|
104
106
|
|
|
105
107
|
-n Gather and show the available file counts only; no backup actions are
|
|
@@ -108,11 +110,12 @@
|
|
|
108
110
|
-W MaxWorkers
|
|
109
111
|
Maximum number of concurrent batch workers for the same command.
|
|
110
112
|
Defaults to 2. A repeat delayed submit normally blocks while the
|
|
111
|
-
batch job is running; if that job has run for more than
|
|
112
|
-
|
|
113
|
-
less than
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
batch job is running; if that job has run for more than 1 hour and
|
|
114
|
+
at its rate so far would not finish its recorded work within about
|
|
115
|
+
two thirds of the walltime (less than 150% of it by the walltime),
|
|
116
|
+
or has run for more than 2 hours with nothing recorded done yet, it
|
|
117
|
+
is treated as stalled and an extra worker is submitted instead, up
|
|
118
|
+
to this limit. Extra workers rely on dataset locking to stay off each
|
|
116
119
|
other's files, so this is pinned to 1 with -l N, and also for the
|
|
117
120
|
transfer action (-A 4, 6, 7), which claims tar files without locking.
|
|
118
121
|
|
{rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26/src/rda_python_dsquasar.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_dsquasar
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.26
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/__init__.py
RENAMED
|
File without changes
|
{rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/ds_quasar.py
RENAMED
|
File without changes
|
|
File without changes
|
{rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/taccrec.py
RENAMED
|
File without changes
|
{rda_python_dsquasar-3.0.24 → rda_python_dsquasar-3.0.26}/src/rda_python_dsquasar/tacctar.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|