Skip to content

Process lifecycle: no atexit flush, no signal handlers, dispose can leak threads #6

@AndresL230

Description

@AndresL230

Summary

Two related lifecycle gaps:

1. No atexit / signal handler

The flush timer is a daemon thread — when the process exits normally (sys.exit(), end of __main__, AWS Lambda invocation completes, SIGTERM in a container), the daemon thread is killed and the current aggregator bucket is lost. Cron jobs, Lambdas, batch scripts, and one-shot CLIs report nothing unless the user manually calls handle.dispose().

2. Dispose during connect leaks FDs

recost/_transport.py:133-146, 199-210's dispose() sets self._running = False and queues a sentinel. If the loop is currently inside websockets.connect(url) (blocking await on TCP connect), the sentinel sits in the queue until the connect either succeeds or hits the OS TCP timeout (~75s on Linux). thread.join(timeout=2.0) returns without joining. The daemon thread + open socket FD leak until process exit. In long-lived processes that call init()/dispose() many times (test suites, Flask dev server with reload), FDs accumulate.

Fix

  • In init(), register atexit.register(_final_flush) and (optionally) signal.signal(SIGTERM, ...). Make handlers idempotent. Respect shutdown_flush_timeout_ms. Provide auto_shutdown_handlers=False opt-out.
  • On dispose, loop.call_soon_threadsafe(loop.stop) and cancel pending tasks. Then join with a timeout derived from shutdown_flush_timeout_ms (currently hardcoded 5s) and log if the thread didn't exit.

Files

  • recost/_init.py
  • recost/_transport.py
  • tests/test_init.py

Priority

P1 — short-lived processes report nothing; long-lived test suites leak FDs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Should fix before next releasebugSomething isn't workingruntimeRuntime / behavioral bug

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions