Firing an event on each load-balanced node when publishing
We have two web servers with "distributedCalls" enabled so that they keep their caches up to date.
I would like to fire an event when someone publishes a page, but I would like that event to fire on both web servers (so I can update another cache that I am using).
We are upgrading to Umbraco 6.1.1. When I looked into this a while ago (an earlier Umbraco 6.x build) I could have sworn I found an event that fires when the cache is updated, but I can't for the life of me find it anywhere now I'm in a position to use it. (Doh!)
I don't suppose anyone can point me in the right direction could they? Or if there's an alternative way. I'm hoping it's right under my nose and I'm just not seeing it.
I know I could write my own thing to look at the distributed list and fire off web service calls etc. but I'd like to keep it as simple as I can, so I'm hoping there's something built-in to do this.
Just to clarify, my issue isn't how to register an event, it's how to have the event trigger on both servers. I can register a publish event no problem, but that's not quite what I'm after unless I do some kind of web servicey type call as per distributed calls (or something like SignalR). Ideally I would like to avoid that if there is a simpler way.
I am convinced that there was an event in there somewhere and am frustrated with myself for not making a note of it!
I don't know about that. I'd be surprised though. :)
Triggering an event on both servers from a database event would need stuff that Umbraco can't support since it supports multiple database platforms, and SignlaR and it's like isn't bundled. Could be that something polls the database, though, but I don't know about that. Might be there's a package that does it.
However, if you subscribe to the published event and write some SignlaR code, I'm convinced you can get away with less than 30 lines of C#. ;)
(I think there's a way to turn off the content cache xml file, but I'm sure you don't want to do that.)
With distributed calls, when I publish a page a web service call is made to each of the two nodes telling them to update their cache. I wouldn't have though it would be out of the question for an event to be raised by each at that point. I will have to have a bigger dig into the source to see what's going on. (I had a quick look but wasn't entirely sure.) Don't think it's really anything to do with the database.
The reason I want to avoid SignalR or anything like that is that regardless of how little code it might need, it still adds extra complexity and more noise on the server which I would like to keep to a minimum.
I suspect I will have to do that kind of thing though, but want to make sure there isn't something nice and simple I can do instead first.
Right, I'm fairly new to Umbraco myself, so haven't looked into anything called "distributed calls" (?).
If there's already something that does that I'd be surprised if there isn't an event when the cache is cleared and stuff is republished. Sure the one(s) I linked to aren't raised?
As long as it's the servers that has to do the service calls, I guess you can do it without SignalR. They can enroll with each other or through some coordinator/master server. Shouldn't be too difficult to set up. (Configure a list of all servers IPs on all servers and make them call each other)
I'd be happy to hear what you end up with, we're probably going to need something like that ourselves. :)
For normal Umbraco stuff you can edit umbracoSettings.config and there's a <distributedCall> section. If you set this to be enabled you can then list out a series of servers. (There are examples in the comments).
What this means is that if you publish a page on one server, web service calls are made to each server in your farm telling them about it so that they can update their caches. (You still need replication of the media folders - we use DFS for this I think.)
For most load-balanced environments this should be enough but I've also got some other bespoke bits and bobs which is why I need to do something similar to update my own cache. I've got a few options - I've not had time to look into it since my original post but should hopefully have a look again soon. I'll update this if I get anywhere.
For now I think every element of the Umbraco "domain model" has at least three implementations each, either with different namespace or different casing.
Best way of searching is Resharper > Ctrl+T / Ctrl+Shift+T / F12 / Mouse thumb and just surf. :) (And sometimes bruteforcing with Ctrl+Shift+F)
I've spent a little time with this now. Although I've not got load balanced servers set up at the moment in dev so haven't proven that this works in particular, it does seem to work fairly well. (I was using Document_AfterPublish initially and had some problems which have now gone away).
A few things worth noting:.
if you unpublish or delete a page this event doesn't get called. Instead you need "AfterClearDocumentCache". So in my case I subscribed to both and they work great.
If you publish a parent and choose to publish all unpublished children it will call the event for each child node.
But if you have a published node and then unpublish its parent, it will not call the event for the children. So I guess in this case I will need to call Descendants and see if there's anything there I'm interested in.
This uses the old Document API not the new content service API. I am just in the process of upgrading our 4.7.2 site to 6.1.1 and we have used Document heavily and found that Document is now seriously buggy. I have rewritten a lot of stuff to use the new service and it works great (it is a big improvement over the original). I suspect when I come to look at the descendants I will need to use a mixture of the two APIs. (IE register the event with Document but then look at the descendants using the ContentService.) I've not verified that but from current experience the Document API in 6.1.1 isn't so much obsolete as totally broken. Hopefully these events will be migrated to the new API in the future.
Anyway, thanks very much for your help - it feels like this has done the trick.
A little side note but I wonder if you could have a CacheDependancy on the umbraco.config file? Can't remember if that gets updated every time but might be worth checking, it might cover some of your other issues too. Only trouble is it only tells you that "something" changed and not what but that might be enough to renew your cache?
That's an interesting idea! Although I would really prefer to only update if I have to. I started to look at the descendant stuff but it occurred to me that in my case having stale entries in the cache isn't really that big a problem, particularly because it will catch up eventually anyway. So I've left it. It seems to work pretty well.
If refreshing all content then e.MessageType will equal MessageType.RefreshAll and e.MessageObject will be null. Otherwise e.MessageObject will contain the thing being updated - it's of type "object". In my testing it seems to be of type IContent so I've done "e.MessageObject as IContent" and checked against nulls just in case it comes through as something else.
I haven't tested it in a load balanced environment yet but from looking at the code it does seem to be triggered by distributed calls if they are enabled so I am reasonably sure it would work.
Yes, this thread has been a potential life saver for me too as I was stuck with a similar problem.
This is based on Umbraco 6.1.6
For the record John's hunch was right - the PageCacheRefresher.CacheUpdated is raised by distributed calls in a load balance envrionment. I registered it in the OnApplicationStarted event.
One thing I found was different to John was that e.MessageObject was being sent as an Int32 and was the ID of the content node being updated.
In a bulk publish the PageCacheRefresher.CacheUpdated event was triggered once for every node being updated (so if ten nodes are published it is raised ten times).
Firing an event on each load-balanced node when publishing
We have two web servers with "distributedCalls" enabled so that they keep their caches up to date.
I would like to fire an event when someone publishes a page, but I would like that event to fire on both web servers (so I can update another cache that I am using).
We are upgrading to Umbraco 6.1.1. When I looked into this a while ago (an earlier Umbraco 6.x build) I could have sworn I found an event that fires when the cache is updated, but I can't for the life of me find it anywhere now I'm in a position to use it. (Doh!)
I don't suppose anyone can point me in the right direction could they? Or if there's an alternative way. I'm hoping it's right under my nose and I'm just not seeing it.
I know I could write my own thing to look at the distributed list and fire off web service calls etc. but I'd like to keep it as simple as I can, so I'm hoping there's something built-in to do this.
Events doc is here:
http://our.umbraco.org/documentation/Reference/Events/application-startup
(Quick google-fu ;) )
You could possibly use serverside SignalR to tell the other server(s) that new content is published.
http://www.asp.net/signalr
Lars-Erik
Thanks for the reply.
Just to clarify, my issue isn't how to register an event, it's how to have the event trigger on both servers. I can register a publish event no problem, but that's not quite what I'm after unless I do some kind of web servicey type call as per distributed calls (or something like SignalR). Ideally I would like to avoid that if there is a simpler way.
I am convinced that there was an event in there somewhere and am frustrated with myself for not making a note of it!
I don't know about that. I'd be surprised though. :)
Triggering an event on both servers from a database event would need stuff that Umbraco can't support since it supports multiple database platforms, and SignlaR and it's like isn't bundled. Could be that something polls the database, though, but I don't know about that. Might be there's a package that does it.
However, if you subscribe to the published event and write some SignlaR code, I'm convinced you can get away with less than 30 lines of C#. ;)
(I think there's a way to turn off the content cache xml file, but I'm sure you don't want to do that.)
Thanks again for the reply.
With distributed calls, when I publish a page a web service call is made to each of the two nodes telling them to update their cache. I wouldn't have though it would be out of the question for an event to be raised by each at that point. I will have to have a bigger dig into the source to see what's going on. (I had a quick look but wasn't entirely sure.) Don't think it's really anything to do with the database.
The reason I want to avoid SignalR or anything like that is that regardless of how little code it might need, it still adds extra complexity and more noise on the server which I would like to keep to a minimum.
I suspect I will have to do that kind of thing though, but want to make sure there isn't something nice and simple I can do instead first.
Right, I'm fairly new to Umbraco myself, so haven't looked into anything called "distributed calls" (?).
If there's already something that does that I'd be surprised if there isn't an event when the cache is cleared and stuff is republished.
Sure the one(s) I linked to aren't raised?
As long as it's the servers that has to do the service calls, I guess you can do it without SignalR. They can enroll with each other or through some coordinator/master server. Shouldn't be too difficult to set up. (Configure a list of all servers IPs on all servers and make them call each other)
I'd be happy to hear what you end up with, we're probably going to need something like that ourselves. :)
For normal Umbraco stuff you can edit umbracoSettings.config and there's a <distributedCall> section. If you set this to be enabled you can then list out a series of servers. (There are examples in the comments).
What this means is that if you publish a page on one server, web service calls are made to each server in your farm telling them about it so that they can update their caches. (You still need replication of the media folders - we use DFS for this I think.)
For most load-balanced environments this should be enough but I've also got some other bespoke bits and bobs which is why I need to do something similar to update my own cache. I've got a few options - I've not had time to look into it since my original post but should hopefully have a look again soon. I'll update this if I get anywhere.
(Oops - double post, sorry!)
Ah, cool. Nice to know about.
I have the core code open and did a 5 minute browse of the code.
Here's the event: :)
umbraco.presentation.content.AfterUpdateDocumentCache
(static)
Ahhh that looks like it! I tried Content with a capital "C" but not a lowercase one. My searching skills are clearly not up to much!
I'll give that a go and post back on here about how I get on. Thank you so much!!
Ahhh that looks like it! I tried Content with a capital "C" but not a lowercase one. My searching skills are clearly not up to much!
I'll give that a go and post back on here about how I get on. Thank you so much!!
For now I think every element of the Umbraco "domain model" has at least three implementations each, either with different namespace or different casing.
Best way of searching is Resharper > Ctrl+T / Ctrl+Shift+T / F12 / Mouse thumb and just surf. :)
(And sometimes bruteforcing with Ctrl+Shift+F)
I learned a lot by this, so thanks yourself!
I've spent a little time with this now. Although I've not got load balanced servers set up at the moment in dev so haven't proven that this works in particular, it does seem to work fairly well. (I was using Document_AfterPublish initially and had some problems which have now gone away).
A few things worth noting:.
Anyway, thanks very much for your help - it feels like this has done the trick.
A little side note but I wonder if you could have a CacheDependancy on the umbraco.config file? Can't remember if that gets updated every time but might be worth checking, it might cover some of your other issues too. Only trouble is it only tells you that "something" changed and not what but that might be enough to renew your cache?
That's an interesting idea! Although I would really prefer to only update if I have to. I started to look at the descendant stuff but it occurred to me that in my case having stale entries in the cache isn't really that big a problem, particularly because it will catch up eventually anyway. So I've left it. It seems to work pretty well.
I noticed a problem with this in that if I right click the "Content" node and republish the entire site, this event wasn't firing.
Rummaging around the source I found another way that appears to work in either case and also uses the newer API:
This event is triggered if something is either added to, or removed from, the cache. The event handler signature looks like this:
If refreshing all content then e.MessageType will equal MessageType.RefreshAll and e.MessageObject will be null. Otherwise e.MessageObject will contain the thing being updated - it's of type "object". In my testing it seems to be of type IContent so I've done "e.MessageObject as IContent" and checked against nulls just in case it comes through as something else.
I haven't tested it in a load balanced environment yet but from looking at the code it does seem to be triggered by distributed calls if they are enabled so I am reasonably sure it would work.
Comment author was deleted
FWIW, this has been a great read for the same issue that I have.
Yes, this thread has been a potential life saver for me too as I was stuck with a similar problem.
This is based on Umbraco 6.1.6
For the record John's hunch was right - the PageCacheRefresher.CacheUpdated is raised by distributed calls in a load balance envrionment. I registered it in the OnApplicationStarted event.
One thing I found was different to John was that e.MessageObject was being sent as an Int32 and was the ID of the content node being updated.
In a bulk publish the PageCacheRefresher.CacheUpdated event was triggered once for every node being updated (so if ten nodes are published it is raised ten times).
is working on a reply...