prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  memcached cache backend does not really test memcached
  @autrilla reported that when he moved Kinto to GCP, the heartbeat reported success for all backends, including cache, even though there is no `memcached`. I think the problem is here:
  
  https://github.com/Kinto/kinto/blob/61494caae2bb8fe342f32f6a5d89f40ed2b62dff/kinto/core/cache/__init__.py#L87-L91
  
  The heartbeat for the cache backend just sends two messages which don't expect responses. For something like memcached which communicates over UDP, these messages can "succeed" by silently going nowhere.
  memcached cache backend does not really test memcached
  @autrilla reported that when he moved Kinto to GCP, the heartbeat reported success for all backends, including cache, even though there is no `memcached`. I think the problem is here:
  
  https://github.com/Kinto/kinto/blob/61494caae2bb8fe342f32f6a5d89f40ed2b62dff/kinto/core/cache/__init__.py#L87-L91
  
  The heartbeat for the cache backend just sends two messages which don't expect responses. For something like memcached which communicates over UDP, these messages can "succeed" by silently going nowhere.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `kinto/core/cache/__init__.py`
  Content:
  ```
  1 import logging
  2 import random
  3 
  4 
  5 logger = logging.getLogger(__name__)
  6 
  7 
  8 _HEARTBEAT_DELETE_RATE = 0.5
  9 _HEARTBEAT_KEY = "__heartbeat__"
  10 _HEARTBEAT_TTL_SECONDS = 3600
  11 
  12 
  13 class CacheBase:
  14     def __init__(self, *args, **kwargs):
  15         self.prefix = kwargs["cache_prefix"]
  16         self.max_size_bytes = kwargs.get("cache_max_size_bytes")
  17 
  18     def initialize_schema(self, dry_run=False):
  19         """Create every necessary objects (like tables or indices) in the
  20         backend.
  21 
  22         This is executed when the ``kinto migrate`` command is run.
  23 
  24         :param bool dry_run: simulate instead of executing the operations.
  25         """
  26         raise NotImplementedError
  27 
  28     def flush(self):
  29         """Delete every values."""
  30         raise NotImplementedError
  31 
  32     def ttl(self, key):
  33         """Obtain the expiration value of the specified `key`.
  34 
  35         :param str key: key
  36         :returns: number of seconds or negative if no TTL.
  37         :rtype: float
  38         """
  39         raise NotImplementedError
  40 
  41     def expire(self, key, ttl):
  42         """Set the expiration value `ttl` for the specified `key`.
  43 
  44         :param str key: key
  45         :param float ttl: number of seconds
  46         """
  47         raise NotImplementedError
  48 
  49     def set(self, key, value, ttl):
  50         """Store a value with the specified `key`.
  51 
  52         :param str key: key
  53         :param str value: value to store
  54         :param float ttl: expire after number of seconds
  55         """
  56         raise NotImplementedError
  57 
  58     def get(self, key):
  59         """Obtain the value of the specified `key`.
  60 
  61         :param str key: key
  62         :returns: the stored value or None if missing.
  63         :rtype: str
  64         """
  65         raise NotImplementedError
  66 
  67     def delete(self, key):
  68         """Delete the value of the specified `key`.
  69 
  70         :param str key: key
  71         """
  72         raise NotImplementedError
  73 
  74 
  75 def heartbeat(backend):
  76     def ping(request):
  77         """Test that cache backend is operational.
  78 
  79         :param request: current request object
  80         :type request: :class:`~pyramid:pyramid.request.Request`
  81         :returns: ``True`` is everything is ok, ``False`` otherwise.
  82         :rtype: bool
  83         """
  84         # No specific case for readonly mode because the cache should
  85         # continue to work in that mode.
  86         try:
  87             if random.SystemRandom().random() < _HEARTBEAT_DELETE_RATE:
  88                 backend.delete(_HEARTBEAT_KEY)
  89             else:
  90                 backend.set(_HEARTBEAT_KEY, "alive", _HEARTBEAT_TTL_SECONDS)
  91             return True
  92         except Exception:
  93             logger.exception("Heartbeat Failure")
  94             return False
  95 
  96     return ping
  97
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/kinto/core/cache/__init__.py b/kinto/core/cache/__init__.py
  --- a/kinto/core/cache/__init__.py
  +++ b/kinto/core/cache/__init__.py
  @@ -86,9 +86,9 @@
           try:
               if random.SystemRandom().random() < _HEARTBEAT_DELETE_RATE:
                   backend.delete(_HEARTBEAT_KEY)
  -            else:
  -                backend.set(_HEARTBEAT_KEY, "alive", _HEARTBEAT_TTL_SECONDS)
  -            return True
  +                return backend.get(_HEARTBEAT_KEY) is None
  +            backend.set(_HEARTBEAT_KEY, "alive", _HEARTBEAT_TTL_SECONDS)
  +            return backend.get(_HEARTBEAT_KEY) == "alive"
           except Exception:
               logger.exception("Heartbeat Failure")
               return False
  
verification_info: {"golden_diff": "diff --git a/kinto/core/cache/__init__.py b/kinto/core/cache/__init__.py\n--- a/kinto/core/cache/__init__.py\n+++ b/kinto/core/cache/__init__.py\n@@ -86,9 +86,9 @@\n         try:\n             if random.SystemRandom().random() < _HEARTBEAT_DELETE_RATE:\n                 backend.delete(_HEARTBEAT_KEY)\n-            else:\n-                backend.set(_HEARTBEAT_KEY, \"alive\", _HEARTBEAT_TTL_SECONDS)\n-            return True\n+                return backend.get(_HEARTBEAT_KEY) is None\n+            backend.set(_HEARTBEAT_KEY, \"alive\", _HEARTBEAT_TTL_SECONDS)\n+            return backend.get(_HEARTBEAT_KEY) == \"alive\"\n         except Exception:\n             logger.exception(\"Heartbeat Failure\")\n             return False\n", "issue": "memcached cache backend does not really test memcached\n@autrilla reported that when he moved Kinto to GCP, the heartbeat reported success for all backends, including cache, even though there is no `memcached`. I think the problem is here:\r\n\r\nhttps://github.com/Kinto/kinto/blob/61494caae2bb8fe342f32f6a5d89f40ed2b62dff/kinto/core/cache/__init__.py#L87-L91\r\n\r\nThe heartbeat for the cache backend just sends two messages which don't expect responses. For something like memcached which communicates over UDP, these messages can \"succeed\" by silently going nowhere.\nmemcached cache backend does not really test memcached\n@autrilla reported that when he moved Kinto to GCP, the heartbeat reported success for all backends, including cache, even though there is no `memcached`. I think the problem is here:\r\n\r\nhttps://github.com/Kinto/kinto/blob/61494caae2bb8fe342f32f6a5d89f40ed2b62dff/kinto/core/cache/__init__.py#L87-L91\r\n\r\nThe heartbeat for the cache backend just sends two messages which don't expect responses. For something like memcached which communicates over UDP, these messages can \"succeed\" by silently going nowhere.\n", "before_files": [{"content": "import logging\nimport random\n\n\nlogger = logging.getLogger(__name__)\n\n\n_HEARTBEAT_DELETE_RATE = 0.5\n_HEARTBEAT_KEY = \"__heartbeat__\"\n_HEARTBEAT_TTL_SECONDS = 3600\n\n\nclass CacheBase:\n    def __init__(self, *args, **kwargs):\n        self.prefix = kwargs[\"cache_prefix\"]\n        self.max_size_bytes = kwargs.get(\"cache_max_size_bytes\")\n\n    def initialize_schema(self, dry_run=False):\n        \"\"\"Create every necessary objects (like tables or indices) in the\n        backend.\n\n        This is executed when the ``kinto migrate`` command is run.\n\n        :param bool dry_run: simulate instead of executing the operations.\n        \"\"\"\n        raise NotImplementedError\n\n    def flush(self):\n        \"\"\"Delete every values.\"\"\"\n        raise NotImplementedError\n\n    def ttl(self, key):\n        \"\"\"Obtain the expiration value of the specified `key`.\n\n        :param str key: key\n        :returns: number of seconds or negative if no TTL.\n        :rtype: float\n        \"\"\"\n        raise NotImplementedError\n\n    def expire(self, key, ttl):\n        \"\"\"Set the expiration value `ttl` for the specified `key`.\n\n        :param str key: key\n        :param float ttl: number of seconds\n        \"\"\"\n        raise NotImplementedError\n\n    def set(self, key, value, ttl):\n        \"\"\"Store a value with the specified `key`.\n\n        :param str key: key\n        :param str value: value to store\n        :param float ttl: expire after number of seconds\n        \"\"\"\n        raise NotImplementedError\n\n    def get(self, key):\n        \"\"\"Obtain the value of the specified `key`.\n\n        :param str key: key\n        :returns: the stored value or None if missing.\n        :rtype: str\n        \"\"\"\n        raise NotImplementedError\n\n    def delete(self, key):\n        \"\"\"Delete the value of the specified `key`.\n\n        :param str key: key\n        \"\"\"\n        raise NotImplementedError\n\n\ndef heartbeat(backend):\n    def ping(request):\n        \"\"\"Test that cache backend is operational.\n\n        :param request: current request object\n        :type request: :class:`~pyramid:pyramid.request.Request`\n        :returns: ``True`` is everything is ok, ``False`` otherwise.\n        :rtype: bool\n        \"\"\"\n        # No specific case for readonly mode because the cache should\n        # continue to work in that mode.\n        try:\n            if random.SystemRandom().random() < _HEARTBEAT_DELETE_RATE:\n                backend.delete(_HEARTBEAT_KEY)\n            else:\n                backend.set(_HEARTBEAT_KEY, \"alive\", _HEARTBEAT_TTL_SECONDS)\n            return True\n        except Exception:\n            logger.exception(\"Heartbeat Failure\")\n            return False\n\n    return ping\n", "path": "kinto/core/cache/__init__.py"}], "after_files": [{"content": "import logging\nimport random\n\n\nlogger = logging.getLogger(__name__)\n\n\n_HEARTBEAT_DELETE_RATE = 0.5\n_HEARTBEAT_KEY = \"__heartbeat__\"\n_HEARTBEAT_TTL_SECONDS = 3600\n\n\nclass CacheBase:\n    def __init__(self, *args, **kwargs):\n        self.prefix = kwargs[\"cache_prefix\"]\n        self.max_size_bytes = kwargs.get(\"cache_max_size_bytes\")\n\n    def initialize_schema(self, dry_run=False):\n        \"\"\"Create every necessary objects (like tables or indices) in the\n        backend.\n\n        This is executed when the ``kinto migrate`` command is run.\n\n        :param bool dry_run: simulate instead of executing the operations.\n        \"\"\"\n        raise NotImplementedError\n\n    def flush(self):\n        \"\"\"Delete every values.\"\"\"\n        raise NotImplementedError\n\n    def ttl(self, key):\n        \"\"\"Obtain the expiration value of the specified `key`.\n\n        :param str key: key\n        :returns: number of seconds or negative if no TTL.\n        :rtype: float\n        \"\"\"\n        raise NotImplementedError\n\n    def expire(self, key, ttl):\n        \"\"\"Set the expiration value `ttl` for the specified `key`.\n\n        :param str key: key\n        :param float ttl: number of seconds\n        \"\"\"\n        raise NotImplementedError\n\n    def set(self, key, value, ttl):\n        \"\"\"Store a value with the specified `key`.\n\n        :param str key: key\n        :param str value: value to store\n        :param float ttl: expire after number of seconds\n        \"\"\"\n        raise NotImplementedError\n\n    def get(self, key):\n        \"\"\"Obtain the value of the specified `key`.\n\n        :param str key: key\n        :returns: the stored value or None if missing.\n        :rtype: str\n        \"\"\"\n        raise NotImplementedError\n\n    def delete(self, key):\n        \"\"\"Delete the value of the specified `key`.\n\n        :param str key: key\n        \"\"\"\n        raise NotImplementedError\n\n\ndef heartbeat(backend):\n    def ping(request):\n        \"\"\"Test that cache backend is operational.\n\n        :param request: current request object\n        :type request: :class:`~pyramid:pyramid.request.Request`\n        :returns: ``True`` is everything is ok, ``False`` otherwise.\n        :rtype: bool\n        \"\"\"\n        # No specific case for readonly mode because the cache should\n        # continue to work in that mode.\n        try:\n            if random.SystemRandom().random() < _HEARTBEAT_DELETE_RATE:\n                backend.delete(_HEARTBEAT_KEY)\n                return backend.get(_HEARTBEAT_KEY) is None\n            backend.set(_HEARTBEAT_KEY, \"alive\", _HEARTBEAT_TTL_SECONDS)\n            return backend.get(_HEARTBEAT_KEY) == \"alive\"\n        except Exception:\n            logger.exception(\"Heartbeat Failure\")\n            return False\n\n    return ping\n", "path": "kinto/core/cache/__init__.py"}]}