This commit is contained in:
2025-08-25 17:13:47 +06:00
parent c06683cd57
commit 36346c9798
5 changed files with 249 additions and 338 deletions

View File

@@ -1,7 +1,11 @@
#pragma once
#include "Common/Abstract.hpp"
#include <array>
#include <boost/lockfree/spsc_queue.hpp>
#include <cassert>
#include <chrono>
#include <functional>
#include <memory>
#include <queue>
#include <string>
@@ -10,6 +14,8 @@
#include <TOSAsync.hpp>
#include <filesystem>
#include <string_view>
#include <thread>
#include <unordered_map>
namespace LV::Client {
@@ -38,7 +44,7 @@ public:
CacheDatabase(const CacheDatabase&) = delete;
CacheDatabase(CacheDatabase&&) = delete;
CacheDatabase& operator=(const CacheDatabase&) = delete;
CacheDatabase& operator=(CacheDatabase&&) = delete;
CacheDatabase& operator=(CacheDatabase&&) = delete;
/*
Выдаёт размер занимаемый всем хранимым кешем
@@ -50,134 +56,141 @@ public:
/*
Создаёт линейный массив в котором подряд указаны все хэш суммы в бинарном виде и возвращает их количество
*/
std::pair<std::string, size_t> getAllHash();
using HASH = std::array<uint8_t, 32>;
// std::pair<std::string, size_t> getAllHash();
/*
Обновляет время использования кеша
*/
void updateTimeFor(HASH hash);
void updateTimeFor(Hash_t hash);
/*
Добавляет запись
*/
void insert(HASH hash, size_t size);
void insert(Hash_t hash, size_t size);
/*
Выдаёт хэши на удаление по размеру в сумме больше bytesToFree.
Сначала удаляется старьё, потом по приоритету дата использования + размер
*/
std::vector<HASH> findExcessHashes(size_t bytesToFree, int timeBefore);
std::vector<Hash_t> findExcessHashes(size_t bytesToFree, int timeBefore);
/*
Удаление записи
*/
void remove(HASH hash);
void remove(Hash_t hash);
static std::string hashToString(HASH hash);
static std::string hashToString(Hash_t hash);
static int hexCharToInt(char c);
static HASH stringToHash(const std::string_view view);
static Hash_t stringToHash(const std::string_view view);
};
/*
Читает и пишет ресурсы на диск
В приоритете чтение
Менеджер предоставления ресурсов. Управляет ресурс паками
и хранением кешированных ресурсов сервера.
Интерфейс однопоточный.
Кодировки только на стороне сервера, на клиенте уже готовые данные
NOT ThreadSafe
Обработка файлов в отдельном потоке
*/
class CacheHandler : public IAsyncDestructible {
protected:
const fs::path Path;
CacheDatabase DB;
size_t MaxCacheDirectorySize;
size_t MaxLifeTime;
class ResourceHandler : public IAsyncDestructible {
public:
using Ptr = std::shared_ptr<CacheHandler>;
using Ptr = std::shared_ptr<ResourceHandler>;
protected:
CacheHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
size_t maxCacheDirectorySize, size_t maxLifeTime);
public:
virtual ~CacheHandler();
// Добавить задачу на запись
virtual void pushWrite(std::string &&data, CacheDatabase::HASH hash) = 0;
// Добавить задачу на чтение
virtual void pushRead(CacheDatabase::HASH hash) = 0;
// Получить считанные данные
virtual std::vector<std::pair<CacheDatabase::HASH, std::string>> pullReads() = 0;
// Получить список доступных ресурсов
std::pair<std::string, size_t> getAll();
// Размер всего хранимого кеша
size_t getCacheSize();
// Обновить параметры хранилища
virtual void updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) = 0;
};
class CacheHandlerBasic : public CacheHandler {
Logger LOG = "CacheHandlerBasic";
struct DataTask {
CacheDatabase::HASH Hash;
std::shared_ptr<std::string> Data;
struct ResourceKey {
Hash_t Hash;
EnumAssets Type;
std::string Domain, Key;
};
// Очередь задач на чтение
SpinlockObject<std::queue<CacheDatabase::HASH>> ReadQueue;
// Кэш данных, которые ещё не записались
SpinlockObject<std::vector<std::pair<CacheDatabase::HASH, std::shared_ptr<std::string>>>> WriteCache;
// Очередь записи данных на диск
SpinlockObject<std::queue<DataTask>> WriteQueue;
// Список полностью считанных файлов
SpinlockObject<std::vector<DataTask>> ReadedQueue;
bool NeedShutdown = false;
std::thread ReadThread, ReadWriteThread;
public:
using Ptr = std::shared_ptr<CacheHandlerBasic>;
private:
virtual coro<> asyncDestructor() override;
void readThread(AsyncUseControl::Lock lock);
void readWriteThread(AsyncUseControl::Lock lock);
protected:
CacheHandlerBasic(boost::asio::io_context &ioc, const fs::path& cachePath,
size_t maxCacheDirectorySize, size_t maxLifeTime);
ResourceHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
size_t maxCacheDatabaseSize, size_t maxLifeTime);
public:
virtual ~CacheHandlerBasic();
static std::shared_ptr<CacheHandlerBasic> Create(asio::io_context &ioc, const fs::path& cachePath,
virtual ~ResourceHandler();
static std::shared_ptr<ResourceHandler> Create(asio::io_context &ioc, const fs::path& cachePath,
size_t maxCacheDirectorySize = 8*1024*1024*1024ULL, size_t maxLifeTime = 7*24*60*60) {
return createShared(ioc, new CacheHandlerBasic(ioc, cachePath, maxCacheDirectorySize, maxLifeTime));
return createShared(ioc, new ResourceHandler(ioc, cachePath, maxCacheDirectorySize, maxLifeTime));
}
virtual void pushWrite(std::string &&data, CacheDatabase::HASH hash) override;
virtual void pushRead(CacheDatabase::HASH hash) override;
virtual std::vector<std::pair<CacheDatabase::HASH, std::string>> pullReads() override;
virtual void updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) override;
// Добавить новый полученный с сервера ресурс
void pushResources(std::vector<Resource> resources) {
WriteQueue.lock()->push_range(resources);
}
// Добавить задачи на чтение
void pushReads(std::vector<ResourceKey> keys) {
ReadQueue.lock()->push_range(keys);
}
// Получить считанные данные
std::vector<std::pair<Hash_t, std::optional<Resource>>> pullReads() {
return std::move(*ReadyQueue.lock());
}
// Размер всего хранимого кеша
size_t getCacheSize() {
return DatabaseSize;
}
// Обновить параметры хранилища кеша
void updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) {
auto lock = Changes.lock();
lock->MaxLifeTime = maxLifeTime;
lock->MaxCacheDatabaseSize = maxCacheDirectorySize;
lock->MaxChange = true;
}
// Установка путей до папок assets
void setResourcePacks(std::vector<fs::path> packsAssets) {
auto lock = Changes.lock();
lock->Assets = std::move(packsAssets);
lock->AssetsChange = true;
}
// Запуск процедуры проверки хешей всего хранимого кеша
void runFullDatabaseRecheck(std::move_only_function<void(std::string result)>&& func) {
auto lock = Changes.lock();
lock->OnRecheckEnd = std::move(func);
lock->FullRecheck = true;
}
// Уведомление о завершении работы
void prepareShutdown() {
NeedShutdown = true;
}
// После этого вызова уже нельзя будет обращатся ко внешним ресурсам
void shutdown() {
OffThread.join();
}
private:
Logger LOG = "Client>ResourceHandler";
const fs::path CachePath;
volatile size_t DatabaseSize = 0;
// Очередь задач на чтение
TOS::SpinlockObject<std::queue<ResourceKey>> ReadQueue;
// Очередь на запись ресурсов
TOS::SpinlockObject<std::queue<Resource>> WriteQueue;
// Очередь на выдачу результатов чтения
TOS::SpinlockObject<std::vector<std::pair<Hash_t, std::optional<Resource>>>> ReadyQueue;
struct Changes_t {
std::vector<fs::path> Assets;
volatile bool AssetsChange = false;
size_t MaxCacheDatabaseSize, MaxLifeTime;
volatile bool MaxChange = false;
std::optional<std::move_only_function<void(std::string)>> OnRecheckEnd;
volatile bool FullRecheck = false;
};
TOS::SpinlockObject<Changes_t> Changes;
bool NeedShutdown = false;
std::thread OffThread;
void readWriteThread(AsyncUseControl::Lock lock);
};
#ifdef LUAVOX_HAVE_LIBURING
class CacheHandlerUring : public CacheHandler {
};
#endif
}