Two-phase commit sample program

An sample program illustrating the two-phase commit service is included with DB-Library’s sample programs. This same example is duplicated below, but with comments added to document how recovery occurs for the different types of failure that may occur at various points in the transaction.

 /*     
 **    twophase.c 
 ** 
 **    Demo of Two-Phase Commit Service 
 ** 
 **    This example uses the two-phase commit service 
 **    to perform a simultaneous update on two servers. 
 **    In this example, one of the servers participating  
 **    in the distributed transaction also functions as  
 **    the commit service. 
 ** 
 **    In this particular example, the same update is  
 **    performed on both servers. You can, however, use  
 **    the commit server to perform completely different  
 **    updates on each server. 
 ** 
 */ 
 
 #include <stdio.h> 
 #include <sybfront.h> 
 #include <sybdb.h> 
 #include "sybdbex.h" 
 
 int err_handler(); 
 int msg_handler(); 
 
 char     cmdbuf[256]; 
 char     xact_string[128]; 
 
 main() 
 { 
 
 DBPROCESS     *dbproc_server1;  
 DBPROCESS     *dbproc_server2; 
 DBPROCESS     *dbproc_commit; 
 LOGINREC      *login; 
 int           commid; 
 
 RETCODE       ret_server1; 
 RETCODE       ret_server2; 
 
 /* Initialize DB-Library. */ 
 if (dbinit() == FAIL) 
 exit(ERREXIT); 

  
      dberrhandle(err_handler); 
      dbmsghandle(msg_handler); 
  
      printf("Demo of Two Phase Commit\n"); 
  
      /* Open connections with the servers and the 
      ** commit service. */ 
      login = dblogin(); 
      DBSETLPWD(login, "server_password"); 
      DBSETLAPP(login, "twophase"); 
  
      dbproc_server1 = dbopen (login, "SERVICE"); 
      dbproc_server2 = dbopen (login, "PRACTICE"); 
      dbproc_commit = open_commit (login, "SERVICE"); 
  
      if (dbproc_server1 == NULL ||  
          dbproc_server2 == NULL ||  
          dbproc_commit  == NULL) 
      { 
           printf (" Connections failed!\n"); 
           exit (ERREXIT); 
      } 
  
      /* Use the "pubs2" database. */ 
      sprintf(cmdbuf, "use pubs2"); 
      dbcmd(dbproc_server1, cmdbuf); 
      dbsqlexec(dbproc_server1); 
      dbcmd(dbproc_server2, cmdbuf); 
      dbsqlexec(dbproc_server2); 
  
      /* 
      ** Start the distributed transaction on the
      ** commit service. 
      */ 
      commid = start_xact(dbproc_commit, "demo", "test", 2); 

NoteThe application is now in the begin phase of the two-phase commit transaction.

     /* Build the transaction name. */ 
      build_xact_string ("test", "SERVICE", commid, xact_string); 
  
      /* Build the first command buffer. */ 
      sprintf(cmdbuf, "begin transaction %s", xact_string); 
  
      /* Begin the transactions on the different servers. */ 
      dbcmd(dbproc_server1, cmdbuf); 
      dbsqlexec(dbproc_server1); 
      dbcmd(dbproc_server2, cmdbuf); 
      dbsqlexec(dbproc_server2); 
  
      /* Do various updates. */ 
      sprintf(cmdbuf, " update titles set price = $1.50 where"); 
      strcat(cmdbuf, " title_id = ’BU1032’"); 
      dbcmd(dbproc_server1, cmdbuf); 
      ret_server1 = dbsqlexec(dbproc_server1); 
      dbcmd(dbproc_server2, cmdbuf); 
      ret_server2 = dbsqlexec(dbproc_server2); 

NoteSee “Program note 1”.

     if (ret_server1 == FAIL || ret_server2 == FAIL) 
      { 
           /* Some part of the transaction failed. */ 
           printf(" Transaction aborted -- dbsqlexec failed\n"); 
           abortall(dbproc_server1, dbproc_server2,
                    dbproc_commit, commid); 
      } 
  
      /* Find out if all servers can commit the transaction. */ 
      sprintf(cmdbuf, "prepare transaction"); 
      dbcmd(dbproc_server1, cmdbuf); 
      dbcmd(dbproc_server2, cmdbuf); 
      ret_server1 = dbsqlexec(dbproc_server1); 

NoteSee “Program note 2”.

     ret_server2 = dbsqlexec(dbproc_server2); 

NoteSee “Program note 3”.

if (ret_server1 == FAIL || ret_server2 == FAIL) 
      { 
           /* One or both of the servers failed to prepare. */ 
           printf(" Transaction aborted -- prepare failed\n"); 
           abortall(dbproc_server1, dbproc_server2,
                    dbproc_commit, commid); 
      } 

NoteSee “Program note 4”.

     /* Commit the transaction. */ 
      if (commit_xact(dbproc_commit, commid) == FAIL) 
      { 
           /* The commit server failed to record the commit. */ 
           printf( " Transaction aborted -- commit_xact failed\n"); 
           abortall(dbproc_server1, dbproc_server2,
                    dbproc_commit, commid); 
           exit(ERREXIT); 
      } 

NoteSee “Program note 5”.

     /* The transaction has successfully committed.  
      ** Inform the servers. 
      */ 
      sprintf(cmdbuf, "commit transaction"); 
      dbcmd(dbproc_server1, cmdbuf); 
      if (dbsqlexec(dbproc_server1) != FAIL) 
           remove_xact(dbproc_commit, commid, 1); 

NoteSee “Program note 6”.

     dbcmd(dbproc_server2, cmdbuf); 
      if (dbsqlexec(dbproc_server2) != FAIL) 
      remove_xact(dbproc_commit, commid, 1); 

NoteSee “Program note 7”.

     /* Close the connection to the commit server. */ 
      close_commit(dbproc_commit); 

NoteSee “Program note 8”.

     printf( "We made it!\n"); 
      dbexit(); 
      exit(STDEXIT); 
 } 
  
 /* Function to abort the distributed transaction. */ 
  
 abortall( dbproc_server1, dbproc_server2, dbproc_commit, commid ) 
 DBPROCESS      *dbproc_server1;  
 DBPROCESS      *dbproc_server2; 
 DBPROCESS      *dbproc_commit; 
 int            commid; 
 { 
      /* Some part of the transaction failed. */ 
  
      /* Inform the commit server of the failure. */ 
      abort_xact(dbproc_commit, commid); 
  
      /* Roll back the transactions on the different servers. */ 
      sprintf(cmdbuf, "rollback transaction"); 
      dbcmd(dbproc_server1, cmdbuf); 
      if (dbsqlexec(dbproc_server1) != FAIL) 
           remove_xact(dbproc_commit, commid, 1); 
      dbcmd(dbproc_server2, cmdbuf); 
      if (dbsqlexec(dbproc_server2) != FAIL) 
           remove_xact(dbproc_commit, commid, 1); 
  
      dbexit(); 
      exit(ERREXIT); 
 } 
  
 /* Message and error handling functions. */ 
  
 int msg_handler(dbproc, msgno, msgstate, severity, msgtext,  
                 servername, procname, line) 
  
 DBPROCESS       *dbproc; 
 DBINT           msgno; 
 int             msgstate; 
 int             severity; 
 char            *msgtext; 
 char            *servername; 
 char            *procname; 
 DBUSMALLINT     line; 
  
 { 
      /*   Msg 5701 is just a use database message, so skip it. */ 
      if (msgno == 5701) 
           return (0); 
  
      /* Print any severity 0 message as is, without extra stuff. */
      if (severity == 0) 
      { 
           (void) fprintf (ERR_CH, "%s\n",msgtext); 
           return (0); 
      } 
  
      (void) fprintf (ERR_CH, "Msg %ld, Level %d, State %d\n",  
              msgno, severity, msgstate); 
  
      if (strlen(servername) > 0) 
           (void) fprintf (ERR_CH, "Server ’%s’, ", servername); 
      if (strlen(procname) > 0) 
           (void) fprintf (ERR_CH, "Procedure ’%s’, ", procname); 
      if (line > 0) 
           (void) fprintf (ERR_CH, "Line %d", line); 
  
      (void) fprintf (ERR_CH, "\n\t%s\n", msgtext); 
  
      if (severity >= 16) 
      { 
           (void) fprintf (ERR_CH, "Program Terminated! Fatal\
                           Adaptive Server error.\n"); 
           exit(ERREXIT); 
      } 
  
      return (0);    
 } 
  
 int err_handler(dbproc, severity, dberr, oserr, dberrstr, oserrstr) 
 DBPROCESS    *dbproc; 
 int          severity; 
 int          dberr; 
 int          oserr; 
 char         *dberrstr; 
 char         *oserrstr; 
 { 
      if ((dbproc == NULL) || (DBDEAD(dbproc))) 
           return (INT_EXIT); 
      else 
      { 
           (void) fprintf (ERR_CH, "DB-Library error: \
                           \n\t %s\n", dberrstr); 
           if (oserr != DBNOERR) 
                (void) fprintf (ERR_CH, "Operating system error:\ 
                                \n\t%s\n", oserrstr); 
      } 
  
      return (INT_CANCEL); 
 }