Closed RavikumarTulugu closed 2 weeks ago
just a bit of more information, the query in our case returns empty results.
I am using mariadb server on linux.
The query in my case is like below
SELECT * FROM room WHERE JSON_CONTAINS ( MEMBER_LIST, ? ) AND OWNER != ?
The member list is an array of strings.
If the failure only happens sometimes, you may be violating this precondition. Remember that a single connection shouldn't be used to initiate any async operation until the one it's currently executing completes.
To validate this hypothesis, you can add a flag that gets set when an operation starts and cleared when it finishes.
If this is the case, you may need to introduce synchronization, maybe using a channel. Let me know if you need help with this.
Let me know if it helps.
Regards, Ruben.
okay let me verify and get back, thanks for the reply.
Thanks again for the insights, indeed the parallel execution was the case, do you have any sample code on this channel stuff ?
This has come up before, so I hope I can implement #367 for Boost 1.88.
You need an object that provides asynchronous mutual exclusion (an async mutex, if you prefer). With ideas taken from the first 2 examples in this Asio page:
asio::awaitable<void> coro_main(
mysql::tcp_connection& conn,
asio::experimental::channel<void()>& lock
)
{
// Claim the lock by sending a message to the channel. Since the
// channel signature is void(), there are no arguments to send in the
// message itself.
co_await lock.async_send();
// Issue the SQL query to the server
const char* sql = "SELECT 'Hello world!'";
mysql::results result;
co_await conn.async_execute(sql, result);
// Release the lock by receiving the message back again.
lock.try_receive([](auto...) {});
}
The channel must be created like this:
asio::experimental::channel<void()> lock(ctx, 1); // ctx is your io_context
The idea is that you create a channel with a buffer size of 1 element. If the lock is free, the channel's buffer will be empty and async_send
will succeed immediately. If it's not, the channel's buffer will be full, and async_send
will block until the operation holding the lock releases it with try_receive
.
Some notes:
Another option is using Boost.MySQL connection_pool
, which implements similar semantics. If you only need 1 connection, you can still use it by setting pool_params::max_size
to 1. You would get re-connection logic and pinging for free. The only caveat is that the feature is still experimental in 1.86, and has undergone some changes in 1.87 (currently in beta, will be released in some weeks), which makes it stable. If you're interested, let me know and I'll write something that avoids the breaking changes. Starting in 1.87, connection_pool
should likely be what you want.
Regards, Ruben.
To make the code clean and easily understandable, I would go with the connection pool. please point me to the patches if any so that i could apply them on my local tree and get the feature. Hope, things are stable and robust.
I'd recommend using the Boost 1.87 beta: https://archives.boost.io/beta/1.87.0.beta1/source/boost_1_87_0_b1_rc2.tar.gz It has the same structure as a regular release. There won't be any breaking changes from the beta to the release.
This is how I'd use the pool:
// In main
// Pool configuration
mysql::pool_params params {
.server_address = mysql::host_and_port("your-hostname"),
.username = "your-username",
.password = "your-password",
.database = "your-database",
.max_size = 1, // Set to the max number of connections you want. You probably want more than 1
};
// Create the pool
mysql::connection_pool pool (ctx, std::move(params));
// Run it. Only needed once per pool
pool.async_run(asio::detached);
// Call pool.cancel() to make run exit when your program
// needs to quit, e.g. from a signal handler
asio::awaitable<void> coro_main(
mysql::connection_pool& pool
)
{
// Get a connection. This is a proxy to an any_connection.
// If all connections are in use, this will wait until one is free.
// By default, this waits forever, so it may be adequate to set a timeout
// (maybe to the entire coroutine) using asio::cancel_after
mysql::pooled_connection conn = co_await pool.async_get_connection();
// Use it normally
mysql::results result;
co_await conn->async_execute("SELECT...", result);
// conn's destructor returns it to the pool
}
Hope it helps.
I am using boost 1.86 and seeing a random assert failure at times. the same query works fine but asserts at times. wondering what is the cause for below assert failure,
/work/opensource//base/boost_1_86_0/boost/mysql/impl/internal/sansio/read_buffer.hpp:90: void boost::mysql::detail::read_buffer::move_to_pending(std::size_t): Assertion `length <= free_size()' failed.
.