La computación distribuida moderna, especialmente en el ámbito de los agentes de IA y las aplicaciones colaborativas en tiempo real, enfrenta un desafío fundamental: cómo mantener el estado, la durabilidad y la baja latencia a escala masiva, sin incurrir en los altos costos operativos y de recursos asociados con la virtualización tradicional (VMs o contenedores por instancia). Los modelos de concurrencia basados en actores, popularizados por lenguajes como Erlang y frameworks como Akka u Orleans, ofrecen una abstracción potente para gestionar el estado concurrente. Sin embargo, su implementación a menudo requiere infraestructuras complejas para la persistencia y la escalabilidad elástica.

Rivet aborda este problema introduciendo un modelo de actor virtual que combina la eficiencia de los procesos compartidos con la durabilidad de SQLite respaldado por almacenamiento de objetos (S3). Esto permite que los actores mantengan su estado en memoria para operaciones de baja latencia, mientras que su estado duradero se almacena de forma económica y escalable. La clave es desacoplar el ciclo de vida del actor de la ejecución de código potencialmente costosa o insegura (como la inferencia de IA en un sandbox), permitiendo que el actor "hiberne" y solo se reactive bajo demanda, optimizando drásticamente el consumo de recursos para cargas de trabajo intermitentes.

Arquitectura del Sistema

La arquitectura de Rivet se compone de un plano de control (Control Plane) escrito en Rust y nodos de ejecución (Actor Nodes) que pueden ser Node.js o Bun. El Control Plane es responsable de la programación (scheduling), enrutamiento y persistencia de los Actores. Utiliza Pegboard para la programación y la red de Actores, Guard como proxy de enrutamiento de tráfico, y Epoxy como un almacén de clave-valor multi-región basado en el protocolo EPaxos para la coordinación y el estado del plano de control. La única dependencia externa del Control Plane es un bucket de almacenamiento de objetos compatible con S3, que actúa como el backend de almacenamiento primario para todos los datos duraderos.

Cada Rivet Actor es una entidad lógica que encapsula estado en memoria, una base de datos SQLite privada y un servidor WebSocket. La base de datos SQLite de cada Actor se persiste en S3, lo que proporciona durabilidad y escalabilidad a bajo costo, emulando velocidades de SSD. Cuando un Actor está inactivo, su estado en memoria se descarga y su instancia se "hiberna", liberando recursos. Al recibir una nueva solicitud, el Actor se "despierta" rápidamente, restaurando su estado. Esta separación del estado duradero en S3 permite que los nodos de ejecución sean efímeros y que los Actores se muevan entre nodos sin perder su estado, proporcionando tolerancia a fallos. La comunicación entre Actores y clientes se realiza a través de WebSockets para interacciones en tiempo real y HTTP para llamadas directas, con tipado de extremo a extremo a través de SDKs.

Flujo de Activación y Persistencia de Actor

  1. 1 Cliente solicita Actor Un cliente (frontend/backend) solicita un Actor por su clave.
  2. 2 Control Plane enruta El Control Plane (Guard) enruta la solicitud al nodo de Actor apropiado o sel...
  3. 3 Actor se activa/despierta Si el Actor está inactivo, el nodo lo activa. Carga el estado de SQLite/S3.
  4. 4 Operación en Actor El Actor ejecuta la lógica de negocio, actualizando su estado en memoria y SQ...
  5. 5 Persistencia en S3 Los cambios en SQLite se sincronizan de forma duradera con S3.
  6. 6 Respuesta al Cliente El Actor envía la respuesta al cliente (vía HTTP/WebSocket).
  7. 7 Actor hiberna (si inactivo) Si el Actor permanece inactivo, su estado en memoria se descarga y se hiberna.
CapaTecnologíaJustificación
orchestration Rivet Control Plane (Rust) Gestiona el ciclo de vida de los Actores, enrutamiento de mensajes, programación y coordinación de la persistencia. Utiliza Pegboard para scheduling y Guard para routing.
storage SQLite Base de datos local por Actor para estado duradero, aprovechando su ligereza y capacidad de incrustación.
storage Amazon S3 (o compatible) Backend de almacenamiento de objetos para la persistencia de las bases de datos SQLite de los Actores, proporcionando durabilidad, escalabilidad y bajo costo. vs Google Cloud Storage, Azure Blob Storage, MinIO, Cloudflare R2
compute Node.js / Bun Runtimes para la ejecución de los Actores, elegidos por su rendimiento, ecosistema y compatibilidad con JavaScript/TypeScript. vs Deno
messaging WebSockets Proporciona comunicación bidireccional en tiempo real entre clientes y Actores, esencial para aplicaciones colaborativas y agentes de IA.
observability OpenTelemetry Estándar para la instrumentación de telemetría (trazas, métricas, logs) para monitorear el comportamiento de los Actores y el sistema.

Trade-offs

Ganancias
  • ▲▲ Latencia de arranque en frío (p99)
  • ▲▲ Memoria por instancia
  • ▲ Costo de almacenamiento
  • ▲▲ Escalabilidad de Actores
Costes
    import { actor, setup } from "rivetkit";
    import { db } from "rivetkit/db";
    import { streamText } from "ai";
    import { openai } from "@ai-sdk/openai";
    
    export const agent = actor({
      db: db({
        onMigrate: async (db) => {
          await db.execute("CREATE TABLE IF NOT EXISTS messages (role TEXT, content TEXT)");
        },
      }),
      createVars: () => ({ online: new Map<string, string>() }),
      onDisconnect: (c, conn) => {
        c.vars.online.delete(conn.id);
        c.broadcast("online", [...c.vars.online.values()]);
      },
      actions: {
        join: (c, user: string) => {
          c.vars.online.set(c.conn.id, user);
          c.broadcast("online", [...c.vars.online.values()]);
        },
        sendMessage: async (c, text: string) => {
          await c.db.execute("INSERT INTO messages VALUES (?, ?)", "user", text);
          const messages = await c.db.execute("SELECT role, content FROM messages");
          const response = streamText({ model: openai("gpt-5"), messages });
          for await (const delta of response.textStream) {
            c.broadcast("token", delta);
          }
          await c.db.execute("INSERT INTO messages VALUES (?, ?)", "assistant", await response.text);
        },
      },
    });
    export const registry = setup({ use: { agent } });
    Ejemplo de cómo se define un Actor Rivet, incluyendo la configuración de su base de datos SQLite, variables en memoria y acciones que manejan mensajes y conexiones WebSocket.

    Fundamentos Teóricos

    El concepto de actores virtuales, como el implementado en Rivet, tiene sus raíces en el modelo de actores propuesto por Carl Hewitt en 1973, que define un actor como una entidad fundamental de computación que, en respuesta a un mensaje, puede tomar decisiones, crear nuevos actores, enviar mensajes a otros actores y modificar su propio estado privado. La noción de "actor virtual" fue popularizada por el proyecto Orleans de Microsoft Research, que abordó el desafío de la escalabilidad y la durabilidad de los actores en sistemas distribuidos. Orleans, descrito en papers como "Orleans: A Framework for Cloud Computing" (Bykov et al., 2011), introdujo la idea de que los actores no necesitan estar activos continuamente en memoria, sino que pueden ser activados y desactivados bajo demanda, con su estado persistido de forma transparente. Rivet extiende esta filosofía aplicando SQLite sobre S3 como mecanismo de persistencia, una combinación que optimiza el costo y la escalabilidad del almacenamiento duradero, un problema recurrente en la gestión de estado distribuido.