Completes: U4-2633 Bundle all cache refresher transmissions into a single call per request for much better performance
This commit is contained in:
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Script.Serialization;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
@@ -520,7 +521,7 @@ namespace Umbraco.Core.Sync
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<IAsyncResult> GetAsyncResults(List<IAsyncResult> asyncResultsList, out List<WaitHandle> waitHandlesList)
|
||||
internal IEnumerable<IAsyncResult> GetAsyncResults(List<IAsyncResult> asyncResultsList, out List<WaitHandle> waitHandlesList)
|
||||
{
|
||||
var asyncResults = asyncResultsList.ToArray();
|
||||
waitHandlesList = new List<WaitHandle>();
|
||||
|
||||
63
src/Umbraco.Core/Sync/RefreshInstruction.cs
Normal file
63
src/Umbraco.Core/Sync/RefreshInstruction.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
[Serializable]
|
||||
public class RefreshInstruction
|
||||
{
|
||||
public RefreshMethodType RefreshType { get; set; }
|
||||
public Guid RefresherId { get; set; }
|
||||
public Guid GuidId { get; set; }
|
||||
public int IntId { get; set; }
|
||||
public string JsonIds { get; set; }
|
||||
public string JsonPayload { get; set; }
|
||||
|
||||
[Serializable]
|
||||
public enum RefreshMethodType
|
||||
{
|
||||
RefreshAll,
|
||||
RefreshByGuid,
|
||||
RefreshById,
|
||||
RefreshByIds,
|
||||
RefreshByJson,
|
||||
RemoveById
|
||||
}
|
||||
|
||||
protected bool Equals(RefreshInstruction other)
|
||||
{
|
||||
return RefreshType == other.RefreshType && RefresherId.Equals(other.RefresherId) && GuidId.Equals(other.GuidId) && IntId == other.IntId && string.Equals(JsonIds, other.JsonIds) && string.Equals(JsonPayload, other.JsonPayload);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((RefreshInstruction) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = (int) RefreshType;
|
||||
hashCode = (hashCode*397) ^ RefresherId.GetHashCode();
|
||||
hashCode = (hashCode*397) ^ GuidId.GetHashCode();
|
||||
hashCode = (hashCode*397) ^ IntId;
|
||||
hashCode = (hashCode*397) ^ (JsonIds != null ? JsonIds.GetHashCode() : 0);
|
||||
hashCode = (hashCode*397) ^ (JsonPayload != null ? JsonPayload.GetHashCode() : 0);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(RefreshInstruction left, RefreshInstruction right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(RefreshInstruction left, RefreshInstruction right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The client Soap service for making distrubuted cache calls between servers
|
||||
/// </summary>
|
||||
@@ -21,6 +20,31 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/BulkRefresh", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void BulkRefresh(RefreshInstruction[] instructions, string login, string password)
|
||||
{
|
||||
this.Invoke("BulkRefresh", new object[] {
|
||||
instructions,
|
||||
login,
|
||||
password});
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public System.IAsyncResult BeginBulkRefresh(RefreshInstruction[] instructions, string login, string password, System.AsyncCallback callback, object asyncState)
|
||||
{
|
||||
return this.BeginInvoke("BulkRefresh", new object[] {
|
||||
instructions,
|
||||
login,
|
||||
password}, callback, asyncState);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
public void EndBulkRefresh(System.IAsyncResult asyncResult)
|
||||
{
|
||||
this.EndInvoke(asyncResult);
|
||||
}
|
||||
|
||||
/// <remarks/>
|
||||
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://umbraco.org/webservices/RefreshAll", RequestNamespace = "http://umbraco.org/webservices/", ResponseNamespace = "http://umbraco.org/webservices/", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
|
||||
public void RefreshAll(System.Guid uniqueIdentifier, string Login, string Password)
|
||||
|
||||
@@ -830,6 +830,7 @@
|
||||
<Compile Include="Standalone\StandaloneCoreBootManager.cs" />
|
||||
<Compile Include="Strings\ContentBaseExtensions.cs" />
|
||||
<Compile Include="Strings\Diff.cs" />
|
||||
<Compile Include="Sync\RefreshInstruction.cs" />
|
||||
<Compile Include="TopologicalSorter.cs" />
|
||||
<Compile Include="Strings\DefaultUrlSegmentProvider.cs" />
|
||||
<Compile Include="Strings\IUrlSegmentProvider.cs" />
|
||||
|
||||
@@ -224,7 +224,7 @@
|
||||
</scheduledTasks>
|
||||
|
||||
<!-- distributed calls make umbraco use webservices to handle cache refreshing -->
|
||||
<distributedCall enable="false">
|
||||
<distributedCall enable="true">
|
||||
<!-- the id of the user who's making the calls -->
|
||||
<!-- needed for security, umbraco will automatically look up correct login and passwords -->
|
||||
<user>0</user>
|
||||
@@ -233,6 +233,7 @@
|
||||
<!-- you can also add optional attributes to force a protocol or port number (see #2) -->
|
||||
<!-- <server>127.0.0.1</server>-->
|
||||
<!-- <server forceProtocol="http|https" forcePortnumber="80|443">127.0.0.1</server>-->
|
||||
<server forcePortnumber="7100">localhost</server>
|
||||
</servers>
|
||||
</distributedCall>
|
||||
|
||||
|
||||
271
src/Umbraco.Web/BatchedServerMessenger.cs
Normal file
271
src/Umbraco.Web/BatchedServerMessenger.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Web.UI.WebControls;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Sync;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
internal class BatchedServerMessenger : DefaultServerMessenger
|
||||
{
|
||||
internal BatchedServerMessenger()
|
||||
{
|
||||
UmbracoModule.EndRequest += UmbracoModule_EndRequest;
|
||||
}
|
||||
|
||||
internal BatchedServerMessenger(string login, string password) : base(login, password)
|
||||
{
|
||||
UmbracoModule.EndRequest += UmbracoModule_EndRequest;
|
||||
}
|
||||
|
||||
internal BatchedServerMessenger(string login, string password, bool useDistributedCalls) : base(login, password, useDistributedCalls)
|
||||
{
|
||||
UmbracoModule.EndRequest += UmbracoModule_EndRequest;
|
||||
}
|
||||
|
||||
public BatchedServerMessenger(Func<Tuple<string, string>> getUserNamePasswordDelegate) : base(getUserNamePasswordDelegate)
|
||||
{
|
||||
UmbracoModule.EndRequest += UmbracoModule_EndRequest;
|
||||
}
|
||||
|
||||
void UmbracoModule_EndRequest(object sender, EventArgs e)
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var items = HttpContext.Current.Items[typeof(BatchedServerMessenger).Name] as List<Message>;
|
||||
if (items != null)
|
||||
{
|
||||
var copied = new Message[items.Count];
|
||||
items.CopyTo(copied);
|
||||
//now set to null so it get's cleaned up on this request
|
||||
HttpContext.Current.Items[typeof (BatchedServerMessenger).Name] = null;
|
||||
|
||||
SendMessages(copied);
|
||||
}
|
||||
}
|
||||
|
||||
private class Message
|
||||
{
|
||||
public IEnumerable<IServerAddress> Servers { get; set; }
|
||||
public ICacheRefresher Refresher { get; set; }
|
||||
public MessageType DispatchType { get; set; }
|
||||
public IEnumerable<object> Ids { get; set; }
|
||||
public Type IdArrayType { get; set; }
|
||||
public string JsonPayload { get; set; }
|
||||
}
|
||||
|
||||
protected override void PerformDistributedCall(
|
||||
IEnumerable<IServerAddress> servers,
|
||||
ICacheRefresher refresher,
|
||||
MessageType dispatchType,
|
||||
IEnumerable<object> ids = null,
|
||||
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)
|
||||
{
|
||||
throw new NotSupportedException("This messenger cannot execute without a valid/current UmbracoContext with an HttpContext assigned");
|
||||
}
|
||||
|
||||
if (UmbracoContext.Current.HttpContext.Items[typeof(BatchedServerMessenger).Name] == null)
|
||||
{
|
||||
UmbracoContext.Current.HttpContext.Items[typeof(BatchedServerMessenger).Name] = new List<Message>();
|
||||
}
|
||||
var list = (List<Message>)UmbracoContext.Current.HttpContext.Items[typeof(BatchedServerMessenger).Name];
|
||||
|
||||
list.Add(new Message
|
||||
{
|
||||
DispatchType = dispatchType,
|
||||
IdArrayType = idArrayType,
|
||||
Ids = ids,
|
||||
JsonPayload = jsonPayload,
|
||||
Refresher = refresher,
|
||||
Servers = servers
|
||||
});
|
||||
}
|
||||
|
||||
private RefreshInstruction[] ConvertToInstruction(Message msg)
|
||||
{
|
||||
switch (msg.DispatchType)
|
||||
{
|
||||
case MessageType.RefreshAll:
|
||||
return new[]
|
||||
{
|
||||
new RefreshInstruction
|
||||
{
|
||||
RefreshType = RefreshInstruction.RefreshMethodType.RefreshAll,
|
||||
RefresherId = msg.Refresher.UniqueIdentifier
|
||||
}
|
||||
};
|
||||
case MessageType.RefreshById:
|
||||
if (msg.IdArrayType == null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot refresh by id if the idArrayType is null");
|
||||
}
|
||||
|
||||
if (msg.IdArrayType == typeof(int))
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
var jsonIds = serializer.Serialize(msg.Ids.Cast<int>().ToArray());
|
||||
|
||||
return new[]
|
||||
{
|
||||
new RefreshInstruction
|
||||
{
|
||||
JsonIds = jsonIds,
|
||||
RefreshType = RefreshInstruction.RefreshMethodType.RefreshByIds,
|
||||
RefresherId = msg.Refresher.UniqueIdentifier
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return msg.Ids.Select(x => new RefreshInstruction
|
||||
{
|
||||
GuidId = (Guid)x,
|
||||
RefreshType = RefreshInstruction.RefreshMethodType.RefreshById,
|
||||
RefresherId = msg.Refresher.UniqueIdentifier
|
||||
}).ToArray();
|
||||
|
||||
case MessageType.RefreshByJson:
|
||||
return new[]
|
||||
{
|
||||
new RefreshInstruction
|
||||
{
|
||||
RefreshType = RefreshInstruction.RefreshMethodType.RefreshByJson,
|
||||
RefresherId = msg.Refresher.UniqueIdentifier,
|
||||
JsonPayload = msg.JsonPayload
|
||||
}
|
||||
};
|
||||
case MessageType.RemoveById:
|
||||
return msg.Ids.Select(x => new RefreshInstruction
|
||||
{
|
||||
IntId = (int)x,
|
||||
RefreshType = RefreshInstruction.RefreshMethodType.RemoveById,
|
||||
RefresherId = msg.Refresher.UniqueIdentifier
|
||||
}).ToArray();
|
||||
case MessageType.RefreshByInstance:
|
||||
case MessageType.RemoveByInstance:
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private void SendMessages(IEnumerable<Message> messages)
|
||||
{
|
||||
var batchedMsg = new List<Tuple<Message, RefreshInstruction[]>>();
|
||||
foreach (var msg in messages)
|
||||
{
|
||||
var instructions = ConvertToInstruction(msg);
|
||||
batchedMsg.Add(new Tuple<Message, RefreshInstruction[]>(msg, instructions));
|
||||
}
|
||||
|
||||
var servers = batchedMsg.SelectMany(x => x.Item1.Servers).Distinct();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
//TODO: We should try to figure out the current server's address and if it matches any of the ones
|
||||
// in the ServerAddress list, then just refresh directly on this server and exclude that server address
|
||||
// from the list, this will save an internal request.
|
||||
|
||||
using (var cacheRefresher = new ServerSyncWebServiceClient())
|
||||
{
|
||||
var asyncResultsList = new List<IAsyncResult>();
|
||||
|
||||
LogStartDispatch();
|
||||
|
||||
// Go through each configured node submitting a request asynchronously
|
||||
//NOTE: 'asynchronously' in this case does not mean that it will continue while we give the page back to the user!
|
||||
foreach (var server in servers)
|
||||
{
|
||||
//set the server address
|
||||
cacheRefresher.Url = server.ServerAddress;
|
||||
|
||||
var instructions = batchedMsg
|
||||
.Where(x => x.Item1.Servers.Contains(server))
|
||||
.SelectMany(x => x.Item2)
|
||||
//only execute distinct instructions - no sense in running the same one.
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
asyncResultsList.Add(
|
||||
cacheRefresher.BeginBulkRefresh(
|
||||
instructions, Login, Password, null, null));
|
||||
}
|
||||
|
||||
List<WaitHandle> waitHandlesList;
|
||||
var asyncResults = GetAsyncResults(asyncResultsList, out waitHandlesList);
|
||||
|
||||
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)
|
||||
{
|
||||
var handleIndex = WaitHandle.WaitAny(waitHandlesList.ToArray(), TimeSpan.FromSeconds(15));
|
||||
try
|
||||
{
|
||||
cacheRefresher.EndBulkRefresh(t);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
LogDispatchNodeError(ex);
|
||||
errorCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogDispatchNodeError(ex);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
LogDispatchBatchResult(errorCount);
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
LogDispatchBatchError(ee);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogDispatchBatchError(Exception ee)
|
||||
{
|
||||
LogHelper.Error<BatchedServerMessenger>("Error refreshing distributed list", ee);
|
||||
}
|
||||
|
||||
private void LogDispatchBatchResult(int errorCount)
|
||||
{
|
||||
LogHelper.Debug<BatchedServerMessenger>(string.Format("Distributed server push completed with {0} nodes reporting an error", errorCount == 0 ? "no" : errorCount.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
private void LogDispatchNodeError(Exception ex)
|
||||
{
|
||||
LogHelper.Error<BatchedServerMessenger>("Error refreshing a node in the distributed list", ex);
|
||||
}
|
||||
|
||||
private void LogDispatchNodeError(WebException ex)
|
||||
{
|
||||
string url = (ex.Response != null) ? ex.Response.ResponseUri.ToString() : "invalid url (responseUri null)";
|
||||
LogHelper.Error<BatchedServerMessenger>("Error refreshing a node in the distributed list, URI attempted: " + url, ex);
|
||||
}
|
||||
|
||||
private void LogStartDispatch()
|
||||
{
|
||||
LogHelper.Info<BatchedServerMessenger>("Submitting calls to distributed servers");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,23 +60,6 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
private static readonly DistributedCache InstanceObject = new DistributedCache();
|
||||
|
||||
///// <summary>
|
||||
///// Fired when any cache refresher method has fired
|
||||
///// </summary>
|
||||
///// <remarks>
|
||||
///// This could be used by developers to know when a cache refresher has executed on the local server.
|
||||
///// Similarly to the content.AfterUpdateDocumentCache which fires locally on each machine.
|
||||
///// </remarks>
|
||||
//public event EventHandler<CacheUpdatedEventArgs> CacheChanged;
|
||||
|
||||
//private void OnCacheChanged(CacheUpdatedEventArgs args)
|
||||
//{
|
||||
// if (CacheChanged != null)
|
||||
// {
|
||||
// CacheChanged(this, args);
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
@@ -109,6 +92,8 @@ namespace Umbraco.Web.Cache
|
||||
/// </remarks>
|
||||
public void Refresh<T>(Guid factoryGuid, Func<T, int> getNumericId, params T[] instances)
|
||||
{
|
||||
if (factoryGuid == Guid.Empty || instances.Length == 0 || getNumericId == null) return;
|
||||
|
||||
ServerMessengerResolver.Current.Messenger.PerformRefresh<T>(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
@@ -124,6 +109,8 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="id">The id of the node.</param>
|
||||
public void Refresh(Guid factoryGuid, int id)
|
||||
{
|
||||
if (factoryGuid == Guid.Empty || id == default(int)) return;
|
||||
|
||||
ServerMessengerResolver.Current.Messenger.PerformRefresh(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
@@ -138,6 +125,8 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="id">The guid of the node.</param>
|
||||
public void Refresh(Guid factoryGuid, Guid id)
|
||||
{
|
||||
if (factoryGuid == Guid.Empty || id == Guid.Empty) return;
|
||||
|
||||
ServerMessengerResolver.Current.Messenger.PerformRefresh(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
@@ -152,6 +141,8 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="jsonPayload"></param>
|
||||
public void RefreshByJson(Guid factoryGuid, string jsonPayload)
|
||||
{
|
||||
if (factoryGuid == Guid.Empty || jsonPayload.IsNullOrWhiteSpace()) return;
|
||||
|
||||
ServerMessengerResolver.Current.Messenger.PerformRefresh(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
@@ -165,6 +156,8 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="factoryGuid">The unique identifier.</param>
|
||||
public void RefreshAll(Guid factoryGuid)
|
||||
{
|
||||
if (factoryGuid == Guid.Empty) return;
|
||||
|
||||
RefreshAll(factoryGuid, true);
|
||||
}
|
||||
|
||||
@@ -178,6 +171,8 @@ namespace Umbraco.Web.Cache
|
||||
/// </param>
|
||||
public void RefreshAll(Guid factoryGuid, bool allServers)
|
||||
{
|
||||
if (factoryGuid == Guid.Empty) return;
|
||||
|
||||
ServerMessengerResolver.Current.Messenger.PerformRefreshAll(
|
||||
allServers
|
||||
? ServerRegistrarResolver.Current.Registrar.Registrations
|
||||
@@ -193,6 +188,8 @@ namespace Umbraco.Web.Cache
|
||||
/// <param name="id">The id.</param>
|
||||
public void Remove(Guid factoryGuid, int id)
|
||||
{
|
||||
if (factoryGuid == Guid.Empty || id == default(int)) return;
|
||||
|
||||
ServerMessengerResolver.Current.Messenger.PerformRemove(
|
||||
ServerRegistrarResolver.Current.Registrar.Registrations,
|
||||
GetRefresherById(factoryGuid),
|
||||
|
||||
@@ -269,6 +269,7 @@
|
||||
<Link>Properties\SolutionInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="ApplicationContextExtensions.cs" />
|
||||
<Compile Include="BatchedServerMessenger.cs" />
|
||||
<Compile Include="CacheHelperExtensions.cs" />
|
||||
<Compile Include="Cache\ApplicationCacheRefresher.cs" />
|
||||
<Compile Include="Cache\ApplicationTreeCacheRefresher.cs" />
|
||||
|
||||
@@ -26,7 +26,8 @@ namespace Umbraco.Web
|
||||
// Request.RawUrl is still there
|
||||
// response.Redirect does?! always remap to /vdir?!
|
||||
|
||||
public class UmbracoModule : IHttpModule
|
||||
public class
|
||||
UmbracoModule : IHttpModule
|
||||
{
|
||||
#region HttpModule event handlers
|
||||
|
||||
@@ -527,6 +528,8 @@ namespace Umbraco.Web
|
||||
LogHelper.Debug<UmbracoModule>("Total milliseconds for umbraco request to process: " + DateTime.Now.Subtract(UmbracoContext.Current.ObjectCreated).TotalMilliseconds);
|
||||
}
|
||||
|
||||
OnEndRequest(new EventArgs());
|
||||
|
||||
DisposeHttpContextItems(httpContext);
|
||||
};
|
||||
|
||||
@@ -560,6 +563,13 @@ namespace Umbraco.Web
|
||||
{
|
||||
if (RouteAttempt != null)
|
||||
RouteAttempt(this, args);
|
||||
}
|
||||
|
||||
internal static event EventHandler<EventArgs> EndRequest;
|
||||
private void OnEndRequest(EventArgs args)
|
||||
{
|
||||
if (EndRequest != null)
|
||||
EndRequest(this, args);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -275,7 +275,8 @@ namespace Umbraco.Web
|
||||
DefaultRenderMvcControllerResolver.Current = new DefaultRenderMvcControllerResolver(typeof(RenderMvcController));
|
||||
|
||||
//Override the ServerMessengerResolver to set a username/password for the distributed calls
|
||||
ServerMessengerResolver.Current.SetServerMessenger(new DefaultServerMessenger(() =>
|
||||
//ServerMessengerResolver.Current.SetServerMessenger(new DefaultServerMessenger(() =>
|
||||
ServerMessengerResolver.Current.SetServerMessenger(new BatchedServerMessenger(() =>
|
||||
{
|
||||
//we should not proceed to change this if the app/database is not configured since there will
|
||||
// be no user, plus we don't need to have server messages sent if this is the case.
|
||||
|
||||
@@ -4,55 +4,106 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Web.Services;
|
||||
using System.Xml;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Sync;
|
||||
|
||||
namespace umbraco.presentation.webservices
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Summary description for CacheRefresher.
|
||||
/// </summary>
|
||||
[WebService(Namespace="http://umbraco.org/webservices/")]
|
||||
public class CacheRefresher : WebService
|
||||
{
|
||||
{
|
||||
|
||||
[WebMethod]
|
||||
public void BulkRefresh(RefreshInstruction[] instructions, string login, string password)
|
||||
{
|
||||
if (BusinessLogic.User.validateCredentials(login, password) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//only execute distinct instructions - no sense in running the same one.
|
||||
foreach (var instruction in instructions.Distinct())
|
||||
{
|
||||
switch (instruction.RefreshType)
|
||||
{
|
||||
case RefreshInstruction.RefreshMethodType.RefreshAll:
|
||||
RefreshAll(instruction.RefresherId);
|
||||
break;
|
||||
case RefreshInstruction.RefreshMethodType.RefreshByGuid:
|
||||
RefreshByGuid(instruction.RefresherId, instruction.GuidId);
|
||||
break;
|
||||
case RefreshInstruction.RefreshMethodType.RefreshById:
|
||||
RefreshById(instruction.RefresherId, instruction.IntId);
|
||||
break;
|
||||
case RefreshInstruction.RefreshMethodType.RefreshByIds:
|
||||
RefreshByIds(instruction.RefresherId, instruction.JsonIds);
|
||||
break;
|
||||
case RefreshInstruction.RefreshMethodType.RefreshByJson:
|
||||
RefreshByJson(instruction.RefresherId, instruction.JsonPayload);
|
||||
break;
|
||||
case RefreshInstruction.RefreshMethodType.RemoveById:
|
||||
RemoveById(instruction.RefresherId, instruction.IntId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
public void RefreshAll(Guid uniqueIdentifier, string Login, string Password)
|
||||
{
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password))
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
cr.RefreshAll();
|
||||
|
||||
RefreshAll(uniqueIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
private void RefreshAll(Guid uniqueIdentifier)
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
cr.RefreshAll();
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
public void RefreshByGuid(Guid uniqueIdentifier, Guid Id, string Login, string Password)
|
||||
{
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password))
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
cr.Refresh(Id);
|
||||
|
||||
RefreshByGuid(uniqueIdentifier, Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshByGuid(Guid uniqueIdentifier, Guid Id)
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
cr.Refresh(Id);
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
public void RefreshById(Guid uniqueIdentifier, int Id, string Login, string Password)
|
||||
{
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password))
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
cr.Refresh(Id);
|
||||
|
||||
RefreshById(uniqueIdentifier, Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
private void RefreshById(Guid uniqueIdentifier, int Id)
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
cr.Refresh(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes objects for all Ids matched in the json string
|
||||
/// </summary>
|
||||
/// <param name="uniqueIdentifier"></param>
|
||||
@@ -62,20 +113,25 @@ namespace umbraco.presentation.webservices
|
||||
[WebMethod]
|
||||
public void RefreshByIds(Guid uniqueIdentifier, string jsonIds, string Login, string Password)
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
var ids = serializer.Deserialize<int[]>(jsonIds);
|
||||
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password))
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
foreach (var i in ids)
|
||||
{
|
||||
cr.Refresh(i);
|
||||
}
|
||||
RefreshByIds(uniqueIdentifier, jsonIds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
private void RefreshByIds(Guid uniqueIdentifier, string jsonIds)
|
||||
{
|
||||
var serializer = new JavaScriptSerializer();
|
||||
var ids = serializer.Deserialize<int[]>(jsonIds);
|
||||
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
foreach (var i in ids)
|
||||
{
|
||||
cr.Refresh(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes objects using the passed in Json payload, it will be up to the cache refreshers to deserialize
|
||||
/// </summary>
|
||||
/// <param name="uniqueIdentifier"></param>
|
||||
@@ -90,25 +146,36 @@ namespace umbraco.presentation.webservices
|
||||
{
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password))
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier) as IJsonCacheRefresher;
|
||||
if (cr == null)
|
||||
{
|
||||
throw new InvalidOperationException("The cache refresher: " + uniqueIdentifier + " is not of type " + typeof (IJsonCacheRefresher));
|
||||
}
|
||||
cr.Refresh(jsonPayload);
|
||||
RefreshByJson(uniqueIdentifier, jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
public void RemoveById(Guid uniqueIdentifier, int Id, string Login, string Password) {
|
||||
private void RefreshByJson(Guid uniqueIdentifier, string jsonPayload)
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier) as IJsonCacheRefresher;
|
||||
if (cr == null)
|
||||
{
|
||||
throw new InvalidOperationException("The cache refresher: " + uniqueIdentifier + " is not of type " + typeof(IJsonCacheRefresher));
|
||||
}
|
||||
cr.Refresh(jsonPayload);
|
||||
}
|
||||
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password)) {
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
cr.Remove(Id);
|
||||
[WebMethod]
|
||||
public void RemoveById(Guid uniqueIdentifier, int Id, string Login, string Password)
|
||||
{
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password))
|
||||
{
|
||||
RemoveById(uniqueIdentifier, Id);
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
private void RemoveById(Guid uniqueIdentifier, int Id)
|
||||
{
|
||||
var cr = CacheRefreshersResolver.Current.GetById(uniqueIdentifier);
|
||||
cr.Remove(Id);
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
public XmlDocument GetRefreshers(string Login, string Password)
|
||||
{
|
||||
if (BusinessLogic.User.validateCredentials(Login, Password))
|
||||
|
||||
Reference in New Issue
Block a user