mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
FastAPI: add redis integration
This includes the addition of the python-fakeredis package, used for stubbing python-redis when a user does not have a configured cache. Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
96d1af9363
commit
91e769f603
7 changed files with 123 additions and 3 deletions
2
INSTALL
2
INSTALL
|
@ -57,7 +57,7 @@ read the instructions below.
|
||||||
|
|
||||||
(FastAPI-Specific)
|
(FastAPI-Specific)
|
||||||
|
|
||||||
# pacman -S redis python-redis
|
# pacman -S redis python-redis python-fakeredis
|
||||||
# systemctl enable --now redis
|
# systemctl enable --now redis
|
||||||
|
|
||||||
5) Create a new MySQL database and a user and import the aurweb SQL schema:
|
5) Create a new MySQL database and a user and import the aurweb SQL schema:
|
||||||
|
|
57
aurweb/redis.py
Normal file
57
aurweb/redis.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import fakeredis
|
||||||
|
|
||||||
|
from redis import ConnectionPool, Redis
|
||||||
|
|
||||||
|
import aurweb.config
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
pool = None
|
||||||
|
|
||||||
|
|
||||||
|
class FakeConnectionPool:
|
||||||
|
""" A fake ConnectionPool class which holds an internal reference
|
||||||
|
to a fakeredis handle.
|
||||||
|
|
||||||
|
We normally deal with Redis by keeping its ConnectionPool globally
|
||||||
|
referenced so we can persist connection state through different calls
|
||||||
|
to redis_connection(), and since FakeRedis does not offer a ConnectionPool,
|
||||||
|
we craft one up here to hang onto the same handle instance as long as the
|
||||||
|
same instance is alive; this allows us to use a similar flow from the
|
||||||
|
redis_connection() user's perspective.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.handle = fakeredis.FakeStrictRedis()
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def redis_connection(): # pragma: no cover
|
||||||
|
global pool
|
||||||
|
|
||||||
|
disabled = aurweb.config.get("options", "cache") != "redis"
|
||||||
|
|
||||||
|
# If we haven't initialized redis yet, construct a pool.
|
||||||
|
if disabled:
|
||||||
|
logger.debug("Initializing fake Redis instance.")
|
||||||
|
if pool is None:
|
||||||
|
pool = FakeConnectionPool()
|
||||||
|
return pool.handle
|
||||||
|
else:
|
||||||
|
logger.debug("Initializing real Redis instance.")
|
||||||
|
if pool is None:
|
||||||
|
redis_addr = aurweb.config.get("options", "redis_address")
|
||||||
|
pool = ConnectionPool.from_url(redis_addr)
|
||||||
|
|
||||||
|
# Create a connection to the pool.
|
||||||
|
return Redis(connection_pool=pool)
|
||||||
|
|
||||||
|
|
||||||
|
def kill_redis():
|
||||||
|
global pool
|
||||||
|
if pool:
|
||||||
|
pool.disconnect()
|
||||||
|
pool = None
|
|
@ -36,11 +36,13 @@ enable-maintenance = 1
|
||||||
maintenance-exceptions = 127.0.0.1
|
maintenance-exceptions = 127.0.0.1
|
||||||
render-comment-cmd = /usr/local/bin/aurweb-rendercomment
|
render-comment-cmd = /usr/local/bin/aurweb-rendercomment
|
||||||
localedir = /srv/http/aurweb/aur.git/web/locale/
|
localedir = /srv/http/aurweb/aur.git/web/locale/
|
||||||
# memcache or apc
|
; memcache, apc, or redis
|
||||||
|
; memcache/apc are supported in PHP, redis is supported in Python.
|
||||||
cache = none
|
cache = none
|
||||||
cache_pkginfo_ttl = 86400
|
cache_pkginfo_ttl = 86400
|
||||||
memcache_servers = 127.0.0.1:11211
|
memcache_servers = 127.0.0.1:11211
|
||||||
salt_rounds = 12
|
salt_rounds = 12
|
||||||
|
redis_address = redis://localhost
|
||||||
|
|
||||||
[ratelimit]
|
[ratelimit]
|
||||||
request_limit = 4000
|
request_limit = 4000
|
||||||
|
|
|
@ -28,10 +28,13 @@ enable-maintenance = 0
|
||||||
localedir = YOUR_AUR_ROOT/web/locale
|
localedir = YOUR_AUR_ROOT/web/locale
|
||||||
; In production, salt_rounds should be higher; suggested: 12.
|
; In production, salt_rounds should be higher; suggested: 12.
|
||||||
salt_rounds = 4
|
salt_rounds = 4
|
||||||
|
; See config.defaults comment about cache.
|
||||||
cache = none
|
cache = none
|
||||||
; In docker, the memcached host is available. On a user's system,
|
; In docker, the memcached host is available. On a user's system,
|
||||||
; this should be set to localhost (most likely).
|
; this should be set to localhost (most likely).
|
||||||
memcache_servers = memcached:11211
|
memcache_servers = memcached:11211
|
||||||
|
; If cache = 'redis' this address is used to connect to Redis.
|
||||||
|
redis_address = redis://127.0.0.1
|
||||||
|
|
||||||
[notifications]
|
[notifications]
|
||||||
; For development/testing, use /usr/bin/sendmail
|
; For development/testing, use /usr/bin/sendmail
|
||||||
|
|
|
@ -15,6 +15,6 @@ pacman -Syu --noconfirm --noprogressbar \
|
||||||
python-email-validator openssh python-lxml mariadb mariadb-libs \
|
python-email-validator openssh python-lxml mariadb mariadb-libs \
|
||||||
python-isort flake8 cgit uwsgi uwsgi-plugin-cgi php php-fpm \
|
python-isort flake8 cgit uwsgi uwsgi-plugin-cgi php php-fpm \
|
||||||
python-asgiref uvicorn python-feedgen memcached php-memcached \
|
python-asgiref uvicorn python-feedgen memcached php-memcached \
|
||||||
python-redis redis
|
python-redis redis python-fakeredis
|
||||||
|
|
||||||
exec "$@"
|
exec "$@"
|
||||||
|
|
|
@ -9,6 +9,24 @@ from fastapi import HTTPException
|
||||||
|
|
||||||
import aurweb.asgi
|
import aurweb.asgi
|
||||||
import aurweb.config
|
import aurweb.config
|
||||||
|
import aurweb.redis
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_asgi_startup_session_secret_exception(monkeypatch):
|
||||||
|
""" Test that we get an IOError on app_startup when we cannot
|
||||||
|
connect to options.redis_address. """
|
||||||
|
|
||||||
|
redis_addr = aurweb.config.get("options", "redis_address")
|
||||||
|
|
||||||
|
def mock_get(section: str, key: str):
|
||||||
|
if section == "fastapi" and key == "session_secret":
|
||||||
|
return None
|
||||||
|
return redis_addr
|
||||||
|
|
||||||
|
with mock.patch("aurweb.config.get", side_effect=mock_get):
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
await aurweb.asgi.app_startup()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|
40
test/test_redis.py
Normal file
40
test/test_redis.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import aurweb.config
|
||||||
|
|
||||||
|
from aurweb.redis import redis_connection
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def rediss():
|
||||||
|
""" Create a RedisStub. """
|
||||||
|
def mock_get(section, key):
|
||||||
|
return "none"
|
||||||
|
|
||||||
|
with mock.patch("aurweb.config.get", side_effect=mock_get):
|
||||||
|
aurweb.config.rehash()
|
||||||
|
redis = redis_connection()
|
||||||
|
aurweb.config.rehash()
|
||||||
|
|
||||||
|
yield redis
|
||||||
|
|
||||||
|
|
||||||
|
def test_redis_stub(rediss):
|
||||||
|
# We don't yet have a test key set.
|
||||||
|
assert rediss.get("test") is None
|
||||||
|
|
||||||
|
# Set the test key to abc.
|
||||||
|
rediss.set("test", "abc")
|
||||||
|
assert rediss.get("test").decode() == "abc"
|
||||||
|
|
||||||
|
# Test expire.
|
||||||
|
rediss.expire("test", 0)
|
||||||
|
assert rediss.get("test") is None
|
||||||
|
|
||||||
|
# Now, set the test key again and use delete() on it.
|
||||||
|
rediss.set("test", "abc")
|
||||||
|
assert rediss.get("test").decode() == "abc"
|
||||||
|
rediss.delete("test")
|
||||||
|
assert rediss.get("test") is None
|
Loading…
Add table
Reference in a new issue