| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| #pragma once |
| |
| #include <unordered_map> |
| #include <string> |
| #include <vector> |
| #include <chrono> |
| #include <filesystem> |
| #include <mutex> |
| #include <shared_mutex> |
| #include <fstream> |
| #include <memory> |
| #include <cstdint> |
| #include <system_error> |
| |
| #include "cripts/Context.hpp" |
| #include "cripts/Time.hpp" |
| |
| // Implemented in the .cc file |
| int _cripts_cache_group_sync(TSCont cont, TSEvent event, void *edata); |
| |
| namespace cripts::Cache |
| { |
| |
| class Group |
| { |
| private: |
| using self_type = Group; |
| |
| struct _Entry { |
| cripts::Time::Point timestamp; // Timestamp of when the entry was created |
| size_t length; // Length of the group ID |
| uint32_t prefix; // First 4 characters of the group ID |
| uint64_t hash; // Hash value of the group ID, needed when writing to disk |
| }; |
| |
| // Header structure for on-disk map files (after VERSION field) |
| struct _MapHeader { |
| int64_t created_ts; |
| int64_t last_write_ts; |
| int64_t last_sync_ts; |
| uint64_t count; |
| }; |
| |
| using _MapType = std::unordered_map<uint64_t, _Entry>; |
| |
| struct _MapSlot { |
| std::unique_ptr<_MapType> map; |
| std::string path; |
| cripts::Time::Point created; |
| cripts::Time::Point last_write; |
| cripts::Time::Point last_sync; |
| }; |
| |
| public: |
| static constexpr uint64_t VERSION = (static_cast<uint64_t>('C') << 56) | (static_cast<uint64_t>('G') << 48) | |
| (static_cast<uint64_t>('M') << 40) | (static_cast<uint64_t>('A') << 32) | |
| (static_cast<uint64_t>('P') << 24) | (static_cast<uint64_t>('S') << 16) | |
| (static_cast<uint64_t>('0') << 8) | 0x00; // Change this on version bump |
| |
| static constexpr std::chrono::seconds DEFAULT_MAX_AGE{63072000}; // 2 Years, max cache lifetime in ATS as well |
| |
| Group(const std::string &name, const std::string &base_dir, size_t max_entries = 1024, size_t num_maps = 3) |
| { |
| Initialize(name, base_dir, max_entries, num_maps, DEFAULT_MAX_AGE); |
| } |
| |
| // Not used at the moment. |
| Group() = default; |
| |
| ~Group() { WriteToDisk(); } |
| |
| Group(const self_type &) = delete; |
| self_type &operator=(const self_type &) = delete; |
| |
| void Initialize(const std::string &name, const std::string &base_dir, size_t max_entries = 1024, size_t num_maps = 3, |
| std::chrono::seconds max_age = DEFAULT_MAX_AGE); |
| |
| void |
| SetMaxEntries(size_t max_entries) |
| { |
| std::unique_lock lock(_mutex); |
| _max_entries = max_entries; |
| } |
| |
| void |
| SetMaxAge(std::chrono::seconds max_age) |
| { |
| std::unique_lock lock(_mutex); |
| _max_age = max_age; |
| } |
| |
| void Insert(cripts::string_view key); |
| void Insert(const std::vector<cripts::string_view> &keys); |
| bool Lookup(cripts::string_view key, cripts::Time::Point age) const; |
| bool Lookup(const std::vector<cripts::string_view> &keys, cripts::Time::Point age) const; |
| |
| bool |
| Lookup(cripts::string_view key, time_t age) const |
| { |
| return Lookup(key, cripts::Time::Clock::from_time_t(age)); |
| } |
| |
| bool |
| Lookup(const std::vector<cripts::string_view> &keys, time_t age) const |
| { |
| return Lookup(keys, cripts::Time::Clock::from_time_t(age)); |
| } |
| |
| cripts::Time::Point |
| LastSync() const |
| { |
| std::shared_lock lock(_mutex); |
| return _last_sync; |
| } |
| |
| void WriteToDisk(); |
| void LoadFromDisk(); |
| |
| private: |
| mutable std::shared_mutex _mutex; |
| std::string _name = "CacheGroup"; |
| size_t _num_maps = 3; |
| size_t _max_entries = 1024; |
| std::chrono::seconds _max_age = DEFAULT_MAX_AGE; |
| size_t _map_index = 0; |
| cripts::Time::Point _last_sync = cripts::Time::Point{}; |
| |
| std::vector<_MapSlot> _slots; |
| std::ofstream _txn_log; |
| std::string _log_path; |
| std::string _base_dir; |
| |
| void appendLog(const _Entry &entry); |
| void clearLog(); |
| bool syncMap(size_t index); |
| |
| public: |
| class Manager |
| { |
| friend int ::_cripts_cache_group_sync(TSCont cont, TSEvent event, void *edata); |
| using self_type = Manager; |
| |
| public: |
| static void *Factory(const std::string &name, size_t max_entries = 1024, size_t num_maps = 3); |
| |
| Manager(const self_type &) = delete; |
| self_type &operator=(const self_type &) = delete; |
| |
| protected: |
| void _scheduleCont(); |
| |
| std::unordered_map<std::string, std::weak_ptr<Group>> _groups; |
| std::mutex _mutex; |
| |
| private: |
| Manager() |
| { |
| _base_dir = TSRuntimeDirGet(); |
| |
| if (std::filesystem::exists(_base_dir)) { |
| _base_dir += "/cache_groups"; |
| if (!std::filesystem::exists(_base_dir)) { |
| std::error_code ec; |
| |
| std::filesystem::create_directories(_base_dir, ec); |
| if (ec) { |
| TSError("cripts::Cache::Group::Manager: Failed to create directory `%s': %s", _base_dir.c_str(), ec.message().c_str()); |
| } else { |
| std::filesystem::permissions(_base_dir, std::filesystem::perms::group_write, std::filesystem::perm_options::add, ec); |
| |
| if (ec) { |
| TSWarning("cripts::Cache::Group::Manager: Failed to set permissions on `%s': %s", _base_dir.c_str(), |
| ec.message().c_str()); |
| } |
| } |
| } |
| } |
| _scheduleCont(); // Kick it off |
| } |
| |
| ~Manager() |
| { |
| if (_action) { |
| TSActionCancel(_action); |
| _action = nullptr; |
| } |
| if (_cont) { |
| TSContDestroy(_cont); |
| _cont = nullptr; |
| } |
| } |
| |
| static self_type &_instance(); |
| |
| TSCont _cont = nullptr; |
| TSAction _action = nullptr; |
| std::string _base_dir; |
| }; |
| }; |
| } // namespace cripts::Cache |