Skip to content

Commit f2bd09f

Browse files
abcxffNathanFlurry
authored andcommitted
feat(serverless): make runner 0 config
1 parent 9cd6530 commit f2bd09f

File tree

7 files changed

+68
-125
lines changed

7 files changed

+68
-125
lines changed

packages/next-js/src/mod.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ export const toNextHandler = (
77
// Don't run server locally since we're using the fetch handler directly
88
inputConfig.disableServer = true;
99
inputConfig.disableActorDriver = true;
10+
// inputConfig.runnerKind = "serverless";
1011

11-
const { fetch } = registry.startServerless(inputConfig);
12+
const { fetch } = registry.start(inputConfig);
1213

1314
const fetchWrapper = async (
1415
request: Request,

packages/rivetkit/src/client/config.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ export const ClientConfigSchema = z.object({
1919
.optional()
2020
.transform((x) => x ?? getEnvUniversal("RIVET_TOKEN")),
2121

22-
totalSlots: z.number().optional(),
23-
2422
headers: z.record(z.string()).optional().default({}),
2523

2624
/** Endpoint to connect to the Rivet engine. Can be configured via RIVET_ENGINE env var. */

packages/rivetkit/src/drivers/default.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import type { DriverConfig, RunConfig } from "@/registry/run-config";
1010
export function chooseDefaultDriver(runConfig: RunConfig): DriverConfig {
1111
if (runConfig.endpoint && runConfig.driver) {
1212
throw new UserError(
13-
"Cannot specify both 'engine' and 'driver' in configuration",
13+
"Cannot specify both 'endpoint' and 'driver' in configuration",
14+
);
15+
}
16+
17+
if (runConfig.runnerKind === "serverless" && !runConfig.endpoint) {
18+
throw new UserError(
19+
"Cannot use 'serverless' runnerKind without the 'endpoint' config (or RIVET_ENGINE env) set",
1420
);
1521
}
1622

packages/rivetkit/src/drivers/engine/actor-driver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ export class EngineActorDriver implements ActorDriver {
9292
endpoint: config.endpoint,
9393
token: runConfig.token ?? config.token,
9494
pegboardEndpoint: config.pegboardEndpoint,
95-
namespace: config.namespace,
95+
namespace: runConfig.namespace ?? config.namespace,
9696
totalSlots: runConfig.totalSlots ?? config.totalSlots,
97-
runnerName: config.runnerName,
97+
runnerName: runConfig.runnerName ?? config.runnerName,
9898
runnerKey: config.runnerKey,
9999
metadata: {
100100
inspectorToken: this.#runConfig.inspector.token(),

packages/rivetkit/src/manager/router.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
type Actor as ApiActor,
5151
} from "@/manager-api/actors";
5252
import { RivetIdSchema } from "@/manager-api/common";
53+
import type { ServerlessActorDriverBuilder } from "@/mod";
5354
import type { RegistryConfig } from "@/registry/config";
5455
import type { RunConfig } from "@/registry/run-config";
5556
import type { ActorOutput, ManagerDriver } from "./driver";
@@ -79,7 +80,7 @@ export function createManagerRouter(
7980
registryConfig: RegistryConfig,
8081
runConfig: RunConfig,
8182
managerDriver: ManagerDriver,
82-
serverlessActorDriverBuilder: (() => ActorDriver) | undefined,
83+
serverlessActorDriverBuilder: ServerlessActorDriverBuilder | undefined,
8384
): { router: Hono; openapi: OpenAPIHono } {
8485
const router = new OpenAPIHono({ strict: false }).basePath(
8586
runConfig.basePath,
@@ -121,10 +122,7 @@ export function createManagerRouter(
121122

122123
function addServerlessRoutes(
123124
runConfig: RunConfig,
124-
serverlessActorDriverBuilder: (
125-
token: string | undefined,
126-
totalSlots: number | undefined,
127-
) => ActorDriver,
125+
serverlessActorDriverBuilder: ServerlessActorDriverBuilder,
128126
router: OpenAPIHono,
129127
) {
130128
// Apply CORS
@@ -141,11 +139,18 @@ function addServerlessRoutes(
141139
router.get("/start", async (c) => {
142140
const token = c.req.header("x-rivet-token");
143141
let totalSlots: number | undefined = parseInt(
144-
c.req.header("x-rivetkit-total-slots") as any,
142+
c.req.header("x-rivet-total-slots") as any,
143+
);
144+
if (!Number.isFinite(totalSlots)) totalSlots = undefined;
145+
const runnerName = c.req.header("x-rivet-runner-name");
146+
const namespace = c.req.header("x-rivet-namespace-id");
147+
148+
const actorDriver = serverlessActorDriverBuilder(
149+
token,
150+
totalSlots,
151+
runnerName,
152+
namespace,
145153
);
146-
if (isNaN(totalSlots)) totalSlots = undefined;
147-
148-
const actorDriver = serverlessActorDriverBuilder(token, totalSlots);
149154
invariant(
150155
actorDriver.serverlessHandleStart,
151156
"missing serverlessHandleStart on ActorDriver",

packages/rivetkit/src/registry/mod.ts

Lines changed: 26 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ import {
2424
} from "./run-config";
2525
import { crossPlatformServe } from "./serve";
2626

27+
export type ServerlessActorDriverBuilder = (
28+
token?: string,
29+
totalSlots?: number,
30+
runnerName?: string,
31+
namespace?: string,
32+
) => ActorDriver;
33+
2734
interface ServerOutput<A extends Registry<any>> {
2835
/** Client to communicate with the actors. */
2936
client: Client<A>;
@@ -72,6 +79,9 @@ export class Registry<A extends RegistryActors> {
7279
config.disableActorDriver = true;
7380
config.noWelcome = true;
7481
}
82+
if (config.runnerKind === "serverless") {
83+
config.disableActorDriver = true;
84+
}
7585

7686
// Configure getUpgradeWebSocket lazily so we can assign it in crossPlatformServe
7787
let upgradeWebSocket: any;
@@ -113,6 +123,7 @@ export class Registry<A extends RegistryActors> {
113123
console.log();
114124
}
115125

126+
let serverlessActorDriverBuilder: undefined | ServerlessActorDriverBuilder;
116127
// HACK: We need to find a better way to let the driver itself decide when to start the actor driver
117128
// Create runner
118129
//
@@ -124,117 +135,22 @@ export class Registry<A extends RegistryActors> {
124135
managerDriver,
125136
client,
126137
);
127-
}
128-
129-
const { router: hono } = createManagerRouter(
130-
this.#config,
131-
config,
132-
managerDriver,
133-
undefined,
134-
);
135-
136-
// Start server
137-
if (!config.disableServer) {
138-
(async () => {
139-
const out = await crossPlatformServe(hono, undefined);
140-
upgradeWebSocket = out.upgradeWebSocket;
141-
})();
142-
}
143-
144-
return {
145-
client,
146-
fetch: hono.fetch.bind(hono),
147-
};
148-
}
149-
150-
public startServerless(inputConfig?: RunConfigInput): ServerOutput<this> {
151-
const config = RunConfigSchema.parse(inputConfig);
152-
153-
// Configure logger
154-
if (config.logging?.baseLogger) {
155-
// Use provided base logger
156-
configureBaseLogger(config.logging.baseLogger);
157138
} else {
158-
// Configure default logger with log level from config
159-
// getPinoLevel will handle env variable priority
160-
configureDefaultLogger(config.logging?.level);
161-
}
162-
163-
// Choose the driver based on configuration
164-
const driver = chooseDefaultDriver(config);
165-
166-
// TODO: Find cleaner way of disabling by default
167-
if (driver.name === "engine") {
168-
config.inspector.enabled = false;
169-
config.disableServer = true;
170-
config.disableActorDriver = true;
171-
}
172-
if (driver.name === "cloudflare-workers") {
173-
config.inspector.enabled = false;
174-
config.disableServer = true;
175-
config.disableActorDriver = true;
176-
config.noWelcome = true;
177-
}
178-
179-
// Configure getUpgradeWebSocket lazily so we can assign it in crossPlatformServe
180-
let upgradeWebSocket: any;
181-
if (!config.getUpgradeWebSocket) {
182-
config.getUpgradeWebSocket = () => upgradeWebSocket!;
183-
}
184-
185-
// Create router
186-
const managerDriver = driver.manager(this.#config, config);
187-
188-
// Create client
189-
const client = createClientWithDriver<this>(managerDriver, config);
190-
191-
const driverLog = managerDriver.extraStartupLog?.() ?? {};
192-
logger().info({
193-
msg: "rivetkit ready",
194-
driver: driver.name,
195-
definitions: Object.keys(this.#config.use).length,
196-
...driverLog,
197-
});
198-
if (config.inspector?.enabled && managerDriver.inspector) {
199-
logger().info({ msg: "inspector ready", url: getInspectorUrl(config) });
200-
}
201-
202-
// Print welcome information
203-
if (!config.noWelcome) {
204-
const displayInfo = managerDriver.displayInformation();
205-
console.log();
206-
console.log(` RivetKit ${pkg.version} (${displayInfo.name})`);
207-
console.log(` - Endpoint: http://127.0.0.1:6420`);
208-
for (const [k, v] of Object.entries(displayInfo.properties)) {
209-
const padding = " ".repeat(Math.max(0, 13 - k.length));
210-
console.log(` - ${k}:${padding}${v}`);
211-
}
212-
if (config.inspector?.enabled && managerDriver.inspector) {
213-
console.log(` - Inspector: ${getInspectorUrl(config)}`);
214-
}
215-
console.log();
216-
}
217-
218-
let serverlessActorDriverBuilder:
219-
| ((token?: string, totalSlots?: number) => ActorDriver)
220-
| undefined = (
221-
token: string | undefined,
222-
totalSlots: number | undefined,
223-
) => {
224-
// Override config
225-
if (token) config.token = token;
226-
if (totalSlots) config.totalSlots = totalSlots;
227-
228-
return driver.actor(this.#config, config, managerDriver, client);
229-
};
230-
231-
// HACK: We need to find a better way to let the driver itself decide when to start the actor driver
232-
// Create runner
233-
//
234-
// Even though we do not use the return value, this is required to start the code that will handle incoming actors
235-
if (!config.disableActorDriver) {
236-
const _actorDriver = serverlessActorDriverBuilder();
237-
serverlessActorDriverBuilder = undefined;
139+
serverlessActorDriverBuilder = (
140+
token,
141+
totalSlots,
142+
runnerName,
143+
namespace,
144+
) => {
145+
// Override config
146+
if (token) config.token = token;
147+
if (totalSlots) config.totalSlots = totalSlots;
148+
if (runnerName) config.runnerName = runnerName;
149+
if (namespace) config.namespace = namespace;
150+
151+
// Create new actor driver with updated config
152+
return driver.actor(this.#config, config, managerDriver, client);
153+
};
238154
}
239155

240156
const { router: hono } = createManagerRouter(

packages/rivetkit/src/registry/run-config.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ClientConfigSchema } from "@/client/config";
66
import { LogLevelSchema } from "@/common/log";
77
import { InspectorConfigSchema } from "@/inspector/config";
88
import type { ManagerDriverBuilder } from "@/manager/driver";
9+
import { getEnvUniversal } from "@/utils";
910

1011
type CorsOptions = NonNullable<Parameters<typeof cors>[0]>;
1112

@@ -35,6 +36,22 @@ export const RunConfigSchema = ClientConfigSchema.extend({
3536
/** @experimental */
3637
disableActorDriver: z.boolean().optional().default(false),
3738

39+
/**
40+
* @experimental
41+
*
42+
* Whether to run runners normally or have them managed
43+
* serverlessly (by the Rivet Engine for example).
44+
*/
45+
runnerKind: z
46+
.enum(["serverless", "normal"])
47+
.optional()
48+
.default(() =>
49+
getEnvUniversal("RIVET_RUNNER_KIND") === "serverless"
50+
? "serverless"
51+
: "normal",
52+
),
53+
totalSlots: z.number().optional(),
54+
3855
/**
3956
* @experimental
4057
*

0 commit comments

Comments
 (0)