From 9dd856e37eb9c80701053f0c1a2c0b268f56f5f2 Mon Sep 17 00:00:00 2001
From: "Brad Payne (Vir Linden)" <vir@lindenlab.com>
Date: Fri, 2 Nov 2012 18:07:07 -0400
Subject: [PATCH] SH-3429 WIP - Added adaptive retry mechanism for failed
 appearance update requests

---
 indra/newview/llappearancemgr.cpp | 85 +++++++++++++++++++++++++++++--
 indra/newview/llappearancemgr.h   |  2 +-
 indra/newview/llcallbacklist.cpp  | 49 ++++++++++++++++++
 indra/newview/llcallbacklist.h    |  6 +++
 4 files changed, 137 insertions(+), 5 deletions(-)
 mode change 100644 => 100755 indra/newview/llcallbacklist.cpp

diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp
index 51de2fd17b3..b2a8a33ecff 100755
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -2652,12 +2652,70 @@ void LLAppearanceMgr::updateClothingOrderingInfo(LLUUID cat_id, bool update_base
 	if (inventory_changed) gInventory.notifyObservers();
 }
 
+// This is intended for use with HTTP Clients/Responders, but is not
+// specifically coupled with those classes.
+class LLHTTPRetryPolicy: public LLThreadSafeRefCount
+{
+public:
+	LLHTTPRetryPolicy() {}
+	virtual ~LLHTTPRetryPolicy() {}
+	virtual bool shouldRetry(U32 status, F32& seconds_to_wait) = 0;
+};
+
+// Example of simplest possible policy, not necessarily recommended.
+class LLAlwaysRetryImmediatelyPolicy: public LLHTTPRetryPolicy
+{
+public:
+	LLAlwaysRetryImmediatelyPolicy() {}
+	bool shouldRetry(U32 status, F32& seconds_to_wait)
+	{
+		seconds_to_wait = 0.0;
+		return true;
+	}
+};
+
+// Very general policy with geometric back-off after failures,
+// up to a maximum delay, and maximum number of retries.
+class LLAdaptiveRetryPolicy: public LLHTTPRetryPolicy
+{
+public:
+	LLAdaptiveRetryPolicy(F32 min_delay, F32 max_delay, F32 backoff_factor, U32 max_retries):
+		mMinDelay(min_delay),
+		mMaxDelay(max_delay),
+		mBackoffFactor(backoff_factor),
+		mMaxRetries(max_retries),
+		mDelay(min_delay),
+		mRetryCount(0)
+	{
+	}
+
+	bool shouldRetry(U32 status, F32& seconds_to_wait)
+	{
+		seconds_to_wait = mDelay;
+		mDelay = llclamp(mDelay*mBackoffFactor,mMinDelay,mMaxDelay);
+		mRetryCount++;
+		return (mRetryCount<=mMaxRetries);
+	}
+
+private:
+	F32 mMinDelay; // delay never less than this value
+	F32 mMaxDelay; // delay never exceeds this value
+	F32 mBackoffFactor; // delay increases by this factor after each retry, up to mMaxDelay.
+	U32 mMaxRetries; // maximum number of times shouldRetry will return true.
+	F32 mDelay; // current delay.
+	U32 mRetryCount; // number of times shouldRetry has been called.
+};
+
 class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder
 {
 public:
 	RequestAgentUpdateAppearanceResponder()
 	{
-		llinfos << "request created" << llendl;
+		mRetryPolicy = new LLAdaptiveRetryPolicy(1.0, 16.0, 2.0, 5);
+	}
+
+	virtual ~RequestAgentUpdateAppearanceResponder()
+	{
 	}
 
 	// Successful completion.
@@ -2669,11 +2727,25 @@ class RequestAgentUpdateAppearanceResponder: public LLHTTPClient::Responder
 	// Error
 	/*virtual*/ void error(U32 status, const std::string& reason)
 	{
-		llwarns << "appearance update request failed, reason: " << reason << llendl;
+		llwarns << "appearance update request failed, status: " << status << " reason: " << reason << llendl;
+		F32 seconds_to_wait;
+		if (mRetryPolicy->shouldRetry(status,seconds_to_wait))
+		{
+			doAfterInterval(boost::bind(&LLAppearanceMgr::requestServerAppearanceUpdate,
+										LLAppearanceMgr::getInstance(),
+										LLCurl::ResponderPtr(this)),
+							seconds_to_wait);
+		}
+		else
+		{
+			llwarns << "giving up after too many retries" << llendl;
+		}
 	}	
+
+	LLPointer<LLHTTPRetryPolicy> mRetryPolicy;
 };
 
-void LLAppearanceMgr::requestServerAppearanceUpdate()
+void LLAppearanceMgr::requestServerAppearanceUpdate(LLCurl::ResponderPtr responder_ptr)
 {
 	if (!gAgent.getRegion())
 	{
@@ -2693,7 +2765,12 @@ void LLAppearanceMgr::requestServerAppearanceUpdate()
 	LLSD body;
 	S32 cof_version = getCOFVersion();
 	body["cof_version"] = cof_version;
-	LLHTTPClient::post(url, body, new RequestAgentUpdateAppearanceResponder);
+	//LLCurl::ResponderPtr responder_ptr;
+	if (!responder_ptr.get())
+	{
+		responder_ptr = new RequestAgentUpdateAppearanceResponder;
+	}
+	LLHTTPClient::post(url, body, responder_ptr);
 	llassert(cof_version >= mLastUpdateRequestCOFVersion);
 	mLastUpdateRequestCOFVersion = cof_version;
 }
diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h
index 4baee10218c..01ed66711c2 100755
--- a/indra/newview/llappearancemgr.h
+++ b/indra/newview/llappearancemgr.h
@@ -187,7 +187,7 @@ class LLAppearanceMgr: public LLSingleton<LLAppearanceMgr>
 
 	bool isInUpdateAppearanceFromCOF() { return mIsInUpdateAppearanceFromCOF; }
 
-	void requestServerAppearanceUpdate();
+	void requestServerAppearanceUpdate(LLCurl::ResponderPtr responder_ptr = NULL);
 
 protected:
 	LLAppearanceMgr();
diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp
old mode 100644
new mode 100755
index 357a6582d16..79ec43dfe92
--- a/indra/newview/llcallbacklist.cpp
+++ b/indra/newview/llcallbacklist.cpp
@@ -27,6 +27,7 @@
 #include "llviewerprecompiledheaders.h"
 
 #include "llcallbacklist.h"
+#include "lleventtimer.h"
 
 // Library includes
 #include "llerror.h"
@@ -180,6 +181,54 @@ void doOnIdleRepeating(bool_func_t callable)
 	gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor);
 }
 
+class NullaryFuncEventTimer: public LLEventTimer
+{
+public:
+	NullaryFuncEventTimer(nullary_func_t callable, F32 seconds):
+		LLEventTimer(seconds),
+		mCallable(callable)
+	{
+	}
+
+private:
+	BOOL tick()
+	{
+		mCallable();
+		return TRUE;
+	}
+
+	nullary_func_t mCallable;
+};
+
+// Call a given callable once after specified interval.
+void doAfterInterval(nullary_func_t callable, F32 seconds)
+{
+	new NullaryFuncEventTimer(callable, seconds);
+}
+
+class BoolFuncEventTimer: public LLEventTimer
+{
+public:
+	BoolFuncEventTimer(bool_func_t callable, F32 seconds):
+		LLEventTimer(seconds),
+		mCallable(callable)
+	{
+	}
+private:
+	BOOL tick()
+	{
+		return mCallable();
+	}
+
+	bool_func_t mCallable;
+};
+
+// Call a given callable every specified number of seconds, until it returns true.
+void doPeriodically(bool_func_t callable, F32 seconds)
+{
+	new BoolFuncEventTimer(callable, seconds);
+}
+
 #ifdef _DEBUG
 
 void test1(void *data)
diff --git a/indra/newview/llcallbacklist.h b/indra/newview/llcallbacklist.h
index 97f3bfd9ee2..0516c9cdb44 100644
--- a/indra/newview/llcallbacklist.h
+++ b/indra/newview/llcallbacklist.h
@@ -61,6 +61,12 @@ void doOnIdleOneTime(nullary_func_t callable);
 // Repeatedly call a callable in idle loop until it returns true.
 void doOnIdleRepeating(bool_func_t callable);
 
+// Call a given callable once after specified interval.
+void doAfterInterval(nullary_func_t callable, F32 seconds);
+
+// Call a given callable every specified number of seconds, until it returns true.
+void doPeriodically(bool_func_t callable, F32 seconds);
+
 extern LLCallbackList gIdleCallbacks;
 
 #endif
-- 
GitLab