Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief ATMI level cache - invalidate
0003  *
0004  * @file atmi_cache_inval.c
0005  */
0006 /* -----------------------------------------------------------------------------
0007  * Enduro/X Middleware Platform for Distributed Transaction Processing
0008  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0009  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0010  * This software is released under one of the following licenses:
0011  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0012  * See LICENSE file for full text.
0013  * -----------------------------------------------------------------------------
0014  * AGPL license:
0015  *
0016  * This program is free software; you can redistribute it and/or modify it under
0017  * the terms of the GNU Affero General Public License, version 3 as published
0018  * by the Free Software Foundation;
0019  *
0020  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0021  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0022  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0023  * for more details.
0024  *
0025  * You should have received a copy of the GNU Affero General Public License along 
0026  * with this program; if not, write to the Free Software Foundation, Inc.,
0027  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0028  *
0029  * -----------------------------------------------------------------------------
0030  * A commercial use license is available from Mavimax, Ltd
0031  * contact@mavimax.com
0032  * -----------------------------------------------------------------------------
0033  */
0034 
0035 /*---------------------------Includes-----------------------------------*/
0036 #include <stdlib.h>
0037 #include <stdio.h>
0038 #include <errno.h>
0039 #include <string.h>
0040 #include <ndrstandard.h>
0041 #include <atmi.h>
0042 #include <atmi_tls.h>
0043 #include <typed_buf.h>
0044 
0045 #include "thlock.h"
0046 #include "userlog.h"
0047 #include "utlist.h"
0048 #include "exregex.h"
0049 #include <exparson.h>
0050 #include <atmi_cache.h>
0051 #include <Exfields.h>
0052 /*---------------------------Externs------------------------------------*/
0053 /*---------------------------Macros-------------------------------------*/
0054 /*---------------------------Enums--------------------------------------*/
0055 /*---------------------------Typedefs-----------------------------------*/
0056 /*---------------------------Globals------------------------------------*/
0057 /*---------------------------Statics------------------------------------*/
0058 /*---------------------------Prototypes---------------------------------*/
0059 
0060 /**
0061  * Invalidate their cache
0062  * @param svc service name called
0063  * @param cache cache descriptor (invalidator)
0064  * @param key key built (their key)
0065  * @param idata input data
0066  * @param ilen input data len
0067  * @return EXSUCCEED/EXFAIL (tperror set)
0068  */
0069 expublic int ndrx_cache_inval_their(char *svc, ndrx_tpcallcache_t *cache, 
0070         char *key, char *idata, long ilen)
0071 {
0072     int ret = EXFALSE;
0073     int tran_started = EXFALSE;
0074     char flags[]={NDRX_TPCACHE_BCAST_DELFULLC, EXEOS};
0075     EDB_txn *txn;
0076     
0077      /*
0078       * just delete record from theyr cache, ptr to cache we have already inside
0079       * the cache object 
0080       */
0081     
0082     if (EXSUCCEED!=(ret=ndrx_cache_edb_begin(cache->inval_cache->cachedb, &txn, 0)))
0083     {
0084         NDRX_LOG(log_error, "%s: failed to start tran", __func__);
0085         goto out;
0086     }
0087     
0088     tran_started = EXTRUE;
0089     
0090     /* If this is not full keygrp inval, then remove record from group */
0091     if (cache->inval_cache->flags & NDRX_TPCACHE_TPCF_KEYITEMS)
0092     {
0093         if (cache->flags & NDRX_TPCACHE_TPCF_INVLKEYGRP)
0094         {
0095             NDRX_LOG(log_debug, "Invalidate whole group!");
0096             /* Remove full group
0097              * key group data must be filled in inval cache!
0098              * TODO: Maybe we need some tests in init?
0099              */
0100             if (EXSUCCEED!=(ret=ndrx_cache_keygrp_inval_by_data(
0101                     cache, idata, ilen,txn)))
0102             {
0103                 NDRX_LOG(log_error, "failed to remove keygroup!");
0104                 goto out;
0105             }
0106             
0107             /* Broadcast the  */
0108             flags[0] = NDRX_TPCACHE_BCAST_GROUPC;
0109             goto ok_broadcast;
0110         }
0111         else
0112         {
0113             /* remove just key item... and continue */
0114             NDRX_LOG(log_debug, "Removing single key item from group (1)");
0115         }
0116     }
0117     
0118     NDRX_LOG(log_debug, "Delete their cache [%s] idx %d",
0119             cache->inval_svc, cache->inval_idx);
0120     
0121     /* If their is part of the cache group, then we shall 
0122      * invalidate whole group.. 
0123      */
0124     if (EXSUCCEED!=(ret=ndrx_cache_edb_del (cache->inval_cache->cachedb, txn, 
0125             key, NULL)))
0126     {
0127         if (EDB_NOTFOUND==ret)
0128         {
0129             ret=EXSUCCEED;
0130         }
0131         else
0132         {
0133             EXFAIL_OUT(ret);
0134         }
0135     }
0136     
0137     /* remove from group... */
0138     if (cache->inval_cache->flags & NDRX_TPCACHE_TPCF_KEYITEMS &&
0139             !(cache->flags & NDRX_TPCACHE_TPCF_INVLKEYGRP))
0140     {
0141         NDRX_LOG(log_debug, "Removing single key item from group (2)");
0142         if (EXSUCCEED!=(ret=ndrx_cache_keygrp_addupd(cache->inval_cache, 
0143                     idata, ilen, key, NULL, EXTRUE, txn)))
0144         {
0145             NDRX_LOG(log_error, "Failed to remove key [%s] from keygroup!");
0146             goto out;
0147         }
0148     }
0149     
0150 ok_broadcast:
0151     /* broadcast if needed */
0152     if (cache->inval_cache->cachedb->flags & NDRX_TPCACHE_FLAGS_BCASTDEL)
0153     {
0154         NDRX_LOG(log_debug, "Broadcast flags [%s]", flags);
0155         if (EXSUCCEED!=ndrx_cache_broadcast(cache->inval_cache, 
0156                 cache->inval_svc, idata, ilen, 
0157                 NDRX_CACHE_BCAST_MODE_DEL, flags, 0, 0, 0, 0))
0158         {
0159             NDRX_LOG(log_error, "WARNING ! Failed to broadcast delete event - continue");
0160             
0161             if (0!=tperrno)
0162             {
0163                 NDRX_LOG(log_error, "TP Error set -> fail");
0164                 EXFAIL_OUT(ret);
0165             }
0166         }
0167     }
0168     
0169 out:
0170 
0171     if (tran_started)
0172     {
0173         if (EXSUCCEED==ret)
0174         {
0175             ndrx_cache_edb_commit(cache->inval_cache->cachedb, txn);
0176         }
0177         else
0178         {
0179             ndrx_cache_edb_abort(cache->inval_cache->cachedb, txn);
0180         }
0181     }
0182 
0183     return ret;
0184 }
0185 
0186 /**
0187  * Delete cache record by data.
0188  * TODO: Currently not used. And this is missing broadcast part.
0189  * Probably could be removed in future.
0190  * @param svc Service name to search cache for
0191  * @param idata input data (XATMI buffer) received from their node
0192  * @param ilen input data len (XATMI buffer)
0193  * @param flags how to delete - if "F" then do not check they key, just full db drop
0194  * @return EXSUCCEED/EXFAIL
0195  */
0196 expublic int ndrx_cache_inval_by_data(char *svc, char *idata, long ilen, char *flags)
0197 {
0198     int ret = EXSUCCEED;
0199     ndrx_tpcache_svc_t *svcc = NULL;
0200     buffer_obj_t *buffer_info;
0201     typed_buffer_descr_t *buf_type;
0202     ndrx_tpcallcache_t *cache;
0203     char key[NDRX_CACHE_KEY_MAX+1];
0204     char errdet[MAX_TP_ERROR_LEN+1];
0205     int tran_started = EXFALSE;
0206     EDB_txn *txn = NULL;
0207     int delete_from_keygroup = EXFALSE;
0208     
0209     /* find svc, find cache, build key, delete record 
0210      * The logic will be more or less like a cache lookup...
0211      */
0212     
0213     /* Find service in cache */
0214     EXHASH_FIND_STR(ndrx_G_tpcache_svc, svc, svcc);
0215     
0216     if (NULL==svcc)
0217     {
0218         NDRX_LOG(log_debug, "No cache defined for service [%s]", svc);
0219         ret = NDRX_TPCACHE_ENOCACHE;
0220         ndrx_TPset_error_fmt(TPENOENT, "No cache defined for service [%s]", svc);
0221         goto out;
0222     }
0223     
0224     if (NULL!=idata)
0225     {
0226         if (NULL==(buffer_info = ndrx_find_buffer(idata)))
0227         {
0228             ndrx_TPset_error_fmt(TPEINVAL, "%s: Buffer %p not known to system!", 
0229                     __func__, idata);
0230             EXFAIL_OUT(ret);
0231         }
0232     }
0233     
0234     /* Loop over the tpcallcaches, if `next' flag present, then perform next
0235      * if we get invalidate their, then delete target records by the key */
0236     buf_type = &G_buf_descr[buffer_info->type_id];
0237     
0238     
0239     /* find exact cache */
0240     if (NULL==(cache = ndrx_cache_findtpcall(svcc, buf_type, idata, ilen, EXFAIL)))
0241     {
0242         NDRX_LOG(log_debug, "No caches matched by expressions for [%s]", svc);
0243         ndrx_TPset_error_fmt(TPENOENT, "No caches matched by expressions for [%s]", 
0244                 svc);
0245         ret = NDRX_TPCACHE_ENOCACHE;
0246         goto out;
0247     }
0248     
0249     /* now delete the record, generate key */
0250     /* Build the key... */
0251     NDRX_STRCPY_SAFE(key, cache->keyfmt);
0252     if (EXSUCCEED!=(ret = ndrx_G_tpcache_types[buffer_info->type_id].pf_get_key(
0253             cache, idata, ilen, key, sizeof(key), errdet, sizeof(errdet))))
0254     {
0255         if (NDRX_TPCACHE_ENOKEYDATA==ret)
0256         {
0257             NDRX_LOG(log_debug, "Failed to build key (no data for key): %s", errdet);
0258             goto out;
0259         }
0260         else
0261         {
0262             NDRX_LOG(log_error, "Failed to build key: ", errdet);
0263 
0264             /* generate TP error here! */
0265             ndrx_TPset_error_fmt(TPESYSTEM, "%s: Failed to build cache key: %s", 
0266                     __func__, errdet);
0267             goto out;
0268         }
0269     }
0270     
0271     /* now delete the record */
0272     /* start transaction */
0273     if (EXSUCCEED!=(ret=ndrx_cache_edb_begin(cache->cachedb, &txn, 0)))
0274     {
0275         NDRX_LOG(log_error, "%s: failed to start tran", __func__);
0276         goto out;
0277     }
0278     tran_started=EXTRUE;
0279     
0280     NDRX_LOG(log_debug, "Delete record by key [%s] from cache svc [%s] index: %d", 
0281             key, cache->svcnm, cache->idx);
0282     
0283     
0284     if (cache->flags & NDRX_TPCACHE_TPCF_KEYITEMS)
0285     {
0286         /* we are part of keyitems group... perform group operation */
0287         
0288         /* Delete by keygroup */
0289         if (strchr(flags, NDRX_TPCACHE_BCAST_GROUPC))
0290         {
0291             NDRX_LOG(log_debug, "Full group delete");
0292             
0293             if (EXSUCCEED!=(ret=ndrx_cache_keygrp_inval_by_data(cache, idata, 
0294                     ilen, txn)))
0295             {
0296                 NDRX_LOG(log_error, "Failed to delete group!");
0297                 goto out;
0298             }
0299             /* All already deleted... */
0300             goto out;
0301         }
0302         else
0303         {
0304             NDRX_LOG(log_debug, "Delete only keyitem from group");
0305             delete_from_keygroup=EXTRUE;
0306         }
0307         
0308     }
0309     
0310     /* delete record (fully) */
0311     if (EXSUCCEED!=(ret=ndrx_cache_edb_del (cache->cachedb, txn, key, NULL)))
0312     {
0313         /* ignore not deleted error... */
0314         if (EDB_NOTFOUND==ret)
0315         {
0316             ret=EXSUCCEED;
0317         }    
0318     }
0319     
0320     /* update keygroup */
0321     
0322     if (delete_from_keygroup)
0323     {
0324         if (EXSUCCEED!=(ret=ndrx_cache_keygrp_addupd(cache, idata, ilen, key, 
0325                 NULL, EXTRUE, txn)))
0326         {
0327             NDRX_LOG(log_error, "Failed to delete key from keygroup [%s]/[%s]!",
0328                     key, cache->keygrpdb->cachedb);
0329             goto out;
0330         }
0331     }
0332     
0333 out:
0334 
0335     if (tran_started)
0336     {
0337         if (EXSUCCEED==ret)
0338         {
0339             if (EXSUCCEED!=ndrx_cache_edb_commit(cache->cachedb, txn))
0340             {
0341                 NDRX_LOG(log_error, "Failed to commit: %s", tpstrerror(tperrno));
0342                 ret=EXFAIL;
0343             }
0344         }
0345         else
0346         {
0347             ndrx_cache_edb_abort(cache->cachedb, txn);
0348         }
0349     }
0350     
0351 
0352     return ret;
0353 }
0354 
0355 /**
0356  * Drop cache by name
0357  * NOTE ! The drop will not perform dropping of keygroup.
0358  * This does not perform any kind of broadcast
0359  * @param cachedbnm cache dabase name (in config, subsect)
0360  * @return EXSUCCEED/EXFAIL (tperror set)
0361  */
0362 expublic int ndrx_cache_drop(char *cachedbnm, short nodeid)
0363 {
0364     int ret = EXSUCCEED;
0365     EDB_txn *txn = NULL;
0366     ndrx_tpcache_db_t* db;
0367     int tran_started = EXFALSE;
0368     
0369     NDRX_LOG(log_info, "Resetting cache db [%s] source node: [%hd]", 
0370             cachedbnm, nodeid);
0371     
0372     /* find cachedb */
0373     
0374     if (NULL==(db=ndrx_cache_dbresolve(cachedbnm, NDRX_TPCACH_INIT_NORMAL)))
0375     {
0376         NDRX_CACHE_TPERRORNOU(TPENOENT, "Failed to get cache record for [%s]: %s", 
0377                 cachedbnm, tpstrerror(tperrno));
0378         EXFAIL_OUT(ret);
0379     }
0380     
0381     /* start transaction */
0382     if (EXSUCCEED!=(ret=ndrx_cache_edb_begin(db, &txn, 0)))
0383     {
0384         NDRX_CACHE_TPERROR(TPESYSTEM, "%s: failed to start tran", __func__);
0385         goto out;
0386     }
0387     
0388     tran_started = EXTRUE;
0389     
0390     if (EXSUCCEED!=(ret=edb_drop(txn, db->dbi, 0)))
0391     {
0392         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0393                 "CACHE: Failed to clear db: [%s]: %s", 
0394                 db->cachedb, edb_strerror(ret));
0395 
0396         EXFAIL_OUT(ret);
0397     }
0398     
0399     /* check if we should broadcast the drop */
0400     
0401     NDRX_LOG(log_warn, "Cache [%s] dropped", cachedbnm);
0402     
0403     if ( (db->flags & NDRX_TPCACHE_FLAGS_BCASTDEL) &&
0404             tpgetnodeid()==nodeid )
0405     {
0406         NDRX_LOG(log_debug, "Same node -> broadcast event of drop");
0407         
0408         /* Broadcast NULL buffer event (ignore result) */
0409         if (EXSUCCEED!=ndrx_cache_broadcast(NULL, cachedbnm, NULL, 0, 
0410                 NDRX_CACHE_BCAST_MODE_KIL,  NDRX_TPCACHE_BCAST_DFLT, 0, 0, 0, 0))
0411         {
0412             NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to broadcast: %s", 
0413                     __func__, tpstrerror(tperrno));
0414         }
0415     }
0416     
0417 out:
0418 
0419     if (tran_started)
0420     {
0421         if (EXSUCCEED==ret)
0422         {
0423             if (EXSUCCEED!=ndrx_cache_edb_commit(db, txn))
0424             {
0425                 NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to commit: %s", 
0426                     __func__, tpstrerror(tperrno));
0427                 ndrx_cache_edb_abort(db, txn);
0428             }
0429         }
0430         else
0431         {
0432             ndrx_cache_edb_abort(db, txn);
0433         }
0434     }
0435     
0436     return ret;
0437 }
0438 
0439 /**
0440  * Invalidate cache by expression
0441  * Key group is not affected by this as we do not have reference to keygroup or
0442  * recover group key from keyitem and perform delete accordingly?
0443  * @param cachedbnm
0444  * @param keyexpr
0445  * @cmds binary commands, here we are interested either regexp kill or 
0446  * plain single key delete
0447  * @nodeid nodeid posting the record, if it is ours then broadcast event, 
0448  * if ours then broadcast (if required)
0449  * @return 0>= - number of records deleted /EXFAIL (tperror set)
0450  */
0451 expublic long ndrx_cache_inval_by_expr(char *cachedbnm, char *keyexpr, short nodeid)
0452 {
0453     int ret = EXSUCCEED;
0454     EDB_txn *txn = NULL;
0455     ndrx_tpcache_db_t* db;
0456     int tran_started = EXFALSE;
0457     regex_t re;
0458     int re_compiled = EXFALSE;
0459     EDB_cursor *cursor;
0460     EDB_cursor_op op;
0461     EDB_val keydb, val;
0462     int deleted = 0;
0463     UBFH *p_ub = NULL;
0464     ndrx_tpcallcache_t* cache;
0465     ndrx_tpcache_data_t *exdata;
0466     int align;
0467     char *defer_free = NULL;
0468     
0469     NDRX_LOG(log_info, "delete cachedb [%s] by expression [%s] from node %d", 
0470             cachedbnm, keyexpr, nodeid);
0471     
0472     /* try compile regexp */
0473     if (EXSUCCEED!=ndrx_regcomp(&re, keyexpr))
0474     {
0475         NDRX_CACHE_TPERRORNOU(TPEINVAL, "Failed to compile regexp [%s]", keyexpr);
0476         EXFAIL_OUT(ret);
0477     }
0478     re_compiled = EXTRUE;
0479     /* find cachedb */
0480     
0481     if (NULL==(db=ndrx_cache_dbresolve(cachedbnm, NDRX_TPCACH_INIT_NORMAL)))
0482     {
0483         NDRX_CACHE_TPERRORNOU(TPENOENT, "Failed to get cache record for [%s]: %s", 
0484                 cachedbnm, tpstrerror(tperrno));
0485         EXFAIL_OUT(ret);
0486     }
0487     
0488     /* start transaction */
0489     if (EXSUCCEED!=(ret=ndrx_cache_edb_begin(db, &txn, 0)))
0490     {
0491         NDRX_CACHE_TPERROR(TPESYSTEM, "%s: failed to start tran", __func__);
0492         goto out;
0493     }
0494     
0495     tran_started = EXTRUE;
0496     
0497     
0498     /* loop over the database */
0499     if (EXSUCCEED!=ndrx_cache_edb_cursor_open(db, txn, &cursor))
0500     {
0501         NDRX_LOG(log_error, "Failed to open cursor");
0502         EXFAIL_OUT(ret);
0503     }
0504     
0505     /* loop over the db and match records  */
0506     
0507     op = EDB_FIRST;
0508     do
0509     {
0510         if (NULL!=defer_free)
0511         {
0512             NDRX_FREE(defer_free);
0513             defer_free = NULL;
0514         }
0515         
0516         if (EXSUCCEED!=(ret = ndrx_cache_edb_cursor_getfullkey(db, cursor, 
0517                 &keydb, &val, op, &align)))
0518         {
0519             if (EDB_NOTFOUND==ret)
0520             {
0521                 /* this is ok */
0522                 ret = EXSUCCEED;
0523                 break;
0524             }
0525             else
0526             {
0527                 NDRX_LOG(log_error, "Failed to loop over the [%s] db", cachedbnm);
0528                 break;
0529             }
0530         }
0531         
0532         if (align)
0533         {
0534             defer_free = val.mv_data;
0535         }
0536         
0537         /* test is last symbols EOS of data, if not this might cause core dump! */
0538         NDRX_CACHE_CHECK_DBKEY((&keydb), TPESYSTEM);
0539         exdata = (ndrx_tpcache_data_t *)val.mv_data;
0540         NDRX_CACHE_CHECK_DBDATA((&val), exdata, keydb.mv_data, TPESYSTEM);
0541 
0542         /* match regex on key */
0543         
0544         if (EXSUCCEED==ndrx_regexec(&re, keydb.mv_data))
0545         {
0546             char keygrp[NDRX_CACHE_KEY_MAX+1] = {EXEOS};
0547             
0548             NDRX_LOG(log_debug, "Key [%s] matched - deleting", keydb.mv_data);
0549             
0550             
0551             /* lookup cache and if in keygroup, then we shall recover the
0552              * data (prepare incoming) and send the record for 
0553              * ndrx_cache_keygrp_addupd(). This will ensure that we can process
0554              * expiry records correctly and clean up the group accordingly.
0555              */
0556             
0557             if (exdata->flags & NDRX_TPCACHE_TPCF_KEYITEMS)
0558             {
0559                 if (NULL==(cache = ndrx_cache_findtpcall_byidx(exdata->svcnm, 
0560                     exdata->cache_idx)))
0561                 {
0562                     NDRX_LOG(log_warn, "Failed to find tpcall cache - no group update!");
0563                 }
0564                 else if (!(cache->flags & NDRX_TPCACHE_TPCF_KEYITEMS))
0565                 {
0566                     NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Cache record with key indicated "
0567                             "as part of keygroup, but pointed cache not! "
0568                             "Have you changed cache config with saved data?");
0569                     EXFAIL_OUT(ret);
0570                 }
0571                 else
0572                 {
0573                     NDRX_LOG(log_debug, "Key is part of key-items -> recovering "
0574                             "buffer and building the key");
0575                     
0576                     if (EXSUCCEED!=(ret = ndrx_cache_keygrp_getkey_from_data(cache, 
0577                         exdata, keygrp, sizeof(keygrp))))
0578                     {
0579                         NDRX_LOG(log_error, "Failed to get keygroup key!");
0580                         goto out;
0581                     }
0582                 }
0583             }
0584            
0585             if (db->flags & NDRX_TPCACHE_FLAGS_KEYGRP)
0586             {
0587                if (EXSUCCEED!=(ret=ndrx_cache_keygrp_inval_by_key(db, keydb.mv_data, 
0588                        txn, NULL)))
0589                {
0590                    NDRX_LOG(log_error, "Failed to remove group record!");
0591                    EXFAIL_OUT(ret);
0592                }
0593             }
0594             if (EXSUCCEED!=ndrx_cache_edb_delfullkey (db, txn, &keydb, NULL))
0595             {
0596                 NDRX_CACHE_TPERRORNOU(TPESYSTEM, "%s: Failure where deleting record! Some one already deleted it?",
0597                     __func__);
0598                 NDRX_LOG(log_debug, "Failed to delete record by key [%s]", 
0599                         keydb.mv_data);
0600                 EXFAIL_OUT(ret);
0601             }
0602             
0603             /* OK now update group  */
0604             if (EXEOS!=keygrp[0])
0605             {
0606                 NDRX_LOG(log_debug, "Removing keyitem [%s] from keygroup [%s]",
0607                         keyexpr, keygrp);
0608                 
0609                 /* remove just key item... and continue */
0610                 if (EXSUCCEED!=(ret=ndrx_cache_keygrp_addupd(cache, 
0611                         NULL, 0, keydb.mv_data, keygrp, EXTRUE, txn)))
0612                 {
0613                     NDRX_LOG(log_error, "Failed to remove key [%s] from keygroup!");
0614                     goto out;
0615                 }
0616             } 
0617             
0618             deleted++;
0619         }
0620         else
0621         {
0622             NDRX_LOG(log_debug, "Key [%s] matched not matched [%s]", 
0623                     keydb.mv_data, keyexpr);
0624         }
0625         
0626         if (EDB_FIRST == op)
0627         {
0628             op = EDB_NEXT;
0629         }
0630         
0631     } while (EXSUCCEED==ret);
0632     
0633 
0634     if ( (db->flags & NDRX_TPCACHE_FLAGS_BCASTDEL) &&
0635             tpgetnodeid()==nodeid )
0636     {
0637         char cmd;
0638         NDRX_LOG(log_debug, "Same node -> broadcast event of drop");
0639         
0640         if (NULL==(p_ub = (UBFH *)tpalloc("UBF", NULL, 1024)))
0641         {
0642             NDRX_LOG(log_error, "Failed to allocate UBF buffer!");
0643             EXFAIL_OUT(ret);
0644         }
0645         
0646         /* Set command code (optional, actual command is encoded in event) */
0647         cmd = NDRX_CACHE_SVCMD_DELBYEXPR;
0648         if (EXSUCCEED!=Bchg(p_ub, EX_CACHE_CMD, 0, &cmd, 0L))
0649         {
0650             NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to set command code of "
0651                     "[%c] to UBF: %s", __func__, cmd, Bstrerror(Berror));
0652             EXFAIL_OUT(ret);
0653         }
0654         
0655         /* Set expression string, mandatory */
0656         if (EXSUCCEED!=Bchg(p_ub, EX_CACHE_OPEXPR, 0, keyexpr, 0L))
0657         {
0658             NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to set operation expression "
0659                     "[%s] to UBF: %s", __func__, keyexpr, Bstrerror(Berror));
0660             EXFAIL_OUT(ret);
0661         }
0662         
0663         
0664         if (EXSUCCEED!=ndrx_cache_broadcast(NULL, cachedbnm, (char *)p_ub, 0, 
0665                 NDRX_CACHE_BCAST_MODE_MSK, NDRX_TPCACHE_BCAST_DFLT,
0666                 0, 0, 0, 0))
0667         {
0668             NDRX_LOG(log_error, "Failed to post event of [%s] expression delete",
0669                     keyexpr);
0670             EXFAIL_OUT(ret);
0671         }
0672     }
0673     
0674 out:
0675 
0676     if (tran_started)
0677     {
0678         if (EXSUCCEED==ret)
0679         {
0680             if (EXSUCCEED!=ndrx_cache_edb_commit(db, txn))
0681             {
0682                 NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to commit: %s", 
0683                     __func__, tpstrerror(tperrno));
0684                 ndrx_cache_edb_abort(db, txn);
0685             }
0686         }
0687         else
0688         {
0689             ndrx_cache_edb_abort(db, txn);
0690         }
0691     }
0692 
0693     if (re_compiled)
0694     {
0695         ndrx_regfree(&re);
0696     }
0697 
0698     if (NULL!=p_ub)
0699     {
0700         tpfree((char *)p_ub);
0701     }
0702 
0703     if (defer_free!=NULL)
0704     {
0705         NDRX_FREE(defer_free);
0706     }
0707 
0708     NDRX_LOG(log_debug, "%s returns %d (deleted: %d)", __func__, ret, deleted);
0709     
0710     if (EXSUCCEED==ret)
0711     {
0712         return deleted;
0713     }
0714     else
0715     {
0716         return ret;
0717     }
0718 }
0719 
0720 /**
0721  * Invalidate by key (delete record too)
0722  * Also this one is not accessible for keygroup as no data reference for
0723  * building the key. Or we need to recover saved cache data and build key
0724  * from there?
0725  * @param cachedbnm
0726  * @param db_resolved do not lookup the hash, if already have db handler.
0727  * @param key note key must be copy to normal memory (not from db) as might cause
0728  * issues with ptr shifting of mdb record.
0729  * @param nodeid nodeid posting the record, if it is ours then broadcast event, 
0730  * if ours then broadcast (if required)
0731  * @param txn any transaction open (if not open process will open one and commit)
0732  * @return 0..1 - nr of recs deleted/EXFAIL (tperror set)
0733  */
0734 expublic int ndrx_cache_inval_by_key(char *cachedbnm, ndrx_tpcache_db_t* db_resolved, 
0735         char *key, short nodeid, EDB_txn *txn, int ext_tran)
0736 {
0737     int ret = EXSUCCEED;
0738     ndrx_tpcache_db_t* db;
0739     int tran_started = EXFALSE;
0740     UBFH *p_ub = NULL;
0741     int deleted = 0;
0742     char cmd;
0743     char keygrp[NDRX_CACHE_KEY_MAX+1] = {EXEOS};
0744     int align;
0745     char *defer_free = NULL;
0746     
0747     EDB_val keydb, val;
0748     ndrx_tpcallcache_t* cache;
0749     ndrx_tpcache_data_t *exdata;
0750     
0751     NDRX_LOG(log_info, "%s: Delete cache db [%s] record by key [%s] source node: [%hd]", 
0752             __func__, cachedbnm, key, nodeid);
0753     
0754     /* find cachedb */
0755     if (NULL!=db_resolved)
0756     {
0757         db = db_resolved;
0758     }
0759     else
0760     {
0761         if (NULL==(db=ndrx_cache_dbresolve(cachedbnm, NDRX_TPCACH_INIT_NORMAL)))
0762         {
0763             NDRX_CACHE_TPERRORNOU(TPENOENT, "Failed to get cache record for [%s]: %s", 
0764                     cachedbnm, tpstrerror(tperrno));
0765             EXFAIL_OUT(ret);
0766         }
0767     }
0768     
0769     /* start transaction */
0770     if (!ext_tran)
0771     {
0772         if (EXSUCCEED!=(ret=ndrx_cache_edb_begin(db, &txn, 0)))
0773         {
0774             NDRX_CACHE_TPERROR(TPESYSTEM, "%s: failed to start tran", __func__);
0775             goto out;
0776         }
0777         tran_started = EXTRUE;
0778     }
0779     
0780     /* we do not know anything about they record here, is it part of cache or
0781      * not?
0782      * So we need to perform lookup... check flags and update keygroup if
0783      * needed
0784      */
0785     
0786     if (EXSUCCEED==(ret=ndrx_cache_edb_get(db, txn, key, &val, EXFALSE, &align)))
0787     {
0788         /* validate db rec... */
0789         if (align)
0790         {
0791             defer_free = val.mv_data;
0792         }
0793         exdata = (ndrx_tpcache_data_t *) (char *)val.mv_data;
0794         NDRX_CACHE_CHECK_DBDATA((&val), exdata, keydb.mv_data, TPESYSTEM);
0795         
0796         /* get key group key */
0797         if (exdata->flags & NDRX_TPCACHE_TPCF_KEYITEMS)
0798         {
0799             NDRX_LOG(log_debug, "record is key item of group");
0800             if (NULL==(cache = ndrx_cache_findtpcall_byidx(exdata->svcnm, 
0801                 exdata->cache_idx)))
0802             {
0803                 NDRX_LOG(log_warn, "Failed to find tpcall cache - no group update!");
0804             }
0805             else if (!(cache->flags & NDRX_TPCACHE_TPCF_KEYITEMS))
0806             {
0807                 NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Cache record with key indicated "
0808                         "as part of keygroup, but pointed cache not! "
0809                         "Have you changed cache config with saved data?");
0810                 EXFAIL_OUT(ret);
0811             }
0812             else
0813             {
0814                 NDRX_LOG(log_debug, "Key is part of key-items -> recovering "
0815                         "buffer and building the key");
0816 
0817                 if (EXSUCCEED!=(ret = ndrx_cache_keygrp_getkey_from_data(cache, 
0818                     exdata, keygrp, sizeof(keygrp))))
0819                 {
0820                     NDRX_LOG(log_error, "Failed to get keygroup key!");
0821                     goto out;
0822                 }
0823             }
0824         }
0825         
0826         /* ok we might be a group record, this we need to remove child items... */
0827         
0828         if (db->flags & NDRX_TPCACHE_FLAGS_KEYGRP)
0829         {
0830             NDRX_LOG(log_debug, "Removing key group");
0831             if (EXSUCCEED!=(ret=ndrx_cache_keygrp_inval_by_key(db, key, txn, NULL)))
0832             {
0833                 NDRX_LOG(log_error, "Failed to remove group record!");
0834                 EXFAIL_OUT(ret);
0835             }
0836         }
0837         else
0838         {
0839             NDRX_LOG(log_error, "Removing rec by key [%s]", key);
0840             if (EXSUCCEED!=(ret=ndrx_cache_edb_del (db, txn, key, NULL)))
0841             {
0842                 if (ret!=EDB_NOTFOUND)
0843                 {
0844                     EXFAIL_OUT(ret);
0845                 }
0846             }
0847             else
0848             {
0849                  deleted = 1;
0850             }
0851         }
0852         
0853         /* OK now update group  */
0854         if (EXEOS!=keygrp[0])
0855         {
0856             NDRX_LOG(log_debug, "Removing keyitem [%s] from keygroup [%s]",
0857                     key, keygrp);
0858 
0859             /* remove just key item... and continue */
0860             if (EXSUCCEED!=(ret=ndrx_cache_keygrp_addupd(cache, 
0861                     NULL, 0, key, keygrp, EXTRUE, txn)))
0862             {
0863                 NDRX_LOG(log_error, "Failed to remove key [%s] from keygroup!");
0864                 goto out;
0865             }
0866         }
0867 
0868         
0869     }
0870     else if (ret!=EDB_NOTFOUND)
0871     {
0872         NDRX_LOG(log_error, "Failed to get DB record!");
0873         EXFAIL_OUT(ret);
0874     }
0875     ret=EXSUCCEED;
0876     
0877     /* continue anyway, we need a broadcast */
0878     
0879     if ( (db->flags & NDRX_TPCACHE_FLAGS_BCASTPUT) &&
0880             tpgetnodeid()==nodeid )
0881     {
0882         NDRX_LOG(log_debug, "Same node -> broadcast event of delete key");
0883         
0884         if (NULL==(p_ub = (UBFH *)tpalloc("UBF", NULL, 1024)))
0885         {
0886             NDRX_LOG(log_error, "Failed to allocate UBF buffer!");
0887             EXFAIL_OUT(ret);
0888         }
0889         
0890         /* Set command code (optional, actual command is encoded in event) */
0891         cmd = NDRX_CACHE_SVCMD_DELBYKEY;
0892         if (EXSUCCEED!=Bchg(p_ub, EX_CACHE_CMD, 0, &cmd, 0L))
0893         {
0894             NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to set command code of "
0895                     "[%c] to UBF: %s", __func__, cmd, Bstrerror(Berror));
0896             EXFAIL_OUT(ret);
0897         }
0898         
0899         /* Set expression string, mandatory */
0900         if (EXSUCCEED!=Bchg(p_ub, EX_CACHE_OPEXPR, 0, key, 0L))
0901         {
0902             NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to set operation expression "
0903                     "[%s] to UBF: %s", __func__, key, Bstrerror(Berror));
0904             EXFAIL_OUT(ret);
0905         }
0906         
0907         /* Broadcast NULL buffer event (ignore result) */
0908         if (EXSUCCEED!=ndrx_cache_broadcast(NULL, cachedbnm, (char *)p_ub, 0, 
0909                 NDRX_CACHE_BCAST_MODE_DKY,  NDRX_TPCACHE_BCAST_DFLT, 0, 0, 0, 0))
0910         {
0911             NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to broadcast: %s", 
0912                     __func__, tpstrerror(tperrno));
0913         }
0914     }
0915     
0916 out:
0917 
0918     if (tran_started)
0919     {
0920         if (EXSUCCEED==ret)
0921         {
0922             if (EXSUCCEED!=ndrx_cache_edb_commit(db, txn))
0923             {
0924                 NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to commit: %s", 
0925                     __func__, tpstrerror(tperrno));
0926                 ndrx_cache_edb_abort(db, txn);
0927             }
0928         }
0929         else
0930         {
0931             ndrx_cache_edb_abort(db, txn);
0932         }
0933     }
0934 
0935     if (NULL!=p_ub)
0936     {
0937         tpfree((char *)p_ub);
0938     }
0939 
0940     if (defer_free)
0941     {
0942         NDRX_FREE(defer_free);
0943     }
0944     
0945     return ret;
0946 }
0947 
0948 /**
0949  * Broadcast delete by key. No local deletes.
0950  * @param cachedbnm cache database name
0951  * @param key key to delete
0952  * @param nodeid cluster node id
0953  * @return EXSUCCEED/EXFAIL
0954  */
0955 expublic int ndrx_cache_inval_by_key_bcastonly(char *cachedbnm, char *key, short nodeid)
0956 {
0957     int ret = EXSUCCEED;
0958     UBFH *p_ub = NULL;
0959     char cmd;
0960 
0961     NDRX_LOG(log_debug, "Same node -> broadcast event of delete key");
0962 
0963     if (NULL==(p_ub = (UBFH *)tpalloc("UBF", NULL, 1024)))
0964     {
0965         NDRX_LOG(log_error, "Failed to allocate UBF buffer!");
0966         EXFAIL_OUT(ret);
0967     }
0968 
0969     /* Set command code (optional, actual command is encoded in event) */
0970     cmd = NDRX_CACHE_SVCMD_DELBYKEY;
0971     if (EXSUCCEED!=Bchg(p_ub, EX_CACHE_CMD, 0, &cmd, 0L))
0972     {
0973         NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to set command code of "
0974                 "[%c] to UBF: %s", __func__, cmd, Bstrerror(Berror));
0975         EXFAIL_OUT(ret);
0976     }
0977 
0978     /* Set expression string, mandatory */
0979     if (EXSUCCEED!=Bchg(p_ub, EX_CACHE_OPEXPR, 0, key, 0L))
0980     {
0981         NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to set operation expression "
0982                 "[%s] to UBF: %s", __func__, key, Bstrerror(Berror));
0983         EXFAIL_OUT(ret);
0984     }
0985 
0986     /* Broadcast NULL buffer event (ignore result) */
0987     if (EXSUCCEED!=ndrx_cache_broadcast(NULL, cachedbnm, (char *)p_ub, 0, 
0988             NDRX_CACHE_BCAST_MODE_DKY,  NDRX_TPCACHE_BCAST_DFLT, 0, 0, 0, 0))
0989     {
0990         NDRX_CACHE_TPERROR(TPESYSTEM, "%s: Failed to broadcast: %s", 
0991                 __func__, tpstrerror(tperrno));
0992     }
0993 out:
0994 
0995     if (NULL!=p_ub)
0996     {
0997         tpfree((char *)p_ub);
0998     }
0999 
1000     return ret;
1001 }
1002 
1003 
1004 /* vim: set ts=4 sw=4 et smartindent: */