rda-python-dsquasar 3.0.22__tar.gz → 3.0.24__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.22/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.24}/PKG-INFO +1 -1
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/pyproject.toml +1 -1
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/dsquasar.py +48 -10
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/dsquasar.usg +3 -2
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/dstacc.py +39 -6
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24/src/rda_python_dsquasar.egg-info}/PKG-INFO +1 -1
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/LICENSE +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/MANIFEST.in +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/README.md +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/setup.cfg +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/__init__.py +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/ds_quasar.py +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/taccrec.py +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/tacctar.py +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar.egg-info/SOURCES.txt +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar.egg-info/dependency_links.txt +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar.egg-info/entry_points.txt +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar.egg-info/requires.txt +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar.egg-info/top_level.txt +0 -0
- {rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/test/test_dsquasar.py +0 -0
{rda_python_dsquasar-3.0.22/src/rda_python_dsquasar.egg-info → rda_python_dsquasar-3.0.24}/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.24
|
|
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.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/dsquasar.py
RENAMED
|
@@ -83,6 +83,8 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
83
83
|
self.TARSIZE = 5*self.PGLOG['ONEGBS'] # 5GB, tarfile size limit
|
|
84
84
|
self.MINSIZE = self.PGLOG['TWOGBS'] # 2GB, minimal tarfile size
|
|
85
85
|
self.ONESIZE = 20*self.PGLOG['ONEGBS'] # 20GB, minimal file size to tar a single file
|
|
86
|
+
self.MISSLOG = 10 # files named in the log per dataset when missing from storage
|
|
87
|
+
self.MISSCNT = 0 # files of the current dataset missing from storage
|
|
86
88
|
self.TFCOUNT = 100 # if file count is greater, use self.MINSIZE for tar file
|
|
87
89
|
self.SUBLMTS = 2000 # file count limit for a sub-group
|
|
88
90
|
# tarring (TARACT) forks one child per tar file, so its parallel unit is the tar
|
|
@@ -105,7 +107,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
105
107
|
# dscheck work done, which is action independent because dcount counts GDEX files
|
|
106
108
|
# for -A 3 but tar files for -A 2/-A 4.
|
|
107
109
|
self.MAXWORKERS = 2 # default maximum concurrent workers per command (-W)
|
|
108
|
-
self.WORKGRACE =
|
|
110
|
+
self.WORKGRACE = 2*3600 # 2 hours before a running job is judged on progress
|
|
109
111
|
self.MINWPROJ = 0.9 # projected done fraction at the walltime that counts as finishing
|
|
110
112
|
self.PGBACK = {
|
|
111
113
|
'workdir' : "{}/{}/quasar_backup".format(self.PGLOG['GDEXWORK'], self.PGLOG['COMMONUSER']),
|
|
@@ -431,8 +433,9 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
431
433
|
dargv = dargv[0:100]
|
|
432
434
|
return self.get_dscheck("dsquasar", dargv, self.PGBACK['workdir'], self.PGLOG['CURUID'], dargx, self.LOGWRN)
|
|
433
435
|
|
|
434
|
-
# a running batch job counts as stalled once it has run longer than WORKGRACE
|
|
435
|
-
# progress so far projects to less than MINWPROJ of
|
|
436
|
+
# a running batch job counts as stalled once it has run longer than WORKGRACE, has
|
|
437
|
+
# recorded some work done, and its progress so far projects to less than MINWPROJ of
|
|
438
|
+
# its recorded work by the walltime:
|
|
436
439
|
# such a job cannot finish this attempt however long it is left alone, so an extra
|
|
437
440
|
# worker is what helps it, not more retries.
|
|
438
441
|
# the test is on the RATE, not on an absolute done fraction as it first was. a fixed
|
|
@@ -447,11 +450,15 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
447
450
|
# runs this and set_walltime_deadline reads the granted walltime back in the batch job.
|
|
448
451
|
# when the daemon caps it to a shorter queue limit the projection is optimistic, which
|
|
449
452
|
# errs towards leaving the job alone.
|
|
453
|
+
# a job that has recorded nothing done is still in its gather phases, which read as a
|
|
454
|
+
# zero rate; it is left for the next submit rather than judged. that guard is what lets
|
|
455
|
+
# WORKGRACE stay short: an extra worker can only be added by a cron submit, every 4
|
|
456
|
+
# hours, so a 6 hour grace kept it out of the first 6 to 10 hours of every attempt.
|
|
450
457
|
def worker_progress_stalled(self, pgrec):
|
|
451
458
|
if not (pgrec['pid'] and pgrec['stttime']): return False
|
|
452
459
|
if self.check_host_pid(pgrec['lockhost'], pgrec['pid']) <= 0: return False
|
|
453
460
|
elapsed = tm() - pgrec['stttime']
|
|
454
|
-
if elapsed < self.WORKGRACE: return False
|
|
461
|
+
if elapsed < self.WORKGRACE or not pgrec['dcount']: return False
|
|
455
462
|
done = pgrec['dcount']/pgrec['fcount'] if pgrec['fcount'] else 0
|
|
456
463
|
return (done*self.WALLTIME/elapsed) < self.MINWPROJ
|
|
457
464
|
|
|
@@ -1220,6 +1227,7 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1220
1227
|
fd = None
|
|
1221
1228
|
fcate = filetype.lower()
|
|
1222
1229
|
tname = fcate + 'file'
|
|
1230
|
+
self.MISSCNT = 0 # files missing from their storage location
|
|
1223
1231
|
for i in range(fcnt):
|
|
1224
1232
|
self.check_batch_deadline()
|
|
1225
1233
|
pgrec = recs[i]
|
|
@@ -1244,6 +1252,12 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1244
1252
|
fd = None
|
|
1245
1253
|
self.process_one_backup_file(qinfo, True, (fcnt-i) > 1)
|
|
1246
1254
|
if fd: fd.close()
|
|
1255
|
+
mcnt = self.MISSCNT
|
|
1256
|
+
if mcnt:
|
|
1257
|
+
# one line per dataset reaches the email; the first file names are in the log
|
|
1258
|
+
s = 's' if mcnt > 1 else ''
|
|
1259
|
+
ftype = 'Saved' if fcate == 's' else 'Web'
|
|
1260
|
+
self.pglog("{}: {} of {} {} file{} missing from storage, not backed up".format(dsid, mcnt, fcnt, ftype, s), self.LOGACT)
|
|
1247
1261
|
|
|
1248
1262
|
# initialize dataset backup dict structure
|
|
1249
1263
|
@staticmethod
|
|
@@ -1388,7 +1402,21 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1388
1402
|
rcnt += 1
|
|
1389
1403
|
return rcnt
|
|
1390
1404
|
|
|
1391
|
-
#
|
|
1405
|
+
# log a GDEX file whose record waits for backup but whose data is not where its
|
|
1406
|
+
# locflag says. the check functions return None for a missing file without logging
|
|
1407
|
+
# it, so such a file used to be skipped silently, run after run. only the first
|
|
1408
|
+
# MISSLOG files of a dataset are named, so a dataset missing thousands of files does
|
|
1409
|
+
# not flood the log every run. returns None, which tells the caller the file is
|
|
1410
|
+
# missing rather than failed
|
|
1411
|
+
def missing_file_stat(self, dsid, cate, fname, path):
|
|
1412
|
+
self.MISSCNT += 1
|
|
1413
|
+
if self.MISSCNT <= self.MISSLOG:
|
|
1414
|
+
self.pglog("{}-{}-{}: Missing {}".format(dsid, cate.upper(), fname, path), self.LOGWRN)
|
|
1415
|
+
return None
|
|
1416
|
+
|
|
1417
|
+
# evaluate file stat on disk/object storage and modify RDADB record if not match.
|
|
1418
|
+
# returns True to back the file up, False when it cannot be checked, and None
|
|
1419
|
+
# when it is missing from its storage location
|
|
1392
1420
|
def evaluate_file_stat(self, dsid, cate, pgrec):
|
|
1393
1421
|
tname = cate + 'file'
|
|
1394
1422
|
fname = pgrec[tname]
|
|
@@ -1397,18 +1425,28 @@ class DsQuasar(PgCMD, PgSplit):
|
|
|
1397
1425
|
bucket = 'gdex-decsdata' if cate == 's' else self.PGLOG['OBJCTBKT']
|
|
1398
1426
|
ofile = self.join_paths(dsid, fname)
|
|
1399
1427
|
info = self.check_object_file(ofile, bucket, 1, self.ERRACT)
|
|
1428
|
+
if info is None: return self.missing_file_stat(dsid, cate, fname, "{}:{}".format(bucket, ofile))
|
|
1400
1429
|
if not info: return False
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
if 'checksum'
|
|
1430
|
+
# an object still matching its record on size and time, with its checksum
|
|
1431
|
+
# recorded already, has nothing to evaluate: downloading it only to md5 it
|
|
1432
|
+
# again costs a transfer per file, and dsarch downloads it anyway to tar it.
|
|
1433
|
+
# a directory key such as a zarr store has no checksum, so it is always fetched
|
|
1434
|
+
if not (info['isfile'] and pgrec['checksum'] and info['data_size'] == pgrec['data_size'] and
|
|
1435
|
+
self.cmptime(info['date_modified'], info['time_modified'],
|
|
1436
|
+
pgrec['date_modified'], pgrec['time_modified']) == 0):
|
|
1437
|
+
lfile = "{}/{}/{}/{}".format(chome, self.PGLOG['BACKUPEP'], dsid, fname)
|
|
1438
|
+
# pass info on so the object is not listed a second time
|
|
1439
|
+
if not self.object_copy_local(lfile, ofile, bucket, self.ERRACT, info): return False
|
|
1440
|
+
cinfo = self.check_local_file(lfile, 32, self.ERRACT)
|
|
1441
|
+
if not cinfo: return False
|
|
1442
|
+
if 'checksum' in cinfo: info['checksum'] = cinfo['checksum']
|
|
1406
1443
|
lname = 'OBS'
|
|
1407
1444
|
else:
|
|
1408
1445
|
lfile = "{}/{}/".format(chome, dsid)
|
|
1409
1446
|
if cate == 's': lfile += pgrec['type'] + '/'
|
|
1410
1447
|
lfile += fname
|
|
1411
1448
|
info = self.check_local_file(lfile, 33, self.ERRACT)
|
|
1449
|
+
if info is None: return self.missing_file_stat(dsid, cate, fname, lfile)
|
|
1412
1450
|
if not info: return False
|
|
1413
1451
|
lname = 'DSK'
|
|
1414
1452
|
record = {}
|
{rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/dsquasar.usg
RENAMED
|
@@ -108,8 +108,9 @@
|
|
|
108
108
|
-W MaxWorkers
|
|
109
109
|
Maximum number of concurrent batch workers for the same command.
|
|
110
110
|
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
|
-
has
|
|
111
|
+
batch job is running; if that job has run for more than 2 hours,
|
|
112
|
+
has recorded some work done, and at its rate so far would finish
|
|
113
|
+
less than 90% of its recorded work within the walltime, it is
|
|
113
114
|
treated as stalled and an extra worker is submitted instead, up to
|
|
114
115
|
this limit. Extra workers rely on dataset locking to stay off each
|
|
115
116
|
other's files, so this is pinned to 1 with -l N, and also for the
|
|
@@ -79,6 +79,8 @@ class DsTACC(PgCMD, PgSplit):
|
|
|
79
79
|
self.TARSIZE = 1024*self.PGLOG['ONEGBS'] # 1TB, tarfile size limit
|
|
80
80
|
self.MINSIZE = 512*self.PGLOG['ONEGBS'] # .5TB, minimal tarfile size
|
|
81
81
|
self.ONESIZE = 20*self.PGLOG['ONEGBS'] # 20GB, minimal file size to tar a single file
|
|
82
|
+
self.MISSLOG = 10 # files named in the log per dataset when missing from storage
|
|
83
|
+
self.MISSCNT = 0 # files of the current dataset missing from storage
|
|
82
84
|
self.TFCOUNT = 1000000 # if file count is greater, use self.MINSIZE for tar file
|
|
83
85
|
self.PGBACK = {
|
|
84
86
|
'workdir' : "{}/{}/tacc_backup".format(self.PGLOG['GDEXWORK'], self.PGLOG['COMMONUSER']),
|
|
@@ -731,6 +733,7 @@ class DsTACC(PgCMD, PgSplit):
|
|
|
731
733
|
fd = None
|
|
732
734
|
fcate = filetype.lower()
|
|
733
735
|
tname = fcate + 'file'
|
|
736
|
+
self.MISSCNT = 0 # files missing from their storage location
|
|
734
737
|
for i in range(fcnt):
|
|
735
738
|
pgrec = recs[i]
|
|
736
739
|
if not self.evaluate_file_stat(dsid, fcate, pgrec): continue
|
|
@@ -754,6 +757,12 @@ class DsTACC(PgCMD, PgSplit):
|
|
|
754
757
|
fd = None
|
|
755
758
|
self.process_one_backup_file(qinfo, True, (fcnt-i) > 1)
|
|
756
759
|
if fd: fd.close()
|
|
760
|
+
mcnt = self.MISSCNT
|
|
761
|
+
if mcnt:
|
|
762
|
+
# one line per dataset reaches the email; the first file names are in the log
|
|
763
|
+
s = 's' if mcnt > 1 else ''
|
|
764
|
+
ftype = 'Saved' if fcate == 's' else 'Web'
|
|
765
|
+
self.pglog("{}: {} of {} {} file{} missing from storage, not backed up".format(dsid, mcnt, fcnt, ftype, s), self.LOGACT)
|
|
757
766
|
|
|
758
767
|
# initialize dataset backup dict structure
|
|
759
768
|
@staticmethod
|
|
@@ -851,7 +860,21 @@ class DsTACC(PgCMD, PgSplit):
|
|
|
851
860
|
rcnt += 1
|
|
852
861
|
return rcnt
|
|
853
862
|
|
|
854
|
-
#
|
|
863
|
+
# log a GDEX file whose record waits for backup but whose data is not where its
|
|
864
|
+
# locflag says. the check functions return None for a missing file without logging
|
|
865
|
+
# it, so such a file used to be skipped silently, run after run. only the first
|
|
866
|
+
# MISSLOG files of a dataset are named, so a dataset missing thousands of files does
|
|
867
|
+
# not flood the log every run. returns None, which tells the caller the file is
|
|
868
|
+
# missing rather than failed
|
|
869
|
+
def missing_file_stat(self, dsid, cate, fname, path):
|
|
870
|
+
self.MISSCNT += 1
|
|
871
|
+
if self.MISSCNT <= self.MISSLOG:
|
|
872
|
+
self.pglog("{}-{}-{}: Missing {}".format(dsid, cate.upper(), fname, path), self.LOGWRN)
|
|
873
|
+
return None
|
|
874
|
+
|
|
875
|
+
# evaluate file stat on disk/object storage and modify RDADB record if not match.
|
|
876
|
+
# returns True to back the file up, False when it cannot be checked, and None
|
|
877
|
+
# when it is missing from its storage location
|
|
855
878
|
def evaluate_file_stat(self, dsid, cate, pgrec):
|
|
856
879
|
tname = cate + 'file'
|
|
857
880
|
fname = pgrec[tname]
|
|
@@ -860,18 +883,28 @@ class DsTACC(PgCMD, PgSplit):
|
|
|
860
883
|
bucket = 'gdex-decsdata' if cate == 's' else self.PGLOG['OBJCTBKT']
|
|
861
884
|
ofile = self.join_paths(dsid, fname)
|
|
862
885
|
info = self.check_object_file(ofile, bucket, 1, self.ERRACT)
|
|
886
|
+
if info is None: return self.missing_file_stat(dsid, cate, fname, "{}:{}".format(bucket, ofile))
|
|
863
887
|
if not info: return False
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
if 'checksum'
|
|
888
|
+
# an object still matching its record on size and time, with its checksum
|
|
889
|
+
# recorded already, has nothing to evaluate: downloading it only to md5 it
|
|
890
|
+
# again costs a transfer per file, and dsarch downloads it anyway to tar it.
|
|
891
|
+
# a directory key such as a zarr store has no checksum, so it is always fetched
|
|
892
|
+
if not (info['isfile'] and pgrec['checksum'] and info['data_size'] == pgrec['data_size'] and
|
|
893
|
+
self.cmptime(info['date_modified'], info['time_modified'],
|
|
894
|
+
pgrec['date_modified'], pgrec['time_modified']) == 0):
|
|
895
|
+
lfile = "{}/{}/{}/{}".format(chome, self.PGLOG['BACKUPEP'], dsid, fname)
|
|
896
|
+
# pass info on so the object is not listed a second time
|
|
897
|
+
if not self.object_copy_local(lfile, ofile, bucket, self.ERRACT, info): return False
|
|
898
|
+
cinfo = self.check_local_file(lfile, 32, self.ERRACT)
|
|
899
|
+
if not cinfo: return False
|
|
900
|
+
if 'checksum' in cinfo: info['checksum'] = cinfo['checksum']
|
|
869
901
|
lname = 'OBS'
|
|
870
902
|
else:
|
|
871
903
|
lfile = "{}/{}/".format(chome, dsid)
|
|
872
904
|
if cate == 's': lfile += pgrec['type'] + '/'
|
|
873
905
|
lfile += fname
|
|
874
906
|
info = self.check_local_file(lfile, 33, self.ERRACT)
|
|
907
|
+
if info is None: return self.missing_file_stat(dsid, cate, fname, lfile)
|
|
875
908
|
if not info: return False
|
|
876
909
|
lname = 'DSK'
|
|
877
910
|
record = {}
|
{rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24/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.24
|
|
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.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/__init__.py
RENAMED
|
File without changes
|
{rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/ds_quasar.py
RENAMED
|
File without changes
|
{rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/src/rda_python_dsquasar/taccrec.py
RENAMED
|
File without changes
|
{rda_python_dsquasar-3.0.22 → rda_python_dsquasar-3.0.24}/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
|