views:

35

answers:

2

I've outgrown the Sql Server custom actions available in WiX, so I'm taking the bold step of creating my own using Deployment Tools Foundation. I want to be a good citizen and make sure that mine support rollback. But what's the best way of doing it?

I need to support SQL Server 2005 and later, all editions.

The problem, as I see it, is that Windows Installer works in two phases: it does the work, storing undo information as it goes. Then, when all the pieces are in place it either commits (deleting the undo information) or does a rollback.

This means that standard transactions won't do the job. They would have to be completed inside my Execute custom action, and I wouldn't get a chance to roll them back later.

I've considered taking a copy-only backup of the database that I can restore in the rollback action if necessary but I think this approach, whilst simple has shortcomings. I don't know how big our databases will get, for example - so I can't guarantee that there will be space available to hold the backup on the target machine. Also, backup and restore can take a while to complete, and I don't want typical installs (where rollback doesn't happen) to be unnecessarily slow.

So that brings me to my current favoured idea: make sure the Distributed Transaction Coordinator is started up, then initialise a Distributed Transaction before making changes, then either committing it or rolling it back in the appropriate custom actions.

It seems I can uses the members of the TransactionInterop class to export a cookie that will enable me to share the transaction between my different custom actions.

Can anyone with experience of this kind of thing say if it is likely to work?

+2  A: 

Some database/instance operations cannot be done inside a transaction (eg. CREATE/ALTER/DROP ENDPOINT), and other operations cannot be done inside a distributed transaction (eg. SAVE TRANSACTION). So you won't be able to do them at all in your proposed plan. Also your DB upgrade scripts will have to all work correctly when run inside an uncommitted transaction.

I would say that there are fewer risks of going down the backup/restore path (or alternatively creating a database snapshot and restoring from the snapshot on rollback, with the drawback of requiring EE).

Also an option is to have an undo script for every do script run during upgrade, and have the undo script run during rollback and remove the effects of the installation. I understand that this is a hard problem, probably doubles the amount of scripts that have to be developed (and tested...) and requires some serious developer discipline.

Remus Rusanu
+1 for that. Can you elaborate on what you mean by scripts needing to work inside an uncommitted transaction. What kind of problems might arise here?
Samuel Jack
The problems I'm afraid of are error handling and rollbacks. Some script might decide to rollback believing that it rolls back only its local actions, but it would rollback everything. I agree, is not a common setup/install script scenario, but it is possible none the less. Similarly, a BEGIN TRY/BEGIN CATCH block that it could decide to handle a non-doomed transaction error (XACT_STATE() is 1) as a doomed case and rollback.
Remus Rusanu
Having slept on this, I think I'm going to go with backup/restore. I'll make choosing of the backup location an explicit part of the setup routine, and give users the option to not delete the backup at the end of the process. That way it can replace the pre-upgrade backup that we would recommend users make anyway.
Samuel Jack
Thought of a further difficulty with backup and restore: we can't assume that the Sql Server is on the same machine. This makes it difficult to, for example, provide a Browse feature for picking the backup location. Even deleting the backup after the install is tricky.
Samuel Jack
There is a set of (undocumented) procedures used by SSMS to offer its backup/restore and add db file dialogs: xp_fixeddrives, xd_dirtree, xp_subdirs.
Remus Rusanu
+1  A: 

I've done quite a few installers with SQL scripts over the years and I've kind of come to the opinion that it's only suited for simple databases like here's my VB app with a local MSDE / MySQL database or here's my local store for code table lookups and temporary commits while we wait to sync it somewhere else.

Once you get into industrial strength heavy lifting enterprise app type situations I like to get my DB configuration out of the installer and into the application as a first run type story. You can do a lot heavier lifting with C# there and not be constrained by MSI.

Christopher Painter
+1 I actually agree with that (despite having a 'competing' answer). I too advocate an application that is capable of self-bootstraping the database schema when started, and be able to upgrade any version schema on-disk to the current application schema, see http://rusanu.com/2009/05/15/version-control-and-your-database/
Remus Rusanu
@Christopher, Surely with Deployment Tools Foundation you get the best of both worlds: use of C# to do the heavy lifting inside MSI, and simplified application start-up that can just assume the database is ready to go?
Samuel Jack