diff --git a/td/telegram/PollManager.cpp b/td/telegram/PollManager.cpp index eace4491b..9e07d83f1 100644 --- a/td/telegram/PollManager.cpp +++ b/td/telegram/PollManager.cpp @@ -238,6 +238,9 @@ PollManager::PollManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::m close_poll_timeout_.set_callback(on_close_poll_timeout_callback); close_poll_timeout_.set_callback_data(static_cast(this)); + + unload_poll_timeout_.set_callback(on_unload_poll_timeout_callback); + unload_poll_timeout_.set_callback_data(static_cast(this)); } void PollManager::start_up() { @@ -282,6 +285,15 @@ void PollManager::on_close_poll_timeout_callback(void *poll_manager_ptr, int64 p send_closure_later(poll_manager->actor_id(poll_manager), &PollManager::on_close_poll_timeout, PollId(poll_id_int)); } +void PollManager::on_unload_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int) { + if (G()->close_flag()) { + return; + } + + auto poll_manager = static_cast(poll_manager_ptr); + send_closure_later(poll_manager->actor_id(poll_manager), &PollManager::on_unload_poll_timeout, PollId(poll_id_int)); +} + bool PollManager::is_local_poll_id(PollId poll_id) { return poll_id.get() < 0 && poll_id.get() > std::numeric_limits::min(); } @@ -295,11 +307,22 @@ const PollManager::Poll *PollManager::get_poll(PollId poll_id) const { } } +const PollManager::Poll *PollManager::get_poll(PollId poll_id) { + auto p = polls_.find(poll_id); + if (p == polls_.end()) { + return nullptr; + } else { + schedule_poll_unload(poll_id); + return p->second.get(); + } +} + PollManager::Poll *PollManager::get_poll_editable(PollId poll_id) { auto p = polls_.find(poll_id); if (p == polls_.end()) { return nullptr; } else { + schedule_poll_unload(poll_id); return p->second.get(); } } @@ -637,6 +660,7 @@ void PollManager::register_poll(PollId poll_id, FullMessageId full_message_id, c !(poll->is_closed && poll->is_updated_after_close)) { update_poll_timeout_.add_timeout_in(poll_id.get(), 0); } + unload_poll_timeout_.cancel_timeout(poll_id.get()); } void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id, const char *source) { @@ -654,6 +678,32 @@ void PollManager::unregister_poll(PollId poll_id, FullMessageId full_message_id, if (message_ids.empty()) { poll_messages_.erase(poll_id); update_poll_timeout_.cancel_timeout(poll_id.get()); + + schedule_poll_unload(poll_id); + } +} + +bool PollManager::can_unload_poll(PollId poll_id) { + if (is_local_poll_id(poll_id) || poll_messages_.count(poll_id) != 0 || pending_answers_.count(poll_id) != 0 || + being_closed_polls_.count(poll_id) != 0) { + return false; + } + + auto it = poll_voters_.find(poll_id); + if (it != poll_voters_.end() && !it->second.empty()) { + for (auto &voters : it->second) { + if (!voters.pending_queries.empty()) { + return false; + } + } + } + + return true; +} + +void PollManager::schedule_poll_unload(PollId poll_id) { + if (can_unload_poll(poll_id)) { + unload_poll_timeout_.set_timeout_in(poll_id.get(), UNLOAD_POLL_DELAY); } } @@ -763,6 +813,7 @@ void PollManager::do_set_poll_answer(PollId poll_id, FullMessageId full_message_ binlog_erase(G()->td_db()->get_binlog(), log_event_id); return; } + unload_poll_timeout_.cancel_timeout(poll_id.get()); auto &pending_answer = pending_answers_[poll_id]; if (!pending_answer.promises_.empty() && pending_answer.options_ == options) { @@ -986,6 +1037,8 @@ void PollManager::get_poll_voters(PollId poll_id, FullMessageId full_message_id, return; } + unload_poll_timeout_.cancel_timeout(poll_id.get()); + auto query_promise = PromiseCreator::lambda([actor_id = actor_id(this), poll_id, option_id, offset = voters.next_offset, limit](Result> &&result) mutable { @@ -1135,6 +1188,8 @@ void PollManager::do_stop_poll(PollId poll_id, FullMessageId full_message_id, un binlog_add(G()->td_db()->get_binlog(), LogEvent::HandlerType::StopPoll, get_log_event_storer(log_event)); } + unload_poll_timeout_.cancel_timeout(poll_id.get()); + bool is_inserted = being_closed_polls_.insert(poll_id).second; CHECK(is_inserted); auto new_promise = get_erase_log_event_promise(log_event_id, std::move(promise)); @@ -1167,8 +1222,7 @@ void PollManager::on_update_poll_timeout(PollId poll_id) { CHECK(!is_local_poll_id(poll_id)); auto poll = get_poll(poll_id); - CHECK(poll != nullptr); - if (poll->is_closed && poll->is_updated_after_close) { + if (poll == nullptr || (poll->is_closed && poll->is_updated_after_close)) { return; } if (pending_answers_.count(poll_id) > 0) { @@ -1197,8 +1251,7 @@ void PollManager::on_close_poll_timeout(PollId poll_id) { CHECK(!is_local_poll_id(poll_id)); auto poll = get_poll_editable(poll_id); - CHECK(poll != nullptr); - if (poll->is_closed || poll->close_date == 0) { + if (poll == nullptr || poll->is_closed || poll->close_date == 0) { return; } @@ -1218,10 +1271,34 @@ void PollManager::on_close_poll_timeout(PollId poll_id) { } } +void PollManager::on_unload_poll_timeout(PollId poll_id) { + if (G()->close_flag()) { + return; + } + CHECK(!is_local_poll_id(poll_id)); + + if (!can_unload_poll(poll_id)) { + return; + } + + LOG(INFO) << "Unload " << poll_id; + + update_poll_timeout_.cancel_timeout(poll_id.get()); + close_poll_timeout_.cancel_timeout(poll_id.get()); + + auto is_deleted = polls_.erase(poll_id) > 0; + CHECK(is_deleted); + + poll_voters_.erase(poll_id); + loaded_from_database_polls_.erase(poll_id); +} + void PollManager::on_get_poll_results(PollId poll_id, uint64 generation, Result> result) { auto poll = get_poll(poll_id); - CHECK(poll != nullptr); + if (poll == nullptr) { + return; + } if (result.is_error()) { if (!(poll->is_closed && poll->is_updated_after_close) && !G()->close_flag() && !td_->auth_manager_->is_bot()) { auto timeout = get_polling_timeout(); @@ -1642,6 +1719,8 @@ PollId PollManager::on_get_poll(PollId poll_id, tl_object_ptris_closed && being_closed_polls_.erase(poll_id) != 0))) { send_closure(G()->td(), &Td::send_update, td_api::make_object(get_poll_object(poll_id, poll))); + + schedule_poll_unload(poll_id); } return poll_id; } diff --git a/td/telegram/PollManager.h b/td/telegram/PollManager.h index 01f2bd130..1922604ac 100644 --- a/td/telegram/PollManager.h +++ b/td/telegram/PollManager.h @@ -136,6 +136,7 @@ class PollManager final : public Actor { }; static constexpr int32 MAX_GET_POLL_VOTERS = 50; // server side limit + static constexpr int32 UNLOAD_POLL_DELAY = 600; // some reasonable value class SetPollAnswerLogEvent; class StopPollLogEvent; @@ -147,6 +148,8 @@ class PollManager final : public Actor { static void on_close_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int); + static void on_unload_poll_timeout_callback(void *poll_manager_ptr, int64 poll_id_int); + static td_api::object_ptr get_poll_option_object(const PollOption &poll_option); static telegram_api::object_ptr get_input_poll_option(const PollOption &poll_option); @@ -159,8 +162,14 @@ class PollManager final : public Actor { const Poll *get_poll(PollId poll_id) const; + const Poll *get_poll(PollId poll_id); + Poll *get_poll_editable(PollId poll_id); + bool can_unload_poll(PollId poll_id); + + void schedule_poll_unload(PollId poll_id); + void notify_on_poll_update(PollId poll_id); static string get_poll_database_key(PollId poll_id); @@ -175,6 +184,8 @@ class PollManager final : public Actor { void on_close_poll_timeout(PollId poll_id); + void on_unload_poll_timeout(PollId poll_id); + void on_online(); Poll *get_poll_force(PollId poll_id); @@ -204,6 +215,7 @@ class PollManager final : public Actor { MultiTimeout update_poll_timeout_{"UpdatePollTimeout"}; MultiTimeout close_poll_timeout_{"ClosePollTimeout"}; + MultiTimeout unload_poll_timeout_{"UnloadPollTimeout"}; Td *td_; ActorShared<> parent_;