Multi-tenancy en CreaRack Pro
Multi-tenancy en CreaRack Pro
Qué es
CreaRack Pro es una aplicación SaaS multi-tenant donde Organization actúa como tenant raíz. Cada User pertenece a exactamente una Organization mediante FK, y todos los recursos principales (Rack, Blueprint, Stencil, SystemLog) llevan un campo organization que los ancla al tenant propietario. El aislamiento se aplica en tres capas: filtros ORM en cada servicio, middleware de Row-Level Security (RLS) que instruye a PostgreSQL con una variable de transacción, y middleware de module-gating que controla qué funcionalidades puede usar el tenant según su plan.
Por qué existe
Sin aislamiento explícito, un bug en cualquier query podría devolver datos de otro cliente. La regulación de datos y los SLAs de cada plan exigen que un tenant nunca acceda ni afecte los recursos de otro. Adicionalmente, los tenants en planes inferiores no deben poder saturar la cola de tareas en background (“noisy neighbor problem”).
Componentes
Organization(core/models.py): tenant raíz. Campos relevantes:is_active,deleted_at(soft-delete),plan,extra_modules.get_active_modules()combina módulos core siempre-activos y los extra del tenant.User(core/models.py): extiendeAbstractUser, FK aOrganization, camporole(admin,operator,readonly).ModuleGatingMiddleware(core/middleware/module_gating.py): resuelve la org del usuario, verificais_activeyis_deleted, bloquea con HTTP 403 cualquier path cuyo módulo requerido no esté en el set activo. Superusuarios bypassean.TenantRLSMiddleware(core/middleware/tenant_rls.py): activa transacción atómica por request y ejecutaSET LOCAL app.current_org_id = <id>en PostgreSQL. Las políticas RLS leen esta variable para restringir filas como red de seguridad. Compatible con pgBouncer transaction pooling porqueSET LOCALse limpia al commit.tenant_throttle(core/tenant_fairness.py): decorador para tareas Huey. Usa Django cache para contar tareas concurrentes por org, limitando según plan (starter: 3,pro: 10,custom: 25). Backoff de 5s si supera.- Services de apps (
racks/services/racks.py,blueprints/services/blueprints.py): cada método recibeorganizationexplícito y aplica.filter(organization=...)en querysets.
Flujos
Request HTTP normal:
AuthenticationMiddlewareidentificaUsery cargaOrganizationvia FK.ModuleGatingMiddlewareverifica que el path esté cubierto por el plan. Si no, 403.TenantRLSMiddlewareabre transacción atómica y ejecutaSET LOCAL app.current_org_id = <org.id>. Políticas RLS activas.- La view llama al servicio (
RackService.list_racks(organization=request.user.organization)) que filtra en ORM. Si el filtro falla, RLS bloquea filas de otros tenants a nivel BD.
Soft-delete de Organization:
- Admin setea
org.deleted_at = now()y guarda. - Siguiente request:
ModuleGatingMiddlewaredetectaorg.is_deleted == Truey retorna 403 conerrors/account_paused.html(o JSON si/api/*). - Datos permanecen aislados y recuperables limpiando
deleted_at.
Tarea background con fairness:
- View encola tarea Huey decorada con
@tenant_throttle()(sin límite fijo, usa el del plan). - Decorador consulta cache
tenant_tasks:<org_id>. Si supera el límite del plan, espera 5s y reintenta. - Incrementa contador antes de ejecutar, decrementa en
finally.
Related
entity--core--model--organization— tenant raíz.entity--core--model--user— FK a Organization.concept--saas--module-gating— lógica de planes y acceso granular.
Véase también
- [[entity—core—model—organization]]
- [[entity—core—model—user]]
- [[concept—saas—module-gating]]
- [[decision—20260101—django-ninja-vs-drf]] — Framework REST que expone las APIs multi-tenant