Skip to content

Conversation

CuriousGeorgiy
Copy link
Member

@CuriousGeorgiy CuriousGeorgiy commented Sep 21, 2025

This patchset performs a major refactoring of the client code to replace all internal Connection usage with ConnectionImpl, and fixes several related bugs.

Closes #51
Closes #132
Closes #140
Closes #142

`Connection`s model shared pointers on `ConnectionImpl` and are intended
for external usage by library consumers. Using them internally in the
Connector can cause bugs. For instance, we internally store `Connection`
objects in the LibevNetProvider, which adds an additional reference to them
and prevents them from being deleted, even when all user objects are dead.

To avoid this, let's internally use `ConnectionImpl` to model weak
pointers. We rely on the fact that the lifetime of these weak pointers is
tied to the lifetime of the shared user objects.

The ideal solution would be to remove the `Connection`<-`ConnectionImpl`
constructor, but we still need to return new Connection objects in methods
like waitAny, so let's just enforce as a rule that we should not use
`Connection` objects internally.

While we are here, let's also fix all the formating issues that the linter
is reporting for the refactored code.

Closes tarantool#140
Currently, to avoid double-close of a connection we check for the `SS_DEAD`
status on its stream. However, the `SS_DEAD` status can be set for a
multitude of reasons without the connection being closed or even connected.
For instance, we set it if a send/recv failed unrecoverably.

To fix this, let's rely on the fact that a connection is open iff the file
descriptor of its stream is a valid (i.e., not -1). This approach seems to
be portable to the Windows platform too.

Closes tarantool#142
Currently, all connection bookkeeping is done opaquely through the network
providers which, in turn, also do this bookkeeping opaquely using system
interfaces (e.g., libev, epoll). Because of this, we cannot handle cases
when waitAny is called and there are no connections (tarantoolgh-51) or when a
connection has ready responses (tarantoolgh-132). In order to improve
`waitAny` robustness, we need to add connection bookkeeping to Connector.

We should move the timer start to the beginning of the waiting loop, since
the preceding checking overhead should not be accounted for the waiting
time.

Closes tarantool#51
Needed for tarantool#132
Currently, `waitAny` does not account for connections that already have
ready responses. Let's handle this by going through the set of owned
connections and checking them for ready responses. We should do this before
the timer start, since the checking overhead should not be accounted for
the waiting time.

Closes tarantool#132
Copy link
Collaborator

@drewdzzz drewdzzz left a comment

Choose a reason for hiding this comment

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

Thank you for the patch! I believe it improves connector usability quite a lot.

{
assert(refs == 0);
if (!strm.has_status(SS_DEAD)) {
if (strm.get_fd() != -1) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: We rely here that closed connection always had fd equal to -1. We should describe it in UnixStream class (to fixate this behavior) or introduce new stem.is_closed() method.

Timer timer{timeout};
timer.start();
while (m_ReadyToDecode.empty()) {
bool any_conn_has_no_err = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: wouldn't has_alive_conn be better? Too many logical negations with this name :)

bool any_conn_has_no_err = false;
for (auto *conn : m_Connections) {
if (!conn->hasError())
any_conn_has_no_err = true;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe we should break here.

LOG_DEBUG("waitAny() called on connector without connections");
return std::nullopt;
}
for (auto *conn : m_Connections) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think waitAny still should trigger net provider to send pending requests (m_NetProvider.wait(0)).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this change be tested somehow?

@drewdzzz drewdzzz assigned CuriousGeorgiy and unassigned drewdzzz Oct 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants