#include #include using namespace panda; using namespace panda::unievent::http::manager; struct TestWorker: Worker { using Worker::Worker; using callback = function; callback kill_cb; callback term_cb; void fetch_state () override { } void terminate () override { if (term_cb) term_cb(); } void kill () override { if (kill_cb) kill_cb(); } }; struct TestMpm: Mpm { using Mpm::Mpm; uint32_t idle_cycles = 0; void run () override { auto_stop_loop(); Mpm::run(); } WorkerPtr create_worker () override { return std::make_unique(); } void terminate_worker(WorkerPtr& it) { worker_terminated(it.get()); } void auto_stop_loop() { idle_cycles = 5; idle_cycle(); } void idle_cycle() { if (idle_cycles > 0) { --idle_cycles; loop->delay([this]{ idle_cycle(); }); } else { loop->stop(); } } bool is_state_initial() { return state == State::initial; } bool is_state_running() { return state == State::running; } bool is_state_stopping() { return state == State::stopping; } bool is_state_stopped() { return state == State::stopped; } auto& get_check_timer() { return check_timer; } auto& get_workers() { return workers; } }; TEST_CASE("mpm", "[mpm]") { auto loop = panda::unievent::Loop::default_loop(); auto cfg = Mpm::Config{}; cfg.server.locations = { {"127.0.0.1", 0} }; SECTION("run + start-event") { cfg.min_servers = 2; TestMpm mpm(cfg, loop, loop); CHECK(mpm.is_state_initial()); int started = 0; mpm.start_event.add([&](auto...){ ++started; }); mpm.run(); CHECK(mpm.is_state_running()); CHECK(started == 1); auto& workers = mpm.get_workers(); CHECK(workers.size() == 2); } SECTION("stop + worker_terminated") { cfg.min_servers = 2; TestMpm mpm(cfg, loop, loop); mpm.run(); CHECK(mpm.is_state_running()); auto& workers = mpm.get_workers(); CHECK(workers.size() == 2); mpm.stop(); while (!workers.empty()) { mpm.terminate_worker(workers.begin()->second); } CHECK(workers.size() == 0); } SECTION("kill inactive workers") { cfg.max_servers = 1; cfg.activity_timeout = 1; TestMpm mpm(cfg, loop, loop); mpm.run(); bool killed = false; auto w = static_cast(mpm.get_workers().begin()->second.get()); w->kill_cb = [&](){ killed = true; }; w->activity_time = 0; w->state = Worker::State::running; mpm.get_check_timer()->call_now(); CHECK(killed); CHECK(mpm.get_workers().size() > 0); } SECTION("autorestart workers") { cfg.max_servers = 1; cfg.max_requests = 1; TestMpm mpm(cfg, loop, loop); mpm.run(); auto w = static_cast(mpm.get_workers().begin()->second.get()); w->total_requests = 2; w->creation_time = 0; w->state = Worker::State::running; mpm.get_check_timer()->call_now(); CHECK(mpm.get_workers().size() == 2); CHECK(w->state == Worker::State::restarting); SECTION("terminate_restared_workers (simple)") { bool terminated = false; w->term_cb = [&](){ terminated = true; }; mpm.get_check_timer()->call_now(); CHECK(terminated); } } SECTION("load average") { // to spawn: round_up(1/0.3) - 1 = 3; cfg.max_servers = 5; cfg.max_load = 0.3; TestMpm mpm(cfg, loop, loop); mpm.run(); REQUIRE(mpm.get_workers().size() == 1); auto w = static_cast(mpm.get_workers().begin()->second.get()); w->load_average = 1; w->state = Worker::State::running; mpm.get_check_timer()->call_now(); CHECK(mpm.get_workers().size() == 4); int terminated = 0; SECTION("back to min") { for(auto& it: mpm.get_workers()) { auto w = static_cast(it.second.get()); w->state = Worker::State::running; w->load_average = 0; w->creation_time = 0; // to terminate; w->term_cb = [&](){ ++terminated; }; } mpm.get_check_timer()->call_now(); CHECK(terminated == 3); } } SECTION("reconfigure") { TestMpm mpm(cfg, loop, loop); cfg.check_interval = 1; mpm.run(); auto& workers = mpm.get_workers(); CHECK(workers.size() == 1); auto w1 = static_cast(workers.begin()->second.get()); w1->state = Worker::State::running; auto cfg2 = cfg; cfg2.check_interval = 2; mpm.reconfigure(cfg2); mpm.auto_stop_loop(); loop->run(); REQUIRE(workers.size() == 2); auto w2 = static_cast(workers.at(w1->id + 1).get()); CHECK(w1->state == Worker::State::terminating); CHECK(w2->state == Worker::State::running); mpm.reconfigure(cfg); mpm.auto_stop_loop(); loop->run(); REQUIRE(workers.size() == 3); auto w3 = static_cast(workers.at(w2->id + 1).get()); CHECK(w1->state == Worker::State::terminating); CHECK(w2->state == Worker::State::terminating); CHECK(w3->state == Worker::State::running); } }