Skip to content

Base de Datos

◄ Anterior: Arquitectura | Índice | Siguiente: API Endpoints ►


Tabla de Contenidos


Tabla: background_jobs

Nivel Multi-tenant: LEVEL_SUCURSAL (CRÍTICO)

Justificación del nivel:

  • Cada sucursal debe ver solo sus jobs
  • Jobs pueden tener datos sensibles en payload
  • Aislamiento completo por schema

Propagación de schema: X-Schema header → ConnectionManager → search_path PostgreSQL


CampoTipoConstraintsDescripción
idSERIALPRIMARY KEYID único autogenerado
typeVARCHAR(100)NOT NULLTipo de job (ej: 'batch_invoicing')
statusVARCHAR(20)NOT NULL DEFAULT 'pending'Estado: pending | running | completed | failed
payloadJSONBNOT NULLDatos necesarios para ejecutar el job
resultJSONBNULLResultado del job (null hasta que complete)
errorTEXTNULLMensaje de error (null si no falló)
user_idINTEGERNOT NULLID del usuario que creó el job
schemaVARCHAR(50)NOT NULLSchema PostgreSQL donde ejecutar (CRÍTICO)
created_atTIMESTAMPNOT NULL DEFAULT NOW()Fecha de creación
started_atTIMESTAMPNULLFecha de inicio de ejecución
completed_atTIMESTAMPNULLFecha de finalización

Índices

NombreColumnasTipoPropósito
idx_background_jobs_user_iduser_idBTREEJobs por usuario
idx_background_jobs_statusstatusBTREEJobs pendientes/running
idx_background_jobs_created_atcreated_atBTREEBúsquedas por fecha
idx_background_jobs_typetypeBTREEJobs por tipo
idx_background_jobs_user_status(user_id, status)BTREEConteo de pendientes (DOS protection)

Constraints Adicionales

sql
-- Check: status solo puede tener valores válidos
CHECK (status IN ('pending', 'running', 'completed', 'failed'))

-- Check: schema tiene formato válido
CHECK (schema ~ '^suc[0-9]{4}(caja[0-9]{3})?$')

-- Check: si completed, debe tener completed_at
CHECK (
  (status = 'completed' AND completed_at IS NOT NULL) OR
  (status != 'completed')
)

-- Check: si failed, debe tener error
CHECK (
  (status = 'failed' AND error IS NOT NULL) OR
  (status != 'failed')
)

Tabla: notifications

Nivel Multi-tenant: LEVEL_SUCURSAL (CRÍTICO)

Justificación del nivel:

  • Cada sucursal debe ver solo sus notificaciones
  • Aislamiento de datos sensibles en mensajes

CampoTipoConstraintsDescripción
idSERIALPRIMARY KEYID único autogenerado
user_idINTEGERNOT NULLUsuario destinatario
typeVARCHAR(20)NOT NULLTipo: success | error | info
titleVARCHAR(200)NOT NULLTítulo breve
messageTEXTNOT NULLMensaje detallado
metadataJSONBNULLDatos adicionales (ej: job_id, result)
is_readBOOLEANNOT NULL DEFAULT FALSESi fue leída
created_atTIMESTAMPNOT NULL DEFAULT NOW()Fecha de creación
read_atTIMESTAMPNULLFecha de lectura

Índices

NombreColumnasTipoPropósito
idx_notifications_user_iduser_idBTREENotificaciones por usuario
idx_notifications_is_readis_readBTREENotificaciones no leídas
idx_notifications_created_atcreated_atBTREEOrdenamiento por fecha
idx_notifications_user_read(user_id, is_read)BTREEBadge de notificaciones (COUNT unread)

Constraints Adicionales

sql
-- Check: type solo puede tener valores válidos
CHECK (type IN ('success', 'error', 'info'))

-- Check: si is_read=true, debe tener read_at
CHECK (
  (is_read = TRUE AND read_at IS NOT NULL) OR
  (is_read = FALSE AND read_at IS NULL)
)

Función PostgreSQL: notify_job_update() (Fase 2)

Propósito: Enviar notificación NOTIFY cuando un job se actualiza

Trigger: AFTER UPDATE en background_jobs

SQL:

sql
CREATE OR REPLACE FUNCTION notify_job_update()
RETURNS TRIGGER AS $$
BEGIN
  PERFORM pg_notify(
    'job_updates_' || NEW.id,
    json_build_object(
      'id', NEW.id,
      'status', NEW.status,
      'result', NEW.result,
      'error', NEW.error
    )::text
  );
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER background_jobs_update_trigger
AFTER UPDATE ON background_jobs
FOR EACH ROW
WHEN (OLD.status IS DISTINCT FROM NEW.status)
EXECUTE FUNCTION notify_job_update();

Uso en PHP (Fase 2):

php
// JobStreamController escucha canal
$pdo->exec("LISTEN job_updates_{$jobId}");

while (true) {
    $notification = pg_get_notify($pdo);
    if ($notification) {
        echo "data: {$notification['payload']}\n\n";
        ob_flush(); flush();
    }
}

◄ Anterior: Arquitectura | Índice | Siguiente: API Endpoints ►