NEW

Pulse 2025 Product Roundup: From Monitoring to AI-Native Control Plane

ClickHouse DB::Exception: Session is locked

ClickHouse raises the SESSION_IS_LOCKED error when a request tries to use a session that is currently occupied by another in-flight request. Sessions in ClickHouse are single-threaded by design -- only one query can hold a session lock at a time. If a second request arrives while the first is still executing, the second request fails with this error.

Impact

The immediate effect is that the second (and any subsequent) concurrent query on the same session is rejected. This does not affect the first query, which continues running normally. Applications that fire multiple parallel queries using the same session ID will see intermittent failures that correlate with query concurrency. In web applications or API servers that accidentally share a session ID across threads, the error can appear unpredictably under load.

Common Causes

  1. Parallel HTTP requests sharing a session ID -- Sending multiple concurrent HTTP requests to ClickHouse with the same session_id parameter. Only one can proceed; the rest are rejected.
  2. Multithreaded application with shared session -- Application code that shares a single session across multiple threads without synchronization.
  3. Long-running query blocking the session -- A slow query holds the session lock for an extended period, causing subsequent requests on the same session to fail.
  4. Retry logic sending duplicate requests -- Aggressive retry mechanisms may resend a query before the original has completed, creating a race condition on the session lock.
  5. Client library connection pooling -- Some connection pools may reuse a session ID across concurrent connections inadvertently.

Troubleshooting and Resolution Steps

  1. Use unique session IDs per concurrent workflow: If you need parallelism, assign a distinct session ID to each concurrent request or thread:

    import uuid
    
    def run_query(query):
        session_id = str(uuid.uuid4())
        return client.execute(query, settings={'session_id': session_id})
    
  2. Serialize requests on a shared session: If you must share a session (for example, to access the same temporary tables), serialize your queries so only one runs at a time:

    import threading
    
    session_lock = threading.Lock()
    
    def run_query_in_session(query, session_id):
        with session_lock:
            return client.execute(query, settings={'session_id': session_id})
    
  3. Check for long-running queries: Identify whether a long-running query is holding the session:

    SELECT query_id, elapsed, query
    FROM system.processes
    ORDER BY elapsed DESC;
    

    If a stuck query is the cause, consider killing it with KILL QUERY WHERE query_id = 'xxx'.

  4. Avoid sessions when they are not needed: If your queries do not require temporary tables or session-scoped settings, simply do not pass a session_id. Stateless queries avoid session contention entirely.

  5. Implement backoff-and-retry: If concurrent access is occasional and unavoidable, catch the SESSION_IS_LOCKED error and retry after a short delay:

    import time
    
    def query_with_retry(query, session_id, retries=3):
        for attempt in range(retries):
            try:
                return client.execute(query, settings={'session_id': session_id})
            except Exception as e:
                if 'SESSION_IS_LOCKED' in str(e) and attempt < retries - 1:
                    time.sleep(0.1 * (attempt + 1))
                else:
                    raise
    

Best Practices

  • Treat each session ID as a single-threaded resource and never share it across concurrent requests.
  • Use sessions only when necessary -- for temporary tables or session-scoped settings. Most queries do not require a session.
  • Set reasonable query timeouts to prevent long-running queries from monopolizing a session.
  • Monitor system.processes for queries that hold sessions for unusually long periods.
  • In connection pool configurations, ensure that session IDs are scoped per connection rather than shared globally.

Frequently Asked Questions

Q: Can I increase the session concurrency limit so multiple queries can use the same session simultaneously?
A: No. ClickHouse sessions are inherently single-threaded. The one-query-per-session restriction is by design, not a tunable parameter. To run queries in parallel, use separate session IDs.

Q: Does this error affect the query that is currently running on the session?
A: No. The query that already holds the session lock continues unaffected. Only the new incoming request that cannot acquire the lock is rejected.

Q: Will switching from the HTTP interface to the native TCP protocol avoid this error?
A: Not directly. The native protocol also uses sessions, and the same locking behavior applies. However, clickhouse-client in interactive mode naturally serializes queries, so you are less likely to hit this issue.

Q: Is SESSION_IS_LOCKED related to table-level locks or mutations?
A: No, it is unrelated. This error is purely about the session-level lock that prevents concurrent use of the same session. Table locks and mutation locks are separate mechanisms.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.