Source File modules/ud/ud_recover.c

    1    | /***************************************
    2    |   $Revision: 1.2 $
    3    |
    4    |   Functions to keep records for crash recovery
    5    |
    6    |   Status: NOT REVUED, NOT TESTED
    7    |
    8    |   Author(s):       Andrei Robachevsky
    9    |
    10   |   ******************/ /******************
    11   |   Modification History:
    12   |         andrei (11/08/2000) Created.
    13   |   ******************/ /******************
    14   |   Copyright (c) 2000                              RIPE NCC
    15   |
    16   |   All Rights Reserved
    17   |
    18   |   Permission to use, copy, modify, and distribute this software and its
    19   |   documentation for any purpose and without fee is hereby granted,
    20   |   provided that the above copyright notice appear in all copies and that
    21   |   both that copyright notice and this permission notice appear in
    22   |   supporting documentation, and that the name of the author not be
    23   |   used in advertising or publicity pertaining to distribution of the
    24   |   software without specific, written prior permission.
    25   |
    26   |   THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
    27   |   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
    28   |   AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
    29   |   DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
    30   |   AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    31   |   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    32   |  ***************************************/
    33   |
    34   | #include "ud_tr.h"
    35   | #include "ud.h"
    36   |
    37   | /*************************************************************
    38   |
    39   | SQL Tables used to keep records needed for crash recovery
    40   |
    41   | CREATE TABLE transaction_rec (
    42   | 0  transaction_id int(11) DEFAULT '0' NOT NULL auto_increment,
    43   | 1  object_id int(10) unsigned DEFAULT '0' NOT NULL,
    44   | 2  sequence_id int(10) unsigned DEFAULT '1' NOT NULL,
    45   | 3  object_type tinyint(3) unsigned DEFAULT '0' NOT NULL,
    46   | 4  save varchar(256) DEFAULT '' NOT NULL,
    47   | 5  error_script blob DEFAULT '' NOT NULL,
    48   | 6  mode tinyint(4) unsigned DEFAULT '0' NOT NULL,
    49   | 7  succeeded tinyint(4) unsigned DEFAULT '0' NOT NULL,
    50   | 8  action tinyint(4) unsigned DEFAULT '0' NOT NULL,
    51   | 9  status tinyint(10) unsigned DEFAULT '0' NOT NULL,
    52   | 10  clean tinyint(3) DEFAULT '0' NOT NULL,
    53   |   PRIMARY KEY (transaction_id)
    54   | );
    55   |
    56   |
    57   |
    58   | CREATE TABLE dummy_rec (
    59   |   transaction_id int(11) DEFAULT '0' NOT NULL,
    60   |   object_id int(10) unsigned DEFAULT '0' NOT NULL,
    61   |   PRIMARY KEY (transaction_id, object_id)
    62   | );
    63   |
    64   | *************************************************************/
    65   |
    66   | /************************************************************
    67   | * int TR_create_record()                                    *
    68   | *                                                           *
    69   | * Create TR record                                          *
    70   | *                                                           *
    71   | * First tries to delete record with the same transaction_id *
    72   | * ( transaction_id == tr->transaction_id )                  *
    73   | * Then creates a new record in transaction_rec table        *
    74   | *                                                           *
    75   | * Returns: transaction_id                                   *
    76   | *                                                           *
    77   | ************************************************************/
    78   |
    79   | long TR_create_record(Transaction_t *tr)
    80   | {
    81   | SQ_result_set_t *sql_result;
    82   | GString *query;
    83   | int sql_err;
    84   |
    85   |  if(tr->load_pass != 0) return(0); /* for fast loader just return */
    86   |
    87   |  if ((query = g_string_sized_new(STR_L)) == NULL){
    88   |   ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    89   |   die;
    90   |  }
    91   |  /* delete record if exists*/
    92   |
    93   |  TR_delete_record(tr);
    94   |
    95   |
    96   |  /* compose record */
    97   |
    98   |  tr->action = TR_ACTION(tr->action) + TCP_ROLLBACK;
    99   |
    100  |  g_string_sprintf(query, "INSERT transaction_rec "
    101  |                          "SET transaction_id=%ld, "
    102  | 			 "object_id=%ld, "
    103  | 			 "sequence_id=%ld, "
    104  | 			 "object_type=%d, "
    105  | 			 "mode=%d, "
    106  | 			 "action=%d, "
    107  | 			 "status=%d ",
    108  | 			 tr->transaction_id, tr->object_id, tr->sequence_id, tr->class_type, tr->mode, TR_ACTION(tr->action), TR_STATUS(TCP_ROLLBACK));
    109  |  sql_err=SQ_execute_query(tr->sql_connection, query->str, &sql_result);
    110  |
    111  |
    112  |  /* in case of an error copy error code and return */
    113  |  if(sql_err) {
    114  |    ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    115  |    die;
    116  |  }
    117  |  g_string_free(query, TRUE);
    118  |  return(tr->transaction_id);
    119  | }
    120  |
    121  |
    122  | /************************************************************
    123  | * int TR_update_record()                                    *
    124  | *                                                           *
    125  | * UPdates TR record (transaction_rec or dummy_rec tables)   *
    126  | *                                                           *
    127  | * Updates the following fields:                             *
    128  | * TF_DUMMY - dummy_rec, adding ID's as dummies are created  *
    129  | * TF_SAVE  - writes down tr->save                           *
    130  | * TF_STATUS - updates status (checkpointing)                *
    131  | * TF_ESCRIPT - saves error script tr->error_script          *
    132  | *                                                           *
    133  | * Returns: transaction_id                                   *
    134  | *                                                           *
    135  | ************************************************************/
    136  |
    137  | long TR_update_record(Transaction_t *tr, int field)
    138  | {
    139  | SQ_result_set_t *sql_result;
    140  | GString *query;
    141  | int sql_err;
    142  |
    143  |  if(tr->load_pass != 0) return(0); /* for fast loader just return */
    144  |
    145  |  if ((query = g_string_sized_new(STR_L)) == NULL){
    146  |   ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    147  |   die;
    148  |  }
    149  |
    150  |  switch(field){
    151  |    case TF_DUMMY:
    152  |           g_string_sprintf(query, "INSERT dummy_rec "
    153  |                                   "SET transaction_id=%ld, "
    154  | 				  "object_id=%ld ",
    155  | 			           tr->transaction_id, tr->dummy_id[tr->ndummy-1]);
    156  | 	  break;
    157  |
    158  |    case TF_STATUS:
    159  |           g_string_sprintf(query, "UPDATE transaction_rec "
    160  |                                   "SET status=%d "
    161  | 				  "WHERE transaction_id=%ld ",
    162  | 			           TR_STATUS(tr->action), tr->transaction_id);
    163  | 	  break;
    164  |
    165  |    case TF_SAVE:
    166  |           g_string_sprintf(query, "UPDATE transaction_rec "
    167  |                                   "SET save='%s' "
    168  | 				  "WHERE transaction_id=%ld ",
    169  | 			           tr->save, tr->transaction_id);
    170  | 	  break;
    171  |
    172  |    case TF_ESCRIPT:
    173  |           g_string_sprintf(query, "UPDATE transaction_rec "
    174  |                                   "SET error_script='%s' "
    175  | 				  "WHERE transaction_id=%ld ",
    176  | 			           (tr->error_script)->str, tr->transaction_id);
    177  | 	  break;
    178  |
    179  |    case TF_ID:
    180  |           g_string_sprintf(query, "UPDATE transaction_rec "
    181  |                                   "SET object_id=%ld, sequence_id=%ld, serial_id=%ld, succeeded=%d "
    182  | 				  "WHERE transaction_id=%ld ",
    183  | 			           tr->object_id, tr->sequence_id, tr->serial_id, tr->succeeded, tr->transaction_id);
    184  | 	  break;
    185  |
    186  |    case TF_CLEAN:
    187  |           g_string_sprintf(query, "UPDATE transaction_rec "
    188  |                                   "SET clean=1 "
    189  | 				  "WHERE transaction_id=%ld ",
    190  | 			           tr->transaction_id);
    191  | 	  break;
    192  |
    193  |   default: die; break;
    194  |  }
    195  |
    196  |  sql_err=SQ_execute_query(tr->sql_connection, query->str, &sql_result);
    197  |
    198  |
    199  |  /* in case of an error copy error code and return */
    200  |  if(sql_err) {
    201  |    ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    202  |    die;
    203  |  }
    204  |  g_string_free(query, TRUE);
    205  |  return(tr->transaction_id);
    206  | }
    207  |
    208  | /* Query the database for transaction record */
    209  | /* if there is no record with the specified ID - this is a new transaction */
    210  | /************************************************************/
    211  | SQ_result_set_t *tr_get_sql_record(SQ_connection_t *sql_connection, long transaction_id)
    212  | {
    213  | SQ_result_set_t *sql_result;
    214  | GString *query;
    215  | int sql_err;
    216  |
    217  |  if ((query = g_string_sized_new(STR_L)) == NULL){
    218  |   ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    219  |   die;
    220  |  }
    221  |
    222  |  /* compose query */
    223  |  if (transaction_id == TR_LAST)
    224  |   g_string_sprintf(query, "SELECT * FROM transaction_rec WHERE clean=%d", TCP_UNCLEAN);
    225  |  else
    226  |   g_string_sprintf(query, "SELECT * FROM transaction_rec WHERE transaction_id=%ld", transaction_id);
    227  |
    228  |  /* execute query */
    229  |  sql_err=SQ_execute_query(sql_connection, query->str, &sql_result);
    230  |
    231  |
    232  | /* in case of an error copy error code and return */
    233  |  if(sql_err) {
    234  |    ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(sql_connection), query->str);
    235  |    die;
    236  |  }
    237  |  g_string_free(query, TRUE);
    238  |  return(sql_result);
    239  | }
    240  |
    241  |
    242  | /************************************************************/
    243  | long tr_get_long(SQ_result_set_t *result, SQ_row_t *row, int col)
    244  | {
    245  |  long val;
    246  |  if( sscanf(SQ_get_column_string_nocopy(result, row, col), "%ld", &val) < 1 ) { die; }
    247  |  return(val);
    248  | }
    249  | /************************************************************/
    250  | int tr_get_int(SQ_result_set_t *result, SQ_row_t *row, int col)
    251  | {
    252  |  int val;
    253  |  if( sscanf(SQ_get_column_string_nocopy(result, row, col), "%d", &val) < 1 ) { die; }
    254  |  return(val);
    255  | }
    256  | /************************************************************/
    257  | char *tr_get_str(SQ_result_set_t *result, SQ_row_t *row, int col)
    258  | {
    259  |  return(SQ_get_column_string_nocopy(result, row, col));
    260  | }
    261  | /************************************************************/
    262  | int tr_get_dummies(Transaction_t *tr)
    263  | {
    264  | SQ_result_set_t *sql_result;
    265  | GString *query;
    266  | int sql_err;
    267  | SQ_row_t *sql_row;
    268  |
    269  |  if ((query = g_string_sized_new(STR_L)) == NULL){
    270  |   ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    271  |   die;
    272  |  }
    273  |
    274  |  /* compose query */
    275  |  g_string_sprintf(query, "SELECT * FROM dummy_rec WHERE transaction_id=%ld", tr->transaction_id);
    276  |  sql_err=SQ_execute_query(tr->sql_connection, query->str, &sql_result);
    277  |
    278  |
    279  |  /* in case of an error copy error code and return */
    280  |  if(sql_err) {
    281  |    ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    282  |    die;
    283  |  }
    284  |  g_string_free(query, TRUE);
    285  |
    286  |  tr->ndummy=0;
    287  |  while ( (sql_row = SQ_row_next(sql_result)) != NULL) {
    288  |    if( sscanf(SQ_get_column_string_nocopy(sql_result, sql_row, DUMMY_OBJECT_ID), "%ld", &(tr->dummy_id[tr->ndummy])) < 1 ) { die; }
    289  |    tr->ndummy++;
    290  |  }
    291  |
    292  |  SQ_free_result(sql_result);
    293  |  return(tr->ndummy);
    294  | }
    295  |
    296  | /************************************************************
    297  | * Transaction_t * TR_get_record()                           *
    298  | *                                                           *
    299  | * Get the record left from the failed transaction           *
    300  | * and fill the tr structure                                 *
    301  | *                                                           *
    302  | * The following fields from transaction are essential:      *
    303  | *                                                           *
    304  | * class_type                                                *
    305  | * action                                                    *
    306  | * object_id                                                 *
    307  | * sequesnce_id                                              *
    308  | * save                                                      *
    309  | * ndummy                                                    *
    310  | * dummy_id[]                                                *
    311  | * error_script                                              *
    312  | *                                                           *
    313  | * The following fields are filled in by transaction_new()   *
    314  | * thread_upd                                                *
    315  | * thread_ins                                                *
    316  | * standalone                                                *
    317  |                                                             *
    318  | * Return codes:                                             *
    319  | *                                                           *
    320  | * NULL - everything is clean, no cleanup is needed             *
    321  | * 1 - the database was recovered successfully               *
    322  | *                                                           *
    323  | ************************************************************/
    324  | Transaction_t *TR_get_record(SQ_connection_t *sql_connection, long transaction_id)
    325  | {
    326  | Transaction_t *tr;
    327  |  /* get the record from SQL table */
    328  | SQ_result_set_t *result;
    329  | SQ_row_t *row;
    330  | C_Type_t class_type;
    331  | int res;
    332  |
    333  |
    334  |   result = tr_get_sql_record(sql_connection, transaction_id);
    335  |   if (result == NULL) return (NULL); /* no further actions */
    336  |
    337  |  /* fill in the Transaction structure */
    338  |   if ((row = SQ_row_next(result))== NULL) return (NULL); /* no further actions */
    339  |
    340  |
    341  |  /* Check if there is more than one row */
    342  |   res = 0;
    343  |   while(SQ_row_next(result))res = -1;
    344  |   if(res == -1) die;
    345  |
    346  |
    347  |   class_type = tr_get_class_type(result, row);
    348  |   if ((tr = transaction_new(sql_connection, class_type)) == NULL) die;
    349  |   tr->object_id = tr_get_object_id(result, row);
    350  |
    351  | /* Fill in all dummies that were created */
    352  |   tr_get_dummies(tr);
    353  |
    354  |   tr->sequence_id = tr_get_sequence_id(result, row);
    355  |   tr->serial_id = tr_get_serial_id(result, row);
    356  |   tr->save = g_strdup(tr_get_save(result, row));
    357  |   g_string_sprintf(tr->error_script, tr_get_escript(result, row));
    358  |
    359  |
    360  |   /* mode of operation */
    361  |   tr->mode = tr_get_mode(result, row);
    362  |   /* indication of success */
    363  |   tr->succeeded = tr_get_success(result, row);
    364  |   /* action is low byte */
    365  |   tr->action = tr_get_action(result, row);
    366  |   /* status is high byte */
    367  |   tr->action |= (tr_get_status(result, row) <<8);
    368  |   tr->action |= (tr_get_clean(result, row) << 8); /* bit0 bears this flag */
    369  |
    370  |   SQ_free_result(result);
    371  |   return(tr);
    372  | }
    373  |
    374  | /************************************************************
    375  | * int TR_delete_record()                                    *
    376  | *                                                           *
    377  | * Deletes all associated sql records                        *
    378  | *                                                           *
    379  | *                                                           *
    380  | ************************************************************/
    381  | void TR_delete_record(Transaction_t *tr)
    382  | {
    383  | GString *query;
    384  | int sql_err;
    385  |
    386  |   if(tr->load_pass != 0) return; /* for fast loader just return */
    387  |
    388  |   /* Delete a record from SQL DB */
    389  |   if ((query = g_string_sized_new(STR_L)) == NULL){
    390  |    ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring\n");
    391  |    die;
    392  |   }
    393  |
    394  |   /* compose query */
    395  |   g_string_sprintf(query, "DELETE FROM dummy_rec WHERE transaction_id=%ld", tr->transaction_id);
    396  |   sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
    397  |   /* in case of an error copy error code and return */
    398  |   if(sql_err) {
    399  |    ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    400  |    die;
    401  |   }
    402  |   g_string_sprintf(query, "DELETE FROM transaction_rec WHERE transaction_id=%ld", tr->transaction_id);
    403  |   sql_err=SQ_execute_query(tr->sql_connection, query->str, NULL);
    404  |   /* in case of an error copy error code and return */
    405  |   if(sql_err) {
    406  |    ER_perror(FAC_UD, UD_SQL, "%s[%s]\n", SQ_error(tr->sql_connection), query->str);
    407  |    die;
    408  |   }
    409  |
    410  |   g_string_free(query, TRUE);
    411  |
    412  | }
    413  |
    414  |
    415  | /************************************************************
    416  | * int TR_recover()                                          *
    417  | *                                                           *
    418  | * Cleans up the database after RIP daemon failure           *
    419  | *                                                           *
    420  | * Return codes:                                             *
    421  | *                                                           *
    422  | * 0 - everything is clean, no cleanup is needed             *
    423  | * 1 - the database was recovered successfully               *
    424  | *                                                           *
    425  | ************************************************************/
    426  | int TR_recover(SQ_connection_t *sql_connection)
    427  | {
    428  | int res;
    429  | Transaction_t * tr;
    430  |
    431  | /* XXX SQ_db_name() ? */
    432  |  fprintf(stderr, "Checking the Database [%s]...", sql_connection->db);
    433  |
    434  |  /* Get the transaction record */
    435  |  /* XXX for NRTM we may specify transaction_id = 0 ? */
    436  |  if ((tr = TR_get_record(sql_connection, TR_LAST)) == NULL) {
    437  |     /* everything is clean */
    438  |     res = 0;
    439  |     fprintf(stderr, "[OK]\n");
    440  |  }
    441  |  else {/* Not everything was perfect :( */
    442  |     fprintf(stderr, "[FAILED]\n"
    443  |                     "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
    444  |                     "+ LAST TRANSACTION IS INCOMPLETE. ENTERING CRASH RECOVERY MODE +\n"
    445  | 		    "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
    446  |    /* Failure occured before the ack was sent */
    447  |    /* Roll back the transaction */
    448  |    /* Delete transaction record (TR) as if it never happened */
    449  |    /************************* R O L L B A C K ***************************/
    450  |    if(TS_ROLLBACK(tr->action)) {
    451  |      fprintf(stderr, "  STATUS: Rollback\n");
    452  |
    453  |     /* don't rollback the transaction if we were to delete the object, but could not */
    454  |      if(!TS_ROLLBACKED(tr->action)){
    455  | 	     fprintf(stderr, "  STATUS: Rollback incomplete, completing...");
    456  | 	     UD_rollback(tr);
    457  | 	     CP_ROLLBACK_PASSED(tr->action); TR_update_status(tr);
    458  | 	     fprintf(stderr, "[OK]\n");
    459  |      } else  fprintf(stderr, "  STATUS: Rollback complete [PASSED]\n");
    460  |
    461  |
    462  |      if(!TS_ROLLBACKED_NH(tr->action)){
    463  | 	     fprintf(stderr, "  STATUS: NH rollback incomplete, completing...");
    464  | 	     NH_rollback(tr->sql_connection);
    465  | 	     CP_ROLLBACK_NH_PASSED(tr->action); TR_update_status(tr);
    466  | 	     fprintf(stderr, "[OK]\n");
    467  |      } else  fprintf(stderr, "  STATUS: NH rollback complete [PASSED]\n");
    468  |      /* In update mode delete TR record. Next time (if any) DBupdate tries to submit, we'll start from scratch */
    469  |      /* In NRTM mode we create a serial record even in case of failure (tr->succeeded ==0)*/
    470  |      /* So in NRTM we need to clean up serials/transaction as well */
    471  |      if(IS_UPDATE(tr->mode)){
    472  | 	     fprintf(stderr, "  STATUS: Serial does not need to be restored, deleting TR...");
    473  | 	     TR_delete_record(tr);
    474  | 	     fprintf(stderr, "[OK]\n");
    475  |      } else {
    476  | 	     fprintf(stderr, "  STATUS: Cleaning serial, deleting TR...");
    477  | 	     if(!TS_CREATED_S(tr->action))
    478  |                UD_rollback_serial(tr);
    479  | 	     else
    480  | 	       UD_commit_serial(tr);
    481  | 	     TR_delete_record(tr);
    482  | 	     fprintf(stderr, "[OK]\n");
    483  |      }
    484  |
    485  |      res = 1;
    486  |    }
    487  |    /************************* C O M M I T ******************************/
    488  |    else { /* commit */
    489  |     /* The ack was sent */
    490  |     /* Complete the commit */
    491  |     fprintf(stderr, "  STATUS: Commit\n");
    492  |     /* We keep the transaction record in case DBupdate failed */
    493  |     /* and requests the same transaction after recovery ? */
    494  |     /* Such approach will allow us to avoid 3-way handshaking with DBupdate */
    495  |     /* So we never blocked or timed out during that phase */
    496  |
    497  |     /* XXX But first I implemented another approach (to keep DB tiny/tidy): */
    498  |     /* 1. Process the transaction */
    499  |     /* 2. In case of failure - rollback - NACK */
    500  |     /* 3. Before commit - ACK (UD_ack()) */
    501  |     /* 4. If UD_ack returns an error preserve a tr_record */
    502  |     /* 5. Commit */
    503  |     /* 6. If still alive and UD_ack passed - delete the record - all is clean */
    504  |     /*    Otherwise preserve a tr_record */
    505  |
    506  |     if(ACT_DELETE(tr->action)) {
    507  |      /* check if we passed deletion process */
    508  |      if(!TS_DELETED(tr->action)){
    509  | 	     fprintf(stderr, "  STATUS: Delete incomplete, completing...");
    510  | 	     UD_delete(tr);
    511  | 	     CP_DELETE_PASSED(tr->action); TR_update_status(tr);
    512  |              fprintf(stderr, "[OK]\n");
    513  |      } else  fprintf(stderr, "  STATUS: Delete complete [PASSED]\n");
    514  |     }
    515  |     else { /* update or create */
    516  |      /* Check if we passed the deletion pass of commit */
    517  |      if(!TS_COMMITTED_I(tr->action)){
    518  | 	     fprintf(stderr, "  STATUS: Commit phase I incomplete, completing...");
    519  | 	     UD_commit_I(tr);
    520  | 	     CP_COMMIT_I_PASSED(tr->action); TR_update_status(tr);
    521  |              fprintf(stderr, "[OK]\n");
    522  |      } else  fprintf(stderr, "  STATUS: Commit phase I complete [PASSED]\n");
    523  |      /* Check if we passed the second pass of commit */
    524  |      if(!TS_COMMITTED_II(tr->action)){
    525  | 	     fprintf(stderr, "  STATUS: Commit phase II incomplete, completing...");
    526  | 	     UD_commit_II(tr);
    527  | 	     CP_COMMIT_II_PASSED(tr->action); TR_update_status(tr);
    528  |              fprintf(stderr, "[OK]\n");
    529  |      } else  fprintf(stderr, "  STATUS: Commit phase II complete [PASSED]\n");
    530  |     } /* end of delete, create, update specific operations */
    531  |
    532  |      /* Check if we passed the NH repository commit */
    533  |      if(!TS_COMMITTED_NH(tr->action)){
    534  | 	     fprintf(stderr, "  STATUS: NH commit incomplete, completing...");
    535  | 	     NH_commit(tr->sql_connection);
    536  | 	     CP_COMMIT_NH_PASSED(tr->action); TR_update_status(tr);
    537  |              fprintf(stderr, "[OK]\n");
    538  |      } else  fprintf(stderr, "  STATUS: NH commit complete [PASSED]\n");
    539  |
    540  |
    541  |      /* create serial file */
    542  |      if(!TS_CREATED_S(tr->action))
    543  |      {
    544  |        fprintf(stderr, "  STATUS: Serial rollback and restore...");
    545  |        UD_rollback_serial(tr);
    546  |        if(ACT_UPD_CLLPS(tr->action)) { /* this is a collapsed update (DEL + ADD) */
    547  |           tr->action=TA_DELETE; UD_create_serial(tr);
    548  | 	  tr->sequence_id++;
    549  |           tr->action=TA_CREATE; UD_create_serial(tr);
    550  |        }else if(ACT_UPD_DUMMY(tr->action)) { /* this was a dummy update - we need only CREATE serial */
    551  |           tr->action=TA_CREATE;
    552  |           tr->sequence_id++; /* because in fact this is an update (sequence_id=2) */
    553  |           UD_create_serial(tr);
    554  |        } else UD_create_serial(tr);
    555  |        CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
    556  |      }
    557  |      fprintf(stderr, "[OK]\n");
    558  |      UD_commit_serial(tr);
    559  |
    560  |      fprintf(stderr, "  STATUS: Marking TR as clean...");
    561  |      TR_mark_clean(tr);
    562  |
    563  |      fprintf(stderr, "[OK]\n");
    564  |
    565  |      res = 2;
    566  |   }
    567  |  }
    568  |  transaction_free(tr);
    569  |  fprintf(stderr, "  STATUS: The Database is clean \n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
    570  |
    571  |  return(res);
    572  | }
    573  |
    574  | /************************************************************
    575  | * int TR_check()                                            *
    576  | *                                                           *
    577  | * Checks if the requested transaction has already been      *
    578  | * processed. This could happen when DBupdate crashes while  *
    579  | * RIPupdate successfully completes the transaction.         *
    580  | *                                                           *
    581  | * If this is the case, RIPupdate will return an ack to      *
    582  | * DBupdate as if the transaction was processed again        *
    583  | *                                                           *
    584  | * Return codes:                                             *
    585  | * 0 - everything is clean - this is a new transaction       *
    586  | * 1 - the stored transaction was re-played                  *
    587  | *                                                           *
    588  | ************************************************************/
    589  | int TR_check(SQ_connection_t *sql_connection, long transaction_id, int sockfd)
    590  | {
    591  | Transaction_t * tr;
    592  |
    593  |
    594  |  /* transaction_id == 0 means that only one record is maintained */
    595  |  /* therefore it is not possible to replay the transaction */
    596  |  /* and transaction_id does not uniquely identify the transaction */
    597  |  /* suitable for NRTM and for backwards compatibility */
    598  |  if(transaction_id <=0) return(0);
    599  |  /* Get the transaction record */
    600  |  /* XXX for NRTM we may specify transaction_id = 0 ? */
    601  |  if ((tr = TR_get_record(sql_connection, transaction_id)) == NULL) return(0); /* everything is clean */
    602  |
    603  |  /* Check if the record is clean (it should be ) */
    604  |  /* that means that either the transaction finished normally */
    605  |  /* or crash recovery procedure cleaned up the database (and record as well ) */
    606  |  if (TS_CLEAN(tr->action)) {
    607  |    /* send an acknowledgement */
    608  |    /* XXX Wait for ack */
    609  |    /* XXX if ack is timed out just return, else delete the tr_record */
    610  |    /* if(UD_ack(tr)==0) TR_delete_record(tr); */
    611  |
    612  |    /* Send an acknowledgement, append note that transaction was rerun */
    613  |    tr->socket=sockfd;
    614  |    g_string_sprintfa(tr->error_script,"I[%ld]: requested transaction was processed before\n", transaction_id);
    615  |    UD_ack(tr);
    616  |    ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "%s requested transaction was processed before [%d]\n", UD_TAG, transaction_id);
    617  |    transaction_free(tr);
    618  |  }
    619  |  else {
    620  | 	 ER_perror(FAC_UD, UD_SQL, "TR is not clean\n");
    621  | 	 die; /* the record should be clean */
    622  |  }
    623  |  return(1);
    624  | }
    625  |
    626  |