Skip to content

Commit 96bb2e1

Browse files
committed
add app name in order to have unique key in case there are multiple app on the same redis db
1 parent 3cb48e6 commit 96bb2e1

7 files changed

Lines changed: 80 additions & 43 deletions

File tree

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ repos:
2626
- pre-commit
2727

2828
- repo: https://github.com/pre-commit/mirrors-mypy
29-
rev: v1.16.1
29+
rev: v1.17.0
3030
hooks:
3131
- id: mypy
3232
additional_dependencies: [ "types-redis", "fastapi", "pydantic"]
@@ -35,7 +35,7 @@ repos:
3535

3636
- repo: https://github.com/astral-sh/uv-pre-commit
3737
# uv version.
38-
rev: 0.7.20
38+
rev: 0.7.21
3939
hooks:
4040
- id: uv-lock
4141

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

pyproject.toml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "fastapi-task-manager"
3-
version = "0.6.1"
3+
version = "0.7.0"
44
description = "A task manager for Fastapi. Robust Scheduling, Distributed Safety"
55
readme = "README.md"
66
authors = [
@@ -37,12 +37,9 @@ dependencies = [
3737
"redis>=6.2.0",
3838
]
3939

40-
[project.scripts]
41-
fastapi-task-manager = "fastapi_task_manager:main"
42-
4340
[build-system]
44-
requires = ["hatchling"]
45-
build-backend = "hatchling.build"
41+
requires = ["uv_build>=0.7.20,<0.8.0"]
42+
build-backend = "uv_build"
4643

4744
[[tool.uv.index]]
4845
name = "testpypi"

src/fastapi_task_manager/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class Config(BaseModel):
77
# --------- End of logging config variables ---------
88

99
# --------- App config variables ---------
10+
app_name: str = __name__
1011
concurrent_tasks: int = 2
1112
statistics_redis_expiration: int = 432_000 # 5 days
1213
# --------- End of app config variables ---------

src/fastapi_task_manager/runner.py

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,17 @@ async def _run(self):
7676
self._running_tasks.pop(task, None)
7777

7878
next_run = datetime(year=2000, month=1, day=1, tzinfo=timezone.utc)
79-
if await self._redis_client.exists(task_group.name + "_" + task.name + "_next_run"):
80-
next_run_b = await self._redis_client.get(task_group.name + "_" + task.name + "_next_run")
79+
if await self._redis_client.exists(
80+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_next_run",
81+
):
82+
next_run_b = await self._redis_client.get(
83+
self._task_manager.config.app_name
84+
+ "_"
85+
+ task_group.name
86+
+ "_"
87+
+ task.name
88+
+ "_next_run",
89+
)
8190
if next_run_b is not None:
8291
next_run = datetime.fromtimestamp(float(next_run_b.decode("utf-8")), tz=timezone.utc)
8392
if next_run <= datetime.now(timezone.utc):
@@ -102,38 +111,52 @@ async def _queue_task(self, task: Task, task_group: TaskGroup):
102111

103112
async def _run_task(self, task_group: TaskGroup, task: Task) -> None: # noqa: PLR0912, C901 # TODO Reduce complexity
104113
try:
105-
if await self._redis_client.exists(task_group.name + "_" + task.name + "_next_run"):
106-
redis_next_run_b = await self._redis_client.get(task_group.name + "_" + task.name + "_next_run")
114+
if await self._redis_client.exists(
115+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_next_run",
116+
):
117+
redis_next_run_b = await self._redis_client.get(
118+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_next_run",
119+
)
107120
if redis_next_run_b is None:
108121
return
109122
redis_next_run = datetime.fromtimestamp(float(redis_next_run_b.decode("utf-8")), tz=timezone.utc)
110123
if redis_next_run > datetime.now(timezone.utc):
111124
return
112125

113-
redis_uuid_exists = await self._redis_client.exists(task_group.name + "_" + task.name + "_runner_uuid")
126+
redis_uuid_exists = await self._redis_client.exists(
127+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_runner_uuid",
128+
)
114129
if not redis_uuid_exists:
115-
await self._redis_client.set(task_group.name + "_" + task.name + "_runner_uuid", self._uuid, ex=15)
130+
await self._redis_client.set(
131+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_runner_uuid",
132+
self._uuid,
133+
ex=15,
134+
)
116135
# Wait a bit to ensure the UUID is set and not overwritten
117136
await asyncio.sleep(0.2)
118-
redis_uuid_b = await self._redis_client.get(task_group.name + "_" + task.name + "_runner_uuid")
137+
redis_uuid_b = await self._redis_client.get(
138+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_runner_uuid",
139+
)
119140
if redis_uuid_b is None:
120141
return
121142
if redis_uuid_b.decode("utf-8") != self._uuid:
122143
return
123144

124145
next_run = next_fire(task.expression)
125146
await self._redis_client.set(
126-
task_group.name + "_" + task.name + "_next_run",
147+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_next_run",
127148
next_run.timestamp(),
128149
# using max to ensure that the expiration isn't lower than 15 seconds
129150
# in this way we avoid potential issues
130151
ex=max(int((next_run - datetime.now(timezone.utc)).total_seconds()) * 2, 15),
131152
)
132153

133-
if await self._redis_client.exists(task_group.name + "_" + task.name + "_disabled"):
154+
if await self._redis_client.exists(
155+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_disabled",
156+
):
134157
# Set the key in order to update the ttl
135158
await self._redis_client.set(
136-
task_group.name + "_" + task.name + "_disabled",
159+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_disabled",
137160
"1",
138161
ex=self._task_manager.config.statistics_redis_expiration,
139162
)
@@ -142,38 +165,50 @@ async def _run_task(self, task_group: TaskGroup, task: Task) -> None: # noqa: P
142165
start = time.monotonic_ns()
143166
thread = asyncio.create_task(run_function(task.function))
144167
while not thread.done():
145-
await self._redis_client.set(task_group.name + "_" + task.name + "_runner_uuid", self._uuid, ex=5)
168+
await self._redis_client.set(
169+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_runner_uuid",
170+
self._uuid,
171+
ex=5,
172+
)
146173
await asyncio.sleep(0.1)
147174
end = time.monotonic_ns()
148175
runs = [] # TODO Evaluate redis linked lists
149-
if await self._redis_client.exists(task_group.name + "_" + task.name + "_runs"):
150-
runs_b = await self._redis_client.get(task_group.name + "_" + task.name + "_runs")
176+
if await self._redis_client.exists(
177+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_runs",
178+
):
179+
runs_b = await self._redis_client.get(
180+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_runs",
181+
)
151182
if runs_b is not None:
152183
runs = runs_b.decode("utf-8").split("\n")
153184
if len(runs) == 30: # noqa: PLR2004 # TODO Configurable
154185
runs.pop(0)
155186
runs.append(str(datetime.now(timezone.utc).timestamp()))
156187
await self._redis_client.set(
157-
task_group.name + "_" + task.name + "_runs",
188+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_runs",
158189
"\n".join(runs),
159190
ex=self._task_manager.config.statistics_redis_expiration,
160191
)
161192
durations_second = [] # TODO Evaluate redis linked lists
162-
if await self._redis_client.exists(task_group.name + "_" + task.name + "_durations_second"):
193+
if await self._redis_client.exists(
194+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_durations_second",
195+
):
163196
durations_second_b = await self._redis_client.get(
164-
task_group.name + "_" + task.name + "_durations_second",
197+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_durations_second",
165198
)
166199
if durations_second_b is not None:
167200
durations_second = durations_second_b.decode("utf-8").split("\n")
168201
if len(durations_second) == 30: # noqa: PLR2004 # TODO Configurable
169202
durations_second.pop(0)
170203
durations_second.append(str((end - start) / 1e9))
171204
await self._redis_client.set(
172-
task_group.name + "_" + task.name + "_durations_second",
205+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_durations_second",
173206
"\n".join(durations_second),
174207
ex=self._task_manager.config.statistics_redis_expiration,
175208
)
176-
await self._redis_client.delete(task_group.name + "_" + task.name + "_runner_uuid")
209+
await self._redis_client.delete(
210+
self._task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_runner_uuid",
211+
)
177212

178213
except asyncio.CancelledError:
179214
msg = f"Task {task.name} was cancelled."

src/fastapi_task_manager/task_router_services.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,23 @@ def get_tasks(
4747
continue
4848

4949
runs = [] # TODO Evaluate redis linked lists
50-
if redis_client.exists(tg.name + "_" + t.name + "_runs"):
51-
runs_b = redis_client.get(tg.name + "_" + t.name + "_runs")
50+
if redis_client.exists(task_manager.config.app_name + "_" + tg.name + "_" + t.name + "_runs"):
51+
runs_b = redis_client.get(task_manager.config.app_name + "_" + tg.name + "_" + t.name + "_runs")
5252
if runs_b is not None:
5353
runs = runs_b.decode("utf-8").split("\n")
5454

5555
durations_second = [] # TODO Evaluate redis linked lists
56-
if redis_client.exists(tg.name + "_" + t.name + "_durations_second"):
56+
if redis_client.exists(task_manager.config.app_name + "_" + tg.name + "_" + t.name + "_durations_second"):
5757
durations_second_b = redis_client.get(
58-
tg.name + "_" + t.name + "_durations_second",
58+
task_manager.config.app_name + "_" + tg.name + "_" + t.name + "_durations_second",
5959
)
6060
if durations_second_b is not None:
6161
durations_second = durations_second_b.decode("utf-8").split("\n")
6262
assert len(runs) == len(durations_second), "Runs and durations_second must have the same length"
6363

6464
next_run = datetime(year=2000, month=1, day=1, tzinfo=timezone.utc)
65-
if redis_client.exists(tg.name + "_" + t.name + "_next_run"):
66-
next_run_b = redis_client.get(tg.name + "_" + t.name + "_next_run")
65+
if redis_client.exists(task_manager.config.app_name + "_" + tg.name + "_" + t.name + "_next_run"):
66+
next_run_b = redis_client.get(task_manager.config.app_name + "_" + tg.name + "_" + t.name + "_next_run")
6767
if next_run_b is not None:
6868
next_run = datetime.fromtimestamp(float(next_run_b.decode("utf-8")), tz=timezone.utc)
6969
list_to_return.append(
@@ -74,7 +74,9 @@ def get_tasks(
7474
expression=t.expression,
7575
high_priority=t.high_priority,
7676
next_run=next_run,
77-
is_active=not redis_client.exists(tg.name + "_" + t.name + "_disabled"),
77+
is_active=not redis_client.exists(
78+
task_manager.config.app_name + "_" + tg.name + "_" + t.name + "_disabled",
79+
),
7880
runs=[
7981
TaskRun(
8082
run_date=datetime.fromtimestamp(float(runs[i]), timezone.utc),
@@ -115,7 +117,7 @@ def disable_task(
115117
db=task_manager.config.redis_db,
116118
)
117119
redis_client.set(
118-
task_group.name + "_" + task.name + "_disabled",
120+
task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_disabled",
119121
"1",
120122
ex=task_manager.config.statistics_redis_expiration,
121123
)
@@ -148,5 +150,5 @@ def enable_task(
148150
password=task_manager.config.redis_password,
149151
db=task_manager.config.redis_db,
150152
)
151-
if redis_client.exists(task_group.name + "_" + task.name + "_disabled"):
152-
redis_client.delete(task_group.name + "_" + task.name + "_disabled")
153+
if redis_client.exists(task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_disabled"):
154+
redis_client.delete(task_manager.config.app_name + "_" + task_group.name + "_" + task.name + "_disabled")

uv.lock

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)