Db2

Db2

Connect with Db2, Informix, Netezza, open source, and other data experts to gain value from your data, share insights, and solve problems.

 View Only
  • 1.  2 Phase Commint from .Net code?

    Posted Mon January 29, 2024 01:54 PM

    I wonder whether anybody tried (and was successful!) in performing 2 phase commit transactions (coordinated by Db2 LUW) with 2 (or more) participating databases using .Net code (or C#). Any code samples would be greatly appreciated.

    I have experience with 2PC and I am able to code 2PC coordinated transactions using:
    - CLP
    - CLI
    - embedded C

    Unfortunately I have no experience with .NET.



    ------------------------------
    Jan Nelken
    ------------------------------


  • 2.  RE: 2 Phase Commint from .Net code?

    Posted Tue January 30, 2024 09:27 AM
    Edited by Bryan Hamby Tue January 30, 2024 09:29 AM

    With .NET, you use the TransactionScope class to handle 2-phase commit.

    .NET uses Microsoft Distributed Transaction Coordinator (MSDTC) to coordinate the transactions across databases. DB2 uses XA transactions for distributed transactions, and there is some configuration you have to do on the machine running the code for MSDTC. Search "Component Services" to load the Control Panel, then go Component Services -> Computers -> My Computer -> Distributed Transaction Coordinator -> Local DTC. Right-click on it and hit properties, then go to the Security tab and check "Enable XA Transactions".

    Some of the gotchas:

    • The connection must be opened inside of the TransactionScope, that is how .NET is aware to enroll it in the distributed transaction
    • `scope.Complete()`does not commit the code in the block, it simply says that block was successful. The commit happens when the last "outer" scope block is disposed of. If there are child scopes inside that scope, then all scopes must have been marked as complete and disposed for it to commit.
      • You should pretty much use a using statement for all TransactionScopes, it just makes the code easier to ensure it's doing the correct thing.

    Here's a short example of some code using TransactionScope to bulk-load a table. (I'd post it as a code block, but the community page errors out if I do that...)

    -------------------------------------------------------------

    public void DoDatabaseWork()
    {
        using (var scope = GetTransactionScope())
        using (var conn = GetConnection("dbName"))
        {
            var curTstamp = DateTime.Now;
            var tableName = "DBA.TEST_TABLE";
        
            using (var adapter = new DB2DataAdapter($"SELECT * FROM {tableName}", conn))
            using (var table = new DataTable())
            {
                adapter.FillSchema(table, SchemaType.Source);
                for (int i = 0; i < 1000; i++)
                {
                    var row = table.NewRow();
                    row["COL1"] = "ABC";
                    row["COL2"] = "123";
                    row["TS_COL"] = curTstamp;
                    row["NULL_COL"] = DBNull.Value;
                    table.Rows.Add(row);
                }
                
                using (var bc = new DB2BulkCopy(conn))
                {
                    bc.DestinationTableName = tableName;
                    bc.WriteToServer(table);
                }
            }

            scope.Complete();
        } // using scope/conn
    }

    -------------------------------------------------------------


    I also recommend having a function that creates the scope and sets some options. The defaults are pretty bad for concurrency. It uses Serializable by default, which is equivalent to DB2 WITH RR (Repeatable Read).

    -------------------------------------------------------------

    public static TransactionScope GetTransactionScope(
        TransactionScopeOption scopeOption = TransactionScopeOption.Required,
        IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
    {
        return new TransactionScope(scopeOption, new TransactionOptions
        {
            Timeout = TransactionManager.MaximumTimeout,
            IsolationLevel = isolationLevel,
        });
    }

    -------------------------------------------------------------

    TransactionScope is kinda magic (in the good and bad senses), but hopefully this will get you on the right path!



  • 3.  RE: 2 Phase Commint from .Net code?

    Posted Tue January 30, 2024 10:47 AM
    Edited by Jan Nelken Tue January 30, 2024 10:53 AM

    Thank you for the answer. 

    I am still confused (probably because I believe that .net is for spiders only)...

    What I am trying to do is something called multisite update - something like this:

    cid:<a href=image001.gif@01DA52E3.92FB0870" class="img-responsive" data-mce-hlimagekey="a02c4b7c-dcc3-bf64-6563-55085baa31ee" data-mce-hlselector="#ReplyInline_0b752e6a469147a98d4c7f51937eaf09-tinyMce">

    This I coded in CLP - was trivial and straightforward - I don't understand how can I set connection to each of the participating database in single UOW in .net -

    Fragment of my CLP proof of concept:

     

    C:\Working\D2>db2 connect to sample_a

       

    Database Connection Information

     Database server        = DB2/NT64 11.5.9.0

     SQL authorization ID   = DB2ADMIN

     Local database alias   = SAMPLE_A

    C:\Working\D2>db2 connect to sample_b

       Database Connection Information

     Database server        = DB2/NT64 11.5.9.0

     SQL authorization ID   = DB2ADMIN

     Local database alias   = SAMPLE_B

    C:\Working\D2>db2 set connection sample_a

       Database Connection Information

     Database server        = DB2/NT64 11.5.9.0

     SQL authorization ID   = DB2ADMIN

     Local database alias   = SAMPLE_A

    C:\Working\D2>db2 -c- insert into org values (99,'Alaska', 999,'Northern','Anchorage')

    DB20000I  The SQL command completed successfully.

    C:\Working\D2>db2 -c- set connection sample_b

       Database Connection Information

     Database server        = DB2/NT64 11.5.9.0

     SQL authorization ID   = DB2ADMIN

     Local database alias   = SAMPLE_B



    C:\Working\D2>db2 -c- insert into staff values (999,'Stolarek', 99,'Mgr',9, 66666.66,6.66)

    DB20000I  The SQL command completed successfully.

    .........

    C:\Working\D2>db2 commit

    DB20000I  The SQL command completed successfully.

    C:\Working\D2>db2 disconnect sample_a

    DB20000I  The SQL DISCONNECT command completed successfully.

    C:\Working\D2>db2 disconnect sample_b

    DB20000I  The SQL DISCONNECT command completed successfully.

    C:\Working\D2>db2 terminate

    DB20000I  The TERMINATE command completed successfully.

     

     



    ------------------------------
    Jan Nelken
    ------------------------------



  • 4.  RE: 2 Phase Commint from .Net code?

    Posted Tue January 30, 2024 12:19 PM
    Edited by Bryan Hamby Tue January 30, 2024 12:28 PM

    Yes, TransactionScope is the way you do multi-database updates in .NET and have the commit/rollback as a unit (2-phase commit). 

    Basically, you just create the TransactionScope block, open a connection to the databases involved (one connection object per database) and perform the work needed, then call scope.Complete() at the end.

    .NET coordinates the commits across all participating databases for you.



    ------------------------------
    Bryan Hamby
    ------------------------------



  • 5.  RE: 2 Phase Commint from .Net code?

    Posted Tue January 30, 2024 03:58 PM

    Just a quick question: if I want to have all my multisite updates rolled back - do I call:

    scope.Dispose()

    and all updates will be rolled back?



    ------------------------------
    Jan Nelken
    ------------------------------



  • 6.  RE: 2 Phase Commint from .Net code?

    Posted Tue January 30, 2024 04:07 PM
    Edited by Bryan Hamby Tue January 30, 2024 04:07 PM

    If you don't scope.Complete() on ALL of the TransactionScope blocks by the time you scope.Dispose() the outermost scope, it will rollback. Usually the easiest way to do that would just be to throw an exception inside your block.



    ------------------------------
    Bryan Hamby
    ------------------------------