From a2ae8199f013fc38f98b88e5103c40158de2734c Mon Sep 17 00:00:00 2001 From: Kishore Nallan Date: Wed, 20 Oct 2021 18:17:30 +0530 Subject: [PATCH] Per request cache ttl. --- include/http_data.h | 9 ++++++- src/core_api.cpp | 66 ++++++++++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/include/http_data.h b/include/http_data.h index c4a748fd..3db000d4 100644 --- a/include/http_data.h +++ b/include/http_data.h @@ -17,6 +17,8 @@ extern "C" { #include "h2o.h" } +using TimePoint = std::chrono::high_resolution_clock::time_point; + struct h2o_custom_timer_t { h2o_timer_t timer; void *data; @@ -167,6 +169,8 @@ struct cached_res_t { uint32_t status_code; std::string content_type_header; std::string body; + TimePoint created_at; + uint32_t ttl; uint64_t hash; bool operator == (const cached_res_t& res) const { @@ -177,10 +181,13 @@ struct cached_res_t { return hash != res.hash; } - void load(uint32_t status_code, const std::string& content_type_header, const std::string& body, uint64_t hash) { + void load(uint32_t status_code, const std::string& content_type_header, const std::string& body, + const TimePoint created_at, const uint32_t ttl, uint64_t hash) { this->status_code = status_code; this->content_type_header = content_type_header; this->body = body; + this->created_at = created_at; + this->ttl = ttl; this->hash = hash; } }; diff --git a/src/core_api.cpp b/src/core_api.cpp index de4f2d21..adc80da1 100644 --- a/src/core_api.cpp +++ b/src/core_api.cpp @@ -14,7 +14,7 @@ using namespace std::chrono_literals; std::shared_mutex mutex; -LRU::TimedCache res_cache(60*1000ms, 1000); +LRU::Cache res_cache; bool handle_authentication(std::map& req_params, const std::string& body, const route_path& rpath, const std::string& auth_key) { @@ -225,8 +225,8 @@ uint64_t hash_request(const std::shared_ptr& req) { } bool get_search(const std::shared_ptr& req, const std::shared_ptr& res) { - const auto cache_it = req->params.find("use_cache"); - bool use_cache = (cache_it != req->params.end()) && (cache_it->second == "1" || cache_it->second == "true"); + const auto use_cache_it = req->params.find("use_cache"); + bool use_cache = (use_cache_it != req->params.end()) && (use_cache_it->second == "1" || use_cache_it->second == "true"); uint64_t req_hash = 0; if(use_cache) { @@ -240,8 +240,18 @@ bool get_search(const std::shared_ptr& req, const std::shared_ptrset_content(cached_value.status_code, cached_value.content_type_header, cached_value.body, true); - return true; + + // we still need to check that TTL has not expired + uint32_t ttl = cached_value.ttl; + uint64_t seconds_elapsed = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - cached_value.created_at).count(); + + if(seconds_elapsed < cached_value.ttl) { + res->set_content(cached_value.status_code, cached_value.content_type_header, cached_value.body, true); + return true; + } + + //LOG(INFO) << "Result found in cache but ttl lapsed."; } } @@ -258,12 +268,17 @@ bool get_search(const std::shared_ptr& req, const std::shared_ptrparams.end() && StringUtils::is_int32_t(cache_ttl_it->second)) { + cache_ttl = std::stoul(cache_ttl_it->second); + } - // NOTE: due to an implementation quirk, erase is required for dealing with expired keys that might still exist - res_cache.erase(req_hash); + cached_res_t cached_res; + cached_res.load(res->status_code, res->content_type_header, res->body, now, cache_ttl, req_hash); + + std::unique_lock lock(mutex); res_cache.insert(req_hash, cached_res); } @@ -271,8 +286,8 @@ bool get_search(const std::shared_ptr& req, const std::shared_ptr& req, const std::shared_ptr& res) { - const auto cache_it = req->params.find("use_cache"); - bool use_cache = (cache_it != req->params.end()) && (cache_it->second == "1" || cache_it->second == "true"); + const auto use_cache_it = req->params.find("use_cache"); + bool use_cache = (use_cache_it != req->params.end()) && (use_cache_it->second == "1" || use_cache_it->second == "true"); uint64_t req_hash = 0; if(use_cache) { @@ -286,8 +301,16 @@ bool post_multi_search(const std::shared_ptr& req, const std::shared_p if(hit_it != res_cache.end()) { //LOG(INFO) << "Result found in cache."; const auto& cached_value = hit_it.value(); - res->set_content(cached_value.status_code, cached_value.content_type_header, cached_value.body, true); - return true; + + // we still need to check that TTL has not expired + uint32_t ttl = cached_value.ttl; + uint64_t seconds_elapsed = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - cached_value.created_at).count(); + + if(seconds_elapsed < cached_value.ttl) { + res->set_content(cached_value.status_code, cached_value.content_type_header, cached_value.body, true); + return true; + } } } @@ -365,12 +388,17 @@ bool post_multi_search(const std::shared_ptr& req, const std::shared_p // we will cache only successful requests if(use_cache) { //LOG(INFO) << "Adding to cache, key = " << req_hash; - cached_res_t cached_res; - cached_res.load(res->status_code, res->content_type_header, res->body, req_hash); - std::unique_lock lock(mutex); + auto now = std::chrono::high_resolution_clock::now(); + const auto cache_ttl_it = req->params.find("cache_ttl"); + uint32_t cache_ttl = 60; + if(cache_ttl_it != req->params.end() && StringUtils::is_int32_t(cache_ttl_it->second)) { + cache_ttl = std::stoul(cache_ttl_it->second); + } - // NOTE: due to an implementation quirk, erase is required for dealing with expired keys that might still exist - res_cache.erase(req_hash); + cached_res_t cached_res; + cached_res.load(res->status_code, res->content_type_header, res->body, now, cache_ttl, req_hash); + + std::unique_lock lock(mutex); res_cache.insert(req_hash, cached_res); }