Limits and alerts
A function runs inside three declared bounds, and tells you when it crosses one. The bounds are the Driftfile's; the telling is an alert you define per function.
Limits, and what the caller sees
Three knobs bound a function, and all three are set in the slice's shape: the memory it books, plus the function timeout and the request rate for the slice. This is what a client gets when one is reached:
- X-Drift-Reason
FUNCTION-TIMEOUT- Cause, and what to do
- The invocation outlived
function_timeout. It is stopped at that budget rather than left running, so a slow function is ended and not merely abandoned.
- X-Drift-Reason
- —
- Cause, and what to do
- The slice passed
rate_limitfor the current window. Counted per account and slice, across every request that arrives however it arrives. Raise the limit, or send less.
- X-Drift-Reason
FUNCTION-MEMORY- Cause, and what to do
- That function is holding all the memory it booked, so there is no room to start another invocation. It clears on its own as calls finish, and booking more
memoryraises how many can run at once. Not a 429 — your caller did nothing wrong.
- X-Drift-Reason
SLICE-MEMORY- Cause, and what to do
- The whole slice is briefly under memory pressure and is shedding new work. It clears on its own.
- X-Drift-Reason
EVICTED- Cause, and what to do
- One invocation grew past the memory its function booked and was stopped, so everything running beside it kept going. The remedy is a bigger
memory: the function was booked too small, not written wrong. Your function's log carries the detail.
- X-Drift-Reason
FUNCTION-FAILED- Cause, and what to do
- The function ran and produced no response that could be sent — it exited without writing one, or wrote something unreadable. Your function's log carries what it printed.
The body says nothing about Drift. Every response above carries a plain
{"error":"service unavailable"} or {"error":"internal error"}, because it is
your API answering your users — and they can neither read your Driftfile nor act
on it. The machine-readable reason rides in the X-Drift-Reason header for your own logging
and alerting, and anything actionable (which function, which figure, which remedy) goes to that
function's log, where the person who can change it is the person reading.
A 503 and a 500 ask for opposite things. A 503 clears by itself,
so a retry is reasonable. A 500 from an eviction repeats identically until the booking
changes, which is why it carries no Retry-After: sending your caller round a loop that
cannot end would be worse than telling them it failed. See
memory and concurrency for how the booking decides both.
A timeout is enforced once, on one clock, so a call cannot be cut off in one place while still running in another. With no runtime limit declared the ceiling is 300 seconds, which stops a connection hanging indefinitely. The free tier resolves to 10 s of runtime, 60 requests per minute, and five function slots booking 8 MB each — 40 MB across the slice, reshapeable into any slots up to five that add up to the same. A configured slice books memory per function and sets its own.
Alerts
The slice evaluates alerts once a minute against each function's error count over a sliding window, and fires a webhook on each state change: one POST when it starts firing, one when it resolves, not one per tick. Alert definitions survive a restart.
drift atomic alert add checkout-errors checkout \
--on errors --threshold 5 --window 5m \
--notify webhook=https://hooks.slack.com/services/…
drift atomic alert list
drift atomic alert remove checkout-errorsOr declare them alongside the function in the Driftfile:
atomic:
functions:
- name: "post:checkout"
handler: PostCheckout
memory: 32MB
alerts:
- on: errors
threshold: 5
window: 5m
notify: "webhook=https://hooks.slack.com/..."
on: errors and notify: webhook= are the whole surface. Other trigger and
notify types are rejected when the alert is registered, not silently ignored. The window minimum is
60 seconds. The webhook receives JSON:
{
"alert": "checkout-errors",
"function": "checkout",
"trigger": "errors",
"transition": "firing", // or "resolved"
"threshold": 5,
"window": 300,
"at": "<RFC 3339 timestamp>"
}