DIRAC 9.0.9__py3-none-any.whl → 9.0.11__py3-none-any.whl

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 (27) hide show
  1. DIRAC/Core/Utilities/ElasticSearchDB.py +1 -2
  2. DIRAC/DataManagementSystem/Client/DataManager.py +6 -7
  3. DIRAC/FrameworkSystem/DB/InstalledComponentsDB.py +1 -1
  4. DIRAC/FrameworkSystem/Utilities/MonitoringUtilities.py +1 -0
  5. DIRAC/Interfaces/Utilities/DConfigCache.py +2 -0
  6. DIRAC/Resources/Computing/BatchSystems/Condor.py +0 -3
  7. DIRAC/Resources/Computing/BatchSystems/executeBatch.py +15 -7
  8. DIRAC/Resources/Computing/LocalComputingElement.py +0 -2
  9. DIRAC/Resources/Computing/SSHComputingElement.py +61 -38
  10. DIRAC/TransformationSystem/Agent/InputDataAgent.py +4 -1
  11. DIRAC/TransformationSystem/Agent/MCExtensionAgent.py +5 -2
  12. DIRAC/TransformationSystem/Agent/TaskManagerAgentBase.py +3 -1
  13. DIRAC/TransformationSystem/Agent/TransformationCleaningAgent.py +44 -9
  14. DIRAC/TransformationSystem/Agent/ValidateOutputDataAgent.py +4 -2
  15. DIRAC/TransformationSystem/Client/TransformationClient.py +9 -1
  16. DIRAC/TransformationSystem/DB/TransformationDB.py +105 -43
  17. DIRAC/WorkloadManagementSystem/Agent/StalledJobAgent.py +39 -7
  18. DIRAC/WorkloadManagementSystem/Agent/test/Test_Agent_StalledJobAgent.py +24 -4
  19. DIRAC/WorkloadManagementSystem/DB/StatusUtils.py +48 -21
  20. DIRAC/WorkloadManagementSystem/DB/tests/Test_StatusUtils.py +19 -4
  21. DIRAC/WorkloadManagementSystem/Service/JobManagerHandler.py +25 -2
  22. {dirac-9.0.9.dist-info → dirac-9.0.11.dist-info}/METADATA +2 -2
  23. {dirac-9.0.9.dist-info → dirac-9.0.11.dist-info}/RECORD +27 -27
  24. {dirac-9.0.9.dist-info → dirac-9.0.11.dist-info}/WHEEL +0 -0
  25. {dirac-9.0.9.dist-info → dirac-9.0.11.dist-info}/entry_points.txt +0 -0
  26. {dirac-9.0.9.dist-info → dirac-9.0.11.dist-info}/licenses/LICENSE +0 -0
  27. {dirac-9.0.9.dist-info → dirac-9.0.11.dist-info}/top_level.txt +0 -0
@@ -82,7 +82,6 @@ def generateDocs(data, withTimeStamp=True):
82
82
 
83
83
 
84
84
  class ElasticSearchDB:
85
-
86
85
  """
87
86
  .. class:: ElasticSearchDB
88
87
 
@@ -506,7 +505,7 @@ class ElasticSearchDB:
506
505
  indexName = self.generateFullIndexName(indexPrefix, period)
507
506
  else:
508
507
  indexName = indexPrefix
509
- sLog.debug(f"Bulk indexing into {indexName} of {data}")
508
+ sLog.debug(f"Bulk indexing into {indexName} of {len(data)}")
510
509
 
511
510
  res = self.existingIndex(indexName)
512
511
  if not res["OK"]:
@@ -10,11 +10,11 @@ This module consists of DataManager and related classes.
10
10
  """
11
11
 
12
12
  # # imports
13
- from datetime import datetime, timedelta
13
+ import errno
14
14
  import fnmatch
15
15
  import os
16
16
  import time
17
- import errno
17
+ from datetime import datetime, timedelta
18
18
 
19
19
  # # from DIRAC
20
20
  import DIRAC
@@ -25,13 +25,13 @@ from DIRAC.Core.Utilities.File import makeGuid, getSize
25
25
  from DIRAC.Core.Utilities.List import randomize, breakListIntoChunks
26
26
  from DIRAC.Core.Utilities.ReturnValues import returnSingleResult
27
27
  from DIRAC.Core.Security.ProxyInfo import getProxyInfo
28
+ from DIRAC.Core.Security.ProxyInfo import getVOfromProxyGroup
28
29
  from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
29
30
  from DIRAC.DataManagementSystem.Client import MAX_FILENAME_LENGTH
30
- from DIRAC.MonitoringSystem.Client.DataOperationSender import DataOperationSender
31
31
  from DIRAC.DataManagementSystem.Utilities.DMSHelpers import DMSHelpers
32
+ from DIRAC.MonitoringSystem.Client.DataOperationSender import DataOperationSender
32
33
  from DIRAC.Resources.Catalog.FileCatalog import FileCatalog
33
34
  from DIRAC.Resources.Storage.StorageElement import StorageElement
34
- from DIRAC.ResourceStatusSystem.Client.ResourceStatus import ResourceStatus
35
35
 
36
36
 
37
37
  # # RSCID
@@ -89,7 +89,7 @@ class DataManager:
89
89
  :param vo: the VO for which the DataManager is created, get VO from the current proxy if not specified
90
90
  """
91
91
  self.log = gLogger.getSubLogger(self.__class__.__name__)
92
- self.voName = vo
92
+ self.voName = vo if vo else getVOfromProxyGroup().get("Value", None)
93
93
 
94
94
  if catalogs is None:
95
95
  catalogs = []
@@ -97,10 +97,9 @@ class DataManager:
97
97
 
98
98
  self.fileCatalog = FileCatalog(catalogs=catalogsToUse, vo=self.voName)
99
99
  self.accountingClient = None
100
- self.resourceStatus = ResourceStatus()
101
100
  self.ignoreMissingInFC = Operations(vo=self.voName).getValue("DataManagement/IgnoreMissingInFC", False)
102
101
  self.useCatalogPFN = Operations(vo=self.voName).getValue("DataManagement/UseCatalogPFN", True)
103
- self.dmsHelper = DMSHelpers(vo=vo)
102
+ self.dmsHelper = DMSHelpers(vo=self.voName)
104
103
  self.registrationProtocol = self.dmsHelper.getRegistrationProtocols()
105
104
  self.thirdPartyProtocols = self.dmsHelper.getThirdPartyProtocols()
106
105
  self.dataOpSender = DataOperationSender()
@@ -90,7 +90,7 @@ class Host(componentsBase):
90
90
  __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"}
91
91
 
92
92
  hostID = Column("HostID", Integer, primary_key=True)
93
- hostName = Column("HostName", String(32), nullable=False)
93
+ hostName = Column("HostName", String(64), nullable=False)
94
94
  cpu = Column("CPU", String(64), nullable=False)
95
95
  installationList = relationship("InstalledComponent", backref="installationHost")
96
96
 
@@ -30,6 +30,7 @@ def monitorInstallation(componentType, system, component, module=None, cpu=None,
30
30
 
31
31
  if not hostname:
32
32
  hostname = socket.getfqdn()
33
+ hostname = hostname[0:64]
33
34
  instance = component[0:32]
34
35
 
35
36
  result = monitoringClient.installationExists(
@@ -9,6 +9,7 @@ from DIRAC import gLogger
9
9
  from DIRAC.Core.Base.Script import Script
10
10
  from DIRAC.Core.Utilities.File import secureOpenForWrite
11
11
  from DIRAC.ConfigurationSystem.Client.ConfigurationData import gConfigurationData
12
+ from DIRAC.ConfigurationSystem.Client.Helpers.Registry import reset_all_caches
12
13
 
13
14
 
14
15
  class ConfigCache:
@@ -69,5 +70,6 @@ class ConfigCache:
69
70
  try:
70
71
  with open(self.configCacheName, "rb") as fh:
71
72
  gConfigurationData.mergedCFG = pickle.load(fh)
73
+ reset_all_caches()
72
74
  except:
73
75
  gLogger.error("Cache corrupt or unreadable")
@@ -203,9 +203,6 @@ class Condor(object):
203
203
  resultDict["Jobs"] = []
204
204
  for i in range(submittedJobs):
205
205
  resultDict["Jobs"].append(".".join([cluster, str(i)]))
206
- # Executable is transferred afterward
207
- # Inform the caller that Condor cannot delete it before the end of the execution
208
- resultDict["ExecutableToKeep"] = executable
209
206
  else:
210
207
  resultDict["Status"] = status
211
208
  resultDict["Message"] = error
@@ -35,8 +35,10 @@ if __name__ == "__main__":
35
35
  from urllib.parse import unquote as urlunquote
36
36
 
37
37
 
38
- arguments = sys.argv[1]
39
- inputDict = json.loads(urlunquote(arguments))
38
+ # Read options from JSON file
39
+ optionsFilePath = sys.argv[1]
40
+ with open(optionsFilePath, 'r') as f:
41
+ inputDict = json.load(f)
40
42
 
41
43
  method = inputDict.pop('Method')
42
44
  batchSystem = inputDict.pop('BatchSystem')
@@ -45,9 +47,15 @@ if __name__ == "__main__":
45
47
  try:
46
48
  result = getattr(batch, method)(**inputDict)
47
49
  except Exception:
48
- result = traceback.format_exc()
49
-
50
- resultJson = urlquote(json.dumps(result))
51
- print("============= Start output ===============")
52
- print(resultJson)
50
+ # Wrap the traceback in a proper error structure
51
+ result = {
52
+ 'Status': -1,
53
+ 'Message': 'Exception during batch method execution',
54
+ 'Traceback': traceback.format_exc()
55
+ }
56
+
57
+ # Write result to JSON file
58
+ resultFilePath = optionsFilePath.replace('.json', '_result.json')
59
+ with open(resultFilePath, 'w') as f:
60
+ json.dump(result, f)
53
61
  """
@@ -182,8 +182,6 @@ class LocalComputingElement(ComputingElement):
182
182
  batchSystemName = self.batchSystem.__class__.__name__.lower()
183
183
  jobIDs = ["ssh" + batchSystemName + "://" + self.ceName + "/" + _id for _id in resultSubmit["Jobs"]]
184
184
  result = S_OK(jobIDs)
185
- if "ExecutableToKeep" in resultSubmit:
186
- result["ExecutableToKeep"] = resultSubmit["ExecutableToKeep"]
187
185
  else:
188
186
  result = S_ERROR(resultSubmit["Message"])
189
187
 
@@ -67,9 +67,10 @@ import json
67
67
  import os
68
68
  import shutil
69
69
  import stat
70
+ import tempfile
70
71
  import uuid
71
72
  from shlex import quote as shlex_quote
72
- from urllib.parse import quote, unquote, urlparse
73
+ from urllib.parse import urlparse
73
74
 
74
75
  import pexpect
75
76
 
@@ -484,47 +485,69 @@ class SSHComputingElement(ComputingElement):
484
485
  options["User"] = self.user
485
486
  options["Queue"] = self.queue
486
487
 
487
- options = json.dumps(options)
488
- options = quote(options)
488
+ localOptionsFile = None
489
+ remoteOptionsFile = None
490
+ localResultFile = None
491
+ remoteResultFile = None
492
+ try:
493
+ # Write options to a local temporary file
494
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
495
+ json.dump(options, f)
496
+ localOptionsFile = f.name
497
+
498
+ # Upload the options file to the remote host
499
+ remoteOptionsFile = f"{self.sharedArea}/batch_options_{uuid.uuid4().hex}.json"
500
+ result = ssh.scpCall(30, localOptionsFile, remoteOptionsFile)
501
+ if not result["OK"]:
502
+ return result
489
503
 
490
- cmd = (
491
- "bash --login -c 'python3 %s/execute_batch %s || python %s/execute_batch %s || python2 %s/execute_batch %s'"
492
- % (self.sharedArea, options, self.sharedArea, options, self.sharedArea, options)
493
- )
504
+ # Execute the batch command with the options file path
505
+ cmd = (
506
+ f"bash --login -c 'python3 {self.sharedArea}/execute_batch {remoteOptionsFile} || "
507
+ f"python {self.sharedArea}/execute_batch {remoteOptionsFile} || "
508
+ f"python2 {self.sharedArea}/execute_batch {remoteOptionsFile}'"
509
+ )
494
510
 
495
- self.log.verbose(f"CE submission command: {cmd}")
511
+ self.log.verbose(f"CE submission command: {cmd}")
496
512
 
497
- result = ssh.sshCall(120, cmd)
498
- if not result["OK"]:
499
- self.log.error(f"{self.ceType} CE job submission failed", result["Message"])
500
- return result
513
+ result = ssh.sshCall(120, cmd)
514
+ if not result["OK"]:
515
+ self.log.error(f"{self.ceType} CE job submission failed", result["Message"])
516
+ return result
501
517
 
502
- sshStatus = result["Value"][0]
503
- sshStdout = result["Value"][1]
504
- sshStderr = result["Value"][2]
505
-
506
- # Examine results of the job submission
507
- if sshStatus == 0:
508
- output = sshStdout.strip().replace("\r", "").strip()
509
- if not output:
510
- return S_ERROR("No output from remote command")
511
-
512
- try:
513
- index = output.index("============= Start output ===============")
514
- output = output[index + 42 :]
515
- except ValueError:
516
- return S_ERROR(f"Invalid output from remote command: {output}")
517
-
518
- try:
519
- output = unquote(output)
520
- result = json.loads(output)
521
- if isinstance(result, str) and result.startswith("Exception:"):
522
- return S_ERROR(result)
523
- return S_OK(result)
524
- except Exception:
525
- return S_ERROR("Invalid return structure from job submission")
526
- else:
527
- return S_ERROR("\n".join([sshStdout, sshStderr]))
518
+ sshStatus = result["Value"][0]
519
+ if sshStatus != 0:
520
+ sshStdout = result["Value"][1]
521
+ sshStderr = result["Value"][2]
522
+ return S_ERROR(f"CE job submission command failed with status {sshStatus}: {sshStdout} {sshStderr}")
523
+
524
+ # The result should be written to a JSON file by execute_batch
525
+ # Compute the expected result file path
526
+ remoteResultFile = remoteOptionsFile.replace(".json", "_result.json")
527
+
528
+ # Try to download the result file
529
+ with tempfile.NamedTemporaryFile(mode="r", suffix=".json", delete=False) as f:
530
+ localResultFile = f.name
531
+
532
+ result = ssh.scpCall(30, localResultFile, remoteResultFile, upload=False)
533
+ if not result["OK"]:
534
+ return result
535
+
536
+ # Read the result from the downloaded file
537
+ with open(localResultFile) as f:
538
+ result = json.load(f)
539
+ return S_OK(result)
540
+ finally:
541
+ # Clean up local temporary file
542
+ if localOptionsFile and os.path.exists(localOptionsFile):
543
+ os.remove(localOptionsFile)
544
+ if localResultFile and os.path.exists(localResultFile):
545
+ os.remove(localResultFile)
546
+ # Clean up remote temporary files
547
+ if remoteOptionsFile:
548
+ ssh.sshCall(30, f"rm -f {remoteOptionsFile}")
549
+ if remoteResultFile:
550
+ ssh.sshCall(30, f"rm -f {remoteResultFile}")
528
551
 
529
552
  def submitJob(self, executableFile, proxy, numberOfJobs=1):
530
553
  # self.log.verbose( "Executable file path: %s" % executableFile )
@@ -74,7 +74,10 @@ class InputDataAgent(AgentModule):
74
74
  """Main execution method"""
75
75
 
76
76
  # Get all the transformations
77
- result = self.transClient.getTransformations({"Status": "Active", "Type": self.transformationTypes})
77
+ result = self.transClient.getTransformations(
78
+ {"Status": "Active", "Type": self.transformationTypes},
79
+ columns=["TransformationID", "AuthorDN", "AuthorGroup"],
80
+ )
78
81
  if not result["OK"]:
79
82
  self.log.error("InputDataAgent.execute: Failed to get transformations.", result["Message"])
80
83
  return S_OK()
@@ -1,4 +1,4 @@
1
- """ Agent to extend the number of tasks given the Transformation definition
1
+ """Agent to extend the number of tasks given the Transformation definition
2
2
 
3
3
  The following options can be set for the MCExtensionAgent.
4
4
 
@@ -8,6 +8,7 @@ The following options can be set for the MCExtensionAgent.
8
8
  :dedent: 2
9
9
  :caption: MCExtensionAgent options
10
10
  """
11
+
11
12
  from DIRAC import S_OK, gLogger
12
13
  from DIRAC.Core.Base.AgentModule import AgentModule
13
14
  from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
@@ -54,7 +55,9 @@ class MCExtensionAgent(AgentModule):
54
55
  return S_OK("Disabled via CS flag")
55
56
 
56
57
  # Obtain the transformations in Cleaning status and remove any mention of the jobs/files
57
- res = self.transClient.getTransformations({"Status": "Active", "Type": self.transformationTypes})
58
+ res = self.transClient.getTransformations(
59
+ {"Status": "Active", "Type": self.transformationTypes}, columns=["TransformationID", "MaxNumberOfTasks"]
60
+ )
58
61
  if res["OK"]:
59
62
  for transDict in res["Value"]:
60
63
  transID = transDict["TransformationID"]
@@ -231,7 +231,9 @@ class TaskManagerAgentBase(AgentModule, TransformationAgentsUtilities):
231
231
  selectCond["Type"] = transType
232
232
  if agentType:
233
233
  selectCond["AgentType"] = agentType
234
- res = self.transClient.getTransformations(condDict=selectCond)
234
+ res = self.transClient.getTransformations(
235
+ condDict=selectCond, columns=["TransformationID", "Body", "Author", "AuthorGroup"]
236
+ )
235
237
  if not res["OK"]:
236
238
  self.log.error("Failed to get transformations:", res["Message"])
237
239
  elif not res["Value"]:
@@ -20,6 +20,7 @@ from DIRAC.ConfigurationSystem.Client.Helpers.Operations import Operations
20
20
  from DIRAC.Core.Base.AgentModule import AgentModule
21
21
  from DIRAC.Core.Utilities.DErrno import cmpError
22
22
  from DIRAC.Core.Utilities.List import breakListIntoChunks
23
+ from DIRAC.Core.Utilities.ObjectLoader import ObjectLoader
23
24
  from DIRAC.Core.Utilities.Proxy import executeWithUserProxy
24
25
  from DIRAC.Core.Utilities.ReturnValues import returnSingleResult
25
26
  from DIRAC.RequestManagementSystem.Client.File import File
@@ -32,7 +33,6 @@ from DIRAC.Resources.Catalog.FileCatalogClient import FileCatalogClient
32
33
  from DIRAC.Resources.Storage.StorageElement import StorageElement
33
34
  from DIRAC.TransformationSystem.Client import TransformationStatus
34
35
  from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient
35
- from DIRAC.WorkloadManagementSystem.DB.JobDB import JobDB
36
36
  from DIRAC.WorkloadManagementSystem.Service.JobPolicy import (
37
37
  RIGHT_DELETE,
38
38
  RIGHT_KILL,
@@ -65,8 +65,11 @@ class TransformationCleaningAgent(AgentModule):
65
65
  self.reqClient = None
66
66
  # # file catalog client
67
67
  self.metadataClient = None
68
- # # JobDB
68
+ # # databases
69
69
  self.jobDB = None
70
+ self.pilotAgentsDB = None
71
+ self.taskQueueDB = None
72
+ self.storageManagementDB = None
70
73
 
71
74
  # # transformations types
72
75
  self.transformationTypes = None
@@ -125,8 +128,26 @@ class TransformationCleaningAgent(AgentModule):
125
128
  self.reqClient = ReqClient()
126
129
  # # file catalog client
127
130
  self.metadataClient = FileCatalogClient()
128
- # # job DB
129
- self.jobDB = JobDB()
131
+ # # databases
132
+ result = ObjectLoader().loadObject("WorkloadManagementSystem.DB.JobDB", "JobDB")
133
+ if not result["OK"]:
134
+ return result
135
+ self.jobDB = result["Value"]()
136
+
137
+ result = ObjectLoader().loadObject("WorkloadManagementSystem.DB.PilotAgentsDB", "PilotAgentsDB")
138
+ if not result["OK"]:
139
+ return result
140
+ self.pilotAgentsDB = result["Value"]()
141
+
142
+ result = ObjectLoader().loadObject("WorkloadManagementSystem.DB.TaskQueueDB", "TaskQueueDB")
143
+ if not result["OK"]:
144
+ return result
145
+ self.taskQueueDB = result["Value"]()
146
+
147
+ result = ObjectLoader().loadObject("StorageManagementSystem.DB.StorageManagementDB", "StorageManagementDB")
148
+ if not result["OK"]:
149
+ return result
150
+ self.storageManagementDB = result["Value"]()
130
151
 
131
152
  return S_OK()
132
153
 
@@ -144,7 +165,8 @@ class TransformationCleaningAgent(AgentModule):
144
165
 
145
166
  # Obtain the transformations in Cleaning status and remove any mention of the jobs/files
146
167
  res = self.transClient.getTransformations(
147
- {"Status": TransformationStatus.CLEANING, "Type": self.transformationTypes}
168
+ {"Status": TransformationStatus.CLEANING, "Type": self.transformationTypes},
169
+ columns=["TransformationID", "Author", "AuthorGroup", "Type"],
148
170
  )
149
171
  if res["OK"]:
150
172
  for transDict in res["Value"]:
@@ -161,7 +183,10 @@ class TransformationCleaningAgent(AgentModule):
161
183
  self.log.error("Failed to get transformations", res["Message"])
162
184
 
163
185
  # Obtain the transformations in RemovingFiles status and removes the output files
164
- res = self.transClient.getTransformations({"Status": "RemovingFiles", "Type": self.transformationTypes})
186
+ res = self.transClient.getTransformations(
187
+ {"Status": "RemovingFiles", "Type": self.transformationTypes},
188
+ columns=["TransformationID", "Author", "AuthorGroup"],
189
+ )
165
190
  if res["OK"]:
166
191
  for transDict in res["Value"]:
167
192
  if self.shifterProxy:
@@ -183,6 +208,7 @@ class TransformationCleaningAgent(AgentModule):
183
208
  {"Status": TransformationStatus.COMPLETED, "Type": self.transformationTypes},
184
209
  older=olderThanTime,
185
210
  timeStamp="LastUpdate",
211
+ columns=["TransformationID", "Author", "AuthorGroup"],
186
212
  )
187
213
  if res["OK"]:
188
214
  for transDict in res["Value"]:
@@ -230,7 +256,10 @@ class TransformationCleaningAgent(AgentModule):
230
256
  return res
231
257
  transformationIDs = res["Value"]
232
258
  if transformationIDs:
233
- res = self.transClient.getTransformations({"TransformationID": transformationIDs})
259
+ res = self.transClient.getTransformations(
260
+ {"TransformationID": transformationIDs},
261
+ columns=["TransformationID", "Status", "Author", "AuthorGroup", "Type"],
262
+ )
234
263
  if not res["OK"]:
235
264
  self.log.error("Failed to get transformations", res["Message"])
236
265
  return res
@@ -607,11 +636,17 @@ class TransformationCleaningAgent(AgentModule):
607
636
  :param self: self reference
608
637
  :param list trasnJobIDs: job IDs
609
638
  """
639
+ db_kwargs = dict(
640
+ jobdb=self.jobDB,
641
+ taskqueuedb=self.taskQueueDB,
642
+ pilotagentsdb=self.pilotAgentsDB,
643
+ storagemanagementdb=self.storageManagementDB,
644
+ )
610
645
  # Prevent 0 job IDs
611
646
  jobIDs = [int(j) for j in transJobIDs if int(j)]
612
647
  allRemove = True
613
648
  for jobList in breakListIntoChunks(jobIDs, 1000):
614
- res = kill_delete_jobs(RIGHT_KILL, jobList, force=True)
649
+ res = kill_delete_jobs(RIGHT_KILL, jobList, force=True, **db_kwargs)
615
650
  if res["OK"]:
616
651
  self.log.info(f"Successfully killed {len(jobList)} jobs from WMS")
617
652
  elif ("InvalidJobIDs" in res) and ("NonauthorizedJobIDs" not in res) and ("FailedJobIDs" not in res):
@@ -623,7 +658,7 @@ class TransformationCleaningAgent(AgentModule):
623
658
  self.log.error("Failed to kill jobs", f"(n={len(res['FailedJobIDs'])})")
624
659
  allRemove = False
625
660
 
626
- res = kill_delete_jobs(RIGHT_DELETE, jobList, force=True)
661
+ res = kill_delete_jobs(RIGHT_DELETE, jobList, force=True, **db_kwargs)
627
662
  if res["OK"]:
628
663
  self.log.info("Successfully deleted jobs from WMS", f"(n={len(jobList)})")
629
664
  elif ("InvalidJobIDs" in res) and ("NonauthorizedJobIDs" not in res) and ("FailedJobIDs" not in res):
@@ -76,7 +76,9 @@ class ValidateOutputDataAgent(AgentModule):
76
76
  self.updateWaitingIntegrity()
77
77
  gLogger.info("-" * 40)
78
78
 
79
- res = self.transClient.getTransformations({"Status": "ValidatingOutput", "Type": self.transformationTypes})
79
+ res = self.transClient.getTransformations(
80
+ {"Status": "ValidatingOutput", "Type": self.transformationTypes}, columns=["TransformationID"]
81
+ )
80
82
  if not res["OK"]:
81
83
  gLogger.error("Failed to get ValidatingOutput transformations", res["Message"])
82
84
  return res
@@ -98,7 +100,7 @@ class ValidateOutputDataAgent(AgentModule):
98
100
  def updateWaitingIntegrity(self):
99
101
  """Get 'WaitingIntegrity' transformations, update to 'ValidatedOutput'"""
100
102
  gLogger.info("Looking for transformations in the WaitingIntegrity status to update")
101
- res = self.transClient.getTransformations({"Status": "WaitingIntegrity"})
103
+ res = self.transClient.getTransformations({"Status": "WaitingIntegrity"}, columns=["TransformationID"])
102
104
  if not res["OK"]:
103
105
  gLogger.error("Failed to get WaitingIntegrity transformations", res["Message"])
104
106
  return res
@@ -114,13 +114,21 @@ class TransformationClient(Client):
114
114
  newer=None,
115
115
  timeStamp=None,
116
116
  orderAttribute=None,
117
- limit=100,
117
+ limit=None,
118
118
  extraParams=False,
119
119
  columns=None,
120
120
  ):
121
121
  """gets all the transformations in the system, incrementally. "limit" here is just used to determine the offset."""
122
122
  rpcClient = self._getRPC()
123
123
 
124
+ # If the body is requested (or is served by default)
125
+ # we take smaller chunk, not to take too much memory
126
+ # on the server
127
+ if columns and "Body" not in columns:
128
+ limit = 100_000
129
+ else:
130
+ limit = 1_000
131
+
124
132
  transformations = []
125
133
  if condDict is None:
126
134
  condDict = {}