Python SDK 4.X cb query fails with no event loop in thread and requires async calls when not using acouchbase

Dear Couchbase team;
we use your product as database which is queried to respond to API calls, for which we use the FastAPI framework. The framework comes with the asyncio library integrated. If you then define an endpoint function with “async” you’ll get asynchronous execution for the code executed to fullfill the request. With the change to 4.x in the Python SDK, we experienced an error when NOT using async for the endpoint which we don’t understand and that’s why we would be interested in an explanation of the behavoir of the Couchbase Python SDK.

The error we got:

RuntimeError: "There is no current event loop in thread 'ThreadPoolExecutor-0_1'".
Traceback:
....
File "/usr/local/lib/python3.9/site-packages/couchbase/cluster.py", line 387, in query
row_iter = cluster.query(
File "/usr/local/lib/python3.9/site-packages/couchbase/n1ql.py", line 44, in generate_n1ql_request
return QueryResult(N1QLRequest.generate_n1ql_request(self.connection,
File "/usr/local/lib/python3.9/site-packages/couchbase/n1ql.py", line 40, in __init__
return cls(connection, query_params, row_factory=row_factory, **kwargs)
File "/usr/local/lib/python3.9/site-packages/couchbase/logic/n1ql.py", line 706, in __init__
super().__init__(connection, query_params, row_factory=row_factory, **kwargs)
File "/usr/local/lib/python3.9/asyncio/queues.py", line 35, in __init__
self._rows = asyncio.Queue()
File "/usr/local/lib/python3.9/asyncio/events.py", line 642, in get_event_loop
self._loop = events.get_event_loop()
raise RuntimeError('There is no current event loop in thread %r.'

One example query:

bucket = "bucket_name"
cluster = Cluster(
    f"couchbase://{couchbase_server}",
    ClusterOptions(PasswordAuthenticator("Account", couchbase_account_pwd), lockmode=0),
)


sql_query = (
    f"SELECT DISTINCT body.`transaction`"
    f"FROM `{bucket}` where document_type = 'doc_type'  AND "
    f"(ANY sgw_obj IN sgw SATISFIES sgw_obj.company_id IN $1 OR sgw_obj.type_id IN $2 END "
    f"OR $3 IN involved_users "
    f"OR meta().id IN (SELECT RAW target_doc FROM `{bucket}` t2 "
    f"WHERE document_type = 'asset_sharing' AND $3 IN target_users))"
)


try:
    row_iter = cluster.query(
        sql_query,
        QueryOptions(
            positional_parameters=[
                ["id0"],
                ["id1", "id2"],
                "id3",
            ]
        ),
    )
except Exception as e:
    pass

We found out that using “async” defined endpoint functions, i.e. running the routine with async, ‘fixed’ the problem but still leaves us slightly confused. We’re using the normal couchbase functions, not ‘acouchbase’ and the routine wasn’t running via async when using SDK version 3.X. The error occured every time we ran functions which used n1ql queries.
So why does couchbase still look for an asyncio event loop even though the routine wasn’t started asynchronously? We hadn’t any issues with any 3.X version of the Python SDK and now we have to explicitly use async even though we don’t use the ‘acouchbase’ library.

Thank you for your help.

This is a bug in how the 4.0.0 queries handle streaming result when executing in python threads. I can see the same error running a query in a thread. The assumption being made in our code was that we either were in the main thread, or we were executing in an asyncio (or twisted) context. I opened an issue for it.

2 Likes

Thanks for the fast confirmation and opened bug report!

Thanks for trying out 4.0.0 so quickly! This pointed out a hole in our unit tests that I plan to fill as well - I’d not considered testing within threads, but it really makes sense given how FastApi (and maybe other frameworks) are implemented.