Google Colab VM Lifecycle and SQLAlchemy Connection Pool Interaction
0 reputation · 23 Oct 2022, 10:52 UTC
0 reputation · 23 Oct 2022, 10:52 UTC
Goal: guarantee that database connections borrowed from a connection pool are properly returned to the pool before the underlying Colab virtual machine is reclaimed, preventing leaked connections and pool exhaustion.
Constraint: Colab notebooks run on temporary VMs that may be recycled after approximately ninety minutes of inactivity or when the runtime is reset, and the platform does not expose a pre‑shutdown hook for user code. The interaction between this VM lifetime, the pool’s idleTimeout setting, and the behavior of a factory reset on open TCP sockets is not documented, leaving designers unsure whether relying on pool timeouts alone is sufficient or whether explicit disposal per cell is required.
Specific questions: Does a Colab factory reset immediately terminate open TCP sockets, or do they persist until the operating system’s TCP keep‑alive times out? Should the pool’s idleTimeout be configured to match the expected VM lifetime, or must connections be explicitly closed at the end of each notebook cell to avoid accumulation? Is there a reliable signal within the notebook that indicates an imminent VM recycle so that a shutdown hook can be triggered?
33275 reputation · 23 Oct 2022, 19:49 UTC
1. Does a Colab factory reset terminate open TCP sockets immediately?
Yes. When Colab performs a factory reset, the Python kernel process and its underlying OS process are killed. The kernel closes all file descriptors, including TCP sockets, sending an RST to the remote peer. The sockets do not linger until a TCP keep‑alive timeout expires.
2. Should the pool’s idle_timeout be tuned to the VM lifetime?
Because sockets are closed instantly on reset, any connection that is still checked out at that moment becomes unusable. If the pool’s idle_timeout is set longer than the expected VM inactivity period (≈90 min), those stale connections will stay in the pool until the timeout fires, potentially exhausting the pool. A safer approach is to set idle_timeout to a value comfortably below the inactivity threshold (e.g., 60 min) and, more importantly, to return connections to the pool before the next cell runs.
3. Is there a reliable pre‑shutdown signal in Colab?
No documented hook or event signals an imminent VM recycle. The only detectable event is the runtime disconnect that occurs after the reset, which is too late for graceful cleanup.
Colab’s VM lifecycle is managed by Google’s infrastructure. When a reset is triggered, the entire runtime environment is torn down. The kernel process is terminated with a SIGKILL, which forces the OS to close all open file descriptors. The remote database server therefore receives a TCP RST, not a graceful FIN. SQLAlchemy’s pool, unaware of this external event, keeps the connection object in its internal list until the user explicitly returns it or the connection times out.
idle_timeout:
engine = create_engine(
"postgresql://user:pass@host/db",
poolclass=QueuePool,
pool_size=5,
max_overflow=10,
pool_timeout=30,
pool_recycle=60 * 60 # 60 minutes
)
try/finally or with block so it returns to the pool before the next cell:
from contextlib import contextmanager
@contextmanager
def get_session():
session = Session(bind=engine)
try:
yield session
finally:
session.close()
# Usage in a cell
with get_session() as s:
result = s.execute("SELECT 1")
print(result.scalar())
engine.dispose()
This clears the pool immediately, useful after a manual restart.
print(engine.pool.status())
Because the reset can happen at any time, relying solely on idle_timeout risks holding onto dead connections for up to that timeout. Explicitly closing the session guarantees the connection is returned to the pool immediately, preventing accumulation. If you have a long‑running notebook that keeps a session open across many cells, consider wrapping the session acquisition in a helper that always closes the session in a finally block.
If you are using a database that implements its own connection‑validation (e.g., pool_pre_ping=True in SQLAlchemy), let us know, as that can affect how quickly a stale connection is detected. Also, if you are using a different pool class (e.g., NullPool or SingletonThreadPool), the recommendations may differ.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.