unit test: Add unit test for per user sla syntax
This commit adds the infrastructure needed to test per user sla, more specificaly, a service level accessor that triggers the update_service_levels_from_distributed_data function uppon any change to the dystributed sla data. A test was added that indirectly consumes this infrastructure by changing the distributed service level data with cql queries. Message-Id: <23b2211e409446c4f4e3e57b00f78d9ff75fc978.1609249294.git.sarna@scylladb.com>
This commit is contained in:
committed by
Piotr Sarna
parent
2701481cbc
commit
144fe02c23
@@ -54,6 +54,7 @@
|
||||
#include <regex>
|
||||
#include "gms/feature.hh"
|
||||
#include "db/query_context.hh"
|
||||
#include "service/qos/qos_common.hh"
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
@@ -4881,3 +4882,81 @@ SEASTAR_THREAD_TEST_CASE(test_query_unselected_columns) {
|
||||
}
|
||||
}, std::move(cfg), thread_attributes{.sched_group = statement_sched_group}).get();
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(test_user_based_sla_queries) {
|
||||
return do_with_cql_env_thread([] (cql_test_env& e) {
|
||||
// test create service level with defaults
|
||||
e.execute_cql("CREATE SERVICE_LEVEL sl_1;").get();
|
||||
auto msg = e.execute_cql("LIST SERVICE_LEVEL sl_1;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{utf8_type->decompose("sl_1")},
|
||||
});
|
||||
e.execute_cql("CREATE SERVICE_LEVEL sl_2;").get();
|
||||
//drop service levels
|
||||
e.execute_cql("DROP SERVICE_LEVEL sl_1;").get();
|
||||
msg = e.execute_cql("LIST ALL SERVICE_LEVELS;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{utf8_type->decompose("sl_2")},
|
||||
});
|
||||
|
||||
// validate exceptions (illegal requests)
|
||||
BOOST_REQUIRE_THROW(e.execute_cql("DROP SERVICE_LEVEL sl_1;").get(), qos::nonexistant_service_level_exception);
|
||||
e.execute_cql("DROP SERVICE_LEVEL IF EXISTS sl_1;").get();
|
||||
|
||||
BOOST_REQUIRE_THROW(e.execute_cql("CREATE SERVICE_LEVEL sl_2;").get(), exceptions::invalid_request_exception);
|
||||
BOOST_REQUIRE_THROW(e.execute_cql("CREATE SERVICE_LEVEL sl_2;").get(), exceptions::invalid_request_exception);
|
||||
e.execute_cql("CREATE SERVICE_LEVEL IF NOT EXISTS sl_2;").get();
|
||||
|
||||
// test attach role
|
||||
e.execute_cql("ATTACH SERVICE_LEVEL sl_2 TO tester").get();
|
||||
msg = e.execute_cql("LIST ATTACHED SERVICE_LEVEL OF tester;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{utf8_type->decompose("tester"), utf8_type->decompose("sl_2")},
|
||||
});
|
||||
msg = e.execute_cql("LIST ALL ATTACHED SERVICE_LEVELS;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{utf8_type->decompose("tester"), utf8_type->decompose("sl_2")},
|
||||
});
|
||||
|
||||
// test attachment illegal request
|
||||
BOOST_REQUIRE_THROW(e.execute_cql("ATTACH SERVICE_LEVEL sl_2 TO tester2;").get(), auth::nonexistant_role);
|
||||
BOOST_REQUIRE_THROW(e.execute_cql("ATTACH SERVICE_LEVEL sl_1 TO tester;").get(), qos::nonexistant_service_level_exception);
|
||||
BOOST_CHECK(true);
|
||||
// tests detaching service levels
|
||||
e.execute_cql("CREATE ROLE tester2;").get();
|
||||
e.execute_cql("CREATE SERVICE_LEVEL sl_1;").get();
|
||||
e.execute_cql("ATTACH SERVICE_LEVEL sl_1 TO tester2;").get();
|
||||
e.execute_cql("DETACH SERVICE_LEVEL FROM tester;").get();
|
||||
msg = e.execute_cql("LIST ATTACHED SERVICE_LEVEL OF tester2;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{utf8_type->decompose("tester2"), utf8_type->decompose("sl_1")},
|
||||
});
|
||||
BOOST_CHECK(true);
|
||||
msg = e.execute_cql("LIST ATTACHED SERVICE_LEVEL OF tester;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
});
|
||||
BOOST_CHECK(true);
|
||||
msg = e.execute_cql("LIST ALL ATTACHED SERVICE_LEVELS;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{utf8_type->decompose("tester2"), utf8_type->decompose("sl_1")},
|
||||
});
|
||||
BOOST_CHECK(true);
|
||||
//test implicit detach when removing role
|
||||
e.execute_cql("DROP ROLE tester2;").get();
|
||||
msg = e.execute_cql("LIST ALL ATTACHED SERVICE_LEVELS;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
});
|
||||
BOOST_CHECK(true);
|
||||
e.execute_cql("ATTACH SERVICE_LEVEL sl_1 TO tester;").get();
|
||||
msg = e.execute_cql("LIST ALL ATTACHED SERVICE_LEVELS;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
{utf8_type->decompose("tester"), utf8_type->decompose("sl_1")},
|
||||
});
|
||||
BOOST_CHECK(true);
|
||||
//test implicit detach when removing service level
|
||||
e.execute_cql("DROP SERVICE_LEVEL sl_1;").get();
|
||||
msg = e.execute_cql("LIST ALL ATTACHED SERVICE_LEVELS;").get0();
|
||||
assert_that(msg).is_rows().with_rows({
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "test/lib/reader_permit.hh"
|
||||
#include "db/query_context.hh"
|
||||
#include "test/lib/test_services.hh"
|
||||
#include "unit_test_service_levels_accessor.hh"
|
||||
#include "db/view/view_builder.hh"
|
||||
#include "db/view/node_view_update_backlog.hh"
|
||||
#include "distributed_loader.hh"
|
||||
@@ -123,6 +124,7 @@ private:
|
||||
sharded<db::view::view_update_generator>& _view_update_generator;
|
||||
sharded<service::migration_notifier>& _mnotifier;
|
||||
sharded<cdc::generation_service>& _cdc_generation_service;
|
||||
sharded<qos::service_level_controller>& _sl_controller;
|
||||
private:
|
||||
struct core_local_state {
|
||||
service::client_state client_state;
|
||||
@@ -143,7 +145,7 @@ private:
|
||||
if (_db.local().has_keyspace(ks_name)) {
|
||||
_core_local.local().client_state.set_keyspace(_db.local(), ks_name);
|
||||
}
|
||||
return ::make_shared<service::query_state>(_core_local.local().client_state, empty_service_permit());
|
||||
return ::make_shared<service::query_state>(_core_local.local().client_state, empty_service_permit(), _sl_controller.local());
|
||||
}
|
||||
public:
|
||||
single_node_cql_env(
|
||||
@@ -154,7 +156,8 @@ public:
|
||||
sharded<db::view::view_builder>& view_builder,
|
||||
sharded<db::view::view_update_generator>& view_update_generator,
|
||||
sharded<service::migration_notifier>& mnotifier,
|
||||
sharded<cdc::generation_service>& cdc_generation_service)
|
||||
sharded<cdc::generation_service>& cdc_generation_service,
|
||||
sharded<qos::service_level_controller> &sl_controller)
|
||||
: _feature_service(feature_service)
|
||||
, _db(db)
|
||||
, _qp(qp)
|
||||
@@ -163,6 +166,7 @@ public:
|
||||
, _view_update_generator(view_update_generator)
|
||||
, _mnotifier(mnotifier)
|
||||
, _cdc_generation_service(cdc_generation_service)
|
||||
, _sl_controller(sl_controller)
|
||||
{ }
|
||||
|
||||
virtual future<::shared_ptr<cql_transport::messages::result_message>> execute_cql(sstring_view text) override {
|
||||
@@ -438,15 +442,27 @@ public:
|
||||
mm_notif.start().get();
|
||||
auto stop_mm_notify = defer([&mm_notif] { mm_notif.stop().get(); });
|
||||
|
||||
sharded<auth::service> auth_service;
|
||||
|
||||
set_abort_on_internal_error(true);
|
||||
const gms::inet_address listen("127.0.0.1");
|
||||
auto sys_dist_ks = seastar::sharded<db::system_distributed_keyspace>();
|
||||
auto sl_controller = sharded<qos::service_level_controller>();
|
||||
sl_controller.start(std::ref(auth_service), qos::service_level_options{}).get();
|
||||
auto stop_sl_controller = defer([&sl_controller] { sl_controller.stop().get(); });
|
||||
sl_controller.invoke_on_all(&qos::service_level_controller::start).get();
|
||||
sl_controller.invoke_on_all([&sys_dist_ks, &sl_controller] (qos::service_level_controller& service) {
|
||||
qos::service_level_controller::service_level_distributed_data_accessor_ptr service_level_data_accessor =
|
||||
::static_pointer_cast<qos::service_level_controller::service_level_distributed_data_accessor>(
|
||||
make_shared<qos::unit_test_service_levels_accessor>(sl_controller,sys_dist_ks));
|
||||
return service.set_distributed_data_accessor(std::move(service_level_data_accessor));
|
||||
}).get();
|
||||
|
||||
sharded<netw::messaging_service> ms;
|
||||
// don't start listening so tests can be run in parallel
|
||||
ms.start(listen, std::move(7000)).get();
|
||||
auto stop_ms = defer([&ms] { ms.stop().get(); });
|
||||
|
||||
sharded<auth::service> auth_service;
|
||||
// Normally the auth server is already stopped in here,
|
||||
// but if there is an initialization failure we have to
|
||||
// make sure to stop it now or ~sharded will assert.
|
||||
@@ -454,7 +470,6 @@ public:
|
||||
auth_service.stop().get();
|
||||
});
|
||||
|
||||
auto sys_dist_ks = seastar::sharded<db::system_distributed_keyspace>();
|
||||
auto stop_sys_dist_ks = defer([&sys_dist_ks] { sys_dist_ks.stop().get(); });
|
||||
|
||||
gms::feature_config fcfg = gms::feature_config_from_db_config(*cfg, cfg_in.disabled_features);
|
||||
@@ -529,7 +544,7 @@ public:
|
||||
|
||||
sharded<cql3::query_processor> qp;
|
||||
cql3::query_processor::memory_config qp_mcfg = {memory::stats().total_memory() / 256, memory::stats().total_memory() / 2560};
|
||||
qp.start(std::ref(proxy), std::ref(db), std::ref(mm_notif), std::ref(mm), qp_mcfg, std::ref(cql_config)).get();
|
||||
qp.start(std::ref(proxy), std::ref(db), std::ref(mm_notif), std::ref(mm), qp_mcfg, std::ref(cql_config), std::ref(sl_controller)).get();
|
||||
auto stop_qp = defer([&qp] { qp.stop().get(); });
|
||||
|
||||
// In main.cc we call db::system_keyspace::setup which calls
|
||||
@@ -643,7 +658,7 @@ public:
|
||||
// The default user may already exist if this `cql_test_env` is starting with previously populated data.
|
||||
}
|
||||
|
||||
single_node_cql_env env(feature_service, db, qp, auth_service, view_builder, view_update_generator, mm_notif, cdc_generation_service);
|
||||
single_node_cql_env env(feature_service, db, qp, auth_service, view_builder, view_update_generator, mm_notif, cdc_generation_service, std::ref(sl_controller));
|
||||
env.start().get();
|
||||
auto stop_env = defer([&env] { env.stop().get(); });
|
||||
|
||||
|
||||
62
test/lib/unit_test_service_levels_accessor.hh
Normal file
62
test/lib/unit_test_service_levels_accessor.hh
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2021 ScyllaDB
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of Scylla.
|
||||
*
|
||||
* Scylla is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Scylla is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "service/qos/service_level_controller.hh"
|
||||
#include "service/qos/qos_common.hh"
|
||||
#include "db/system_distributed_keyspace.hh"
|
||||
#pragma once
|
||||
|
||||
namespace qos {
|
||||
|
||||
/**
|
||||
* This class is a helper for unit testing. It implements the service level distributed
|
||||
* accessor interface in order to be used in the unit testing environment. The advantage
|
||||
* of this class over the standard implementation is that it makes sure that updates are
|
||||
* Immediately propagated to the underlying service level controller.
|
||||
*/
|
||||
class unit_test_service_levels_accessor : public service_level_controller::service_level_distributed_data_accessor {
|
||||
sharded<service_level_controller> &_sl_controller;
|
||||
sharded<db::system_distributed_keyspace> &_sys_dist_ks;
|
||||
public:
|
||||
unit_test_service_levels_accessor(sharded<service_level_controller>& sl_controller, sharded<db::system_distributed_keyspace> &sys_dist_ks)
|
||||
: _sl_controller(sl_controller)
|
||||
, _sys_dist_ks(sys_dist_ks)
|
||||
{}
|
||||
virtual future<qos::service_levels_info> get_service_levels() const {
|
||||
return _sys_dist_ks.local().get_service_levels();
|
||||
}
|
||||
virtual future<qos::service_levels_info> get_service_level(sstring service_level_name) const {
|
||||
return _sys_dist_ks.local().get_service_level(service_level_name);
|
||||
}
|
||||
virtual future<> set_service_level(sstring service_level_name, qos::service_level_options slo) const {
|
||||
return _sys_dist_ks.local().set_service_level(service_level_name, slo).then([this] () {
|
||||
return _sl_controller.invoke_on_all(&service_level_controller::update_service_levels_from_distributed_data);
|
||||
});
|
||||
|
||||
}
|
||||
virtual future<> drop_service_level(sstring service_level_name) const {
|
||||
return _sys_dist_ks.local().drop_service_level(service_level_name).then([this] () {
|
||||
return _sl_controller.invoke_on_all(&service_level_controller::update_service_levels_from_distributed_data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user