Email copiado — support@tuurt.com
Cargando experiencia
gcp · 04 de septiembre de 2026 · 6 min

Cloud Tasks vs Pub/Sub en GCP: cuándo encolar una tarea y cuándo publicar un evento

Cloud Tasks entrega a un destino único con control de tasa; Pub/Sub publica un evento para cualquier número de suscriptores. Repasamos dónde cada uno encaja mal en un backend real.

Por Equipo Tuurt

Cloud Tasks vs Pub/Sub en GCP: cuándo encolar una tarea y cuándo publicar un evento

Cuando un backend en GCP necesita desacoplar trabajo asíncrono, casi siempre aparece la misma pregunta en el diseño: ¿esto va a Cloud Tasks o a Pub/Sub? Las dos son colas administradas, las dos desacoplan al productor del consumidor, y en la documentación de ambas aparece la palabra "asíncrono" con la misma frecuencia. Esa similitud superficial es la razón por la que terminamos, más de una vez, usando la herramienta equivocada para un problema que a primera vista parecía intercambiable.

La diferencia no está en qué tan rápido entregan un mensaje ni en cuánto cuestan. Está en el modelo de entrega: Cloud Tasks entrega a un destino específico, con control fino sobre la tasa y el reintento por tarea individual. Pub/Sub publica un evento que cualquier número de suscriptores puede recibir, cada uno de forma independiente. Confundir esos dos modelos no rompe el sistema de inmediato, pero sí produce, con el tiempo, un diseño que pelea contra la herramienta en vez de apoyarse en ella.

Cloud Tasks: un consumidor, control de tasa, reintento por tarea

Cloud Tasks nació para un caso concreto: código que sabe exactamente a quién le tiene que entregar un trabajo, y necesita controlar cuánto de ese trabajo se procesa por segundo. La API se ve así:

from google.cloud import tasks_v2
import json

client = tasks_v2.CloudTasksClient()
parent = client.queue_path("mi-proyecto", "us-central1", "envio-emails")

task = {
    "http_request": {
        "http_method": tasks_v2.HttpMethod.POST,
        "url": "https://api.miapp.com/internal/enviar-email",
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"usuario_id": "u_123", "plantilla": "bienvenida"}).encode(),
    }
}

client.create_task(parent=parent, task=task)

Cada tarea tiene un único destino HTTP. La cola tiene una configuración de tasa (max_dispatches_per_second) y de concurrencia (max_concurrent_dispatches) que aplica a esa cola entera, y cada tarea individual tiene su propia política de reintento configurable, con backoff y un número máximo de intentos. Si la tarea falla, Cloud Tasks la reintenta contra el mismo destino hasta agotar la política; nadie más se entera de que esa tarea existió.

Esto encaja bien cuando el trabajo tiene un solo responsable de ejecutarlo y ese responsable necesita protegerse de un pico de carga. El ejemplo típico es una API externa con límite de tasa: enviar correos a través de un proveedor que cobra o bloquea por ráfagas, sincronizar contra un sistema legado que se cae si recibe más de cierto número de peticiones por segundo, o programar una tarea para que se ejecute en un momento futuro específico (Cloud Tasks soporta schedule_time de forma nativa, algo que Pub/Sub no ofrece sin construir un mecanismo de retraso aparte).

Pub/Sub: un evento, cero o varios suscriptores

Pub/Sub resuelve un problema distinto: cuando lo que pasó le importa a más de un consumidor, y ninguno de ellos debería tener que saber de la existencia del otro.

from google.cloud import pubsub_v1
import json

publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path("mi-proyecto", "pedido-creado")

data = json.dumps({"pedido_id": "p_456", "monto": 4500}).encode("utf-8")
publisher.publish(topic_path, data)

Quien publica en pedido-creado no sabe, ni le importa, cuántos suscriptores hay escuchando ese tópico. Puede haber uno que actualiza el inventario, otro que dispara una notificación push, y un tercero que alimenta un pipeline de analítica, todos suscritos al mismo tópico de forma independiente. Agregar un cuarto suscriptor no requiere tocar el código de quien publica, y ese es exactamente el punto: el productor declara un hecho del dominio, no una instrucción dirigida a un destinatario.

La contrapartida es que Pub/Sub no da control fino sobre la tasa de entrega hacia un consumidor específico. Cada suscripción tiene su propio flujo de entrega y su propio backlog, y limitar cuánta carga recibe un suscriptor puntual es responsabilidad de ese suscriptor -por ejemplo, ajustando cuántos mensajes extrae por segundo-, no algo que se configure de forma centralizada como el max_dispatches_per_second de una cola de Cloud Tasks.

Dónde cada uno encaja mal

Usar Pub/Sub para lo que en realidad es una tarea dirigida a un único consumidor con necesidad de control de tasa produce un problema recurrente: para simular ese control hay que construir, por fuera de Pub/Sub, la lógica de limitación que Cloud Tasks ya trae integrada. Vimos esto en un flujo de sincronización contra un ERP de un cliente que devolvía error 429 si recibía más de un puñado de peticiones por segundo. La primera versión usaba un tópico de Pub/Sub con una sola suscripción push hacia el endpoint de sincronización, y para no saturar el ERP tuvimos que agregar un semáforo con Redis en el propio endpoint que rechazaba peticiones por encima de cierta tasa y dejaba que Pub/Sub reintentara los mensajes rechazados. Funcionaba, pero era reconstruir a mano, con una pieza más en el sistema, algo que la cola de Cloud Tasks resuelve con dos parámetros de configuración.

El caso inverso también aparece: usar Cloud Tasks para un evento que en realidad varios sistemas necesitan conocer. Si el flujo original era "cuando se crea un pedido, encolar una tarea que actualiza el inventario" con Cloud Tasks, y más adelante aparece la necesidad de también notificar al usuario y también alimentar el pipeline de analítica cuando se crea un pedido, la opción más directa -encolar tres tareas distintas desde el mismo punto del código que crea el pedido- ata a quien crea el pedido con el conocimiento de los tres consumidores. Cada consumidor nuevo obliga a tocar el código que publica, exactamente lo que Pub/Sub evita por diseño.

Lo que no funcionó: usar Pub/Sub como sustituto de tareas programadas

Un intento que revertimos fue usar Pub/Sub para tareas con retraso, aprovechando que Cloud Scheduler puede publicar en un tópico y que ya teníamos infraestructura de Pub/Sub en el proyecto. La idea era publicar un mensaje con un campo ejecutar_despues_de y que el suscriptor revisara ese campo antes de procesar, reencolando el mensaje si todavía no tocaba. En la práctica esto generaba un ciclo de mensajes que se reenviaban a sí mismos una y otra vez hasta cumplir el retraso, inflando el conteo de entregas de la suscripción y complicando cualquier intento de ver, en las métricas, cuántos mensajes "reales" estaban pendientes versus cuántos eran reintentos de espera. Cloud Tasks resuelve esto de forma nativa con schedule_time en la creación de la tarea, sin ningún mecanismo adicional. Volvimos a Cloud Tasks para ese caso puntual y dejamos Pub/Sub únicamente para los eventos de dominio que de verdad tienen más de un interesado.

El criterio que aplicamos

La pregunta que nos hacemos antes de elegir no es cuál de las dos herramientas es más moderna o cuál usa el resto del equipo en otros servicios. Es: ¿este trabajo tiene un único destino conocido de antemano y necesito controlar cuánto de él se procesa por segundo? Eso es Cloud Tasks. ¿Esto es un hecho del dominio que puede interesarle a consumidores que ni siquiera existen todavía, y quiero que agregar uno nuevo no toque el código que lo publica? Eso es Pub/Sub. Cuando un flujo empieza pareciendo del primer tipo y con el tiempo gana un segundo o tercer interesado, ese es el momento de migrarlo a Pub/Sub -no antes, por si acaso.

gcp cloud-tasks pubsub backend
← Volver al blog