Queue-SQL: un paquete que divide UPDATE y DELETE masivos de Laravel en jobs de cola paralelos
Queue-SQL, un paquete Laravel de Kamran Atayev, trocea sentencias UPDATE y DELETE enormes en rangos de claves primarias procesados como jobs paralelos mediante el batching nativo de Laravel.
Ofrece una macro queue(), monitorización CLI, vista previa dry-run y etiquetado en Horizon; requiere PHP 8.1+ y Laravel 10 a 13.
Ejecutar un único UPDATE o DELETE sobre millones de filas puede mantener bloqueos en la base de datos durante minutos, dejar atrás a las réplicas o agotar el tiempo de espera durante un despliegue. Queue-SQL, un paquete Laravel de Kamran Atayev, lo resuelve dividiendo las escrituras grandes en jobs paralelos: calcula las claves primarias mínima y máxima de la consulta, corta ese rango de ID en segmentos acotados y despacha un Illuminate\Bus\Batch que procesa cada segmento de forma independiente.
El paquete registra una macro queue() tanto en el Query Builder como en Eloquent, exponiendo update, delete, insert y upsert. Las restricciones se compilan en fragmentos SQL y bindings, de modo que closures anidados, whereHas, whereExists y subconsultas llegan a los workers sin problemas de serialización. El tamaño de los jobs se define con chunk (filas por job) o maxJobs (objetivo total sobre el rango de claves); usar ambos lanza una InvalidArgumentException. dispatch() devuelve un Illuminate\Bus\Batch nativo con then, catch, finally y allowFailures.
El uso consiste en encadenar queue() antes del método de escritura y terminar con dispatch() — nada se encola hasta entonces. Ejemplo: Order::where('status', 'complete')->queue(chunk: 25000, tries: 2, onQueue: 'maintenance', throttle: 4)->update(['status' => 'completed'])->dispatch(). La opción throttle limita los jobs por segundo, protegiendo las conexiones y las réplicas en backfills grandes. Los borrados usan la misma sintaxis; las inserciones masivas omiten la planificación por rangos y reparten el array de registros entre los jobs.
Como cada job de update o delete apunta a un segmento fijo de claves, los reintentos son idempotentes. Los jobs de inserción reintentados no lo son y pueden crear duplicados: la documentación recomienda un índice único o upsert() cuando importa la seguridad ante reintentos. Frente al trait Prunable y al comando model:prune de Laravel, que borran secuencialmente en un solo proceso, Queue-SQL trabaja en paralelo, sigue el progreso como batch y además admite updates arbitrarios e inserciones masivas.
Sustituir dispatch() por dryRun() muestra una vista previa del plan de batching (operación, tabla, número de jobs, rangos, filas estimadas) sin encolar nada. El troceado por rangos de claves se basa en los valores mín/máx de la clave primaria, no en recuentos exactos: en tablas con grandes huecos de ID algunos jobs procesan menos filas, aunque el número total de jobs permanece fijo.
La CLI monitoriza los batches directamente sobre la tabla de batches del framework: queue-sql:status lista o inspecciona batches, queue-sql:cancel cancela uno, --watch se actualiza cada dos segundos en terminales interactivos (imprime una vez en entornos no interactivos) y --json formatea la salida. El estado muestra jobs totales, pendientes y fallidos, porcentajes de progreso y estado del batch. En Horizon los jobs se etiquetan automáticamente con queue-sql, el tipo de operación y las tablas destino. Los valores por defecto (tamaño de chunk, reintentos, backoff, throttling, retrasos y nombres de cola) se configuran en config/queue-sql.php.
Requiere PHP 8.1+ y Laravel 10 a 13, con soporte para SQLite, MySQL y PostgreSQL. Se instala con composer require kamranata/queue-sql, seguido de queue:batches-table y migrate, ya que depende del batching nativo. Dos advertencias: el troceado exige una clave primaria entera autoincremental (de lo contrario todo corre en un único job), y los límites de claves se fijan al llamar a dispatch(), por lo que las filas insertadas después con ID superiores quedan fuera. El repositorio incluye un script de benchmark (benchmarks/lock_duration.php); en una prueba de borrado de 20.000 filas en SQLite, un DELETE sin batching mantuvo el bloqueo 3,6 veces más tiempo que el bloqueo más largo registrado por Queue-SQL.
Tribuna de lectores
Aún no hay aportaciones — abre el debate.
Inicia la conversación
Sin cuenta ni contraseña — introduce tu correo y te enviamos un enlace de acceso de un solo uso. ¿Primera vez? Todo se configura automáticamente.
Tu valoración se aplicará automáticamente al iniciar sesión.
Revisa tu bandeja de entrada
Hemos enviado un enlace de acceso a …. Ábrelo en este dispositivo — esta pestaña te conectará automáticamente.
Esperando tu clic …
·