Store based eventing is a feature that was added to Exchange 2000. These events are fired when messages or other storage items are delivered, modified, deleted, etc. They enable an application to take action based on activity it is interested in. There are two types of events: sync and async. Sync events can be extremely useful because they allow an application to take actions that are transacted with the original client operation the event was fired for. So for instance, you could create an app that every time a particular form was delivered to a user it could create or modify another message that kept track of that activity and it would be guaranteed to only update that data when the delivery was successful. Even though they can be awfully useful, we generally discourage their use because they are costly in terms of performance. We usually try to steer developers towards designing their applications to use the async variety of storage events.
Why are sync events so costly? This has to do with a number of factors. First off, operations that take place in a sync event have to be transacted with the originally client operation (that’s what makes them synchronous). In the Exchange store, the database technology that is used is called ESE. Although this technology does support ACID transactions, its implementation is such that transactions cannot exist for a long period of time (longer than say a second or so) without causing problems. ESE has something called a version store that keeps snapshots of changes made in all concurrent transactions so that each one can view its own changes but not see other uncommitted changes. This version store will grow out bounds when a long running transaction is sitting around. The Exchange store is designed around this feature and does not create long running transactions. If we were to fire our sync events and use ESE transactions to manage them, we could no longer guarantee this since we don’t know what the custom event code is doing. To work around this problem, we also created something called group operations. Group operations are basically a logical queuing within the store memory of MAPI functions. When a group operation is committed, the store then plays those operations back and executes all the database activity in a single ESE transaction. This feature required extensive changes in many functions in the store process and results in a those functions having to be executed in multiple passes, resulting in higher processing overhead.
Another performance problem with sync events has to do with how we handle messages internally versus how client applications see messages. The copy message function is a good case in point. What we used to do (and still do if there are no sync events) when a message is copied from one folder to another is process the database rows that correspond to that message directly. If there is a sync event present, however, the store needs to manufacture an OMSG object to present to the application. This entails a lot more overhead.
Async events don’t suffer from these problems. We merely write them to a separate queue and another thread pulls them out and sends them to registered clients. Async events are similar to old MAPI notifications, except they are guaranteed to fire (MAPI notifications will get dropped in the case of a store being stopped).