karrio-server 2025.5rc32__py3-none-any.whl → 2025.5rc34__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.
- karrio/server/VERSION +1 -1
- karrio/server/settings/base.py +2 -0
- karrio/server/settings/cache.py +27 -6
- karrio/server/settings/workers.py +114 -52
- {karrio_server-2025.5rc32.dist-info → karrio_server-2025.5rc34.dist-info}/METADATA +1 -1
- {karrio_server-2025.5rc32.dist-info → karrio_server-2025.5rc34.dist-info}/RECORD +9 -9
- {karrio_server-2025.5rc32.dist-info → karrio_server-2025.5rc34.dist-info}/WHEEL +0 -0
- {karrio_server-2025.5rc32.dist-info → karrio_server-2025.5rc34.dist-info}/entry_points.txt +0 -0
- {karrio_server-2025.5rc32.dist-info → karrio_server-2025.5rc34.dist-info}/top_level.txt +0 -0
karrio/server/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2025.
|
|
1
|
+
2025.5rc34
|
karrio/server/settings/base.py
CHANGED
karrio/server/settings/cache.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# type: ignore
|
|
2
|
+
import sys
|
|
2
3
|
from decouple import config
|
|
3
4
|
from karrio.server.settings.base import *
|
|
4
5
|
from karrio.server.settings.apm import HEALTH_CHECK_APPS
|
|
@@ -7,6 +8,16 @@ from karrio.server.core.logging import logger
|
|
|
7
8
|
|
|
8
9
|
CACHE_TTL = 60 * 15
|
|
9
10
|
|
|
11
|
+
# Check if worker is running in detached mode (separate from API server)
|
|
12
|
+
DETACHED_WORKER = config("DETACHED_WORKER", default=False, cast=bool)
|
|
13
|
+
|
|
14
|
+
# Detect if running as a worker process (via run_huey command)
|
|
15
|
+
IS_WORKER_PROCESS = any("run_huey" in arg for arg in sys.argv)
|
|
16
|
+
|
|
17
|
+
# Skip default Redis cache configuration if in worker mode
|
|
18
|
+
# Workers only need HUEY Redis, not the default Django cache
|
|
19
|
+
SKIP_DEFAULT_CACHE = DETACHED_WORKER or IS_WORKER_PROCESS
|
|
20
|
+
|
|
10
21
|
# Redis configuration - REDIS_URL takes precedence and supersedes granular env vars
|
|
11
22
|
REDIS_URL = config("REDIS_URL", default=None)
|
|
12
23
|
REDIS_PREFIX = config("REDIS_PREFIX", default="karrio")
|
|
@@ -31,7 +42,7 @@ if REDIS_URL is not None:
|
|
|
31
42
|
|
|
32
43
|
# Build connection URL with database 1 for cache
|
|
33
44
|
REDIS_AUTH = f"{REDIS_USERNAME}:{REDIS_PASSWORD}@" if REDIS_PASSWORD else ""
|
|
34
|
-
REDIS_CONNECTION_URL = f
|
|
45
|
+
REDIS_CONNECTION_URL = f"{REDIS_SCHEME}://{REDIS_AUTH}{REDIS_HOST}:{REDIS_PORT}/1"
|
|
35
46
|
|
|
36
47
|
else:
|
|
37
48
|
# Fall back to individual parameters
|
|
@@ -43,10 +54,12 @@ else:
|
|
|
43
54
|
if REDIS_HOST is not None:
|
|
44
55
|
REDIS_AUTH = f"{REDIS_USERNAME}:{REDIS_PASSWORD}@" if REDIS_PASSWORD else ""
|
|
45
56
|
REDIS_SCHEME = "rediss" if REDIS_SSL else "redis"
|
|
46
|
-
REDIS_CONNECTION_URL =
|
|
57
|
+
REDIS_CONNECTION_URL = (
|
|
58
|
+
f'{REDIS_SCHEME}://{REDIS_AUTH}{REDIS_HOST}:{REDIS_PORT or "6379"}/1'
|
|
59
|
+
)
|
|
47
60
|
|
|
48
|
-
# Configure Django cache if Redis is available
|
|
49
|
-
if REDIS_HOST is not None:
|
|
61
|
+
# Configure Django cache if Redis is available and not in worker mode
|
|
62
|
+
if REDIS_HOST is not None and not SKIP_DEFAULT_CACHE:
|
|
50
63
|
HEALTH_CHECK_APPS += ["health_check.contrib.redis"]
|
|
51
64
|
INSTALLED_APPS += ["health_check.contrib.redis"]
|
|
52
65
|
|
|
@@ -56,9 +69,17 @@ if REDIS_HOST is not None:
|
|
|
56
69
|
"LOCATION": REDIS_CONNECTION_URL,
|
|
57
70
|
"OPTIONS": {
|
|
58
71
|
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
|
59
|
-
**(
|
|
72
|
+
**(
|
|
73
|
+
{"CONNECTION_POOL_KWARGS": {"ssl_cert_reqs": None}}
|
|
74
|
+
if REDIS_SSL
|
|
75
|
+
else {}
|
|
76
|
+
),
|
|
60
77
|
},
|
|
61
78
|
"KEY_PREFIX": REDIS_PREFIX,
|
|
62
79
|
}
|
|
63
80
|
}
|
|
64
|
-
|
|
81
|
+
print(f"Redis cache connection initialized")
|
|
82
|
+
elif SKIP_DEFAULT_CACHE:
|
|
83
|
+
print(
|
|
84
|
+
"Skipping default Redis cache configuration (worker mode - only HUEY Redis needed)"
|
|
85
|
+
)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# type: ignore
|
|
2
2
|
import os
|
|
3
|
+
import sys
|
|
4
|
+
import socket
|
|
3
5
|
import huey
|
|
4
6
|
import redis
|
|
5
7
|
import decouple
|
|
@@ -12,65 +14,125 @@ DEFAULT_TRACKERS_UPDATE_INTERVAL = decouple.config(
|
|
|
12
14
|
"TRACKING_PULSE", default=7200, cast=int
|
|
13
15
|
) # value is seconds. so 10800 seconds = 3 Hours
|
|
14
16
|
|
|
17
|
+
# Check if worker is running in detached mode (separate from API server)
|
|
18
|
+
DETACHED_WORKER = decouple.config("DETACHED_WORKER", default=False, cast=bool)
|
|
19
|
+
|
|
15
20
|
WORKER_IMMEDIATE_MODE = decouple.config(
|
|
16
21
|
"WORKER_IMMEDIATE_MODE", default=False, cast=bool
|
|
17
22
|
)
|
|
18
23
|
|
|
19
|
-
#
|
|
20
|
-
|
|
24
|
+
# Detect if running as a worker process (via run_huey command)
|
|
25
|
+
# Workers always need Huey configured regardless of DETACHED_WORKER setting
|
|
26
|
+
IS_WORKER_PROCESS = any("run_huey" in arg for arg in sys.argv)
|
|
21
27
|
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
# Skip Huey configuration only if:
|
|
29
|
+
# 1. Running in detached worker mode (DETACHED_WORKER=True)
|
|
30
|
+
# 2. AND not running as a worker process (IS_WORKER_PROCESS=False)
|
|
31
|
+
# This ensures workers always get Huey configured, but API servers don't when detached
|
|
32
|
+
if DETACHED_WORKER and not IS_WORKER_PROCESS:
|
|
33
|
+
# API server in detached mode - skip Huey configuration
|
|
34
|
+
HUEY = None
|
|
35
|
+
else:
|
|
36
|
+
# Either: worker process, or API server with embedded workers
|
|
37
|
+
# Redis configuration - REDIS_URL takes precedence and supersedes granular env vars
|
|
38
|
+
REDIS_URL = decouple.config("REDIS_URL", default=None)
|
|
25
39
|
|
|
26
|
-
|
|
40
|
+
# Parse REDIS_URL or construct from individual parameters
|
|
41
|
+
if REDIS_URL is not None:
|
|
42
|
+
from urllib.parse import urlparse
|
|
27
43
|
|
|
28
|
-
|
|
29
|
-
REDIS_HOST = parsed.hostname
|
|
30
|
-
REDIS_PORT = parsed.port or 6379
|
|
31
|
-
REDIS_USERNAME = parsed.username or "default"
|
|
32
|
-
REDIS_PASSWORD = parsed.password
|
|
44
|
+
parsed = urlparse(REDIS_URL)
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
# Extract values from REDIS_URL (these supersede granular env vars)
|
|
47
|
+
REDIS_HOST = parsed.hostname
|
|
48
|
+
REDIS_PORT = parsed.port or 6379
|
|
49
|
+
REDIS_USERNAME = parsed.username or "default"
|
|
50
|
+
REDIS_PASSWORD = parsed.password
|
|
37
51
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
REDIS_USERNAME = decouple.config("REDIS_USERNAME", default="default")
|
|
44
|
-
REDIS_SSL = decouple.config("REDIS_SSL", default=False, cast=bool)
|
|
45
|
-
|
|
46
|
-
# Configure HUEY based on available Redis configuration
|
|
47
|
-
if REDIS_HOST is not None:
|
|
48
|
-
pool = redis.ConnectionPool(
|
|
49
|
-
host=REDIS_HOST,
|
|
50
|
-
port=REDIS_PORT,
|
|
51
|
-
max_connections=20,
|
|
52
|
-
**({"ssl": REDIS_SSL} if REDIS_SSL else {}),
|
|
53
|
-
**({"password": REDIS_PASSWORD} if REDIS_PASSWORD else {}),
|
|
54
|
-
**({"username": REDIS_USERNAME} if REDIS_USERNAME else {}),
|
|
55
|
-
)
|
|
56
|
-
HUEY = huey.RedisHuey(
|
|
57
|
-
"default",
|
|
58
|
-
connection_pool=pool,
|
|
59
|
-
**({"immediate": WORKER_IMMEDIATE_MODE} if WORKER_IMMEDIATE_MODE else {}),
|
|
60
|
-
)
|
|
52
|
+
# Determine SSL from URL scheme (rediss:// means SSL is enabled)
|
|
53
|
+
REDIS_SCHEME = (
|
|
54
|
+
parsed.scheme if parsed.scheme in ("redis", "rediss") else "redis"
|
|
55
|
+
)
|
|
56
|
+
REDIS_SSL = REDIS_SCHEME == "rediss"
|
|
61
57
|
|
|
62
|
-
else:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
58
|
+
else:
|
|
59
|
+
# Fall back to individual parameters
|
|
60
|
+
REDIS_HOST = decouple.config("REDIS_HOST", default=None)
|
|
61
|
+
REDIS_PORT = decouple.config("REDIS_PORT", default=None)
|
|
62
|
+
REDIS_PASSWORD = decouple.config("REDIS_PASSWORD", default=None)
|
|
63
|
+
REDIS_USERNAME = decouple.config("REDIS_USERNAME", default="default")
|
|
64
|
+
REDIS_SSL = decouple.config("REDIS_SSL", default=False, cast=bool)
|
|
65
|
+
|
|
66
|
+
# Configure HUEY based on available Redis configuration
|
|
67
|
+
if REDIS_HOST is not None:
|
|
68
|
+
# Calculate max connections based on environment
|
|
69
|
+
# Each worker replica needs: (workers_per_replica + 1 scheduler) connections
|
|
70
|
+
# Formula: (worker_replicas * (threads_per_worker + 1)) + api_connections + buffer
|
|
71
|
+
# Example: 100 connections = (5 replicas * (8 workers + 1 scheduler)) + 40 API + 15 buffer
|
|
72
|
+
REDIS_MAX_CONNECTIONS = decouple.config(
|
|
73
|
+
"REDIS_MAX_CONNECTIONS", default=100, cast=int
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Connection pool configuration with timeouts
|
|
77
|
+
# Use BlockingConnectionPool to wait for available connections instead of failing immediately
|
|
78
|
+
pool_kwargs = {
|
|
79
|
+
"host": REDIS_HOST,
|
|
80
|
+
"port": REDIS_PORT,
|
|
81
|
+
"max_connections": REDIS_MAX_CONNECTIONS,
|
|
82
|
+
"timeout": 20, # Wait up to 20 seconds for an available connection
|
|
83
|
+
# Timeout settings to prevent hung connections
|
|
84
|
+
"socket_timeout": 10, # Command execution timeout (seconds)
|
|
85
|
+
"socket_connect_timeout": 10, # Connection establishment timeout (seconds)
|
|
86
|
+
# Keep connections alive to prevent closure by firewalls/load balancers
|
|
87
|
+
"socket_keepalive": True,
|
|
88
|
+
# Retry on transient failures
|
|
89
|
+
"retry_on_timeout": True,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Add TCP keepalive options if available (Linux/Unix only)
|
|
93
|
+
try:
|
|
94
|
+
pool_kwargs["socket_keepalive_options"] = {
|
|
95
|
+
socket.TCP_KEEPIDLE: 60, # Start keepalive after 60s idle
|
|
96
|
+
socket.TCP_KEEPINTVL: 10, # Keepalive interval
|
|
97
|
+
socket.TCP_KEEPCNT: 3, # Keepalive probes before timeout
|
|
98
|
+
}
|
|
99
|
+
except AttributeError:
|
|
100
|
+
# TCP keepalive constants not available on this platform
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
# Add authentication if provided
|
|
104
|
+
if REDIS_PASSWORD:
|
|
105
|
+
pool_kwargs["password"] = REDIS_PASSWORD
|
|
106
|
+
if REDIS_USERNAME:
|
|
107
|
+
pool_kwargs["username"] = REDIS_USERNAME
|
|
108
|
+
|
|
109
|
+
# Add SSL/TLS configuration if enabled
|
|
110
|
+
if REDIS_SSL:
|
|
111
|
+
# Use SSLConnection class for SSL/TLS connections
|
|
112
|
+
pool_kwargs["connection_class"] = redis.SSLConnection
|
|
113
|
+
pool_kwargs["ssl_cert_reqs"] = None # For Azure Redis compatibility
|
|
114
|
+
|
|
115
|
+
# Use BlockingConnectionPool to wait for connections instead of raising errors immediately
|
|
116
|
+
pool = redis.BlockingConnectionPool(**pool_kwargs)
|
|
117
|
+
|
|
118
|
+
HUEY = huey.RedisHuey(
|
|
119
|
+
"default",
|
|
120
|
+
connection_pool=pool,
|
|
121
|
+
**({"immediate": WORKER_IMMEDIATE_MODE} if WORKER_IMMEDIATE_MODE else {}),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
else:
|
|
125
|
+
# No Redis configured, use SQLite
|
|
126
|
+
WORKER_DB_DIR = decouple.config("WORKER_DB_DIR", default=settings.WORK_DIR)
|
|
127
|
+
WORKER_DB_FILE_NAME = os.path.join(WORKER_DB_DIR, "tasks.sqlite3")
|
|
128
|
+
|
|
129
|
+
settings.DATABASES["workers"] = {
|
|
130
|
+
"NAME": WORKER_DB_FILE_NAME,
|
|
131
|
+
"ENGINE": "django.db.backends.sqlite3",
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
HUEY = huey.SqliteHuey(
|
|
135
|
+
name="default",
|
|
136
|
+
filename=WORKER_DB_FILE_NAME,
|
|
137
|
+
**({"immediate": WORKER_IMMEDIATE_MODE} if WORKER_IMMEDIATE_MODE else {}),
|
|
138
|
+
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
karrio/server/VERSION,sha256=
|
|
1
|
+
karrio/server/VERSION,sha256=UvdnhEGXMLVD-es3uMQ4wsjc4zpHw83BPAZp3oKp7Rk,10
|
|
2
2
|
karrio/server/__init__.py,sha256=iOEMwnlORWezdO8-2vxBIPSR37D7JGjluZ8f55vzxls,81
|
|
3
3
|
karrio/server/__main__.py,sha256=hy2-Zb2wSVe_Pu6zWZ-BhiM4rBaGZ3D16HSuhudygqg,632
|
|
4
4
|
karrio/server/asgi.py,sha256=LsZYMWo8U9zURVPdHnvUsziOhMjdCdQoD2-gMJbS2U0,462
|
|
@@ -7,12 +7,12 @@ karrio/server/wsgi.py,sha256=SpWqkEYlMsj89_znZ8p8IjH3EgTVRWRq_9eS8t64dMw,403
|
|
|
7
7
|
karrio/server/lib/otel_huey.py,sha256=6MP6vX6b6x6RPF2K1m8B8L8S9GK1Q3vANmLidsxh65k,5428
|
|
8
8
|
karrio/server/settings/__init__.py,sha256=iw-NBcReOnDYpnvSEBdYDfV7jC0040jYdupnmSdElec,866
|
|
9
9
|
karrio/server/settings/apm.py,sha256=fp9_CVzNfQ8MvNp1-WQbCILzLU3RSJeeu1vYAtdrgfs,7415
|
|
10
|
-
karrio/server/settings/base.py,sha256=
|
|
11
|
-
karrio/server/settings/cache.py,sha256=
|
|
10
|
+
karrio/server/settings/base.py,sha256=UbZ4pEBEKG0mJSlNED1iU9KEpNmrZjjmwdujwnQ30fI,22285
|
|
11
|
+
karrio/server/settings/cache.py,sha256=nAHLgqynNtohxCJSMYP2RzcnBGC0ZC30KH6LlKdxgo4,3218
|
|
12
12
|
karrio/server/settings/constance.py,sha256=wKi7u-NORAPjIJMIbl3k5kPRkUA6jJJWe9kxsV3aoeA,7113
|
|
13
13
|
karrio/server/settings/debug.py,sha256=fFyK2XX47UGeK0eRNSV-9ZLaComay5QvJW0692vaH98,527
|
|
14
14
|
karrio/server/settings/email.py,sha256=bHBLKM_v3HTkmjrz_Msdj_Z7_kMzAb7i6pvJCZuzu1E,1403
|
|
15
|
-
karrio/server/settings/workers.py,sha256=
|
|
15
|
+
karrio/server/settings/workers.py,sha256=Ey59-TQ2odW-e15Mf2OZjQSLzX3jNCnWMC7dy-T2A3o,5755
|
|
16
16
|
karrio/server/static/extra/branding/android-chrome-192x192.png,sha256=qSwEKKBtk4udSHb0OWGC5-jfkP5cVpULDD1Cdkuk8PU,7005
|
|
17
17
|
karrio/server/static/extra/branding/android-chrome-512x512.png,sha256=YFwVPnPChO30tTuyrwSc_z548H7C6OFXh4CCu2krvHA,21062
|
|
18
18
|
karrio/server/static/extra/branding/favicon-16x16.png,sha256=wkELpij29bIvvKr5sDcjfNeYvj7i0yk-__bJbZckEK8,1085
|
|
@@ -73,8 +73,8 @@ karrio/server/templates/admin/base_site.html,sha256=kbcdvehXZ1EHaw07JL7fSZmjrnVM
|
|
|
73
73
|
karrio/server/templates/openapi/openapi.html,sha256=3ApCZ5pE6Wjv7CJllVbqD2WiDQuLy-BFS-IIHurXhBY,1133
|
|
74
74
|
karrio/server/urls/__init__.py,sha256=Ah-XqaqRsfecQgCGRHjxmXe8O7a0avq5ocU90tkVwQI,1998
|
|
75
75
|
karrio/server/urls/jwt.py,sha256=QN2L-EpUEQCF2UGYPu_VVlA49Fc0BtcY7Ov3-xpp7_U,6772
|
|
76
|
-
karrio_server-2025.
|
|
77
|
-
karrio_server-2025.
|
|
78
|
-
karrio_server-2025.
|
|
79
|
-
karrio_server-2025.
|
|
80
|
-
karrio_server-2025.
|
|
76
|
+
karrio_server-2025.5rc34.dist-info/METADATA,sha256=wwmvWTOS9_hybwmqLEB-DevkkDSdRNWsH2y1R_4Agso,4305
|
|
77
|
+
karrio_server-2025.5rc34.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
78
|
+
karrio_server-2025.5rc34.dist-info/entry_points.txt,sha256=c2eftt6MpJjyp0OFv1OmO9nUYSDemt9fGq_RDdvpGLw,55
|
|
79
|
+
karrio_server-2025.5rc34.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
|
|
80
|
+
karrio_server-2025.5rc34.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|