Designing for Scalability with Microsoft Windows DNA (DV-MPS Designing)
In the stateful model, you let your main business objects keep state for their clients, which means that they must live as long as the client needs them. When you use COM+ or MTS, there's still a limit to how long you should keep a business object alive. As soon as an object has sent its data to update the database, the object should be killed, and there's a good reason for that. When a stateful object sends its data to the database, several things can happen to that data:
- The data can be accepted by the database with no change. In this case, the temporary state you hold in the business object and the permanent state you hold in the database are consistent with each other. You have no problem with that.
- The data can be accepted by the database, but not without changes. A stored procedure or a trigger in the database might do things with your transaction, following certain business rules that aren't known to your business object. In this case, the temporary state held in your business object and the permanent state held in your database are no longer in sync. This could cause severe problems.
- The data can be rejected by the database altogether. As in the previous case, the temporary state held in your business object and the permanent state held in your database are no longer in sync. As in the previous case, this could cause nasty problems.
COM+ and MTS have a safeguard for this. As soon as an object involved in a transaction calls the SetComplete or SetAbort method, COM+ and MTS auto matically deactivate that object and also all other objects either of them owns. When this happens to the object owning the transaction, the entire transaction either aborts or is committed; in either case, all the objects that were involved in the transaction no longer exist.
Your state-keeping components must live over several calls from their clients. That's why we call them stateful. If they don't, they can't fulfill their obligation to keep state for their clients. This means that you can't have your state-keeping components call the SetComplete or SetAbort method as soon as they've completed a task. No, such an object must stay alive as long as its client needs it.
But it shouldn't! An object should cease to exist as soon as it has sent something to the database. As soon as it has asked for data to be saved or deleted, such an object must make sure that it's killed (or if pooled, at least deactivated). In effect, the object must commit suicide because the state it now keeps is a dangerous state. For reasons just explained, that state might be out of sync with the state permanently saved in the database. So, in the Save and Delete operations of such stateful components, you should make them call SetComplete or SetAbort. Which one you choose shouldn't matter since none of these components should support transactions.
We've heard from many developers that they have stateful MTS components, living forever and ever, requiring transactions. We've also heard from the same developers that their applications don't work as they expect, setting and keeping locks in the database and causing deadlocks. So, these developers say, MTS must be in very bad shape. But they're wrong; it's not MTS but their own applications that are in bad shape. Of course their applications don't work—their transactions never end. Of course they get different kinds of locks, including deadlocks. In our opinion, designs that allow permanently stateful objects to engage in or support transactions are very bad indeed.
So, to finish off this subject, here's a solid recommendation from us to you: Don't allow your stateful and state-keeping components to support or require transactions! Also, don't let them live—or at least don't let them keep their state—after having sent their data to the database for insertion, updating, or deletion. If you allow them to stay alive, at least make sure that they refresh the data they keep, resynchronizing themselves with the database.