Hallo zusammen,
Ich betreibe eine Multi-Tenant-Automatisierungsplattform auf Basis von n8n und bemerke zunehmend Probleme, bei denen einige Tenants mit hohem Volumen die Erfahrung aller anderen beeinträchtigen.
Aktuelle Architektur:
Gemeinsame Queue
↓
Gemeinsamer Worker Pool
↓
Gemeinsame Infrastruktur
Probleme, die ich beobachte:
• Ein einzelner Tenant kann die Queue überlasten
• Langläufige Workflows verzögern kleinere Jobs
• API-intensive Tenants verbrauchen überproportional viel Worker-Kapazität
• Erhöhte Latenz für Tenants mit geringem Volumen
Meine Ziele sind:
• Gerechte Ressourcenverteilung zwischen Tenants
• Probleme durch „noisy neighbors
Hi @Keira_Becky Der häufigste Ansatz ist die Kombination von Rate Limits, Queue Isolation und Worker Isolation.
Shared Queue + Per-Tenant Limits + Dedicated Workers for Heavy Tenants
Dies verhindert, dass ein einzelner Tenant alle verfügbaren Ressourcen verbraucht.
Häufige Strategien
Wenden Sie Pro-Tenant-Rate-Limits und Kontingente an
Verwenden Sie Priority Queues für verschiedene Service Tiers
Leiten Sie High-Volume-Tenants zu dedizierten Queues oder Workern weiter
Behalten Sie kleinere Tenants auf gemeinsamer Infrastruktur
Für große Tenants verwenden viele Teams: Enterprise Tenant → Dedicated Queue → Dedicated Worker Pool
Achten Sie darauf zu vermeiden
Eine globale Queue ohne Limits
Behandlung aller Tenants gleich, unabhängig von der Nutzung
Unbegrenzte Wiederholungen oder lange laufende Jobs auf gemeinsamen Workern zulassen
Die sauberste Lösung für das Problem der lauten Nachbarn (noisy-neighbor) ist, die gemeinsam genutzte Ressource nicht mehr zu teilen. Statt einer gemeinsamen Warteschlange und eines Worker-Pools mit aufgesetzten Limits: Betreib eine n8n-Instanz pro Mandant – eigene Warteschlange, eigene Worker, eigene Datenbank und Verschlüsselungsschlüssel, eigener Container. Das Problem der lauten Nachbarn hört auf, etwas zu sein, das du feinabstimmst, und wird strukturell unmöglich, weil es keine gemeinsame Warteschlange gibt, die ein Mandant überfluten könnte, und keinen gemeinsamen Pool, den ein API-intensiver Mandant aushungern könnte.
Die Route „gemeinsame Warteschlange plus Limits
Willkommen @Keira_Becky!
Der direkteste Hebel im n8n-Queue-Modus ist EXECUTIONS_CONCURRENCY_ACTIVE - stelle ihn pro Worker-Container ein, nicht global. Für das Noisy-Neighbor-Problem funktioniert in der Praxis folgendes Muster: Betreibe 2-3 Worker-Tiers mit unterschiedlichen Concurrency-Limits, dann leite schwere Mandanten über einen separaten Redis-Queue-Key-Präfix zu ihrer eigenen Worker-Gruppe (setze dies per QUEUE_BULL_PREFIX pro Worker). Mandanten mit hohem Volumen erhalten einen dedizierten Worker mit niedrigerem EXECUTIONS_CONCURRENCY_ACTIVE (z. B. 5), die gemeinsame Tier bekommt mehr Worker, aber jeder ist auf 10 begrenzt. Langläufige Workflows sind die schwierigeren Fälle - nutze EXECUTIONS_TIMEOUT auf Instanzebene und EXECUTIONS_TIMEOUT_MAX pro Workflow, um zu verhindern, dass eine einzelne Ausführung einen Worker auf unbestimmte Zeit blockiert. Wenn zwei oder drei Mandanten konstant ihre eigene Queue maximal auslasten, ist ein Upgrade auf eine dedizierte n8n-Instanz normalerweise sauberer, als zu versuchen, sie in einem gemeinsamen Cluster zu isolieren.
Das gleiche Problem hat uns früher in diesem Jahr auf einer Multi-Tenant n8n+Postgres-Plattform getroffen — LLM-Aufrufe, die Redis-TTLs überdauern, ein langsamer Tenant blockiert alle anderen.
Das, was die Blutung tatsächlich gestoppt hat: BullMQ Queue-Präfixe pro Tenant-Tier (QUEUE_BULL_PREFIX) + separate EXECUTIONS_CONCURRENCY_ACTIVE-Limits pro Worker-Pool. Redis-Lock ist der schnelle Pfad. Dann Postgres UNIQUE (tenant_id, message_id) mit ON CONFLICT DO NOTHING als haltbare Idempotenz-Gate — das ist die Wahrheitsschicht, wenn Gemini-Aufrufe die Redis-TTL überdauern.
Es lohnt sich, bei dir zu überprüfen: Ist deine Redis-Lock-TTL länger als dein P99-LLM-Aufruf? Das ist normalerweise, wo die Duplikat-Ausführungsblutung beginnt.
Auch — wenn p95 LLM unter 2s bleibt und pool_wait flach ist, ist die Kontention irgendwo anders. Normalerweise QUEUE_RECOVERY_INTERVAL-Churn im Scheduler.