prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Setting value in cache should never fail
  The cache is a key value store. The transaction isolation and integrity constraints are details of implementation. Setting a value in the cache should just never fail.
  Setting value in cache should never fail
  The cache is a key value store. The transaction isolation and integrity constraints are details of implementation. Setting a value in the cache should just never fail.
  
  --- 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/postgresql/__init__.py`
  Content:
  ```
  1 from __future__ import absolute_import
  2 
  3 import os
  4 
  5 from kinto.core import logger
  6 from kinto.core.cache import CacheBase
  7 from kinto.core.storage.postgresql.client import create_from_config
  8 from kinto.core.utils import json
  9 
  10 
  11 class Cache(CacheBase):
  12     """Cache backend using PostgreSQL.
  13 
  14     Enable in configuration::
  15 
  16         kinto.cache_backend = kinto.core.cache.postgresql
  17 
  18     Database location URI can be customized::
  19 
  20         kinto.cache_url = postgres://user:pass@db.server.lan:5432/dbname
  21 
  22     Alternatively, username and password could also rely on system user ident
  23     or even specified in :file:`~/.pgpass` (*see PostgreSQL documentation*).
  24 
  25     .. note::
  26 
  27         Some tables and indices are created when ``kinto migrate`` is run.
  28         This requires some privileges on the database, or some error will
  29         be raised.
  30 
  31         **Alternatively**, the schema can be initialized outside the
  32         python application, using the SQL file located in
  33         :file:`kinto/core/cache/postgresql/schema.sql`. This allows to
  34         distinguish schema manipulation privileges from schema usage.
  35 
  36 
  37     A connection pool is enabled by default::
  38 
  39         kinto.cache_pool_size = 10
  40         kinto.cache_maxoverflow = 10
  41         kinto.cache_max_backlog = -1
  42         kinto.cache_pool_recycle = -1
  43         kinto.cache_pool_timeout = 30
  44         kinto.cache_poolclass =
  45             kinto.core.storage.postgresql.pool.QueuePoolWithMaxBacklog
  46 
  47     The ``max_backlog``  limits the number of threads that can be in the queue
  48     waiting for a connection.  Once this limit has been reached, any further
  49     attempts to acquire a connection will be rejected immediately, instead of
  50     locking up all threads by keeping them waiting in the queue.
  51 
  52     See `dedicated section in SQLAlchemy documentation
  53     <http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html>`_
  54     for default values and behaviour.
  55 
  56     .. note::
  57 
  58         Using a `dedicated connection pool <http://pgpool.net>`_ is still
  59         recommended to allow load balancing, replication or limit the number
  60         of connections used in a multi-process deployment.
  61 
  62     :noindex:
  63     """  # NOQA
  64     def __init__(self, client, *args, **kwargs):
  65         super(Cache, self).__init__(*args, **kwargs)
  66         self.client = client
  67 
  68     def initialize_schema(self, dry_run=False):
  69         # Check if cache table exists.
  70         query = """
  71         SELECT 1
  72           FROM information_schema.tables
  73          WHERE table_name = 'cache';
  74         """
  75         with self.client.connect(readonly=True) as conn:
  76             result = conn.execute(query)
  77             if result.rowcount > 0:
  78                 logger.info("PostgreSQL cache schema is up-to-date.")
  79                 return
  80 
  81         # Create schema
  82         here = os.path.abspath(os.path.dirname(__file__))
  83         sql_file = os.path.join(here, 'schema.sql')
  84 
  85         if dry_run:
  86             logger.info("Create cache schema from %s" % sql_file)
  87             return
  88 
  89         # Since called outside request, force commit.
  90         schema = open(sql_file).read()
  91         with self.client.connect(force_commit=True) as conn:
  92             conn.execute(schema)
  93         logger.info('Created PostgreSQL cache tables')
  94 
  95     def flush(self):
  96         query = """
  97         DELETE FROM cache;
  98         """
  99         # Since called outside request (e.g. tests), force commit.
  100         with self.client.connect(force_commit=True) as conn:
  101             conn.execute(query)
  102         logger.debug('Flushed PostgreSQL cache tables')
  103 
  104     def ttl(self, key):
  105         query = """
  106         SELECT EXTRACT(SECOND FROM (ttl - now())) AS ttl
  107           FROM cache
  108          WHERE key = :key
  109            AND ttl IS NOT NULL;
  110         """
  111         with self.client.connect(readonly=True) as conn:
  112             result = conn.execute(query, dict(key=self.prefix + key))
  113             if result.rowcount > 0:
  114                 return result.fetchone()['ttl']
  115         return -1
  116 
  117     def expire(self, key, ttl):
  118         query = """
  119         UPDATE cache SET ttl = sec2ttl(:ttl) WHERE key = :key;
  120         """
  121         with self.client.connect() as conn:
  122             conn.execute(query, dict(ttl=ttl, key=self.prefix + key))
  123 
  124     def set(self, key, value, ttl=None):
  125         if ttl is None:
  126             logger.warning("No TTL for cache key %r" % key)
  127         query = """
  128         WITH upsert AS (
  129             UPDATE cache SET value = :value, ttl = sec2ttl(:ttl)
  130              WHERE key=:key
  131             RETURNING *)
  132         INSERT INTO cache (key, value, ttl)
  133         SELECT :key, :value, sec2ttl(:ttl)
  134         WHERE NOT EXISTS (SELECT * FROM upsert)
  135         """
  136         value = json.dumps(value)
  137         with self.client.connect() as conn:
  138             conn.execute(query, dict(key=self.prefix + key,
  139                                      value=value, ttl=ttl))
  140 
  141     def get(self, key):
  142         purge = "DELETE FROM cache WHERE ttl IS NOT NULL AND now() > ttl;"
  143         query = "SELECT value FROM cache WHERE key = :key;"
  144         with self.client.connect() as conn:
  145             conn.execute(purge)
  146             result = conn.execute(query, dict(key=self.prefix + key))
  147             if result.rowcount > 0:
  148                 value = result.fetchone()['value']
  149                 return json.loads(value)
  150 
  151     def delete(self, key):
  152         query = "DELETE FROM cache WHERE key = :key"
  153         with self.client.connect() as conn:
  154             conn.execute(query, dict(key=self.prefix + key))
  155 
  156 
  157 def load_from_config(config):
  158     settings = config.get_settings()
  159     client = create_from_config(config, prefix='cache_', with_transaction=False)
  160     return Cache(client=client, cache_prefix=settings['cache_prefix'])
  161
  ```
  --- 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/postgresql/__init__.py b/kinto/core/cache/postgresql/__init__.py
  --- a/kinto/core/cache/postgresql/__init__.py
  +++ b/kinto/core/cache/postgresql/__init__.py
  @@ -1,13 +1,41 @@
   from __future__ import absolute_import
  +from functools import wraps
   
   import os
  +import time
   
   from kinto.core import logger
   from kinto.core.cache import CacheBase
   from kinto.core.storage.postgresql.client import create_from_config
  +from kinto.core.storage.exceptions import BackendError
   from kinto.core.utils import json
   
   
  +DELAY_BETWEEN_RETRIES_IN_SECONDS = 0.005
  +MAX_RETRIES = 10
  +
  +
  +def retry_on_failure(func):
  +    try:
  +        import psycopg2
  +    except ImportError:  # pragma: no cover
  +        pass  # Do not break (but will fail nicely later anyway)
  +
  +    @wraps(func)
  +    def wraps_func(self, *args, **kwargs):
  +        tries = kwargs.pop('tries', 0)
  +        try:
  +            return func(self, *args, **kwargs)
  +        except psycopg2.IntegrityError as e:
  +            if tries < MAX_RETRIES:
  +                # Skip delay the 2 first times.
  +                delay = max(0, tries - 1) * DELAY_BETWEEN_RETRIES_IN_SECONDS
  +                time.sleep(delay)
  +                return wraps_func(self, tries=(tries + 1), *args, **kwargs)
  +            raise BackendError(original=e)
  +    return wraps_func
  +
  +
   class Cache(CacheBase):
       """Cache backend using PostgreSQL.
   
  @@ -121,6 +149,7 @@
           with self.client.connect() as conn:
               conn.execute(query, dict(ttl=ttl, key=self.prefix + key))
   
  +    @retry_on_failure
       def set(self, key, value, ttl=None):
           if ttl is None:
               logger.warning("No TTL for cache key %r" % key)
  
verification_info: {"golden_diff": "diff --git a/kinto/core/cache/postgresql/__init__.py b/kinto/core/cache/postgresql/__init__.py\n--- a/kinto/core/cache/postgresql/__init__.py\n+++ b/kinto/core/cache/postgresql/__init__.py\n@@ -1,13 +1,41 @@\n from __future__ import absolute_import\n+from functools import wraps\n \n import os\n+import time\n \n from kinto.core import logger\n from kinto.core.cache import CacheBase\n from kinto.core.storage.postgresql.client import create_from_config\n+from kinto.core.storage.exceptions import BackendError\n from kinto.core.utils import json\n \n \n+DELAY_BETWEEN_RETRIES_IN_SECONDS = 0.005\n+MAX_RETRIES = 10\n+\n+\n+def retry_on_failure(func):\n+    try:\n+        import psycopg2\n+    except ImportError:  # pragma: no cover\n+        pass  # Do not break (but will fail nicely later anyway)\n+\n+    @wraps(func)\n+    def wraps_func(self, *args, **kwargs):\n+        tries = kwargs.pop('tries', 0)\n+        try:\n+            return func(self, *args, **kwargs)\n+        except psycopg2.IntegrityError as e:\n+            if tries < MAX_RETRIES:\n+                # Skip delay the 2 first times.\n+                delay = max(0, tries - 1) * DELAY_BETWEEN_RETRIES_IN_SECONDS\n+                time.sleep(delay)\n+                return wraps_func(self, tries=(tries + 1), *args, **kwargs)\n+            raise BackendError(original=e)\n+    return wraps_func\n+\n+\n class Cache(CacheBase):\n     \"\"\"Cache backend using PostgreSQL.\n \n@@ -121,6 +149,7 @@\n         with self.client.connect() as conn:\n             conn.execute(query, dict(ttl=ttl, key=self.prefix + key))\n \n+    @retry_on_failure\n     def set(self, key, value, ttl=None):\n         if ttl is None:\n             logger.warning(\"No TTL for cache key %r\" % key)\n", "issue": "Setting value in cache should never fail\nThe cache is a key value store. The transaction isolation and integrity constraints are details of implementation. Setting a value in the cache should just never fail.\nSetting value in cache should never fail\nThe cache is a key value store. The transaction isolation and integrity constraints are details of implementation. Setting a value in the cache should just never fail.\n", "before_files": [{"content": "from __future__ import absolute_import\n\nimport os\n\nfrom kinto.core import logger\nfrom kinto.core.cache import CacheBase\nfrom kinto.core.storage.postgresql.client import create_from_config\nfrom kinto.core.utils import json\n\n\nclass Cache(CacheBase):\n    \"\"\"Cache backend using PostgreSQL.\n\n    Enable in configuration::\n\n        kinto.cache_backend = kinto.core.cache.postgresql\n\n    Database location URI can be customized::\n\n        kinto.cache_url = postgres://user:pass@db.server.lan:5432/dbname\n\n    Alternatively, username and password could also rely on system user ident\n    or even specified in :file:`~/.pgpass` (*see PostgreSQL documentation*).\n\n    .. note::\n\n        Some tables and indices are created when ``kinto migrate`` is run.\n        This requires some privileges on the database, or some error will\n        be raised.\n\n        **Alternatively**, the schema can be initialized outside the\n        python application, using the SQL file located in\n        :file:`kinto/core/cache/postgresql/schema.sql`. This allows to\n        distinguish schema manipulation privileges from schema usage.\n\n\n    A connection pool is enabled by default::\n\n        kinto.cache_pool_size = 10\n        kinto.cache_maxoverflow = 10\n        kinto.cache_max_backlog = -1\n        kinto.cache_pool_recycle = -1\n        kinto.cache_pool_timeout = 30\n        kinto.cache_poolclass =\n            kinto.core.storage.postgresql.pool.QueuePoolWithMaxBacklog\n\n    The ``max_backlog``  limits the number of threads that can be in the queue\n    waiting for a connection.  Once this limit has been reached, any further\n    attempts to acquire a connection will be rejected immediately, instead of\n    locking up all threads by keeping them waiting in the queue.\n\n    See `dedicated section in SQLAlchemy documentation\n    <http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html>`_\n    for default values and behaviour.\n\n    .. note::\n\n        Using a `dedicated connection pool <http://pgpool.net>`_ is still\n        recommended to allow load balancing, replication or limit the number\n        of connections used in a multi-process deployment.\n\n    :noindex:\n    \"\"\"  # NOQA\n    def __init__(self, client, *args, **kwargs):\n        super(Cache, self).__init__(*args, **kwargs)\n        self.client = client\n\n    def initialize_schema(self, dry_run=False):\n        # Check if cache table exists.\n        query = \"\"\"\n        SELECT 1\n          FROM information_schema.tables\n         WHERE table_name = 'cache';\n        \"\"\"\n        with self.client.connect(readonly=True) as conn:\n            result = conn.execute(query)\n            if result.rowcount > 0:\n                logger.info(\"PostgreSQL cache schema is up-to-date.\")\n                return\n\n        # Create schema\n        here = os.path.abspath(os.path.dirname(__file__))\n        sql_file = os.path.join(here, 'schema.sql')\n\n        if dry_run:\n            logger.info(\"Create cache schema from %s\" % sql_file)\n            return\n\n        # Since called outside request, force commit.\n        schema = open(sql_file).read()\n        with self.client.connect(force_commit=True) as conn:\n            conn.execute(schema)\n        logger.info('Created PostgreSQL cache tables')\n\n    def flush(self):\n        query = \"\"\"\n        DELETE FROM cache;\n        \"\"\"\n        # Since called outside request (e.g. tests), force commit.\n        with self.client.connect(force_commit=True) as conn:\n            conn.execute(query)\n        logger.debug('Flushed PostgreSQL cache tables')\n\n    def ttl(self, key):\n        query = \"\"\"\n        SELECT EXTRACT(SECOND FROM (ttl - now())) AS ttl\n          FROM cache\n         WHERE key = :key\n           AND ttl IS NOT NULL;\n        \"\"\"\n        with self.client.connect(readonly=True) as conn:\n            result = conn.execute(query, dict(key=self.prefix + key))\n            if result.rowcount > 0:\n                return result.fetchone()['ttl']\n        return -1\n\n    def expire(self, key, ttl):\n        query = \"\"\"\n        UPDATE cache SET ttl = sec2ttl(:ttl) WHERE key = :key;\n        \"\"\"\n        with self.client.connect() as conn:\n            conn.execute(query, dict(ttl=ttl, key=self.prefix + key))\n\n    def set(self, key, value, ttl=None):\n        if ttl is None:\n            logger.warning(\"No TTL for cache key %r\" % key)\n        query = \"\"\"\n        WITH upsert AS (\n            UPDATE cache SET value = :value, ttl = sec2ttl(:ttl)\n             WHERE key=:key\n            RETURNING *)\n        INSERT INTO cache (key, value, ttl)\n        SELECT :key, :value, sec2ttl(:ttl)\n        WHERE NOT EXISTS (SELECT * FROM upsert)\n        \"\"\"\n        value = json.dumps(value)\n        with self.client.connect() as conn:\n            conn.execute(query, dict(key=self.prefix + key,\n                                     value=value, ttl=ttl))\n\n    def get(self, key):\n        purge = \"DELETE FROM cache WHERE ttl IS NOT NULL AND now() > ttl;\"\n        query = \"SELECT value FROM cache WHERE key = :key;\"\n        with self.client.connect() as conn:\n            conn.execute(purge)\n            result = conn.execute(query, dict(key=self.prefix + key))\n            if result.rowcount > 0:\n                value = result.fetchone()['value']\n                return json.loads(value)\n\n    def delete(self, key):\n        query = \"DELETE FROM cache WHERE key = :key\"\n        with self.client.connect() as conn:\n            conn.execute(query, dict(key=self.prefix + key))\n\n\ndef load_from_config(config):\n    settings = config.get_settings()\n    client = create_from_config(config, prefix='cache_', with_transaction=False)\n    return Cache(client=client, cache_prefix=settings['cache_prefix'])\n", "path": "kinto/core/cache/postgresql/__init__.py"}], "after_files": [{"content": "from __future__ import absolute_import\nfrom functools import wraps\n\nimport os\nimport time\n\nfrom kinto.core import logger\nfrom kinto.core.cache import CacheBase\nfrom kinto.core.storage.postgresql.client import create_from_config\nfrom kinto.core.storage.exceptions import BackendError\nfrom kinto.core.utils import json\n\n\nDELAY_BETWEEN_RETRIES_IN_SECONDS = 0.005\nMAX_RETRIES = 10\n\n\ndef retry_on_failure(func):\n    try:\n        import psycopg2\n    except ImportError:  # pragma: no cover\n        pass  # Do not break (but will fail nicely later anyway)\n\n    @wraps(func)\n    def wraps_func(self, *args, **kwargs):\n        tries = kwargs.pop('tries', 0)\n        try:\n            return func(self, *args, **kwargs)\n        except psycopg2.IntegrityError as e:\n            if tries < MAX_RETRIES:\n                # Skip delay the 2 first times.\n                delay = max(0, tries - 1) * DELAY_BETWEEN_RETRIES_IN_SECONDS\n                time.sleep(delay)\n                return wraps_func(self, tries=(tries + 1), *args, **kwargs)\n            raise BackendError(original=e)\n    return wraps_func\n\n\nclass Cache(CacheBase):\n    \"\"\"Cache backend using PostgreSQL.\n\n    Enable in configuration::\n\n        kinto.cache_backend = kinto.core.cache.postgresql\n\n    Database location URI can be customized::\n\n        kinto.cache_url = postgres://user:pass@db.server.lan:5432/dbname\n\n    Alternatively, username and password could also rely on system user ident\n    or even specified in :file:`~/.pgpass` (*see PostgreSQL documentation*).\n\n    .. note::\n\n        Some tables and indices are created when ``kinto migrate`` is run.\n        This requires some privileges on the database, or some error will\n        be raised.\n\n        **Alternatively**, the schema can be initialized outside the\n        python application, using the SQL file located in\n        :file:`kinto/core/cache/postgresql/schema.sql`. This allows to\n        distinguish schema manipulation privileges from schema usage.\n\n\n    A connection pool is enabled by default::\n\n        kinto.cache_pool_size = 10\n        kinto.cache_maxoverflow = 10\n        kinto.cache_max_backlog = -1\n        kinto.cache_pool_recycle = -1\n        kinto.cache_pool_timeout = 30\n        kinto.cache_poolclass =\n            kinto.core.storage.postgresql.pool.QueuePoolWithMaxBacklog\n\n    The ``max_backlog``  limits the number of threads that can be in the queue\n    waiting for a connection.  Once this limit has been reached, any further\n    attempts to acquire a connection will be rejected immediately, instead of\n    locking up all threads by keeping them waiting in the queue.\n\n    See `dedicated section in SQLAlchemy documentation\n    <http://docs.sqlalchemy.org/en/rel_1_0/core/engines.html>`_\n    for default values and behaviour.\n\n    .. note::\n\n        Using a `dedicated connection pool <http://pgpool.net>`_ is still\n        recommended to allow load balancing, replication or limit the number\n        of connections used in a multi-process deployment.\n\n    :noindex:\n    \"\"\"  # NOQA\n    def __init__(self, client, *args, **kwargs):\n        super(Cache, self).__init__(*args, **kwargs)\n        self.client = client\n\n    def initialize_schema(self, dry_run=False):\n        # Check if cache table exists.\n        query = \"\"\"\n        SELECT 1\n          FROM information_schema.tables\n         WHERE table_name = 'cache';\n        \"\"\"\n        with self.client.connect(readonly=True) as conn:\n            result = conn.execute(query)\n            if result.rowcount > 0:\n                logger.info(\"PostgreSQL cache schema is up-to-date.\")\n                return\n\n        # Create schema\n        here = os.path.abspath(os.path.dirname(__file__))\n        sql_file = os.path.join(here, 'schema.sql')\n\n        if dry_run:\n            logger.info(\"Create cache schema from %s\" % sql_file)\n            return\n\n        # Since called outside request, force commit.\n        schema = open(sql_file).read()\n        with self.client.connect(force_commit=True) as conn:\n            conn.execute(schema)\n        logger.info('Created PostgreSQL cache tables')\n\n    def flush(self):\n        query = \"\"\"\n        DELETE FROM cache;\n        \"\"\"\n        # Since called outside request (e.g. tests), force commit.\n        with self.client.connect(force_commit=True) as conn:\n            conn.execute(query)\n        logger.debug('Flushed PostgreSQL cache tables')\n\n    def ttl(self, key):\n        query = \"\"\"\n        SELECT EXTRACT(SECOND FROM (ttl - now())) AS ttl\n          FROM cache\n         WHERE key = :key\n           AND ttl IS NOT NULL;\n        \"\"\"\n        with self.client.connect(readonly=True) as conn:\n            result = conn.execute(query, dict(key=self.prefix + key))\n            if result.rowcount > 0:\n                return result.fetchone()['ttl']\n        return -1\n\n    def expire(self, key, ttl):\n        query = \"\"\"\n        UPDATE cache SET ttl = sec2ttl(:ttl) WHERE key = :key;\n        \"\"\"\n        with self.client.connect() as conn:\n            conn.execute(query, dict(ttl=ttl, key=self.prefix + key))\n\n    @retry_on_failure\n    def set(self, key, value, ttl=None):\n        if ttl is None:\n            logger.warning(\"No TTL for cache key %r\" % key)\n        query = \"\"\"\n        WITH upsert AS (\n            UPDATE cache SET value = :value, ttl = sec2ttl(:ttl)\n             WHERE key=:key\n            RETURNING *)\n        INSERT INTO cache (key, value, ttl)\n        SELECT :key, :value, sec2ttl(:ttl)\n        WHERE NOT EXISTS (SELECT * FROM upsert)\n        \"\"\"\n        value = json.dumps(value)\n        with self.client.connect() as conn:\n            conn.execute(query, dict(key=self.prefix + key,\n                                     value=value, ttl=ttl))\n\n    def get(self, key):\n        purge = \"DELETE FROM cache WHERE ttl IS NOT NULL AND now() > ttl;\"\n        query = \"SELECT value FROM cache WHERE key = :key;\"\n        with self.client.connect() as conn:\n            conn.execute(purge)\n            result = conn.execute(query, dict(key=self.prefix + key))\n            if result.rowcount > 0:\n                value = result.fetchone()['value']\n                return json.loads(value)\n\n    def delete(self, key):\n        query = \"DELETE FROM cache WHERE key = :key\"\n        with self.client.connect() as conn:\n            conn.execute(query, dict(key=self.prefix + key))\n\n\ndef load_from_config(config):\n    settings = config.get_settings()\n    client = create_from_config(config, prefix='cache_', with_transaction=False)\n    return Cache(client=client, cache_prefix=settings['cache_prefix'])\n", "path": "kinto/core/cache/postgresql/__init__.py"}]}