Repository: rucio/rucio

Issue:
exception in reaper when all deletions fail from an rse
Motivation
----------

```
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/rucio/daemons/reaper/reaper.py", line 604, in reaper
    delete_replicas(rse_id=rse_id, files=deleted_files)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 365, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/replica.py", line 1538, in delete_replicas
    __delete_replicas(rse_id, files, ignore_availability=ignore_availability, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/replica.py", line 1648, in __delete_replicas
    decrease(rse_id=rse_id, files=delta, bytes_=bytes_, session=session)
  File "/usr/local/lib/python3.6/site-packages/rucio/db/sqla/session.py", line 379, in new_funct
    result = function(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rucio/core/rse_counter.py", line 59, in decrease
    return increase(rse_id=rse_id, files=-files, bytes_=-bytes_, session=session)
TypeError: bad operand type for unary -: 'NoneType'
```

Modification
------------

Probably enough to return fast from delete_replicas if the list of files to remove is empty. 



Discussion:

Resolving patch:
diff --git a/lib/rucio/core/replica.py b/lib/rucio/core/replica.py
--- a/lib/rucio/core/replica.py
+++ b/lib/rucio/core/replica.py
@@ -1546,6 +1546,9 @@ def delete_replicas(rse_id, files, ignore_availability=True, session=None):
 
 
 def __delete_replicas(rse_id, files, ignore_availability=True, session=None):
+    if not files:
+        return
+
     replica_rse = get_rse(rse_id=rse_id, session=session)
 
     if not (replica_rse.availability & 1) and not ignore_availability:


Test patch:
diff --git a/lib/rucio/tests/test_replica.py b/lib/rucio/tests/test_replica.py
--- a/lib/rucio/tests/test_replica.py
+++ b/lib/rucio/tests/test_replica.py
@@ -194,7 +194,12 @@ def test_add_list_replicas(self, rse_factory, mock_scope, root_account):
 
         assert nbfiles == replica_cpt
 
-    def test_delete_replicas(self, rse_factory, mock_scope, root_account):
+    @pytest.mark.parametrize("file_config_mock", [
+        # Run test twice: with, and without, temp tables
+        {"overrides": [('core', 'use_temp_tables', 'True')]},
+        {"overrides": [('core', 'use_temp_tables', 'False')]},
+    ], indirect=True)
+    def test_delete_replicas(self, rse_factory, mock_scope, root_account, file_config_mock):
         """ REPLICA (CORE): Delete replicas """
         _, rse1_id = rse_factory.make_mock_rse()
         _, rse2_id = rse_factory.make_mock_rse()
@@ -208,6 +213,9 @@ def test_delete_replicas(self, rse_factory, mock_scope, root_account):
         add_replicas(rse_id=rse1_id, files=files2, account=root_account, ignore_availability=True)
         add_replicas(rse_id=rse2_id, files=files2, account=root_account, ignore_availability=True)
 
+        # calling delete_replicas without any files must not fail
+        delete_replicas(rse_id=rse1_id, files=[])
+
         delete_replicas(rse_id=rse1_id, files=files1 + files2)
 
         for file in files1: