DeployKit

Lifecycle and cancelling

Starting and closing a client, and stopping work part-way.

One client per process

Create one client (Promise) or provide one layer (Effect) and share it. It holds:

  • the HTTP client, including any upload throttle,
  • the memory and disk budgets that every deploy through it shares.

Two clients don't share budgets or throttles, so creating one per request defeats both.

Closing

The layer's resources live as long as the scope that provides it. With Effect.provide on a single program, that's the program. For a long-running service, build it once with a ManagedRuntime or at the top of your app's layer graph, not per request.

import { Layer, ManagedRuntime } from "effect"
import { NodeFileSystem } from "@effect/platform-node"
import * as Vercel from "@deploykit/vercel"

const runtime = ManagedRuntime.make(Vercel.layerConfig().pipe(Layer.provide(NodeFileSystem.layer)))
// ...
await runtime.dispose()

Cancelling a call

Interrupt the fiber. Timeouts, Effect.race and scope closure all do this.

import { Effect } from "effect"

const bounded = publish.pipe(Effect.timeout("10 minutes"))

What cancelling does

  • Stops reading files and closes their streams.
  • Stops uploads that are in progress.
  • Releases memory and disk reservations and deletes temporary files.

What it can't do: undo a request the host already accepted. If you cancel during the create step, the deployment may exist anyway. Treat it like an unknown outcome.

Persist before and after

Cancellation, crashes and lost responses all have the same answer: write down what you're about to do, and what you got back.

  1. Before deploy: save the attempt, and its operationId on Vercel.
  2. After deploy: save the deployment id immediately, before waiting or checking.
  3. On restart: look for attempts with no deployment id and reconcile them.

onProgress isn't a substitute for step 2. It can be cut off, and an interrupted call may never return its result.

Last updated on

On this page