summaryrefslogtreecommitdiff
path: root/indra/llmessage
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llmessage')
-rw-r--r--indra/llmessage/llexperiencecache.cpp353
-rw-r--r--indra/llmessage/llexperiencecache.h45
2 files changed, 230 insertions, 168 deletions
diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp
index 562c4c40df..18b950b61e 100644
--- a/indra/llmessage/llexperiencecache.cpp
+++ b/indra/llmessage/llexperiencecache.cpp
@@ -23,25 +23,26 @@
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
+#include "llexperiencecache.h"
-#include "linden_common.h"
#include "llavatarname.h"
#include "llframetimer.h"
#include "llhttpclient.h"
#include "llsdserialize.h"
#include <set>
#include <map>
-#include "boost\tokenizer.hpp"
+#include "boost/tokenizer.hpp"
+
-#include "llexperiencecache.h"
namespace LLExperienceCache
{
+ const std::string& MAP_KEY = PUBLIC_KEY;
std::string sLookupURL;
- typedef std::set<LLUUID> ask_queue_t;
+ typedef std::map<LLUUID, std::string> ask_queue_t;
ask_queue_t sAskQueue;
typedef std::map<LLUUID, F64> pending_queue_t;
@@ -65,20 +66,40 @@ namespace LLExperienceCache
bool max_age_from_cache_control(const std::string& cache_control, S32 *max_age);
void eraseExpired();
- void processExperience( const LLUUID& agent_id, const LLExperienceData& experience )
+ void processExperience( const LLUUID& public_key, const LLSD& experience )
{
- sCache[agent_id]=experience;
+ sCache[public_key]=experience;
+ LLSD & row = sCache[public_key];
+
+ if(row.has("expires"))
+ {
+ row["expires"] = row["expires"].asReal() + LLFrameTimer::getTotalSeconds();
+ }
+
+ if(row.has(PUBLIC_KEY))
+ {
+ sPendingQueue.erase(row[PUBLIC_KEY].asUUID());
+ }
+
+ if(row.has(PRIVATE_KEY))
+ {
+ sPendingQueue.erase(row[PRIVATE_KEY].asUUID());
+ }
+
+ if(row.has(CREATOR_KEY))
+ {
+ sPendingQueue.erase(row[CREATOR_KEY].asUUID());
+ }
- sPendingQueue.erase(agent_id);
//signal
- signal_map_t::iterator sig_it = sSignalMap.find(agent_id);
+ signal_map_t::iterator sig_it = sSignalMap.find(public_key);
if (sig_it != sSignalMap.end())
{
callback_signal_t* signal = sig_it->second;
- (*signal)(agent_id, experience);
+ (*signal)(experience);
- sSignalMap.erase(agent_id);
+ sSignalMap.erase(public_key);
delete signal;
}
@@ -109,7 +130,7 @@ namespace LLExperienceCache
std::string cache_control = cache_control_header.asString();
if (max_age_from_cache_control(cache_control, &max_age))
{
- LL_DEBUGS("ExperienceCache")
+ LL_WARNS("ExperienceCache")
<< "got expiration from headers, max_age " << max_age
<< LL_ENDL;
F64 now = LLFrameTimer::getTotalSeconds();
@@ -186,16 +207,14 @@ namespace LLExperienceCache
S32 parse_count = LLSDSerialize::fromXMLDocument(data, istr);
if(parse_count < 1) return;
- LLSD agents = data["agents"];
+ LLSD experiences = data["experiences"];
- LLUUID agent_id;
- LLExperienceData experience;
- LLSD::map_const_iterator it = agents.beginMap();
- for(; it != agents.endMap() ; ++it)
+ LLUUID public_key;
+ LLSD::map_const_iterator it = experiences.beginMap();
+ for(; it != experiences.endMap() ; ++it)
{
- agent_id.set(it->first);
- experience.fromLLSD( it->second);
- sCache[agent_id]=experience;
+ public_key.set(it->first);
+ sCache[public_key]=it->second;
}
LL_INFOS("ExperienceCache") << "loaded " << sCache.size() << LL_ENDL;
@@ -203,16 +222,20 @@ namespace LLExperienceCache
void exportFile(std::ostream& ostr)
{
- LLSD agents;
+ LLSD experiences;
cache_t::const_iterator it =sCache.begin();
for( ; it != sCache.end() ; ++it)
{
- agents[it->first.asString()] = it->second.asLLSD();
+ if(!it->second.has(PUBLIC_KEY) || it->second[PUBLIC_KEY].asUUID().isNull() ||
+ it->second.has("error"))
+ continue;
+
+ experiences[it->first.asString()] = it->second;
}
LLSD data;
- data["agents"] = agents;
+ data["experiences"] = experiences;
LLSDSerialize::toPrettyXML(data, ostr);
}
@@ -220,8 +243,8 @@ namespace LLExperienceCache
class LLExperienceResponder : public LLHTTPClient::Responder
{
public:
- LLExperienceResponder(const std::vector<LLUUID>& agent_ids)
- :mAgentIds(agent_ids)
+ LLExperienceResponder(const ask_queue_t& keys)
+ :mKeys(keys)
{
}
@@ -233,60 +256,65 @@ namespace LLExperienceCache
virtual void result(const LLSD& content)
{
- LLSD agents = content["agents"];
- LLSD::array_const_iterator it = agents.beginArray();
- for( /**/ ; it != agents.endArray(); ++it)
+ LLSD experiences = content["experience_keys"];
+ LLSD::array_const_iterator it = experiences.beginArray();
+ for( /**/ ; it != experiences.endArray(); ++it)
{
const LLSD& row = *it;
- LLUUID agent_id = row["id"].asUUID();
+ LLUUID public_key = row[PUBLIC_KEY].asUUID();
- LLExperienceData experience;
- if(experience.fromLLSD(row))
- {
- LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Received result for " << agent_id
- << "display '" << experience.mDisplayName << "'" << LL_ENDL ;
+ LL_INFOS("ExperienceCache") << "Received result for " << public_key
+ << " display '" << row[LLExperienceCache::NAME].asString() << "'" << LL_ENDL ;
- processExperience(agent_id, experience);
- }
+ processExperience(public_key, row);
}
- LLSD unresolved_agents = content["bad_ids"];
- S32 num_unresolved = unresolved_agents.size();
- if(num_unresolved > 0)
+ LLSD error_ids = content["error_ids"];
+ LLSD::map_const_iterator errIt = error_ids.beginMap();
+ for( /**/ ; errIt != error_ids.endMap() ; ++errIt )
{
- LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "Ignoring " << num_unresolved
- << " bad ids" << LL_ENDL ;
+ LLUUID id = LLUUID(errIt->first);
+ for( it = errIt->second.beginArray(); it != errIt->second.endArray() ; ++it)
+ {
+ LL_INFOS("ExperienceCache") << "Clearing error result for " << id
+ << " of type '" << it->asString() << "'" << LL_ENDL ;
+
+ erase(id, it->asString());
+ }
}
- LL_DEBUGS("ExperienceCache") << __FUNCTION__ << sCache.size() << " cached experiences" << LL_ENDL;
+ LL_INFOS("ExperienceCache") << sCache.size() << " cached experiences" << LL_ENDL;
}
- void error(U32 status, const std::string& reason)
+ virtual void error(U32 status, const std::string& reason)
{
- // We're going to construct a dummy record and cache it for a while,
- // either briefly for a 503 Service Unavailable, or longer for other
- // errors.
- F64 retry_timestamp = errorRetryTimestamp(status);
-
- LLExperienceData experience;
- experience.mDisplayName = LLExperienceCache::DUMMY_NAME;
- experience.mDescription = LLExperienceCache::DUMMY_NAME;
- experience.mExpires = retry_timestamp;
-
- // Add dummy records for all agent IDs in this request
- std::vector<LLUUID>::const_iterator it = mAgentIds.begin();
- for ( ; it != mAgentIds.end(); ++it)
+ LL_WARNS("ExperienceCache") << "Request failed "<<status<<" "<<reason<< LL_ENDL;
+ // We're going to construct a dummy record and cache it for a while,
+ // either briefly for a 503 Service Unavailable, or longer for other
+ // errors.
+ F64 retry_timestamp = errorRetryTimestamp(status);
+
+
+ // Add dummy records for all agent IDs in this request
+ ask_queue_t::const_iterator it = mKeys.begin();
+ for ( ; it != mKeys.end(); ++it)
{
- LLExperienceCache::processExperience((*it), experience);
- }
+ LLSD exp;
+ exp["expires"]=retry_timestamp;
+ exp[it->second] = it->first;
+ exp["key_type"] = it->second;
+ exp["uuid"] = it->first;
+ exp["error"] = (LLSD::Integer)status;
+ LLExperienceCache::processExperience(it->first, exp);
+ }
+
}
// Return time to retry a request that generated an error, based on
// error type and headers. Return value is seconds-since-epoch.
F64 errorRetryTimestamp(S32 status)
{
- F64 now = LLFrameTimer::getTotalSeconds();
// Retry-After takes priority
LLSD retry_after = mHeaders["retry-after"];
@@ -297,7 +325,7 @@ namespace LLExperienceCache
if (delta_seconds > 0)
{
// ...valid delta-seconds
- return now + F64(delta_seconds);
+ return F64(delta_seconds);
}
}
@@ -313,78 +341,87 @@ namespace LLExperienceCache
{
// ...service unavailable, retry soon
const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
- return now + SERVICE_UNAVAILABLE_DELAY;
+ return SERVICE_UNAVAILABLE_DELAY;
+ }
+ else if (status == 499)
+ {
+ // ...we were probably too busy, retry quickly
+ const F64 BUSY_DELAY = 10.0; // 10 seconds
+ return BUSY_DELAY;
+
}
else
{
// ...other unexpected error
const F64 DEFAULT_DELAY = 3600.0; // 1 hour
- return now + DEFAULT_DELAY;
+ return DEFAULT_DELAY;
}
}
private:
- std::vector<LLUUID> mAgentIds;
+ ask_queue_t mKeys;
LLSD mHeaders;
};
void requestExperiences()
{
- if(sAskQueue.empty())
+ if(sAskQueue.empty() || sLookupURL.empty())
return;
F64 now = LLFrameTimer::getTotalSeconds();
- const U32 NAME_URL_MAX = 4096;
- const U32 NAME_URL_SEND_THRESHOLD = 3000;
+ const U32 EXP_URL_SEND_THRESHOLD = 3000;
- std::string url;
- url.reserve(NAME_URL_MAX);
- std::vector<LLUUID> agent_ids;
- agent_ids.reserve(128);
+ std::ostringstream ostr;
- url += sLookupURL;
+ ask_queue_t keys;
- std::string arg="?ids=";
+ ostr << sLookupURL;
+
+ char arg='?';
int request_count = 0;
for(ask_queue_t::const_iterator it = sAskQueue.begin() ; it != sAskQueue.end() && request_count < sMaximumLookups; ++it)
{
- const LLUUID& agent_id = *it;
+ const LLUUID& key = it->first;
+ const std::string& key_type = it->second;
+
+ ostr << arg << key_type << '=' << key.asString() ;
- url += arg;
- url += agent_id.asString();
- agent_ids.push_back(agent_id);
+ keys[key]=key_type;
request_count++;
- sPendingQueue[agent_id] = now;
+ sPendingQueue[key] = now;
- arg[0]='&';
+ arg='&';
- if(url.size() > NAME_URL_SEND_THRESHOLD)
+ if(ostr.tellp() > EXP_URL_SEND_THRESHOLD)
{
- LLHTTPClient::get(url, new LLExperienceResponder(agent_ids));
- url = sLookupURL;
- arg[0]='?';
- agent_ids.clear();
+ LL_INFOS("ExperienceCache") << " query: " << ostr.str() << LL_ENDL;
+ LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
+ ostr.clear();
+ ostr.str(sLookupURL);
+ arg='?';
+ keys.clear();
}
}
- if(url.size() > sLookupURL.size())
+ if(ostr.tellp() > sLookupURL.size())
{
- LLHTTPClient::get(url, new LLExperienceResponder(agent_ids));
+ LL_INFOS("ExperienceCache") << " query: " << ostr.str() << LL_ENDL;
+ LLHTTPClient::get(ostr.str(), new LLExperienceResponder(keys));
}
sAskQueue.clear();
}
- bool isRequestPending(const LLUUID& agent_id)
+ bool isRequestPending(const LLUUID& public_key)
{
bool isPending = false;
const F64 PENDING_TIMEOUT_SECS = 5.0 * 60.0;
- pending_queue_t::const_iterator it = sPendingQueue.find(agent_id);
+ pending_queue_t::const_iterator it = sPendingQueue.find(public_key);
if(it != sPendingQueue.end())
{
@@ -399,6 +436,10 @@ namespace LLExperienceCache
void setLookupURL( const std::string& lookup_url )
{
sLookupURL = lookup_url;
+ if(!sLookupURL.empty())
+ {
+ sLookupURL += "id/";
+ }
}
bool hasLookupURL()
@@ -429,87 +470,136 @@ namespace LLExperienceCache
}
}
- void erase( const LLUUID& agent_id )
+ struct FindByKey
+ {
+ FindByKey(const LLUUID& key, const std::string& key_type):mKey(key), mKeyType(key_type){}
+ const LLUUID& mKey;
+ const std::string& mKeyType;
+
+ bool operator()(cache_t::value_type& experience)
+ {
+ return experience.second.has(mKeyType) && experience.second[mKeyType].asUUID() == mKey;
+ }
+ };
+
+
+ cache_t::iterator Find(const LLUUID& key, const std::string& key_type)
+ {
+ LL_INFOS("ExperienceCache") << " searching for " << key << " of type " << key_type << LL_ENDL;
+ if(key_type == MAP_KEY)
+ {
+ return sCache.find(key);
+ }
+
+ return std::find_if(sCache.begin(), sCache.end(), FindByKey(key, key_type));
+ }
+
+
+ void erase( const LLUUID& key, const std::string& key_type )
{
- sCache.erase(agent_id);
+ cache_t::iterator it = Find(key, key_type);
+
+ if(it != sCache.end())
+ {
+ sCache.erase(it);
+ }
}
void eraseExpired()
{
- S32 expired_count = 0;
F64 now = LLFrameTimer::getTotalSeconds();
cache_t::iterator it = sCache.begin();
while (it != sCache.end())
{
cache_t::iterator cur = it;
+ LLSD& exp = cur->second;
++it;
- const LLExperienceData& experience = cur->second;
- if (experience.mExpires < now)
+ if(exp.has("expires") && exp["expires"].asReal() < now)
{
- sCache.erase(cur);
- expired_count++;
+ if(exp.has("key_type") && exp.has("uuid"))
+ {
+ fetch(exp["uuid"].asUUID(), exp["key_type"].asString(), true);
+ sCache.erase(cur);
+ }
+ else if(exp.has(MAP_KEY))
+ {
+ LLUUID id = exp[MAP_KEY];
+ if(!id.isNull())
+ {
+ fetch(id, MAP_KEY, true);
+ }
+ }
}
}
}
-
- void fetch( const LLUUID& agent_id )
+
+ bool fetch( const LLUUID& key, const std::string& key_type, bool refresh/* = true*/ )
{
- LL_DEBUGS("ExperienceCache") << __FUNCTION__ << "queue request for agent" << agent_id << LL_ENDL ;
- sAskQueue.insert(agent_id);
+ if(!key.isNull() && !isRequestPending(key) && (refresh || Find(key, key_type)==sCache.end()))
+ {
+ LL_INFOS("ExperienceCache") << " queue request for " << key_type << " " << key << LL_ENDL ;
+ sAskQueue[key]=key_type;
+
+ return true;
+ }
+ return false;
}
- void insert( const LLUUID& agent_id, const LLExperienceData& experience_data )
+ void insert(const LLSD& experience_data )
{
- sCache[agent_id]=experience_data;
+ if(experience_data.has(MAP_KEY))
+ {
+ sCache[experience_data[MAP_KEY].asUUID()]=experience_data;
+ }
+ else
+ {
+ LL_WARNS("ExperienceCache") << ": Ignoring cache insert of experience which is missing " << MAP_KEY << LL_ENDL;
+ }
}
- bool get( const LLUUID& agent_id, LLExperienceData* experience_data )
+ bool get( const LLUUID& key, const std::string& key_type, LLSD& experience_data )
{
- cache_t::const_iterator it = sCache.find(agent_id);
+ if(key.isNull()) return false;
+ cache_t::const_iterator it = Find(key, key_type);
+
if (it != sCache.end())
{
- llassert(experience_data);
- *experience_data = it->second;
+ experience_data = it->second;
return true;
}
- if(!isRequestPending(agent_id))
- {
- fetch(agent_id);
- }
+ fetch(key, key_type);
return false;
}
-
- void get( const LLUUID& agent_id, callback_slot_t slot )
+ void get( const LLUUID& key, const std::string& key_type, callback_slot_t slot )
{
- cache_t::const_iterator it = sCache.find(agent_id);
+ if(key.isNull()) return;
+
+ cache_t::const_iterator it = Find(key, key_type);
if (it != sCache.end())
{
// ...name already exists in cache, fire callback now
callback_signal_t signal;
signal.connect(slot);
- signal(agent_id, it->second);
+
+ signal(it->second);
return;
}
- // schedule a request
- if (!isRequestPending(agent_id))
- {
- sAskQueue.insert(agent_id);
- }
+ fetch(key, key_type);
// always store additional callback, even if request is pending
- signal_map_t::iterator sig_it = sSignalMap.find(agent_id);
+ signal_map_t::iterator sig_it = sSignalMap.find(key);
if (sig_it == sSignalMap.end())
{
// ...new callback for this id
callback_signal_t* signal = new callback_signal_t();
signal->connect(slot);
- sSignalMap[agent_id] = signal;
+ sSignalMap[key] = signal;
}
else
{
@@ -520,30 +610,3 @@ namespace LLExperienceCache
}
}
-
-static const std::string EXPERIENCE_NAME("username");
-static const std::string EXPERIENCE_DESCRIPTION("display_name");
-static const std::string EXPERIENCE_EXPIRATION("display_name_expires");
-
-bool LLExperienceData::fromLLSD( const LLSD& sd )
-{
- mDisplayName = sd[EXPERIENCE_NAME].asString();
- mDescription = sd[EXPERIENCE_DESCRIPTION].asString();
- LLDate expiration = sd[EXPERIENCE_EXPIRATION];
- mExpires = expiration.secondsSinceEpoch();
-
- if(mDisplayName.empty() || mDescription.empty()) return false;
-
- mDescription += " % Hey, this is a description!";
- return true;
-}
-
-LLSD LLExperienceData::asLLSD() const
-{
- LLSD sd;
- sd[EXPERIENCE_NAME] = mDisplayName;
- sd[EXPERIENCE_DESCRIPTION] = mDescription.substr(0, llmin(mDescription.size(),mDescription.find(" %")));
- sd[EXPERIENCE_EXPIRATION] = LLDate(mExpires);
-
- return sd;
-}
diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h
index 2f2647d5a3..396a244935 100644
--- a/indra/llmessage/llexperiencecache.h
+++ b/indra/llmessage/llexperiencecache.h
@@ -29,36 +29,35 @@
#ifndef LL_LLEXPERIENCECACHE_H
#define LL_LLEXPERIENCECACHE_H
-#include <string>
+#include "linden_common.h"
#include <boost/signals2.hpp>
+class LLSD;
class LLUUID;
-class LLExperienceData
-{
-public:
- bool fromLLSD(const LLSD& sd);
- LLSD asLLSD() const;
-
-
- std::string mDisplayName;
- std::string mDescription;
- F64 mExpires;
-};
-
-
namespace LLExperienceCache
{
+ const std::string PUBLIC_KEY = "public-id";
+ const std::string PRIVATE_KEY = "private-id";
+ const std::string CREATOR_KEY = "creator-id";
+ const std::string NAME = "name";
+ const std::string PROPERTIES = "properties";
+ const std::string EXPIRES = "expires";
+
+ const int EXPERIENCE_INVALID = (1 << 0);
+ const int EXPERIENCE_NORMAL = (1 << 1);
+ const int EXPERIENCE_REGION = (1 << 2);
+ const static F64 DEFAULT_EXPIRATION = 600.0;
+
// dummy name used when we have nothing else
const std::string DUMMY_NAME = "\?\?\?";
// Callback types for get() below
- typedef boost::signals2::signal<
- void (const LLUUID& agent_id, const LLExperienceData& experience)>
+ typedef boost::signals2::signal<void (const LLSD& experience)>
callback_signal_t;
typedef callback_signal_t::slot_type callback_slot_t;
- typedef std::map<LLUUID, LLExperienceData> cache_t;
+ typedef std::map<LLUUID, LLSD> cache_t;
void setLookupURL(const std::string& lookup_url);
@@ -70,14 +69,14 @@ namespace LLExperienceCache
void exportFile(std::ostream& ostr);
void importFile(std::istream& istr);
void initClass();
-
- void erase(const LLUUID& agent_id);
- void fetch(const LLUUID& agent_id);
- void insert(const LLUUID& agent_id, const LLExperienceData& experience_data);
- bool get(const LLUUID& agent_id, LLExperienceData* experience_data);
+
+ void erase(const LLUUID& key, const std::string& key_type);
+ bool fetch(const LLUUID& key, const std::string& key_type, bool refresh = false);
+ void insert(LLSD& experience_data);
+ bool get(const LLUUID& key, const std::string& key_type, LLSD& experience_data);
// If name information is in cache, callback will be called immediately.
- void get(const LLUUID& agent_id, callback_slot_t slot);
+ void get(const LLUUID& key, const std::string& key_type, callback_slot_t slot);
const cache_t& getCached();
};