Repository: rucio/rucio

Issue:
submitter doesn't update the 'transfertool' field of requests when needed
Motivation
----------

We had a case recently when a multi-transfertool submitter (the globus+fts3 one) detected that the main "globus" source rse cannot be used for transfer. So it fall-back to fts3 submission. however, when doing so, the "transfertool" field of the request was not updated. So we ended with a request submitted to fts, but marked with transfertool=globus. 

The globus poller was unable to poll it (obviously). 

It's probably enough to just correctly update the transfertool (if needed) when submitting the transfer. We have to also check if preparer cannot be made more intelligent to avoid picking the wrong source and the wrong transfertool at the first place.





Discussion:

Resolving patch:
diff --git a/lib/rucio/core/transfer.py b/lib/rucio/core/transfer.py
--- a/lib/rucio/core/transfer.py
+++ b/lib/rucio/core/transfer.py
@@ -400,15 +400,25 @@ def ensure_db_sources(
 
 
 @transactional_session
-def set_transfers_state(transfers, state, submitted_at, external_host, external_id, *, session: "Session", logger):
+def set_transfers_state(
+        transfers,
+        state: "RequestState",
+        submitted_at: datetime.datetime,
+        external_host: str,
+        external_id: str,
+        transfertool: str,
+        *,
+        session: "Session",
+        logger
+):
     """
     Update the transfer info of a request.
     :param transfers:  Dictionary containing request transfer info.
     :param session:    Database session to use.
     """
 
-    logger(logging.INFO, 'Setting state(%s), external_host(%s) and eid(%s) for transfers: %s',
-           state.name, external_host, external_id, ', '.join(t.rws.request_id for t in transfers))
+    logger(logging.INFO, 'Setting state(%s), transfertool(%s), external_host(%s) and eid(%s) for transfers: %s',
+           state.name, transfertool, external_host, external_id, ', '.join(t.rws.request_id for t in transfers))
     try:
         for transfer in transfers:
             rws = transfer.rws
@@ -422,11 +432,12 @@ def set_transfers_state(transfers, state, submitted_at, external_host, external_
                 synchronize_session=False
             ).values(
                 {
-                    'state': state,
-                    'external_id': external_id,
-                    'external_host': external_host,
-                    'source_rse_id': transfer.src.rse.id,
-                    'submitted_at': submitted_at,
+                    models.Request.state: state,
+                    models.Request.external_id: external_id,
+                    models.Request.external_host: external_host,
+                    models.Request.source_rse_id: transfer.src.rse.id,
+                    models.Request.submitted_at: submitted_at,
+                    models.Request.transfertool: transfertool,
                 }
             )
             rowcount = session.execute(stmt).rowcount
diff --git a/lib/rucio/daemons/conveyor/common.py b/lib/rucio/daemons/conveyor/common.py
--- a/lib/rucio/daemons/conveyor/common.py
+++ b/lib/rucio/daemons/conveyor/common.py
@@ -493,8 +493,15 @@ def _submit_transfers(transfertool_obj, transfers, job_params, submitter='submit
 
     if state_to_set:
         try:
-            transfer_core.set_transfers_state(transfers, state=state_to_set, external_host=transfertool_obj.external_host,
-                                              external_id=eid, submitted_at=datetime.datetime.utcnow(), logger=logger)
+            transfer_core.set_transfers_state(
+                transfers,
+                state=state_to_set,
+                external_host=transfertool_obj.external_host,
+                external_id=eid,
+                submitted_at=datetime.datetime.utcnow(),
+                transfertool=transfertool_obj.external_name,
+                logger=logger
+            )
         except Exception:
             logger(logging.ERROR, 'Failed to register transfer state with error', exc_info=True)
             if eid is not None:


Test patch:
diff --git a/lib/rucio/tests/test_conveyor_submitter.py b/lib/rucio/tests/test_conveyor_submitter.py
--- a/lib/rucio/tests/test_conveyor_submitter.py
+++ b/lib/rucio/tests/test_conveyor_submitter.py
@@ -299,6 +299,7 @@ def __setup_test():
     submitter(once=True, rses=[{'id': rse_id} for rse_id in (src_rse_id, dst_rse_id)], partition_wait_time=None, transfertools=['mock'], transfertype='single', ignore_availability=True)
     request = request_core.get_request_by_did(rse_id=dst_rse_id, **did)
     assert request['state'] == RequestState.SUBMITTED
+    assert request['transfertool'] == 'mock'
 
 
 @pytest.mark.noparallel(reason="multiple submitters cannot be run in parallel due to partial job assignment by hash")
@@ -374,8 +375,10 @@ def test_globus(rse_factory, did_factory, root_account):
         assert job_did2['metadata']['name'] == did2['name']
     request = request_core.get_request_by_did(rse_id=rse2_id, **did1)
     assert request['state'] == RequestState.SUBMITTED
+    assert request['transfertool'] == 'globus'
     request = request_core.get_request_by_did(rse_id=rse4_id, **did2)
     assert request['state'] == RequestState.SUBMITTED
+    assert request['transfertool'] == 'globus'
 
 
 @pytest.mark.noparallel(reason="multiple submitters cannot be run in parallel due to partial job assignment by hash")