Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Distributed Transaction Processing:
0003  *   The TX (Transaction Demarcation) Specification implementation X/Open
0004  *
0005  * @file tx.c
0006  */
0007 /* -----------------------------------------------------------------------------
0008  * Enduro/X Middleware Platform for Distributed Transaction Processing
0009  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0010  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0011  * This software is released under one of the following licenses:
0012  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0013  * See LICENSE file for full text.
0014  * -----------------------------------------------------------------------------
0015  * AGPL license:
0016  *
0017  * This program is free software; you can redistribute it and/or modify it under
0018  * the terms of the GNU Affero General Public License, version 3 as published
0019  * by the Free Software Foundation;
0020  *
0021  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0022  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0023  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0024  * for more details.
0025  *
0026  * You should have received a copy of the GNU Affero General Public License along 
0027  * with this program; if not, write to the Free Software Foundation, Inc.,
0028  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0029  *
0030  * -----------------------------------------------------------------------------
0031  * A commercial use license is available from Mavimax, Ltd
0032  * contact@mavimax.com
0033  * -----------------------------------------------------------------------------
0034  */
0035 #include <string.h>
0036 #include <stdio.h>
0037 #include <stdlib.h>
0038 #include <memory.h>
0039 #include <errno.h>
0040 #include <dlfcn.h>
0041 
0042 #include <atmi.h>
0043 #include <atmi_tls.h>
0044 #include <ndrstandard.h>
0045 #include <ndebug.h>
0046 #include <ndrxdcmn.h>
0047 #include <userlog.h>
0048 #include <tx.h>
0049 #include <Exfields.h>
0050 /*---------------------------Externs------------------------------------*/
0051 /*---------------------------Macros-------------------------------------*/
0052 /*---------------------------Enums--------------------------------------*/
0053 /*---------------------------Typedefs-----------------------------------*/
0054 /*---------------------------Globals------------------------------------*/
0055 /*---------------------------Statics------------------------------------*/
0056 /*---------------------------Prototypes---------------------------------*/
0057 
0058 /**
0059  * Map the tperror to tx error
0060  * @param dbg debug msg
0061  * @param tpret Tp return code
0062  * @param allow_error is TX_ERROR code allowed?
0063  * @return TX_* error code
0064  */
0065 exprivate int tx_map_error1(const char *dbg, int tpret, int allow_error)
0066 {
0067     int ret = TX_FAIL;
0068     int tperr;
0069     
0070     if (0==tpret)
0071     {
0072         tperr = EXSUCCEED;
0073     }
0074     else
0075     {
0076         tperr = tperrno;
0077     }
0078     
0079     NDRX_LOG(log_debug, "tpret=%d tperr=%d", tpret, tperr);
0080     
0081     switch (tperr)
0082     {
0083         case 0:
0084             ret = TX_OK;
0085             break;
0086         case TPEPROTO:
0087             ret = TX_PROTOCOL_ERROR;
0088             break;
0089         case TPESVCFAIL:
0090         case TPESVCERR:
0091         case TPETIME:
0092             if (allow_error)
0093             {
0094                 ret = TX_ERROR;
0095             }
0096             else
0097             {
0098                 ret = TX_FAIL;
0099             }
0100             break;
0101         case TPESYSTEM:
0102         case TPEOS:
0103         case TPERMERR:
0104         case TPEINVAL:
0105             ret = TX_FAIL;
0106             break;
0107         case TPEHAZARD:
0108             ret = TX_HAZARD;
0109             break;
0110         case TPEHEURISTIC:
0111             ret = TX_MIXED;
0112             break;
0113         case TPEMATCH:
0114         case TPEABORT:
0115             ret = TX_ROLLBACK;
0116             break;
0117     }
0118     
0119     if (TX_OK==ret)
0120     {
0121         NDRX_LOG(log_debug, "%s: TX_OK", dbg);
0122     }
0123     else
0124     {
0125         NDRX_LOG(log_error, "%s tp error %d mapped to tx %d: %s", dbg, 
0126                 tperr, ret, tpstrerror(tperr));
0127     }
0128     
0129     return ret;
0130 }
0131 
0132 /**
0133  * Begin transaction
0134  * @return TX_ return code
0135  */
0136 expublic int tx_begin(void)
0137 {
0138     int ret;
0139     ATMI_TLS_ENTRY;
0140     
0141     ret = tpbegin(G_atmi_tls->tx_transaction_timeout, 0);
0142     
0143     /* map tp error to tx */
0144     ret = tx_map_error1(__func__, ret, EXTRUE);
0145     
0146     return ret;
0147    
0148 }
0149 
0150 /**
0151  * Close resource manager
0152  * @return TX_ return code
0153  */
0154 expublic int tx_close(void)
0155 {
0156     int ret;
0157     
0158     ret = tpclose();
0159     
0160     ret = tx_map_error1(__func__, ret, EXTRUE);
0161     
0162     return ret;
0163 }
0164 
0165 /**
0166  * Commit transaction, chain the next transaction if needed.
0167  * If chained mode is set for transactions, start new TRAN if possible.
0168  * If not then append errors with NO_BEGIN code (i.e. chain start failed).
0169  * @return TX_ return code
0170  */
0171 expublic int tx_commit(void)
0172 {
0173     int ret, ret2;
0174     long flags = 0;
0175     ATMI_TLS_ENTRY;
0176     
0177     if (TX_COMMIT_DECISION_LOGGED==G_atmi_tls->tx_commit_return)
0178     {
0179         flags|=TP_CMT_LOGGED;
0180     }
0181     
0182     ret = tpcommit(flags);
0183     
0184     ret = tx_map_error1(__func__, ret, EXFALSE);
0185     
0186     /* chain the next transaction if required */
0187     
0188     if (TX_CHAINED==G_atmi_tls->tx_transaction_control)
0189     {
0190         if (TX_PROTOCOL_ERROR == ret ||  TX_FAIL==ret)
0191             
0192         {
0193             NDRX_LOG(log_error, "Fatal error cannot chain tx");
0194         }
0195         else
0196         {
0197             ret2 = tpbegin(G_atmi_tls->tx_transaction_timeout, 0);
0198             ret2 = tx_map_error1("tx_commit next tran begin: ", ret2, EXTRUE);
0199             
0200             /* if fail + fail base... */
0201             if (TX_OK!=ret2)
0202             {
0203                 ret2 += TX_NO_BEGIN;
0204             }
0205         }
0206         
0207         ret = ret2;
0208     }
0209     
0210     NDRX_LOG(log_debug, "returns %d", ret);
0211     return ret;
0212 }
0213 
0214 /**
0215  * Fill in transaction info block
0216  * @param txinfo
0217  * @return TX_OK/
0218  */
0219 expublic int ndrx_tx_info(TXINFO * txinfo)
0220 {
0221     int ret = TX_OK; /* not in tran mode */
0222     UBFH *p_ub = NULL;
0223     short   txstage;  /* In what state we are */
0224     ATMI_TLS_ENTRY;
0225     
0226     txinfo->transaction_control = G_atmi_tls->tx_transaction_control;
0227     txinfo->transaction_timeout = G_atmi_tls->tx_transaction_timeout;
0228     txinfo->when_return = G_atmi_tls->tx_commit_return;
0229     
0230     /* if TX is not open */
0231     
0232     if (!G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
0233     {
0234         NDRX_LOG(log_warn, "XA interface is no topen!");
0235         ret = TX_PROTOCOL_ERROR;
0236     }
0237     else if (!G_atmi_tls->G_atmi_xa_curtx.txinfo)
0238     {
0239         txinfo->xid.formatID = EXFAIL;
0240         goto out;
0241     }
0242     else
0243     {
0244         ret = 1; /* we are in transaction */
0245         txinfo->transaction_state = TX_ACTIVE;
0246         /* copy off xid */
0247         atmi_xa_deserialize_xid((unsigned char *)G_atmi_tls->G_atmi_xa_curtx.txinfo->tmxid,
0248             &G_atmi_tls->xid);
0249         
0250         /* check are we marked for rollback? */
0251         
0252         if (G_atmi_tls->G_atmi_xa_curtx.txinfo->tmtxflags & TMTXFLAGS_IS_ABORT_ONLY)
0253         {
0254             /* is it rollback only? */
0255             txinfo->transaction_state = TX_ROLLBACK_ONLY;
0256         }
0257         else
0258         {
0259             /* check with TM, what actually status we have? */
0260             if (NULL==(p_ub=atmi_xa_call_tm_generic(ATMI_XA_STATUS, EXFALSE, EXFAIL, 
0261                 G_atmi_tls->G_atmi_xa_curtx.txinfo, 0L, EXFAIL)))
0262             {
0263                 int tperr = tperrno;
0264                 NDRX_LOG(log_error, "Tran info failed with: %d", tperr);
0265                 
0266                 if (TPEMATCH==tperr)
0267                 {
0268                     NDRX_LOG(log_debug, "Not matched by TM -> TX_TIMEOUT_ROLLBACK_ONLY");
0269                     txinfo->transaction_state = TX_TIMEOUT_ROLLBACK_ONLY;
0270                     
0271                     /* switch tran state? */
0272                     G_atmi_tls->G_atmi_xa_curtx.txinfo->tmtxflags |=TMTXFLAGS_IS_ABORT_ONLY;
0273                 }
0274                 else
0275                 {
0276                     /* return the error if */
0277                     ret = TX_FAIL;
0278                 }
0279                 goto out;
0280             } /* if transaction not found.. */
0281             
0282             /* got transaction infos, check the state? 
0283              * if if is rollback state the return that...
0284              */
0285             
0286             if (EXSUCCEED!=Bget(p_ub, TMTXSTAGE, 0, (char *)&txstage, 0L))
0287             {
0288                 NDRX_LOG(log_error, "Failed to get TMTXSTAGE from tmsrv: %s", 
0289                         Bstrerror(Berror));
0290                 ret = TX_FAIL;
0291                 goto out;
0292             }
0293             
0294             /* analyze the stage */
0295             NDRX_LOG(log_debug, "txstage=%hd", txstage);
0296             if (txstage >= XA_TX_STAGE_ABORTING  && txstage <=XA_TX_STAGE_ABORTING)
0297             {
0298                 NDRX_LOG(log_warn, "TM is rolling back..!");
0299                 txinfo->transaction_state = TX_ROLLBACK;
0300             }
0301         } /* if not local abort only.. */
0302     } /* if have global tx */
0303     
0304 out:    
0305             
0306     if (NULL!=p_ub)
0307     {
0308         tpfree((char *)p_ub);
0309     }
0310         
0311     return ret;
0312 }
0313 
0314 /**
0315  * Open resource manager
0316  * @return TX_* errors
0317  */
0318 expublic int tx_open(void)
0319 {
0320     int ret;
0321     
0322     ret = tpopen();
0323     
0324     ret = tx_map_error1(__func__, ret, EXFALSE);
0325     
0326     return ret;
0327 }
0328 
0329 /**
0330  * Rollback transaction
0331  * @return TX_* errors
0332  */
0333 expublic int tx_rollback(void)
0334 {
0335     int ret, ret2;
0336     
0337     ret = tpabort(0L);
0338     
0339     ret = tx_map_error1(__func__, ret, EXFALSE);
0340     
0341     /* chain the next transaction if required */
0342     
0343     if (TX_CHAINED==G_atmi_tls->tx_transaction_control)
0344     {
0345         if (TX_PROTOCOL_ERROR == ret || TX_FAIL==ret)
0346             
0347         {
0348             NDRX_LOG(log_error, "Fatal error cannot chain tx");
0349         }
0350         else
0351         {
0352             ret2 = tpbegin(G_atmi_tls->tx_transaction_timeout, 0);
0353             ret2 = tx_map_error1("tx_commit next tran begin: ", ret2, EXTRUE);
0354             
0355             /* if fail + fail base... */
0356             if (TX_OK!=ret2)
0357             {
0358                 ret2 += TX_NO_BEGIN;
0359             }
0360         }
0361         
0362         ret = ret2;
0363     }
0364     
0365     NDRX_LOG(log_debug, "returns %d", ret);
0366     return ret;
0367 }
0368 
0369 /**
0370  * Set when to return from commit. Either when transaction is completed
0371  * or when transaction is logged in persisted device for further background
0372  * completion.
0373  * The default is TX_COMMIT_COMPLETED.
0374  * @param cr TX_COMMIT_DECISION_LOGGED or TX_COMMIT_COMPLETED
0375  * @return TX_OK/TX_EINVAL/TX_PROTOCOL_ERROR
0376  */
0377 expublic int tx_set_commit_return(COMMIT_RETURN cr)
0378 {
0379     int ret = TX_OK;
0380     ATMI_TLS_ENTRY;
0381     
0382     if (!G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
0383     {
0384         ret = TX_PROTOCOL_ERROR;
0385         goto out;
0386     }
0387     
0388     if (TX_COMMIT_DECISION_LOGGED!=cr &&
0389             TX_COMMIT_COMPLETED!=cr)
0390     {
0391         NDRX_LOG(log_error, "Invalid value: commit return %ld", (long)cr);
0392         ret = TX_EINVAL;
0393         goto out;
0394     }
0395     
0396     G_atmi_tls->tx_commit_return = cr;
0397     NDRX_LOG(log_info, "Commit return set to %ld", 
0398             (long)G_atmi_tls->tx_commit_return);
0399     
0400 out:
0401     return ret;
0402 }
0403 
0404 /**
0405  * Set what commit/rollback will do:- either open next tran
0406  * or leave with out transaction
0407  * @param tc TX_UNCHAINED/TX_CHAINED 
0408  * @return TX_OK/TX_EINVAL
0409  */
0410 expublic int tx_set_transaction_control(TRANSACTION_CONTROL tc)
0411 {
0412     int ret = TX_OK;
0413     ATMI_TLS_ENTRY;
0414     
0415     if (!G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
0416     {
0417         ret = TX_PROTOCOL_ERROR;
0418         goto out;
0419     }
0420     
0421     if (TX_UNCHAINED!=tc &&
0422             TX_CHAINED!=tc)    
0423     {
0424         NDRX_LOG(log_error, "Invalid value: transaction control %ld", (long)tc);
0425         ret = TX_EINVAL;
0426         goto out;
0427     }
0428     
0429     G_atmi_tls->tx_transaction_control = tc;
0430     NDRX_LOG(log_info, "Transaction control set to %ld", 
0431             (long)G_atmi_tls->tx_transaction_control);
0432     
0433 out:
0434     return ret;
0435 }
0436 
0437 /**
0438  * Set seconds for the transaction to time out by transaction manager
0439  * @param tt timeout in seconds. 0 disables tout (i.e. uses maximum time by tmsrv)
0440  * @return TX_OK/TX_EINVAL/TX_PROTOCOL_ERROR/
0441  */
0442 expublic int tx_set_transaction_timeout(TRANSACTION_TIMEOUT tt)
0443 {
0444     int ret = TX_OK;
0445     ATMI_TLS_ENTRY;
0446     
0447     if (!G_atmi_tls->G_atmi_xa_curtx.is_xa_open)
0448     {
0449         ret = TX_PROTOCOL_ERROR;
0450         goto out;
0451     }
0452     
0453     if (tt < 0)
0454     {
0455         NDRX_LOG(log_error, "Invalid value: timeout %ld", (int)tt);
0456         ret = TX_EINVAL;
0457         goto out;
0458     }
0459     
0460     G_atmi_tls->tx_transaction_timeout = tt;
0461     
0462     NDRX_LOG(log_info, "Transaction timeout out set to %ld", 
0463             (long)G_atmi_tls->tx_transaction_timeout);
0464     
0465 out:
0466     return ret;
0467 }
0468 
0469 /* vim: set ts=4 sw=4 et smartindent: */