From 1e9c4d2f502409b983f83b3e85273be82d968122 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 17:20:26 +0800 Subject: [PATCH 01/31] Adjust the unit startup mode --- frameworks/Python/blacksheep/app.py | 47 ++++++++++--------- .../blacksheep-nginx-unit.dockerfile | 20 +++++--- frameworks/Python/blacksheep/requirements.txt | 2 +- frameworks/Python/blacksheep/start-unit.sh | 18 ------- ...-config.template.json => unit-config.json} | 2 +- 5 files changed, 39 insertions(+), 50 deletions(-) delete mode 100755 frameworks/Python/blacksheep/start-unit.sh rename frameworks/Python/blacksheep/{unit-config.template.json => unit-config.json} (92%) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 422a987df43..27e3d9cfedc 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -3,26 +3,27 @@ import asyncpg import random import asyncio -from operator import itemgetter import blacksheep as bs import jinja2 import msgspec from pathlib import Path +try: + import uvloop + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) +except Exception: + ... READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_POOL_SIZE = 1000 // multiprocessing.cpu_count() -MIN_POOL_SIZE = max(int(MAX_POOL_SIZE / 2), 1) +MAX_CONNECTIONS = 1900 +CORE_COUNT = multiprocessing.cpu_count() +WORKER_PROCESSES = CORE_COUNT +MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) +MIN_POOL_SIZE = max(1, MAX_POOL_SIZE // 2) db_pool = None key = itemgetter(1) -try: - import uvloop - asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) -except Exception: - ... - async def setup_db(app): global db_pool db_pool = await asyncpg.create_pool( @@ -33,8 +34,17 @@ async def setup_db(app): port=5432, min_size=MIN_POOL_SIZE, max_size=MAX_POOL_SIZE, + command_timeout=5, + max_inactive_connection_lifetime=60, + server_settings={'jit': 'off'}, ) +async def shutdown_db(app): + """Close asyncpg connection pool for the current process.""" + global db_pool + if db_pool is not None: + await db_pool.close() + db_pool = None def load_fortunes_template(): with Path("templates/fortune.html").open("r") as f: @@ -45,7 +55,7 @@ def load_fortunes_template(): app = bs.Application() app.on_start += setup_db - +app.on_stop += shutdown_db def get_num_queries(request): try: @@ -55,14 +65,9 @@ def get_num_queries(request): query_count = int(value[0]) except (KeyError, IndexError, ValueError): return 1 - if query_count < 1: - return 1 - if query_count > 500: - return 500 - return query_count + return min(max(query_count, 1), 500) ENCODER = msgspec.json.Encoder() -DECODER = msgspec.json.Decoder() JSON_CONTENT_TYPE = b"application/json" def jsonify( data, @@ -122,7 +127,7 @@ async def fortunes_test(request): fortunes = await db_conn.fetch("SELECT * FROM Fortune") fortunes.append(ADDITIONAL_ROW) - fortunes.sort(key = key) + fortunes.sort(key=lambda row: row[1]) data = fortune_template.render(fortunes=fortunes) return bs.html(data) @@ -130,18 +135,14 @@ async def fortunes_test(request): @bs.get('/updates') async def db_updates_test(request): num_queries = get_num_queries(request) - ids = sorted(random.sample(range(1, 10000 + 1), num_queries)) - numbers = sorted(random.sample(range(1, 10000), num_queries)) - updates = list(zip(ids, numbers)) - - # worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ] + updates = [(row_id, random.randint(1, 10000)) for row_id in random.sample(range(1, 10000), num_queries)] worlds = [Result(id=row_id, randomNumber=number) for row_id, number in updates] + # worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ] async with db_pool.acquire() as db_conn: statement = await db_conn.prepare(READ_ROW_SQL) for row_id, _ in updates: await statement.fetchval(row_id) await db_conn.executemany(WRITE_ROW_SQL, updates) - return jsonify(worlds) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index cd09391f6ac..8ee9f1d005d 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -4,14 +4,20 @@ WORKDIR /blacksheep COPY ./ /blacksheep -RUN pip3 install -U pip -RUN pip3 install Cython==3.0.12 -RUN pip3 install -r /blacksheep/requirements.txt -RUN pip3 install -r /blacksheep/requirements-uvicorn.txt - -RUN chmod +x start-unit.sh +RUN pip3 install -U pip -q +RUN pip3 install Cython==3.0.12 -q +RUN pip3 install -r /blacksheep/requirements.txt -q +RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q ENV PGSSLMODE=disable +RUN CORE_COUNT=$(nproc) && \ + MAX_PROCESSES=$((CORE_COUNT)) && \ + sed -i "s|\"processes\": [0-9]*|\"processes\": $MAX_PROCESSES|g" /blacksheep/unit-config.json + +RUN unitd && \ + curl -X PUT --data-binary @/blacksheep/unit-config.json --unix-socket \ + /var/run/control.unit.sock http://localhost/config +ENTRYPOINT [] EXPOSE 8080 -CMD ["./start-unit.sh"] +CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] diff --git a/frameworks/Python/blacksheep/requirements.txt b/frameworks/Python/blacksheep/requirements.txt index 9c83c9fad47..fa93007d8e7 100644 --- a/frameworks/Python/blacksheep/requirements.txt +++ b/frameworks/Python/blacksheep/requirements.txt @@ -1,4 +1,4 @@ asyncpg==0.30.0 Jinja2==3.1.6 -blacksheep==2.1.0 +blacksheep==2.2.0 msgspec==0.19.0 diff --git a/frameworks/Python/blacksheep/start-unit.sh b/frameworks/Python/blacksheep/start-unit.sh deleted file mode 100755 index 9d2b923e166..00000000000 --- a/frameworks/Python/blacksheep/start-unit.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -NPROC=$(nproc) -sed "s/{{NPROC}}/$NPROC/" unit-config.template.json > nginx-unit-config.json - -unitd --no-daemon --control unix:/var/run/control.unit.sock & - -# wait UNIT started -sleep 1 - -# PUT configure -curl -X PUT \ - --data-binary @nginx-unit-config.json \ - --unix-socket /var/run/control.unit.sock \ - http://localhost/config - -# Then keep the container alive -wait \ No newline at end of file diff --git a/frameworks/Python/blacksheep/unit-config.template.json b/frameworks/Python/blacksheep/unit-config.json similarity index 92% rename from frameworks/Python/blacksheep/unit-config.template.json rename to frameworks/Python/blacksheep/unit-config.json index ee02557db34..2b704a3ceeb 100644 --- a/frameworks/Python/blacksheep/unit-config.template.json +++ b/frameworks/Python/blacksheep/unit-config.json @@ -12,7 +12,7 @@ "protocol": "asgi", "module": "app", "callable": "app", - "processes": {{NPROC}} + "processes": 56 } }, "access_log": "/dev/null" From 5428a366c204dbd6e55999be1a22d0eeea635d05 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 17:25:04 +0800 Subject: [PATCH 02/31] fix --- frameworks/Python/blacksheep/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 27e3d9cfedc..78303ba3181 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -22,7 +22,6 @@ MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) MIN_POOL_SIZE = max(1, MAX_POOL_SIZE // 2) db_pool = None -key = itemgetter(1) async def setup_db(app): global db_pool From b5f059684218d428a19aa39f8e1023dd4e14114a Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 17:32:54 +0800 Subject: [PATCH 03/31] try fix --- frameworks/Python/blacksheep/app.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 78303ba3181..03e056ea1b4 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -33,9 +33,6 @@ async def setup_db(app): port=5432, min_size=MIN_POOL_SIZE, max_size=MAX_POOL_SIZE, - command_timeout=5, - max_inactive_connection_lifetime=60, - server_settings={'jit': 'off'}, ) async def shutdown_db(app): From 9eba43f79383a54cc5deba49b98593848138cc0c Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 17:42:25 +0800 Subject: [PATCH 04/31] try fix --- frameworks/Python/blacksheep/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 03e056ea1b4..7b40a50214b 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -16,7 +16,7 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 1900 +MAX_CONNECTIONS = 1200 CORE_COUNT = multiprocessing.cpu_count() WORKER_PROCESSES = CORE_COUNT MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) From babbedb2855f3993e7fe36786e7cd87be55e3753 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 17:49:26 +0800 Subject: [PATCH 05/31] try fix --- frameworks/Python/blacksheep/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 7b40a50214b..38632426f93 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -131,7 +131,7 @@ async def fortunes_test(request): @bs.get('/updates') async def db_updates_test(request): num_queries = get_num_queries(request) - updates = [(row_id, random.randint(1, 10000)) for row_id in random.sample(range(1, 10000), num_queries)] + updates = [(row_id, random.randint(1, 10000)) for row_id in sorted(random.sample(range(1, 10000), num_queries))] worlds = [Result(id=row_id, randomNumber=number) for row_id, number in updates] # worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ] async with db_pool.acquire() as db_conn: From 7179a0143ab84914aefd1f2c9a960925634d4454 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 17:57:05 +0800 Subject: [PATCH 06/31] try fix --- frameworks/Python/blacksheep/app.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 38632426f93..317912903e2 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -16,7 +16,7 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 1200 +MAX_CONNECTIONS = 1000 CORE_COUNT = multiprocessing.cpu_count() WORKER_PROCESSES = CORE_COUNT MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) @@ -131,7 +131,10 @@ async def fortunes_test(request): @bs.get('/updates') async def db_updates_test(request): num_queries = get_num_queries(request) - updates = [(row_id, random.randint(1, 10000)) for row_id in sorted(random.sample(range(1, 10000), num_queries))] + updates = list(zip( + sample(range(1, 10000), num_queries), + sorted(sample(range(1, 10000), num_queries)) + )) worlds = [Result(id=row_id, randomNumber=number) for row_id, number in updates] # worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ] async with db_pool.acquire() as db_conn: From 42ebf90c8048220de1a5aea4e6f1aaa64ea7691b Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 18:01:19 +0800 Subject: [PATCH 07/31] try fix --- frameworks/Python/blacksheep/app.py | 4 ++-- frameworks/Python/blacksheep/blacksheep.dockerfile | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 317912903e2..81c4fc32d79 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -132,8 +132,8 @@ async def fortunes_test(request): async def db_updates_test(request): num_queries = get_num_queries(request) updates = list(zip( - sample(range(1, 10000), num_queries), - sorted(sample(range(1, 10000), num_queries)) + random.sample(range(1, 10000), num_queries), + sorted(random.sample(range(1, 10000), num_queries)) )) worlds = [Result(id=row_id, randomNumber=number) for row_id, number in updates] # worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ] diff --git a/frameworks/Python/blacksheep/blacksheep.dockerfile b/frameworks/Python/blacksheep/blacksheep.dockerfile index e87d8f14e5a..d9d23b4c068 100644 --- a/frameworks/Python/blacksheep/blacksheep.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep.dockerfile @@ -4,11 +4,11 @@ WORKDIR /blacksheep COPY ./ /blacksheep -RUN pip3 install -U pip -RUN pip3 install Cython==3.0.12 -RUN pip3 install -r /blacksheep/requirements.txt -RUN pip3 install -r /blacksheep/requirements-gunicorn.txt -RUN pip3 install -r /blacksheep/requirements-uvicorn.txt +RUN pip3 install -U pip -q +RUN pip3 install Cython==3.0.12 -q +RUN pip3 install -r /blacksheep/requirements.txt -q +RUN pip3 install -r /blacksheep/requirements-gunicorn.txt -q +RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q EXPOSE 8080 From 31f12bec9b8fe29db60f6dced9dc2050811b7e64 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 18:13:20 +0800 Subject: [PATCH 08/31] rollback --- frameworks/Python/blacksheep/app.py | 2 +- .../blacksheep/blacksheep-nginx-unit.dockerfile | 6 ++---- frameworks/Python/blacksheep/start-unit.sh | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 frameworks/Python/blacksheep/start-unit.sh diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 81c4fc32d79..6cd84337eb5 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -16,7 +16,7 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 1000 +MAX_CONNECTIONS = 1800 CORE_COUNT = multiprocessing.cpu_count() WORKER_PROCESSES = CORE_COUNT MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index 8ee9f1d005d..86c07c8c030 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -14,10 +14,8 @@ RUN CORE_COUNT=$(nproc) && \ MAX_PROCESSES=$((CORE_COUNT)) && \ sed -i "s|\"processes\": [0-9]*|\"processes\": $MAX_PROCESSES|g" /blacksheep/unit-config.json -RUN unitd && \ - curl -X PUT --data-binary @/blacksheep/unit-config.json --unix-socket \ - /var/run/control.unit.sock http://localhost/config +RUN chmod +x start-unit.sh ENTRYPOINT [] EXPOSE 8080 -CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] +CMD ["./start-unit.sh"] \ No newline at end of file diff --git a/frameworks/Python/blacksheep/start-unit.sh b/frameworks/Python/blacksheep/start-unit.sh new file mode 100644 index 00000000000..ebfc559a15b --- /dev/null +++ b/frameworks/Python/blacksheep/start-unit.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +unitd --no-daemon --control unix:/var/run/control.unit.sock & + +# wait UNIT started +sleep 1 + +# PUT configure +curl -X PUT \ + --data-binary @unit-config.json \ + --unix-socket /var/run/control.unit.sock \ + http://localhost/config + +# Then keep the container alive +wait \ No newline at end of file From ad9c0659b48113cb3526ac5c4c751aae8e723804 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 20:52:35 +0800 Subject: [PATCH 09/31] adjust --- frameworks/Python/blacksheep/app.py | 2 +- .../blacksheep/blacksheep-nginx-unit.dockerfile | 16 +++++++++++----- frameworks/Python/blacksheep/blacksheep_conf.py | 9 +++++++-- frameworks/Python/blacksheep/unit-config.json | 6 +++++- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 6cd84337eb5..35487e93dc4 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -16,7 +16,7 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 1800 +MAX_CONNECTIONS = 2000 CORE_COUNT = multiprocessing.cpu_count() WORKER_PROCESSES = CORE_COUNT MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index 86c07c8c030..a7b1e2c0ce0 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -11,11 +11,17 @@ RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q ENV PGSSLMODE=disable RUN CORE_COUNT=$(nproc) && \ - MAX_PROCESSES=$((CORE_COUNT)) && \ - sed -i "s|\"processes\": [0-9]*|\"processes\": $MAX_PROCESSES|g" /blacksheep/unit-config.json - -RUN chmod +x start-unit.sh + MAX_PROCESSES=$((CORE_COUNT <= 4 ? CORE_COUNT : CORE_COUNT)) && \ + SPARE_PROCESSES=$((MAX_PROCESSES // 4 > 0 ? MAX_PROCESSES // 4 : 1)) && \ + sed -i "s|\"max\": [0-9]*|\"max\": $MAX_PROCESSES|g" /blacksheep/unit-config.json && \ + sed -i "s|\"spare\": [0-9]*|\"spare\": $SPARE_PROCESSES|g" /blacksheep/unit-config.json && \ + sed -i "s|\"idle_timeout\": [0-9]*|\"idle_timeout\": 3600|g" /blacksheep/unit-config.json +# RUN chmod +x start-unit.sh +RUN unitd && \ + curl -X PUT --data-binary @/blacksheep/unit-config.json --unix-socket \ + /var/run/control.unit.sock http://localhost/config ENTRYPOINT [] EXPOSE 8080 -CMD ["./start-unit.sh"] \ No newline at end of file +CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] +# CMD ["./start-unit.sh"] \ No newline at end of file diff --git a/frameworks/Python/blacksheep/blacksheep_conf.py b/frameworks/Python/blacksheep/blacksheep_conf.py index 4f4e08a729e..e88ebd5e602 100644 --- a/frameworks/Python/blacksheep/blacksheep_conf.py +++ b/frameworks/Python/blacksheep/blacksheep_conf.py @@ -2,13 +2,18 @@ import os _is_travis = os.environ.get('TRAVIS') == 'true' +CPU_CORES = multiprocessing.cpu_count() +MAX_CONNECTIONS = 2000 +CONNECTIONS_PER_WORKER = 50 +max_pg_workers = MAX_CONNECTIONS // CONNECTIONS_PER_WORKER -workers = multiprocessing.cpu_count() +workers = min(CPU_CORES, max_pg_workers) if _is_travis: workers = 2 bind = "0.0.0.0:8080" -keepalive = 120 +keepalive = 1 +timeout = 0 errorlog = '-' pidfile = '/tmp/blacksheep.pid' loglevel = 'error' diff --git a/frameworks/Python/blacksheep/unit-config.json b/frameworks/Python/blacksheep/unit-config.json index 2b704a3ceeb..8bd16c0c219 100644 --- a/frameworks/Python/blacksheep/unit-config.json +++ b/frameworks/Python/blacksheep/unit-config.json @@ -12,7 +12,11 @@ "protocol": "asgi", "module": "app", "callable": "app", - "processes": 56 + "processes": { + "max": 56, + "spare": 14, + "idle_timeout": 3600 + } } }, "access_log": "/dev/null" From 49491a94958a4d9ced5667bb9af66667e47bcfd7 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 20:58:50 +0800 Subject: [PATCH 10/31] fix --- frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index a7b1e2c0ce0..09863c10419 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -11,8 +11,8 @@ RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q ENV PGSSLMODE=disable RUN CORE_COUNT=$(nproc) && \ - MAX_PROCESSES=$((CORE_COUNT <= 4 ? CORE_COUNT : CORE_COUNT)) && \ - SPARE_PROCESSES=$((MAX_PROCESSES // 4 > 0 ? MAX_PROCESSES // 4 : 1)) && \ + MAX_PROCESSES=$((CORE_COUNT <= 4 ? CORE_COUNT : CORE_COUNT / 2)) && \ + SPARE_PROCESSES=$((MAX_PROCESSES / 4 > 0 ? MAX_PROCESSES / 4 : 1)) && \ sed -i "s|\"max\": [0-9]*|\"max\": $MAX_PROCESSES|g" /blacksheep/unit-config.json && \ sed -i "s|\"spare\": [0-9]*|\"spare\": $SPARE_PROCESSES|g" /blacksheep/unit-config.json && \ sed -i "s|\"idle_timeout\": [0-9]*|\"idle_timeout\": 3600|g" /blacksheep/unit-config.json From 65b02f692f371ccc544d50270c292f4e9383a7f7 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 21:47:47 +0800 Subject: [PATCH 11/31] try change --- .../Python/blacksheep/blacksheep-nginx-unit.dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index 09863c10419..b504a3915e0 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -18,9 +18,11 @@ RUN CORE_COUNT=$(nproc) && \ sed -i "s|\"idle_timeout\": [0-9]*|\"idle_timeout\": 3600|g" /blacksheep/unit-config.json # RUN chmod +x start-unit.sh -RUN unitd && \ - curl -X PUT --data-binary @/blacksheep/unit-config.json --unix-socket \ - /var/run/control.unit.sock http://localhost/config +# RUN unitd && \ +# curl -X PUT --data-binary @/blacksheep/unit-config.json --unix-socket \ +# /var/run/control.unit.sock http://localhost/config + +COPY ./unit-config.json /docker-entrypoint.d/unit-config.json ENTRYPOINT [] EXPOSE 8080 CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] From d126655690223696e47ee6fad4b60a23a37f20d2 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 21:58:42 +0800 Subject: [PATCH 12/31] try change --- .../Python/blacksheep/blacksheep-nginx-unit.dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index b504a3915e0..1cbd2d58fe4 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -22,8 +22,9 @@ RUN CORE_COUNT=$(nproc) && \ # curl -X PUT --data-binary @/blacksheep/unit-config.json --unix-socket \ # /var/run/control.unit.sock http://localhost/config -COPY ./unit-config.json /docker-entrypoint.d/unit-config.json +COPY ./unit-config.json /docker-entrypoint.d/config.json ENTRYPOINT [] EXPOSE 8080 -CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] + +# CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] # CMD ["./start-unit.sh"] \ No newline at end of file From 3a2ec40adab47b40f0632b4a491b997113d638ca Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 8 May 2025 22:12:59 +0800 Subject: [PATCH 13/31] fix --- .../Python/blacksheep/blacksheep-nginx-unit.dockerfile | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index 1cbd2d58fe4..a160f038c5e 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -17,14 +17,9 @@ RUN CORE_COUNT=$(nproc) && \ sed -i "s|\"spare\": [0-9]*|\"spare\": $SPARE_PROCESSES|g" /blacksheep/unit-config.json && \ sed -i "s|\"idle_timeout\": [0-9]*|\"idle_timeout\": 3600|g" /blacksheep/unit-config.json -# RUN chmod +x start-unit.sh -# RUN unitd && \ -# curl -X PUT --data-binary @/blacksheep/unit-config.json --unix-socket \ -# /var/run/control.unit.sock http://localhost/config - -COPY ./unit-config.json /docker-entrypoint.d/config.json +RUN chmod +x start-unit.sh ENTRYPOINT [] EXPOSE 8080 # CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] -# CMD ["./start-unit.sh"] \ No newline at end of file +CMD ["./start-unit.sh"] \ No newline at end of file From 8b6a4cffe57b44a1cca34eeb378cdc7caac086db Mon Sep 17 00:00:00 2001 From: nazo Date: Fri, 9 May 2025 10:29:36 +0800 Subject: [PATCH 14/31] Tuning --- frameworks/Python/blacksheep/app.py | 6 +++++- .../blacksheep-nginx-unit.dockerfile | 18 ++++++++++++------ .../Python/blacksheep/blacksheep.dockerfile | 2 +- .../Python/blacksheep/blacksheep_conf.py | 6 +++--- frameworks/Python/blacksheep/start-unit.sh | 7 ++++++- frameworks/Python/blacksheep/unit-config.json | 9 +++------ 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 35487e93dc4..3f611195b66 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -16,8 +16,12 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 2000 +MAX_CONNECTIONS = 1900 CORE_COUNT = multiprocessing.cpu_count() +PROCESSES = CPU_CORES if os.getenv('GUNICORN') else min(8, CORE_COUNT // 4) if CORE_COUNT > 4 else CORE_COUNT +MAX_POOL_SIZE = max(1,int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) +MIN_POOL_SIZE = max(1,int(os.getenv('MIN_POOL_SIZE', MAX_POOL_SIZE // 2))) + WORKER_PROCESSES = CORE_COUNT MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) MIN_POOL_SIZE = max(1, MAX_POOL_SIZE // 2) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index a160f038c5e..3f5ebd5eca8 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -11,15 +11,21 @@ RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q ENV PGSSLMODE=disable RUN CORE_COUNT=$(nproc) && \ - MAX_PROCESSES=$((CORE_COUNT <= 4 ? CORE_COUNT : CORE_COUNT / 2)) && \ - SPARE_PROCESSES=$((MAX_PROCESSES / 4 > 0 ? MAX_PROCESSES / 4 : 1)) && \ - sed -i "s|\"max\": [0-9]*|\"max\": $MAX_PROCESSES|g" /blacksheep/unit-config.json && \ - sed -i "s|\"spare\": [0-9]*|\"spare\": $SPARE_PROCESSES|g" /blacksheep/unit-config.json && \ - sed -i "s|\"idle_timeout\": [0-9]*|\"idle_timeout\": 3600|g" /blacksheep/unit-config.json + PROCESSES=$(($CORE_COUNT <= 4 ? $CORE_COUNT : $CORE_COUNT / 4)) && \ + PROCESSES=$(($PROCESSES < 4 ? 4 : $PROCESSES > 16 ? 16 : $PROCESSES)) && \ + THREADS=$(($CORE_COUNT / $PROCESSES)) && \ + THREADS=$(($THREADS < 10 ? 10 : $THREADS)) && \ + MAX_POOL_SIZE=$((2000 / $PROCESSES)) && \ + MIN_POOL_SIZE=$(($MAX_POOL_SIZE / 2)) && \ + sed -i "s|\"processes\": [0-9]*.*\"threads\": [0-9]*|\"processes\": $PROCESSES, \"threads\": $THREADS|g" /blacksheep/unit-config.json && \ + echo "export MAX_POOL_SIZE=$MAX_POOL_SIZE" >> /blacksheep/env.sh && \ + echo "export MIN_POOL_SIZE=$MIN_POOL_SIZE" >> /blacksheep/env.sh RUN chmod +x start-unit.sh +RUN chmod +x env.sh ENTRYPOINT [] EXPOSE 8080 # CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] -CMD ["./start-unit.sh"] \ No newline at end of file +# CMD ["./start-unit.sh"] +CMD ["bash", "-c", "source /blacksheep/env.sh && ./start-unit.sh"] \ No newline at end of file diff --git a/frameworks/Python/blacksheep/blacksheep.dockerfile b/frameworks/Python/blacksheep/blacksheep.dockerfile index d9d23b4c068..f158f27a37a 100644 --- a/frameworks/Python/blacksheep/blacksheep.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep.dockerfile @@ -9,7 +9,7 @@ RUN pip3 install Cython==3.0.12 -q RUN pip3 install -r /blacksheep/requirements.txt -q RUN pip3 install -r /blacksheep/requirements-gunicorn.txt -q RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q - +ENV GUNICORN=1 EXPOSE 8080 CMD gunicorn app:app -k uvicorn.workers.UvicornWorker -c blacksheep_conf.py diff --git a/frameworks/Python/blacksheep/blacksheep_conf.py b/frameworks/Python/blacksheep/blacksheep_conf.py index e88ebd5e602..7324c9efddb 100644 --- a/frameworks/Python/blacksheep/blacksheep_conf.py +++ b/frameworks/Python/blacksheep/blacksheep_conf.py @@ -3,11 +3,11 @@ _is_travis = os.environ.get('TRAVIS') == 'true' CPU_CORES = multiprocessing.cpu_count() -MAX_CONNECTIONS = 2000 -CONNECTIONS_PER_WORKER = 50 +MAX_CONNECTIONS = 1900 +CONNECTIONS_PER_WORKER = 100 max_pg_workers = MAX_CONNECTIONS // CONNECTIONS_PER_WORKER -workers = min(CPU_CORES, max_pg_workers) +workers = CPU_CORES if _is_travis: workers = 2 diff --git a/frameworks/Python/blacksheep/start-unit.sh b/frameworks/Python/blacksheep/start-unit.sh index ebfc559a15b..016ee8c1020 100644 --- a/frameworks/Python/blacksheep/start-unit.sh +++ b/frameworks/Python/blacksheep/start-unit.sh @@ -1,6 +1,11 @@ #!/usr/bin/env bash +CORE_COUNT=$(nproc) +sysctl -w net.core.somaxconn=65535 +sysctl -w net.ipv4.tcp_max_syn_backlog=65535 +ulimit -n 65535 +taskset -c 0-$(($CORE_COUNT-1)) unitd --no-daemon --control unix:/var/run/control.unit.sock & -unitd --no-daemon --control unix:/var/run/control.unit.sock & +# unitd --no-daemon --control unix:/var/run/control.unit.sock & # wait UNIT started sleep 1 diff --git a/frameworks/Python/blacksheep/unit-config.json b/frameworks/Python/blacksheep/unit-config.json index 8bd16c0c219..677f05f1fbe 100644 --- a/frameworks/Python/blacksheep/unit-config.json +++ b/frameworks/Python/blacksheep/unit-config.json @@ -9,14 +9,11 @@ "type": "python", "path": "/blacksheep", "working_directory": "/blacksheep", + "processes": 14, + "threads": 10, "protocol": "asgi", "module": "app", - "callable": "app", - "processes": { - "max": 56, - "spare": 14, - "idle_timeout": 3600 - } + "callable": "app" } }, "access_log": "/dev/null" From e7598bacb36e81424cc786fc065728378b699189 Mon Sep 17 00:00:00 2001 From: nazo Date: Fri, 9 May 2025 10:57:15 +0800 Subject: [PATCH 15/31] fix --- frameworks/Python/blacksheep/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 3f611195b66..a50e9c87653 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -18,7 +18,7 @@ ADDITIONAL_ROW = [0, "Additional fortune added at request time."] MAX_CONNECTIONS = 1900 CORE_COUNT = multiprocessing.cpu_count() -PROCESSES = CPU_CORES if os.getenv('GUNICORN') else min(8, CORE_COUNT // 4) if CORE_COUNT > 4 else CORE_COUNT +PROCESSES = CORE_COUNT if os.getenv('GUNICORN') else min(8, CORE_COUNT // 4) if CORE_COUNT > 4 else CORE_COUNT MAX_POOL_SIZE = max(1,int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) MIN_POOL_SIZE = max(1,int(os.getenv('MIN_POOL_SIZE', MAX_POOL_SIZE // 2))) From 8090190206ecd122f8b47e408511864e319a36ff Mon Sep 17 00:00:00 2001 From: nazo Date: Fri, 9 May 2025 11:06:53 +0800 Subject: [PATCH 16/31] try fix --- frameworks/Python/blacksheep/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index a50e9c87653..9a0a131a594 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -16,7 +16,7 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 1900 +MAX_CONNECTIONS = 1800 if os.getenv('GUNICORN') else 1000 CORE_COUNT = multiprocessing.cpu_count() PROCESSES = CORE_COUNT if os.getenv('GUNICORN') else min(8, CORE_COUNT // 4) if CORE_COUNT > 4 else CORE_COUNT MAX_POOL_SIZE = max(1,int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) From c8c4b5312ce51f6e1955c3dd44671478aecb32c5 Mon Sep 17 00:00:00 2001 From: nazo Date: Fri, 9 May 2025 11:37:16 +0800 Subject: [PATCH 17/31] try fix --- frameworks/Python/blacksheep/app.py | 4 ++-- .../blacksheep/blacksheep-nginx-unit.dockerfile | 14 ++------------ frameworks/Python/blacksheep/unit-config.json | 1 - 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 9a0a131a594..43078a4ba44 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -16,9 +16,9 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 1800 if os.getenv('GUNICORN') else 1000 +MAX_CONNECTIONS = 1900 CORE_COUNT = multiprocessing.cpu_count() -PROCESSES = CORE_COUNT if os.getenv('GUNICORN') else min(8, CORE_COUNT // 4) if CORE_COUNT > 4 else CORE_COUNT +PROCESSES = CORE_COUNT MAX_POOL_SIZE = max(1,int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) MIN_POOL_SIZE = max(1,int(os.getenv('MIN_POOL_SIZE', MAX_POOL_SIZE // 2))) diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index 3f5ebd5eca8..ca4f573fa78 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -11,21 +11,11 @@ RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q ENV PGSSLMODE=disable RUN CORE_COUNT=$(nproc) && \ - PROCESSES=$(($CORE_COUNT <= 4 ? $CORE_COUNT : $CORE_COUNT / 4)) && \ - PROCESSES=$(($PROCESSES < 4 ? 4 : $PROCESSES > 16 ? 16 : $PROCESSES)) && \ - THREADS=$(($CORE_COUNT / $PROCESSES)) && \ - THREADS=$(($THREADS < 10 ? 10 : $THREADS)) && \ - MAX_POOL_SIZE=$((2000 / $PROCESSES)) && \ - MIN_POOL_SIZE=$(($MAX_POOL_SIZE / 2)) && \ - sed -i "s|\"processes\": [0-9]*.*\"threads\": [0-9]*|\"processes\": $PROCESSES, \"threads\": $THREADS|g" /blacksheep/unit-config.json && \ - echo "export MAX_POOL_SIZE=$MAX_POOL_SIZE" >> /blacksheep/env.sh && \ - echo "export MIN_POOL_SIZE=$MIN_POOL_SIZE" >> /blacksheep/env.sh + sed -i "s|\"processes\": [0-9]*|\"processes\": $CORE_COUNT|g" /blacksheep/unit-config.json RUN chmod +x start-unit.sh -RUN chmod +x env.sh ENTRYPOINT [] EXPOSE 8080 # CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"] -# CMD ["./start-unit.sh"] -CMD ["bash", "-c", "source /blacksheep/env.sh && ./start-unit.sh"] \ No newline at end of file +CMD ["./start-unit.sh"] \ No newline at end of file diff --git a/frameworks/Python/blacksheep/unit-config.json b/frameworks/Python/blacksheep/unit-config.json index 677f05f1fbe..186d581296f 100644 --- a/frameworks/Python/blacksheep/unit-config.json +++ b/frameworks/Python/blacksheep/unit-config.json @@ -10,7 +10,6 @@ "path": "/blacksheep", "working_directory": "/blacksheep", "processes": 14, - "threads": 10, "protocol": "asgi", "module": "app", "callable": "app" From 36d3800379a9071c09e2a355429acfbed2a73698 Mon Sep 17 00:00:00 2001 From: nazo Date: Wed, 21 May 2025 11:34:06 +0800 Subject: [PATCH 18/31] add pypy --- frameworks/Python/blacksheep/app-socketify.py | 165 ++++++++++++++++++ frameworks/Python/blacksheep/app.py | 24 +++ .../Python/blacksheep/benchmark_config.json | 23 +++ .../blacksheep-nginx-unit.dockerfile | 2 + .../blacksheep-socketify-asgi-pypy.dockerfile | 15 ++ .../Python/blacksheep/blacksheep.dockerfile | 3 +- frameworks/Python/blacksheep/config.toml | 18 ++ .../blacksheep/requirements-gunicorn.txt | 1 - .../blacksheep/requirements-hypercorn.txt | 1 - .../Python/blacksheep/requirements-pypy.txt | 2 + .../blacksheep/requirements-uvicorn.txt | 2 + frameworks/Python/blacksheep/requirements.txt | 3 +- 12 files changed, 254 insertions(+), 5 deletions(-) create mode 100644 frameworks/Python/blacksheep/app-socketify.py create mode 100644 frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile delete mode 100644 frameworks/Python/blacksheep/requirements-gunicorn.txt delete mode 100644 frameworks/Python/blacksheep/requirements-hypercorn.txt create mode 100644 frameworks/Python/blacksheep/requirements-pypy.txt diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py new file mode 100644 index 00000000000..c86c26fc11c --- /dev/null +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -0,0 +1,165 @@ +import multiprocessing +import os +import asyncpg +import platform +import random +import asyncio +import blacksheep as bs +import jinja2 +# import json +from pathlib import Path +try: + import uvloop + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) +except Exception: + ... + +READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' +WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' +ADDITIONAL_ROW = [0, "Additional fortune added at request time."] +MAX_CONNECTIONS = 1900 +CORE_COUNT = multiprocessing.cpu_count() +PROCESSES = CORE_COUNT +MAX_POOL_SIZE = max(1,int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) +MIN_POOL_SIZE = max(1,int(os.getenv('MIN_POOL_SIZE', MAX_POOL_SIZE // 2))) + +WORKER_PROCESSES = CORE_COUNT +MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) +MIN_POOL_SIZE = max(1, MAX_POOL_SIZE // 2) +db_pool = None + +async def setup_db(app): + global db_pool + db_pool = await asyncpg.create_pool( + user=os.getenv('PGUSER', "benchmarkdbuser"), + password=os.getenv('PGPASS', "benchmarkdbpass"), + database='hello_world', + host="tfb-database", + port=5432, + min_size=MIN_POOL_SIZE, + max_size=MAX_POOL_SIZE, + ) + +async def shutdown_db(app): + """Close asyncpg connection pool for the current process.""" + global db_pool + if db_pool is not None: + await db_pool.close() + db_pool = None + +def load_fortunes_template(): + with Path("templates/fortune.html").open("r") as f: + return jinja2.Template(f.read()) + + +fortune_template = load_fortunes_template() + +app = bs.Application() +app.on_start += setup_db +app.on_stop += shutdown_db + +def get_num_queries(request): + try: + value = request.query.get('queries') + if value is None: + return 1 + query_count = int(value[0]) + except (KeyError, IndexError, ValueError): + return 1 + return min(max(query_count, 1), 500) + +JSON_CONTENT_TYPE = b"application/json" + + +# ------------------------------------------------------------------------------------------ + +@bs.get('/json') +async def json_test(request): + return bs.json( {'message': 'Hello, world!'} ) + +@bs.get('/db') +async def single_db_query_test(request): + row_id = random.randint(1, 10000) + + async with db_pool.acquire() as db_conn: + number = await db_conn.fetchval(READ_ROW_SQL, row_id) + + # return jsonify(Result(id=row_id, randomNumber=number)) + # return ({'id': row_id, 'randomNumber': number}) + return bs.json({'id': row_id, 'randomNumber': number}) + + +@bs.get('/queries') +async def multiple_db_queries_test(request): + num_queries = get_num_queries(request) + row_ids = random.sample(range(1, 10000), num_queries) + worlds = [] + + async with db_pool.acquire() as db_conn: + statement = await db_conn.prepare(READ_ROW_SQL) + for row_id in row_ids: + number = await statement.fetchval(row_id) + worlds.append( {"id": row_id, "randomNumber": number} ) + # worlds.append(Result(id=row_id, randomNumber=number)) + + return bs.json(worlds) + # return jsonify(worlds) + + +@bs.get('/fortunes') +async def fortunes_test(request): + async with db_pool.acquire() as db_conn: + fortunes = await db_conn.fetch("SELECT * FROM Fortune") + + fortunes.append(ADDITIONAL_ROW) + fortunes.sort(key=lambda row: row[1]) + data = fortune_template.render(fortunes=fortunes) + return bs.html(data) + + +@bs.get('/updates') +async def db_updates_test(request): + num_queries = get_num_queries(request) + updates = list(zip( + random.sample(range(1, 10000), num_queries), + sorted(random.sample(range(1, 10000), num_queries)) + )) + # worlds = [Result(id=row_id, randomNumber=number) for row_id, number in updates] + worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ] + async with db_pool.acquire() as db_conn: + statement = await db_conn.prepare(READ_ROW_SQL) + for row_id, _ in updates: + await statement.fetchval(row_id) + await db_conn.executemany(WRITE_ROW_SQL, updates) + return bs.json(worlds) + # return jsonify(worlds) + + +@bs.get('/plaintext') +async def plaintext_test(request): + return bs.Response(200, content=bs.Content(b"text/plain", b'Hello, World!')) + #return bs.text('Hello, World!') + + +if platform.python_implementation() == 'PyPy': + from socketify import ASGI + workers = int(multiprocessing.cpu_count()) + if _is_travis: + workers = 2 + + def run_app(): + ASGI(app).listen(8080, lambda config: logging.info(f"Listening on port http://localhost:{config.port} now\n")).run() + + + def create_fork(): + n = os.fork() + # n greater than 0 means parent process + if not n > 0: + run_app() + + + # fork limiting the cpu count - 1 + for i in range(1, workers): + create_fork() + + run_app() \ No newline at end of file diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index 43078a4ba44..a5090311c6a 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -1,6 +1,7 @@ import multiprocessing import os import asyncpg +import platform import random import asyncio import blacksheep as bs @@ -154,3 +155,26 @@ async def plaintext_test(request): return bs.Response(200, content=bs.Content(b"text/plain", b'Hello, World!')) #return bs.text('Hello, World!') + +if platform.python_implementation() == 'PyPy': + from socketify import ASGI + workers = int(multiprocessing.cpu_count()) + if _is_travis: + workers = 2 + + def run_app(): + ASGI(app).listen(8080, lambda config: logging.info(f"Listening on port http://localhost:{config.port} now\n")).run() + + + def create_fork(): + n = os.fork() + # n greater than 0 means parent process + if not n > 0: + run_app() + + + # fork limiting the cpu count - 1 + for i in range(1, workers): + create_fork() + + run_app() \ No newline at end of file diff --git a/frameworks/Python/blacksheep/benchmark_config.json b/frameworks/Python/blacksheep/benchmark_config.json index fb05ddddbe8..db211d08363 100644 --- a/frameworks/Python/blacksheep/benchmark_config.json +++ b/frameworks/Python/blacksheep/benchmark_config.json @@ -46,6 +46,29 @@ "display_name": "blacksheep-nginx-unit", "versus": "None", "notes": "" + }, + "pypy-socketify": { + "json_url": "/json", + "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "framework": "blacksheep", + "language": "Python", + "flavor": "Python3", + "platform": "ASGI", + "webserver": "Socketify.py", + "os": "Linux", + "orm": "Raw", + "database_os": "Linux", + "database": "Postgres", + "display_name": "Blacksheep [Socketify.py PyPy3]", + "versus": "None", + "notes": "" } }] } diff --git a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile index ca4f573fa78..3014ba7cfdb 100644 --- a/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-nginx-unit.dockerfile @@ -4,6 +4,8 @@ WORKDIR /blacksheep COPY ./ /blacksheep +RUN apt-get update; apt-get install libuv1 -y + RUN pip3 install -U pip -q RUN pip3 install Cython==3.0.12 -q RUN pip3 install -r /blacksheep/requirements.txt -q diff --git a/frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile b/frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile new file mode 100644 index 00000000000..643c7fe7238 --- /dev/null +++ b/frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile @@ -0,0 +1,15 @@ +FROM pypy:3.11-bookworm + +ADD ./ /blacksheep + +WORKDIR /blacksheep + +RUN apt-get update; apt-get install libuv1 -y + +RUN pip3 install -r /blacksheep/requirements.txt +RUN pip3 install -r /blacksheep/requirements-pypy.txt + +EXPOSE 8080 + +CMD python ./app.py + diff --git a/frameworks/Python/blacksheep/blacksheep.dockerfile b/frameworks/Python/blacksheep/blacksheep.dockerfile index f158f27a37a..2495d578252 100644 --- a/frameworks/Python/blacksheep/blacksheep.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep.dockerfile @@ -4,10 +4,11 @@ WORKDIR /blacksheep COPY ./ /blacksheep +RUN apt-get update; apt-get install libuv1 -y + RUN pip3 install -U pip -q RUN pip3 install Cython==3.0.12 -q RUN pip3 install -r /blacksheep/requirements.txt -q -RUN pip3 install -r /blacksheep/requirements-gunicorn.txt -q RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q ENV GUNICORN=1 EXPOSE 8080 diff --git a/frameworks/Python/blacksheep/config.toml b/frameworks/Python/blacksheep/config.toml index c37e96092d5..ea30e9cad43 100644 --- a/frameworks/Python/blacksheep/config.toml +++ b/frameworks/Python/blacksheep/config.toml @@ -35,3 +35,21 @@ orm = "Raw" platform = "ASGI" webserver = "nginx-unit" versus = "None" + + +[pypy-socketify] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Platform" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "ASGI" +webserver = "Socketify.py" +versus = "None" diff --git a/frameworks/Python/blacksheep/requirements-gunicorn.txt b/frameworks/Python/blacksheep/requirements-gunicorn.txt deleted file mode 100644 index 4afe40e70ce..00000000000 --- a/frameworks/Python/blacksheep/requirements-gunicorn.txt +++ /dev/null @@ -1 +0,0 @@ -gunicorn==23.0.0 diff --git a/frameworks/Python/blacksheep/requirements-hypercorn.txt b/frameworks/Python/blacksheep/requirements-hypercorn.txt deleted file mode 100644 index 3e0a21f2fe2..00000000000 --- a/frameworks/Python/blacksheep/requirements-hypercorn.txt +++ /dev/null @@ -1 +0,0 @@ -hypercorn==0.17.3 diff --git a/frameworks/Python/blacksheep/requirements-pypy.txt b/frameworks/Python/blacksheep/requirements-pypy.txt new file mode 100644 index 00000000000..c69a47d5692 --- /dev/null +++ b/frameworks/Python/blacksheep/requirements-pypy.txt @@ -0,0 +1,2 @@ +socketify +h11 \ No newline at end of file diff --git a/frameworks/Python/blacksheep/requirements-uvicorn.txt b/frameworks/Python/blacksheep/requirements-uvicorn.txt index 7a5c5d8140d..5db1075c36d 100644 --- a/frameworks/Python/blacksheep/requirements-uvicorn.txt +++ b/frameworks/Python/blacksheep/requirements-uvicorn.txt @@ -1,3 +1,5 @@ uvloop==0.21.0 uvicorn==0.34.1 httptools==0.6.4 +gunicorn==23.0.0 +msgspec==0.19.0 diff --git a/frameworks/Python/blacksheep/requirements.txt b/frameworks/Python/blacksheep/requirements.txt index fa93007d8e7..1f15278011e 100644 --- a/frameworks/Python/blacksheep/requirements.txt +++ b/frameworks/Python/blacksheep/requirements.txt @@ -1,4 +1,3 @@ asyncpg==0.30.0 Jinja2==3.1.6 -blacksheep==2.2.0 -msgspec==0.19.0 +blacksheep==2.3.1 From 540651b305f542a777e953584e9f66e805727ccb Mon Sep 17 00:00:00 2001 From: nazo Date: Wed, 21 May 2025 11:52:34 +0800 Subject: [PATCH 19/31] try fix pypy --- frameworks/Python/blacksheep/app-socketify.py | 100 +++++++----------- .../blacksheep-socketify-asgi-pypy.dockerfile | 2 +- .../Python/blacksheep/requirements-pypy.txt | 1 + .../blacksheep/requirements-uvicorn.txt | 1 + frameworks/Python/blacksheep/requirements.txt | 2 +- 5 files changed, 42 insertions(+), 64 deletions(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index c86c26fc11c..ad2c9a0f87d 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -1,27 +1,22 @@ import multiprocessing import os -import asyncpg +import psycopg import platform import random import asyncio import blacksheep as bs import jinja2 -# import json from pathlib import Path -try: - import uvloop - asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) -except Exception: - ... - -READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' -WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' +from psycopg_pool import AsyncConnectionPool + +READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = %s' +WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=%s WHERE id=%s' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] MAX_CONNECTIONS = 1900 CORE_COUNT = multiprocessing.cpu_count() PROCESSES = CORE_COUNT -MAX_POOL_SIZE = max(1,int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) -MIN_POOL_SIZE = max(1,int(os.getenv('MIN_POOL_SIZE', MAX_POOL_SIZE // 2))) +MAX_POOL_SIZE = max(1, int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) +MIN_POOL_SIZE = max(1, int(os.getenv('MIN_POOL_SIZE', MAX_POOL_SIZE // 2))) WORKER_PROCESSES = CORE_COUNT MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) @@ -30,18 +25,19 @@ async def setup_db(app): global db_pool - db_pool = await asyncpg.create_pool( - user=os.getenv('PGUSER', "benchmarkdbuser"), - password=os.getenv('PGPASS', "benchmarkdbpass"), - database='hello_world', - host="tfb-database", - port=5432, + conninfo = ( + f"postgresql://{os.getenv('PGUSER', 'benchmarkdbuser')}:{os.getenv('PGPASS', 'benchmarkdbpass')}" + f"@tfb-database:5432/hello_world" + ) + db_pool = AsyncConnectionPool( + conninfo=conninfo, min_size=MIN_POOL_SIZE, max_size=MAX_POOL_SIZE, + open=False, ) + await db_pool.open() async def shutdown_db(app): - """Close asyncpg connection pool for the current process.""" global db_pool if db_pool is not None: await db_pool.close() @@ -51,7 +47,6 @@ def load_fortunes_template(): with Path("templates/fortune.html").open("r") as f: return jinja2.Template(f.read()) - fortune_template = load_fortunes_template() app = bs.Application() @@ -70,53 +65,43 @@ def get_num_queries(request): JSON_CONTENT_TYPE = b"application/json" - -# ------------------------------------------------------------------------------------------ - @bs.get('/json') async def json_test(request): - return bs.json( {'message': 'Hello, world!'} ) + return bs.json({'message': 'Hello, world!'}) @bs.get('/db') async def single_db_query_test(request): row_id = random.randint(1, 10000) - - async with db_pool.acquire() as db_conn: - number = await db_conn.fetchval(READ_ROW_SQL, row_id) - - # return jsonify(Result(id=row_id, randomNumber=number)) - # return ({'id': row_id, 'randomNumber': number}) - return bs.json({'id': row_id, 'randomNumber': number}) - + async with db_pool.connection() as db_conn: + async with db_conn.cursor() as cursor: + await cursor.execute(READ_ROW_SQL, (row_id,)) + number = await cursor.fetchone() + return bs.json({'id': row_id, 'randomNumber': number[1]}) @bs.get('/queries') async def multiple_db_queries_test(request): num_queries = get_num_queries(request) row_ids = random.sample(range(1, 10000), num_queries) worlds = [] - - async with db_pool.acquire() as db_conn: - statement = await db_conn.prepare(READ_ROW_SQL) - for row_id in row_ids: - number = await statement.fetchval(row_id) - worlds.append( {"id": row_id, "randomNumber": number} ) - # worlds.append(Result(id=row_id, randomNumber=number)) - + async with db_pool.connection() as db_conn: + async with db_conn.cursor() as cursor: + for row_id in row_ids: + await cursor.execute(READ_ROW_SQL, (row_id,)) + number = await cursor.fetchone() + worlds.append({"id": row_id, "randomNumber": number[1]}) return bs.json(worlds) - # return jsonify(worlds) - @bs.get('/fortunes') async def fortunes_test(request): - async with db_pool.acquire() as db_conn: - fortunes = await db_conn.fetch("SELECT * FROM Fortune") - + async with db_pool.connection() as db_conn: + async with db_conn.cursor() as cursor: + await cursor.execute("SELECT * FROM Fortune") + fortunes = await cursor.fetchall() fortunes.append(ADDITIONAL_ROW) fortunes.sort(key=lambda row: row[1]) data = fortune_template.render(fortunes=fortunes) return bs.html(data) - @bs.get('/updates') async def db_updates_test(request): num_queries = get_num_queries(request) @@ -124,22 +109,18 @@ async def db_updates_test(request): random.sample(range(1, 10000), num_queries), sorted(random.sample(range(1, 10000), num_queries)) )) - # worlds = [Result(id=row_id, randomNumber=number) for row_id, number in updates] - worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ] - async with db_pool.acquire() as db_conn: - statement = await db_conn.prepare(READ_ROW_SQL) - for row_id, _ in updates: - await statement.fetchval(row_id) - await db_conn.executemany(WRITE_ROW_SQL, updates) + worlds = [{"id": row_id, "randomNumber": number} for row_id, number in updates] + async with db_pool.connection() as db_conn: + async with db_conn.cursor() as cursor: + for row_id, number in updates: + await cursor.execute(READ_ROW_SQL, (row_id,)) + await cursor.fetchone() + await cursor.executemany(WRITE_ROW_SQL, [(number, row_id) for row_id, number in updates]) return bs.json(worlds) - # return jsonify(worlds) - @bs.get('/plaintext') async def plaintext_test(request): return bs.Response(200, content=bs.Content(b"text/plain", b'Hello, World!')) - #return bs.text('Hello, World!') - if platform.python_implementation() == 'PyPy': from socketify import ASGI @@ -150,16 +131,11 @@ async def plaintext_test(request): def run_app(): ASGI(app).listen(8080, lambda config: logging.info(f"Listening on port http://localhost:{config.port} now\n")).run() - def create_fork(): n = os.fork() - # n greater than 0 means parent process if not n > 0: run_app() - - # fork limiting the cpu count - 1 for i in range(1, workers): create_fork() - run_app() \ No newline at end of file diff --git a/frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile b/frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile index 643c7fe7238..a3c9ed60643 100644 --- a/frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile @@ -4,7 +4,7 @@ ADD ./ /blacksheep WORKDIR /blacksheep -RUN apt-get update; apt-get install libuv1 -y +RUN apt-get update; apt-get install libuv1 libpq5 -y RUN pip3 install -r /blacksheep/requirements.txt RUN pip3 install -r /blacksheep/requirements-pypy.txt diff --git a/frameworks/Python/blacksheep/requirements-pypy.txt b/frameworks/Python/blacksheep/requirements-pypy.txt index c69a47d5692..b232eb36b2a 100644 --- a/frameworks/Python/blacksheep/requirements-pypy.txt +++ b/frameworks/Python/blacksheep/requirements-pypy.txt @@ -1,2 +1,3 @@ +psycopg[pool] socketify h11 \ No newline at end of file diff --git a/frameworks/Python/blacksheep/requirements-uvicorn.txt b/frameworks/Python/blacksheep/requirements-uvicorn.txt index 5db1075c36d..92e7e8b8455 100644 --- a/frameworks/Python/blacksheep/requirements-uvicorn.txt +++ b/frameworks/Python/blacksheep/requirements-uvicorn.txt @@ -3,3 +3,4 @@ uvicorn==0.34.1 httptools==0.6.4 gunicorn==23.0.0 msgspec==0.19.0 +asyncpg==0.30.0 \ No newline at end of file diff --git a/frameworks/Python/blacksheep/requirements.txt b/frameworks/Python/blacksheep/requirements.txt index 1f15278011e..12af6e9f9b2 100644 --- a/frameworks/Python/blacksheep/requirements.txt +++ b/frameworks/Python/blacksheep/requirements.txt @@ -1,3 +1,3 @@ -asyncpg==0.30.0 Jinja2==3.1.6 blacksheep==2.3.1 +psycopg \ No newline at end of file From 49396a07abae64f15c6396ebc8dbc91eadcebab7 Mon Sep 17 00:00:00 2001 From: nazo Date: Wed, 21 May 2025 11:59:49 +0800 Subject: [PATCH 20/31] fix --- ...-asgi-pypy.dockerfile => blacksheep-pypy-socketify.dockerfile} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename frameworks/Python/blacksheep/{blacksheep-socketify-asgi-pypy.dockerfile => blacksheep-pypy-socketify.dockerfile} (100%) diff --git a/frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile b/frameworks/Python/blacksheep/blacksheep-pypy-socketify.dockerfile similarity index 100% rename from frameworks/Python/blacksheep/blacksheep-socketify-asgi-pypy.dockerfile rename to frameworks/Python/blacksheep/blacksheep-pypy-socketify.dockerfile From 868d923e61cb39b17456f262d05986d3c235560d Mon Sep 17 00:00:00 2001 From: nazo Date: Wed, 21 May 2025 12:07:00 +0800 Subject: [PATCH 21/31] fix: app.py -> app-socketify.py --- .../Python/blacksheep/blacksheep-pypy-socketify.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Python/blacksheep/blacksheep-pypy-socketify.dockerfile b/frameworks/Python/blacksheep/blacksheep-pypy-socketify.dockerfile index a3c9ed60643..b730747562f 100644 --- a/frameworks/Python/blacksheep/blacksheep-pypy-socketify.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep-pypy-socketify.dockerfile @@ -11,5 +11,5 @@ RUN pip3 install -r /blacksheep/requirements-pypy.txt EXPOSE 8080 -CMD python ./app.py +CMD python ./app-socketify.py From 47cfb5b97ee9a06c227395fe2e41ad9c1c08613b Mon Sep 17 00:00:00 2001 From: nazo Date: Wed, 21 May 2025 12:25:49 +0800 Subject: [PATCH 22/31] fix: _is_travis --- frameworks/Python/blacksheep/app-socketify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index ad2c9a0f87d..f8fca56ad1b 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -125,7 +125,7 @@ async def plaintext_test(request): if platform.python_implementation() == 'PyPy': from socketify import ASGI workers = int(multiprocessing.cpu_count()) - if _is_travis: + if os.environ.get('TRAVIS') == 'true': workers = 2 def run_app(): From 1f4a0495ca5410137d3b2158e4e9cb16fc2cbf8f Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 22 May 2025 10:47:33 +0800 Subject: [PATCH 23/31] fix error --- frameworks/Python/blacksheep/app-socketify.py | 20 +++++++++---------- .../Python/blacksheep/benchmark_config.json | 4 ++-- .../Python/blacksheep/blacksheep.dockerfile | 2 +- frameworks/Python/blacksheep/config.toml | 4 ++-- .../blacksheep/requirements-uvicorn.txt | 3 ++- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index f8fca56ad1b..180f2ef2990 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -53,12 +53,11 @@ def load_fortunes_template(): app.on_start += setup_db app.on_stop += shutdown_db -def get_num_queries(request): +def get_num_queries(queries): try: - value = request.query.get('queries') - if value is None: + if queries is None: return 1 - query_count = int(value[0]) + query_count = int(queries) except (KeyError, IndexError, ValueError): return 1 return min(max(query_count, 1), 500) @@ -78,9 +77,9 @@ async def single_db_query_test(request): number = await cursor.fetchone() return bs.json({'id': row_id, 'randomNumber': number[1]}) -@bs.get('/queries') -async def multiple_db_queries_test(request): - num_queries = get_num_queries(request) +@bs.get('/queries/{queries}') +async def multiple_db_queries_test(request, queries): + num_queries = get_num_queries(queries) row_ids = random.sample(range(1, 10000), num_queries) worlds = [] async with db_pool.connection() as db_conn: @@ -102,9 +101,9 @@ async def fortunes_test(request): data = fortune_template.render(fortunes=fortunes) return bs.html(data) -@bs.get('/updates') -async def db_updates_test(request): - num_queries = get_num_queries(request) +@bs.get('/updates/{queries}') +async def db_updates_test(request,queries): + num_queries = get_num_queries(queries) updates = list(zip( random.sample(range(1, 10000), num_queries), sorted(random.sample(range(1, 10000), num_queries)) @@ -123,6 +122,7 @@ async def plaintext_test(request): return bs.Response(200, content=bs.Content(b"text/plain", b'Hello, World!')) if platform.python_implementation() == 'PyPy': + import logging from socketify import ASGI workers = int(multiprocessing.cpu_count()) if os.environ.get('TRAVIS') == 'true': diff --git a/frameworks/Python/blacksheep/benchmark_config.json b/frameworks/Python/blacksheep/benchmark_config.json index db211d08363..5d86e4b12ae 100644 --- a/frameworks/Python/blacksheep/benchmark_config.json +++ b/frameworks/Python/blacksheep/benchmark_config.json @@ -52,8 +52,8 @@ "fortune_url": "/fortunes", "plaintext_url": "/plaintext", "db_url": "/db", - "query_url": "/queries?queries=", - "update_url": "/updates?queries=", + "query_url": "/queries/", + "update_url": "/updates/", "port": 8080, "approach": "Realistic", "classification": "Micro", diff --git a/frameworks/Python/blacksheep/blacksheep.dockerfile b/frameworks/Python/blacksheep/blacksheep.dockerfile index 2495d578252..9948bd51766 100644 --- a/frameworks/Python/blacksheep/blacksheep.dockerfile +++ b/frameworks/Python/blacksheep/blacksheep.dockerfile @@ -13,4 +13,4 @@ RUN pip3 install -r /blacksheep/requirements-uvicorn.txt -q ENV GUNICORN=1 EXPOSE 8080 -CMD gunicorn app:app -k uvicorn.workers.UvicornWorker -c blacksheep_conf.py +CMD gunicorn app:app -k uvicorn_worker.UvicornWorker -c blacksheep_conf.py diff --git a/frameworks/Python/blacksheep/config.toml b/frameworks/Python/blacksheep/config.toml index ea30e9cad43..5835a6a6b98 100644 --- a/frameworks/Python/blacksheep/config.toml +++ b/frameworks/Python/blacksheep/config.toml @@ -41,8 +41,8 @@ versus = "None" urls.plaintext = "/plaintext" urls.json = "/json" urls.db = "/db" -urls.query = "/queries?queries=" -urls.update = "/updates?queries=" +urls.query = "/queries/" +urls.update = "/updates/" urls.fortune = "/fortunes" approach = "Realistic" classification = "Platform" diff --git a/frameworks/Python/blacksheep/requirements-uvicorn.txt b/frameworks/Python/blacksheep/requirements-uvicorn.txt index 92e7e8b8455..4784617228a 100644 --- a/frameworks/Python/blacksheep/requirements-uvicorn.txt +++ b/frameworks/Python/blacksheep/requirements-uvicorn.txt @@ -1,5 +1,6 @@ uvloop==0.21.0 -uvicorn==0.34.1 +uvicorn==0.34.2 +uvicorn-worker httptools==0.6.4 gunicorn==23.0.0 msgspec==0.19.0 From 1f4c9a3a647252bfb1978712392dfed39a9dc9e6 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 22 May 2025 11:20:12 +0800 Subject: [PATCH 24/31] try fix --- frameworks/Python/blacksheep/app-socketify.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index 180f2ef2990..feed49e9335 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -78,6 +78,7 @@ async def single_db_query_test(request): return bs.json({'id': row_id, 'randomNumber': number[1]}) @bs.get('/queries/{queries}') +@bs.get('/queries/') async def multiple_db_queries_test(request, queries): num_queries = get_num_queries(queries) row_ids = random.sample(range(1, 10000), num_queries) @@ -102,12 +103,13 @@ async def fortunes_test(request): return bs.html(data) @bs.get('/updates/{queries}') +@bs.get('/updates/') async def db_updates_test(request,queries): num_queries = get_num_queries(queries) - updates = list(zip( + updates = sorted(zip( random.sample(range(1, 10000), num_queries), - sorted(random.sample(range(1, 10000), num_queries)) - )) + random.sample(range(1, 10000), num_queries) + ), key=lambda x: x[1]) worlds = [{"id": row_id, "randomNumber": number} for row_id, number in updates] async with db_pool.connection() as db_conn: async with db_conn.cursor() as cursor: From 8fe9059f83b07a8dac875f9777d5c658a906b4c5 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 22 May 2025 11:30:12 +0800 Subject: [PATCH 25/31] try fix --- frameworks/Python/blacksheep/app-socketify.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index feed49e9335..8a610a5c913 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -116,7 +116,14 @@ async def db_updates_test(request,queries): for row_id, number in updates: await cursor.execute(READ_ROW_SQL, (row_id,)) await cursor.fetchone() - await cursor.executemany(WRITE_ROW_SQL, [(number, row_id) for row_id, number in updates]) + for _ in range(5): + try: + await cursor.executemany(WRITE_ROW_SQL, [(number, row_id) for row_id, number in updates]) + break + except psycopg.errors.DeadlockDetected: + await db_conn.rollback() + continue + # await cursor.executemany(WRITE_ROW_SQL, [(number, row_id) for row_id, number in updates]) return bs.json(worlds) @bs.get('/plaintext') From cfd22f59f350b8c6233a87bebd0da275ee6643c4 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 22 May 2025 11:42:12 +0800 Subject: [PATCH 26/31] try fix --- frameworks/Python/blacksheep/app-socketify.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index 8a610a5c913..93bd14902bb 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -12,14 +12,12 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = %s' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=%s WHERE id=%s' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 1900 +MAX_CONNECTIONS = 200 CORE_COUNT = multiprocessing.cpu_count() PROCESSES = CORE_COUNT -MAX_POOL_SIZE = max(1, int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) -MIN_POOL_SIZE = max(1, int(os.getenv('MIN_POOL_SIZE', MAX_POOL_SIZE // 2))) WORKER_PROCESSES = CORE_COUNT -MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) +MAX_POOL_SIZE = CORE_COUNT * 2 MIN_POOL_SIZE = max(1, MAX_POOL_SIZE // 2) db_pool = None @@ -34,6 +32,8 @@ async def setup_db(app): min_size=MIN_POOL_SIZE, max_size=MAX_POOL_SIZE, open=False, + timeout=5.0, + max_lifetime=1800, ) await db_pool.open() @@ -112,13 +112,14 @@ async def db_updates_test(request,queries): ), key=lambda x: x[1]) worlds = [{"id": row_id, "randomNumber": number} for row_id, number in updates] async with db_pool.connection() as db_conn: + await db_conn.execute("SET TRANSACTION ISOLATION LEVEL READ COMMITTED") async with db_conn.cursor() as cursor: for row_id, number in updates: await cursor.execute(READ_ROW_SQL, (row_id,)) await cursor.fetchone() for _ in range(5): try: - await cursor.executemany(WRITE_ROW_SQL, [(number, row_id) for row_id, number in updates]) + await cursor.executemany(WRITE_ROW_SQL + " NOWAIT", [(number, row_id) for row_id, number in updates]) break except psycopg.errors.DeadlockDetected: await db_conn.rollback() From 2bf2f9708614d4cf33a169d6eb7f685f31a18aea Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 22 May 2025 12:01:14 +0800 Subject: [PATCH 27/31] try fix --- frameworks/Python/blacksheep/app-socketify.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index 93bd14902bb..81746c60139 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -111,21 +111,26 @@ async def db_updates_test(request,queries): random.sample(range(1, 10000), num_queries) ), key=lambda x: x[1]) worlds = [{"id": row_id, "randomNumber": number} for row_id, number in updates] - async with db_pool.connection() as db_conn: - await db_conn.execute("SET TRANSACTION ISOLATION LEVEL READ COMMITTED") - async with db_conn.cursor() as cursor: - for row_id, number in updates: - await cursor.execute(READ_ROW_SQL, (row_id,)) - await cursor.fetchone() - for _ in range(5): - try: - await cursor.executemany(WRITE_ROW_SQL + " NOWAIT", [(number, row_id) for row_id, number in updates]) - break - except psycopg.errors.DeadlockDetected: - await db_conn.rollback() - continue - # await cursor.executemany(WRITE_ROW_SQL, [(number, row_id) for row_id, number in updates]) - return bs.json(worlds) + for _ in range(5): + async with db_pool.connection() as db_conn: + try: + await db_conn.execute("SET TRANSACTION ISOLATION LEVEL READ COMMITTED") + async with db_conn.cursor() as cursor: + for row_id, number in updates: + await cursor.execute(READ_ROW_SQL, (row_id,)) + await cursor.fetchone() + for _ in range(5): + try: + await cursor.executemany(WRITE_ROW_SQL, [(number, row_id) for row_id, number in updates]) + return bs.json(worlds) + except psycopg.errors.DeadlockDetected: + await db_conn.rollback() + continue + # await cursor.executemany(WRITE_ROW_SQL, [(number, row_id) for row_id, number in updates]) + except (psycopg.errors.OperationalError, psycopg.errors.PipelineAborted): + await db_conn.rollback() + continue + raise Exception("connect error") @bs.get('/plaintext') async def plaintext_test(request): From b6b87e8b864e41e0e0ff214f623bad45ef5546a1 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 22 May 2025 12:20:14 +0800 Subject: [PATCH 28/31] try optimization --- frameworks/Python/blacksheep/app-socketify.py | 6 ++---- frameworks/Python/blacksheep/app.py | 6 +----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index 81746c60139..f3a0e7ea923 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -12,12 +12,10 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = %s' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=%s WHERE id=%s' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 200 +MAX_CONNECTIONS = 1000 CORE_COUNT = multiprocessing.cpu_count() -PROCESSES = CORE_COUNT -WORKER_PROCESSES = CORE_COUNT -MAX_POOL_SIZE = CORE_COUNT * 2 +MAX_POOL_SIZE = (MAX_CONNECTIONS // CORE_COUNT) MIN_POOL_SIZE = max(1, MAX_POOL_SIZE // 2) db_pool = None diff --git a/frameworks/Python/blacksheep/app.py b/frameworks/Python/blacksheep/app.py index a5090311c6a..80dd48919e9 100644 --- a/frameworks/Python/blacksheep/app.py +++ b/frameworks/Python/blacksheep/app.py @@ -19,13 +19,9 @@ ADDITIONAL_ROW = [0, "Additional fortune added at request time."] MAX_CONNECTIONS = 1900 CORE_COUNT = multiprocessing.cpu_count() -PROCESSES = CORE_COUNT -MAX_POOL_SIZE = max(1,int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // PROCESSES))) +MAX_POOL_SIZE = max(1,int(os.getenv('MAX_POOL_SIZE', MAX_CONNECTIONS // CORE_COUNT))) MIN_POOL_SIZE = max(1,int(os.getenv('MIN_POOL_SIZE', MAX_POOL_SIZE // 2))) -WORKER_PROCESSES = CORE_COUNT -MAX_POOL_SIZE = max(1, MAX_CONNECTIONS // WORKER_PROCESSES) -MIN_POOL_SIZE = max(1, MAX_POOL_SIZE // 2) db_pool = None async def setup_db(app): From c291210b464fa0c60a30009f91b08082cf76219a Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 22 May 2025 12:29:39 +0800 Subject: [PATCH 29/31] try fix --- frameworks/Python/blacksheep/app-socketify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index f3a0e7ea923..5636ee21772 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -12,7 +12,7 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = %s' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=%s WHERE id=%s' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 1000 +MAX_CONNECTIONS = 200 CORE_COUNT = multiprocessing.cpu_count() MAX_POOL_SIZE = (MAX_CONNECTIONS // CORE_COUNT) From f10963b539f36ca1ede812e7e8dc49ae91c9d892 Mon Sep 17 00:00:00 2001 From: nazo Date: Thu, 22 May 2025 13:23:54 +0800 Subject: [PATCH 30/31] rollback --- frameworks/Python/blacksheep/app-socketify.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index 5636ee21772..ac273608c11 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -12,10 +12,9 @@ READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = %s' WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=%s WHERE id=%s' ADDITIONAL_ROW = [0, "Additional fortune added at request time."] -MAX_CONNECTIONS = 200 CORE_COUNT = multiprocessing.cpu_count() -MAX_POOL_SIZE = (MAX_CONNECTIONS // CORE_COUNT) +MAX_POOL_SIZE = CORE_COUNT * 2 MIN_POOL_SIZE = max(1, MAX_POOL_SIZE // 2) db_pool = None From b2e4ed3013e204f4132605ff7786271f7e6201d1 Mon Sep 17 00:00:00 2001 From: nazo Date: Tue, 27 May 2025 16:40:15 +0800 Subject: [PATCH 31/31] update socketify and fix query --- frameworks/Python/blacksheep/app-socketify.py | 21 +++++++++---------- .../Python/blacksheep/benchmark_config.json | 4 ++-- frameworks/Python/blacksheep/config.toml | 4 ++-- .../Python/blacksheep/requirements-pypy.txt | 2 +- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/frameworks/Python/blacksheep/app-socketify.py b/frameworks/Python/blacksheep/app-socketify.py index ac273608c11..47ed2663804 100644 --- a/frameworks/Python/blacksheep/app-socketify.py +++ b/frameworks/Python/blacksheep/app-socketify.py @@ -50,11 +50,12 @@ def load_fortunes_template(): app.on_start += setup_db app.on_stop += shutdown_db -def get_num_queries(queries): +def get_num_queries(request): try: - if queries is None: + value = request.query.get('queries') + if value is None: return 1 - query_count = int(queries) + query_count = int(value[0]) except (KeyError, IndexError, ValueError): return 1 return min(max(query_count, 1), 500) @@ -74,10 +75,9 @@ async def single_db_query_test(request): number = await cursor.fetchone() return bs.json({'id': row_id, 'randomNumber': number[1]}) -@bs.get('/queries/{queries}') -@bs.get('/queries/') -async def multiple_db_queries_test(request, queries): - num_queries = get_num_queries(queries) +@bs.get('/queries') +async def multiple_db_queries_test(request): + num_queries = get_num_queries(request) row_ids = random.sample(range(1, 10000), num_queries) worlds = [] async with db_pool.connection() as db_conn: @@ -99,10 +99,9 @@ async def fortunes_test(request): data = fortune_template.render(fortunes=fortunes) return bs.html(data) -@bs.get('/updates/{queries}') -@bs.get('/updates/') -async def db_updates_test(request,queries): - num_queries = get_num_queries(queries) +@bs.get('/updates') +async def db_updates_test(request): + num_queries = get_num_queries(request) updates = sorted(zip( random.sample(range(1, 10000), num_queries), random.sample(range(1, 10000), num_queries) diff --git a/frameworks/Python/blacksheep/benchmark_config.json b/frameworks/Python/blacksheep/benchmark_config.json index 5d86e4b12ae..db211d08363 100644 --- a/frameworks/Python/blacksheep/benchmark_config.json +++ b/frameworks/Python/blacksheep/benchmark_config.json @@ -52,8 +52,8 @@ "fortune_url": "/fortunes", "plaintext_url": "/plaintext", "db_url": "/db", - "query_url": "/queries/", - "update_url": "/updates/", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", "port": 8080, "approach": "Realistic", "classification": "Micro", diff --git a/frameworks/Python/blacksheep/config.toml b/frameworks/Python/blacksheep/config.toml index 5835a6a6b98..ea30e9cad43 100644 --- a/frameworks/Python/blacksheep/config.toml +++ b/frameworks/Python/blacksheep/config.toml @@ -41,8 +41,8 @@ versus = "None" urls.plaintext = "/plaintext" urls.json = "/json" urls.db = "/db" -urls.query = "/queries/" -urls.update = "/updates/" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" urls.fortune = "/fortunes" approach = "Realistic" classification = "Platform" diff --git a/frameworks/Python/blacksheep/requirements-pypy.txt b/frameworks/Python/blacksheep/requirements-pypy.txt index b232eb36b2a..f2c3d4fe9b6 100644 --- a/frameworks/Python/blacksheep/requirements-pypy.txt +++ b/frameworks/Python/blacksheep/requirements-pypy.txt @@ -1,3 +1,3 @@ psycopg[pool] -socketify +git+https://github.com/cirospaciari/socketify.py.git@main#egg=socketify h11 \ No newline at end of file