fast-priority 0.0.14__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.
- fast_priority/__init__.py +0 -0
- fast_priority/app.py +219 -0
- fast_priority/cli.py +93 -0
- fast_priority/utils.py +17 -0
- fast_priority-0.0.14.dist-info/METADATA +139 -0
- fast_priority-0.0.14.dist-info/RECORD +9 -0
- fast_priority-0.0.14.dist-info/WHEEL +4 -0
- fast_priority-0.0.14.dist-info/entry_points.txt +3 -0
- fast_priority-0.0.14.dist-info/licenses/LICENSE +21 -0
|
File without changes
|
fast_priority/app.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
from fastapi import FastAPI, HTTPException, Request, Response
|
|
11
|
+
from redis import Redis
|
|
12
|
+
from redis.exceptions import ConnectionError as RedisConnectionError
|
|
13
|
+
from rq import Queue
|
|
14
|
+
from rq.job import JobStatus
|
|
15
|
+
|
|
16
|
+
from fast_priority.utils import generate_enpoint_list, run_request
|
|
17
|
+
|
|
18
|
+
load_dotenv()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class HighPrioPaths(StrEnum):
|
|
22
|
+
LISTED = "listed"
|
|
23
|
+
UNLISTED = "unlisted"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger("uvicorn.error")
|
|
27
|
+
# logger.setLevel(logging.DEBUG)
|
|
28
|
+
|
|
29
|
+
redis_conn = Redis(
|
|
30
|
+
host=os.environ.get("FAST_PRIORITY_REDIS_HOST", "localhost"),
|
|
31
|
+
port=int(os.environ.get("FAST_PRIORITY_REDIS_PORT", 6379)),
|
|
32
|
+
username=os.environ.get("FAST_PRIORITY_REDIS_USER", None),
|
|
33
|
+
password=os.environ.get("FAST_PRIORITY_REDIS_PASSWORD", None),
|
|
34
|
+
)
|
|
35
|
+
low_queue = Queue("low", connection=redis_conn)
|
|
36
|
+
high_queue = Queue("high", connection=redis_conn)
|
|
37
|
+
|
|
38
|
+
target_base_url = os.environ["FAST_PRIORITY_TARGET_BASE_URL"]
|
|
39
|
+
priority_mode = HighPrioPaths(
|
|
40
|
+
os.environ.get("FAST_PRIORITY_HIGH_PRIO_PATHS", "unlisted")
|
|
41
|
+
)
|
|
42
|
+
job_poll_interval = float(os.environ.get("FAST_PRIORITY_POLL_INTERVAL", 1.0))
|
|
43
|
+
job_ttl = int(os.environ.get("FAST_PRIORITY_TTL", 60 * 5))
|
|
44
|
+
|
|
45
|
+
doc_path = os.environ.get("FAST_PRIORITY_DOC_PATH", "/gateway_docs")
|
|
46
|
+
redoc_path = os.environ.get("FAST_PRIORITY_DOC_PATH", "/gateway_redoc")
|
|
47
|
+
health_path = os.environ.get("FAST_PRIORITY_HEALTH_PATH", "/gateway_health")
|
|
48
|
+
|
|
49
|
+
prio_paths = None
|
|
50
|
+
prio_base_paths = None
|
|
51
|
+
pass_through_paths = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@asynccontextmanager
|
|
55
|
+
async def lifespan(app: FastAPI):
|
|
56
|
+
global prio_paths
|
|
57
|
+
global prio_base_paths
|
|
58
|
+
global pass_through_paths
|
|
59
|
+
|
|
60
|
+
pass_through_paths = ["health"]
|
|
61
|
+
pass_through_env = os.environ.get("FAST_PRIORITY_PASS_THROUGH", None)
|
|
62
|
+
if pass_through_env:
|
|
63
|
+
pass_through_paths = generate_enpoint_list(pass_through_env)
|
|
64
|
+
|
|
65
|
+
prio_paths = generate_enpoint_list(os.environ.get("FAST_PRIORITY_PRIO_PATHS", None))
|
|
66
|
+
prio_base_paths = generate_enpoint_list(
|
|
67
|
+
os.environ.get("FAST_PRIORITY_PRIO_BASE_PATHS", None)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if not prio_base_paths and not prio_paths:
|
|
71
|
+
logger.warning("No low priority endpoints defined.")
|
|
72
|
+
else:
|
|
73
|
+
if priority_mode == HighPrioPaths.LISTED:
|
|
74
|
+
logger.info("The following path will be passed to the **HIGH** queue")
|
|
75
|
+
else:
|
|
76
|
+
logger.info("The following path will be passed to the **LOW** queue")
|
|
77
|
+
if prio_paths:
|
|
78
|
+
logger.info("Low priority path")
|
|
79
|
+
for path in prio_paths:
|
|
80
|
+
logger.info(f" {path}")
|
|
81
|
+
if prio_base_paths:
|
|
82
|
+
logger.info("Low priority base path")
|
|
83
|
+
for path in prio_base_paths:
|
|
84
|
+
logger.info(f" {path}")
|
|
85
|
+
|
|
86
|
+
if pass_through_paths:
|
|
87
|
+
logger.info("Pass through paths")
|
|
88
|
+
for path in pass_through_paths:
|
|
89
|
+
logger.info(f" {path}")
|
|
90
|
+
yield
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
app = FastAPI(
|
|
94
|
+
lifespan=lifespan,
|
|
95
|
+
docs_url=doc_path,
|
|
96
|
+
redoc_url=redoc_path,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@app.get(health_path)
|
|
101
|
+
async def heath_check() -> Any:
|
|
102
|
+
try:
|
|
103
|
+
async with httpx.AsyncClient() as client:
|
|
104
|
+
await client.get(f"{target_base_url}")
|
|
105
|
+
except httpx.ConnectError:
|
|
106
|
+
target_reachable = False
|
|
107
|
+
else:
|
|
108
|
+
target_reachable = True
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
redis_conn.info()
|
|
112
|
+
except RedisConnectionError:
|
|
113
|
+
redis_reachable = False
|
|
114
|
+
else:
|
|
115
|
+
redis_reachable = True
|
|
116
|
+
|
|
117
|
+
n_jobs_queued_low = None
|
|
118
|
+
n_jobs_queued_high = None
|
|
119
|
+
if redis_reachable:
|
|
120
|
+
n_jobs_queued_high = len(high_queue.jobs)
|
|
121
|
+
n_jobs_queued_low = len(low_queue.jobs)
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
"target_reachable": target_reachable,
|
|
125
|
+
"redis_reachable": redis_reachable,
|
|
126
|
+
"queue": {"high": n_jobs_queued_high, "low": n_jobs_queued_low},
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@app.get("/docs", include_in_schema=False)
|
|
131
|
+
@app.get("/redoc", include_in_schema=False)
|
|
132
|
+
async def get_target_docs(request: Request) -> Any:
|
|
133
|
+
current_path = request.url.path
|
|
134
|
+
print(current_path)
|
|
135
|
+
async with httpx.AsyncClient(timeout=None, follow_redirects=True) as client:
|
|
136
|
+
response = await client.get( # type: ignore
|
|
137
|
+
url=f"{target_base_url}{current_path}",
|
|
138
|
+
)
|
|
139
|
+
new_content = response.content.decode("utf-8").replace(
|
|
140
|
+
"/openapi.json", "/target_openapi.json"
|
|
141
|
+
)
|
|
142
|
+
new_bytes = new_content.encode("utf-8")
|
|
143
|
+
new_headers = dict(response.headers)
|
|
144
|
+
new_headers["content-length"] = str(len(new_bytes))
|
|
145
|
+
return Response(
|
|
146
|
+
content=new_bytes,
|
|
147
|
+
status_code=response.status_code,
|
|
148
|
+
headers=new_headers,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@app.get("/target_openapi.json")
|
|
153
|
+
async def get_target_openapi_spec() -> Any:
|
|
154
|
+
async with httpx.AsyncClient() as client:
|
|
155
|
+
response = await client.get(f"{target_base_url}/openapi.json")
|
|
156
|
+
return Response(
|
|
157
|
+
content=response.content,
|
|
158
|
+
status_code=response.status_code,
|
|
159
|
+
headers=dict(response.headers),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
async def forward_request(request: Request, path: str) -> Response:
|
|
164
|
+
assert prio_paths is not None
|
|
165
|
+
assert prio_base_paths is not None
|
|
166
|
+
# Prepare request components
|
|
167
|
+
url = f"/{path}?{request.url.query}"
|
|
168
|
+
headers = dict(request.headers)
|
|
169
|
+
headers.pop("host", None) # Remove original host header
|
|
170
|
+
|
|
171
|
+
request_body = await request.body()
|
|
172
|
+
|
|
173
|
+
if pass_through_paths and path in pass_through_paths:
|
|
174
|
+
logger.debug("%s in pass through. Skipping queue", path)
|
|
175
|
+
async with httpx.AsyncClient(timeout=None, follow_redirects=True) as client:
|
|
176
|
+
return await client.request( # type: ignore
|
|
177
|
+
method=request.method,
|
|
178
|
+
url=f"{target_base_url}{url}",
|
|
179
|
+
headers=headers,
|
|
180
|
+
content=request_body,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
use_queue = high_queue if priority_mode == HighPrioPaths.UNLISTED else low_queue
|
|
184
|
+
if path in prio_paths or any(path.startswith(b) for b in prio_base_paths):
|
|
185
|
+
use_queue = low_queue if priority_mode == HighPrioPaths.UNLISTED else high_queue
|
|
186
|
+
|
|
187
|
+
job = use_queue.enqueue(
|
|
188
|
+
run_request,
|
|
189
|
+
ttl=job_ttl,
|
|
190
|
+
failure_ttl=60 * 60,
|
|
191
|
+
kwargs=dict(
|
|
192
|
+
method=request.method,
|
|
193
|
+
url=f"{target_base_url}{url}",
|
|
194
|
+
headers=headers,
|
|
195
|
+
content=request_body,
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
while job.result is None:
|
|
200
|
+
status = job.get_status(refresh=True)
|
|
201
|
+
if status in [JobStatus.FAILED, JobStatus.STOPPED, JobStatus.CANCELED]:
|
|
202
|
+
raise HTTPException(status_code=500, detail="Could not run request")
|
|
203
|
+
logger.debug(
|
|
204
|
+
"Waiting. Current status: %s of %s", job.get_status(refresh=True), job.id
|
|
205
|
+
)
|
|
206
|
+
await asyncio.sleep(job_poll_interval)
|
|
207
|
+
|
|
208
|
+
return job.result
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@app.api_route("/{path:path}", methods=["GET", "POST"], include_in_schema=False)
|
|
212
|
+
async def proxy_request(request: Request, path: str) -> Any:
|
|
213
|
+
response = await forward_request(request, path)
|
|
214
|
+
print(response)
|
|
215
|
+
return Response(
|
|
216
|
+
content=response.content,
|
|
217
|
+
status_code=response.status_code,
|
|
218
|
+
headers=response.headers,
|
|
219
|
+
)
|
fast_priority/cli.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from urllib.parse import quote
|
|
5
|
+
|
|
6
|
+
import uvicorn
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def urlencode(s: str) -> str:
|
|
10
|
+
safe = "-_.~"
|
|
11
|
+
return quote(s, safe=safe)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
role = "worker" if os.getenv("FAST_PRIORITY_WORKERS") else "api"
|
|
16
|
+
|
|
17
|
+
print("======================================")
|
|
18
|
+
print("==> Fast Priority Queue RUN SCRIPT <==")
|
|
19
|
+
print("======================================")
|
|
20
|
+
print(f"--> MODE: {role}")
|
|
21
|
+
print("======================================")
|
|
22
|
+
|
|
23
|
+
if role == "api":
|
|
24
|
+
from fast_priority.app import app
|
|
25
|
+
|
|
26
|
+
uvicorn.run(
|
|
27
|
+
app,
|
|
28
|
+
host=os.getenv("FAST_PRIORITY_HOST", "0.0.0.0"),
|
|
29
|
+
port=int(os.getenv("FAST_PRIORITY_PORT", 8000)),
|
|
30
|
+
# log_level="debug",
|
|
31
|
+
reload=False, # Disabled for production
|
|
32
|
+
)
|
|
33
|
+
elif role == "worker":
|
|
34
|
+
from rq.cli.cli import main as rq_cli
|
|
35
|
+
|
|
36
|
+
print("Running queue worker")
|
|
37
|
+
workers = os.getenv("FAST_PRIORITY_WORKERS", "1")
|
|
38
|
+
print(f"Using {workers} workers")
|
|
39
|
+
|
|
40
|
+
redis_host = os.getenv("FAST_PRIORITY_REDIS_HOST", "localhost")
|
|
41
|
+
redis_user = os.getenv("FAST_PRIORITY_REDIS_USER", "")
|
|
42
|
+
redis_password = os.getenv("FAST_PRIORITY_REDIS_PASSWORD", "")
|
|
43
|
+
redis_port = os.getenv("FAST_PRIORITY_REDIS_PORT", "")
|
|
44
|
+
|
|
45
|
+
auth = ""
|
|
46
|
+
if redis_user and redis_password:
|
|
47
|
+
auth = f"{urlencode(redis_user)}:{urlencode(redis_password)}@"
|
|
48
|
+
elif redis_password:
|
|
49
|
+
auth = f":{urlencode(redis_password)}@"
|
|
50
|
+
|
|
51
|
+
uri = f"redis://{auth}{redis_host}"
|
|
52
|
+
if redis_port:
|
|
53
|
+
uri += f":{redis_port}"
|
|
54
|
+
|
|
55
|
+
print(f" Using redis uri: {uri}")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
worker_count = int(workers)
|
|
59
|
+
if worker_count < 1:
|
|
60
|
+
raise ValueError
|
|
61
|
+
except ValueError:
|
|
62
|
+
print("ERR:: Invalid value for worker")
|
|
63
|
+
sys.exit(1)
|
|
64
|
+
|
|
65
|
+
if worker_count == 1:
|
|
66
|
+
print("Running with one worker")
|
|
67
|
+
rq_cli(["worker", "high", "low", "--url", uri], standalone_mode=True)
|
|
68
|
+
|
|
69
|
+
else:
|
|
70
|
+
print("Running multiple workers")
|
|
71
|
+
rq_cli(
|
|
72
|
+
[
|
|
73
|
+
"worker-pool",
|
|
74
|
+
"high",
|
|
75
|
+
"low",
|
|
76
|
+
"-n",
|
|
77
|
+
str(worker_count),
|
|
78
|
+
"--url",
|
|
79
|
+
uri,
|
|
80
|
+
],
|
|
81
|
+
standalone_mode=True,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
else:
|
|
85
|
+
print("ERR:: Invalid run mode")
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
import dotenv
|
|
91
|
+
|
|
92
|
+
dotenv.load_dotenv()
|
|
93
|
+
main()
|
fast_priority/utils.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def generate_enpoint_list(raw_endpoint_str: str | None) -> list[str]:
|
|
5
|
+
ret = []
|
|
6
|
+
if raw_endpoint_str:
|
|
7
|
+
ret = [
|
|
8
|
+
p.strip() for p in raw_endpoint_str.strip().split(",") if p not in {"", " "}
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
return ret
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def run_request(method: str, url: str, headers: dict, content: dict):
|
|
15
|
+
# print(f"Got: {method=}, {url=}")
|
|
16
|
+
with httpx.Client(timeout=None, follow_redirects=True) as client:
|
|
17
|
+
return client.request(method=method, url=url, headers=headers, content=content)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fast-priority
|
|
3
|
+
Version: 0.0.14
|
|
4
|
+
Summary: Simple and streigt forward priority queing gateway using fastapi
|
|
5
|
+
Keywords: queue,fastapi,priority,redis
|
|
6
|
+
Author-email: Korbinian Schweiger <korbinian.schweiger@gmail.com>
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Framework :: FastAPI
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: Topic :: Internet
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: fastapi[standard]>=0.115.8
|
|
18
|
+
Requires-Dist: httpx
|
|
19
|
+
Requires-Dist: python-dotenv>=1.0.1
|
|
20
|
+
Requires-Dist: rq>=2.1.0
|
|
21
|
+
Requires-Dist: click
|
|
22
|
+
Requires-Dist: uvicorn>=0.34.0
|
|
23
|
+
|
|
24
|
+
# 🚀 Fast Priority Queue 🔥
|
|
25
|
+
|
|
26
|
+
A minimal priority queuing gateway built with FastAPI using Redis.
|
|
27
|
+
|
|
28
|
+
It is designed to sit between your clients and a backend REST API, managing two priority levels—high and low—using the [`rq` package](https://python-rq.org/). Requests are enqueued based on the request path and processed synchronously by dedicated worker processes, so the overall throughput is limited by the number of workers.
|
|
29
|
+
|
|
30
|
+
## Overview
|
|
31
|
+
|
|
32
|
+
- Intercepts incoming client requests and forwards them to a target REST API.
|
|
33
|
+
- Enqueues requests into either a high-priority or low-priority queue based on configurable path matching.
|
|
34
|
+
- Processes queued requests via worker processes running in a separate environment.
|
|
35
|
+
- Offers Dockerized deployment for both the gateway and worker processes.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Configuration ⚙️
|
|
39
|
+
|
|
40
|
+
Both the gateway and workers are fully configurable via the following environment variables.
|
|
41
|
+
|
|
42
|
+
### Gateway
|
|
43
|
+
|
|
44
|
+
| ENV | Description | Required | Default |
|
|
45
|
+
|-------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|----------|-----------|
|
|
46
|
+
| FAST_PRIORITY_TARGET_BASE_URL | Base url of the target REST api which should run behind the gateway | x | |
|
|
47
|
+
| FAST_PRIORITY_HIGH_PRIO_PATHS | Switch between **listed** and **unlisted** modes. If **listed** (**unlisted**) the paths defined in the _PATH_ env variables are put on the **high** (**low**) queue | | unlisted |
|
|
48
|
+
| FAST_PRIORITY_PRIO_PATHS | Comma separated list of paths on the target API that should have low priority. Low priority for exact matches | | None |
|
|
49
|
+
| FAST_PRIORITY_PRIO_BASE_PATHS | Comma separated list of paths on the target API that should have low priority. Low priority if a request paths starts with the value. | | None |
|
|
50
|
+
| FAST_PRIORITY_PASS_THROUGH | Comma separated list of paths on the target API that should skip the queue. Request will be directly be passed on. | | health/ |
|
|
51
|
+
| FAST_PRIORITY_POLL_INTERVAL | How often should each request check if the job is finished | | 1.0 |
|
|
52
|
+
| FAST_PRIORITY_TTL | Time-to-live (in seconds) for jobs on the queues. | | 300 |
|
|
53
|
+
| FAST_PRIORITY_REDIS_HOST | Redis host | | localhost |
|
|
54
|
+
| FAST_PRIORITY_REDIS_PORT | Redis port | | 6379 |
|
|
55
|
+
| FAST_PRIORITY_REDIS_USER | Redis username | | None |
|
|
56
|
+
| FAST_PRIORITY_REDIS_PASSWORD | Redis password | | None |
|
|
57
|
+
|
|
58
|
+
### Queue worker (docker)
|
|
59
|
+
|
|
60
|
+
| ENV | Description | Required | Default |
|
|
61
|
+
|-------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|----------|-----------|
|
|
62
|
+
| FAST_PRIORITY_WORKERS | Set (e.g., non-empty or a number) to run as a worker instead of the gateway. Controls the number of worker processes to launch. | x | |
|
|
63
|
+
| FAST_PRIORITY_REDIS_HOST | Redis host | | localhost |
|
|
64
|
+
| FAST_PRIORITY_REDIS_PORT | Redis port | | 6379 |
|
|
65
|
+
| FAST_PRIORITY_REDIS_USER | Redis username | | None |
|
|
66
|
+
| FAST_PRIORITY_REDIS_PASSWORD | Redis password | | None |
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
Fast Priority Queue is designed to run via Docker (or Docker Compose) or using the script installed with the package `run-fast-priority`. The configuration works the same for both approaches using the environment variables described in [Configuration section](#configuration). The script is mainly provide to give the user the flexibility to include the gateway in their own docker containers by installing the package via pypi.
|
|
71
|
+
|
|
72
|
+
For development (or more specialized application) the gateway can also be run using the underlying command like this:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
fastapi run fast_priority/app.py --host 0.0.0.0 --port 8001
|
|
76
|
+
rq worker high low
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Docker 🐳
|
|
80
|
+
|
|
81
|
+
You can build the Docker container using the provided Dockerfile. The container adapts its run mode based on the presence of the environment variable `FAST_PRIORITY_WORKER`:
|
|
82
|
+
|
|
83
|
+
- If `FAST_PRIORITY_WORKER` is set, the container starts the worker(s).
|
|
84
|
+
- If not, the gateway is started.
|
|
85
|
+
|
|
86
|
+
The same strategy can be used with you own container and installing the package in the Dockerfile from pypi.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
#### Examples
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# API
|
|
93
|
+
docker run -p 8010:8000 -e FAST_PRIORITY_TARGET_BASE_URL=http://localhost:8011 -e FAST_PRIORITY_REDIS_HOST=localhost fast_priority:latest
|
|
94
|
+
|
|
95
|
+
# Workers
|
|
96
|
+
docker run -p 8010:8000 -e FAST_PRIORITY_WORKER=1 -e FAST_PRIORITY_REDIS_HOST=localhost fast_priority:latest
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
#### Compose 🐳🐳🐳
|
|
101
|
+
|
|
102
|
+
The simplest way to run Fast Priority Queue and its dependencies is via Docker Compose. Below is an example configuration:
|
|
103
|
+
|
|
104
|
+
```yml
|
|
105
|
+
services:
|
|
106
|
+
behind_gateway_api:
|
|
107
|
+
...
|
|
108
|
+
priorityity-gateway:
|
|
109
|
+
image: fast_priority:latest
|
|
110
|
+
environment:
|
|
111
|
+
- FAST_PRIORITY_TARGET_BASE_URL=http://behind_gateway_api:8000
|
|
112
|
+
- FAST_PRIORITY_REDIS_HOST=queue
|
|
113
|
+
- FAST_PRIORITY_PRIO_PATHS=endpoint_1,endpoint_2
|
|
114
|
+
ports:
|
|
115
|
+
- 8066:8000
|
|
116
|
+
|
|
117
|
+
priority-gateway-worker:
|
|
118
|
+
image: fast_priority:latest
|
|
119
|
+
environment:
|
|
120
|
+
- FAST_PRIORITY_WORKERS=1
|
|
121
|
+
- FAST_PRIORITY_REDIS_HOST=queue
|
|
122
|
+
networks:
|
|
123
|
+
- default
|
|
124
|
+
queue:
|
|
125
|
+
image: redis
|
|
126
|
+
networks:
|
|
127
|
+
- default
|
|
128
|
+
|
|
129
|
+
networks:
|
|
130
|
+
default:
|
|
131
|
+
driver: bridge
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
## Contributing
|
|
137
|
+
Contributions to Fast Priority Queue are welcome! Feel free to open issues or submit pull requests with improvements, bug fixes, or feature suggestions.
|
|
138
|
+
|
|
139
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
fast_priority/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
fast_priority/app.py,sha256=uz8oGINOQo0uQarYjL7LXnLiVb-bpDtE4FYDl8NFQRM,7039
|
|
3
|
+
fast_priority/cli.py,sha256=_iOLrqyO2jgCsHJ6XIY5gzQHiF9jZ2C-htMDOtqrr8s,2579
|
|
4
|
+
fast_priority/utils.py,sha256=4zy87IfoGm74pjR1blvRVTgQr9E2AmM0yLQiyaM5d6Y,523
|
|
5
|
+
fast_priority-0.0.14.dist-info/entry_points.txt,sha256=Kf719P--xxutgzEuNco6CE2Nbm9CdfmL3OBWGLL1v24,60
|
|
6
|
+
fast_priority-0.0.14.dist-info/licenses/LICENSE,sha256=aDYGoGk5kTmy_DbWBRQdenLc8YoarfYvLxQ9JeRhaQA,1076
|
|
7
|
+
fast_priority-0.0.14.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82
|
|
8
|
+
fast_priority-0.0.14.dist-info/METADATA,sha256=t1HkhrjbStzR6jZXBLZyLCCw_z9qVvhPUcqVfn6HSFg,8113
|
|
9
|
+
fast_priority-0.0.14.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Korbinian Schweiger
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|