Pular para o conteúdo
use-cases / cron-per-customer / hero
SaaS multi-tenant / Agendamento por cliente

Um crontab separado para cada cliente, automaticamente

Seu SaaS deixa cada cliente agendar a própria geração de relatórios. O design ingênuo é um scheduler compartilhado, IDs de cliente no payload do job, dedos cruzados pra ninguém deixar ninguém sem comer. O design Hoody dá a cada tenant seu próprio contêiner e seu próprio serviço hoody-cron.

Leia a documentação

Três estados do ciclo de vida, uma API HTTP. PROVISION adiciona entradas, cron ticks as executam, DELETE suspende. O cron de cada tenant vive em seu próprio contêiner — sem fila compartilhada, sem risco de vizinho barulhento.

use-cases / cron-per-customer / mechanism

Uma chamada PUT provisiona o crontab inteiro de um tenant

Cada contêiner de cliente expõe a API HTTP do hoody-cron. Para definir o agendamento, você substitui o crontab com um único PUT. Sem fila compartilhada, sem faixa de prioridade, sem config de scheduler para reimplantar.

POST a managed entry — creates a cron job with UUID, enabled state, and human-readable schedule_human fieldPOST /provision
request
# POST managed entry for acme-corp tenant
POST acme-cron.hoody.com/users/root/entries
Content-Type: application/json

{
  "schedule": "0 9 * * *",
  "command": "/usr/local/bin/digest.sh",
  "comment": "daily digest",
  "enabled": true
}
response
HTTP/1.1 201 Created
Content-Type: application/json

{
  "id": "7d3f2a1b-8c4e-4f9a-b2d5",
  "schedule": "0 9 * * *",
  "schedule_human": "At 09:00",
  "enabled": true,
  "user": "root"
}
201 Created. Entry ID is returned for future PATCH or DELETE. schedule_human confirms expression was parsed correctly.

O endpoint substitui o crontab inteiro de forma atômica. Entradas removidas são reportadas de volta para que seu control plane possa auditar deriva. O cron de cada tenant vive dentro do próprio contêiner; nada sobre o agendamento da acme-corp é visível para a globex-saas, e um job descontrolado em um contêiner não pode matar de fome o scheduler em outro.

use-cases / cron-per-customer / powers

O que crontabs vinculados ao contêiner destravam

Três propriedades caem do design de graça, porque o isolamento é o substrato, não uma feature que você escreveu.

Fleet billing breakdownper-tenant cost = server divided by tenants
Your tenants
acme-corpSM
globex-saasMD
initech-incLG
+ 57 more
Flat-rate server / mo$29One bare-metal node. 60 containers. Bill stays flat.
÷tenants
Per-tenant cost<$0.49Drops as you add more tenants

Noisy-neighbor incidents disappear

When initech-inc's scrape.js hangs, acme-corp's 9am digest still fires. Different crontabs, different process trees, different filesystems.

Schedule changes propagate instantly

POST a new entry and the tenant's hoody-cron service picks it up immediately. No central scheduler to reload, no broadcast to send.

Per-tenant logs, one container

When globex-saas asks why their 6pm rollup ran twice, you read one container's log — not a shared scheduler grep across nine machines.

use-cases / cron-per-customer / compare

Scheduler compartilhado vs. crontab vinculado ao contêiner

Três eixos onde o design antigo cobra imposto do seu time e o design Hoody simplesmente não cobra.

EixoScheduler compartilhadoVinculado ao contêiner
Isolamento
tenant_id no payload do jobUma linha ruim, fila de todo tenant trava
/etc/crontab separado por contêinerTravamentos são locais. Sempre.
Provisionamento
INSERT INTO scheduled_jobsAcoplamento de migração, lock de schema
PUT /users/root/crontabUma chamada HTTP, troca atômica
Auditoria
grep tenant_id=42 logs/*9 máquinas, 1 arquivo de log cada
GET ctr_8a3f1c/cron/logUm contêiner, um log, uma verdade

A coluna antiga é o que todo time escreve da primeira vez que entrega agendamento multi-tenant. A coluna nova é o que você entrega quando a plataforma dá a cada tenant seu próprio contêiner por padrão.

use-cases / cron-per-customer / capacity

Capacidade nos limites

O que uma única máquina bare-metal Hoody faz quando cada cliente ganha o próprio crontab.

  1. Tenants por máquina60

    Sessenta contêineres de cliente em um nó bare-metal, cada um com seu próprio serviço hoody-cron rodando. Sem scheduler compartilhado pra fazer gargalo.

  2. Propagação de agendamento<1s

    Da requisição PUT ao primeiro tique do novo agendamento, observado numa frota de 60 contêineres num nó típico de 64 cores.

  3. Filas entre tenants0

    Literalmente não existe fila compartilhada, faixa de prioridade ou thread de scheduler pela qual dois tenants competem. Isolamento é o substrato.

Os números de capacidade são valores típicos observados num nó bare-metal de 64 cores / 256GB rodando densidade padrão de contêineres Hoody. A capacidade real depende dos orçamentos de CPU e memória por tenant e do trabalho que cada cron job faz. O zero em filas entre tenants é estrutural, não um benchmark.

use-cases / cron-per-customer / punchline

O cron de um cliente não pode matar de fome o de outro porque eles não estão no mesmo crontab.

Antes / scheduler compartilhadoDepois / vinculado ao contêiner
Compartilhadoscheduled_jobs WHERE tenant_id = 42Uma linha numa tabela que todo mundo lê
Por tenantPUT acme-cron.hoody.com/users/root/crontabUma chamada HTTP, um contêiner, um crontab
Leia a API de cron
use-cases / cron-per-customer / replaces

O que isso substitui

As arquiteturas que times constroem para compartilhar um crontab entre tenants. A Hoody coloca cada tenant no próprio crontab — sem roteador, sem fila de fairness, sem vizinho barulhento.

  • Crontabs multi-tenant compartilhadosUm regex ruim mata de fome 400 clientes
  • Isolamento de tenant feito em casaUm scheduler com tenant_id em toda linha
  • pg_cron do PostgresAcoplado ao banco; um upgrade e todo mundo quebra
  • Quartz scheduler com filtrosUma JVM e uma fila com sharding por região
  • Filas de tenant no SidekiqDoze filas, doze arquivos de config
  • Kubernetes CronJobs por tenantUm namespace, um RBAC, um YAML, um pager
use-cases / cron-per-customer / cta

Pare de escrever tenant_id em todo lugar. Dê a cada cliente seu próprio contêiner e deixe o cron fazer o que o cron sempre fez, em isolamento.

Leia a documentação
use-cases / cron-per-customer / related

Leia os outros