Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: cache data #16

Merged
merged 15 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions src/Infrastructure/Cache/Cache.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include <Infrastructure/Cache/Cache.h>
#include <filesystem>
#include <format>
#include <fstream>
#include <functional>
#include <optional>
#include <spdlog/spdlog.h>
#include <string>

namespace evento {

namespace fs = std::filesystem;

std::string CacheManager::generateKey(http::verb verb,
urls::url_view url,
const std::initializer_list<urls::param>& params) {
std::string key = std::format("{}|{}", url.data(), static_cast<int>(verb));
for (const auto& param : params) {
key += std::format("|{}={}", param.key, param.value);
}
return key;
}

std::string CacheManager::generateFilename(urls::url_view url) {
auto name = std::to_string(std::hash<std::string>{}(url.data()));
auto extension = url.path().substr(url.path().find_last_of('.'));
return name + extension;
}

bool CacheManager::isExpired(const CacheEntry& entry) {
return std::chrono::steady_clock::now() - entry.insertTime >= entry.ttl;
}

std::optional<std::filesystem::path> CacheManager::cacheDir() {
fs::path cacheFileDir;
#if defined(_WIN32) || defined(_WIN64)
auto localAppData = std::getenv("LOCALAPPDATA");
if (localAppData == nullptr) {
spdlog::warn("LOCALAPPDATA environment variable not found");
return std::nullopt;
}
cacheFileDir = fs::path(localAppData) / "Programs" / "evento";

#elif __linux__
auto xdgCacheHome = std::getenv("XDG_CACHE_HOME");
if (xdgCacheHome == nullptr) {
auto home = std::getenv("HOME");
if (home == nullptr) {
spdlog::warn("HOME environment variable not found");
return std::nullopt;
}
cacheFileDir = fs::path(home) / ".cache" / "evento";
} else {
cacheFileDir = fs::path(xdgCacheHome) / "evento";
}
#elif __APPLE__
auto home = std::getenv("HOME");
if (home == nullptr) {
spdlog::warn("HOME environment variable not found");
return;
}
cacheFileDir = fs::path(home) / "Library" / "Caches" / "evento";
#else
#error "Unsupported platform"
#endif

if (!fs::is_directory(cacheFileDir))
return std::nullopt;

return fs::absolute(cacheFileDir);
}

void CacheManager::insert(const std::string& key, const CacheEntry& entry) {
auto it = _cacheMap.find(key);
if (it != _cacheMap.end()) {
_cacheList.erase(it->second);
_currentCacheSize -= it->second->second.size;
}

_cacheList.emplace_front(key, entry);
_cacheMap[key] = _cacheList.begin();
_currentCacheSize += entry.size;

while (_currentCacheSize > MAX_CACHE_SIZE) {
auto last = _cacheList.end();
--last;
_currentCacheSize -= last->second.size;
_cacheMap.erase(last->first);
_cacheList.pop_back();
}
}

std::optional<CacheEntry> CacheManager::get(std::string const& key) {
if (isExpired(_cacheMap[key]->second)) {
_currentCacheSize -= _cacheMap[key]->second.size;
_cacheList.erase(_cacheMap[key]);
_cacheMap.erase(key);
return std::nullopt;
}
auto it = _cacheMap.find(key);
if (it == _cacheMap.end()) {
return std::nullopt;
}
return it->second->second;
}

bool CacheManager::saveToDisk(std::string const& data, fs::path const& path) {
fs::create_directories(path.parent_path());
std::ofstream file(path, std::ios::binary);
if (file.is_open()) {
file << data;
return true;
}
spdlog::warn("Failed to save cache to disk: {}", path.string());
return false;
}

} // namespace evento
52 changes: 52 additions & 0 deletions src/Infrastructure/Cache/Cache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once

#include <boost/beast/http.hpp>
#include <boost/url.hpp>
#include <chrono>
#include <list>
#include <nlohmann/json.hpp>
#include <optional>
#include <unordered_map>

namespace evento {

namespace beast = boost::beast;
namespace http = beast::http;
namespace urls = boost::urls;

struct CacheEntry {
nlohmann::basic_json<> data;
std::chrono::steady_clock::time_point insertTime;
std::chrono::steady_clock::duration ttl;
std::size_t size; //cache size
};

class CacheManager {
public:
static std::string generateKey(http::verb verb,
urls::url_view url,
const std::initializer_list<urls::param>& params);

static std::string generateFilename(urls::url_view url);

static std::optional<std::filesystem::path> cacheDir();

static bool isExpired(const CacheEntry& entry);

static bool saveToDisk(std::string const& data, std::filesystem::path const& path);

void insert(const std::string& key, const CacheEntry& entry);

static std::size_t currentCacheSize() { return _currentCacheSize; }

std::optional<CacheEntry> get(std::string const& key);

static constexpr size_t MAX_CACHE_SIZE = 64 * 1024 * 1024;

private:
std::list<std::pair<std::string, CacheEntry>> _cacheList;
std::unordered_map<std::string, decltype(_cacheList.begin())> _cacheMap;
inline static std::size_t _currentCacheSize = 0;
};

} // namespace evento
4 changes: 0 additions & 4 deletions src/Infrastructure/Network/HttpsAccessManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@
#include <boost/asio/awaitable.hpp>
#include <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/ssl/stream_base.hpp>
#include <boost/beast/http/dynamic_body.hpp>
#include <boost/beast/http/field.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/system/detail/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <boost/url.hpp>

Expand Down
3 changes: 0 additions & 3 deletions src/Infrastructure/Network/HttpsAccessManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@
#include <boost/asio/use_awaitable.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/http/dynamic_body.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/verb.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/url/url.hpp>
#include <chrono>
Expand Down
Loading