The checker requires Redis

Remove the abstraction in searx.shared.SharedDict.
Implement a basic and dedicated scheduler for the checker using a Redis script.
This commit is contained in:
Alexandre Flament 2022-07-15 18:38:32 +02:00
parent d764d94a70
commit fe419e355b
12 changed files with 167 additions and 237 deletions

View file

@ -1,39 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# lint: pylint
"""Initialization of a *shared* storage.
"""
import logging
import importlib
logger = logging.getLogger('searx.shared')
__all__ = ['SharedDict', 'schedule']
try:
uwsgi = importlib.import_module('uwsgi')
except:
# no uwsgi
from .shared_simple import SimpleSharedDict as SharedDict, schedule
logger.info('Use shared_simple implementation')
else:
try:
uwsgi.cache_update('dummy', b'dummy')
if uwsgi.cache_get('dummy') != b'dummy':
raise Exception()
except:
# uwsgi.ini configuration problem: disable all scheduling
logger.error(
'uwsgi.ini configuration error, add this line to your uwsgi.ini\n'
'cache2 = name=searxngcache,items=2000,blocks=2000,blocksize=4096,bitmap=1'
)
from .shared_simple import SimpleSharedDict as SharedDict
def schedule(delay, func, *args):
return False
else:
# uwsgi
from .shared_uwsgi import UwsgiCacheSharedDict as SharedDict, schedule
logger.info('Use shared_uwsgi implementation')
storage = SharedDict()
from . import redisdb

View file

@ -26,26 +26,20 @@ import redis
from searx import get_setting
logger = logging.getLogger('searx.shared.redis')
logger = logging.getLogger('searx.shared.redisdb')
_client = None
def client():
global _client # pylint: disable=global-statement
if _client is None:
# not thread safe: in the worst case scenario, two or more clients are
# initialized only one is kept, the others are garbage collected.
_client = redis.Redis.from_url(get_setting('redis.url'))
def client() -> redis.Redis:
return _client
def init():
def initialize():
global _client # pylint: disable=global-statement
try:
c = client()
logger.info("connected redis DB --> %s", c.acl_whoami())
return True
_client = redis.Redis.from_url(get_setting('redis.url'))
logger.info("connected redis: %s", get_setting('redis.url'))
except redis.exceptions.ConnectionError as exc:
_pw = pwd.getpwuid(os.getuid())
logger.error("[%s (%s)] can't connect redis DB ...", _pw.pw_name, _pw.pw_uid)
logger.error(" %s", exc)
return False

View file

@ -1,22 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pyright: strict
from abc import ABC, abstractmethod
from typing import Optional
class SharedDict(ABC):
@abstractmethod
def get_int(self, key: str) -> Optional[int]:
pass
@abstractmethod
def set_int(self, key: str, value: int):
pass
@abstractmethod
def get_str(self, key: str) -> Optional[str]:
pass
@abstractmethod
def set_str(self, key: str, value: str):
pass

View file

@ -1,40 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import threading
from typing import Optional
from . import shared_abstract
class SimpleSharedDict(shared_abstract.SharedDict):
__slots__ = ('d',)
def __init__(self):
self.d = {}
def get_int(self, key: str) -> Optional[int]:
return self.d.get(key, None)
def set_int(self, key: str, value: int):
self.d[key] = value
def get_str(self, key: str) -> Optional[str]:
return self.d.get(key, None)
def set_str(self, key: str, value: str):
self.d[key] = value
def schedule(delay, func, *args):
def call_later():
t = threading.Timer(delay, wrapper)
t.daemon = True
t.start()
def wrapper():
call_later()
func(*args)
call_later()
return True

View file

@ -1,64 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
import time
from typing import Optional
import uwsgi # pyright: ignore # pylint: disable=E0401
from . import shared_abstract
_last_signal = 10
class UwsgiCacheSharedDict(shared_abstract.SharedDict):
def get_int(self, key: str) -> Optional[int]:
value = uwsgi.cache_get(key)
if value is None:
return value
else:
return int.from_bytes(value, 'big')
def set_int(self, key: str, value: int):
b = value.to_bytes(4, 'big')
uwsgi.cache_update(key, b)
def get_str(self, key: str) -> Optional[str]:
value = uwsgi.cache_get(key)
if value is None:
return value
else:
return value.decode('utf-8')
def set_str(self, key: str, value: str):
b = value.encode('utf-8')
uwsgi.cache_update(key, b)
def schedule(delay, func, *args):
"""
Can be implemented using a spooler.
https://uwsgi-docs.readthedocs.io/en/latest/PythonDecorators.html
To make the uwsgi configuration simple, use the alternative implementation.
"""
global _last_signal
def sighandler(signum):
now = int(time.time())
key = 'scheduler_call_time_signal_' + str(signum)
uwsgi.lock()
try:
updating = uwsgi.cache_get(key)
if updating is not None:
updating = int.from_bytes(updating, 'big')
if now - updating < delay:
return
uwsgi.cache_update(key, now.to_bytes(4, 'big'))
finally:
uwsgi.unlock()
func(*args)
signal_num = _last_signal
_last_signal += 1
uwsgi.register_signal(signal_num, 'worker', sighandler)
uwsgi.add_timer(signal_num, delay)
return True