Skip to content

connection_pool: fix excessive idle CPU usage while waiting for requests - #336

Merged
Mockird31 merged 1 commit into
tarantool:masterfrom
ThCompiler:master
Jul 16, 2026
Merged

connection_pool: fix excessive idle CPU usage while waiting for requests#336
Mockird31 merged 1 commit into
tarantool:masterfrom
ThCompiler:master

Conversation

@ThCompiler

@ThCompilerThCompiler commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

While using ConnectionPool, it was found that it uses 100% cpu for its threads when at rest. After examining its operation, it was realized that the _request_process_loop runs in an infinite loop, without blocking on waiting for the next request. To reduce the load, I suggest using already implemented wait for the next element in the queue's from package queue .

To check the solution, I implemented a simple test that calculates the n-th element of the Fibonacci sequence using a ConnectionPool running in the background:

importqueueimporttimefromtarantoolimportConnectionfromtarantool.connection_poolimportConnectionPoolasOriginalConnectionPooldeffib(n):
a=0b=1for_inrange(n):
a, b=b, a+breturnaclassConnectionPool(OriginalConnectionPool):
# Override of the standard pool loop, which loaded the processor at 100% # with an infinite constant loopdef_request_process_loop( # noqa: max-complexity=8self, key, unit, last_refresh,
):
""" Request process background loop for a pool server. Started in a separate thread, one thread per server. :param key: Result of :meth:`~tarantool.connection_pool._make_key`. :type key: :obj:`str` :param unit: Server metainfo. :type unit: :class:`~tarantool.connection_pool.PoolUnit` :param last_refresh: Time of last metainfo refresh. :type last_refresh: :obj:`float` """whileunit.request_processing_enabled:
try:
task=unit.input_queue.get(timeout=self.refresh_delay)
exceptqueue.Empty:
task=Noneiftask:
method=getattr(Connection, task.method_name)
try:
resp=method(unit.conn, *task.args, **task.kwargs)
exceptExceptionasexc:
unit.output_queue.put(exc)
else:
unit.output_queue.put(resp)
now=time.time()
ifnow-last_refresh>self.refresh_delay:
self._refresh_state(key)
last_refresh=time.time()
deftest_bench_updated_pool(benchmark):
pool=ConnectionPool(
addrs=[
{
'host': '127.0.0.1',
'port': 3301,
},
{
'host': '127.0.0.1',
'port': 3302,
},
{
'host': '127.0.0.1',
'port': 3303,
},
],
user='guest',
)
benchmark(fib, 1000000)
pool.close()
deftest_bench_original_pool(benchmark):
pool=OriginalConnectionPool(
addrs=[
{
'host': '127.0.0.1',
'port': 3301,
},
{
'host': '127.0.0.1',
'port': 3302,
},
{
'host': '127.0.0.1',
'port': 3303,
},
],
user='guest',
)
benchmark(fib, 1000000)
pool.close()

I ran the test with the changes and the old ConnectionPool behavior. So i got the following test results:
image

I also looked at the distribution of processor activity using py-spy for each of the tests:

  • for current behavior:
image
  • for new behavior:
image

As you can see from the py-spy snapshots, after the change, the processor is almost fully used for calculating Fibonacci sequence, while with the current behavior, most of the time is spent on _request_process_loop.

@bigbesbigbes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment threadtarantool/connection_pool.py
Comment threadtarantool/connection_pool.py

@oleg-jukovecoleg-jukovec left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, update the commit message (see above) and add a new entry to the CHANGELOG.md.

@ThCompilerThCompiler changed the title Fix cpu overload when working with connection_poolconnection_pool: fix excessive idle CPU usage while waiting for requestsJun 22, 2026
@ThCompiler

ThCompiler commented Jun 22, 2026

Copy link
Copy Markdown
ContributorAuthor

I had to update the build os version for the readthedocs to 24.04, as it stopped supporting 20.04. 24.04 was selected based on a similar version update in #330.

@ThCompiler

ThCompiler commented Jun 22, 2026

Copy link
Copy Markdown
ContributorAuthor

I checked failed jobs on CI.

Windows system jobs fail because the default version of setuptools is 65.5.0 in the CI image. When dependencies are installed in a step, setuptools updates to the latest version 82.0.1 which meets the requirements of >=75.3.2. The pkg_resources has been removed in this version of setuptools. https://setuptools.pypa.io/en/latest/. As a quick solution, I can set the upper bound for the setuptools version to <82.0 in the testing requirements.

In Linux, a jobs that use the master Tarantool branch fail because the maximum decimal precision has been increased to 76 in Tarantool 3.5. tarantool/tarantool#11497. I can try to fix this in the implementation of the decimal type for msgpack.

Should I make these changes in this pull request?

@ThCompiler

ThCompiler commented Jun 26, 2026

Copy link
Copy Markdown
ContributorAuthor

I decided to have a separate PR(#342) for the CI fix because there are a lot of changes related to the new decimal precision. Could you check this PR please? After it we will be able to successfully finish workflow in this PR.

@Mockird31

Copy link
Copy Markdown

Thanks for the patch!

@Mockird31
Mockird31 removed the request for review from oleg-jukovecJuly 16, 2026 07:47
@Mockird31
Mockird31 merged commit 1719e9c into tarantool:masterJul 16, 2026
36 checks passed
@Mockird31Mockird31 mentioned this pull request Aug 16, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@ThCompiler@Mockird31@bigbes@DifferentialOrange@oleg-jukovec