Skip to content

Concurrent cold-starts hang: socket accepts but backend never serves (appears fixed by #52's pglite 0.5.x bump) #55

Description

@allansimon-mistral

Summary

Under concurrent cold-starts (many PGlite instances started at once, for example pytest-xdist with more workers than the machine has cores), a fraction of instances reach a state where the pglite-socket TCP server is listening but the Postgres backend never completes a client connection. PGliteManager.start() reports the instance ready (it only checks that a bare TCP connect succeeds), and every real connection to that instance then hangs until it times out.

Symptoms

  • psycopg.errors.ConnectionTimeout (not connection-refused) on the first real query.
  • The node process is alive, with no error and no crash. Its last stdout line is Server started on TCP 127.0.0.1:<port>, then silence.
  • With the default readiness (wait_for_ready retries 15 times at connect_timeout=60), this stalls for about 15 minutes before failing. That long stall is a separate, defensive fix in Lower readiness connect_timeout so wait_for_ready fails fast on a hung backend #54.

Reproduction and measured rates

Start N PGliteManager(use_tcp=True) instances concurrently, process-parallel so each has its own work directory, and after each start() returns, open a real connection with a short connect_timeout and run SELECT 1. On an oversubscribed machine a fraction time out even though start() reported success.

Matched runs, 24 concurrent cold-starts x 8 rounds (192 starts) on a 10-core machine, changing only the pglite / pglite-socket versions:

@electric-sql/pglite / pglite-socket hangs
^0.3.0 / ^0.0.8 (current main) 132 / 192 (68.8%)
^0.5.0 / ^0.2.1 (from #52) 0 / 192 (0%)

The hang rate scales with how much the run oversubscribes the CPU (workers per core), consistent with starvation of the single-threaded node/WASM backend. It also reproduces at lower rates with milder oversubscription and on Linux CI.

This looks fixed by #52

The only difference between the two rows above is the pglite and pglite-socket versions, and PR #52 ("update pglite 0.5.0, allow multiple concurrent connections") bumps exactly those. With those versions the hang does not reproduce at all (0 / 192). So merging #52 appears to resolve this.

#54 is complementary rather than an alternative: it makes wait_for_ready fail fast if a backend is ever unreachable, but it does not fix the hang itself. #52 does.

Environment

py-pglite 0.5.3, TCP mode, macOS (10 cores) and Linux CI.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions