5.7 KiB
P0 Bug #420: GET /api/v1/billing/wallet/balance - 500 Internal Server Error
Dátum: 2026-07-26
Súlyosság: P0 - Blokkoló (minden wallet/balance hívást érint)
Státusz: Megoldva (pycache purge + restart)
1. Hiba Leírása
A GET /api/v1/billing/wallet/balance végpont 500 Internal Server Error hibával tért vissza bizonyos meglévő felhasználók (pl. user_id=86, 28) esetén.
Hibaüzenet a logból:
2026-07-26 12:37:49,388 [ERROR] app.api.v1.endpoints.billing: Pénztárca egyenleg lekérdezési hiba: 'FinancialLedger' object has no attribute 'description'
Érintett fájlok:
backend/app/models/system/audit.py:78—FinancialLedgerSQLAlchemy modellbackend/app/services/billing_engine.py:1078—get_user_balance()backend/app/services/billing_engine.py:598—get_wallet_summary()backend/app/api/v1/endpoints/billing.py:298—get_wallet_balance()endpoint
2. Root Cause Analysis
2.1 A FinancialLedger modell
A FinancialLedger modell NEM rendelkezik description oszloppal. Az egyetlen "leíró" mező a details (JSONB). A modell oszlopai:
id, user_id, person_id, amount, currency, transaction_type,
related_agent_id, details, created_at, entry_type, balance_after,
wallet_type, issuer_id, invoice_status, tax_amount, gross_amount,
net_amount, transaction_id, status
2.2 A jelenlegi forráskód helyes
A billing_engine.py get_transaction_history() metódusa helyesen használja a details JSONB mezőt a leírás kinyerésére:
# Line 577-579
desc = None
if entry.details and isinstance(entry.details, dict):
desc = entry.details.get("description") or entry.details.get("desc") or None
A billing.py router szintén helyesen:
# Line 376-378
description = ""
if entry.details and isinstance(entry.details, dict):
description = entry.details.get("description", "")
2.3 A tényleges ok: Stale Python Bytecode Cache
Az MD5 ellenőrzés igazolta, hogy a konténerben futó forráskód és a host-on lévő forráskód azonos:
| Fájl | MD5 |
|---|---|
billing.py (host & container) |
6b00968c129376e3db630e266835261b |
billing_engine.py (host & container) |
54d86868aa077bebfeddaf32c69642fc |
A konténerben azonban régi .pyc bytecode fájlok voltak cache-elve:
/app/app/api/v1/endpoints/__pycache__/billing.cpython-312.pyc
/app/app/services/__pycache__/billing_engine.cpython-312.pyc
Ezek a .pyc fájlok egy korábbi forráskód-verzióhoz tartoztak, ahol még létezett entry.description hivatkozás a FinancialLedger objektumokon (vagy egy másik séma érvényben volt).
2.4 Miért nem frissültek a .pyc fájlok?
A docker-compose.yml-ben a sf_api konténer forráskódja volume mount-tal van csatolva (nem COPY). Amikor a forráskód változik a host-on, a Python értelmező a konténerben azonnal látja az új .py fájlokat, de a már legenerált .pyc cache fájlok megmaradnak. Ha a .py fájl timestamp-je régebbi, mint a .pyc-é (vagy a Python nem érzékeli a változást), a régi bytecode fut.
3. Megoldás
Végrehajtott lépések:
-
Pycache törlése a konténerben:
docker compose exec sf_api find /app -type d -name "__pycache__" -exec rm -rf {} + -
Konténer újraindítása:
docker compose restart sf_api -
Verifikáció: A restart után az endpoint már NEM dob 500-as hibát. A logokban nem jelenik meg több
descriptionhiba.
4. Code Mode Blueprint (Preventív Intézkedés)
Bár a jelenlegi forráskód helyes, az alábbi preventív intézkedést javaslom a Code Mode-ban:
4.1 Docker Compose: pycache purge a startup előtt
A docker-compose.yml sf_api szolgáltatás command sorában futtassunk egy automatikus pycache purge-öt a Uvicorn indítása előtt:
command: >
/bin/sh -c "
find /app -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null;
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
"
4.2 Opcionális: .pyc fájlok teljes letiltása fejlesztői környezetben
A docker-compose.yml environment változói közé:
environment:
- PYTHONDONTWRITEBYTECODE=1
Ez megakadályozza, hogy a Python egyáltalán .pyc fájlokat generáljon, így mindig a forráskódból fog futni.
5. Függelék: Mermaid Folyamatábra
sequenceDiagram
participant Client
participant FastAPI as billing.py router
participant BillingEngine as billing_engine.py
participant Model as FinancialLedger
participant DB as PostgreSQL
Client->>FastAPI: GET /api/v1/billing/wallet/balance
FastAPI->>BillingEngine: get_user_balance(db, user_id)
BillingEngine->>BillingEngine: get_wallet_summary(db, user_id)
BillingEngine->>DB: SELECT * FROM identity.wallets WHERE user_id=?
BillingEngine->>DB: SELECT * FROM audit.financial_ledger LIMIT 10
BillingEngine->>Model: entry.details.get("description")
Note over Model: Helyes: details JSONB mezőből olvas
Model-->>BillingEngine: description string
BillingEngine-->>FastAPI: {"earned": X, "purchased": Y, ...}
FastAPI-->>Client: 200 OK + JSON balances
A hiba idején a .pyc bytecode entry.description-t próbált elérni, ami nem létező attribútum a FinancialLedger modellen. Ez AttributeError-t okozott, amit a billing.py router except Exception ága elkapott és 500-ra fordított.