Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief ATMI level cache, EDB access
0003  *   NOTE! To have aligned data access LMDB key and data must be aligned size.
0004  *   For some very long keys (how long?), the rule could change. In case of
0005  *   UBF buffers, data is also aligned. If adding new buffer types, probably
0006  *   we will need to align the data size too (for strings, carrays..)
0007  *
0008  * @file atmi_cache_edb.c
0009  */
0010 /* -----------------------------------------------------------------------------
0011  * Enduro/X Middleware Platform for Distributed Transaction Processing
0012  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0013  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0014  * This software is released under one of the following licenses:
0015  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0016  * See LICENSE file for full text.
0017  * -----------------------------------------------------------------------------
0018  * AGPL license:
0019  *
0020  * This program is free software; you can redistribute it and/or modify it under
0021  * the terms of the GNU Affero General Public License, version 3 as published
0022  * by the Free Software Foundation;
0023  *
0024  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0025  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0026  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0027  * for more details.
0028  *
0029  * You should have received a copy of the GNU Affero General Public License along 
0030  * with this program; if not, write to the Free Software Foundation, Inc.,
0031  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0032  *
0033  * -----------------------------------------------------------------------------
0034  * A commercial use license is available from Mavimax, Ltd
0035  * contact@mavimax.com
0036  * -----------------------------------------------------------------------------
0037  */
0038 
0039 /*---------------------------Includes-----------------------------------*/
0040 #include <stdlib.h>
0041 #include <stdio.h>
0042 #include <errno.h>
0043 #include <string.h>
0044 #include <ndrstandard.h>
0045 #include <atmi.h>
0046 #include <atmi_tls.h>
0047 #include <typed_buf.h>
0048 
0049 #include "thlock.h"
0050 #include "userlog.h"
0051 #include "utlist.h"
0052 #include "exregex.h"
0053 #include <exparson.h>
0054 #include <atmi_cache.h>
0055 #include <Exfields.h>
0056 /*---------------------------Externs------------------------------------*/
0057 /*---------------------------Macros-------------------------------------*/
0058 
0059 #ifdef EX_ALIGNMENT_FORCE
0060 
0061 #define DATA_ALIGN_DEF\
0062     ndrx_tpcache_data_t *aligndata;\
0063     int alignmiss;\
0064     char *tmp__;\
0065     int err__;
0066 
0067 #define DATA_ALIGN_DO\
0068     aligndata = (ndrx_tpcache_data_t *)data_out->mv_data;\
0069     alignmiss = ((unsigned long)((char *)&(aligndata->magic))) % EX_ALIGNMENT_BYTES;\
0070     *align = alignmiss;\
0071     /* shift bytes: */\
0072     if (alignmiss > 0)\
0073     {\
0074         if (NULL==(tmp__ = NDRX_MALLOC(data_out->mv_size)))\
0075         {\
0076             *align=0;\
0077             NDRX_LOG(log_error, "Failed malloc %d bytes: %s", data_out->mv_size, strerror(err__));\
0078             userlog("Failed malloc %d bytes: %s", data_out->mv_size, strerror(err__));\
0079             ret=err__;\
0080             goto out;\
0081         }\
0082         memcpy(tmp__, data_out->mv_data, data_out->mv_size);\
0083         data_out->mv_data = tmp__;\
0084     }
0085 #else
0086 
0087 #define DATA_ALIGN_DEF
0088 
0089 #define DATA_ALIGN_DO *align = 0;
0090 
0091 #endif
0092 
0093 
0094 #define KEY_ALIGN_DEF \
0095     char *keyalign = NULL;\
0096     int keyalignmod;\
0097     int keyallocsz;
0098 
0099 
0100 #define KEY_DO_ALIGN\
0101     /* Align the key */\
0102     keyalignmod = keydb.mv_size % EX_ALIGNMENT_BYTES;\
0103     if (keyalignmod > 0 )\
0104     {\
0105         int err;\
0106         keyallocsz = keydb.mv_size+ EX_ALIGNMENT_BYTES - keyalignmod;\
0107         keyalign = NDRX_CALLOC(1, keyallocsz);\
0108         err = errno;\
0109         if (NULL==keyalign)\
0110         {\
0111             NDRX_LOG(log_error, "Failed calloc %d bytes: %s", keyallocsz, strerror(err));\
0112             userlog("Failed calloc %d bytes: %s", keyallocsz, strerror(err));\
0113             EXFAIL_OUT(ret);\
0114         }\
0115         strcpy(keyalign, key);\
0116         keydb.mv_data = keyalign;\
0117         keydb.mv_size = keyallocsz;\
0118     }
0119 
0120 #define KEY_ALIGN_FREE\
0121     if (NULL!=keyalign)\
0122     {\
0123         NDRX_FREE(keyalign);\
0124     }
0125 
0126 /*---------------------------Enums--------------------------------------*/
0127 /*---------------------------Typedefs-----------------------------------*/
0128 /*---------------------------Globals------------------------------------*/
0129 /*---------------------------Statics------------------------------------*/
0130 /*---------------------------Prototypes---------------------------------*/
0131 
0132 /**
0133  * Compare cache entries
0134  * @param a
0135  * @param b
0136  * @return -1, 0 (equals), 1
0137  */
0138 expublic int ndrx_cache_cmp_fun(const EDB_val *a, const EDB_val *b)
0139 {
0140     ndrx_tpcache_data_t *ad = (ndrx_tpcache_data_t *)a->mv_data;
0141     ndrx_tpcache_data_t *bd = (ndrx_tpcache_data_t *)b->mv_data;
0142     int result = 0;
0143     
0144     
0145     if (ad->t > bd->t)
0146     {
0147         result = 1;
0148     }
0149     else if (ad->t < bd->t)
0150     {
0151         result = -1;
0152     }
0153     else
0154     {
0155         /* equals compare, microsec */
0156         
0157         if (ad->tusec > bd->tusec)
0158         {
0159             result = 1;
0160         }
0161         else if (ad->tusec < bd->tusec)
0162         {
0163             result = -1;
0164         }
0165         else
0166         {
0167             /* equals now decide from node id, the higher number wins */
0168             
0169             if (ad->nodeid > bd->nodeid)
0170             {
0171                 result = 1;
0172             }
0173             else if (ad->nodeid < bd->nodeid)
0174             {
0175                 result = -1;
0176             }
0177             else
0178             {
0179                 /* local node two records a the same time, so equals... */
0180                 result = 0;
0181             }
0182         }
0183     }
0184     
0185     return result;
0186 }
0187 
0188 /**
0189  * Begin MDB transaction
0190  * @param db db handler
0191  * @param txn transaction obj (out)
0192  * @param flags mdb flags
0193  * @return EXSUCCEED or edb error
0194  */
0195 expublic int ndrx_cache_edb_begin(ndrx_tpcache_db_t *db, EDB_txn **txn,
0196         unsigned int flags)
0197 {
0198     int ret = EXSUCCEED;
0199     
0200     if (EXSUCCEED!=(ret=edb_txn_begin(db->phy->env, NULL, flags, txn)))
0201     {
0202         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0203                 "Failed to begin transaction for [%s]: %s", 
0204                 db->cachedb, edb_strerror(ret));
0205         
0206         goto out;
0207     }
0208     
0209 out:
0210     return ret;
0211 }
0212 
0213 /**
0214  * Commit transaction
0215  * @param db db handler
0216  * @param txn transaction (in)
0217  * @return EXSUCCEED or edb error
0218  */
0219 expublic int ndrx_cache_edb_commit(ndrx_tpcache_db_t *db, EDB_txn *txn)
0220 {
0221     int ret = EXSUCCEED;
0222     
0223     if (EXSUCCEED!=(ret=edb_txn_commit(txn)))
0224     {
0225         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0226                 "Failed to commit transaction for [%s]: %s", 
0227                 db->cachedb, edb_strerror(ret));
0228         
0229         goto out;
0230     }
0231     
0232 out:
0233     return ret;
0234 }
0235 
0236 /**
0237  * Abort transaction
0238  * @param db db handler
0239  * @param txn transaction (in)
0240  * @return EXSUCCEED or edb error
0241  */
0242 expublic int ndrx_cache_edb_abort(ndrx_tpcache_db_t *db, EDB_txn *txn)
0243 {
0244     int ret = EXSUCCEED;
0245     
0246     edb_txn_abort(txn);
0247     
0248 out:
0249     return ret;
0250 }
0251 
0252 /**
0253  * Get data from DB with transaction.
0254  * @param db db handler
0255  * @param key string key
0256  * @param data_out data out obj data is valid till update or end of tran
0257  * @param seterror_not_found if EXTRUE, set error even record is not found
0258  * @return EXSUCCEED/edb error
0259  */
0260 expublic int ndrx_cache_edb_get(ndrx_tpcache_db_t *db, EDB_txn *txn, 
0261         char *key, EDB_val *data_out, int seterror_not_found, int *align)
0262 {
0263     int ret = EXSUCCEED;
0264     EDB_val keydb;
0265     KEY_ALIGN_DEF;
0266     DATA_ALIGN_DEF;
0267     
0268     /* TODO: Might want to think about aligned key sizes.... in some future
0269      * for making less data copies for aligned cpus only...
0270      */
0271     keydb.mv_data = key;
0272     keydb.mv_size = strlen(key)+1;
0273     
0274     /* Align the key */
0275     KEY_DO_ALIGN;
0276     
0277     if (EXSUCCEED!=(ret=edb_get(txn, db->dbi, &keydb, data_out)))
0278     {
0279         if (ret!=EDB_NOTFOUND)
0280         {
0281             NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0282                 "Failed to get data from db [%s] for key [%s]: %s", 
0283                 db->cachedb, key, edb_strerror(ret));
0284         }
0285         else
0286         {
0287             if (seterror_not_found)
0288             {
0289                 NDRX_CACHE_TPERRORNOU(TPENOENT, "Failed to get data from db "
0290                         "[%s] for key [%s]: %s", db->cachedb, key, edb_strerror(ret));
0291             }
0292             else
0293             {
0294                 NDRX_LOG(log_debug, "Failed to get data from db [%s] for key [%s]: %s", 
0295                     db->cachedb, key, edb_strerror(ret));
0296             }
0297         }
0298         goto out;
0299     }
0300     
0301     /* prepare the data with aligned option... */
0302     DATA_ALIGN_DO;
0303     
0304 out:
0305     
0306     KEY_ALIGN_FREE;
0307 
0308     return ret;
0309 }
0310 
0311 /**
0312  * Get data for cursor
0313  * @param db
0314  * @param cursor
0315  * @param key
0316  * @param data_out
0317  * @param op
0318  * @param align data offset in structure
0319  * @return
0320  */
0321 expublic int ndrx_cache_edb_cursor_get(ndrx_tpcache_db_t *db, EDB_cursor * cursor,
0322         char *key, EDB_val *data_out, EDB_cursor_op op, int *align)
0323 {
0324     int ret = EXSUCCEED;
0325     EDB_val keydb;
0326     
0327     KEY_ALIGN_DEF;
0328     DATA_ALIGN_DEF;
0329             
0330     keydb.mv_data = key;
0331     keydb.mv_size = strlen(key)+1;
0332     
0333     KEY_DO_ALIGN;
0334     
0335     if (EXSUCCEED!=(ret=edb_cursor_get(cursor, &keydb, data_out, op)))
0336     {
0337         if (ret!=EDB_NOTFOUND)
0338         {
0339             NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0340                 "Failed to get data from db [%s] for key [%s]: %s", 
0341                 db->cachedb, key, edb_strerror(ret));
0342         }
0343         else
0344         {
0345             NDRX_LOG(log_debug, "EOF [%s] for key [%s]: %s", 
0346                 db->cachedb, key, edb_strerror(ret));
0347         }
0348         goto out;
0349     }
0350     
0351     /* prepare the data with aligned option... */
0352     DATA_ALIGN_DO;
0353     
0354 out:
0355     
0356     KEY_ALIGN_FREE;
0357     return ret;
0358 }
0359 
0360 /**
0361  * Get data for cursor
0362  * @param db
0363  * @param cursor
0364  * @param key get by real key
0365  * @param data_out
0366  * @param op
0367  * @param align number of bytes to offset from start positions to the left
0368  *  so that data start on algined address
0369  * @return 
0370  */
0371 expublic int ndrx_cache_edb_cursor_getfullkey(ndrx_tpcache_db_t *db, EDB_cursor * cursor,
0372         EDB_val *keydb, EDB_val *data_out, EDB_cursor_op op, int *align)
0373 {
0374     int ret = EXSUCCEED;
0375     DATA_ALIGN_DEF;
0376     
0377     if (EXSUCCEED!=(ret=edb_cursor_get(cursor, keydb, data_out, op)))
0378     {
0379         if (ret!=EDB_NOTFOUND)
0380         {
0381             NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0382                 "%s: Failed to get data from db [%s]]: %s", 
0383                 __func__, db->cachedb, edb_strerror(ret));
0384         }
0385         else
0386         {
0387             NDRX_LOG(log_debug, "%s: EOF [%s]: %s", 
0388                 __func__, db->cachedb, edb_strerror(ret));
0389         }
0390         goto out;
0391     }
0392     /* prepare the data with aligned option... */
0393     DATA_ALIGN_DO;
0394 out:
0395     return ret;
0396 }
0397 
0398 /**
0399  * Set compare function
0400  * @param db
0401  * @param txn
0402  * @param cmp
0403  * @return 
0404  */
0405 expublic int ndrx_cache_edb_set_dupsort(ndrx_tpcache_db_t *db, EDB_txn *txn, 
0406             EDB_cmp_func *cmp)
0407 {
0408     int ret = EXSUCCEED;
0409     
0410     if (EXSUCCEED!=(ret=edb_set_dupsort(txn, db->dbi, cmp)))
0411     {
0412         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0413                 "Failed to set dupsort cmp func for db [%s] %p: %s", 
0414             db->cachedb, cmp, edb_strerror(ret));
0415     }
0416     
0417 out:
0418     return ret;
0419 }
0420 
0421 /**
0422  * Open cursor 
0423  * @param db
0424  * @param txn
0425  * @param cursor cursor out
0426  * @return 
0427  */
0428 expublic int ndrx_cache_edb_cursor_open(ndrx_tpcache_db_t *db, EDB_txn *txn, 
0429             EDB_cursor ** cursor)
0430 {
0431     int ret = EXSUCCEED;
0432     
0433     if (EXSUCCEED!=(ret=edb_cursor_open(txn, db->dbi, cursor)))
0434     {
0435         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0436                 "Failed to open cursor [%s]: %s", 
0437                 db->cachedb, edb_strerror(ret));
0438     }
0439     
0440 out:
0441     return ret;
0442 }
0443 
0444 /**
0445  * Delete db record full or particular
0446  * @param db handler
0447  * @param txn transaction
0448  * @param key key (string based)
0449  * @param data data to delete, can be NULL, then full delete. Only for duplicate recs
0450  * @return EXSUCCEED/EXFAIL/DBERR
0451  */
0452 expublic int ndrx_cache_edb_del (ndrx_tpcache_db_t *db, EDB_txn *txn, 
0453         char *key, EDB_val *data)
0454 {
0455     int ret = EXSUCCEED;
0456     EDB_val keydb;
0457     KEY_ALIGN_DEF;
0458     
0459     keydb.mv_data = key;
0460     keydb.mv_size = strlen(key)+1;
0461     
0462     KEY_DO_ALIGN;
0463     
0464             
0465     if (EXSUCCEED!=(ret=edb_del(txn, db->dbi, &keydb, data)))
0466     {
0467         if (ret!=EDB_NOTFOUND)
0468         {
0469             NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0470                 "Failed to delete from db [%s] for key [%s], data: %p: %s", 
0471                 db->cachedb, key, data, edb_strerror(ret));
0472         }
0473         else
0474         {
0475             NDRX_LOG(log_debug, "EOF [%s] for delete of key [%s] data: %p: %s", 
0476                 db->cachedb, key, data, edb_strerror(ret));
0477         }
0478     }
0479 out:
0480     
0481     KEY_ALIGN_FREE;
0482     return ret;
0483 }
0484 
0485 /**
0486  * Delete db record full or particular
0487  * @param db handler
0488  * @param txn transaction
0489  * @param key full 
0490  * @param data data to delete, can be NULL, then full delete. Only for duplicate recs
0491  * @return EXSUCCEED/EXFAIL/DBERR
0492  */
0493 expublic int ndrx_cache_edb_delfullkey (ndrx_tpcache_db_t *db, EDB_txn *txn, 
0494         EDB_val *keydb, EDB_val *data)
0495 {
0496     int ret = EXSUCCEED;
0497             
0498     if (EXSUCCEED!=(ret=edb_del(txn, db->dbi, keydb, data)))
0499     {
0500         if (ret!=EDB_NOTFOUND)
0501         {
0502             NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0503                 "Failed to delete from db [%s] for key [%s], data: %p: %s", 
0504                 db->cachedb, keydb->mv_data, data, edb_strerror(ret));
0505         }
0506         else
0507         {
0508             NDRX_LOG(log_debug, "EOF [%s] for delete of key [%s] data: %p: %s", 
0509                 db->cachedb, keydb->mv_data, data, edb_strerror(ret));
0510         }
0511     }
0512 out:
0513     return ret;
0514 }
0515 
0516 
0517 /**
0518  * Add data to database
0519  * @param db db hander
0520  * @param txn transaction
0521  * @param key string key
0522  * @param data data to put
0523  * @param flags LMDB flags
0524  * @return EXSUCCEED/LMDB err
0525  */
0526 expublic int ndrx_cache_edb_put (ndrx_tpcache_db_t *db, EDB_txn *txn, 
0527         char *key, EDB_val *data, unsigned int flags, int ignore_err)
0528 {
0529     int ret = EXSUCCEED;
0530     EDB_val keydb;
0531     KEY_ALIGN_DEF;
0532     
0533     keydb.mv_data = key;
0534     keydb.mv_size = strlen(key)+1;
0535     
0536     KEY_DO_ALIGN;
0537     
0538     if (EXSUCCEED!=(ret=edb_put(txn, db->dbi, &keydb, data, flags)))
0539     {
0540         if (ignore_err)
0541         {
0542             NDRX_CACHE_ERROR("Failed to to put to db [%s] key [%s], data: %p: %s", 
0543                 db->cachedb, key, data, edb_strerror(ret));
0544         }
0545         else
0546         {
0547             NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0548                 "Failed to to put to db [%s] key [%s], data: %p: %s", 
0549                 db->cachedb, key, data, edb_strerror(ret));
0550         }
0551     }
0552 out:
0553     
0554     KEY_ALIGN_FREE;
0555 
0556     return ret;
0557 }
0558 
0559 /**
0560  * Get database statistics
0561  * @param db db handlers
0562  * @param txn transaction
0563  * @param stat where to return infos
0564  * @return EXSUCCEED/EXFAIL
0565  */
0566 expublic int ndrx_cache_edb_stat (ndrx_tpcache_db_t *db, EDB_txn *txn, 
0567         EDB_stat * stat)
0568 {
0569     int ret = EXSUCCEED;
0570             
0571     if (EXSUCCEED!=(ret=edb_stat(txn, db->dbi, stat)))
0572     {
0573         NDRX_CACHE_TPERROR(ndrx_cache_maperr(ret), 
0574             "Failed to stat [%s] db: %s", 
0575             db->cachedb, edb_strerror(ret));
0576     }
0577 out:
0578     return ret;
0579 }
0580 
0581 /* vim: set ts=4 sw=4 et smartindent: */