cache server icon results

This commit is contained in:
HJfod 2024-03-10 13:26:47 +02:00
parent e74e56e8ff
commit ff5af54b07
2 changed files with 139 additions and 1 deletions

View file

@ -97,4 +97,140 @@ namespace server {
ServerPromise<ServerModsList> getMods(ModsQuery const& query);
ServerPromise<ServerModMetadata> getMod(std::string const& id);
ServerPromise<ByteVector> getModLogo(std::string const& id);
// Caching for server endpoints
namespace impl_cache {
template <class R, class Q>
struct ExtractServerReqParams {
using Result = R;
using Query = Q;
ExtractServerReqParams(ServerPromise<R>(*)(Q const&)) {}
};
template <auto F>
class ServerResultCache final {
public:
using Extract = decltype(ExtractServerReqParams(F));
using Result = Extract::Result;
using Query = Extract::Query;
private:
std::mutex m_cachedMutex;
// The result and the query used for cached value
std::optional<std::pair<Result, Query>> m_cached;
ServerPromise<Result> fetch(Query&& query) {
return ServerPromise<Result>([this, query = std::move(query)](auto resolve, auto reject, auto progress, auto cancelled) {
F(Query(query))
.then([this, resolve, query = std::move(query)](auto res) {
std::unique_lock lock(m_cachedMutex);
m_cached = { res, query };
lock.unlock();
resolve(res);
})
.expect([reject](auto err) {
reject(err);
})
.progress([progress](auto prog) {
progress(prog);
})
.link(cancelled);
});
}
public:
ServerPromise<Result> get(Query&& query) {
std::unique_lock lock(m_cachedMutex);
// Return cached value if there is one and the query matches
if (m_cached && m_cached->second == query) {
auto cached = m_cached->first;
lock.unlock();
return ServerPromise<Result>([cached = std::move(cached)](auto resolve, auto) {
resolve(std::move(cached));
});
}
lock.unlock();
return this->fetch(std::move(query));
}
ServerPromise<Result> refetch(Query&& query) {
// Clear cache
std::unique_lock lock(m_cachedMutex);
m_cached.reset();
lock.unlock();
// Fetch new value
return this->fetch(std::move(query));
}
};
template <auto F>
class ServerMultiResultCache final {
public:
using Extract = decltype(ExtractServerReqParams(F));
using Result = Extract::Result;
using Query = Extract::Query;
private:
std::mutex m_queriesMutex;
std::map<Query, Result> m_queries;
ServerPromise<Result> fetch(Query const& query) {
return ServerPromise<Result>([this, query = Query(query)](auto resolve, auto reject, auto progress, auto cancelled) {
F(Query(query))
.then([this, resolve, query = std::move(query)](auto res) {
std::unique_lock lock(m_queriesMutex);
m_queries[std::move(query)] = res;
lock.unlock();
resolve(res);
})
.expect([reject](auto err) {
reject(err);
})
.progress([progress](auto prog) {
progress(prog);
})
.link(cancelled);
});
}
public:
ServerPromise<Result> get(Query const& query) {
std::unique_lock lock(m_queriesMutex);
// Return cached value if there is one and the query matches
if (m_queries.contains(query)) {
auto cached = m_queries.at(query);
lock.unlock();
return ServerPromise<Result>([cached = std::move(cached)](auto resolve, auto) {
resolve(std::move(cached));
});
}
lock.unlock();
return this->fetch(std::move(query));
}
ServerPromise<Result> refetch(Query const& query) {
// Clear cache for this query only
std::unique_lock lock (m_queriesMutex);
m_queries.erase(query);
lock.unlock();
// Fetch new value
return this->fetch(std::move(query));
}
// Clear all caches
void invalidateAll() {
std::unique_lock _(m_queriesMutex);
m_queries.clear();
}
};
}
// todo: default shared caching for endpoints for all users
// todo: (so it can be automatically cleared for example after leaving the geode layer)
// template <auto F>
// impl_cache::ServerResultCache<F> sharedCache() {}
}

View file

@ -70,6 +70,8 @@ protected:
std::string m_modID;
CCNode* m_sprite = nullptr;
EventListener<PromiseEventFilter<ByteVector, server::ServerError>> m_listener;
// todo: use shared cache once impl'd
static inline server::impl_cache::ServerMultiResultCache<&server::getModLogo> s_cache = {};
bool init(std::string const& id, bool fetch) {
if (!CCNode::init())
@ -93,7 +95,7 @@ protected:
this->setSprite(CCSprite::create("loadingCircle.png"));
static_cast<CCSprite*>(m_sprite)->setBlendFunc({ GL_ONE, GL_ONE });
m_sprite->runAction(CCRepeatForever::create(CCRotateBy::create(1.f, 360.f)));
m_listener.setFilter(server::getModLogo(id).listen());
m_listener.setFilter(s_cache.get(id).listen());
}
return true;