Skip to content

Commit c4d527e

Browse files
committed
feat: getPilotInfo use temporary table
1 parent d5449c1 commit c4d527e

1 file changed

Lines changed: 69 additions & 17 deletions

File tree

src/DIRAC/WorkloadManagementSystem/DB/PilotAgentsDB.py

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ class PilotAgentsDB(DB):
4141
"VO",
4242
}
4343

44+
# Number of values inserted into the in-memory temp tables per batch in
45+
# getPilotInfo. Keeping batches bounded avoids overflowing MEMORY tables
46+
# (ERROR 1114: The table is full) when querying large pilot reference lists.
47+
PILOT_INFO_BATCH_SIZE = 10000
48+
4449
def __init__(self, parentLogger=None):
4550
super().__init__("PilotAgentsDB", "WorkloadManagement/PilotAgentsDB", parentLogger=parentLogger)
4651
self._defaultLogger = self.log
@@ -286,23 +291,70 @@ def getPilotInfo(self, pilotRef=False, conn=False, paramNames=[], pilotID=False)
286291
return S_ERROR(f"Unknown column: {col}")
287292

288293
cmd = f"SELECT {', '.join(parameters)} FROM PilotAgents" # nosec
289-
condSQL = []
290-
for key, value in [
291-
("PilotJobReference", pilotRef),
292-
("PilotID", pilotID),
293-
]:
294-
resList = []
295-
for v in value if isinstance(value, list) else [value] if value else []:
296-
result = self._escapeString(v)
297-
if not result["OK"]:
298-
return result
299-
resList.append(result["Value"])
300-
if resList:
301-
condSQL.append(f"{key} IN ({','.join(resList)})")
302-
if condSQL:
303-
cmd = f"{cmd} WHERE {' AND '.join(condSQL)}"
304-
305-
result = self._query(cmd, conn=conn)
294+
tempTables = []
295+
activeKeys = []
296+
result = None
297+
try:
298+
for key, value in [("PilotJobReference", pilotRef), ("PilotID", pilotID)]:
299+
values = []
300+
for v in value if isinstance(value, list) else [value] if value else []:
301+
values.append(v)
302+
if not values:
303+
continue
304+
tableName = f"to_select_PilotAgents_{key}"
305+
if key == "PilotID":
306+
sqlCmd = (
307+
f"CREATE TEMPORARY TABLE {tableName}"
308+
f" (PilotID INT UNSIGNED NOT NULL, PRIMARY KEY (PilotID)) ENGINE=MEMORY;"
309+
)
310+
insertValues = [(int(v),) for v in values]
311+
else:
312+
sqlCmd = (
313+
f"CREATE TEMPORARY TABLE {tableName}"
314+
f" (PilotJobReference VARCHAR(255) NOT NULL, PRIMARY KEY (PilotJobReference)) ENGINE=MEMORY;"
315+
)
316+
insertValues = [(v,) for v in values]
317+
returnValueOrRaise(self._update(sqlCmd, conn=conn))
318+
tempTables.append(tableName)
319+
activeKeys.append((key, tableName, insertValues))
320+
321+
joinSQL = [f"JOIN {tn} USING ({k})" for k, tn, _ in activeKeys]
322+
selectCmd = f"{cmd} {' '.join(joinSQL)}" if joinSQL else cmd
323+
324+
if not activeKeys:
325+
# No conditions: plain SELECT over the whole table.
326+
result = self._query(selectCmd, conn=conn)
327+
else:
328+
# Load every condition key fully except the last one, which is
329+
# processed batch by batch (truncating between batches) so the
330+
# in-memory temp table never holds all the rows at once. This
331+
# preserves the AND semantics: the union of the per-batch
332+
# results equals the full JOIN result.
333+
for key, tableName, insertValues in activeKeys[:-1]:
334+
insertCmd = f"INSERT INTO {tableName} ({key}) VALUES ( %s )" # nosec: B608
335+
for i in range(0, len(insertValues), self.PILOT_INFO_BATCH_SIZE):
336+
returnValueOrRaise(
337+
self._updatemany(insertCmd, insertValues[i : i + self.PILOT_INFO_BATCH_SIZE], conn=conn)
338+
)
339+
batchKey, batchTable, batchValues = activeKeys[-1]
340+
insertCmd = f"INSERT INTO {batchTable} ({batchKey}) VALUES ( %s )" # nosec: B608
341+
resultRows = []
342+
for i in range(0, len(batchValues), self.PILOT_INFO_BATCH_SIZE):
343+
returnValueOrRaise(self._update(f"TRUNCATE TABLE {batchTable}", conn=conn))
344+
returnValueOrRaise(
345+
self._updatemany(insertCmd, batchValues[i : i + self.PILOT_INFO_BATCH_SIZE], conn=conn)
346+
)
347+
batchResult = self._query(selectCmd, conn=conn)
348+
if not batchResult["OK"]:
349+
result = batchResult
350+
break
351+
resultRows.extend(batchResult["Value"])
352+
else:
353+
result = S_OK(resultRows)
354+
finally:
355+
for tableName in tempTables:
356+
sqlCmd = f"DROP TEMPORARY TABLE {tableName}"
357+
returnValueOrRaise(self._update(sqlCmd, conn=conn))
306358
if not result["OK"]:
307359
return result
308360
if not result["Value"]:

0 commit comments

Comments
 (0)