Skip to content
Snippets Groups Projects
Commit 4b3269c9 authored by Rider Linden's avatar Rider Linden
Browse files

Some initial changes to convert the experience cache to a singleton

--HG--
branch : MAINT-5575
parent 99e56eed
No related branches found
No related tags found
No related merge requests found
...@@ -32,22 +32,18 @@ ...@@ -32,22 +32,18 @@
#include <set> #include <set>
#include <map> #include <map>
#include "boost/tokenizer.hpp" #include "boost/tokenizer.hpp"
#include <boost/concept_check.hpp>
typedef std::map<LLUUID, LLUUID> KeyMap; typedef std::map<LLUUID, LLUUID> KeyMap;
KeyMap privateToPublicKeyMap; KeyMap privateToPublicKeyMap;
void mapKeys(const LLSD& legacyKeys);
std::string sLookupURL; std::string sLookupURL;
typedef std::map<LLUUID, std::string> ask_queue_t;
ask_queue_t sAskQueue;
typedef std::map<LLUUID, F64> pending_queue_t; typedef std::map<LLUUID, F64> pending_queue_t;
pending_queue_t sPendingQueue; pending_queue_t sPendingQueue;
LLExperienceCache::cache_t sCache;
int sMaximumLookups = 10; int sMaximumLookups = 10;
LLFrameTimer sRequestTimer; LLFrameTimer sRequestTimer;
...@@ -55,16 +51,41 @@ LLFrameTimer sRequestTimer; ...@@ -55,16 +51,41 @@ LLFrameTimer sRequestTimer;
// Periodically clean out expired entries from the cache // Periodically clean out expired entries from the cache
LLFrameTimer sEraseExpiredTimer; LLFrameTimer sEraseExpiredTimer;
// May have multiple callbacks for a single ID, which are
// represented as multiple slots bound to the signal.
// Avoid copying signals via pointers.
typedef std::map<LLUUID, callback_signal_t*> signal_map_t;
signal_map_t sSignalMap;
bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age); //=========================================================================
void eraseExpired(); namespace LLExperienceCacheImpl
{
bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
void mapKeys(const LLSD& legacyKeys);
}
//=========================================================================
const std::string LLExperienceCache::PRIVATE_KEY = "private_id";
const std::string LLExperienceCache::MISSING = "DoesNotExist";
const std::string LLExperienceCache::AGENT_ID = "agent_id";
const std::string LLExperienceCache::GROUP_ID = "group_id";
const std::string LLExperienceCache::EXPERIENCE_ID = "public_id";
const std::string LLExperienceCache::NAME = "name";
const std::string LLExperienceCache::PROPERTIES = "properties";
const std::string LLExperienceCache::EXPIRES = "expiration";
const std::string LLExperienceCache::DESCRIPTION = "description";
const std::string LLExperienceCache::QUOTA = "quota";
const std::string LLExperienceCache::MATURITY = "maturity";
const std::string LLExperienceCache::METADATA = "extended_metadata";
const std::string LLExperienceCache::SLURL = "slurl";
// should be in sync with experience-api/experiences/models.py
const int LLExperienceCache::PROPERTY_INVALID = 1 << 0;
const int LLExperienceCache::PROPERTY_PRIVILEGED = 1 << 3;
const int LLExperienceCache::PROPERTY_GRID = 1 << 4;
const int LLExperienceCache::PROPERTY_PRIVATE = 1 << 5;
const int LLExperienceCache::PROPERTY_DISABLED = 1 << 6;
const int LLExperienceCache::PROPERTY_SUSPENDED = 1 << 7;
// default values
const F64 LLExperienceCache::DEFAULT_EXPIRATION = 600.0;
const S32 LLExperienceCache::DEFAULT_QUOTA = 128; // this is megabytes
//========================================================================= //=========================================================================
LLExperienceCache::LLExperienceCache() LLExperienceCache::LLExperienceCache()
...@@ -118,7 +139,7 @@ void LLExperienceCache::exportFile(std::ostream& ostr) const ...@@ -118,7 +139,7 @@ void LLExperienceCache::exportFile(std::ostream& ostr) const
// *TODO$: Rider: These three functions not seem to be used... it may be useful in testing. // *TODO$: Rider: These three functions not seem to be used... it may be useful in testing.
void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration) void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration)
{ {
mapKeys(legacyKeys); LLExperienceCacheImpl::mapKeys(legacyKeys);
LLSD::array_const_iterator it = legacyKeys.beginArray(); LLSD::array_const_iterator it = legacyKeys.beginArray();
for (/**/; it != legacyKeys.endArray(); ++it) for (/**/; it != legacyKeys.endArray(); ++it)
{ {
...@@ -140,18 +161,6 @@ void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration) ...@@ -140,18 +161,6 @@ void LLExperienceCache::bootstrap(const LLSD& legacyKeys, int initialExpiration)
} }
} }
void LLExperienceCache::mapKeys(const LLSD& legacyKeys)
{
LLSD::array_const_iterator exp = legacyKeys.beginArray();
for (/**/; exp != legacyKeys.endArray(); ++exp)
{
if (exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY))
{
privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()] = (*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID();
}
}
}
LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found) LLUUID LLExperienceCache::getExperienceId(const LLUUID& private_key, bool null_if_not_found)
{ {
if (private_key.isNull()) if (private_key.isNull())
...@@ -190,12 +199,10 @@ void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD& ...@@ -190,12 +199,10 @@ void LLExperienceCache::processExperience(const LLUUID& public_key, const LLSD&
signal_map_t::iterator sig_it = sSignalMap.find(public_key); signal_map_t::iterator sig_it = sSignalMap.find(public_key);
if (sig_it != sSignalMap.end()) if (sig_it != sSignalMap.end())
{ {
callback_signal_t* signal = sig_it->second; signal_ptr signal = sig_it->second;
(*signal)(experience); (*signal)(experience);
sSignalMap.erase(public_key); sSignalMap.erase(public_key);
delete signal;
} }
} }
...@@ -236,60 +243,6 @@ static const std::string MAX_AGE("max-age"); ...@@ -236,60 +243,6 @@ static const std::string MAX_AGE("max-age");
static const boost::char_separator<char> EQUALS_SEPARATOR("="); static const boost::char_separator<char> EQUALS_SEPARATOR("=");
static const boost::char_separator<char> COMMA_SEPARATOR(","); static const boost::char_separator<char> COMMA_SEPARATOR(",");
bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
{
// Split the string on "," to get a list of directives
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
tokenizer directives(cache_control, COMMA_SEPARATOR);
tokenizer::iterator token_it = directives.begin();
for ( ; token_it != directives.end(); ++token_it)
{
// Tokens may have leading or trailing whitespace
std::string token = *token_it;
LLStringUtil::trim(token);
if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
{
// ...this token starts with max-age, so let's chop it up by "="
tokenizer subtokens(token, EQUALS_SEPARATOR);
tokenizer::iterator subtoken_it = subtokens.begin();
// Must have a token
if (subtoken_it == subtokens.end()) return false;
std::string subtoken = *subtoken_it;
// Must exactly equal "max-age"
LLStringUtil::trim(subtoken);
if (subtoken != MAX_AGE) return false;
// Must have another token
++subtoken_it;
if (subtoken_it == subtokens.end()) return false;
subtoken = *subtoken_it;
// Must be a valid integer
// *NOTE: atoi() returns 0 for invalid values, so we have to
// check the string first.
// *TODO: Do servers ever send "0000" for zero? We don't handle it
LLStringUtil::trim(subtoken);
if (subtoken == "0")
{
*max_age = 0;
return true;
}
S32 val = atoi( subtoken.c_str() );
if (val > 0 && val < S32_MAX)
{
*max_age = val;
return true;
}
return false;
}
}
return false;
}
class LLExperienceResponder : public LLHTTPClient::Responder class LLExperienceResponder : public LLHTTPClient::Responder
{ {
...@@ -435,7 +388,6 @@ void LLExperienceCache::requestExperiences() ...@@ -435,7 +388,6 @@ void LLExperienceCache::requestExperiences()
ostr << sLookupURL << "?page_size=" << PAGE_SIZE; ostr << sLookupURL << "?page_size=" << PAGE_SIZE;
int request_count = 0; int request_count = 0;
while(!sAskQueue.empty() && request_count < sMaximumLookups) while(!sAskQueue.empty() && request_count < sMaximumLookups)
{ {
...@@ -503,7 +455,6 @@ bool LLExperienceCache::hasLookupURL() ...@@ -503,7 +455,6 @@ bool LLExperienceCache::hasLookupURL()
void LLExperienceCache::idle() void LLExperienceCache::idle()
{ {
const F32 SECS_BETWEEN_REQUESTS = 0.1f; const F32 SECS_BETWEEN_REQUESTS = 0.1f;
if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS)) if (!sRequestTimer.checkExpirationAndReset(SECS_BETWEEN_REQUESTS))
{ {
...@@ -517,7 +468,6 @@ void LLExperienceCache::idle() ...@@ -517,7 +468,6 @@ void LLExperienceCache::idle()
eraseExpired(); eraseExpired();
} }
if(!sAskQueue.empty()) if(!sAskQueue.empty())
{ {
requestExperiences(); requestExperiences();
...@@ -568,7 +518,6 @@ void LLExperienceCache::eraseExpired() ...@@ -568,7 +518,6 @@ void LLExperienceCache::eraseExpired()
} }
} }
} }
bool LLExperienceCache::fetch(const LLUUID& key, bool refresh/* = true*/) bool LLExperienceCache::fetch(const LLUUID& key, bool refresh/* = true*/)
{ {
...@@ -594,10 +543,12 @@ void LLExperienceCache::insert(const LLSD& experience_data) ...@@ -594,10 +543,12 @@ void LLExperienceCache::insert(const LLSD& experience_data)
} }
} }
static LLSD empty;
const LLSD& LLExperienceCache::get(const LLUUID& key) const LLSD& LLExperienceCache::get(const LLUUID& key)
{ {
if(key.isNull()) return empty; static const LLSD empty;
if(key.isNull())
return empty;
cache_t::const_iterator it = sCache.find(key); cache_t::const_iterator it = sCache.find(key);
if (it != sCache.end()) if (it != sCache.end())
...@@ -609,9 +560,10 @@ const LLSD& LLExperienceCache::get(const LLUUID& key) ...@@ -609,9 +560,10 @@ const LLSD& LLExperienceCache::get(const LLUUID& key)
return empty; return empty;
} }
void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot) void LLExperienceCache::get(const LLUUID& key, LLExperienceCache::Callback_t slot)
{ {
if(key.isNull()) return; if(key.isNull())
return;
cache_t::const_iterator it = sCache.find(key); cache_t::const_iterator it = sCache.find(key);
if (it != sCache.end()) if (it != sCache.end())
...@@ -626,12 +578,20 @@ void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot) ...@@ -626,12 +578,20 @@ void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot)
fetch(key); fetch(key);
signal_ptr signal = signal_ptr(new callback_signal_t());
std::pair<signal_map_t::iterator, bool> result = sSignalMap.insert(signal_map_t::value_type(key, signal));
if (!result.second)
signal = result.first.second;
signal->connect(slot);
#if 0
// always store additional callback, even if request is pending // always store additional callback, even if request is pending
signal_map_t::iterator sig_it = sSignalMap.find(key); signal_map_t::iterator sig_it = sSignalMap.find(key);
if (sig_it == sSignalMap.end()) if (sig_it == sSignalMap.end())
{ {
// ...new callback for this id // ...new callback for this id
callback_signal_t* signal = new callback_signal_t(); signal_ptr signal = signal_ptr(new callback_signal_t());
signal->connect(slot); signal->connect(slot);
sSignalMap[key] = signal; sSignalMap[key] = signal;
} }
...@@ -641,6 +601,76 @@ void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot) ...@@ -641,6 +601,76 @@ void LLExperienceCache::get(const LLUUID& key, callback_slot_t slot)
callback_signal_t* signal = sig_it->second; callback_signal_t* signal = sig_it->second;
signal->connect(slot); signal->connect(slot);
} }
#endif
}
//=========================================================================
void LLExperienceCacheImpl::mapKeys(const LLSD& legacyKeys)
{
LLSD::array_const_iterator exp = legacyKeys.beginArray();
for (/**/; exp != legacyKeys.endArray(); ++exp)
{
if (exp->has(LLExperienceCache::EXPERIENCE_ID) && exp->has(LLExperienceCache::PRIVATE_KEY))
{
privateToPublicKeyMap[(*exp)[LLExperienceCache::PRIVATE_KEY].asUUID()] = (*exp)[LLExperienceCache::EXPERIENCE_ID].asUUID();
}
}
} }
bool LLExperienceCacheImpl::max_age_from_cache_control(const std::string& cache_control, S32 *max_age)
{
// Split the string on "," to get a list of directives
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
tokenizer directives(cache_control, COMMA_SEPARATOR);
tokenizer::iterator token_it = directives.begin();
for ( ; token_it != directives.end(); ++token_it)
{
// Tokens may have leading or trailing whitespace
std::string token = *token_it;
LLStringUtil::trim(token);
if (token.compare(0, MAX_AGE.size(), MAX_AGE) == 0)
{
// ...this token starts with max-age, so let's chop it up by "="
tokenizer subtokens(token, EQUALS_SEPARATOR);
tokenizer::iterator subtoken_it = subtokens.begin();
// Must have a token
if (subtoken_it == subtokens.end()) return false;
std::string subtoken = *subtoken_it;
// Must exactly equal "max-age"
LLStringUtil::trim(subtoken);
if (subtoken != MAX_AGE) return false;
// Must have another token
++subtoken_it;
if (subtoken_it == subtokens.end()) return false;
subtoken = *subtoken_it;
// Must be a valid integer
// *NOTE: atoi() returns 0 for invalid values, so we have to
// check the string first.
// *TODO: Do servers ever send "0000" for zero? We don't handle it
LLStringUtil::trim(subtoken);
if (subtoken == "0")
{
*max_age = 0;
return true;
}
S32 val = atoi( subtoken.c_str() );
if (val > 0 && val < S32_MAX)
{
*max_age = val;
return true;
}
return false;
}
}
return false;
}
...@@ -56,10 +56,45 @@ class LLExperienceCache: public LLSingleton < LLExperienceCache > ...@@ -56,10 +56,45 @@ class LLExperienceCache: public LLSingleton < LLExperienceCache >
private: private:
// Callback types for get() // Callback types for get()
// typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t; typedef boost::signals2::signal < void(const LLSD &) > callback_signal_t;
typedef boost::signals2::signal < Callback_t > callback_signal_t; typedef boost::shared_ptr<callback_signal_t> signal_ptr;
typedef std::map<LLUUID, LLSD> cache_t; // May have multiple callbacks for a single ID, which are
// represented as multiple slots bound to the signal.
// Avoid copying signals via pointers.
typedef std::map<LLUUID, signal_ptr> signal_map_t;
typedef std::map<LLUUID, LLSD> cache_t;
typedef std::set<LLUUID> ask_queue_t;
//--------------------------------------------
static const std::string PRIVATE_KEY; // "private_id"
static const std::string MISSING; // "DoesNotExist"
static const std::string AGENT_ID; // "agent_id"
static const std::string GROUP_ID; // "group_id"
static const std::string EXPERIENCE_ID; // "public_id"
static const std::string NAME; // "name"
static const std::string PROPERTIES; // "properties"
static const std::string EXPIRES; // "expiration"
static const std::string DESCRIPTION; // "description"
static const std::string QUOTA; // "quota"
static const std::string MATURITY; // "maturity"
static const std::string METADATA; // "extended_metadata"
static const std::string SLURL; // "slurl"
// should be in sync with experience-api/experiences/models.py
static const int PROPERTY_INVALID; // 1 << 0
static const int PROPERTY_PRIVILEGED; // 1 << 3
static const int PROPERTY_GRID; // 1 << 4
static const int PROPERTY_PRIVATE; // 1 << 5
static const int PROPERTY_DISABLED; // 1 << 6
static const int PROPERTY_SUSPENDED; // 1 << 7
// default values
static const F64 DEFAULT_EXPIRATION; // 600.0
static const S32 DEFAULT_QUOTA; // 128 this is megabytes
//-------------------------------------------- //--------------------------------------------
LLExperienceCache(); LLExperienceCache();
virtual ~LLExperienceCache(); virtual ~LLExperienceCache();
...@@ -70,36 +105,13 @@ class LLExperienceCache: public LLSingleton < LLExperienceCache > ...@@ -70,36 +105,13 @@ class LLExperienceCache: public LLSingleton < LLExperienceCache >
//-------------------------------------------- //--------------------------------------------
void processExperience(const LLUUID& public_key, const LLSD& experience); void processExperience(const LLUUID& public_key, const LLSD& experience);
//--------------------------------------------
const std::string PRIVATE_KEY = "private_id"; cache_t sCache;
const std::string MISSING = "DoesNotExist"; signal_map_t sSignalMap;
ask_queue_t sAskQueue;
const std::string AGENT_ID = "agent_id";
const std::string GROUP_ID = "group_id"; void eraseExpired();
const std::string EXPERIENCE_ID = "public_id";
const std::string NAME = "name";
const std::string PROPERTIES = "properties";
const std::string EXPIRES = "expiration";
const std::string DESCRIPTION = "description";
const std::string QUOTA = "quota";
const std::string MATURITY = "maturity";
const std::string METADATA = "extended_metadata";
const std::string SLURL = "slurl";
// should be in sync with experience-api/experiences/models.py
const int PROPERTY_INVALID = 1 << 0;
const int PROPERTY_PRIVILEGED = 1 << 3;
const int PROPERTY_GRID = 1 << 4;
const int PROPERTY_PRIVATE = 1 << 5;
const int PROPERTY_DISABLED = 1 << 6;
const int PROPERTY_SUSPENDED = 1 << 7;
// default values
const static F64 DEFAULT_EXPIRATION = 600.0;
const static S32 DEFAULT_QUOTA = 128; // this is megabytes
void setLookupURL(const std::string& lookup_url); void setLookupURL(const std::string& lookup_url);
bool hasLookupURL(); bool hasLookupURL();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment