-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b53c73a
commit 2fd687d
Showing
22 changed files
with
687 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from typing import Annotated | ||
|
||
from asgikit.responses import respond_json | ||
from selva.di import Inject | ||
from selva.web import controller, get | ||
|
||
from .service import RedisService | ||
|
||
|
||
@controller | ||
class Controller: | ||
redis_service: Annotated[RedisService, Inject] | ||
|
||
@get | ||
async def index(self, request): | ||
number = await self.redis_service.get_incr() | ||
await respond_json(request.response, {"number": number}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
from typing import Annotated | ||
|
||
from redis.asyncio import Redis | ||
|
||
from selva.di import service, Inject | ||
|
||
|
||
@service | ||
class RedisService: | ||
redis: Annotated[Redis, Inject] | ||
|
||
async def initialize(self): | ||
await self.redis.set("number", 0) | ||
|
||
async def get_incr(self) -> int: | ||
return await self.redis.incr("number") | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
extensions: | ||
- selva.ext.data.redis | ||
|
||
data: | ||
redis: | ||
default: | ||
host: "localhost" | ||
options: | ||
retry: | ||
retries: 1 | ||
backoff: | ||
constant: | ||
backoff: 1 | ||
|
||
other: | ||
url: "redis://localhost:6379/1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from importlib.util import find_spec | ||
|
||
from selva.configuration.settings import Settings | ||
from selva.di.container import Container | ||
|
||
from .service import make_service | ||
|
||
|
||
def selva_extension(container: Container, settings: Settings): | ||
if find_spec("redis") is None: | ||
return | ||
|
||
for name in settings.data.redis: | ||
service_name = name if name != "default" else None | ||
|
||
container.register(make_service(name), name=service_name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
from redis.asyncio import Redis | ||
from redis.backoff import ( | ||
AbstractBackoff, | ||
ConstantBackoff, | ||
DecorrelatedJitterBackoff, | ||
EqualJitterBackoff, | ||
ExponentialBackoff, | ||
FullJitterBackoff, | ||
NoBackoff, | ||
) | ||
from redis.retry import Retry | ||
|
||
from selva.configuration.settings import Settings | ||
from selva.ext.data.redis.settings import RedisSettings | ||
|
||
from .settings import BackoffSchema, RetrySchema | ||
|
||
|
||
def build_backoff(data: BackoffSchema) -> AbstractBackoff: | ||
if "no_backoff" in data.model_fields_set: | ||
return NoBackoff() | ||
|
||
if value := data.constant: | ||
return ConstantBackoff(**value.model_dump(exclude_unset=True)) | ||
|
||
if value := data.exponential: | ||
return ExponentialBackoff(**value.model_dump(exclude_unset=True)) | ||
|
||
if value := data.full_jitter: | ||
return FullJitterBackoff(**value.model_dump(exclude_unset=True)) | ||
|
||
if value := data.equal_jitter: | ||
return EqualJitterBackoff(**value.model_dump(exclude_unset=True)) | ||
|
||
if value := data.decorrelated_jitter: | ||
return DecorrelatedJitterBackoff(**value.model_dump(exclude_unset=True)) | ||
|
||
|
||
def build_retry(data: RetrySchema): | ||
kwargs = { | ||
"backoff": build_backoff(data.backoff), | ||
"retries": data.retries, | ||
} | ||
|
||
if supported_errors := data.supported_errors: | ||
kwargs["supported_errors"] = supported_errors | ||
|
||
return Retry(**kwargs) | ||
|
||
|
||
def make_service(name: str): | ||
async def redis_service( | ||
settings: Settings, | ||
) -> Redis: | ||
redis_settings = RedisSettings.model_validate(dict(settings.data.redis[name])) | ||
|
||
kwargs = redis_settings.model_dump(exclude_unset=True) | ||
if (options := redis_settings.options) and (retry := options.retry): | ||
kwargs["retry"] = build_retry(retry) | ||
|
||
if url := kwargs.pop("url", ""): | ||
redis = Redis.from_url(url, **kwargs) | ||
else: | ||
redis = Redis(**kwargs) | ||
|
||
await redis.initialize() | ||
yield redis | ||
await redis.aclose() | ||
|
||
return redis_service |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
from collections.abc import Callable | ||
from types import NoneType | ||
from typing import Self, Type | ||
|
||
from pydantic import BaseModel, model_serializer, model_validator | ||
from redis import RedisError | ||
from redis.credentials import CredentialProvider | ||
|
||
from selva._util.pydantic import DottedPath | ||
|
||
|
||
class ConstantBackoffSchema(BaseModel): | ||
backoff: int | ||
|
||
|
||
class ExponentialBackoffSchema(BaseModel): | ||
cap: int = None | ||
base: int = None | ||
|
||
|
||
class BackoffSchema(BaseModel): | ||
no_backoff: NoneType = None | ||
constant: ConstantBackoffSchema = None | ||
exponential: ExponentialBackoffSchema = None | ||
full_jitter: ExponentialBackoffSchema = None | ||
equal_jitter: ExponentialBackoffSchema = None | ||
decorrelated_jitter: ExponentialBackoffSchema = None | ||
|
||
@model_validator(mode="before") | ||
def validator(cls, data): | ||
if len(data) != 1: | ||
raise ValueError("Only one backoff value can be set") | ||
|
||
return data | ||
|
||
|
||
class RetrySchema(BaseModel): | ||
backoff: BackoffSchema | ||
retries: int | ||
supported_errors: tuple[DottedPath[Type[RedisError]], ...] = None | ||
|
||
|
||
class RedisOptions(BaseModel): | ||
socket_timeout: float = None | ||
socket_connect_timeout: float = None | ||
socket_keepalive: bool = None | ||
socket_keepalive_options: dict[int, int | bytes] = None | ||
unix_socket_path: str = None | ||
encoding: str = None | ||
encoding_errors: str = None | ||
decode_responses: bool = None | ||
retry_on_timeout: bool = None | ||
retry_on_error: list = None | ||
ssl: bool = None | ||
ssl_keyfile: str = None | ||
ssl_certfile: str = None | ||
ssl_cert_reqs: str = None | ||
ssl_ca_certs: str = None | ||
ssl_ca_data: str = None | ||
ssl_check_hostname: bool = None | ||
max_connections: int = None | ||
single_connection_client: bool = None | ||
health_check_interval: int = None | ||
client_name: str = None | ||
lib_name: str = None | ||
lib_version: str = None | ||
retry: RetrySchema = None | ||
auto_close_connection_pool: bool = None | ||
redis_connect_func: DottedPath[Callable] = None | ||
credential_provider: DottedPath[CredentialProvider] = None | ||
protocol: int = None | ||
|
||
|
||
class RedisSettings(BaseModel): | ||
url: str = None | ||
host: str = None | ||
port: int = None | ||
db: int = None | ||
username: str = None | ||
password: str = None | ||
options: RedisOptions = None | ||
|
||
@model_validator(mode="after") | ||
def validator(self) -> Self: | ||
if self.url and (self.host or self.port or (self.db is not None)): | ||
raise ValueError( | ||
"Either 'url' should be provided, or 'host', 'port' and 'db'" | ||
) | ||
|
||
return self | ||
|
||
@model_serializer(when_used="unless-none") | ||
def serializer(self): | ||
data = { | ||
field: getattr(self, field) | ||
for field in self.model_fields_set | ||
if field != "options" | ||
} | ||
|
||
if self.options: | ||
data |= self.options.model_dump(exclude_unset=True) | ||
|
||
return data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.