Skip to content

Commit

Permalink
v0.4.5b4 - Adding request caching
Browse files Browse the repository at this point in the history
  • Loading branch information
kbuffington committed Feb 29, 2020
1 parent 702cc90 commit b3a450d
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 8 deletions.
78 changes: 78 additions & 0 deletions src/LruCache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#pragma once

#include "stdafx.h"
#include <list>
#include <unordered_map>

using namespace std;

template <typename K, typename V>
class LruCache {
public:
LruCache(unsigned int capacity) : capacity_(capacity) {};
void set(const K& k, const V& v);
bool get(const K& k, V& v);
bool erase(const K& k);
void setCacheSize(unsigned int capacity) { capacity_ = capacity; };
int getCacheSize();
private:
unsigned int capacity_;
std::list<std::pair <K, V>> cacheList;
using list_iterator = typename std::list <std::pair <K, V>> ::iterator;
std::unordered_map <K, list_iterator>cacheMap;
};

template <typename K, typename V>
int LruCache<K, V>::getCacheSize() {
int cacheSize = cacheList.size();

return cacheSize;
}

template <typename K, typename V>
void LruCache <K, V>::set(const K& k, const V& v) {
auto itr = cacheMap.find(k);

if (itr != cacheMap.end()) {
itr->second->second = v;
cacheList.splice(cacheList.begin(), cacheList, itr->second);
itr->second = cacheList.begin();
}
else {
while (cacheMap.size() >= capacity_) {
cacheMap.erase(cacheList.back().first);
cacheList.pop_back();
}
cacheList.emplace_front(k, v);
cacheMap.emplace(k, cacheList.begin());
}
}

template <typename K, typename V>
bool LruCache <K, V>::get(const K& k, V& v) {
auto itr = cacheMap.find(k);
bool ret = false;

if (itr != cacheMap.end()) {
cacheList.splice(cacheList.begin(), cacheList, itr->second);
itr->second = cacheList.begin(); // update iterator
v = itr->second->second;
ret = true;
}

return ret;
}

template <typename K, typename V>
bool LruCache <K, V>::erase(const K& k) {
auto itr = cacheMap.find(k);
bool ret = false;

if (itr != cacheMap.end()) {
cacheList.erase(itr->second);
cacheMap.erase(itr);
ret = true;
}

return ret;
}
Binary file modified src/Release/foo_musicbrainz/foo_musicbrainz.pdb
Binary file not shown.
2 changes: 1 addition & 1 deletion src/foo_musicbrainz.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace mb
{
static constexpr const char* component_title = "MusicBrainz Tagger";
static constexpr const char* component_dll_name = "foo_musicbrainz.dll";
static constexpr const char* component_version = "0.4.5b3";
static constexpr const char* component_version = "0.4.5b4";
static constexpr const char* component_info = "Copyright (C) 2009-2012 Dremora\nCopyright (C) 2015-2020 marc2003\nCopyright (C) 2020 MordredKLB\n\nBuild: " __TIME__ ", " __DATE__;
}
4 changes: 4 additions & 0 deletions src/foo_musicbrainz.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ copy /Y "$(TargetDir)$(TargetName).pdb" "%component_path%\"</Command>
<ClInclude Include="helpers.h" />
<ClInclude Include="preferences.h" />
<ClInclude Include="query.h" />
<ClInclude Include="query_cache.h" />
<ClInclude Include="request_thread.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="sha1.h" />
Expand All @@ -127,8 +128,11 @@ copy /Y "$(TargetDir)$(TargetName).pdb" "%component_path%\"</Command>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Use</PrecompiledHeader>
</ClCompile>
<ClCompile Include="helpers.cpp" />
<ClCompile Include="initquit.cpp" />
<ClCompile Include="LruCache.cpp" />
<ClCompile Include="preferences.cpp" />
<ClCompile Include="query.cpp" />
<ClCompile Include="query_cache.cpp" />
<ClCompile Include="request_thread.cpp" />
<ClCompile Include="sha1.cpp" />
<ClCompile Include="thread_pool.cpp" />
Expand Down
12 changes: 12 additions & 0 deletions src/foo_musicbrainz.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
<ClInclude Include="thread_pool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="query_cache.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="foo_musicbrainz.rc">
Expand Down Expand Up @@ -97,5 +100,14 @@
<ClCompile Include="thread_pool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="initquit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LruCache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="query_cache.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
4 changes: 4 additions & 0 deletions src/helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,8 @@ namespace mb
nullptr
);
}

uint64_t fileTimeToMilliseconds(uint64_t ft) {
return (ft - 116444736000000000 + /*rounding*/10000000 / 2) / 10000;
}
}
1 change: 1 addition & 0 deletions src/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,5 @@ namespace mb
void filter_releases(json releases, size_t count, pfc::string_list_impl& out);
void get_artist_credit(json j, str8& name, pfc::string_list_impl& ids);
void tagger(metadb_handle_list_cref handles, Release release, size_t current_disc);
uint64_t fileTimeToMilliseconds(uint64_t ft);
}
13 changes: 13 additions & 0 deletions src/initquit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "stdafx.h"
#include "thread_pool.h"

class mb_initquit : public initquit {
public:
void on_init() {
}
void on_quit() {
simple_thread_pool::instance().exit();
}
};

static initquit_factory_t<mb_initquit> g_myinitquit_factory;
35 changes: 32 additions & 3 deletions src/query.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
#include "stdafx.h"
#include "query.h"
#include "query_cache.h"
#include <mutex>

namespace mb
{
query_cache cache;
uint64_t last_http_request = 0;
std::mutex querying_mb;

query::query(pfc::stringp entity, pfc::stringp id)
{
url << prefs::get_server() << "/ws/2/" << entity;
Expand All @@ -14,19 +20,42 @@ namespace mb
{
try
{
str8 buffer;
if (cache.get(url, &buffer)) {
json j = json::parse(buffer.get_ptr(), nullptr, false);
if (j.is_object())
{
return j;
}
}
// cache miss

// Download
auto http = http_client::get();
auto request = http->create_request("GET");
request->add_header("User-Agent", PFC_string_formatter() << "foo_musicbrainz/" << component_version);
{
std::lock_guard<std::mutex> guard(querying_mb); // lock_guard is destroyed when exiting block

uint64_t now = fileTimeToMilliseconds(pfc::fileTimeNow());
if (now < last_http_request + 1000) {
uint32_t delay = (uint32_t)(last_http_request + 1000 - now);
Sleep(delay);
}
last_http_request = fileTimeToMilliseconds(pfc::fileTimeNow());
}
#ifdef DEBUG
// TODO: Remove this after beta
FB2K_console_formatter() << component_title << ": " << "Querying MB at: " << last_http_request;
#endif
auto response = request->run_ex(url, abort);

// Get string
str8 buffer;
response->read_string_raw(buffer, abort);

json j = json::parse(buffer.get_ptr(), nullptr, false);
if (j.is_object())
{
cache.set(url, buffer);
return j;
}

Expand All @@ -35,7 +64,7 @@ namespace mb
{
ptr->get_status(buffer);
}

popup_message::g_show(buffer, component_title);
return json();
}
Expand Down
50 changes: 50 additions & 0 deletions src/query_cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "stdafx.h"
#include "query_cache.h"

// cache entries expire after 1 hour
#define CACHE_EXPIRATION 3600

namespace mb {

bool query_cache::get(str8 url, str8* val)
{
int hash = hashCode(url);
CacheObj cache_obj;

bool hit = cache.get(hash, cache_obj);
if (hit) {
if (pfc::fileTimeWtoU(cache_obj.cached_time) > pfc::fileTimeWtoU(pfc::fileTimeNow()) - CACHE_EXPIRATION) {
*val = cache_obj.response;
}
else {
// stale
hit = false;
}
}
return hit;
}

void query_cache::set(str8 url, str8 response)
{
int hash = hashCode(url);
CacheObj cache_obj;

cache_obj.cached_time = pfc::fileTimeNow();
cache_obj.response = response;

cache.set(hash, cache_obj);
}

int query_cache::hashCode(str8 text) {
int hash = 0, strlen = text.length(), i;
char character;
if (strlen == 0)
return hash;
for (i = 0; i < strlen; i++) {
character = text[i];
hash = (31 * hash) + (character);
}
return hash;
}

}
21 changes: 21 additions & 0 deletions src/query_cache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once
#include "LruCache.cpp"

namespace mb {
struct CacheObj {
t_filetimestamp cached_time;
str8 response;
};

class query_cache
{
public:
query_cache() : cache(100) {}; // setting max size of cache to 100
bool get(str8 url, str8* val);
void set(str8 url, str8 response);

private:
int hashCode(str8 text);
LruCache<int, CacheObj> cache;
};
}
9 changes: 5 additions & 4 deletions src/request_thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "dialog_tagger.h"
#include "request_thread.h"
#include "thread_pool.h"
#include "query_cache.h"
#include <atomic>
#include <mutex>

Expand Down Expand Up @@ -127,16 +128,14 @@ namespace mb

for (size_t i = 0; i < count; ++i)
{
Sleep(1000);
status.set_title(PFC_string_formatter() << "Fetching release " << (i + 1) << " of " << count);
status.set_progress(i + 1, count);

/**
* this was pre-existing logic which would abort when we didn't receive anything back
* from musicbrainz. This would prevent hammering if mb is down, but also (silently!)
* fails even if we had some number of requests that already completed successfully.
* Keeping this for now, but it's subject to change later.
**/
// TOOD: This code is now worthless since all the threads are spawned before queries are
// made. We'll need to abort elsewhere if we care enough.
if (m_failed) {
FB2K_console_formatter() << component_title << ": Musicbrainz query failed.";
return;
Expand All @@ -149,6 +148,8 @@ namespace mb
Sleep(10); // wait for last thread to start
while (m_release_list.size() < thread_counter && !abort.is_aborting()) {
// Is there a better way to wait for all the threads to complete?
status.set_title(PFC_string_formatter() << "Fetching release " << (m_release_list.size() + 1) << " of " << count);
status.set_progress(1 + m_release_list.size(), count);
Sleep(10);
}
if (m_failed) {
Expand Down

0 comments on commit b3a450d

Please sign in to comment.