Staging Data: Locking Danger with ALTER SCHEMA TRANSFER

Developers have struggled with a problem for a long time: how do I load up a new table, then quickly switch it in and replace it, to make it visible to users?

There’s a few different approaches to reloading data and switching it in, and unfortunately most of them have big problems involving locking. One method is this:

  • Create a schema for staging
  • When loading tables, create them in the staging schema
  • When load is complete, drop the table from your production schema
  • Then use ALTER SCHEMA TRANSFER to move the staging table into the production schema

This method sounds good, because items being worked on for staging are easy to identify in their own schema. There’s clearly going to be issues with concurrency between dropping the table and transferring in the new table, but the gamble is that will be fast and can be minimized.

ALTER SCHEMA TRANSFER locks the whole schema

The concurrency problem is worse than you might think. When you run ALTER SCHEMA TRANSFER, it locks up everything in that schema, so nobody can use any table– the lock is not limited to the table you’re transferring.

Not sure? Let’s take a look. First, we create database:

The create a schema named LockMe in the database. Create a table named InnocentTable in the LockMe schema, and insert some rows.

Now create a new table in the dbo schema and add a few rows. We’re going to transfer this into the LockMe schema.

Alright, now it’s time to do the transfer! Here we go. To make this easy to see, we’re going to leave a transaction open:

In another session, we just try to query LockMe.InnocentTable. We didn’t run any commands against it, but this query gets blocked and can’t make any progress:

Looking at this in a third session with sp_WhoIsActive, we can see the problem:

Let go of that schema!
Let go of that schema!

Some Bad News: There Is No Awesome Way to Swap In a Table

The locking you see above is a very high level lock. The query can’t even get a schema stability lock on LockMe.InnocentTable.  There’s no magic hint that will get you around it: adding a NOLOCK hint won’t work (it still needs a schema stability lock).

Optimistic locking won’t get you around it either — queries in SNAPSHOT and READ COMMITTED SNAPSHOT need to get a schema stability lock, too.

If You Must Swap In a Table, ALTER SCHEMA is the Greater Evil

Unless you really want to block absolutely everyone who might use the schema (and live with the consequences), this command will cause you pain.

Need to get to the root cause of blocking? Read more on locking here on our blog for free, or check out our SQL Critical Care®.

Previous Post
Watch Brent Tune Servers [Video] #MSIgnite
Next Post
Finches and Job Roles

16 Comments. Leave new

  • What’s wrong with using SWITCH PARTITION? It doesn’t require partitioning to be used.

    Reply
    • Kendra Little
      May 12, 2015 8:30 am

      Partition switching still requires a schema modification lock, and can block and be blocked.

      Reply
  • What’s wrong with a simple table rename? ie:
    exec sp_Rename ‘tab’, ‘tab_old’;
    exec sp_Rename ‘tab_new’, ‘tab’;

    Or does this cause the schema lock as well?

    Reply
    • Kendra Little
      May 12, 2015 10:49 am

      Yep, renaming causes an exclusive lock. If you don’t have a large amount of read activity or long running reads, that can be OK. But if your number of reads goes up or you start to have longrunning transactions holding locks, it becomes very painful to scale.

      Reply
  • Ray Herring
    May 12, 2015 10:47 am

    I often use partitioned views for this pattern.
    The Alter View statement is blocking but it does not take much time. According to BOL it takes an Exclusive Schema Lock on the View.
    One of my staging ‘tables’ receives on the order of 175M rows/week (200GB). A job creates a new table each week and then alters the view to add the new “sub-table” and remove the oldest “sub-table”. We have applied this to several of our big staging tables and it works very well.
    This approach saves us loads of cleanup since we can just Truncate/Drop the old “sub-table”.

    Reply
    • Kendra Little
      May 12, 2015 10:50 am

      I love partitioned views.

      As long as it’s pretty fast, you’re doing fine. Some folks find themselves in a position where they have to kill off activity to be able to modify the view, and that gets ugly really fast. Table partitioning has similar issues switching in partitions, but they have added some automation as to whether to kill others/kill self/etc in 2014 at least.

      Reply
  • James Lupolt
    May 12, 2015 12:33 pm

    This seems like it might be a good fit for a retry loop with a low lock_timeout or the low priority waits in 2014 and higher. Of course if you have super long running transactions it’s still going to take forever or fail. What do you think of this idea?

    Reply
  • jeff humphreys
    May 12, 2015 12:40 pm

    Seems like a good reason to use several schemas? That at least reduces the scope of the lockout.

    Reply
  • Mark Freeman
    May 12, 2015 12:53 pm

    I’m with Jeff. Why not just use a schema per staging table (or a group of such tables that tend to be updated as a batch) to avoid the collateral damage from the schema locks when transferring or renaming? This may be a nuisance if you have lots of staging tables, but how bad can that really be?

    Reply
    • Kendra Little
      May 12, 2015 1:34 pm

      Let’s say I have a long running query on the currently active table. As soon as I start to do anything that requires a schema modification lock, I’m blocked by it and can create a massive blocking chain behind *me*. It can be really, really bad.

      Reply
  • Thanks for blogging about this Kendra, this is really cool. ALTER SCHEMA TRANSFER has always been in my bag of dev tricks and now I know to tread lightly.

    It’s interesting, I’ve been bitten by terrible locking scenarios like the one you describe like The Sch-M lock is Evil.

    Ironically, in that case, it turns out that the ALTER SCHEMA command could have saved me like I demonstrate in this demo. In this case, I created a special schema called “obsolete” that no one else cared about. I was locking on the ignorable “obsolete” schema rather than the important “dbo” schema. That’s the distinction

    Reply
  • Alex Friedman
    May 14, 2015 1:22 am

    Yay for synonyms. Still have locking issues when switching them, of course, but then what doesn’t.

    Reply
  • […] In the past, I wrote that if you have to do this switcheroo, sp_rename is better than ALTER SCHEMA TRANSFER, but it still has a bunch of problems. […]

    Reply
  • We have the need to occasionally reload our entire EDW set of tables. The only access our customers can use are through our views. So we copy all rows to shadow db tables and re-point the views to that shadow db while we do the reload. Of course there’s the potential locking of views issue. But we monitor usage and do it off hours and its quick. Which is the best method to play the switching game – synonyms, schema transfer or alter view? Partitions don’t seem appropriate here for multiple reasons. Or is there another option I haven’t thought of?

    Reply
  • I had to learn this the hard way. I wish I had read this article before implementing a solution recommended by the architect. I used Alter Schema to move staging tables to final schema, major locks happened. Now I will have to go back and change it.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.