From 05561dccd73a6c6d5b4b9f55e79864ea47d120d5 Mon Sep 17 00:00:00 2001 From: Rafael Date: Tue, 5 Mar 2024 22:10:26 +0000 Subject: [PATCH] Feat/replace orm for psycopg (#1) * feat: trim project and fix admin * feat: replace django orm for query raw * feat: use psycopg connection pool --- .gitignore | 2 +- .vscode/launch.json | 20 ++++++++ README.md | 18 +++++-- conf/nginx.conf | 2 +- conf/postgresql.conf | 6 +++ docker-compose.yml | 16 ++++-- docs/gatling.png | Bin 0 -> 81242 bytes gunicorn.conf.py | 11 ++-- poetry.lock | 14 ++--- pyproject.toml | 2 +- rinha.http | 5 +- rinha/apps/core/views.py | 107 +++++++++++++++++++++++---------------- rinha/settings.py | 34 +++---------- rinha/urls.py | 2 +- rinha/wsgi.py | 7 ++- 15 files changed, 146 insertions(+), 100 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 conf/postgresql.conf create mode 100644 docs/gatling.png diff --git a/.gitignore b/.gitignore index 6769e21..b0b6f3a 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file +.idea/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..118892b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Django", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/manage.py", + "args": [ + "runserver", + "5047" + ], + "django": true, + "autoStartBrowser": false + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index b5cdf43..3f4a3c3 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,18 @@ Versão Python, Django e PostgreSQL da [rinha de backend 2ª edição - 2024/Q1] - PostgreSQL - nginx -## Running the project +## Executando o projeto -```sh -python manage.py runserver 5047 -``` \ No newline at end of file +```bash +docker compose up api1 api2 nginx +``` + +## Resultados dos testes locais com gatling + +![Métricas do gatling](docs/gatling.png) + + +## Outras versões da rinha + +- [aspnet com EF Core e PostgreSQL](https://github.com/rafaelpadovezi/rinha-2) +- [aspnet com MongoDB](https://github.com/rafaelpadovezi/rinha-2-mongo) \ No newline at end of file diff --git a/conf/nginx.conf b/conf/nginx.conf index c12cf44..f0f65e3 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -1,5 +1,5 @@ events { - worker_connections 1024; + worker_connections 2048; } http { diff --git a/conf/postgresql.conf b/conf/postgresql.conf new file mode 100644 index 0000000..960eb62 --- /dev/null +++ b/conf/postgresql.conf @@ -0,0 +1,6 @@ +listen_addresses = '*' + +max_connections = 200 +client_encoding = utf8 +default_transaction_isolation = 'read committed' +timezone = UTC \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f94faae..5bbe159 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,8 @@ services: - "5432:5432" volumes: - ./conf/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d + - ./conf/postgresql.conf:/etc/postgresql/postgresql.conf + command: postgres -c config_file=/etc/postgresql/postgresql.conf environment: POSTGRES_PASSWORD: postgres POSTGRES_DB: rinha @@ -15,9 +17,8 @@ services: deploy: resources: limits: - cpus: "0.4" - memory: "200MB" - command: postgres -c max_connections=500 + cpus: "0.40" + memory: "250MB" healthcheck: test: [ @@ -46,16 +47,21 @@ services: condition: service_healthy environment: - DB_HOST=db + - USE_STATIC_FILE_HANDLER_FROM_WSGI=TRUE + - DATABASE_URL=host=db dbname=rinha user=postgres password=postgres + - GUNICORN_WORKERS=2 + - DB_POOL_MAX_SIZE=45 + - DEBUG=False deploy: resources: limits: cpus: "0.47" - memory: "165MB" + memory: "140MB" api2: <<: *api1 hostname: api2 ports: - - "8080:8080" + - "8081:8080" nginx: image: nginx:alpine ports: diff --git a/docs/gatling.png b/docs/gatling.png new file mode 100644 index 0000000000000000000000000000000000000000..7a84fe109beb64f8976bdd5c8ea6684194577ced GIT binary patch literal 81242 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

5Mb{Y)2TCgw@34@lw`b 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"] -readme = "README.md" +package-mode = false [tool.poetry.dependencies] python = "^3.12" diff --git a/rinha.http b/rinha.http index 45866d1..361d992 100644 --- a/rinha.http +++ b/rinha.http @@ -16,7 +16,6 @@ POST http://localhost:5047/clientes/1/transacoes Content-Type: application/json { - "valor": 1.2, - "tipo" : "c", - "descricao" : "descricao" + "valor": 1, + "tipo" : "c" } \ No newline at end of file diff --git a/rinha/apps/core/views.py b/rinha/apps/core/views.py index f8e9334..4de28ad 100644 --- a/rinha/apps/core/views.py +++ b/rinha/apps/core/views.py @@ -1,44 +1,53 @@ from rest_framework.request import Request from rest_framework.response import Response -from rest_framework.views import APIView from rest_framework.decorators import api_view -from rinha.apps.core.models import Cliente -from rinha.apps.core.models import Transacao -from django.db import transaction -from rinha.apps.core.serializers import TransacaoSerializer import logging +from psycopg.rows import dict_row +from psycopg_pool import ConnectionPool +from rinha.settings import DATABASE_URL +from rinha.settings import DB_POOL_MAX_SIZE logger = logging.getLogger(__name__) - -class TransacaoView(APIView): - def post(self, request): - pass - +pool = ConnectionPool(DATABASE_URL, open=True, min_size=5, max_size=DB_POOL_MAX_SIZE) @api_view(["GET"]) def get_extrato(request: Request, id: int) -> Response: - try: - cliente = Cliente.objects.get(pk=id) - except Cliente.DoesNotExist: - return Response({"message": "Cliente não encontrado"}, status=404) - transacoes = Transacao.objects.order_by("-id").filter(cliente__id=id)[:10] ultimas_transacoes = [] - for transacao in transacoes: - ultimas_transacoes.append( - { - "valor": transacao.valor, - "tipo": transacao.tipo, - "descricao": transacao.descricao, - "realizada_em": transacao.realizada_em, - } - ) + cliente = None + with pool.connection() as conn: + with conn.cursor(row_factory=dict_row) as cur: + cur.execute(""" + SELECT c.limite, c.saldo, t.* + FROM core_cliente c + LEFT JOIN ( + SELECT * + FROM core_transacao + ORDER BY id DESC + LIMIT 10 + ) t ON c.id = t.cliente_id + WHERE c.id = %s;""", [id]) + for record in cur: + if cliente is None: + cliente = { + "limite": record["limite"], + "saldo": record["saldo"], + } + if record["valor"] is not None: + ultimas_transacoes.append({ + "valor": record["valor"], + "tipo": record["tipo"], + "descricao": record["descricao"], + "realizado_em": record["realizada_em"], + }) + if cliente is None: + return Response({"message": "Cliente não encontrado"}, status=404) return Response( { "saldo": { - "limite": cliente.limite, - "total": cliente.saldo, + "limite": cliente["limite"], + "total": cliente["saldo"], }, "ultimas_transacoes": ultimas_transacoes, } @@ -47,26 +56,36 @@ def get_extrato(request: Request, id: int) -> Response: @api_view(["POST"]) def create_transacao(request: Request, id: int) -> Response: - serializer = TransacaoSerializer(data=request.data) - if not serializer.is_valid(): - return Response(serializer.errors, status=422) - - transacao = serializer.validated_data + transacao = request.data + tipo = transacao.get("tipo") + valor = transacao.get("valor") + descricao = transacao.get("descricao") + if tipo not in ["c", "d"]: + return Response({"message": "Tipo inválido"}, status=422) + if not isinstance(valor, int) or valor < 1: + return Response({"message": "Valor inválido"}, status=422) + if descricao is None or not (1 <= len(descricao) <= 10): + return Response({"message": "Descrição inválida"}, status=422) valor_transacao = ( transacao["valor"] if transacao["tipo"] == "c" else transacao["valor"] * -1 ) - with transaction.atomic(): - cliente = Cliente.objects.select_for_update().get(pk=id) - if cliente.saldo + valor_transacao < cliente.limite * -1: - return Response({"message": "Saldo insuficiente"}, status=422) + with pool.connection() as conn: + + with conn.cursor(row_factory=dict_row) as cur: + cur.execute("SELECT c.limite, c.saldo FROM core_cliente c WHERE c.id = %s FOR UPDATE", [id]) + result = cur.fetchone() + cliente = { + "limite": result["limite"], + "saldo": result["saldo"], + } + novo_saldo = cliente["saldo"] + valor_transacao + if novo_saldo < cliente["limite"] * -1: + return Response({"message": "Saldo insuficiente"}, status=422) + cur.execute("UPDATE core_cliente SET saldo = %s WHERE id = %s", (novo_saldo, id)) + cur.execute( + """INSERT INTO core_transacao (cliente_id, valor, tipo, descricao, realizada_em) + VALUES (%s, %s, %s, %s, 'now'); + """, (id, transacao["valor"], transacao["tipo"], transacao["descricao"])) - cliente.saldo += valor_transacao - cliente.save() - Transacao.objects.create( - cliente=cliente, - valor=transacao["valor"], - tipo=transacao["tipo"], - descricao=transacao["descricao"], - ) - return Response({"saldo": cliente.saldo, "limite": cliente.limite}) + return Response({"saldo": novo_saldo, "limite": cliente["limite"]}) diff --git a/rinha/settings.py b/rinha/settings.py index a3cb90d..30da886 100644 --- a/rinha/settings.py +++ b/rinha/settings.py @@ -1,15 +1,3 @@ -""" -Django settings for rinha project. - -Generated by 'django-admin startproject' using Django 5.0.2. - -For more information on this file, see -https://docs.djangoproject.com/en/5.0/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/5.0/ref/settings/ -""" - import os from pathlib import Path @@ -24,7 +12,7 @@ SECRET_KEY = "django-insecure-k!x96799qq7@lcd(9l2ra*4+p29bw2%024)nha$3n^*bjzdw@)" # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = os.getenv("DEBUG", True) ALLOWED_HOSTS = ["*"] @@ -38,17 +26,17 @@ }, "root": { "handlers": ["console"], - "level": "WARNING", + "level": os.getenv("DJANGO_LOG_LEVEL", "ERROR"), }, "loggers": { "django": { "handlers": ["console"], - "level": os.getenv("DJANGO_LOG_LEVEL", "WARNING"), + "level": os.getenv("DJANGO_LOG_LEVEL", "ERROR"), "propagate": False, }, "django.db.backends": { "handlers": ["console"], - "level": "WARNING", + "level": os.getenv("DJANGO_LOG_LEVEL", "ERROR"), "propagate": True, }, }, @@ -59,22 +47,12 @@ INSTALLED_APPS = [ "rinha.apps.core", - "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", ] MIDDLEWARE = [ - "django.middleware.security.SecurityMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", ] ROOT_URLCONF = "rinha.urls" @@ -153,3 +131,7 @@ # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +USE_STATIC_FILE_HANDLER_FROM_WSGI = os.getenv("USE_STATIC_FILE_HANDLER_FROM_WSGI", False) +DATABASE_URL = os.getenv("DATABASE_URL", "host=localhost dbname=rinha user=postgres password=postgres") +DB_POOL_MAX_SIZE = int(os.getenv("DB_POOL_MAX_SIZE", "15")) diff --git a/rinha/urls.py b/rinha/urls.py index 731ebbd..7546a18 100644 --- a/rinha/urls.py +++ b/rinha/urls.py @@ -3,7 +3,7 @@ from rinha.apps.core import views urlpatterns = [ - path("admin/", admin.site.urls), + # path("admin/", admin.site.urls), path("clientes//extrato", views.get_extrato), path("clientes//transacoes", views.create_transacao), ] diff --git a/rinha/wsgi.py b/rinha/wsgi.py index 5cdd769..fd8c280 100644 --- a/rinha/wsgi.py +++ b/rinha/wsgi.py @@ -10,7 +10,12 @@ import os from django.core.wsgi import get_wsgi_application +from rinha import settings +from django.contrib.staticfiles.handlers import StaticFilesHandler os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rinha.settings') -application = get_wsgi_application() +if settings.USE_STATIC_FILE_HANDLER_FROM_WSGI: + application = StaticFilesHandler(get_wsgi_application()) +else: + application = get_wsgi_application()