Skip to content

Run unit tests in CI - #14

Merged
merlimat merged 4 commits into
apache:mainfrom
merlimat:enable-unit-tests
Oct 1, 2022
Merged

Run unit tests in CI#14
merlimat merged 4 commits into
apache:mainfrom
merlimat:enable-unit-tests

Conversation

@merlimat

Copy link
Copy Markdown
Contributor

Motivation

Compile and run unit tests against a standalone Pulsar service running inside a docker container.

@merlimat merlimat added this to the 3.0.0 milestone Sep 30, 2022
@merlimat
merlimat merged commit 27caf45 into apache:main Oct 1, 2022
@merlimat
merlimat deleted the enable-unit-tests branch October 1, 2022 16:46
BewareMyPower referenced this pull request in BewareMyPower/pulsar-client-cpp Jan 5, 2023
Fixes apache#167

### Motivation

Here are some debugging info when the segfault happened in
`testCloseClient`. The outputs have been trimmed to make them clear.

An example crash at `async_write`:

```
#12 0x00007ffff7496dad in basic_stream_socket<...>::boost::asio::async_write /usr/include/boost/asio/impl/write.hpp:512
#13 0x00007ffff748e003 in ClientConnection::asyncWrite lib/ClientConnection.h:245
#14 0x00007ffff746e0b6 in ClientConnection::handleHandshake (this=0x555555e689d0) lib/ClientConnection.cc:502
```

Another example crash at `async_receive`:

```
#6  0x00007ffff7497247 in basic_stream_socket<...>::async_receive /usr/include/boost/asio/basic_stream_socket.hpp:677
#7  0x00007ffff748e647 in ClientConnection::asyncReceive lib/ClientConnection.h:258
#8  0x00007ffff746fa5d in ClientConnection::readNextCommand lib/ClientConnection.cc:606
```

The frame where it crashed:

```
245       if (descriptor_data->shutdown_)
(gdb) p descriptor_data
$2 = (boost::asio::detail::epoll_reactor::per_descriptor_data &) @0x555555e4a780: 0x0
```

We can see the socket descriptor is `nullptr`. The root cause is when
`async_receive` or `async_write` is called, the `io_service` object
might be closed. This case happened when `createProducerAsync` is
called, the actual producer creation continues in another thread, while
the `client.close()` happens in the current thread.

### Modifications

Check if the `ClientConnection` is closed before `async_receive` or
`async_write`. To avoid the use of lock, changing the `state_` field to
atomic.

### Verifications

```bash
./tests/pulsar-tests --gtest_filter='ClientTest.testCloseClient' --gtest_repeat=20
```

It never crashed after applying this patch.
RobertIndie pushed a commit that referenced this pull request Jan 13, 2023
Fixes #167

### Motivation

Here are some debugging info when the segfault happened in
`testCloseClient`. The outputs have been trimmed to make them clear.

An example crash at `async_write`:

```
#12 0x00007ffff7496dad in basic_stream_socket<...>::boost::asio::async_write /usr/include/boost/asio/impl/write.hpp:512
#13 0x00007ffff748e003 in ClientConnection::asyncWrite lib/ClientConnection.h:245
#14 0x00007ffff746e0b6 in ClientConnection::handleHandshake (this=0x555555e689d0) lib/ClientConnection.cc:502
```

Another example crash at `async_receive`:

```
#6  0x00007ffff7497247 in basic_stream_socket<...>::async_receive /usr/include/boost/asio/basic_stream_socket.hpp:677
#7  0x00007ffff748e647 in ClientConnection::asyncReceive lib/ClientConnection.h:258
#8  0x00007ffff746fa5d in ClientConnection::readNextCommand lib/ClientConnection.cc:606
```

The frame where it crashed:

```
245       if (descriptor_data->shutdown_)
(gdb) p descriptor_data
$2 = (boost::asio::detail::epoll_reactor::per_descriptor_data &) @0x555555e4a780: 0x0
```

We can see the socket descriptor is `nullptr`. The root cause is when
`async_receive` or `async_write` is called, the `io_service` object
might be closed. This case happened when `createProducerAsync` is
called, the actual producer creation continues in another thread, while
the `client.close()` happens in the current thread.

### Modifications

Check if the `ClientConnection` is closed before `async_receive` or
`async_write`. To avoid the use of lock, changing the `state_` field to
atomic.

### Verifications

```bash
./tests/pulsar-tests --gtest_filter='ClientTest.testCloseClient' --gtest_repeat=20
```

It never crashed after applying this patch.
merlimat pushed a commit that referenced this pull request Jan 19, 2023
Fixes #167

### Motivation

Here are some debugging info when the segfault happened in
`testCloseClient`. The outputs have been trimmed to make them clear.

An example crash at `async_write`:

```
#12 0x00007ffff7496dad in basic_stream_socket<...>::boost::asio::async_write /usr/include/boost/asio/impl/write.hpp:512
#13 0x00007ffff748e003 in ClientConnection::asyncWrite lib/ClientConnection.h:245
#14 0x00007ffff746e0b6 in ClientConnection::handleHandshake (this=0x555555e689d0) lib/ClientConnection.cc:502
```

Another example crash at `async_receive`:

```
#6  0x00007ffff7497247 in basic_stream_socket<...>::async_receive /usr/include/boost/asio/basic_stream_socket.hpp:677
#7  0x00007ffff748e647 in ClientConnection::asyncReceive lib/ClientConnection.h:258
#8  0x00007ffff746fa5d in ClientConnection::readNextCommand lib/ClientConnection.cc:606
```

The frame where it crashed:

```
245       if (descriptor_data->shutdown_)
(gdb) p descriptor_data
$2 = (boost::asio::detail::epoll_reactor::per_descriptor_data &) @0x555555e4a780: 0x0
```

We can see the socket descriptor is `nullptr`. The root cause is when
`async_receive` or `async_write` is called, the `io_service` object
might be closed. This case happened when `createProducerAsync` is
called, the actual producer creation continues in another thread, while
the `client.close()` happens in the current thread.

### Modifications

Check if the `ClientConnection` is closed before `async_receive` or
`async_write`. To avoid the use of lock, changing the `state_` field to
atomic.

### Verifications

```bash
./tests/pulsar-tests --gtest_filter='ClientTest.testCloseClient' --gtest_repeat=20
```

It never crashed after applying this patch.
BewareMyPower added a commit that referenced this pull request Sep 20, 2023
### Motivation

We observed server null `ClientConnection` accesses in test environment.
See the `this=0x0` outputs in the following two typical stacks.

```
#8  bytesWritten (this=0xb8, size=371) at lib/SharedBuffer.h:166
#9  pulsar::ClientConnection::handleRead (this=0x0, err=..., bytesTransferred=371, minReadSize=4) at lib/ClientConnection.cc:609
```

```
#12 0x00007f33202933d2 in unique_lock (__m=..., this=0x7f3311c82800) at /opt/rh/devtoolset-7/root/usr/include/c++/7/bits/std_mutex.h:197
#13 pulsar::ClientConnection::sendPendingCommands (this=0x0) at lib/ClientConnection.cc:1071
#14 0x00007f3320293d2d in pulsar::ClientConnection::handleSendPair (this=0x0, err=...) at lib/ClientConnection.cc:1066
```

Though `shared_from_this()` is always passed to the `std::bind`
function, when the method of `ClientConnection` is called, the pointer
is still `null`.

### Modifications

First, replace all `std::bind` calls with the lambda expression that
catches `std::weak_ptr<ClientConnection>` and perform null checks
explicitly on the value returned by the `lock()` method.

Since now all asio callbacks don't hold a `shared_ptr`, the owner of
the `ClientConnection` object should be `ConnectionPool`, i.e. the pool
maintains some connections, while all asio callbacks use `weak_ptr` to
test if the connection is present.

Second, make `ClientConnection::getConnection` return `shared_ptr`
rather than `weak_ptr` so that the caller side does not need to check if
`lock()` returns null in the callback of this future.

We cannot make `ConnectionPool::getConnectionAsync` return `shared_ptr`
because it could return the future of `connectPromise_`, which is hold
by `ClientConnection` itself. We should avoid holding a `shared_ptr` of
`ClientConnection` because its owner is `ConnectionPool`.
@BewareMyPower BewareMyPower mentioned this pull request Mar 9, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants