Skip to content

fix(embeddings): bound the connection separately from the read - #346

Open
bakiburakogun wants to merge 1 commit into
nextcloud:masterfrom
bakiburakogun:fix/embedding-connect-timeout
Open

fix(embeddings): bound the connection separately from the read#346
bakiburakogun wants to merge 1 commit into
nextcloud:masterfrom
bakiburakogun:fix/embedding-connect-timeout

Conversation

@bakiburakogun

Copy link
Copy Markdown

Fixes #345

Problem

_get_embedding() passed a single value as the timeout, so nothing bounded establishing the connection — only reading the response. When the TLS handshake to the embedding service stalls (TCP connects, the handshake never completes) the worker waits in recv for the whole request_timeout, which defaults to 1800 s.

It costs more than the one request: files_indexing_thread dispatches a batch across workers and then waits for all of them, so a single stalled handshake holds up the batch and no further queue items are fetched until it returns. In the logs that shows up as every worker of a batch finishing at the same moment, at the timeout, instead of at its own pace.

py-spy on a stalled worker put it inside connect(), in the handshake exchange:

sync_recv_gro (urllib3/contrib/ssa/_gro.py:205)
__exchange_until (urllib3/backend/hface.py:1018)
_post_conn (urllib3/backend/hface.py:712)
connect (urllib3/connection.py:894)
...
post (niquests/api.py:401)
_get_embedding (context_chat_backend/network_em.py:99)

Change

Add HTTP_CONNECT_TIMEOUT next to the existing TCP_CONNECT_TIMEOUT and pass (connect, read) to niquests.post. The read timeout is unchanged, so slow embedding responses are still tolerated exactly as before; only a connection that will not come up now fails quickly, and the retry already implemented in _get_embedding() can land on a healthy connection.

15 s is generous relative to what a working handshake costs — the same endpoint from the same container answers a one-text request in 0.28 s — while being far below the read timeout.

Testing

Deployed on Nextcloud 34.0.3, four backend instances, external embedding service. Same corpus, same nodes, nothing else changed:

before after
worker duration ~295–305 s, all finishing together 1.4–85 s
batch interval ~5 min ~1.5 min
indexing rate 5.2 documents/min 60–65 documents/min

Sustained over the following hours at 60–65 documents/min. Before the change the run looked stopped rather than slow, which is what sent us looking for a hang in the first place.

The embedding request passed a single timeout value, so nothing bounded
establishing the connection. When the TLS handshake to the embedding service
stalls — TCP connects, the handshake never completes — the worker waits in recv
for the whole request_timeout, 1800 s by default.

That stops more than the one request: files_indexing_thread waits for every
worker of the batch, so one stalled handshake holds up the batch and no further
queue items are fetched until it returns.

Give the connection its own bound so the retry already implemented in
_get_embedding() can land on a healthy connection. On our deployment this took
worker durations from ~300 s, all finishing together at the timeout, to 1.4-85 s,
and indexing from 5.2 to 60-65 documents per minute.

Fixes nextcloud#345

Signed-off-by: Baki Burak Öğün <63836730+bakiburakogun@users.noreply.github.com>
@bakiburakogun

Copy link
Copy Markdown
Author

Please hold this one for a moment — see #345 (comment).

Running with this change I still caught workers stuck for 28 minutes in the HTTP/2 handshake exchange (_post_conn / __exchange_until), which sits after the socket connect and before the read, so neither timeout bounds it. The timeout here is still worth having, but on its own it does not fix what #345 describes and I did not want the PR to claim more than it does.

Forcing HTTP/1.1 for the embedding request removed the stall on our four instances (1 of 4 nodes working before, 4 of 4 after). Happy to extend this PR with that, or to make the protocol configurable instead — whichever shape you prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A stalled connection to the embedding service blocks the whole indexing batch for request_timeout

1 participant