mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-23 18:10:39 +00:00
Decrease the default connection timeout to 3s to better align with the default CQL query timeout of 10s. The previous timeout allowed only one failover request in high availability scenario before hitting the CQL query timeout. By decreasing the timeout to 3s, we can perform up to three failover requests within the CQL query timeout, which significantly improves the chances of successfully completing the query in high availability scenarios. Fixes: SCYLLADB-95
257 lines
10 KiB
C++
257 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2025-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1
|
|
*/
|
|
|
|
#include <seastar/http/common.hh>
|
|
#include "vector_search/client.hh"
|
|
#include "vector_search/utils.hh"
|
|
#include "vs_mock_server.hh"
|
|
#include "unavailable_server.hh"
|
|
#include "utils.hh"
|
|
#include "utils/rjson.hh"
|
|
#include <boost/test/tools/old/interface.hpp>
|
|
#include <seastar/testing/test_case.hh>
|
|
#include <seastar/coroutine/as_future.hh>
|
|
|
|
using namespace seastar;
|
|
using namespace vector_search;
|
|
using namespace test::vector_search;
|
|
using namespace seastar::httpd;
|
|
using namespace std::chrono_literals;
|
|
|
|
namespace {
|
|
|
|
logging::logger client_test_logger("client_test");
|
|
|
|
const auto UNREACHABLE_NODE_DETECTION_WINDOW = utils::updateable_value<uint32_t>{std::chrono::milliseconds(test::vector_search::STANDARD_WAIT).count()};
|
|
constexpr auto PATH = "/api/v1/indexes/ks/idx/ann";
|
|
constexpr auto CONTENT = R"({"vector": [0.1, 0.2, 0.3], "limit": 10})";
|
|
|
|
template <typename Server>
|
|
client::endpoint_type make_endpoint(const std::unique_ptr<Server>& server) {
|
|
return client::endpoint_type{server->host(), server->port(), seastar::net::inet_address(server->host())};
|
|
}
|
|
|
|
future<std::unique_ptr<vs_mock_server>> make_available(std::unique_ptr<unavailable_server>& down_server) {
|
|
// Replace the unavailable server with an available one.
|
|
auto server = std::make_unique<vs_mock_server>();
|
|
co_await server->start(co_await down_server->take_socket());
|
|
co_return server;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
SEASTAR_TEST_CASE(is_up_after_construction) {
|
|
auto server = co_await make_vs_mock_server();
|
|
client client{client_test_logger, make_endpoint(server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
BOOST_CHECK(client.is_up());
|
|
|
|
co_await client.close();
|
|
co_await server->stop();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(is_up_when_server_returned_ok_status) {
|
|
abort_source_timeout as;
|
|
auto server = co_await make_vs_mock_server();
|
|
client client{client_test_logger, make_endpoint(server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as.reset());
|
|
|
|
BOOST_CHECK(client.is_up());
|
|
BOOST_CHECK(res);
|
|
|
|
co_await client.close();
|
|
co_await server->stop();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(is_up_when_server_returned_client_error_status) {
|
|
abort_source_timeout as;
|
|
auto server = co_await make_vs_mock_server();
|
|
server->next_ann_response(vs_mock_server::response{seastar::http::reply::status_type::bad_request, "Bad request"});
|
|
client client{client_test_logger, make_endpoint(server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as.reset());
|
|
|
|
BOOST_CHECK(client.is_up());
|
|
BOOST_CHECK(res);
|
|
BOOST_CHECK_EQUAL(res.value().status, seastar::http::reply::status_type::bad_request);
|
|
|
|
co_await client.close();
|
|
co_await server->stop();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(is_up_when_request_is_aborted) {
|
|
abort_source as;
|
|
auto server = co_await make_vs_mock_server();
|
|
server->next_ann_response(vs_mock_server::response{seastar::http::reply::status_type::ok, "{}"});
|
|
client client{client_test_logger, make_endpoint(server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
as.request_abort();
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as);
|
|
|
|
BOOST_CHECK(client.is_up());
|
|
BOOST_CHECK(!res);
|
|
BOOST_CHECK(std::holds_alternative<aborted_error>(res.error()));
|
|
|
|
co_await client.close();
|
|
co_await server->stop();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(is_up_when_server_returned_server_error_status) {
|
|
abort_source_timeout as;
|
|
auto server = co_await make_vs_mock_server();
|
|
server->next_ann_response(vs_mock_server::response{seastar::http::reply::status_type::internal_server_error, "Internal Server Error"});
|
|
|
|
client client{client_test_logger, make_endpoint(server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as.reset());
|
|
|
|
BOOST_CHECK(client.is_up());
|
|
BOOST_CHECK(res);
|
|
BOOST_CHECK(res->status == seastar::http::reply::status_type::internal_server_error);
|
|
|
|
co_await client.close();
|
|
co_await server->stop();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(is_up_when_server_returned_service_unavailable_status) {
|
|
abort_source_timeout as;
|
|
auto server = co_await make_vs_mock_server();
|
|
server->next_ann_response(vs_mock_server::response{seastar::http::reply::status_type::service_unavailable, "Service Unavailable"});
|
|
|
|
client client{client_test_logger, make_endpoint(server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as.reset());
|
|
|
|
BOOST_CHECK(client.is_up());
|
|
BOOST_CHECK(res);
|
|
BOOST_CHECK(res->status == seastar::http::reply::status_type::service_unavailable);
|
|
|
|
co_await client.close();
|
|
co_await server->stop();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(is_down_when_server_is_not_available) {
|
|
abort_source_timeout as;
|
|
auto down_server = co_await make_unavailable_server();
|
|
client client{client_test_logger, make_endpoint(down_server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as.reset());
|
|
|
|
BOOST_CHECK(!client.is_up());
|
|
BOOST_CHECK(!res);
|
|
BOOST_CHECK(std::holds_alternative<service_unavailable_error>(res.error()));
|
|
|
|
co_await client.close();
|
|
co_await down_server->stop();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(becomes_up_when_server_status_is_serving) {
|
|
abort_source_timeout as;
|
|
auto down_server = co_await make_unavailable_server();
|
|
client client{client_test_logger, make_endpoint(down_server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as.reset());
|
|
auto server = co_await make_available(down_server);
|
|
server->next_status_response(vs_mock_server::response{seastar::http::reply::status_type::ok, rjson::quote_json_string("SERVING")});
|
|
|
|
auto became_up = co_await repeat_until([&client]() -> future<bool> {
|
|
co_return client.is_up();
|
|
});
|
|
BOOST_CHECK(became_up);
|
|
|
|
co_await client.close();
|
|
co_await server->stop();
|
|
co_await down_server->stop();
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(remains_down_when_server_status_is_not_serving) {
|
|
abort_source_timeout as;
|
|
std::vector<sstring> non_serving_statuses{
|
|
"INITIALIZING",
|
|
"CONNECTING_TO_DB",
|
|
"BOOTSTRAPPING",
|
|
};
|
|
for (auto const& status : non_serving_statuses) {
|
|
auto down_server = co_await make_unavailable_server();
|
|
client client{client_test_logger, make_endpoint(down_server), UNREACHABLE_NODE_DETECTION_WINDOW, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as.reset());
|
|
BOOST_CHECK(!res);
|
|
auto server = co_await make_available(down_server);
|
|
server->next_status_response(vs_mock_server::response{seastar::http::reply::status_type::ok, rjson::quote_json_string(status)});
|
|
|
|
auto got_2_status_requests = co_await repeat_until([&]() -> future<bool> {
|
|
// waiting for 2 status requests to be sure that node had a chance to become up
|
|
co_return server->status_requests().size() >= 2;
|
|
});
|
|
BOOST_CHECK(got_2_status_requests);
|
|
BOOST_CHECK(!client.is_up());
|
|
|
|
co_await client.close();
|
|
co_await server->stop();
|
|
co_await down_server->stop();
|
|
}
|
|
}
|
|
|
|
SEASTAR_TEST_CASE(is_down_when_connection_times_out) {
|
|
abort_source_timeout as;
|
|
auto unreachable = co_await make_unreachable_socket();
|
|
client client{client_test_logger, client::endpoint_type{unreachable.host, unreachable.port, seastar::net::inet_address(unreachable.host)},
|
|
utils::updateable_value<uint32_t>{5000}, shared_ptr<seastar::tls::certificate_credentials>{}};
|
|
|
|
auto res = co_await client.request(operation_type::POST, PATH, CONTENT, as.reset());
|
|
|
|
BOOST_CHECK(!client.is_up());
|
|
BOOST_CHECK(!res);
|
|
BOOST_CHECK(std::holds_alternative<service_unavailable_error>(res.error()));
|
|
|
|
co_await unreachable.close();
|
|
co_await client.close();
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(test_get_keepalive_parameters) {
|
|
|
|
auto params_10s = get_keepalive_parameters(10s);
|
|
BOOST_CHECK_EQUAL(params_10s.idle.count(), 4);
|
|
BOOST_CHECK_EQUAL(params_10s.interval.count(), 2);
|
|
BOOST_CHECK_EQUAL(params_10s.count, 3);
|
|
|
|
auto params_5s = get_keepalive_parameters(5s);
|
|
BOOST_CHECK_EQUAL(params_5s.idle.count(), 2);
|
|
BOOST_CHECK_EQUAL(params_5s.interval.count(), 1);
|
|
BOOST_CHECK_EQUAL(params_5s.count, 3);
|
|
|
|
auto params_3s = get_keepalive_parameters(3s);
|
|
BOOST_CHECK_EQUAL(params_3s.idle.count(), 1);
|
|
BOOST_CHECK_EQUAL(params_3s.interval.count(), 1);
|
|
BOOST_CHECK_EQUAL(params_3s.count, 2);
|
|
|
|
auto params_2s = get_keepalive_parameters(2s);
|
|
BOOST_CHECK_EQUAL(params_2s.idle.count(), 1);
|
|
BOOST_CHECK_EQUAL(params_2s.interval.count(), 1);
|
|
BOOST_CHECK_EQUAL(params_2s.count, 1);
|
|
|
|
auto params_1s = get_keepalive_parameters(1s);
|
|
BOOST_CHECK_EQUAL(params_1s.idle.count(), 0);
|
|
BOOST_CHECK_EQUAL(params_1s.interval.count(), 1);
|
|
BOOST_CHECK_EQUAL(params_1s.count, 1);
|
|
|
|
// For timeout < 1s the function should return minimal valid parameters.
|
|
auto params_500ms = get_keepalive_parameters(std::chrono::milliseconds(500));
|
|
BOOST_CHECK_GE(params_500ms.idle.count(), 0);
|
|
BOOST_CHECK_GE(params_500ms.interval.count(), 1);
|
|
BOOST_CHECK_GE(static_cast<int>(params_500ms.count), 1);
|
|
|
|
// For timeout < 1s the function should return minimal valid parameters.
|
|
auto params_0ms = get_keepalive_parameters(std::chrono::milliseconds(0));
|
|
BOOST_CHECK_GE(params_0ms.idle.count(), 0);
|
|
BOOST_CHECK_GE(params_0ms.interval.count(), 1);
|
|
BOOST_CHECK_GE(static_cast<int>(params_0ms.count), 1);
|
|
}
|