<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Alberto Ferrari : SSIS</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx</link><description>Tags: SSIS</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>SSIS: living in a parallel world? Not yet...</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/2008/09/14/ssis-living-in-a-parallel-world-not-yet.aspx</link><pubDate>Sun, 14 Sep 2008 19:57:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:8899</guid><dc:creator>AlbertoFerrari</dc:creator><slash:comments>4</slash:comments><comments>http://sqlblog.com/blogs/alberto_ferrari/comments/8899.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/alberto_ferrari/commentrss.aspx?PostID=8899</wfw:commentRss><description>&lt;P&gt;Today I come into a nice bug (misfeature?) of SSIS 2008 and I'd like to share it with you. Let's start from the beginning:&lt;/P&gt;
&lt;P&gt;One of the keys to make something speedy is to parallelize it. In TableDifference I read from two inputs, compare them and produce several outputs for new, deleted, updated rows and so on. It is the classical "produce/consumer" paradigm that works fine in parallel, creating a consumer thread that compares rows and different threads to produce them. &lt;/P&gt;
&lt;P&gt;During the porting of the component in SQL 2008 I discovered that my code does not work as expected. It reads all the lines, then terminates abnormally without producing a single row of output. It took me a couple of hours and several coffes to discover the change in the way SSIS handles asyncronous components causing my bug.&lt;/P&gt;
&lt;P&gt;In order to make the problem easier to understand (for microsoft connect too), I produced the same behaviour with a much simpler script component. Imagine to have a script transformation that needs to make some very complex work on each row. You will end up with something like this:&lt;/P&gt;&lt;PRE class=csharpcode&gt;    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;override&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Input0_ProcessInputRow(Input0Buffer Row) {
        Output0Buffer.AddRow();
        Output0Buffer.LineID = VeryComplexFunction(Row);
        &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (Row.EndOfRowset ()) {
           OutputBuffer.SetEndOfRowset ();
        }
    }&lt;/PRE&gt;
&lt;P&gt;But, if you have a 64 CPU computer. You can imagine to detach many threads, each one handling just one very complex function, in this way you will produce a massive parallel computation. Wow! The solution to get maximum speed would be this:&lt;/P&gt;&lt;PRE class=csharpcode&gt;    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;override&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Input0_ProcessInputRow(Input0Buffer Row) {
        Thread T = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; Thread(SendAsyncRow);
        T.Start(Row.EndOfRowset());
    }

    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; SendAsyncRow(Object O) {
        Output0Buffer.AddRow();
        Output0Buffer.LineID = VeryComplexFunction();
        &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; ((&lt;SPAN class=kwrd&gt;bool&lt;/SPAN&gt;)O == &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;) {
            Output0Buffer.SetEndOfRowset();
        }
    }&lt;/PRE&gt;
&lt;P&gt;You detach a separate thread for each row, the thread will compute the very complex function and send it to the output buffer as soon as it is ready. In order to make SSIS understand when everything is finished, you signal SetEndOfRowset in the last thread.&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;EM&gt;Don't start crying, this code will never work, the SetEndOfRowset is called on the last read line and not on the last produced one, but this is just pseudocode to understand the topic, don't blame me for inconsistencies. :) Let's go on with the main topic.&lt;/EM&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Under SSIS 2005 this code (in VB, of course) would work fine as the engine detects the end of processing when it receives the EndOfRowset on the outputs. In SSIS 2008 the engine detects the end of the processing as soon as the &lt;EM&gt;&lt;STRONG&gt;last call to ProcessInputRow has finished&lt;/STRONG&gt;&lt;/EM&gt;. So, in our case, after the last thread has been detached SSIS believes that the processing is finished and calls the cleaning up of the component, completely ignoring both the presence of separate threads running and our williness to not terminate the component because we know that we need some more time.&lt;/P&gt;
&lt;P&gt;The result? As with any other parallel bug you will sometime get a crash, on the same inputs sometime finish corectly, sometime get an error... the nightmare of any programmer.&lt;/P&gt;
&lt;P&gt;If you want to try it by yourself, cut and paste this code to a transformation script in a data flow that process some lines ( rows 10/20 lines are enough):&lt;/P&gt;&lt;PRE class=csharpcode&gt;&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System;
&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.Data;
&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; Microsoft.SqlServer.Dts.Pipeline.Wrapper;
&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; Microsoft.SqlServer.Dts.Runtime.Wrapper;

&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.Threading;

[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
&lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;class&lt;/SPAN&gt; ScriptMain : UserComponent {

    &lt;SPAN class=kwrd&gt;bool&lt;/SPAN&gt; useThreads = &lt;SPAN class=kwrd&gt;false&lt;/SPAN&gt;;

    &lt;SPAN class=rem&gt;/// &amp;lt;summary&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// Processes input rows&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &amp;lt;/summary&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &amp;lt;param name="Row"&amp;gt;&amp;lt;/param&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;override&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Input0_ProcessInputRow(Input0Buffer Row) {
        Thread T = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; Thread(SendAsyncRow);

        &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (useThreads) {
            T.Start(Row.EndOfRowset());
        } &lt;SPAN class=kwrd&gt;else&lt;/SPAN&gt; {
            SendRow();
        }
    }

    &lt;SPAN class=rem&gt;/// &amp;lt;summary&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// Sends a row in sync mode&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &amp;lt;/summary&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=kwrd&gt;private&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; SendRow() {
        &lt;SPAN class=rem&gt;// Thread.Sleep(100);&lt;/SPAN&gt;
        Output0Buffer.AddRow();
        Output0Buffer.LineID = VeryComplexFunction();
    }

    &lt;SPAN class=rem&gt;/// &amp;lt;summary&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// Sends a row in async mode&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &amp;lt;/summary&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &amp;lt;param name="O"&amp;gt;&amp;lt;/param&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; SendAsyncRow(Object O) {
        Thread.Sleep(100);
        Output0Buffer.AddRow();
        Output0Buffer.LineID = VeryComplexFunction();
        &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; ((&lt;SPAN class=kwrd&gt;bool&lt;/SPAN&gt;)O == &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;) {
            Output0Buffer.SetEndOfRowset();
        }
    }

    &lt;SPAN class=rem&gt;/// &amp;lt;summary&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// Not very complex... but it's a demo!&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &amp;lt;/summary&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=rem&gt;/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;/SPAN&gt;
    &lt;SPAN class=kwrd&gt;private&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;int&lt;/SPAN&gt; VeryComplexFunction() {
        &lt;SPAN class=kwrd&gt;return&lt;/SPAN&gt; 10;
    }
}&lt;/PRE&gt;
&lt;P&gt;using Serial mode (useThreads se to false) everything works fine. Using parallel mode (useThreads set to true) SSIS will crash and produce no output at all. The bug in TableDifference was very similar to that, I needed to handle the wait for termination of the consumer process during the ProcessInput function.&lt;/P&gt;
&lt;P&gt;It is not the first time I discover inconsistencies in the way SSIS handles parallelism, in order to try to make it easier to write code (using 2008 mode there is no need to call SetEndOfRowset and your program will run fine if you forget it) the architects of SSIS introduce a behavior that will make better and correct code crash. If you believe this is a problem, vote &lt;A href="https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=367692" target=_blank&gt;here&lt;/A&gt;, anyway... be aware of it, it mighe be useful in the future. :)&lt;/P&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=8899" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SQL+Server/default.aspx">SQL Server</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/TableDifference/default.aspx">TableDifference</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx">SSIS</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/Components/default.aspx">Components</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SQL/default.aspx">SQL</category></item><item><title>Table Difference 2.0 - Call for beta</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/2007/12/17/table-difference-2-0-call-for-beta.aspx</link><pubDate>Mon, 17 Dec 2007 11:53:37 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:4030</guid><dc:creator>AlbertoFerrari</dc:creator><slash:comments>0</slash:comments><comments>http://sqlblog.com/blogs/alberto_ferrari/comments/4030.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/alberto_ferrari/commentrss.aspx?PostID=4030</wfw:commentRss><description>&lt;p&gt;The new version 2.0 of SSIS SCD handling component &lt;a href="http://www.sqlbi.eu/Projects/TableDifference.aspx" target="_blank"&gt;TableDifference&lt;/a&gt; is in beta, I am searching for beta testers and some help in writing an installer for it. You will find all the informations at &lt;a href="http://www.sqlbi.eu"&gt;www.sqlbi.eu&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;The final release of the component will be as usually freely available with sources as soon as it has been tested enough.&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=4030" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/TableDifference/default.aspx">TableDifference</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx">SSIS</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/Components/default.aspx">Components</category></item><item><title>SSIS Multicast and Trash or Separate and Union?</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/2007/11/15/ssis-multicast-and-trash-or-separate-and-union.aspx</link><pubDate>Thu, 15 Nov 2007 12:15:22 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:3345</guid><dc:creator>AlbertoFerrari</dc:creator><slash:comments>2</slash:comments><comments>http://sqlblog.com/blogs/alberto_ferrari/comments/3345.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/alberto_ferrari/commentrss.aspx?PostID=3345</wfw:commentRss><description>&lt;p&gt;Today I was&amp;nbsp;optimizing&amp;nbsp;a package that handles a very common topic: process some rows and send all them to a flow but, for some of them, make additional processing. &lt;/p&gt; &lt;p&gt;A simple Multicast and a Conditional Split solve the problem, the picture explains it better than my words:&lt;/p&gt; &lt;p&gt;&lt;a href="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/SSISandtheageofconsumerism_C3D4/p.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="358" alt="" src="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/SSISandtheageofconsumerism_C3D4/p_thumb.png" width="461" border="0"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;Now, I was wondering why we should duplicate one million rows to two millions and then trash half of them away. I thought a better solution was to separate the rows before and duplicate only half a million, bringing them together again with the other half million from the other flow. So I tried this solution, thas does exactly the same but does not trash anything. We are not in the age of consumerism and we do not like to trash anything, don't we?&lt;/p&gt; &lt;p&gt;&lt;a href="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/SSISandtheageofconsumerism_C3D4/image_3.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="348" alt="image" src="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/SSISandtheageofconsumerism_C3D4/image_thumb_3.png" width="369" border="0"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;Well, even if I thought it would be a better solution, it is not. The first one (with one million rows of 1K each) takes approximately 13 seconds to run, the second one with the same data 16 seconds, that is 23% slower than before.&lt;/p&gt; &lt;p&gt;So, the conclusion is that SSIS still lives in the age of consumerism, the process of UNION ALL takes a very long time to process and makes trashing a better solution. There are good explanations for this (UNION ALL does a lot more work than Multicast does) but I did not think that a 23% degradation would occur. &lt;/p&gt; &lt;p&gt;The lesson is: it is best to trash then try to recycle, at least with data with SSIS. :)&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=3345" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx">SSIS</category></item><item><title>Table Difference 1.2</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/2007/07/23/table-difference-1-2.aspx</link><pubDate>Mon, 23 Jul 2007 08:16:29 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:1900</guid><dc:creator>AlbertoFerrari</dc:creator><slash:comments>0</slash:comments><comments>http://sqlblog.com/blogs/alberto_ferrari/comments/1900.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/alberto_ferrari/commentrss.aspx?PostID=1900</wfw:commentRss><description>&lt;p&gt;&lt;a href="http://www.sqlbi.eu/Projects/TableDifference/tabid/74/Default.aspx"&gt;TableDifference&lt;/a&gt; has been updated to version 1.2. For those who want to read the main description of the component can be found &lt;a href="http://www.sqlbi.eu/Home/tabid/36/ctl/Details/mid/374/ItemID/0/Default.aspx"&gt;here&lt;/a&gt;&amp;nbsp;but take a look at the full story, this component and the documentaton has been updated several times.&lt;/p&gt; &lt;p&gt;You will find complete documentation about the changes at &lt;a href="http://www.sqlbi.eu"&gt;www.sqlbi.eu&lt;/a&gt;&amp;nbsp;and all the sources and binaries in the download section.&lt;/p&gt; &lt;p&gt;Have fun with your SCD handling, any comment&amp;nbsp;will&amp;nbsp;be&amp;nbsp;very welcome.&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=1900" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/TableDifference/default.aspx">TableDifference</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx">SSIS</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/Components/default.aspx">Components</category></item><item><title>Mantaining order in SSIS flow, problems with Merge</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/2007/04/10/mantaining-order-in-ssis-flow-problems-with-merge.aspx</link><pubDate>Tue, 10 Apr 2007 06:48:26 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:1896</guid><dc:creator>AlbertoFerrari</dc:creator><slash:comments>2</slash:comments><comments>http://sqlblog.com/blogs/alberto_ferrari/comments/1896.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/alberto_ferrari/commentrss.aspx?PostID=1896</wfw:commentRss><description>&lt;p&gt;In a &lt;a href="http://sqlblog.com/blogs/alberto_ferrari/archive/2007/04/08/making-fast-load-really-fast-on-clustered-indexed-tables-with-ssis.aspx"&gt;previous post&lt;/a&gt; I spoke about the advantages of having sorted flows in SSIS to greatly speed up data insertion using fastload. The need to have a sorted flow brings some severe problems to the SSIS programmer that he need to be aware of and that IMHO Microsoft should address with a future implementation of SQL Server.  &lt;p&gt;Let’s have a look at the problem. &lt;p&gt;In the image you can see a very typical SSIS data flow where you need to manage the error flow of a lookup component and go on with the processing. Even if you can’t see it from the picture, think that Sample Source will produce a sorted output of several millions rows and we want to insert into TestTable with the same sorting.  &lt;p&gt;&lt;a href="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/MantainingorderinSSISflowproblemswithMer_9222/Merge%20using%20Union%20All.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="325" alt="Merge using Union All" src="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/MantainingorderinSSISflowproblemswithMer_9222/Merge%20using%20Union%20All_thumb.png" width="364" border="0"&gt;&lt;/a&gt;  &lt;p&gt;This task works fine but it has a big problem: the Union All component will lose the sorting of the flow as it will handle data from both its input in an unordered way. This is not a bug, Union All has its behaviour by design. Still we have a problem and we know that sorting several millions rows after the Union All component is not an option for memory consumption. &lt;p&gt;In SSIS you have another component, Merge, that will kindly maintain the order of its inputs, so you can change your package this way: &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;a href="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/MantainingorderinSSISflowproblemswithMer_9222/Merge%20using%20Merge.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="320" alt="Merge using Merge" src="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/MantainingorderinSSISflowproblemswithMer_9222/Merge%20using%20Merge_thumb.png" width="353" border="0"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;Everything will work fine until you have a package that should handle several millions rows where only a very few of them (say 1.000) will enter the lookup error output flow. &lt;p&gt;In such a situation Merge will start caching ALL the rows from its first input (the row that correctly matched lookup) until something will come from its second input (rows coming from the Derived Column task). The problem is that SSIS will NOT call the ProcessInput method of the Derived Column task until its buffer reaches a certain amount of rows (normally 10.000 or a number like it) and this will not happen because only 1.000 rows will enter the Derived Column path. In such a situation Merge will start consuming memory and will fill up all the available memory really fast leading to crashes and/or very poor performances. &lt;p&gt;Even this behavior of both Merge and SSIS is “by design”, so we cannot complain it. But it could be easily solved setting the max number of rows of the buffer in the Derived column to 1 in order to call ProcessInput immediately (if I know what kind of data I will read I can easily set up values that make my SSIS package run faster). Unfortunately there is no such kind of option in SSIS so, at present, the problem cannot be solved. &lt;p&gt;Needless to say, even if this is a problem, you can try to make your sorted package run without any merge component and you will be able to maintain the sort of the flow but this is a really big limitation in expressivity of your ETL algorithm so awareness of the problem is mandatory to successfully complete your ETL process.&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=1896" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx">SSIS</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SQL/default.aspx">SQL</category></item><item><title>Making Fast Load really fast on clustered indexed tables with SSIS</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/2007/04/08/making-fast-load-really-fast-on-clustered-indexed-tables-with-ssis.aspx</link><pubDate>Sun, 08 Apr 2007 06:48:26 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:1895</guid><dc:creator>AlbertoFerrari</dc:creator><slash:comments>21</slash:comments><comments>http://sqlblog.com/blogs/alberto_ferrari/comments/1895.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/alberto_ferrari/commentrss.aspx?PostID=1895</wfw:commentRss><description>&lt;p&gt;I have been so used to use int identity fields as primary key for any table and to believe it’s the fastest way to define a clustered primary key that I never thought this can cause serious performance problems to SSIS. Until I tested it and discovered that &lt;b&gt;int identity primary keys are among the slowest way to insert huge amount of data with SSIS&lt;/b&gt;. In the post I’ll describe the technique that – from my tests – is the fastest way to insert data into tables with clustered index using SSIS. &lt;p&gt;As a test case I used a very simple table with only two fields: &lt;p&gt;&lt;pre&gt;&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;CREATE&lt;/span&gt;&lt;span style="color:#000000;"&gt; &lt;/span&gt;&lt;span style="color:#0000ff;"&gt;TABLE&lt;/span&gt;&lt;span style="color:#000000;"&gt; [dbo]&lt;/span&gt;&lt;span style="color:#808080;"&gt;.&lt;/span&gt;&lt;span style="color:#000000;"&gt;[FastLoadTests]&lt;/span&gt;&lt;span style="color:#808080;"&gt;(
&lt;/span&gt;&lt;span style="color:#000000;"&gt;      [Id] [int] &lt;/span&gt;&lt;span style="color:#0000ff;"&gt;Identity&lt;/span&gt;&lt;span style="color:#000000;"&gt; &lt;/span&gt;&lt;span style="color:#808080;"&gt;NOT&lt;/span&gt;&lt;span style="color:#000000;"&gt; &lt;/span&gt;&lt;span style="color:#808080;"&gt;NULL,
&lt;/span&gt;&lt;span style="color:#000000;"&gt;      [TestString] [varchar]&lt;/span&gt;&lt;span style="color:#808080;"&gt;(&lt;/span&gt;&lt;span style="color:#000000;"&gt;2048&lt;/span&gt;&lt;span style="color:#808080;"&gt;)&lt;/span&gt;&lt;span style="color:#000000;"&gt; &lt;/span&gt;&lt;span style="color:#808080;"&gt;NOT&lt;/span&gt;&lt;span style="color:#000000;"&gt; &lt;/span&gt;&lt;span style="color:#808080;"&gt;NULL
)&lt;/span&gt;&lt;span style="color:#000000;"&gt; &lt;/span&gt;&lt;span style="color:#0000ff;"&gt;ON&lt;/span&gt;&lt;span style="color:#000000;"&gt; [PRIMARY]
&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;I filled it with a very simple task that generates one million of monotonically increasing Id and TestStrings of exactly 512 bytes each, throwing them into an OleDb destination adapter that uses FastLoad to fill the table.
&lt;p&gt;If the table has no index at all the package runs in 18.5 seconds, pretty nice. Now, I created an index on the table like this:
&lt;p&gt;CREATE UNIQUE CLUSTERED INDEX [FastLoadTests_ClusteredIndex] ON [dbo].[FastLoadTests] ([Id] ASC) ON [PRIMARY]
&lt;p&gt;And run the package once more. The execution time is now 1.08.5 (one minute and eight seconds). It is &lt;b&gt;more or less 4 times slower&lt;/b&gt;. Clearly, as SQL has to sort one million rows, I was expecting poor performances. The first trial has been that of reducing the number of rows to sort.
&lt;p&gt;Looking at the various options in the OleDb destination adapter it is easy to find that it has a parameter (Maximun Insert Commit Size) that defaults to 0. 
&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/MakingFastLoadreallyfastonclusteredindex_912C/OleDB-MICS.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="639" alt="OleDB-MICS" src="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/MakingFastLoadreallyfastonclusteredindex_912C/OleDB-MICS_thumb.png" width="650" border="0"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Setting it to 10.000 reduced execution time to 44.5 seconds, a better result but still more than two times slower than the table with no clustered index. Is this the best result? Not at all! Let’s try something different.
&lt;p&gt;Reading (carefully) the MS documentation about the OleDB destination adapter (&lt;a href="http://msdn2.microsoft.com/en-us/library/ms141237.aspx"&gt;http://msdn2.microsoft.com/en-us/library/ms141237.aspx&lt;/a&gt;) you’ll discover that you can gain performance if the input data is sorted accordingly to the clustered index on the table, specifying the ORDER option with the advanced editor.
&lt;p&gt;After some trials I managed to set it with the advanced editor:
&lt;p&gt;&lt;a href="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/MakingFastLoadreallyfastonclusteredindex_912C/OleDB-ORDER.png"&gt;&lt;img style="border-right:0px;border-top:0px;border-left:0px;border-bottom:0px;" height="639" alt="OleDB-ORDER" src="http://sqlblog.com/blogs/alberto_ferrari/WindowsLiveWriter/MakingFastLoadreallyfastonclusteredindex_912C/OleDB-ORDER_thumb.png" width="624" border="0"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It has been surprising that, running the package, I got this error:
&lt;p&gt;[Test Table [44]] Error: SSIS Error Code DTS_E_OLEDBERROR.&amp;nbsp; An OLE DB error has occurred. Error code: 0x80004005. An OLE DB record is available.&amp;nbsp; Source: "Microsoft SQL Native Client" Hresult: 0x80004005 Description: "Could not bulk load. The sorted column 'Id' is not valid. The ORDER hint is ignored.".
&lt;p&gt;So, it seems that setting this option on an INT IDENTITTY field causes FastLoad to fail. Really surprising because it seems that SQL Server does not know that the keys generated by itself will be sorted! Anyway, removing the Identity setting from the table and using the package generated ID resulted in execution time of 46.23 seconds. &lt;b&gt;No performance gain at all. &lt;/b&gt;Anyway as it seemed an interesting way to search into, I went more on trials.
&lt;p&gt;The last and resolving trial test has been that of REMOVING the Maximum Insert Commit Size parameter from the destination adapter. Running the package again I got a result of 19.07 seconds execution time, more or less the same time as inserting the data with no clustered index at all. Got it! Now I have a really fast way to insert data.
&lt;p&gt;The results are summarized in this table&lt;/p&gt;
&lt;table cellspacing="0" cellpadding="2"&gt;

&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;No Index&lt;br&gt;0 MICS&lt;/td&gt;
&lt;td&gt;Index&lt;br&gt;10.000 MICS&lt;/td&gt;
&lt;td&gt;Index&lt;br&gt;0 MICS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Int Identity key&lt;/td&gt;
&lt;td&gt;18.85&lt;/td&gt;
&lt;td&gt;41.15&lt;/td&gt;
&lt;td&gt;1.08.57&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSIS generated key&lt;/td&gt;
&lt;td&gt;18.59&lt;/td&gt;
&lt;td&gt;44.54&lt;/td&gt;
&lt;td&gt;1.10.03&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSIS key and ORDER option&lt;/td&gt;
&lt;td&gt;18.85&lt;/td&gt;
&lt;td&gt;46.23&lt;/td&gt;
&lt;td&gt;&lt;b&gt;19.07&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;p&gt;&lt;i&gt;&lt;/i&gt;
&lt;p&gt;&lt;i&gt;*MICS = Maximum Insert Commit Size&lt;/i&gt;
&lt;p&gt;You can easily see that using the ORDER option gives a dramatic speed improvement when you have a clustered index but ONLY if you are able to generate the sorting key in the SSIS package and DO NOT USE the MICS parameter.
&lt;p&gt;I think that the documentation for this behavior should have been made far more accessible and that ORDER parameter is so important that should have been shown in the first page of the standard editor for OleDb destination, hope it will be done in the next release of Sql Server.
&lt;p&gt;Moreover, as using MICS parameter leads to a dramatic performance degradation, I think that &lt;b&gt;the OleDb destination adapter should issue a warning if both ORDER and MICS parameter are set &lt;/b&gt;so to make the programmer think twice before using them together.
&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=1895" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SQL+Server/default.aspx">SQL Server</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx">SSIS</category></item><item><title>Data Flow Components, constructors and SSIS</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/2006/07/31/data-flow-components-constructors-and-ssis.aspx</link><pubDate>Mon, 31 Jul 2006 06:48:26 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:1892</guid><dc:creator>AlbertoFerrari</dc:creator><slash:comments>0</slash:comments><comments>http://sqlblog.com/blogs/alberto_ferrari/comments/1892.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/alberto_ferrari/commentrss.aspx?PostID=1892</wfw:commentRss><description>&lt;p&gt;Yesterday I had to fix a simple still subtle bug in &lt;a href="http://www.sqlbi.eu/Home/tabid/36/ctl/Details/mid/374/ItemID/0/Default.aspx"&gt;TableDifference&lt;/a&gt;, I think sharing the experience will be useful for everybody involved in custom component creation. &lt;p&gt;The component has some fields that are initialized in the component constructor, at the end of the work the component does not clear them as I would expect the object to be destroyed by the SSIS engine. Everything works fine until&amp;nbsp;you run the component in a data flow task contained in a foreach loop then... bang! the component crash. &lt;p&gt;The problem is that the component was not fresh-built but contained properties with the same data that were there&amp;nbsp;at the end of&amp;nbsp;its first execution. It seems to me that SSIS does not destroy components after a data flow terminates but reuses them during the subsequent runs of the same data flow task recalling their pre-execute method. I solved the problem clearing variable values in the pre-execute method, the problem is solved but I don't like the way the SSIS engine works, I would really like an object to be destroyed and then recreated when the container data flow task finishes execution. &lt;p&gt;If confirmed (&lt;em&gt;is anybody from&amp;nbsp;Microsoft listening?&lt;/em&gt;) this behaviour is - in my opinion - very interesting but &lt;strong&gt;wrong&lt;/strong&gt;. You can decide to use this behaviour to implement some sort of state management in a component but in the same time you are prone to very nasty bugs if you do not clear all your properties in the pre-execute method and then initialize them to a meaningful value. &lt;p&gt;I did not found useful documentation about this behaviour, has anybody done any kind of investigation about it?&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=1892" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx">SSIS</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/Components/default.aspx">Components</category></item><item><title>Threads and custom components: FlowSync 1.0</title><link>http://sqlblog.com/blogs/alberto_ferrari/archive/2006/07/06/threads-and-custom-components-flowsync-1-0.aspx</link><pubDate>Thu, 06 Jul 2006 06:48:26 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:1889</guid><dc:creator>AlbertoFerrari</dc:creator><slash:comments>0</slash:comments><comments>http://sqlblog.com/blogs/alberto_ferrari/comments/1889.aspx</comments><wfw:commentRss>http://sqlblog.com/blogs/alberto_ferrari/commentrss.aspx?PostID=1889</wfw:commentRss><description>&lt;p&gt;Several people downloaded &lt;a href="http://www.sqlbi.eu/Home/tabid/36/ctl/Details/mid/374/ItemID/0/Default.aspx"&gt;TableDifference&lt;/a&gt;&amp;nbsp;to handle SCD faster, some of them, especially using it on huge table (more than 10 millions rows) noticed memory problems. The problem is that of a flow running too fast and making TableDifference cache data, we know of it and now we decided to solve it creating a new component called "FlowSync". You can find all the details and source code&amp;nbsp;&lt;a href="http://www.sqlbi.eu/Home/tabid/36/ctl/Details/mid/374/ItemID/1/Default.aspx"&gt;here&lt;/a&gt;. &lt;p&gt;In the article there is a brief discussion about how SSIS handles the ProcessInput method of a component with more than one input, here is an extract: &lt;p&gt;As you may already know ProcessInput is called once for every buffer and, in the case of a component with two or more inputs like TableDifference or Union All, this method is called once for each buffer of each input, so the inputs are mixed together and handled by the same method.&amp;nbsp;A solution to the problem of syncronizing input,&amp;nbsp;before deciding to develop FlowSync, has been that of using semaphores to stop the faster input inside the ProcessInput method. It would have been a nicer solution BUT ProcessInput is called in only ONE thread, even if it has two input flows. So, if ProcessInput is stopped then all the inputs of the components are stopped and the system will be in a deadlock state.  &lt;p&gt;This is very strange because each flow runs in a separate thread but it seems that the two thread synchronize on a single one when they need to pass data to the component. So the solution has been that of inserting the sync technique where we still have separate threads, hence directly on the flows with a transformation component: FlowSync. &lt;p&gt;I would really like to see in the next version of SSIS the ability to decide if – when developing a component – we want ProcessInput to be called in a multithreaded environment or not, my personal opinion is that – using threads – programs become easier to write and maintain, TableDifference may be a good candidate to demonstrate this statement.&lt;/p&gt;&lt;img src="http://sqlblog.com/aggbug.aspx?PostID=1889" width="1" height="1"&gt;</description><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/SSIS/default.aspx">SSIS</category><category domain="http://sqlblog.com/blogs/alberto_ferrari/archive/tags/Components/default.aspx">Components</category></item></channel></rss>