Skip to content

Commit 51fbf6c

Browse files
committed
Publish the libraries a few at a time
Once the tests are done, the run publishes one library at a time, and what that costs is not the copying but waiting for the far side: each library asks rsync four times over ssh, and the one that matters lists a remote directory of tens of thousands of files to honour `--delete-excluded`. Three things were paid for per library and are now paid for once: | What | Before | After | | ---------------------------------- | ------------ | ------------ | | git log of this repository | once per lib | once | | git log of the reference files | once per lib | once per dir | | rsync connections to make the dirs | 3 | 1 | The directories are made in one connection by telling rsync -R where the relative part of the source path starts, so `emptydir/./<lib>/files` creates the branch, the library and its files directory at once instead of one rsync per level. The libraries are then published four at a time. Not more: each one is an ssh connection of its own, and sshd allows ten unauthenticated connections by default. The loop body became a function, which also stops it shadowing the `stats` dictionary of the run with a list of its own. Assisted-by: Claude Opus 5
1 parent cd9e396 commit 51fbf6c

1 file changed

Lines changed: 85 additions & 67 deletions

File tree

‎test.py‎

Lines changed: 85 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,6 +1254,79 @@ def artifactSuffix(simulator):
12541254
publishes the workspace itself, so only the others have one."""
12551255
return "_%s" % simulator if simulator else ""
12561256

1257+
try:
1258+
githuburltesting = "https://github.com/OpenModelica/OpenModelicaLibraryTesting/commit/"
1259+
gitloglibrarytesting = check_output_log(["git", "log", '--pretty=<table><tr><th>Commit</th><th>Date</th><th>Author</th><th>Summary</th></tr><tr><td><a href="%s/%%h">%%h</a></td><td>%%ai</td><td>%%an</td><td>%%s</td></tr></table>' % (githuburltesting), "-1"], cwd="./").decode("utf-8")
1260+
except subprocess.CalledProcessError as e:
1261+
print(str(e))
1262+
gitloglibrarytesting = "<table><tr><td>could not get the git log for OpenModelicaLibraryTesting</td></tr></table>"
1263+
1264+
def readReferenceFilesVersion(c):
1265+
# adrpo: attempt to get the revision of the reference files if possible
1266+
if DEBUG:
1267+
print("referenceFiles git ... attempting to retrieve info from directory: %s" % c)
1268+
sys.stdout.flush()
1269+
gitReferenceFiles = c
1270+
# see if we have a commit file
1271+
f = os.path.join(c, "commit")
1272+
if os.path.exists(f):
1273+
with open(f) as fin:
1274+
print("referenceFiles git ... read from file %s" % f)
1275+
sys.stdout.flush()
1276+
return fin.read()
1277+
try:
1278+
if isinstance(c, (str, bytes)):
1279+
if DEBUG:
1280+
print("referenceFiles git ... see if directory has an evironment variable")
1281+
sys.stdout.flush()
1282+
m = re.search("^[$][A-Z_]+", c)
1283+
if m:
1284+
k = m.group(0)[1:]
1285+
if k not in os.environ:
1286+
if DEBUG:
1287+
print("referenceFiles git ... environment variable used in the directory cannot be found in the environment: %s" % k)
1288+
sys.stdout.flush()
1289+
raise Exception("Environment variable %s not defined, but used in JSON config for reference files" % k)
1290+
gitReferenceFiles = c.replace(m.group(0), os.environ[k])
1291+
if DEBUG:
1292+
print("referenceFiles git ... directory after replacing the environment variable: %s" % gitReferenceFiles)
1293+
sys.stdout.flush()
1294+
sys.stdout.flush()
1295+
try:
1296+
gitReferenceFilesURL = check_output_log(["git", "config", "--get", "remote.origin.url"], cwd=gitReferenceFiles).decode("utf-8")
1297+
except subprocess.CalledProcessError as e:
1298+
print(e)
1299+
gitReferenceFilesURL = gitReferenceFiles
1300+
gitReferenceFilesVersion = check_output_log(["git", "log", '--pretty=<table><tr><th>Commit</th><th>Date</th><th>Author</th><th>Summary</th></tr><tr><td><a href="%s/%%h">%%h</a></td><td>%%ai</td><td>%%an</td><td>%%s</td></tr></table>' % (gitReferenceFilesURL), "-1"], cwd=gitReferenceFiles).decode("utf-8")
1301+
print("referenceFiles git ... got version information: %s" % gitReferenceFilesVersion)
1302+
sys.stdout.flush()
1303+
return gitReferenceFilesVersion
1304+
except subprocess.CalledProcessError as e:
1305+
print("referenceFiles git ... something went wrong with getting the git info for directory: %s" % c)
1306+
print(str(e))
1307+
sys.stdout.flush()
1308+
return ""
1309+
1310+
referenceFilesVersions = {}
1311+
1312+
def referenceFilesVersion(c):
1313+
"""The git log of a directory of reference files.
1314+
1315+
Nearly every library shares the directory with another, and the git log of it
1316+
is the same for all of them, so it is asked for once per directory instead of
1317+
once per library.
1318+
"""
1319+
if not c:
1320+
return ""
1321+
if c not in referenceFilesVersions:
1322+
referenceFilesVersions[c] = readReferenceFilesVersion(c)
1323+
return referenceFilesVersions[c]
1324+
1325+
# Publishing one library is rsync waiting for the far side to enumerate a
1326+
# directory of tens of thousands of files, so the libraries are published a few
1327+
# at a time. Not many more: each one is an ssh connection of its own.
1328+
PUBLISH_JOBS = 4
1329+
12571330
# Every runner publishes its own results - .sim and diff files included - to the
12581331
# directory of its own branch; the .err of the build is shared, so each of them
12591332
# gets a copy of it.
@@ -1268,9 +1341,9 @@ def artifactSuffix(simulator):
12681341
os.makedirs(resRootPath)
12691342

12701343
htmltpl=open("library.html.tpl").read()
1271-
for libname in stats_by_libname.keys():
1344+
def publishLibrary(libname):
12721345
if libname in skipped_libs or not ranRunner(libname, runner):
1273-
continue
1346+
return
12741347
s = None # Make sure I don't use this
12751348
simulator = simulatorKey(libname, runner)
12761349
suffix = artifactSuffix(simulator)
@@ -1332,61 +1405,7 @@ def artifactSuffix(simulator):
13321405
for s in natsorted(stats, key=lambda s: s[1])])
13331406
numSucceeded = [len(stats)] + [sum(1 if s[3]["phase"]>=i else 0 for s in stats) for i in range(1,8)]
13341407

1335-
try:
1336-
githuburltesting = "https://github.com/OpenModelica/OpenModelicaLibraryTesting/commit/"
1337-
gitloglibrarytesting = check_output_log(["git", "log", '--pretty=<table><tr><th>Commit</th><th>Date</th><th>Author</th><th>Summary</th></tr><tr><td><a href="%s/%%h">%%h</a></td><td>%%ai</td><td>%%an</td><td>%%s</td></tr></table>' % (githuburltesting), "-1"], cwd="./").decode("utf-8")
1338-
except subprocess.CalledProcessError as e:
1339-
print(str(e))
1340-
gitloglibrarytesting = "<table><tr><td>could not get the git log for OpenModelicaLibraryTesting</td></tr></table>"
1341-
1342-
# adrpo: attempt to get the revision of the reference files if possible
1343-
if conf.get("referenceFiles"):
1344-
c = conf.get("referenceFiles")
1345-
if DEBUG:
1346-
print("referenceFiles git ... attempting to retrieve info from directory: %s" % c)
1347-
sys.stdout.flush()
1348-
gitReferenceFiles = c
1349-
# see if we have a commit file
1350-
f = os.path.join(c, "commit")
1351-
if os.path.exists(f):
1352-
with open(f) as fin:
1353-
gitReferenceFilesVersion = fin.read()
1354-
print("referenceFiles git ... read from file %s" % f)
1355-
sys.stdout.flush()
1356-
else:
1357-
try:
1358-
if isinstance(c, (str, bytes)):
1359-
if DEBUG:
1360-
print("referenceFiles git ... see if directory has an evironment variable")
1361-
sys.stdout.flush()
1362-
m = re.search("^[$][A-Z_]+", c)
1363-
if m:
1364-
k = m.group(0)[1:]
1365-
if k not in os.environ:
1366-
if DEBUG:
1367-
print("referenceFiles git ... environment variable used in the directory cannot be found in the environment: %s" % k)
1368-
sys.stdout.flush()
1369-
raise Exception("Environment variable %s not defined, but used in JSON config for reference files" % k)
1370-
gitReferenceFiles = c.replace(m.group(0), os.environ[k])
1371-
if DEBUG:
1372-
print("referenceFiles git ... directory after replacing the environment variable: %s" % gitReferenceFiles)
1373-
sys.stdout.flush()
1374-
sys.stdout.flush()
1375-
try:
1376-
gitReferenceFilesURL = check_output_log(["git", "config", "--get", "remote.origin.url"], cwd=gitReferenceFiles).decode("utf-8")
1377-
except subprocess.CalledProcessError as e:
1378-
print(e)
1379-
gitReferenceFilesURL = gitReferenceFiles
1380-
gitReferenceFilesVersion = check_output_log(["git", "log", '--pretty=<table><tr><th>Commit</th><th>Date</th><th>Author</th><th>Summary</th></tr><tr><td><a href="%s/%%h">%%h</a></td><td>%%ai</td><td>%%an</td><td>%%s</td></tr></table>' % (gitReferenceFilesURL), "-1"], cwd=gitReferenceFiles).decode("utf-8")
1381-
print("referenceFiles git ... got version information: %s" % gitReferenceFilesVersion)
1382-
sys.stdout.flush()
1383-
except subprocess.CalledProcessError as e:
1384-
print("referenceFiles git ... something went wrong with getting the git info for directory: %s" % c)
1385-
print(str(e))
1386-
sys.stdout.flush()
1387-
gitReferenceFilesVersion = ""
1388-
else:
1389-
gitReferenceFilesVersion = ""
1408+
gitReferenceFilesVersion = referenceFilesVersion(conf.get("referenceFiles"))
13901409

13911410
replacements = (
13921411
(u"#sysInfo#", html.escape(sysInfo)),
@@ -1433,19 +1452,16 @@ def artifactSuffix(simulator):
14331452
# move results by sync operations (not available under win)
14341453
if result_location != "" and not isWin and not noSync:
14351454
result_location_libname = "%s/%s" % (result_location, libname)
1436-
try:
1437-
os.mkdir(os.path.join(stageRoot, "emptydir"))
1438-
except:
1439-
pass
1440-
check_output_log(["rsync", "-aR", "emptydir/", result_location], cwd=stageRoot)
1441-
check_output_log(["rsync", "-aR", "emptydir/", result_location_libname], cwd=stageRoot)
1442-
check_output_log(["rsync", "-aR", "emptydir/", result_location_libname+"/files"], cwd=stageRoot)
1455+
def makeRemoteDirs():
1456+
# The /./ tells rsync -R where the relative part starts, so the branch,
1457+
# the library and its files directory are created in one connection.
1458+
os.makedirs(os.path.join(stageRoot, "emptydir", libname, "files"), exist_ok=True)
1459+
check_output_log(["rsync", "-aR", "emptydir/./%s/files" % libname, result_location], cwd=stageRoot)
1460+
makeRemoteDirs()
14431461
try:
14441462
check_output_log(["rsync", "-aR", "--delete-excluded", "--include-from=%s.files" % libname, "--exclude=*", "./", result_location_libname], cwd=stageRoot)
14451463
except:
1446-
check_output_log(["rsync", "-aR", "emptydir/", result_location], cwd=stageRoot)
1447-
check_output_log(["rsync", "-aR", "emptydir/", result_location_libname], cwd=stageRoot)
1448-
check_output_log(["rsync", "-aR", "emptydir/", result_location_libname+"/files"], cwd=stageRoot)
1464+
makeRemoteDirs()
14491465
check_output_log(["rsync", "-aR", "--delete-excluded", "--include-from=%s.files" % libname, "--exclude=*", "./", result_location_libname], cwd=stageRoot)
14501466
if (conf.get("referenceFiles") or "") != "" and dygraphs:
14511467
check_output_log(["rsync", "-a", dygraphs, result_location_libname+"/files"])
@@ -1482,6 +1498,8 @@ def artifactSuffix(simulator):
14821498
print("-- problem during file copy... maybe the file is still hooked by a process... :" + file)
14831499
pass
14841500

1501+
Parallel(n_jobs=PUBLISH_JOBS, backend="threading")(delayed(publishLibrary)(libname) for libname in stats_by_libname.keys())
1502+
14851503
if clean:
14861504
for g in ["*.o","*.so","*.h","*.c","*.cpp","*.simsuccess","*.conf.json","*.tmpfiles","*.log","*.libs","OMCpp*","*.fmu*","temp_*", "*.exe", "HelloWorld.bat", "*.makefile", "*.mat","*.xml", "*.bin", "*.json"]:
14871505
for f in glob.glob(g):

0 commit comments

Comments
 (0)