From bf54144093a4462ec59a1cf05ccac4982fa1a11c Mon Sep 17 00:00:00 2001 From: "r.kuznetsov" Date: Wed, 10 May 2017 14:16:37 +0300 Subject: [PATCH] Added processing of failures in local ads campaigns downloading --- map/local_ads_manager.cpp | 103 ++++++++++++++++++++++++++++---------- map/local_ads_manager.hpp | 32 +++++++++--- 2 files changed, 101 insertions(+), 34 deletions(-) diff --git a/map/local_ads_manager.cpp b/map/local_ads_manager.cpp index 70402d07ac..385cdc7e4a 100644 --- a/map/local_ads_manager.cpp +++ b/map/local_ads_manager.cpp @@ -37,6 +37,9 @@ std::string const kCampaignPageUrl = LOCAL_ADS_COMPANY_PAGE_URL; std::string const kCampaignFile = "local_ads_campaigns.dat"; std::string const kLocalAdsSymbolsFile = "local_ads_symbols.txt"; auto constexpr kWWanUpdateTimeout = std::chrono::hours(12); +uint8_t constexpr kRequestMinZoomLevel = 12; +auto constexpr kFailedDownloadingTimeout = std::chrono::seconds(2); +auto constexpr kMaxDownloadingAttempts = 5; void SerializeCampaign(FileWriter & writer, std::string const & countryName, LocalAdsManager::Timestamp const & ts, @@ -76,20 +79,6 @@ std::string MakeCampaignDownloadingURL(MwmSet::MwmId const & mwmId) return ss.str(); } -std::vector DownloadCampaign(MwmSet::MwmId const & mwmId) -{ - std::string const url = MakeCampaignDownloadingURL(mwmId); - if (url.empty()) - return {}; - - platform::HttpClient request(url); - if (!request.RunHttpRequest() || request.ErrorCode() != 200) - return {}; - - string const & response = request.ServerResponse(); - return std::vector(response.cbegin(), response.cend()); -} - df::CustomSymbols ParseCampaign(std::vector const & rawData, MwmSet::MwmId const & mwmId, LocalAdsManager::Timestamp timestamp) { @@ -225,7 +214,7 @@ void LocalAdsManager::UpdateViewport(ScreenBase const & screen) { auto connectionStatus = GetPlatform().ConnectionStatus(); if (kServerUrl.empty() || connectionStatus == Platform::EConnectionType::CONNECTION_NONE || - df::GetZoomLevel(screen.GetScale()) <= scales::GetUpperWorldScale()) + df::GetZoomLevel(screen.GetScale()) < kRequestMinZoomLevel) { return; } @@ -246,6 +235,18 @@ void LocalAdsManager::UpdateViewport(ScreenBase const & screen) auto info = mwm.GetInfo(); if (!info) continue; + + // Skip downloading request if maximum attempts count has reached or + // we are waiting for new attempt. + auto const failedDownloadsIt = m_failedDownloads.find(mwm); + if (failedDownloadsIt != m_failedDownloads.cend() && + (failedDownloadsIt->second.m_attemptsCount >= kMaxDownloadingAttempts || + Now() <= failedDownloadsIt->second.m_lastDownloading + + failedDownloadsIt->second.m_currentTimeout)) + { + continue; + } + std::string const & mwmName = info->GetCountryName(); auto campaignIt = m_campaigns.find(mwmName); if (campaignIt == m_campaigns.end()) @@ -276,6 +277,51 @@ void LocalAdsManager::UpdateViewport(ScreenBase const & screen) } } +bool LocalAdsManager::DownloadCampaign(MwmSet::MwmId const & mwmId, std::vector & bytes) +{ + bytes.clear(); + + std::string const url = MakeCampaignDownloadingURL(mwmId); + if (url.empty()) + return true; + + // Skip already downloaded campaigns. We do not lock whole method because RunHttpRequest + // is a synchronous method and may take a lot of time. The case in which countryName will + // be added to m_campaigns between locks is neglected. + { + std::lock_guard lock(m_mutex); + auto const & countryName = mwmId.GetInfo()->GetCountryName(); + if (m_campaigns.find(countryName) != m_campaigns.cend()) + return false; + } + + platform::HttpClient request(url); + bool const success = request.RunHttpRequest() && request.ErrorCode() == 200; + + std::lock_guard lock(m_mutex); + if (!success) + { + auto const it = m_failedDownloads.find(mwmId); + if (it == m_failedDownloads.cend()) + { + m_failedDownloads.insert(std::make_pair( + mwmId, BackoffStats(Now(), kFailedDownloadingTimeout, 1 /* m_attemptsCount */))); + } + else + { + // Here we increase timeout as power 2 function. + it->second.m_currentTimeout = std::chrono::seconds(it->second.m_currentTimeout.count() << 1); + it->second.m_attemptsCount++; + } + return false; + } + + m_failedDownloads.erase(mwmId); + auto const & response = request.ServerResponse(); + bytes.assign(response.cbegin(), response.cend()); + return true; +} + void LocalAdsManager::ThreadRoutine() { local_ads::IconsInfo::Instance().SetSourceFile(kLocalAdsSymbolsFile); @@ -299,25 +345,28 @@ void LocalAdsManager::ThreadRoutine() { // Download campaign data from server. CampaignInfo info; - info.m_data = DownloadCampaign(mwm.first); - if (info.m_data.empty()) + if (!DownloadCampaign(mwm.first, info.m_data)) continue; - info.m_created = Now(); - // Parse data and send symbols to rendering. - auto symbols = ParseCampaign(std::move(info.m_data), mwm.first, info.m_created); - if (symbols.empty()) + // Parse data and send symbols to rendering (or delete from rendering). + if (!info.m_data.empty()) { - std::lock_guard lock(m_mutex); - m_campaigns.erase(countryName); - m_info.erase(countryName); - DeleteSymbolsFromRendering(mwm.first); + auto symbols = ParseCampaign(std::move(info.m_data), mwm.first, info.m_created); + if (symbols.empty()) + { + DeleteSymbolsFromRendering(mwm.first); + } + else + { + UpdateFeaturesCache(symbols); + SendSymbolsToRendering(std::move(symbols)); + } } else { - UpdateFeaturesCache(symbols); - SendSymbolsToRendering(std::move(symbols)); + DeleteSymbolsFromRendering(mwm.first); } + info.m_created = Now(); // Update run-time data. { diff --git a/map/local_ads_manager.hpp b/map/local_ads_manager.hpp index 0740fbd2f6..815bf7d436 100644 --- a/map/local_ads_manager.hpp +++ b/map/local_ads_manager.hpp @@ -82,6 +82,8 @@ private: void FillSupportedTypes(); + bool DownloadCampaign(MwmSet::MwmId const & mwmId, std::vector & bytes); + GetMwmsByRectFn m_getMwmsByRectFn; GetMwmIdByName m_getMwmIdByNameFn; @@ -98,16 +100,32 @@ private: df::CustomSymbols m_symbolsCache; std::mutex m_symbolsCacheMutex; + std::set m_featuresCache; + mutable std::mutex m_featuresCacheMutex; + + ftypes::HashSetMatcher m_supportedTypes; + + struct BackoffStats + { + BackoffStats() = default; + BackoffStats(Timestamp lastDownloading, std::chrono::seconds currentTimeout, + uint8_t attemptsCount) + : m_lastDownloading(lastDownloading) + , m_currentTimeout(currentTimeout) + , m_attemptsCount(attemptsCount) + {} + + Timestamp m_lastDownloading = {}; + std::chrono::seconds m_currentTimeout = std::chrono::seconds(0); + uint8_t m_attemptsCount = 0; + }; + std::map m_failedDownloads; + + local_ads::Statistics m_statistics; + bool m_isRunning = false; std::condition_variable m_condition; std::set m_requestedCampaigns; std::mutex m_mutex; threads::SimpleThread m_thread; - - local_ads::Statistics m_statistics; - - std::set m_featuresCache; - mutable std::mutex m_featuresCacheMutex; - - ftypes::HashSetMatcher m_supportedTypes; };