FASE 7b: DB Performance — Pooling, Stock Summary, N+1 fix

Cambios implementados:

1. Connection pooling (tenant_db.py):
   - psycopg2.pool.ThreadedConnectionPool para master y tenants
   - Wrapper _PooledConnection que devuelve al pool en .close()
   - Cero cambios en blueprints (backward compatible)

2. Tabla inventory_stock_summary + triggers (v3.2):
   - O(1) stock lookup en vez de SUM() sobre historial completo
   - Trigger AFTER INSERT en inventory_operations recalcula stock
   - Poblada inicialmente en ambos tenants
   - Refactor en 6 archivos de servicios para usar la nueva tabla

3. Fix N+1 en process_sale (pos_engine.py):
   - Precarga retail_price en bulk query FOR UPDATE
   - Elimina SELECT individual por item en loop

4. Índices críticos:
   - idx_parts_name_part + pattern_ops (master)
   - idx_inv_ops_inventory_branch_created (tenants)
   - idx_wi_part_stock_positive (master, ya existía desde Fase 1)

Tests: 73/73 pasando (compat + fase3 + fase5 + fase6)
Migración: v3.2_db_performance.sql
This commit is contained in:
2026-04-27 07:34:31 +00:00
parent 175dda6212
commit e3c85fd647
9 changed files with 173 additions and 40 deletions

View File

@@ -224,7 +224,7 @@ def process_sale(conn, sale_data):
# Lock inventory rows to prevent race conditions on concurrent sales
cur.execute("""
SELECT id, part_number, name, cost, price_1, price_2, price_3,
tax_rate, branch_id
tax_rate, branch_id, retail_price
FROM inventory
WHERE id = ANY(%s) AND is_active = true
ORDER BY id
@@ -327,10 +327,9 @@ def process_sale(conn, sale_data):
# Create sale items (batch insert) and deduct inventory
sale_items_data = []
for item in totals['items']:
# Fetch retail_price for savings calculation
cur.execute("SELECT retail_price FROM inventory WHERE id = %s", (item['inventory_id'],))
rp_row = cur.fetchone()
retail_price = rp_row[0] if rp_row else None
# retail_price from preloaded bulk query (index 9)
inv = inv_rows.get(item['inventory_id'])
retail_price = inv[9] if inv else None
sale_items_data.append((
sale_id, item['inventory_id'], item['part_number'], item['name'],