We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Storing bytes in the memory backend should convert it to str.
- This is an inconsistency with other backends that returns unicode when storing bytes.
Storing bytes in the memory backend should convert it to str.
- This is an inconsistency with other backends that returns unicode when storing bytes.

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

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

{"golden_diff": "diff --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@@ -56,6 +56,8 @@\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:\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@@ -121,6 +121,9 @@\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", "issue": "Storing bytes in the memory backend should convert it to str.\n- This is an inconsistency with other backends that returns unicode when storing bytes.\nStoring bytes in the memory backend should convert it to str.\n- This is an inconsistency with other backends that returns unicode when storing bytes.\n", "before_files": [{"content": "from kinto import logger\nfrom kinto.core.cache import CacheBase\nfrom kinto.core.utils import msec_time\nfrom kinto.core.decorators import synchronized\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        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 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().__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 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"}], "after_files": [{"content": "from kinto import logger\nfrom kinto.core.cache import CacheBase\nfrom kinto.core.utils import msec_time\nfrom kinto.core.decorators import synchronized\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 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().__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"}]}