Repository navigation
Conversation
6 tasks done
NorthArea
force-pushed
the
northarea/reconnect-loop-fixes
branch
from
October 27, 2026 14:05
7325a00 to
3273756
Compare
4 of 6 tasks
NorthArea
pushed a commit
that referenced
this pull request
Oct 28, 2026
The previous probe also required no `unreachable` replica and zero unknown buckets. In CI (tests-crud-integration 3.5.0, job 122708866002) it returned false for the whole 120 s window in RepositoryViaJavaConfigTest while bootstrap had succeeded, so the container failed to start. Requiring only an `available` master is not enough either. On a stand the router state stayed `storage-002 master=unreachable replica=available` for 120 s: the router connected to storage-002-a before its storage was configured, the vshard handshake failed with "storage is not configured", and vshard closed the connection (vshard/replicaset.lua conn_vconnect_check_or_close). Only a request to that master reopens it, and discovery and failover talk to the replica. The first asynchronous crud map call reopens it but does not wait for the new handshake, which is the VHANDSHAKE_NOT_COMPLETE seen in #139. Make a synchronous `callrw('vshard.storage.buckets_count')` to the master of every replicaset: it reopens a closed connection and waits for the handshake. Require all calls to succeed and every master to be `available`, and log call errors, replica and bucket states.
NorthArea
pushed a commit
that referenced
this pull request
Oct 29, 2026
The readiness probe added in 1.7.0 iterated `rs.replicas`, a field that `vshard.router.info()` does not return (vshard 0.1.25 reports `master` and `replica` per replicaset). The loop never ran, so the container was reported ready while a replicaset master was still in the vshard handshake, and the first `crud.truncate` in spring-data integration tests failed with VHANDSHAKE_NOT_COMPLETE (CI of #139). Checking the master status alone is not enough. On a stand the router state stayed `storage-002 master=unreachable replica=available` for 120 s: the router connected to storage-002-a before its storage was configured, the vshard handshake failed with "storage is not configured", and vshard closed the connection (vshard/replicaset.lua conn_vconnect_check_or_close). Only a request to that master reopens it; discovery and failover talk to the replica. The first asynchronous crud map call reopens it but does not wait for the new handshake. Requiring zero unknown buckets does not work either: the router may not know part of the buckets for a while after the masters are ready. Make a synchronous `callrw('vshard.storage.buckets_count')` to the master of every replicaset: it reopens a closed connection and waits for the handshake. Require all calls to succeed and every master to be `available`, and log call errors, replica and bucket states.
nickkkccc
pushed a commit
that referenced
this pull request
Oct 29, 2026
The readiness probe added in 1.7.0 iterated `rs.replicas`, a field that `vshard.router.info()` does not return (vshard 0.1.25 reports `master` and `replica` per replicaset). The loop never ran, so the container was reported ready while a replicaset master was still in the vshard handshake, and the first `crud.truncate` in spring-data integration tests failed with VHANDSHAKE_NOT_COMPLETE (CI of #139). Checking the master status alone is not enough. On a stand the router state stayed `storage-002 master=unreachable replica=available` for 120 s: the router connected to storage-002-a before its storage was configured, the vshard handshake failed with "storage is not configured", and vshard closed the connection (vshard/replicaset.lua conn_vconnect_check_or_close). Only a request to that master reopens it; discovery and failover talk to the replica. The first asynchronous crud map call reopens it but does not wait for the new handshake. Requiring zero unknown buckets does not work either: the router may not know part of the buckets for a while after the masters are ready. Make a synchronous `callrw('vshard.storage.buckets_count')` to the master of every replicaset: it reopens a closed connection and waits for the handshake. Require all calls to succeed and every master to be `available`, and log call errors, replica and bucket states.
NorthArea
force-pushed
the
northarea/reconnect-loop-fixes
branch
3 times, most recently
from
October 29, 2026 15:15
e6781c9 to
f0d30b1
Compare
bitgorbovsky
requested changes
Oct 29, 2026
NorthArea
force-pushed
the
northarea/reconnect-loop-fixes
branch
from
October 30, 2026 07:37
f0d30b1 to
a2941d2
Compare
added 5 commits
October 30, 2026 20:33
When a server accepts the TCP connection but does not send the greeting, the greeting timer closed the channel without setting a close reason. The close handler then treated it as a close by the server: it fired CLOSE_BY_REMOTE with "Connection closed by server" next to the TimeoutException of the connect future. The pool listens to both, so every failed attempt was handled twice and the reconnect chains multiplied: in one run of the new pool test, 12 attempts to a silent server scheduled 66 reconnects instead of one per attempt. Fail the connect promise with TimeoutException first and close the channel only if that succeeded, so a connection whose greeting has just arrived is never closed by the timer. Close it with a dedicated reason and do not fire a close event for it. The timer also stayed armed when the server closed the connection before the greeting, and then fired during the next attempt and closed its channel: in two runs of a test the second attempt was closed 220 and 326 ms after it started, with a 600 ms timeout. Let the timer close the channel only of the attempt it was scheduled for.
When a server, or a proxy in front of it, accepted the TCP connection and closed it before the greeting, the close was reported twice: the connect future failed with ConnectionClosedException, and the close handler also fired CLOSE_BY_REMOTE. The pool listens to both and scheduled a reconnect for each, so the reconnect chains multiplied: in one run of the new pool test, 50 attempts in 3 s scheduled 1325 reconnects. A connection that has not received the greeting has no requests in flight and no registered watchers, and its failure already reaches the caller through the connect future. Fire close events only for a connection that has received the greeting.
With graceful shutdown enabled, IProtoClientImpl.connect() called
watch("box.shutdown", this::shutdownEventCallback) on every attempt.
watch() appends the callback to the existing watcher and nothing
removes it, so a client that kept reconnecting to an unavailable server
accumulated one callback per attempt, and the event after a successful
connect ran the callback once per previous attempt.
The watcher itself survives reconnects and is re-subscribed by
updateWatchers(), so add the callback only on the first connect.
The pool.connect.time sample was stopped only when the connect future succeeded, so every failed attempt left an active sample behind: in the new test, 12 attempts to a silent server left 12 active samples. Stop the sample when the connect future completes either way.
TarantoolClientImpl calls pool.setGroups() before pool.setReconnectAfter(), so the connections were created with the pool default of 1000 ms. setReconnectAfter() changed only the pool field, and PoolEntry kept its final copy, so withReconnectAfter() and the reconnect-after property of Spring Data had no effect on these connections. Pass the new value to existing connections, as setConnectTimeout() already does.
NorthArea
force-pushed
the
northarea/reconnect-loop-fixes
branch
from
October 30, 2026 15:33
a2941d2 to
be673fb
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a server is unreachable, silent or closes the connection before the greeting, the client's reconnect loop grows instead of making one attempt per period. This PR fixes the causes.
fix(core): report greeting timeout as a timeout— a silent server was reported both asTimeoutExceptionand asCLOSE_BY_REMOTE, and the pool scheduled a reconnect for each. Now onlyTimeoutException. The greeting timer of a previous attempt no longer closes the next one.fix(core): skip close event before the greeting— the same double report when a server or proxy accepts and immediately closes. Close events are fired only after the greeting; the order from f7f34cd (stateCLOSEDfirst, then the event) is kept.fix(core): register box.shutdown callback once— with graceful shutdown, every attempt added one more callback.fix(pool): stop connect timer on failed attempt—pool.connect.timewas stopped only on success.fix(pool): pass reconnectAfter to pool entries—withReconnectAfter()and Spring Datareconnect-afterhad no effect; connections kept the 1000 ms default.Each fix has a test that fails without it; the greeting timeout tests are in
ConnectionToTarantoolTest.Если сервер недоступен, молчит или закрывает соединение до приветствия, цикл переподключения клиента разрастается вместо одной попытки за период. PR устраняет причины.
fix(core): report greeting timeout as a timeout— молчащий сервер сообщался и какTimeoutException, и какCLOSE_BY_REMOTE, и пул планировал переподключение на каждое. Теперь толькоTimeoutException. Таймер приветствия прошлой попытки больше не закрывает следующую.fix(core): skip close event before the greeting— тот же двойной отчёт, когда сервер или прокси принимает соединение и сразу закрывает. События закрытия шлются только после приветствия; порядок из f7f34cd (сначалаCLOSED, потом событие) сохранён.fix(core): register box.shutdown callback once— при graceful shutdown каждая попытка добавляла ещё один колбэк.fix(pool): stop connect timer on failed attempt—pool.connect.timeостанавливался только при успехе.fix(pool): pass reconnectAfter to pool entries—withReconnectAfter()иreconnect-afterв Spring Data не действовали, соединения оставались с умолчанием 1000 мс.У каждого исправления есть тест, который без него падает; тесты таймаута приветствия — в
ConnectionToTarantoolTest.I haven't forgotten about:
Replaces #138