Skip to content

Commit f2097d8

Browse files
authored
Fix invocation retry logic (#413)
In the current code-base, we could retry invocations in the following cases. - In the `_do_invoke` function, when a target connection for the invocation cannot be determined. This may happen in any other thread than the reactor thread. But, in this case, a message is not sent from the client (even, a message is not even put into one of the write queue of a connection). Hence, the `sent_connection` of such invocations is `None`. - When an exceptional response is received from the server, which always happen in the reactor thread. - When a connection is closed, and we detect it in the clean resources timer, which also always happens in the reactor thread. Under this scenario, it was not possible for an invocation to be retried multiple times, once for the exceptional response and one for the connection close case, like the Java client. But, there were a couple of problematic scenarios. - When we schedule an invocation for retry, we were not resetting the `sent_connection`. So, if the `sent_connection` is closed in the meanwhile, it was possible to schedule `invocation_retry_pause / clean_resources_timer_period` retries. Note that, an invocation retry which will happen in the future does not care about its last `sent_connection`. - If we schedule an invocation for a retry and somehow, its result is set in the meanwhile, while we are waiting for the `invocation_retry_pause`, we will retry the invocation anyway. Now, we check whether or not an invocation is done, before retrying it. To tackle 1, we reset the `sent_connection` before scheduling the retry. To tackle 2, we check the invocation's future before retrying.
1 parent c82f449 commit f2097d8

File tree

1 file changed

+6
-1
lines changed

1 file changed

+6
-1
lines changed

hazelcast/invocation.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,14 @@ def _notify_error(self, invocation, error):
270270
self._complete_with_error(invocation, error)
271271
return
272272

273-
invoke_func = functools.partial(self._do_invoke, invocation)
273+
invocation.sent_connection = None
274+
invoke_func = functools.partial(self._retry_if_not_done, invocation)
274275
self._reactor.add_timer(self._invocation_retry_pause, invoke_func)
275276

277+
def _retry_if_not_done(self, invocation):
278+
if not invocation.future.done():
279+
self._do_invoke(invocation)
280+
276281
def _should_retry(self, invocation, error):
277282
if invocation.connection and isinstance(error, (IOError, TargetDisconnectedError)):
278283
return False

0 commit comments

Comments
 (0)