Olá pessoal,
Estou executando uma plataforma n8n multi-tenant onde cada tenant se conecta a APIs externas com diferentes limites de taxa.
O desafio é que alguns tenants têm limites muito baixos, enquanto outros têm quotas muito maiores.
Configuração atual: Webhook → Fila → Worker → API Externa
Problemas que estou vendo:
• Alguns tenants atingem limites de taxa muito mais rápido que outros
• Retentativas criam picos de tráfego
• Um tenant ocupado pode consumir uma grande parte da capacidade do worker
• Difícil de enforçar uso justo entre tenants
Estou considerando:
• Filas por tenant
• Limitação de taxa com token bucket / leaky bucket
• Contadores Redis
• Workers dedicados para tenants de alto volume
Para equipes rodando automações multi-tenant em escala:
• Como vocês estão enforçando limites de taxa por tenant?
• Vocês isolam filas por tenant ou usam uma fila compartilhada com throttling?
• Algum padrão recomendado para evitar que um tenant afete outros mantendo uma boa throughput?
Descreva o problema/erro/pergunta
Qual é a mensagem de erro (se houver)?
Por favor, compartilhe seu workflow
(Selecione os nós em sua tela e use os atalhos de teclado CMD+C/CTRL+C e CMD+V/CTRL+V para copiar e colar o workflow.)
Oi @Decoure_Ryan uma abordagem comum é usar rate limiting por inquilino em vez de um limite global único.
Webhook → Queue → Rate Limit Check → Worker → API
Rastreie requisições por inquilino usando Redis ou um banco de dados, e processe os jobs apenas quando esse inquilino estiver dentro do seu limite permitido.
Isso ajuda a Prevenir que um inquilino afete outros
Lidar com limites de API diferentes por inquilino
Reduzir picos causados por tentativas
Para inquilinos de alto volume: Filas separadas
Workers dedicados para que seu tráfego não impacte todos os outros.
Bem-vindo @Decoure_Ryan à nossa comunidade! Sou Jay e sou um criador verificado n8n.
A abordagem mais prática específica para n8n aqui é usar um nó Code antes de cada chamada de API externa para verificar e atualizar um contador por tenant. Se você estiver usando Redis, armazene a chave como rate_limit:{tenantId} com uma janela de expiração que corresponda ao período de reset da API — incremente a cada requisição e, se o tenant estiver acima do limite, roteie para um nó Wait em vez de prosseguir.
Para o problema de rajada de retry: em vez do retry integrado do n8n (que é disparado imediatamente e pode amplificar 429s), capture a saída de erro do nó HTTP Request e roteie para um nó Wait com um atraso fixo ou dinâmico extraído do cabeçalho de resposta Retry-After, depois volte para a requisição.
Se você estiver no modo de fila, os limites de concorrência por workflow fornecem isolamento natural por tenant quando os jobs de cada tenant são executados em um sub-workflow dedicado — defina concurrency: 1 por workflow de tenant e a fila gerencia o throttling para você sem nenhuma lógica de contador Redis.
Aqui estão algumas coisas que funcionaram em escala:
As filas por tenant são a escolha certa. Filas compartilhadas com throttling parecem simples, mas sempre acabam com problemas de noisy-neighbor. Uma fila isolada oferece isolamento real sem lógica de prioridade complexa.
Quanto à implementação de token bucket, Redis é sólido, apenas armazene uma chave por tenant usando refill baseado em TTL. Retentativas não disparam todas ao mesmo tempo após o reset da janela de rate limit, use exponential backoff com jitter.
Quanto à alocação de workers, considere um modelo em camadas: um pequeno pool de workers dedicados para os tenants de maior volume, workers compartilhados para todos os outros. Isso evitará over-provisioning enquanto ainda protege o throughput dos top tenants.
Algo que frequentemente é negligenciado é instrumentar a profundidade da fila por tenant e tempo de espera, não apenas os hits de rate limit. É aqui que você geralmente localizará o gargalo real antes de haver um problema de SLA.
Qual tipo de APIs externas você está acionando? Algumas têm allowances de burst que podem aliviar consideravelmente o problema de retentativas.
Token bucket por tenant em Redis é o instinto certo, e a escolha de design chave é impor o limite antes do job chegar ao worker, não dentro dele. Se o worker puxar um job e depois esperar por um rate limit, você amarrou a capacidade do worker fazendo nada, que é exatamente seu problema de “um tenant ocupado consome a capacidade de todos”.
Então o formato é: webhook para fila, depois um gate que verifica o bucket do tenant no Redis e só libera o job para um worker quando esse tenant tem orçamento, caso contrário re-coloca na fila com um atraso. Isso impede que o backlog de um tenant limitado bloqueie os outros.
Nas rajadas de retry: seus retries também precisam de backoff por tenant, não um global, caso contrário um tenant que já está no seu limite tenta novamente contra a mesma barreira e amplifica a rajada. Exponential backoff com jitter, contabilizado contra o mesmo bucket.
Para justiça real com centenas de tenants, filas separadas para seus poucos tenants de maior volume e uma fila compartilhada para a cauda longa é geralmente a divisão pragmática. Isolamento total de fila por tenant é mais limpo mas muito mais para operar. Mais um: coloque uma verificação na profundidade da fila por tenant para que quando uma começar a se acumular acima de um limite você descubra cedo, em vez de descobrir quando esse tenant reclama que seus jobs estão horas atrasados.