*
This commit is contained in:
@@ -156,7 +156,7 @@ std::pair<std::string, size_t> CacheDatabase::getAllHash() {
|
|||||||
return {out, readed};
|
return {out, readed};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CacheDatabase::updateTimeFor(HASH hash) {
|
void CacheDatabase::updateTimeFor(Hash_t hash) {
|
||||||
sqlite3_bind_blob(STMT_UPDATE_TIME, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
|
sqlite3_bind_blob(STMT_UPDATE_TIME, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
|
||||||
sqlite3_bind_int(STMT_UPDATE_TIME, 2, time(nullptr));
|
sqlite3_bind_int(STMT_UPDATE_TIME, 2, time(nullptr));
|
||||||
if(sqlite3_step(STMT_UPDATE_TIME) != SQLITE_DONE) {
|
if(sqlite3_step(STMT_UPDATE_TIME) != SQLITE_DONE) {
|
||||||
@@ -167,7 +167,7 @@ void CacheDatabase::updateTimeFor(HASH hash) {
|
|||||||
sqlite3_reset(STMT_UPDATE_TIME);
|
sqlite3_reset(STMT_UPDATE_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CacheDatabase::insert(HASH hash, size_t size) {
|
void CacheDatabase::insert(Hash_t hash, size_t size) {
|
||||||
assert(size < (size_t(1) << 31)-1 && size > 0);
|
assert(size < (size_t(1) << 31)-1 && size > 0);
|
||||||
|
|
||||||
sqlite3_bind_blob(STMT_INSERT, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
|
sqlite3_bind_blob(STMT_INSERT, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
|
||||||
@@ -181,8 +181,8 @@ void CacheDatabase::insert(HASH hash, size_t size) {
|
|||||||
sqlite3_reset(STMT_INSERT);
|
sqlite3_reset(STMT_INSERT);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CacheDatabase::HASH> CacheDatabase::findExcessHashes(size_t bytesToFree, int timeBefore = time(nullptr)-604800) {
|
std::vector<Hash_t> CacheDatabase::findExcessHashes(size_t bytesToFree, int timeBefore = time(nullptr)-604800) {
|
||||||
std::vector<HASH> out;
|
std::vector<Hash_t> out;
|
||||||
size_t removed = 0;
|
size_t removed = 0;
|
||||||
|
|
||||||
sqlite3_bind_int(STMT_OLD, 1, timeBefore);
|
sqlite3_bind_int(STMT_OLD, 1, timeBefore);
|
||||||
@@ -197,7 +197,7 @@ std::vector<CacheDatabase::HASH> CacheDatabase::findExcessHashes(size_t bytesToF
|
|||||||
|
|
||||||
const uint8_t *hash = (const uint8_t*) sqlite3_column_blob(STMT_OLD, 0);
|
const uint8_t *hash = (const uint8_t*) sqlite3_column_blob(STMT_OLD, 0);
|
||||||
removed += sqlite3_column_int(STMT_OLD, 1);
|
removed += sqlite3_column_int(STMT_OLD, 1);
|
||||||
HASH obj;
|
Hash_t obj;
|
||||||
for(int iter = 0; iter < 32; iter++)
|
for(int iter = 0; iter < 32; iter++)
|
||||||
obj[iter] = hash[iter];
|
obj[iter] = hash[iter];
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ std::vector<CacheDatabase::HASH> CacheDatabase::findExcessHashes(size_t bytesToF
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t *hash = (const uint8_t*) sqlite3_column_blob(STMT_TO_FREE, 0);
|
const uint8_t *hash = (const uint8_t*) sqlite3_column_blob(STMT_TO_FREE, 0);
|
||||||
HASH obj;
|
Hash_t obj;
|
||||||
for(int iter = 0; iter < 32; iter++)
|
for(int iter = 0; iter < 32; iter++)
|
||||||
obj[iter] = hash[iter];
|
obj[iter] = hash[iter];
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ std::vector<CacheDatabase::HASH> CacheDatabase::findExcessHashes(size_t bytesToF
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CacheDatabase::remove(HASH hash) {
|
void CacheDatabase::remove(Hash_t hash) {
|
||||||
sqlite3_bind_blob(STMT_REMOVE, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
|
sqlite3_bind_blob(STMT_REMOVE, 1, (const void*) hash.data(), 32, SQLITE_STATIC);
|
||||||
if(sqlite3_step(STMT_REMOVE) != SQLITE_DONE) {
|
if(sqlite3_step(STMT_REMOVE) != SQLITE_DONE) {
|
||||||
sqlite3_reset(STMT_REMOVE);
|
sqlite3_reset(STMT_REMOVE);
|
||||||
@@ -242,7 +242,7 @@ void CacheDatabase::remove(HASH hash) {
|
|||||||
sqlite3_reset(STMT_REMOVE);
|
sqlite3_reset(STMT_REMOVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CacheDatabase::hashToString(HASH hash) {
|
std::string CacheDatabase::hashToString(Hash_t hash) {
|
||||||
std::string text;
|
std::string text;
|
||||||
text.reserve(64);
|
text.reserve(64);
|
||||||
|
|
||||||
@@ -263,125 +263,41 @@ std::string CacheDatabase::hashToString(HASH hash) {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CacheDatabase::hexCharToInt(char c) {
|
// int CacheDatabase::hexCharToInt(char c) {
|
||||||
if (c >= '0' && c <= '9') return c - '0';
|
// if (c >= '0' && c <= '9') return c - '0';
|
||||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
// if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||||
throw std::invalid_argument("Invalid hexadecimal character");
|
// throw std::invalid_argument("Invalid hexadecimal character");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Hash_t CacheDatabase::stringToHash(const std::string_view view) {
|
||||||
|
// if (view.size() != 64)
|
||||||
|
// throw std::invalid_argument("Hex string must be exactly 64 characters long");
|
||||||
|
|
||||||
|
// Hash_t hash;
|
||||||
|
|
||||||
|
// for (size_t i = 0; i < 32; ++i) {
|
||||||
|
// size_t offset = 62 - i * 2;
|
||||||
|
// int high = hexCharToInt(view[offset]);
|
||||||
|
// int low = hexCharToInt(view[offset + 1]);
|
||||||
|
// hash[i] = (high << 4) | low;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return hash;
|
||||||
|
// }
|
||||||
|
|
||||||
|
coro<> ResourceHandler::asyncDestructor() {
|
||||||
|
assert(NeedShutdown); // Нормальный shutdown должен быть вызван
|
||||||
|
co_await IAsyncDestructible::asyncDestructor();
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheDatabase::HASH CacheDatabase::stringToHash(const std::string_view view) {
|
void ResourceHandler::readWriteThread(AsyncUseControl::Lock lock) {
|
||||||
if (view.size() != 64)
|
|
||||||
throw std::invalid_argument("Hex string must be exactly 64 characters long");
|
|
||||||
|
|
||||||
HASH hash;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 32; ++i) {
|
|
||||||
size_t offset = 62 - i * 2;
|
|
||||||
int high = hexCharToInt(view[offset]);
|
|
||||||
int low = hexCharToInt(view[offset + 1]);
|
|
||||||
hash[i] = (high << 4) | low;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
CacheHandler::CacheHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
|
|
||||||
size_t maxCacheDirectorySize, size_t maxLifeTime)
|
|
||||||
: IAsyncDestructible(ioc), Path(cachePath), DB(Path),
|
|
||||||
MaxCacheDirectorySize(maxCacheDirectorySize), MaxLifeTime(maxLifeTime)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CacheHandler::~CacheHandler() = default;
|
|
||||||
|
|
||||||
std::pair<std::string, size_t> CacheHandler::getAll() {
|
|
||||||
return DB.getAllHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t CacheHandler::getCacheSize() {
|
|
||||||
return DB.getCacheSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
coro<> CacheHandlerBasic::asyncDestructor() {
|
|
||||||
NeedShutdown = true;
|
|
||||||
co_await CacheHandler::asyncDestructor();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CacheHandlerBasic::readThread(AsyncUseControl::Lock lock) {
|
|
||||||
LOG.info() << "Поток чтения запущен";
|
|
||||||
|
|
||||||
while(!NeedShutdown) {
|
|
||||||
if(ReadQueue.get_read().empty())
|
|
||||||
goto wait;
|
|
||||||
else {
|
|
||||||
auto lock = ReadQueue.lock();
|
|
||||||
if(lock->empty())
|
|
||||||
goto wait;
|
|
||||||
|
|
||||||
CacheDatabase::HASH hash = lock->front();
|
|
||||||
lock->pop();
|
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
std::string name = CacheDatabase::hashToString(hash);
|
|
||||||
fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
|
|
||||||
|
|
||||||
std::shared_ptr<std::string> data;
|
|
||||||
|
|
||||||
{
|
|
||||||
auto lock_wc = WriteCache.lock();
|
|
||||||
auto iter = lock_wc->begin();
|
|
||||||
while(iter != lock_wc->end()) {
|
|
||||||
if(iter->first == hash) {
|
|
||||||
// Копируем
|
|
||||||
data = std::make_shared<std::string>(*iter->second);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!data) {
|
|
||||||
data = std::make_shared<std::string>();
|
|
||||||
|
|
||||||
try {
|
|
||||||
std::ifstream fd(path, std::ios::binary | std::ios::ate);
|
|
||||||
if (!fd.is_open())
|
|
||||||
MAKE_ERROR("!is_open(): " << fd.exceptions());
|
|
||||||
|
|
||||||
if (fd.fail())
|
|
||||||
MAKE_ERROR("fail(): " << fd.exceptions());
|
|
||||||
|
|
||||||
std::ifstream::pos_type size = fd.tellg();
|
|
||||||
fd.seekg(0, std::ios::beg);
|
|
||||||
data->resize(size);
|
|
||||||
fd.read(data->data(), size);
|
|
||||||
|
|
||||||
if (!fd.good())
|
|
||||||
MAKE_ERROR("!good(): " << fd.exceptions());
|
|
||||||
} catch(const std::exception &exc) {
|
|
||||||
LOG.error() << "Не удалось считать ресурс " << path.c_str() << ": " << exc.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadedQueue.lock()->emplace_back(hash, std::move(data));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
wait:
|
|
||||||
TOS::Time::sleep3(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG.info() << "Поток чтения остановлен";
|
|
||||||
lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CacheHandlerBasic::readWriteThread(AsyncUseControl::Lock lock) {
|
|
||||||
LOG.info() << "Поток чтения/записи запущен";
|
LOG.info() << "Поток чтения/записи запущен";
|
||||||
|
|
||||||
while(!NeedShutdown || !WriteQueue.get_read().empty()) {
|
while(!NeedShutdown || !WriteQueue.get_read().empty()) {
|
||||||
if(!ReadQueue.get_read().empty()) {
|
if(!ReadQueue.get_read().empty()) {
|
||||||
auto lock = ReadQueue.lock();
|
auto lock = ReadQueue.lock();
|
||||||
if(!lock->empty()) {
|
if(!lock->empty()) {
|
||||||
CacheDatabase::HASH hash = lock->front();
|
Hash_t hash = lock->front();
|
||||||
lock->pop();
|
lock->pop();
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
@@ -448,11 +364,11 @@ void CacheHandlerBasic::readWriteThread(AsyncUseControl::Lock lock) {
|
|||||||
if(ssize_t free = ssize_t(MaxCacheDirectorySize)-DB.getCacheSize(); free < task.Data->size()) {
|
if(ssize_t free = ssize_t(MaxCacheDirectorySize)-DB.getCacheSize(); free < task.Data->size()) {
|
||||||
// Недостаточно места, сколько необходимо освободить с запасом
|
// Недостаточно места, сколько необходимо освободить с запасом
|
||||||
ssize_t need = task.Data->size()-free + 64*1024*1024;
|
ssize_t need = task.Data->size()-free + 64*1024*1024;
|
||||||
std::vector<CacheDatabase::HASH> hashes = DB.findExcessHashes(need, time(nullptr)-MaxLifeTime);
|
std::vector<Hash_t> hashes = DB.findExcessHashes(need, time(nullptr)-MaxLifeTime);
|
||||||
|
|
||||||
LOG.warn() << "Удаление устаревшего кеша в количестве " << hashes.size() << "...";
|
LOG.warn() << "Удаление устаревшего кеша в количестве " << hashes.size() << "...";
|
||||||
|
|
||||||
for(CacheDatabase::HASH hash : hashes) {
|
for(Hash_t hash : hashes) {
|
||||||
std::string name = CacheDatabase::hashToString(hash);
|
std::string name = CacheDatabase::hashToString(hash);
|
||||||
fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
|
fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
|
||||||
DB.remove(hash);
|
DB.remove(hash);
|
||||||
@@ -496,80 +412,46 @@ void CacheHandlerBasic::readWriteThread(AsyncUseControl::Lock lock) {
|
|||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheHandlerBasic::CacheHandlerBasic(boost::asio::io_context &ioc, const fs::path &cachePath,
|
ResourceHandler::ResourceHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
|
||||||
size_t maxCacheDirectorySize, size_t maxLifeTime)
|
size_t maxCacheDirectorySize, size_t maxLifeTime)
|
||||||
: CacheHandler(ioc, cachePath, maxCacheDirectorySize, maxLifeTime),
|
: IAsyncDestructible(ioc),
|
||||||
ReadThread(&CacheHandlerBasic::readThread, this, AUC.use()),
|
OffThread(&ResourceHandler::readWriteThread, this, AUC.use())
|
||||||
ReadWriteThread(&CacheHandlerBasic::readWriteThread, this, AUC.use())
|
|
||||||
{
|
{
|
||||||
LOG.info() << "Инициализировано хранилище кеша: " << cachePath.c_str();
|
LOG.info() << "Инициализировано хранилище кеша: " << cachePath.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheHandlerBasic::~CacheHandlerBasic() {
|
|
||||||
ReadThread.join();
|
|
||||||
ReadWriteThread.join();
|
|
||||||
LOG.info() << "ДеИнициализировано хранилище кеша: " << Path.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CacheHandlerBasic::pushWrite(std::string &&data, CacheDatabase::HASH hash) {
|
// void ResourceHandler::updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) {
|
||||||
std::shared_ptr<std::string> dat = std::make_shared<std::string>(std::move(data));
|
// MaxLifeTime = maxLifeTime;
|
||||||
WriteCache.lock()->push_back({hash, dat});
|
|
||||||
WriteQueue.lock()->push({hash, dat});
|
|
||||||
}
|
|
||||||
|
|
||||||
void CacheHandlerBasic::pushRead(CacheDatabase::HASH hash) {
|
// if(MaxCacheDirectorySize != maxCacheDirectorySize) {
|
||||||
ReadQueue.lock()->push(hash);
|
// MaxCacheDirectorySize = maxCacheDirectorySize;
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<CacheDatabase::HASH, std::string>> CacheHandlerBasic::pullReads() {
|
// size_t size = DB.getCacheSize();
|
||||||
std::vector<DataTask> data;
|
// if(size > maxCacheDirectorySize) {
|
||||||
|
// size_t needToFree = size-maxCacheDirectorySize+64*1024*1024;
|
||||||
|
// try {
|
||||||
|
// LOG.info() << "Начата вычистка кеша на сумму " << needToFree/1024/1024 << " Мб";
|
||||||
|
// std::vector<Hash_t> hashes = DB.findExcessHashes(needToFree, time(nullptr)-MaxLifeTime);
|
||||||
|
// LOG.warn() << "Удаление кеша в количестве " << hashes.size() << "...";
|
||||||
|
|
||||||
{
|
// for(Hash_t hash : hashes) {
|
||||||
auto lock = ReadedQueue.lock();
|
// std::string name = CacheDatabase::hashToString(hash);
|
||||||
data = std::move(*lock);
|
// fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
|
||||||
}
|
// DB.remove(hash);
|
||||||
|
// fs::remove(path);
|
||||||
|
|
||||||
std::vector<std::pair<CacheDatabase::HASH, std::string>> out;
|
// fs::path up1 = path.parent_path();
|
||||||
out.reserve(data.size());
|
// LOG.info() << "В директории " << up1.c_str() << " не осталось файлов, удаляем...";
|
||||||
|
// size_t count = std::distance(fs::directory_iterator(up1), fs::directory_iterator());
|
||||||
for(auto &value : data) {
|
// if(count == 0)
|
||||||
out.emplace_back(value.Hash, std::move(*value.Data));
|
// fs::remove(up1);
|
||||||
}
|
// }
|
||||||
|
// } catch(const std::exception &exc) {
|
||||||
return out;
|
// LOG.error() << "Не удалось очистить кеш до новой границы: " << exc.what();
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
void CacheHandlerBasic::updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) {
|
// }
|
||||||
MaxLifeTime = maxLifeTime;
|
// }
|
||||||
|
|
||||||
if(MaxCacheDirectorySize != maxCacheDirectorySize) {
|
|
||||||
MaxCacheDirectorySize = maxCacheDirectorySize;
|
|
||||||
|
|
||||||
size_t size = DB.getCacheSize();
|
|
||||||
if(size > maxCacheDirectorySize) {
|
|
||||||
size_t needToFree = size-maxCacheDirectorySize+64*1024*1024;
|
|
||||||
try {
|
|
||||||
LOG.info() << "Начата вычистка кеша на сумму " << needToFree/1024/1024 << " Мб";
|
|
||||||
std::vector<CacheDatabase::HASH> hashes = DB.findExcessHashes(needToFree, time(nullptr)-MaxLifeTime);
|
|
||||||
LOG.warn() << "Удаление кеша в количестве " << hashes.size() << "...";
|
|
||||||
|
|
||||||
for(CacheDatabase::HASH hash : hashes) {
|
|
||||||
std::string name = CacheDatabase::hashToString(hash);
|
|
||||||
fs::path path = Path / name.substr(0, 2) / name.substr(2, 2) / name.substr(4);
|
|
||||||
DB.remove(hash);
|
|
||||||
fs::remove(path);
|
|
||||||
|
|
||||||
fs::path up1 = path.parent_path();
|
|
||||||
LOG.info() << "В директории " << up1.c_str() << " не осталось файлов, удаляем...";
|
|
||||||
size_t count = std::distance(fs::directory_iterator(up1), fs::directory_iterator());
|
|
||||||
if(count == 0)
|
|
||||||
fs::remove(up1);
|
|
||||||
}
|
|
||||||
} catch(const std::exception &exc) {
|
|
||||||
LOG.error() << "Не удалось очистить кеш до новой границы: " << exc.what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/Abstract.hpp"
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <boost/lockfree/spsc_queue.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -10,6 +14,8 @@
|
|||||||
#include <TOSAsync.hpp>
|
#include <TOSAsync.hpp>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
namespace LV::Client {
|
namespace LV::Client {
|
||||||
@@ -50,134 +56,141 @@ public:
|
|||||||
/*
|
/*
|
||||||
Создаёт линейный массив в котором подряд указаны все хэш суммы в бинарном виде и возвращает их количество
|
Создаёт линейный массив в котором подряд указаны все хэш суммы в бинарном виде и возвращает их количество
|
||||||
*/
|
*/
|
||||||
std::pair<std::string, size_t> getAllHash();
|
// std::pair<std::string, size_t> getAllHash();
|
||||||
|
|
||||||
using HASH = std::array<uint8_t, 32>;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Обновляет время использования кеша
|
Обновляет время использования кеша
|
||||||
*/
|
*/
|
||||||
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.
|
Выдаёт хэши на удаление по размеру в сумме больше 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 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 {
|
class ResourceHandler : public IAsyncDestructible {
|
||||||
protected:
|
|
||||||
const fs::path Path;
|
|
||||||
CacheDatabase DB;
|
|
||||||
size_t MaxCacheDirectorySize;
|
|
||||||
size_t MaxLifeTime;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Ptr = std::shared_ptr<CacheHandler>;
|
using Ptr = std::shared_ptr<ResourceHandler>;
|
||||||
|
|
||||||
|
struct ResourceKey {
|
||||||
|
Hash_t Hash;
|
||||||
|
EnumAssets Type;
|
||||||
|
std::string Domain, Key;
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CacheHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
|
ResourceHandler(boost::asio::io_context &ioc, const fs::path &cachePath,
|
||||||
size_t maxCacheDirectorySize, size_t maxLifeTime);
|
size_t maxCacheDatabaseSize, size_t maxLifeTime);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~CacheHandler();
|
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 ResourceHandler(ioc, cachePath, maxCacheDirectorySize, maxLifeTime));
|
||||||
|
}
|
||||||
|
|
||||||
// Добавить задачу на запись
|
// Добавить новый полученный с сервера ресурс
|
||||||
virtual void pushWrite(std::string &&data, CacheDatabase::HASH hash) = 0;
|
void pushResources(std::vector<Resource> resources) {
|
||||||
|
WriteQueue.lock()->push_range(resources);
|
||||||
|
}
|
||||||
|
|
||||||
// Добавить задачу на чтение
|
// Добавить задачи на чтение
|
||||||
virtual void pushRead(CacheDatabase::HASH hash) = 0;
|
void pushReads(std::vector<ResourceKey> keys) {
|
||||||
|
ReadQueue.lock()->push_range(keys);
|
||||||
|
}
|
||||||
|
|
||||||
// Получить считанные данные
|
// Получить считанные данные
|
||||||
virtual std::vector<std::pair<CacheDatabase::HASH, std::string>> pullReads() = 0;
|
std::vector<std::pair<Hash_t, std::optional<Resource>>> pullReads() {
|
||||||
|
return std::move(*ReadyQueue.lock());
|
||||||
// Получить список доступных ресурсов
|
}
|
||||||
std::pair<std::string, size_t> getAll();
|
|
||||||
|
|
||||||
// Размер всего хранимого кеша
|
// Размер всего хранимого кеша
|
||||||
size_t getCacheSize();
|
size_t getCacheSize() {
|
||||||
|
return DatabaseSize;
|
||||||
|
}
|
||||||
|
|
||||||
// Обновить параметры хранилища
|
// Обновить параметры хранилища кеша
|
||||||
virtual void updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) = 0;
|
void updateParams(size_t maxLifeTime, size_t maxCacheDirectorySize) {
|
||||||
};
|
auto lock = Changes.lock();
|
||||||
|
lock->MaxLifeTime = maxLifeTime;
|
||||||
|
lock->MaxCacheDatabaseSize = maxCacheDirectorySize;
|
||||||
|
lock->MaxChange = true;
|
||||||
|
}
|
||||||
|
|
||||||
class CacheHandlerBasic : public CacheHandler {
|
// Установка путей до папок assets
|
||||||
Logger LOG = "CacheHandlerBasic";
|
void setResourcePacks(std::vector<fs::path> packsAssets) {
|
||||||
|
auto lock = Changes.lock();
|
||||||
|
lock->Assets = std::move(packsAssets);
|
||||||
|
lock->AssetsChange = true;
|
||||||
|
}
|
||||||
|
|
||||||
struct DataTask {
|
// Запуск процедуры проверки хешей всего хранимого кеша
|
||||||
CacheDatabase::HASH Hash;
|
void runFullDatabaseRecheck(std::move_only_function<void(std::string result)>&& func) {
|
||||||
std::shared_ptr<std::string> Data;
|
auto lock = Changes.lock();
|
||||||
};
|
lock->OnRecheckEnd = std::move(func);
|
||||||
|
lock->FullRecheck = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Очередь задач на чтение
|
// Уведомление о завершении работы
|
||||||
SpinlockObject<std::queue<CacheDatabase::HASH>> ReadQueue;
|
void prepareShutdown() {
|
||||||
// Кэш данных, которые ещё не записались
|
NeedShutdown = true;
|
||||||
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;
|
// После этого вызова уже нельзя будет обращатся ко внешним ресурсам
|
||||||
|
void shutdown() {
|
||||||
public:
|
OffThread.join();
|
||||||
using Ptr = std::shared_ptr<CacheHandlerBasic>;
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual coro<> asyncDestructor() override;
|
Logger LOG = "Client>ResourceHandler";
|
||||||
|
const fs::path CachePath;
|
||||||
|
volatile size_t DatabaseSize = 0;
|
||||||
|
|
||||||
void readThread(AsyncUseControl::Lock lock);
|
// Очередь задач на чтение
|
||||||
|
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);
|
void readWriteThread(AsyncUseControl::Lock lock);
|
||||||
|
|
||||||
protected:
|
|
||||||
CacheHandlerBasic(boost::asio::io_context &ioc, const fs::path& cachePath,
|
|
||||||
size_t maxCacheDirectorySize, size_t maxLifeTime);
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~CacheHandlerBasic();
|
|
||||||
|
|
||||||
static std::shared_ptr<CacheHandlerBasic> 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef LUAVOX_HAVE_LIBURING
|
|
||||||
|
|
||||||
class CacheHandlerUring : public CacheHandler {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
#include "Abstract.hpp"
|
#include "Abstract.hpp"
|
||||||
#include "Common/Net.hpp"
|
#include "Common/Net.hpp"
|
||||||
#include "TOSLib.hpp"
|
#include "TOSLib.hpp"
|
||||||
|
#include <boost/interprocess/file_mapping.hpp>
|
||||||
|
#include <boost/interprocess/mapped_region.hpp>
|
||||||
#include "boost/json.hpp"
|
#include "boost/json.hpp"
|
||||||
|
#include "sha2.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/iostreams/filtering_streambuf.hpp>
|
#include <boost/iostreams/filtering_streambuf.hpp>
|
||||||
#include <boost/iostreams/copy.hpp>
|
#include <boost/iostreams/copy.hpp>
|
||||||
@@ -16,6 +19,8 @@
|
|||||||
|
|
||||||
namespace LV {
|
namespace LV {
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
|
||||||
CompressedVoxels compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
|
CompressedVoxels compressVoxels_byte(const std::vector<VoxelCube>& voxels) {
|
||||||
std::u8string compressed;
|
std::u8string compressed;
|
||||||
@@ -1970,4 +1975,49 @@ std::u8string PreparedModel::dump() const {
|
|||||||
return result.complite();
|
return result.complite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Resource::InlineMMap {
|
||||||
|
boost::interprocess::file_mapping MMap;
|
||||||
|
boost::interprocess::mapped_region Region;
|
||||||
|
Hash_t Hash;
|
||||||
|
|
||||||
|
InlineMMap(fs::path path)
|
||||||
|
: MMap(path.c_str(), boost::interprocess::read_only),
|
||||||
|
Region(MMap, boost::interprocess::read_only)
|
||||||
|
{
|
||||||
|
Hash = sha2::sha256((const uint8_t*) Region.get_address(), Region.get_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::byte* data() const { return (const std::byte*) Region.get_address(); }
|
||||||
|
size_t size() const { return Region.get_size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Resource::InlinePtr {
|
||||||
|
std::vector<uint8_t> Data;
|
||||||
|
Hash_t Hash;
|
||||||
|
|
||||||
|
InlinePtr(const uint8_t* data, size_t size) {
|
||||||
|
Data.resize(size);
|
||||||
|
std::copy(data, data+size, Data.data());
|
||||||
|
Hash = sha2::sha256(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::byte* data() const { return (const std::byte*) Data.data(); }
|
||||||
|
size_t size() const { return Data.size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Resource::Resource(fs::path path)
|
||||||
|
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlineMMap(path)))
|
||||||
|
{}
|
||||||
|
|
||||||
|
Resource::Resource(const uint8_t* data, size_t size)
|
||||||
|
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlinePtr(data, size)))
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::byte* Resource::data() const { assert(In); return std::visit<const std::byte*>([](auto& obj){ return obj.data(); }, *In); }
|
||||||
|
size_t Resource::size() const { assert(In); return std::visit<size_t>([](auto& obj){ return obj.size(); }, *In); }
|
||||||
|
Hash_t Resource::hash() const { assert(In); return std::visit<Hash_t>([](auto& obj){ return obj.Hash; }, *In); }
|
||||||
|
|
||||||
|
auto Resource::operator<=>(const Resource&) const = default;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -691,6 +691,29 @@ inline std::pair<std::string, std::string> parseDomainKey(const std::string& val
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct Resource {
|
||||||
|
private:
|
||||||
|
struct InlineMMap;
|
||||||
|
struct InlinePtr;
|
||||||
|
|
||||||
|
std::shared_ptr<std::variant<InlineMMap, InlinePtr>> In;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Resource(std::filesystem::path path);
|
||||||
|
Resource(const uint8_t* data, size_t size);
|
||||||
|
|
||||||
|
Resource(const Resource&) = default;
|
||||||
|
Resource(Resource&&) = default;
|
||||||
|
Resource& operator=(const Resource&) = default;
|
||||||
|
Resource& operator=(Resource&&) = default;
|
||||||
|
auto operator<=>(const Resource&) const;
|
||||||
|
|
||||||
|
const std::byte* data() const;
|
||||||
|
size_t size() const;
|
||||||
|
Hash_t hash() const;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,8 @@
|
|||||||
#include "Common/Abstract.hpp"
|
#include "Common/Abstract.hpp"
|
||||||
#include "TOSLib.hpp"
|
#include "TOSLib.hpp"
|
||||||
#include "Common/Net.hpp"
|
#include "Common/Net.hpp"
|
||||||
#include "assets.hpp"
|
|
||||||
#include "sha2.hpp"
|
#include "sha2.hpp"
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <boost/interprocess/file_mapping.hpp>
|
|
||||||
#include <boost/interprocess/mapped_region.hpp>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -18,60 +15,6 @@ namespace LV::Server {
|
|||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
struct Resource {
|
|
||||||
private:
|
|
||||||
struct InlineMMap {
|
|
||||||
boost::interprocess::file_mapping MMap;
|
|
||||||
boost::interprocess::mapped_region Region;
|
|
||||||
Hash_t Hash;
|
|
||||||
|
|
||||||
InlineMMap(fs::path path)
|
|
||||||
: MMap(path.c_str(), boost::interprocess::read_only),
|
|
||||||
Region(MMap, boost::interprocess::read_only)
|
|
||||||
{
|
|
||||||
Hash = sha2::sha256((const uint8_t*) Region.get_address(), Region.get_size());
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::byte* data() const { return (const std::byte*) Region.get_address(); }
|
|
||||||
size_t size() const { return Region.get_size(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InlinePtr {
|
|
||||||
std::vector<uint8_t> Data;
|
|
||||||
Hash_t Hash;
|
|
||||||
|
|
||||||
InlinePtr(const uint8_t* data, size_t size) {
|
|
||||||
Data.resize(size);
|
|
||||||
std::copy(data, data+size, Data.data());
|
|
||||||
Hash = sha2::sha256(data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::byte* data() const { return (const std::byte*) Data.data(); }
|
|
||||||
size_t size() const { return Data.size(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<std::variant<InlineMMap, InlinePtr>> In;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Resource(fs::path path)
|
|
||||||
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlineMMap(path)))
|
|
||||||
{}
|
|
||||||
|
|
||||||
Resource(const uint8_t* data, size_t size)
|
|
||||||
: In(std::make_shared<std::variant<InlineMMap, InlinePtr>>(InlinePtr(data, size)))
|
|
||||||
{}
|
|
||||||
|
|
||||||
Resource(const Resource&) = default;
|
|
||||||
Resource(Resource&&) = default;
|
|
||||||
Resource& operator=(const Resource&) = default;
|
|
||||||
Resource& operator=(Resource&&) = default;
|
|
||||||
bool operator<=>(const Resource&) const = default;
|
|
||||||
|
|
||||||
const std::byte* data() const { return std::visit<const std::byte*>([](auto& obj){ return obj.data(); }, *In); }
|
|
||||||
size_t size() const { return std::visit<size_t>([](auto& obj){ return obj.size(); }, *In); }
|
|
||||||
Hash_t hash() const { return std::visit<Hash_t>([](auto& obj){ return obj.Hash; }, *In); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Используется для расчёта коллизии,
|
Используется для расчёта коллизии,
|
||||||
если это необходимо.
|
если это необходимо.
|
||||||
|
|||||||
Reference in New Issue
Block a user