A deadlock was detected while trying to lock variables in SSIS

by Darren Green 10 Aug 2010 22:19
Error: 0xC001405C at SQL Log Status: A deadlock was detected while trying to lock variables 
"User::RowCount" for read/write access. A lock cannot be acquired after 16 attempts. The locks timed out.

Have you ever considered variable locking when building your SSIS packages? I expect many people haven’t just because most of the time you never see an error like the one above. I’ll try and explain a few key concepts about variable locking and hopefully you never will see that error.

First of all, what is all this variable locking all about? Put simply SSIS variables have to be locked before they can be accessed, and then of course unlocked once you have finished with them. This is baked into SSIS, presumably to reduce the risk of race conditions, but with that comes some additional overhead in that you need to be careful to avoid lock conflicts in some scenarios. The most obvious place you will come across any hint of locking (no pun intended) is the Script Task or Script Component with their ReadOnlyVariables and ReadWriteVariables properties.

Script Task Editor

These two properties allow you to enter lists of variables to be used within the task, or to put it another way, these lists of variables to be locked, so that they are available within the task. During the task pre-execute phase the variables and locked, you then use them during the execute phase when you code is run, and then unlocked for you during the post-execute phase. So by entering the variable names in one of the two list, the locking is taken care of for you, and you just read and write to the Dts.Variables collection that is exposed in the task for the purpose.

As you can see in the image above, the variable PackageInt is specified, which means when I write the code inside that task I don’t have to worry about locking at all, as shown below.

public void Main()
    // Set the variable value to something new
    Dts.Variables["PackageInt"].Value = 199;

    // Raise an event so we can play in the event handler
    bool fireAgain = true;
    Dts.Events.FireInformation(0, "Script Task Code", 
        "This is the script task raising an event.", null, 0, ref fireAgain);

    Dts.TaskResult = (int)ScriptResults.Success;

As you can see as well as accessing the variable, hassle free, I also raise an event. Now consider a scenario where I have an event hander as well as shown below.

Package Overview

Now what if my event handler uses tries to use the same variable as well? Well obviously for the point of this post, it fails with the error quoted previously. The reason why is clearly illustrated if you consider the following sequence of events.

  1. Package execution starts
  2. Script Task in Control Flow starts
  3. Script Task in Control Flow locks the PackageInt variable as specified in the ReadWriteVariables property
  4. Script Task in Control Flow executes script, and the On Information event is raised
  5. The On Information event handler starts
  6. Script Task in On Information event handler starts
  7. Script Task in On Information event handler attempts to lock the PackageInt variable (for either read or write it doesn’t matter), but will fail because the variable is already locked.

The problem is caused by the event handler task trying to use a variable that is already locked by the task in Control Flow. Events are always raised synchronously, therefore the task in Control Flow that is raising the event will not regain control until the event handler has completed, so we really do have un-resolvable locking conflict, better known as a deadlock.

In this scenario we can easily resolve the problem by managing the variable locking explicitly in code, so no need to specify anything for the ReadOnlyVariables and ReadWriteVariables properties.

public void Main()
    // Set the variable value to something new, with explicit lock control
    Variables lockedVariables = null;
    Dts.VariableDispenser.LockOneForWrite("PackageInt", ref lockedVariables);
    lockedVariables["PackageInt"].Value = 199;

    // Raise an event so we can play in the event handler
    bool fireAgain = true;
    Dts.Events.FireInformation(0, "Script Task Code", 
        "This is the script task raising an event.", null, 0, ref fireAgain);

    Dts.TaskResult = (int)ScriptResults.Success;

Now the package will execute successfully because the variable lock has already been released by the time the event is raised, so no conflict occurs.

For those of you with a SQL Engine background this should all sound strangely familiar, and boils down to getting in and out as fast as you can to reduce the risk of lock contention, be that SQL pages or SSIS variables.

Unfortunately we cannot always manage the locking ourselves. The Execute SQL Task is very often used in conjunction with variables, either to pass in parameter values or get results out. Either way the task will manage the locking for you, and will fail when it cannot lock the variables it requires.

The scenario outlined above is clear cut deadlock scenario, both parties are waiting on each other, so it is un-resolvable. The mechanism used within SSIS isn’t actually that clever, and whilst the message says it is a deadlock, it really just means it tried a few times, and then gave up. The last part of the error message is actually the most accurate in terms of the failure, A lock cannot be acquired after 16 attempts. The locks timed out. 

Now this may come across as a recommendation to always manage locking manually in the Script Task or Script Component yourself, but I think that would be an overreaction. It is more of a reminder to be aware that in high concurrency scenarios, especially when sharing variables across multiple objects, locking is important design consideration.

Update – Make sure you don’t try and use explicit locking as well as leaving the variable names in the ReadOnlyVariables and ReadWriteVariables lock lists otherwise you’ll get the deadlock error, you cannot lock a variable twice!

Comments (3) -

8/27/2010 7:31:58 PM #

Todd McDermid

To support Darren's statement that this method has a valuable but narrow range of applications, I've posted my thoughts on why you should use the ReadOnlyVariables and ReadWriteVariables properties in Scripts almost exclusively: toddmcdermid.blogspot.com/.../...ariables-and.html

Todd McDermid Canada

5/25/2011 6:45:39 PM #

Michael Rivera

How would you troubleshoot this if this is occurring in a foreach loop?
It occurs sometimes during the assignment of a variable in a foreach loop

Michael Rivera United States

6/30/2011 1:36:00 PM #

Darren Green

If you get the deadlock error then it is still because of multiple consumers using the same variable. If one of them is the For Each Loop assignment, you cannot do much about that, so you need to look at what else is using that variable. Something must also be using the variable at the same time as the For Each Loop assignment is trying to take place. One approach to reducing the risk is to use variables scoped to the item that uses them. In your case use a variable scoped to the For Each Loop itself. It will only be available to the loop and objects within it.

Darren Green United Kingdom

Pingbacks and trackbacks (1)+

Add comment

  Country flag

  • Comment
  • Preview

SQLIS | Data Generator Source Adapter

Data Generator Source Adapter

by Darren Green 5 Jun 2012 14:19

This component needs little explanation. It generates random integer (DT_I4) and string (DT_WSTR) data and places them in the pipeline. You specify how many columns of each you would like and for any string columns you pass a fixed length value. You then need to specify how many rows in total you require to be generated.

This component is used by us to do testing of the pipeline and components downstream. Previously we would have used a script component (as a source) to generate the rows but found ourselves rewriting the code too often so created this component.


Data Generator Editor 2005
SQL Server 2005 Integration Services
Data Generator Editor 2008/2012
SQL Server 2008/2012 Integration Services

The component is provided as an MSI file, however to complete the installation, you will have to add the transformation to the Visual Studio toolbox manually. Right-click the toolbox, and select Choose Items.... Select the SSIS Data Flow Items tab, and then check the Data Generator Source from the list.


The Data Generator Source Adapter is available for SQL Server 2005, SQL Server 2008 (includes R2) and SQL Server 2012. Please choose the version to match your SQL Server version, or you can install multiple versions and use them side by side if you have more than one version of SQL Server installed.

Data Generator Source Adapter for SQL Server 2005

Data Generator Source Adapter for SQL Server 2008

Data Generator Source Adapter for SQL Server 2012

Version History

SQL Server 2012

Version - SQL Server 2012 release. Includes upgrade support for both 2005 and 2008 packages to 2012.
(5 Jun 2012)

SQL Server 2008

Version - SQL Server 2008 February 2008 CTP. Includes support for upgrade of 2005 packages. Simplified user interface.
(4 Mar 2008)

Version - SQL Server 2008 November 2007 CTP. String columns will now use the default system code page. Previously string columns always used 1252.
(15 Feb 2008)

SQL Server 2005

Version - SQL Server 2005 RTM Refresh. SP1 Compatibility Testing.
(12 Jun 2006)

Version - SQL Server 2005 IDW 16 Sept CTP. Public release.
(6 Oct 2005)

Add comment

  Country flag

  • Comment
  • Preview