Fixes some of U4-2633 Bundle all cache refresher transmissions into a single call per request for much better performance. See last note on issue.

This commit is contained in:
Shannon
2014-08-04 17:50:19 -06:00
parent b5f900e789
commit 7261162d8a
3 changed files with 67 additions and 36 deletions

View File

@@ -20,18 +20,19 @@ namespace Umbraco.Core.Sync
{
private readonly Func<Tuple<string, string>> _getUserNamePasswordDelegate;
private volatile bool _hasResolvedDelegate = false;
private readonly object _locker = new object();
private bool _useDistributedCalls;
private readonly object _locker = new object();
protected string Login { get; private set; }
protected string Password{ get; private set; }
protected bool UseDistributedCalls { get; private set; }
/// <summary>
/// Without a username/password all distribuion will be disabled
/// </summary>
internal DefaultServerMessenger()
{
_useDistributedCalls = false;
UseDistributedCalls = false;
}
/// <summary>
@@ -55,7 +56,7 @@ namespace Umbraco.Core.Sync
if (login == null) throw new ArgumentNullException("login");
if (password == null) throw new ArgumentNullException("password");
_useDistributedCalls = useDistributedCalls;
UseDistributedCalls = useDistributedCalls;
Login = login;
Password = password;
}
@@ -221,13 +222,13 @@ namespace Umbraco.Core.Sync
{
Login = null;
Password = null;
_useDistributedCalls = false;
UseDistributedCalls = false;
}
else
{
Login = result.Item1;
Password = result.Item2;
_useDistributedCalls = UmbracoSettings.UseDistributedCalls;
UseDistributedCalls = UmbracoSettings.UseDistributedCalls;
}
}
catch (Exception ex)
@@ -235,7 +236,7 @@ namespace Umbraco.Core.Sync
LogHelper.Error<DefaultServerMessenger>("Could not resolve username/password delegate, server distribution will be disabled", ex);
Login = null;
Password = null;
_useDistributedCalls = false;
UseDistributedCalls = false;
}
}
}
@@ -314,7 +315,7 @@ namespace Umbraco.Core.Sync
//Now, check if we are using Distrubuted calls. If there are no servers in the list then we
// can definitely not distribute.
if (!_useDistributedCalls || !servers.Any())
if (!UseDistributedCalls || !servers.Any())
{
//if we are not, then just invoke the call on the cache refresher
InvokeMethodOnRefresherInstance(refresher, dispatchType, getId, instances);
@@ -325,7 +326,7 @@ namespace Umbraco.Core.Sync
MessageSeversForIdsOrJson(servers, refresher, dispatchType, instances.Select(getId));
}
private void MessageSeversForIdsOrJson(
protected virtual void MessageSeversForIdsOrJson(
IEnumerable<IServerAddress> servers,
ICacheRefresher refresher,
MessageType dispatchType,
@@ -345,7 +346,7 @@ namespace Umbraco.Core.Sync
//Now, check if we are using Distrubuted calls. If there are no servers in the list then we
// can definitely not distribute.
if (!_useDistributedCalls || !servers.Any())
if (!UseDistributedCalls || !servers.Any())
{
//if we are not, then just invoke the call on the cache refresher
InvokeMethodOnRefresherInstance(refresher, dispatchType, ids, jsonPayload);
@@ -456,16 +457,16 @@ namespace Umbraco.Core.Sync
}
}
List<WaitHandle> waitHandlesList;
var asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList);
var waitHandlesList = asyncResultsList.Select(x => x.AsyncWaitHandle).ToArray();
var errorCount = 0;
// Once for each WaitHandle that we have, wait for a response and log it
// We're previously submitted all these requests effectively in parallel and will now retrieve responses on a FIFO basis
foreach (var t in asyncResults)
//Wait for all requests to complete
WaitHandle.WaitAll(waitHandlesList.ToArray());
foreach (var t in asyncResultsList)
{
var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
//var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
try
{
@@ -520,18 +521,7 @@ namespace Umbraco.Core.Sync
LogDispatchBatchError(ee);
}
}
internal IEnumerable<IAsyncResult> GetAsyncResults(List<IAsyncResult> asyncResultsList, out List<WaitHandle> waitHandlesList)
{
var asyncResults = asyncResultsList.ToArray();
waitHandlesList = new List<WaitHandle>();
foreach (var asyncResult in asyncResults)
{
waitHandlesList.Add(asyncResult.AsyncWaitHandle);
}
return asyncResults;
}
private void LogDispatchBatchError(Exception ee)
{
LogHelper.Error<DefaultServerMessenger>("Error refreshing distributed list", ee);

View File

@@ -8,5 +8,7 @@ namespace Umbraco.Core.Sync
internal interface IServerAddress
{
string ServerAddress { get; }
//TODO : Should probably add things like port, protocol, server name, app id
}
}

View File

@@ -67,6 +67,43 @@ namespace Umbraco.Web
public string JsonPayload { get; set; }
}
/// <summary>
/// We need to check if distributed calls are enabled, if they are we also want to make sure
/// that the current server's cache is updated internally in real time instead of at the end of
/// the call. This is because things like the URL cache, etc... might need to be updated during
/// the request that is making these calls.
/// </summary>
/// <param name="servers"></param>
/// <param name="refresher"></param>
/// <param name="dispatchType"></param>
/// <param name="ids"></param>
/// <param name="jsonPayload"></param>
/// <remarks>
/// See: http://issues.umbraco.org/issue/U4-2633#comment=67-15604
/// </remarks>
protected override void MessageSeversForIdsOrJson(IEnumerable<IServerAddress> servers, ICacheRefresher refresher, MessageType dispatchType, IEnumerable<object> ids = null, string jsonPayload = null)
{
//do all the normal stuff
base.MessageSeversForIdsOrJson(servers, refresher, dispatchType, ids, jsonPayload);
//Now, check if we are using Distrubuted calls
if (UseDistributedCalls && servers.Any())
{
//invoke on the current server - we will basically be double cache refreshing for the calling
// server but that just needs to be done currently, see the link above for details.
InvokeMethodOnRefresherInstance(refresher, dispatchType, ids, jsonPayload);
}
}
/// <summary>
/// This adds the call to batched list
/// </summary>
/// <param name="servers"></param>
/// <param name="refresher"></param>
/// <param name="dispatchType"></param>
/// <param name="ids"></param>
/// <param name="idArrayType"></param>
/// <param name="jsonPayload"></param>
protected override void PerformDistributedCall(
IEnumerable<IServerAddress> servers,
ICacheRefresher refresher,
@@ -75,6 +112,7 @@ namespace Umbraco.Web
Type idArrayType = null,
string jsonPayload = null)
{
//NOTE: we use UmbracoContext instead of HttpContext.Current because when some web methods run async, the
// HttpContext.Current is null but the UmbracoContext.Current won't be since we manually assign it.
if (UmbracoContext.Current == null || UmbracoContext.Current.HttpContext == null)
@@ -208,16 +246,17 @@ namespace Umbraco.Web
instructions, Login, Password, null, null));
}
List<WaitHandle> waitHandlesList;
var asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList);
var waitHandlesList = asyncResultsList.Select(x => x.AsyncWaitHandle).ToArray();
var errorCount = 0;
// Once for each WaitHandle that we have, wait for a response and log it
// We're previously submitted all these requests effectively in parallel and will now retrieve responses on a FIFO basis
foreach (var t in asyncResults)
//Wait for all requests to complete
WaitHandle.WaitAll(waitHandlesList.ToArray());
foreach (var t in asyncResultsList)
{
var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
//var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
try
{
cacheRefresher.EndBulkRefresh(t);