Repository: rucio/rucio

Issue:
Replace dependency `python-memcached`
Motivation
----------
The `python-memcached` dependency is used by dogpile as a memcached client. However, development and maintenance has stalled, and it has not been updated in 5 years.

There is a view alternatives lined out here https://github.com/pinterest/pymemcache#comparison-with-other-libraries

With [pymemcache](https://github.com/pinterest/pymemcache) being one of them.

Modification
------------
Identify alternatives which work well with dogpile and switch the requirements and code to use them.


Discussion:

Resolving patch:
diff --git a/lib/rucio/common/cache.py b/lib/rucio/common/cache.py
--- a/lib/rucio/common/cache.py
+++ b/lib/rucio/common/cache.py
@@ -12,29 +12,49 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+from __future__ import absolute_import
 
+import logging
 from dogpile.cache import make_region
 
 from rucio.common.config import config_get
 
+CACHE_URL = config_get('cache', 'url', False, '127.0.0.1:11211', check_config_table=False)
+
+ENABLE_CACHING = True
+_mc_client = None
+try:
+    import pymemcache
+    _mc_client = pymemcache.Client(CACHE_URL, connect_timeout=1, timeout=1)
+    _mc_client.version()
+except (IOError, ImportError):
+    logging.warning("Cannot access memcached at {}. Caching will be disabled".format(CACHE_URL), exc_info=True)
+    ENABLE_CACHING = False
+finally:
+    if _mc_client:
+        _mc_client.close()
+
 
 def make_region_memcached(expiration_time, function_key_generator=None):
     """
-    Make and configure a dogpile.cache.memcached region
+    Make and configure a dogpile.cache.pymemcache region
     """
     if function_key_generator:
         region = make_region(function_key_generator=function_key_generator)
     else:
         region = make_region()
 
-    region.configure(
-        'dogpile.cache.memcached',
-        expiration_time=expiration_time,
-        arguments={
-            'url': config_get('cache', 'url', False, '127.0.0.1:11211', check_config_table=False),
-            'distributed_lock': True,
-            'memcached_expire_time': expiration_time + 60,  # must be bigger than expiration_time
-        }
-    )
+    if ENABLE_CACHING:
+        region.configure(
+            'dogpile.cache.pymemcache',
+            expiration_time=expiration_time,
+            arguments={
+                'url': CACHE_URL,
+                'distributed_lock': True,
+                'memcached_expire_time': expiration_time + 60,  # must be bigger than expiration_time
+            }
+        )
+    else:
+        region.configure('dogpile.cache.null')
 
     return region
diff --git a/setuputil.py b/setuputil.py
--- a/setuputil.py
+++ b/setuputil.py
@@ -79,7 +79,7 @@
         'pysftp',
         'sqlalchemy',
         'alembic',
-        'python-memcached',
+        'pymemcache',
         'python-dateutil',
         'stomp.py',
         'statsd',


Test patch:
diff --git a/lib/rucio/tests/test_judge_repairer.py b/lib/rucio/tests/test_judge_repairer.py
--- a/lib/rucio/tests/test_judge_repairer.py
+++ b/lib/rucio/tests/test_judge_repairer.py
@@ -302,7 +302,7 @@ def test_to_repair_a_rule_with_only_1_rse_whose_site_is_blocklisted(self):
         rule_repairer(once=True)  # Clean out the repairer
 
         region = make_region().configure(
-            'dogpile.cache.memcached',
+            'dogpile.cache.pymemcache',
             expiration_time=900,
             arguments={'url': config_get('cache', 'url', False, '127.0.0.1:11211'), 'distributed_lock': True}
         )