# Flushing & serverless (/docs/sdk/flushing-and-serverless)



TokenPolice ships `/log` telemetry in the background so it never slows your call. In a long-running
server that's invisible. But when the process is about to stop — a serverless function freezing, a
script exiting — you need to **drain** those pending logs first, or the last calls are lost.

## The one asymmetry to remember [#the-one-asymmetry-to-remember]

| Call                       | Python                                   | Node                                                |
| -------------------------- | ---------------------------------------- | --------------------------------------------------- |
| `flush()`                  | `await flush()` — real drain             | `await tp.flush()` — real drain                     |
| `flush_sync` / `flushSync` | `flush_sync()` — real **blocking** drain | `flushSync()` — **diagnostic only, drains nothing** |

Both SDKs have a real drain. The trap is `flushSync` in **Node**: it can't block on a promise, so
it does **not** drain — it's for diagnostics only. Node's real drain is always `await`ed.

```python
# Python — either of these genuinely drains
import token_police as tp
tp.flush_sync()          # blocking, for sync code
# or:  await tp.flush()  # for async code
```

```typescript
// Node — the real drain is awaited
import * as tp from "token-police";
await tp.flush();
```

## Serverless [#serverless]

Set `deployment="serverless"` (or leave it `"auto"` — most platforms are detected). That switches
internal behavior, but it does **not** flush for you. Two ways to drain before freeze:

**Wrap the handler with `serverless()`** — it drains in a `finally`:

```python
import token_police as tp

@tp.serverless
async def handler(event, context):
    ...   # drained automatically on the way out
```

**Or flush yourself** in a `finally`:

```typescript
export async function handler(event) {
  try {
    // ... your LLM calls
  } finally {
    await tp.flush();
  }
}
```

<Callout type="info">
  Node **async** handlers get a real drain from `serverless()` and from `await tp.flush()`. Node
  **sync** handlers can't await, so durability there comes from `keepalive` on the `/log` requests
  — the platform keeps them alive briefly after the handler returns.
</Callout>

## Scripts and short-lived processes [#scripts-and-short-lived-processes]

A script that exits immediately can cut off in-flight logs — and in Node the open rule-stream can
keep the event loop alive.

* **Python:** call `flush_sync()` before exit. (Normal exits are also covered automatically.)
  Python has **no** `shutdown()` — `flush_sync()` is the whole story.
* **Node:** call `await tp.shutdown()` — it stops the stream and drains. Without it a short script
  may hang on the open connection. `flushSync()` is **not** a substitute: in Node it drains
  nothing.

```typescript
await tp.shutdown();   // Node scripts/CLIs: stops the stream and drains
```

## Edge runtimes [#edge-runtimes]

On edge runtimes (`deployment="edge"`), the underlying instrumentors can't load, so **usage
metering is limited** to the providers TokenPolice taps manually. **Enforcement still works** —
the pre-flight `/check` is a plain `fetch` and runs everywhere.

## Next [#next]

<Cards>
  <Card title="Node & ESM" href="/docs/sdk/node-esm" description="Import order and instrumentModules for native ESM." />

  <Card title="Serverless flush" href="/docs/troubleshooting/serverless-flush" description="Fixing missing logs from a serverless function." />
</Cards>
