6

Most if not all of the NSB examples for ASP.NET (or MVC) have the web application sending a message using Bus.Send and possibly registering for a simple callback, which is essentially how I'm using it in my application.

What I'm wondering is if it's possible and/or makes any sense to handle messages in the same ASP.NET application.

The main reason I'm asking is caching. The process might go something like this:

  1. User initiates a request from the web app.
  2. Web app sends a message to a standalone app server, and logs the change in a local database.
  3. On future page requests from the same user, the web app is aware of the change and lists it in a "pending" status.
  4. A bunch of stuff happens on the back-end and eventually the requests gets approved or rejected. An event is published referencing the original request.
  5. At this point, the web app should start displaying the most recent information.

Now, in a real web app, it's almost a sure thing that this pending request is going to be cached, quite possibly for a long period of time, because otherwise the app has to query the database for pending changes every time the user asks for the current info.

So when the request finally completes on the back-end - which might take a minute or a day - the web app needs, at a minimum, to invalidate this cache entry and do another DB lookup.

Now I realize that this can be managed with SqlDependency objects and so on, but let's assume that they aren't available - perhaps it's not a SQL Server back-end or perhaps the current-info query goes to a web service, whatever. The question is, how does the web app become aware of the change in status?

If it is possible to handle NServiceBus messages in an ASP.NET application, what is the context of the handler? In other words, the IoC container is going to have to inject a bunch of dependencies, but what is their scope? Does this all execute in the context of an HTTP request? Or does everything need to be static/singleton for the message handler?

Is there a better/recommended approach to this type of problem?

Aaronaught
  • 120,909
  • 25
  • 266
  • 342
  • I don't know about handling events in IIS, but anytime you introduce async processes you'll have to deal with eventual consistency. Does the web app need to update immediately or can you get away with refreshing the cache every minute, 15 minutes, hourly? – Ryan Jan 16 '12 at 21:06
  • @Ryan: Can we get away with it? Probably. Am I comfortable with it from a customer POV? Not really. I'm fine with EC in general - for example, I realize that nothing will be handled if the site or app pool isn't running, until it starts up again, and *that* is completely fine - but the information should be up-to-date within a few minutes of some user actually requesting it, and the data really should be cached for much longer. This discrepancy is ordinarily resolved by manual or dependency-based cache invalidation. – Aaronaught Jan 16 '12 at 21:09
  • Right, and that's what I do in my synchronous apps. For NSB I just hit the DB every time but I just have a back office app with a few users. – Ryan Jan 16 '12 at 22:05
  • @Ryan: Certainly, for a small-scale app you don't need the caching at all, and that would solve the problem (though I'm sure there are other scenarios in which you might want to handle a message from within ASP.NET). This is a customer-facing app and while we are not Stack Overflow scale, assume at least a few hundred users online at a time - the caching is definitely important. – Aaronaught Jan 17 '12 at 03:27

2 Answers2

3

I've wondered the same thing myself - what's an appropriate level of coupling for a web app with the NServiceBus infrastructure? In my domain, I have a similar problem to solve involving the use of SignalR in place of a cache. Like you, I've not found a lot of documentation about this particular pattern. However, I think it's possible to reason through some of the implications of following it, then decide if it makes sense in your environment.

In short, I would say that I believe it is entirely possible to have a web application subscribe to NServiceBus events. I don't think there would be any technical roadblocks, though I have to confess I have not actually tried it - if you have the time, by all means give it a shot. I just get the strong feeling that if one starts needing to do this, then there is probably a better overall design waiting to be discovered. Here's why I think this is so:

  1. A relevant question to ask relates to your cache implementation. If it's a distributed or centralized model (think SQL, MongoDB, Memcached, etc), then the approach that @Adam Fyles suggests sounds like a good idea. You wouldn't need to notify every web application - updating your cache can be done by a single NServiceBus endpoint that's not part of your web application. In other words, every instance of your web application and the "cache-update" endpoint would access the same shared cache. If your cache is in-process however, like Microsoft's Web Cache, then of course you are left with a much trickier problem to solve unless you can lean on Eventual Consistency as was suggested.
  2. If your web app subscribes to a particular NServiceBus event, then it becomes necessary for you to have a unique input queue for each instance of your web app. Since it's best practice to consider scale-out of your web app using a load balancer, that means that you could end up with N queues and at least N subscriptions, which is more to worry about than a constant number of subscriptions. Again, not a technical roadblock, just something that would make me raise an eyebrow.
  3. The David Boike article that was linked raises an interesting point about app pools and how their lifetimes might be uncertain. Also, if you have multiple app pools running simultaneously for the same application on a server (a common scenario), they will all be trying to read from the same message queue, and there's no good way to determine which one will actually handle the message. More of then than not, that will matter. Sending commands, in contrast, does not require an input queue according to this post by Udi Dahan. This is why I think one-way commands sent by web apps are much more commonly seen in practice.
  4. There's a lot to be said for the Single Responsibility Principle here. In general, I would say that if you can delegate the "expertise" of sending and receiving messages to an NServiceBus Host as much as possible, your overall architecture will be cleaner and more manageable. Through experience, I've found that if I treat my web farm as a single entity, i.e. strip away all acknowledgement of individual web server identity, that I tend to have less to worry about. Having each web server be an endpoint on the bus kind of breaks that notion, because now "which server" comes up again in the form of message queues.

Does this help clarify things?

killthrush
  • 4,859
  • 3
  • 35
  • 38
1

An endpoint(NSB) can be created to subscribe to the published event and update the cache. The event shouldn't be published until the actual update is made so you don't get out of sync. The web app would continue to pull data from the cache on the next request, or you can build in some kind of delay.

Adam Fyles
  • 6,030
  • 1
  • 23
  • 29
  • This sounds a little vague to me; of course the event won't be published until the transaction has actually happened but at what point is it guaranteed to reach the message handler in the ASP.NET application? And do you have any comment on my question about the dependency scopes? I'm trying to understand if it's OK for a message handler to take on a dependency to something that's bound to the HTTP request (Ninject - `InRequestScope`). – Aaronaught Jan 18 '12 at 23:05
  • It would be guaranteed via the transport, assuming it is transactional and durable. The scope would be per request, check out the AsyncPages example. This registers a callback for the page and is handling messages, this sounds like it may be good enough in this case. It just seems to me by the time the next read is made the update will be there and so you could do some UI things to help that. This site works that way, if you refresh fast enough the data isn't there. – Adam Fyles Jan 19 '12 at 13:59
  • This isn't the exact same situation, but I think it is a good read for some background on NSB/web apps: http://www.make-awesome.com/2010/10/why-not-publish-nservicebus-messages-from-a-web-application/ – Adam Fyles Jan 19 '12 at 14:01
  • Thanks - I am familiar with that page and don't publish events to NServiceBus from web apps, but unfortunately it doesn't have much to say on handling events as a subscriber... it definitely makes sense that callbacks would be executed in the same request that originated them, but it seems like as far as `IHandleMessages` and subscriptions are concerned, I'm just going to have to experiment... – Aaronaught Jan 20 '12 at 12:29
  • I'm sorry I don't have more info, but I've haven't personally done this for fear of stealing resources from the web app. I'd be interesting in your results. – Adam Fyles Jan 20 '12 at 13:28
  • Good point, I don't know how resource-hungry NServiceBus is when you're running handlers (although I am pretty sure that it peeks the input queue whether you have any handlers or not). Thanks for the advice so far, I'll see what happens and report back. – Aaronaught Jan 20 '12 at 13:49