Zum Inhalt springen
GigadriveDocs

Sticky sessions

Signed URLs that send every request sharing a key to the same copy of a Function, so state your code holds in memory survives past one request.

Gigadrive Network runs several copies of a Function at once and admits each request independently, so two requests from the same browser can land on different copies and read different state. A sticky session URL is a signed link that sends every request carrying the same key to one copy.

When you need one

Anything your Function keeps in a variable rather than in a database or a bucket: a room's member list, a game board, an open collaborative document, a set of WebSocket peers that have to see each other's frames. Without a sticky URL each of those requests picks its own copy and finds nothing.

You do not need one when every request can be served from shared storage. A pin constrains where a request may run, which is a real cost, so use it only when the state cannot leave memory.

Two URLs minted with the same key for the same Function share one pin, so an HTTP call and a WebSocket connection can be tied together.

Minting a URL

Only a deployed Function can mint a sticky URL, and only for itself. The API checks that the caller's token belongs to a Function and that the token names that same Function, so a personal API key, or anything running on your laptop, gets 403. The credentials injected into every Function already carry the network:sticky_sessions:write scope the call needs, which is why the client in the following example is built with no arguments. OIDC federation covers where those credentials come from.

// api/index.js: hands each room a sticky WebSocket URL
import http from 'node:http';
import { GigadriveClient } from '@gigadrive/sdk';

const gigadrive = new GigadriveClient();

const server = http.createServer(async (req, res) => {
  const room = new URL(req.url, 'https://example.invalid').searchParams.get('room') ?? 'lobby';

  const { url, expiresAt } = await gigadrive.stickySessions.createUrl({
    key: `room:${room}`,
    endpoint: '/socket',
    expiresInSeconds: 3600,
  });

  res.writeHead(200, { 'content-type': 'application/json' });
  res.end(JSON.stringify({ url: url.replace('https://', 'wss://'), expiresAt }));
});

server.listen(3000);

The URL is on the deployment's own hostname rather than your production domain, and carries the capability in a __gd_sticky query parameter. expiresAt is an ISO 8601 timestamp.

FieldDefaultNotes
keyrequiredOpaque to the platform, 1 to 512 characters. It is hashed before it enters the URL, and the raw value is never stored
endpointrequiredA relative path starting with /, up to 2048 characters, that resolves to a Function route on the deployment minting it
methodGETOne of GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH
expiresInSeconds1440060 to 86400

Using the URL

Path and method are part of the signature and are checked on every request. A WebSocket handshake is a GET, which is the default, so leave method alone unless you are pinning a request that is not one.

The URL comes back on https. Swap the scheme to wss to open a socket with it: the signature covers the deployment, the path, the method and the key, not the scheme. WebSockets covers what your Function has to look like to accept the upgrade.

__gd_sticky is removed before the request reaches your code, and stripped out of URL-valued headers such as Referer, so it does not leak into your logs or into outbound links.

Lifetime

expiresInSeconds defaults to 14400, four hours, and accepts 60 seconds to 86400, one day. Past that the URL is dead: the edge answers 403 with the error code EDGE_STICKY_SESSION_REJECTED, and a WebSocket gets the same status at the handshake rather than a socket that closes a moment later. A URL copied to another route or another deployment is refused the same way.

Each request refreshes the pin to the expiry of the URL it used. Minting a fresh URL for a key that is still pinned extends it and rejoins the same copy, so a busy key stays put without any coordination on your side.

The pin lasts as long as the copy does. When that copy is retired, or dropped from routing after a health failure, the next request on the key claims a new copy and your in-memory state starts empty. A sticky URL also names the deployment that minted it, so it keeps reaching that deployment after you ship a new one, and state does not move across a deploy.

A sticky request that cannot be admitted to its copy within 2 seconds, because the copy is still starting or is already full, gets 503 with EDGE_FUNCTION_STARTING and Retry-After: 2.

Limits

LimitValue
Who can mintA deployed Function, for itself
Scopenetwork:sticky_sessions:write
URL lifetime60 seconds to one day, four hours by default
Concurrent requests per key32, the pinned copy's ceiling
Concurrent WebSockets per key250 per 256 MB of the pinned copy's memory
Plan Concurrency limitUnchanged. A sticky request counts against it like any other

A key that needs more concurrent traffic than one copy can hold cannot be spread across copies, because spreading it is the thing you asked the platform not to do. Concurrency covers how a Function scales when nothing is pinned.