by-framework 0.2.2.dev6__py3-none-any.whl → 0.2.2.dev8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
by_framework/admin/cli.py CHANGED
@@ -13,7 +13,8 @@ from rich.console import Console
13
13
  from rich.table import Table
14
14
 
15
15
  from by_framework.admin.worker_manager import WorkerManager
16
- from by_framework.common.redis_client import init_redis_from_url
16
+ from by_framework.common.config import RedisConfig
17
+ from by_framework.common.redis_client import init_redis, init_redis_from_url
17
18
  from by_framework.core.registry import WorkerRegistry
18
19
  from by_framework.metrics.snapshot import (
19
20
  build_observability_snapshot,
@@ -27,7 +28,12 @@ from by_framework.metrics.snapshot import (
27
28
  app = typer.Typer(
28
29
  name="by-admin",
29
30
  no_args_is_help=True,
30
- help="by-framework cluster admin CLI",
31
+ help=(
32
+ "by-framework cluster admin CLI. For Redis Cluster, omit --redis-url "
33
+ "and set REDIS_CLUSTER_HOST (comma-separated host:port list; implies "
34
+ "cluster mode) and REDIS_KEY_SCHEMA_VERSION=v2. REDIS_MODE=cluster + "
35
+ "REDIS_CLUSTER_NODES also still works."
36
+ ),
31
37
  )
32
38
  worker_app = typer.Typer(no_args_is_help=True)
33
39
  type_app = typer.Typer(no_args_is_help=True)
@@ -43,7 +49,8 @@ app.add_typer(metrics_app, name="metrics", help="Metrics and observability")
43
49
 
44
50
  console = Console()
45
51
  err_console = Console(stderr=True)
46
- _redis_url = "redis://localhost:6379/0"
52
+ _DEFAULT_REDIS_URL = "redis://localhost:6379/0"
53
+ _redis_url: Optional[str] = None
47
54
 
48
55
 
49
56
  @app.callback()
@@ -52,13 +59,16 @@ def _global(
52
59
  None,
53
60
  "--redis-url",
54
61
  envvar=["BYAI_REDIS_URL", "REDIS_URL"],
55
- help="Redis connection URL [env: BYAI_REDIS_URL]",
62
+ help=(
63
+ "Standalone Redis connection URL [env: BYAI_REDIS_URL]. "
64
+ "For Redis Cluster, omit this and set REDIS_CLUSTER_HOST "
65
+ "(comma-separated host:port list)."
66
+ ),
56
67
  ),
57
68
  ):
58
69
  """by-framework cluster admin CLI."""
59
70
  global _redis_url
60
- if redis_url:
61
- _redis_url = redis_url
71
+ _redis_url = redis_url
62
72
 
63
73
 
64
74
  def _run(coro): # type: ignore[no-untyped-def]
@@ -70,8 +80,14 @@ def _die(msg: str, code: int = 1) -> None:
70
80
  raise typer.Exit(code)
71
81
 
72
82
 
73
- def _get_redis():
74
- return init_redis_from_url(_redis_url)
83
+ def _get_redis(redis_url: Optional[str] = None):
84
+ configured_url = redis_url if redis_url is not None else _redis_url
85
+ if configured_url:
86
+ return init_redis_from_url(configured_url)
87
+ config = RedisConfig.from_env()
88
+ if config.mode == "cluster":
89
+ return init_redis(config=config)
90
+ return init_redis_from_url(_DEFAULT_REDIS_URL)
75
91
 
76
92
 
77
93
  # --------------------------------------------------------------------------- #
@@ -6,7 +6,7 @@ Provides typed configuration loaded from environment variables.
6
6
 
7
7
  import os
8
8
  from dataclasses import dataclass, field
9
- from typing import Optional
9
+ from typing import Literal, Optional
10
10
 
11
11
  from by_framework.common.constants import RedisKeys
12
12
 
@@ -22,13 +22,35 @@ class RedisConfig:
22
22
  username: Optional[str] = None
23
23
  decode_responses: bool = True
24
24
  max_connections: Optional[int] = None
25
+ mode: Literal["standalone", "cluster"] = "standalone"
26
+ cluster_nodes: Optional[list[tuple[str, int]]] = None
27
+ socket_connect_timeout: int = 5
28
+ socket_timeout: int = 10
25
29
 
26
30
  @classmethod
27
31
  def from_env(cls) -> "RedisConfig":
28
- """Load Redis configuration from environment variables."""
32
+ """Load Redis configuration from environment variables.
33
+
34
+ REDIS_CLUSTER_HOST (comma-separated "host:port" list) is the
35
+ preferred way to configure Cluster mode: just setting it is enough
36
+ to switch to cluster mode, no separate REDIS_MODE=cluster needed.
37
+ An explicit REDIS_MODE still takes precedence when set, so existing
38
+ explicit REDIS_MODE=cluster + REDIS_CLUSTER_NODES setups keep working.
39
+ """
29
40
  password = os.environ.get("REDIS_PASSWORD", "")
30
41
  username = os.environ.get("REDIS_USERNAME") or None
31
42
  max_connections = os.environ.get("REDIS_MAX_CONNECTIONS")
43
+ cluster_host_str = os.environ.get("REDIS_CLUSTER_HOST")
44
+ cluster_nodes_str = cluster_host_str or os.environ.get("REDIS_CLUSTER_NODES")
45
+ mode = os.environ.get("REDIS_MODE") or (
46
+ "cluster" if cluster_host_str else "standalone"
47
+ )
48
+ cluster_nodes = None
49
+ if cluster_nodes_str:
50
+ cluster_nodes = []
51
+ for node in cluster_nodes_str.split(","):
52
+ node_host, node_port = node.rsplit(":", 1)
53
+ cluster_nodes.append((node_host, int(node_port)))
32
54
  return cls(
33
55
  host=os.environ.get("REDIS_HOST", "localhost"),
34
56
  port=int(os.environ.get("REDIS_PORT", "6379")),
@@ -36,6 +58,8 @@ class RedisConfig:
36
58
  password=password,
37
59
  username=username,
38
60
  max_connections=int(max_connections) if max_connections else None,
61
+ mode=mode,
62
+ cluster_nodes=cluster_nodes,
39
63
  )
40
64
 
41
65