ADR 0013 — Memcached, Kafka, NATS e invalidación entre instancias
- Estado: aceptada
- Fecha: 2026-09-25
Decisiones
- Memcached no borra por prefijo, así que cada tenant tiene un número de versión dentro de sus llaves (
tenancy:<tenant>:v<n>:<llave>).flushTenantincrementa la versión: lo anterior queda inalcanzable y vence solo. La versión se recuerda ~1 s en el proceso: tras un flush en otra réplica, esta lo ve como mucho 1 s después. Las llaves largas (>200 bytes) o con espacios o caracteres de control se guardan como hash. - Kafka (
kafkajs): productor idempotente,acks: -1, y la clave del mensaje es eltenantId: los eventos de un tenant van a la misma partición y conservan su orden.consumeKafkaconfirma el offset solo si el handler termina bien. - NATS JetStream: el stream se crea si falta, y el id del evento va en
Nats-Msg-Id, así el servidor descarta los duplicados de un reenvío de la outbox.consumeNatsusa un consumidor durable conackexplícito,nakcon espera ymaxDeliver. - Puerto
InvalidationBusen el núcleo. Las cachés de búsqueda (tenants y dominios) avisan cada cambio; el núcleo junta los de una misma operación en un solo mensaje y aplica los que llegan de otras instancias, ignorando los propios. El tema no necesita mensaje: su clave de render incluyeupdatedAt. La implementación con Redis pub/sub (redisInvalidation()) está en@tenancy-node/cache-redis. - Un fallo del bus nunca falla la escritura: queda en el log (
cache.invalidation.publish) y en el registro de errores, y el TTL de la caché sigue poniendo el límite. La entrega es "a lo sumo una vez", como Redis pub/sub.
Consecuencias
tenancy.tenants.invalidate(id)olvida la entrada del tenant (no la de sus dominios) en la caché de búsqueda de todas las instancias; lo usatenancy move.- Probado contra Memcached 1.6, Kafka 3.9 (KRaft), NATS 2 con JetStream y Valkey 8.