Skip to content

Worker freezes ~75s on first outbound socket, with timers frozen too, on ~50% of cold isolates #738

Description

@pajervoj03

Summary

On a hosted project, roughly half of cold isolates freeze for ~75 seconds on the first outbound TCP connection, and the isolate's timers are frozen along with it — a setTimeout armed before the connect does not fire until the freeze ends. The freeze always ends at ~75,013ms after boot, which is the same constant at which an idle worker is shut down.

It is not the connection target: the retry connects in ~450ms on the same isolate immediately afterwards, and the behaviour is identical against two completely different hosts.

Observed

Function logs from one invocation (offsets relative to booted):

19:22:38  +    38ms  internal-db: DB_POOLER_URL -> aws-1-eu-north-1.pooler.supabase.com
19:23:53  + 75021ms  CLIENT_DEADLINE: database call exceeded the client deadline
19:23:54  + 75431ms  acquire_song_request_lock: took 75393ms
19:23:54  + 76173ms  ingestion: 200 in 76135ms
19:23:54  + 76179ms  shutdown

The CLIENT_DEADLINE line is an 8-second setTimeout racing the query:

await Promise.race([
  sql.begin(async (tx) => { /* one SELECT */ }),
  new Promise((_, reject) => setTimeout(() => reject(deadlineError()), 8_000)),
])

It is logged at 75,021ms. The timer was armed at ~38ms, so it should have fired at ~8,038ms. The handler had started — the line at +38ms is printed by the code immediately preceding the connect.

The 75,013ms constant

Across 11 triggers:

values (ms)
freeze ends / 8s deadline finally raised 75015, 75014, 75013, 75021, 75013, 75015
shutdown on a healthy idle worker 75015, 75012, 75013, 75011, 75010, 75012

Those are the same number. A network timeout scatters by hundreds of ms (DNS backoff, SYN retries, TLS); this does not vary by more than ~10ms, and it coincides exactly with the idle-worker teardown interval. That is what makes me think the isolate is not being scheduled rather than waiting on the network.

Not the host

Reproduced identically against both:

  • db.<ref>.supabase.co (direct, AAAA only)
  • aws-1-eu-north-1.pooler.supabase.com:6543 (Supavisor, A records, IPv4)

Same ~75s freeze, same constant, same ~450ms success on the retry that follows.

Pattern

Triggering the function every ~95s, the outcome alternated — stall, fast, stall, fast — over 11 triggers, independent of the gap between them and of which host was configured. Healthy cold isolates complete the same query in ~500ms.

Environment

  • Hosted project (not self-hosted), region eu-north-1
  • Deno driver: postgres.js (npm:postgres@3), max: 1, connect_timeout: 5, prepare: false, ssl: 'require'
  • Function invoked by a database webhook (after insert trigger)
  • deno_version = 2 in config.toml

Impact

The function it affects has a fixed wall-clock budget shared by its database calls and an outbound API call that takes 40–80s. A 75s freeze spends most of the budget before any work starts, so the API call is opened with too little time and gets aborted — surfacing to users as a failure of the API call rather than of the connection that actually stalled. Because the freeze takes the timers with it, no in-isolate deadline can bound it; the only workaround I found was to detect the lost budget after the fact and requeue the job onto a fresh isolate.

What I ruled out

  • Credentials / target availability — the retry succeeds immediately on the same isolate
  • IPv6 reachability — reproduced on an IPv4-only pooler host
  • Cold start generally — healthy cold isolates do the same work in ~500ms
  • Load — single sequential invocations, ~95s apart, no concurrency

Happy to supply request IDs or the project ref privately if that helps narrow it down.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions