Cron jobs
Las tareas programadas están en Google Cloud Scheduler (project api-cards-prod, region us-central1). Cada job hace POST a un endpoint del Cloud Run autenticado con X-Internal-Cron-Secret.
Panel y comandos
Sección titulada «Panel y comandos»| Panel | https://console.cloud.google.com/cloudscheduler?project=api-cards-prod |
| Listar | gcloud scheduler jobs list --project=api-cards-prod --location=us-central1 |
| Pausar uno | gcloud scheduler jobs pause <name> --project=... --location=... |
| Forzar ejecución manual | gcloud scheduler jobs run <name> --project=... --location=... |
Jobs activos
Sección titulada «Jobs activos»mongo-backup-hourly
Sección titulada «mongo-backup-hourly»| Frecuencia | 0 * * * * (cada hora) |
| Qué hace | Dispara backup de Mongo Atlas via API |
| Endpoint | externo (Atlas API), no es de tcgcards-api |
| RPO efectivo | ~1 hora |
| Runbook | Ver runbook restore |
orders-tick
Sección titulada «orders-tick»| Frecuencia | 0 * * * * (cada hora) |
| Endpoint | POST /api/v1/internal/cron/orders-tick |
| Código | src/cron/orderTickService.ts |
| Qué hace | 4 sub-tareas (ver abajo) |
| Idempotente | Sí — re-procesar es seguro |
Sub-tareas que ejecuta:
| Sub-tarea | Trigger | Acción |
|---|---|---|
processPending | Orden creada hace >72h, vendedor no aceptó | Auto-cancela + restaura stock + mail |
processAccepted | Orden aceptada hace >168h (7 días), no se envió | Auto-cancela + restaura stock + mail |
processShipped | Orden enviada hace >336h (14 días), buyer no confirmó | Auto-completa (libera pago al vendedor) + mail |
processExpiredSuspensions | Usuario con suspendedUntil vencido | Desuspende automáticamente |
Warning de 24h antes: para cada deadline, manda email “se cancelará/recibirá en ~24h” cuando faltan menos de 24h.
orders-awaiting-payment-cleanup
Sección titulada «orders-awaiting-payment-cleanup»| Frecuencia | */5 * * * * (cada 5 min) |
| Endpoint | POST /api/v1/internal/cron/orders-awaiting-payment-cleanup |
| Código | src/cron/awaitingPaymentCleanupService.ts |
| Qué hace | Cancela órdenes en awaiting_payment cuyo paymentTimeoutAt ya pasó (30 min por defecto). Restaura stock. Manda mail de timeout. |
| Idempotente | Sí |
| Índice clave | {status:1, paymentTimeoutAt:1} en orders (agregado 2026-05-20) |
También maneja orphan pending orders: órdenes que quedaron en estado pending sin paymentId por crash del proceso entre create() y updateById(). Las cancela si llevan >60 min.
wallet-reconcile-daily
Sección titulada «wallet-reconcile-daily»| Frecuencia | 0 3 * * * (diario 3am UTC) |
| Endpoint | POST /api/v1/internal/cron/wallet-reconcile |
| Código | src/cron/walletReconcile.ts → ReconcileService.reconcileAll() |
| Qué hace | Reconcilia user.walletBalance (denormalizado) contra la suma del ledger wallet_entries. Loguea ERROR si encuentra divergencias. |
| Idempotente | Sí (read-only) |
Si encuentra una divergencia, escribe log estructurado [wallet-reconcile] DIVERGENCES DETECTED que debe disparar alerta en Sentry. La acción de corrección es manual (endpoint /admin/reconcile o intervención de un dev).
refunds-reconcile-daily
Sección titulada «refunds-reconcile-daily»| Frecuencia | 0 4 * * * (diario 4am UTC) |
| Endpoint | POST /api/v1/internal/cron/refunds-reconcile |
| Código | src/payments/services/RefundReconcileService.ts |
| Qué hace | Detecta órdenes canceladas con paymentId cuyo refund no se procesó correctamente. Log ERROR si encuentra inconsistencias. |
| Idempotente | Sí (read-only) |
Cómo cambiar la frecuencia de un cron
Sección titulada «Cómo cambiar la frecuencia de un cron»gcloud scheduler jobs update http <job-name> \ --schedule="*/10 * * * *" \ --project=api-cards-prod \ --location=us-central1Cambia el schedule (cron expression). Las request a la API no cambian; solo el timing.
Cómo agregar un cron nuevo
Sección titulada «Cómo agregar un cron nuevo»gcloud scheduler jobs create http nuevo-cron \ --project=api-cards-prod \ --location=us-central1 \ --schedule="0 * * * *" \ --time-zone="UTC" \ --uri="https://tcgcards-api-1033181994095.us-central1.run.app/api/v1/internal/cron/nuevo-cron" \ --http-method=POST \ --oidc-service-account-email=mongo-backup-scheduler@api-cards-prod.iam.gserviceaccount.com \ --headers="X-Internal-Cron-Secret=<valor del secret>"(Antes de crear, hay que implementar el endpoint en el código + redeploy.)
Cuellos de botella conocidos
Sección titulada «Cuellos de botella conocidos»Detallado en la conversación de 2026-05-20:
| Job | Estado actual | Punto de quiebre |
|---|---|---|
orders-awaiting-payment-cleanup | OK con índice nuevo | ~1.000 órdenes vencidas simultáneas |
orders-tick | OK pero usa findActiveByStatus() que trae todo a memoria | ~5.000 órdenes activas (RAM + tiempo) |
wallet-reconcile-daily | OK | ~50.000 usuarios con wallet (depende de Cloud Run timeout) |
refunds-reconcile-daily | OK | sin riesgo previsible |
mongo-backup-hourly | OK | sin riesgo (no depende de nuestro código) |
Si lo notamos crecer a esos volúmenes hay fixes preparados — ver decisiones técnicas para detalles.