We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Should we allow cache entries without ttl?
Or define a max_ttl setting that is always applied ?
Should we allow cache entries without ttl?
Or define a max_ttl setting that is always applied ?

--- 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/memory.py`
Content:
```
1 import logging
2 
3 from kinto.core.cache import CacheBase
4 from kinto.core.utils import msec_time
5 from kinto.core.decorators import synchronized
6 
7 
8 logger = logging.getLogger(__name__)
9 
10 
11 class Cache(CacheBase):
12     """Cache backend implementation in local process memory.
13 
14     Enable in configuration::
15 
16         kinto.cache_backend = kinto.core.cache.memory
17 
18     :noindex:
19     """
20 
21     def __init__(self, *args, **kwargs):
22         super().__init__(*args, **kwargs)
23         self.flush()
24 
25     def initialize_schema(self, dry_run=False):
26         # Nothing to do.
27         pass
28 
29     def flush(self):
30         self._created_at = {}
31         self._ttl = {}
32         self._store = {}
33         self._quota = 0
34 
35     def _clean_expired(self):
36         current = msec_time()
37         expired = [k for k, v in self._ttl.items() if current >= v]
38         for expired_item_key in expired:
39             self.delete(expired_item_key[len(self.prefix):])
40 
41     def _clean_oversized(self):
42         if self._quota < self.max_size_bytes:
43             return
44 
45         for key, value in sorted(self._created_at.items(), key=lambda k: k[1]):
46             if self._quota < (self.max_size_bytes * 0.8):
47                 break
48             self.delete(key[len(self.prefix):])
49 
50     @synchronized
51     def ttl(self, key):
52         ttl = self._ttl.get(self.prefix + key)
53         if ttl is not None:
54             return (ttl - msec_time()) / 1000.0
55         return -1
56 
57     @synchronized
58     def expire(self, key, ttl):
59         self._ttl[self.prefix + key] = msec_time() + int(ttl * 1000.0)
60 
61     @synchronized
62     def set(self, key, value, ttl=None):
63         if isinstance(value, bytes):
64             raise TypeError("a string-like object is required, not 'bytes'")
65         self._clean_expired()
66         self._clean_oversized()
67         if ttl is not None:
68             self.expire(key, ttl)
69         else:
70             logger.warning("No TTL for cache key '{}'".format(key))
71         item_key = self.prefix + key
72         self._store[item_key] = value
73         self._created_at[item_key] = msec_time()
74         self._quota += size_of(item_key, value)
75 
76     @synchronized
77     def get(self, key):
78         self._clean_expired()
79         return self._store.get(self.prefix + key)
80 
81     @synchronized
82     def delete(self, key):
83         key = self.prefix + key
84         self._ttl.pop(key, None)
85         self._created_at.pop(key, None)
86         value = self._store.pop(key, None)
87         self._quota -= size_of(key, value)
88 
89 
90 def load_from_config(config):
91     settings = config.get_settings()
92     return Cache(cache_prefix=settings['cache_prefix'],
93                  cache_max_size_bytes=settings['cache_max_size_bytes'])
94 
95 
96 def size_of(key, value):
97     # Key used for ttl, created_at and store.
98     # Int size is 24 bytes one for ttl and one for created_at values
99     return len(key) * 3 + len(str(value)) + 24 * 2
100
```

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

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 
15     def __init__(self, *args, **kwargs):
16         self.prefix = kwargs['cache_prefix']
17         self.max_size_bytes = kwargs.get('cache_max_size_bytes')
18 
19     def initialize_schema(self, dry_run=False):
20         """Create every necessary objects (like tables or indices) in the
21         backend.
22 
23         This is executed when the ``kinto migrate`` command is run.
24 
25         :param bool dry_run: simulate instead of executing the operations.
26         """
27         raise NotImplementedError
28 
29     def flush(self):
30         """Delete every values."""
31         raise NotImplementedError
32 
33     def ttl(self, key):
34         """Obtain the expiration value of the specified `key`.
35 
36         :param str key: key
37         :returns: number of seconds or negative if no TTL.
38         :rtype: float
39         """
40         raise NotImplementedError
41 
42     def expire(self, key, ttl):
43         """Set the expiration value `ttl` for the specified `key`.
44 
45         :param str key: key
46         :param float ttl: number of seconds
47         """
48         raise NotImplementedError
49 
50     def set(self, key, value, ttl=None):
51         """Store a value with the specified `key`. If `ttl` is provided,
52         set an expiration value.
53 
54         :param str key: key
55         :param str value: value to store
56         :param float ttl: expire after number of seconds
57         """
58         raise NotImplementedError
59 
60     def get(self, key):
61         """Obtain the value of the specified `key`.
62 
63         :param str key: key
64         :returns: the stored value or None if missing.
65         :rtype: str
66         """
67         raise NotImplementedError
68 
69     def delete(self, key):
70         """Delete the value of the specified `key`.
71 
72         :param str key: key
73         """
74         raise NotImplementedError
75 
76 
77 def heartbeat(backend):
78     def ping(request):
79         """Test that cache backend is operationnal.
80 
81         :param request: current request object
82         :type request: :class:`~pyramid:pyramid.request.Request`
83         :returns: ``True`` is everything is ok, ``False`` otherwise.
84         :rtype: bool
85         """
86         # No specific case for readonly mode because the cache should
87         # continue to work in that mode.
88         try:
89             if random.SystemRandom().random() < _HEARTBEAT_DELETE_RATE:
90                 backend.delete(_HEARTBEAT_KEY)
91             else:
92                 backend.set(_HEARTBEAT_KEY, 'alive', _HEARTBEAT_TTL_SECONDS)
93             return True
94         except:
95             logger.exception("Heartbeat Failure")
96             return False
97 
98     return ping
99
```
--- 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()

```

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
@@ -47,9 +47,8 @@
         """
         raise NotImplementedError
 
-    def set(self, key, value, ttl=None):
-        """Store a value with the specified `key`. If `ttl` is provided,
-        set an expiration value.
+    def set(self, key, value, ttl):
+        """Store a value with the specified `key`.
 
         :param str key: key
         :param str value: value to store
diff --git a/kinto/core/cache/memory.py b/kinto/core/cache/memory.py
--- a/kinto/core/cache/memory.py
+++ b/kinto/core/cache/memory.py
@@ -59,15 +59,12 @@
         self._ttl[self.prefix + key] = msec_time() + int(ttl * 1000.0)
 
     @synchronized
-    def set(self, key, value, ttl=None):
+    def set(self, key, value, ttl):
         if isinstance(value, bytes):
             raise TypeError("a string-like object is required, not 'bytes'")
         self._clean_expired()
         self._clean_oversized()
-        if ttl is not None:
-            self.expire(key, ttl)
-        else:
-            logger.warning("No TTL for cache key '{}'".format(key))
+        self.expire(key, ttl)
         item_key = self.prefix + key
         self._store[item_key] = value
         self._created_at[item_key] = msec_time()
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
@@ -123,12 +123,10 @@
         with self.client.connect() as conn:
             conn.execute(query, dict(ttl=ttl, key=self.prefix + key))
 
-    def set(self, key, value, ttl=None):
+    def set(self, key, value, ttl):
         if isinstance(value, bytes):
             raise TypeError("a string-like object is required, not 'bytes'")
 
-        if ttl is None:
-            logger.warning("No TTL for cache key '{}'".format(key))
         query = """
         INSERT INTO cache (key, value, ttl)
         VALUES (:key, :value, sec2ttl(:ttl))

{"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@@ -47,9 +47,8 @@\n         \"\"\"\n         raise NotImplementedError\n \n-    def set(self, key, value, ttl=None):\n-        \"\"\"Store a value with the specified `key`. If `ttl` is provided,\n-        set an expiration value.\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\ndiff --git a/kinto/core/cache/memory.py b/kinto/core/cache/memory.py\n--- a/kinto/core/cache/memory.py\n+++ b/kinto/core/cache/memory.py\n@@ -59,15 +59,12 @@\n         self._ttl[self.prefix + key] = msec_time() + int(ttl * 1000.0)\n \n     @synchronized\n-    def set(self, key, value, ttl=None):\n+    def set(self, key, value, ttl):\n         if isinstance(value, bytes):\n             raise TypeError(\"a string-like object is required, not 'bytes'\")\n         self._clean_expired()\n         self._clean_oversized()\n-        if ttl is not None:\n-            self.expire(key, ttl)\n-        else:\n-            logger.warning(\"No TTL for cache key '{}'\".format(key))\n+        self.expire(key, ttl)\n         item_key = self.prefix + key\n         self._store[item_key] = value\n         self._created_at[item_key] = msec_time()\ndiff --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@@ -123,12 +123,10 @@\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+    def set(self, key, value, ttl):\n         if isinstance(value, bytes):\n             raise TypeError(\"a string-like object is required, not 'bytes'\")\n \n-        if ttl is None:\n-            logger.warning(\"No TTL for cache key '{}'\".format(key))\n         query = \"\"\"\n         INSERT INTO cache (key, value, ttl)\n         VALUES (:key, :value, sec2ttl(:ttl))\n", "issue": "Should we allow cache entries without ttl?\nOr define a max_ttl setting that is always applied ?\nShould we allow cache entries without ttl?\nOr define a max_ttl setting that is always applied ?\n", "before_files": [{"content": "import logging\n\nfrom kinto.core.cache import CacheBase\nfrom kinto.core.utils import msec_time\nfrom kinto.core.decorators import synchronized\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass Cache(CacheBase):\n    \"\"\"Cache backend implementation in local process memory.\n\n    Enable in configuration::\n\n        kinto.cache_backend = kinto.core.cache.memory\n\n    :noindex:\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.flush()\n\n    def initialize_schema(self, dry_run=False):\n        # Nothing to do.\n        pass\n\n    def flush(self):\n        self._created_at = {}\n        self._ttl = {}\n        self._store = {}\n        self._quota = 0\n\n    def _clean_expired(self):\n        current = msec_time()\n        expired = [k for k, v in self._ttl.items() if current >= v]\n        for expired_item_key in expired:\n            self.delete(expired_item_key[len(self.prefix):])\n\n    def _clean_oversized(self):\n        if self._quota < self.max_size_bytes:\n            return\n\n        for key, value in sorted(self._created_at.items(), key=lambda k: k[1]):\n            if self._quota < (self.max_size_bytes * 0.8):\n                break\n            self.delete(key[len(self.prefix):])\n\n    @synchronized\n    def ttl(self, key):\n        ttl = self._ttl.get(self.prefix + key)\n        if ttl is not None:\n            return (ttl - msec_time()) / 1000.0\n        return -1\n\n    @synchronized\n    def expire(self, key, ttl):\n        self._ttl[self.prefix + key] = msec_time() + int(ttl * 1000.0)\n\n    @synchronized\n    def set(self, key, value, ttl=None):\n        if isinstance(value, bytes):\n            raise TypeError(\"a string-like object is required, not 'bytes'\")\n        self._clean_expired()\n        self._clean_oversized()\n        if ttl is not None:\n            self.expire(key, ttl)\n        else:\n            logger.warning(\"No TTL for cache key '{}'\".format(key))\n        item_key = self.prefix + key\n        self._store[item_key] = value\n        self._created_at[item_key] = msec_time()\n        self._quota += size_of(item_key, value)\n\n    @synchronized\n    def get(self, key):\n        self._clean_expired()\n        return self._store.get(self.prefix + key)\n\n    @synchronized\n    def delete(self, key):\n        key = self.prefix + key\n        self._ttl.pop(key, None)\n        self._created_at.pop(key, None)\n        value = self._store.pop(key, None)\n        self._quota -= size_of(key, value)\n\n\ndef load_from_config(config):\n    settings = config.get_settings()\n    return Cache(cache_prefix=settings['cache_prefix'],\n                 cache_max_size_bytes=settings['cache_max_size_bytes'])\n\n\ndef size_of(key, value):\n    # Key used for ttl, created_at and store.\n    # Int size is 24 bytes one for ttl and one for created_at values\n    return len(key) * 3 + len(str(value)) + 24 * 2\n", "path": "kinto/core/cache/memory.py"}, {"content": "import logging\nimport os\n\nfrom kinto.core.cache import CacheBase\nfrom kinto.core.storage.postgresql.client import create_from_config\nfrom kinto.core.utils import json\n\n\nlogger = logging.getLogger(__name__)\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().__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 '{}'\".format(sql_file))\n            return\n\n        # Since called outside request, force commit.\n        with open(sql_file) as f:\n            schema = f.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 isinstance(value, bytes):\n            raise TypeError(\"a string-like object is required, not 'bytes'\")\n\n        if ttl is None:\n            logger.warning(\"No TTL for cache key '{}'\".format(key))\n        query = \"\"\"\n        INSERT INTO cache (key, value, ttl)\n        VALUES (:key, :value, sec2ttl(:ttl))\n        ON CONFLICT (key) DO UPDATE\n        SET value = :value,\n            ttl = sec2ttl(:ttl);\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"}, {"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\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=None):\n        \"\"\"Store a value with the specified `key`. If `ttl` is provided,\n        set an expiration value.\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 operationnal.\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:\n            logger.exception(\"Heartbeat Failure\")\n            return False\n\n    return ping\n", "path": "kinto/core/cache/__init__.py"}], "after_files": [{"content": "import logging\n\nfrom kinto.core.cache import CacheBase\nfrom kinto.core.utils import msec_time\nfrom kinto.core.decorators import synchronized\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass Cache(CacheBase):\n    \"\"\"Cache backend implementation in local process memory.\n\n    Enable in configuration::\n\n        kinto.cache_backend = kinto.core.cache.memory\n\n    :noindex:\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.flush()\n\n    def initialize_schema(self, dry_run=False):\n        # Nothing to do.\n        pass\n\n    def flush(self):\n        self._created_at = {}\n        self._ttl = {}\n        self._store = {}\n        self._quota = 0\n\n    def _clean_expired(self):\n        current = msec_time()\n        expired = [k for k, v in self._ttl.items() if current >= v]\n        for expired_item_key in expired:\n            self.delete(expired_item_key[len(self.prefix):])\n\n    def _clean_oversized(self):\n        if self._quota < self.max_size_bytes:\n            return\n\n        for key, value in sorted(self._created_at.items(), key=lambda k: k[1]):\n            if self._quota < (self.max_size_bytes * 0.8):\n                break\n            self.delete(key[len(self.prefix):])\n\n    @synchronized\n    def ttl(self, key):\n        ttl = self._ttl.get(self.prefix + key)\n        if ttl is not None:\n            return (ttl - msec_time()) / 1000.0\n        return -1\n\n    @synchronized\n    def expire(self, key, ttl):\n        self._ttl[self.prefix + key] = msec_time() + int(ttl * 1000.0)\n\n    @synchronized\n    def set(self, key, value, ttl):\n        if isinstance(value, bytes):\n            raise TypeError(\"a string-like object is required, not 'bytes'\")\n        self._clean_expired()\n        self._clean_oversized()\n        self.expire(key, ttl)\n        item_key = self.prefix + key\n        self._store[item_key] = value\n        self._created_at[item_key] = msec_time()\n        self._quota += size_of(item_key, value)\n\n    @synchronized\n    def get(self, key):\n        self._clean_expired()\n        return self._store.get(self.prefix + key)\n\n    @synchronized\n    def delete(self, key):\n        key = self.prefix + key\n        self._ttl.pop(key, None)\n        self._created_at.pop(key, None)\n        value = self._store.pop(key, None)\n        self._quota -= size_of(key, value)\n\n\ndef load_from_config(config):\n    settings = config.get_settings()\n    return Cache(cache_prefix=settings['cache_prefix'],\n                 cache_max_size_bytes=settings['cache_max_size_bytes'])\n\n\ndef size_of(key, value):\n    # Key used for ttl, created_at and store.\n    # Int size is 24 bytes one for ttl and one for created_at values\n    return len(key) * 3 + len(str(value)) + 24 * 2\n", "path": "kinto/core/cache/memory.py"}, {"content": "import logging\nimport os\n\nfrom kinto.core.cache import CacheBase\nfrom kinto.core.storage.postgresql.client import create_from_config\nfrom kinto.core.utils import json\n\n\nlogger = logging.getLogger(__name__)\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().__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 '{}'\".format(sql_file))\n            return\n\n        # Since called outside request, force commit.\n        with open(sql_file) as f:\n            schema = f.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):\n        if isinstance(value, bytes):\n            raise TypeError(\"a string-like object is required, not 'bytes'\")\n\n        query = \"\"\"\n        INSERT INTO cache (key, value, ttl)\n        VALUES (:key, :value, sec2ttl(:ttl))\n        ON CONFLICT (key) DO UPDATE\n        SET value = :value,\n            ttl = sec2ttl(:ttl);\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"}, {"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\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 operationnal.\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:\n            logger.exception(\"Heartbeat Failure\")\n            return False\n\n    return ping\n", "path": "kinto/core/cache/__init__.py"}]}