LCOV - code coverage report
Current view: top level - funk - test_funk_common.hpp (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 257 316 81.3 %
Date: 2025-10-06 04:40:49 Functions: 22 27 81.5 %

          Line data    Source code
       1             : #include "fd_funk.h"
       2             : #include <map>
       3             : #include <vector>
       4             : #include <set>
       5             : #include <algorithm>
       6             : #include <stdlib.h>
       7             : #include <assert.h>
       8             : #include <unistd.h>
       9             : #include <sys/stat.h>
      10             : 
      11             : static const ulong ROOT_KEY     = ULONG_MAX;
      12             : static const ulong MAX_TXNS     = 100;
      13             : static const ulong MAX_CHILDREN = 100;
      14             : static const uint MAX_PARTS     = 8;
      15             : 
      16             : struct fake_rec {
      17             :     ulong _key;
      18             :     std::vector<long> _data;
      19             :     bool _erased = false;
      20             :     bool _touched = false;
      21             :     static std::set<fake_rec*> _all;
      22             : 
      23             :     fake_rec() = delete;
      24     1322751 :     fake_rec(ulong key) : _key(key) {
      25     1322751 :       assert(_all.count(this) == 0);
      26     1322751 :       _all.insert(this);
      27     1322751 :     }
      28     1322751 :     ~fake_rec() {
      29     1322751 :       assert(_all.count(this) == 1);
      30     1322751 :       _all.erase(this);
      31     1322751 :     }
      32             : 
      33     1322751 :     static fake_rec * make_random() {
      34     1322751 :       auto * rec = new fake_rec(((ulong)lrand48())%MAX_CHILDREN);
      35     1322751 :       auto len = ((ulong)lrand48())%8UL;
      36     1322751 :       rec->_data.resize(len);
      37     5947251 :       for (ulong i = 0; i < len; ++i)
      38     4624500 :         rec->_data[i] = lrand48();
      39     1322751 :       return rec;
      40     1322751 :     }
      41             : 
      42     1545780 :     fd_funk_rec_key_t real_id() const {
      43     1545780 :       fd_funk_rec_key_t i;
      44     1545780 :       memset(&i, 0, sizeof(i));
      45     1545780 :       i.ul[0] = _key;
      46     1545780 :       return i;
      47     1545780 :     }
      48             : 
      49    33861528 :     ulong size() const {
      50    33861528 :       return _data.size()*sizeof(long);
      51    33861528 :     }
      52             : 
      53    16302324 :     const uchar* data() const {
      54    16302324 :       return (const uchar*)_data.data();
      55    16302324 :     }
      56             : };
      57             : 
      58             : std::set<fake_rec*> fake_rec::_all;
      59             : 
      60             : struct fake_txn {
      61             :     ulong _key;
      62             :     std::vector<fake_rec*> _recs;
      63             :     std::map<ulong,fake_txn*> _children;
      64             :     fake_txn * _parent = NULL;
      65             :     bool _touched = false;
      66             : 
      67      240003 :     fake_txn(ulong key) : _key(key) { }
      68      240003 :     ~fake_txn() {
      69     1170216 :       for (auto i : _recs) {
      70     1170216 :         delete i;
      71     1170216 :       }
      72      240003 :     }
      73             : 
      74     2022783 :     fd_funk_txn_xid_t real_id() const {
      75     2022783 :       fd_funk_txn_xid_t i;
      76     2022783 :       memset(&i, 0, sizeof(i));
      77     2022783 :       i.ul[0] = _key;
      78     2022783 :       return i;
      79     2022783 :     }
      80             : 
      81     1459506 :     bool insert(fake_rec* rec) {
      82     1459506 :       for (auto i : _recs)
      83    15072126 :         if( i->_key == rec->_key ) {
      84       65871 :           delete rec;
      85       65871 :           return false; /* Error */
      86       65871 :         }
      87     1393635 :       auto sz = _recs.size();
      88     1393635 :       _recs.resize(sz+1);
      89     1393635 :       _recs[sz] = rec;
      90     1393635 :       return true;
      91     1459506 :     }
      92             : };
      93             : 
      94             : struct fake_funk {
      95             :     fd_wksp_t * _wksp;
      96             :     fd_funk_t _real[1];
      97             :     std::map<ulong,fake_txn*> _txns;
      98             :     ulong _lastxid = 0;
      99             : 
     100           3 :     fake_funk(int * argc, char *** argv) {
     101           3 :       fd_boot( argc, argv );
     102             : 
     103           3 :       ulong txn_max = 128;
     104           3 :       uint  rec_max = 1<<16;
     105           3 :       ulong numa_idx = fd_shmem_numa_idx( 0 );
     106           3 :       _txns[ROOT_KEY] = new fake_txn(ROOT_KEY);
     107             : 
     108           3 :       _wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, 1U, fd_shmem_cpu_idx( numa_idx ), "wksp", 0UL );
     109           3 :       void * mem = fd_wksp_alloc_laddr( _wksp, fd_funk_align(), fd_funk_footprint( txn_max, rec_max ), FD_FUNK_MAGIC );
     110           3 :       FD_TEST( fd_funk_join( _real, fd_funk_new( mem, 1, 1234U, txn_max, rec_max ) ) );
     111           3 :     }
     112           3 :     ~fake_funk() {
     113           3 :       for (auto i : _txns)
     114          81 :         delete i.second;
     115           3 :       for( auto i : fake_rec::_all )
     116           0 :         FD_LOG_NOTICE(( "leaked record 0x%lx!", (ulong)i ));
     117             : 
     118           3 :     }
     119             : 
     120     1560000 :     fake_txn * pick_unfrozen_txn() {
     121     1560000 :       fake_txn* list[MAX_TXNS];
     122     1560000 :       uint listlen = 0;
     123     1560000 :       for (auto i : _txns)
     124    34015860 :         if (i.second->_children.size() == 0)
     125    17090100 :           list[listlen++] = i.second;
     126     1560000 :       return list[((uint)lrand48())%listlen];
     127     1560000 :     }
     128             : 
     129           0 :     fd_funk_txn_xid_t const * get_real_txn(fake_txn * txn) {
     130           0 :       if (txn->_key == ROOT_KEY)
     131           0 :         return fd_funk_last_publish( _real );
     132           0 :       fd_funk_txn_map_t * txn_map = fd_funk_txn_map( _real );
     133           0 :       auto xid = txn->real_id();
     134           0 :       fd_funk_txn_t * rtxn = fd_funk_txn_query(&xid, txn_map);
     135           0 :       if( !rtxn ) return fd_funk_last_publish( _real );
     136           0 :       return &rtxn->xid;
     137           0 :     }
     138             : 
     139     1260000 :     void random_insert() {
     140     1260000 :       fake_txn * txn = pick_unfrozen_txn();
     141     1260000 :       if( txn->_recs.size() == MAX_CHILDREN ) return;
     142     1256880 :       fake_rec * rec = NULL;
     143     1322751 :       do {
     144     1322751 :         rec = fake_rec::make_random();
     145             :         /* Prevent duplicate keys */
     146     1322751 :       } while (!txn->insert(rec));
     147             : 
     148     1256880 :       auto key = rec->real_id();
     149     1256880 :       fd_funk_rec_prepare_t prepare[1];
     150     1256880 :       fd_funk_rec_t * rec2 = fd_funk_rec_prepare(_real, get_real_txn(txn), &key, prepare, NULL);
     151     1256880 :       void * val = fd_funk_val_truncate(rec2, fd_funk_alloc( _real ), _wksp, 0UL, rec->size(), NULL);
     152     1256880 :       memcpy(val, rec->data(), rec->size());
     153     1256880 :       fd_funk_rec_publish( _real, prepare );
     154     1256880 :       assert(fd_funk_val_sz(rec2) == rec->size());
     155     1256880 :     }
     156             : 
     157      300000 :     void random_remove() {
     158      300000 :       fake_txn * txn = pick_unfrozen_txn();
     159      300000 :       auto& recs = txn->_recs;
     160      300000 :       fake_rec* list[MAX_CHILDREN];
     161      300000 :       uint listlen = 0;
     162      300000 :       for (auto i : recs)
     163     2200485 :         if (!i->_erased)
     164     1899756 :           list[listlen++] = i;
     165      300000 :       if (!listlen) return;
     166      288900 :       auto* rec = list[((uint)lrand48())%listlen];
     167             : 
     168      288900 :       auto key = rec->real_id();
     169      288900 :       assert(fd_funk_rec_remove(_real, get_real_txn(txn), &key, NULL) == FD_FUNK_SUCCESS);
     170             : 
     171      288900 :       rec->_erased = true;
     172      288900 :       rec->_data.clear();
     173      288900 :     }
     174             : 
     175      240000 :     void random_new_txn() {
     176      240000 :       if (_txns.size() == MAX_TXNS)
     177           0 :         return;
     178             : 
     179      240000 :       fake_txn* list[MAX_TXNS];
     180      240000 :       uint listlen = 0;
     181      240000 :       for (auto i : _txns)
     182     3869550 :         list[listlen++] = i.second;
     183      240000 :       auto * parent = list[((uint)lrand48())%listlen];
     184             : 
     185      240000 :       ulong key = ++_lastxid;
     186      240000 :       auto * txn = _txns[key] = new fake_txn(key);
     187             : 
     188      240000 :       txn->_parent = parent;
     189      240000 :       parent->_children[key] = txn;
     190             : 
     191      240000 :       auto xid = txn->real_id();
     192      240000 :       fd_funk_txn_prepare(_real, get_real_txn(parent), &xid);
     193      240000 :     }
     194             : 
     195      210396 :     void fake_cancel_family(fake_txn* txn) {
     196      210396 :       assert(txn->_key != ROOT_KEY);
     197      358887 :       while (!txn->_children.empty())
     198      148491 :         fake_cancel_family(txn->_children.begin()->second);
     199      210396 :       txn->_parent->_children.erase(txn->_key);
     200      210396 :       _txns.erase(txn->_key);
     201      210396 :       delete txn;
     202      210396 :     }
     203             : 
     204       29526 :     void fake_publish_to_parent(fake_txn* txn) {
     205             :       // Move records into parent
     206       29526 :       auto* parent = txn->_parent;
     207      136755 :       for (auto i : txn->_recs) {
     208      136755 :         uint p = 0;
     209     4416702 :         for (auto j : parent->_recs) {
     210     4416702 :           if( i->_key == j->_key ) {
     211       86664 :             delete j;
     212       86664 :             parent->_recs.erase(parent->_recs.begin()+p);
     213       86664 :             break;
     214       86664 :           }
     215     4330038 :           p++;
     216     4330038 :         }
     217      136755 :         parent->insert(i);
     218      136755 :       }
     219       29526 :       txn->_recs.clear();
     220             : 
     221             :       // Cancel siblings
     222       85431 :       for (;;) {
     223       85431 :         bool repeat = false;
     224       85431 :         for (auto i : parent->_children)
     225      119310 :           if (txn != i.second) {
     226       55905 :             fake_cancel_family(i.second);
     227       55905 :             repeat = true;
     228       55905 :             break;
     229       55905 :           }
     230       85431 :         if (!repeat) break;
     231       85431 :       }
     232       29526 :       assert(parent->_children.size() == 1 && parent->_children[txn->_key] == txn);
     233             : 
     234             :       // Move children up
     235       29526 :       parent->_children.clear();
     236       44256 :       for (auto i : txn->_children) {
     237       44256 :         auto* child = i.second;
     238       44256 :         child->_parent = parent;
     239       44256 :         parent->_children[child->_key] = child;
     240       44256 :       }
     241             : 
     242       29526 :       _txns.erase(txn->_key);
     243       29526 :       delete txn;
     244       29526 :     }
     245             : 
     246       17526 :     void fake_publish(fake_txn* txn) {
     247       17526 :       assert(txn->_key != ROOT_KEY);
     248       17526 :       if (txn->_parent->_key != ROOT_KEY)
     249       11526 :         fake_publish(txn->_parent);
     250       17526 :       assert(txn->_parent->_key == ROOT_KEY);
     251       17526 :       fake_publish_to_parent(txn);
     252       17526 :     }
     253             : 
     254           0 :     void random_publish() {
     255           0 :       fake_txn* list[MAX_TXNS];
     256           0 :       uint listlen = 0;
     257           0 :       for (auto i : _txns)
     258           0 :         if (i.second->_key != ROOT_KEY)
     259           0 :           list[listlen++] = i.second;
     260           0 :       if (!listlen) return;
     261           0 :       auto * txn = list[((uint)lrand48())%listlen];
     262           0 : 
     263           0 :       assert(fd_funk_txn_publish(_real, get_real_txn(txn)) > 0);
     264           0 : 
     265           0 :       // Simulate publication
     266           0 :       fake_publish(txn);
     267           0 :     }
     268             : 
     269           0 :     void random_publish_into_parent() {
     270           0 :       fake_txn* list[MAX_TXNS];
     271           0 :       uint listlen = 0;
     272           0 :       for (auto i : _txns)
     273           0 :         if (i.second->_key != ROOT_KEY)
     274           0 :           list[listlen++] = i.second;
     275           0 :       if (!listlen) {
     276           0 :         return;
     277           0 :       }
     278           0 :       auto * txn = list[((uint)lrand48())%listlen];
     279           0 : 
     280           0 :       fd_funk_txn_publish_into_parent(_real, get_real_txn(txn));
     281           0 : 
     282           0 :       // Simulate publication
     283           0 :       fake_publish_to_parent(txn);
     284           0 :     }
     285             : 
     286           0 :     void random_cancel() {
     287           0 :       fake_txn* list[MAX_TXNS];
     288           0 :       uint listlen = 0;
     289           0 :       for (auto i : _txns)
     290           0 :         if (i.second->_key != ROOT_KEY)
     291           0 :           list[listlen++] = i.second;
     292           0 :       if (!listlen) return;
     293           0 :       auto * txn = list[((uint)lrand48())%listlen];
     294           0 : 
     295           0 :       assert(fd_funk_txn_cancel(_real, get_real_txn(txn)) > 0);
     296           0 : 
     297           0 :       // Simulate cancel
     298           0 :       fake_cancel_family(txn);
     299           0 :     }
     300             : 
     301      102003 :     void verify() {
     302             : #ifdef FD_FUNK_HANDHOLDING
     303             :       assert(fd_funk_verify(_real) == FD_FUNK_SUCCESS);
     304             : #endif
     305             : 
     306     1949829 :       for (auto i : _txns) {
     307     1949829 :         assert(i.first == i.second->_key);
     308    18806709 :         for (auto j : i.second->_recs) {
     309    18806709 :           j->_touched = false;
     310    18806709 :         }
     311     1949829 :       }
     312             : 
     313      102003 :       fd_funk_all_iter_t iter[1];
     314    18908712 :       for( fd_funk_all_iter_new( _real, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
     315    18806709 :         fd_funk_rec_t const * rec = fd_funk_all_iter_ele_const( iter );
     316    18806709 :         auto const * xid = fd_funk_rec_xid( rec );
     317    18806709 :         auto i = _txns.find(xid->ul[0]);
     318             : 
     319    18806709 :         assert(i != _txns.end());
     320    18806709 :         auto const * key = fd_funk_rec_key( rec );
     321    18806709 :         auto& recs = i->second->_recs;
     322   561845241 :         auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     323    18806709 :         assert(j != recs.end());
     324    18806709 :         auto * rec2 = *j;
     325    18806709 :         if (rec2->_erased) {
     326     3761265 :           assert(rec->flags & FD_FUNK_REC_FLAG_ERASE);
     327     3761265 :           assert(fd_funk_val_sz(rec) == 0);
     328    15045444 :         } else {
     329    15045444 :           assert(!(rec->flags & FD_FUNK_REC_FLAG_ERASE));
     330    15045444 :           assert(fd_funk_val_sz(rec) == rec2->size());
     331    15045444 :           assert(memcmp(fd_funk_val(rec, _wksp), rec2->data(), rec2->size()) == 0);
     332    15045444 :         }
     333             : 
     334    18806709 :         fd_funk_txn_map_t * txn_map = fd_funk_txn_map( _real );
     335    18806709 :         fd_funk_txn_t * txn = fd_funk_txn_query( xid, txn_map );
     336    18806709 :         if( !txn ) xid = fd_funk_last_publish( _real );
     337    18806709 :         fd_funk_rec_query_t query[1];
     338    18806709 :         auto* rec3 = fd_funk_rec_query_try_global(_real, xid, rec->pair.key, NULL, query);
     339    18806709 :         if( ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) )
     340    18806709 :           assert(rec3 == NULL);
     341    15045444 :         else
     342    18806709 :           assert(rec == rec3);
     343    18806709 :         assert(!fd_funk_rec_query_test( query ));
     344             : 
     345    18806709 :         assert(!rec2->_touched);
     346    18806709 :         rec2->_touched = true;
     347    18806709 :       }
     348             : 
     349     1949829 :       for (auto i : _txns) {
     350    18806709 :         for (auto j : i.second->_recs) {
     351    18806709 :           assert(j->_touched || j->_erased);
     352    18806709 :         }
     353     1949829 :       }
     354             : 
     355     1949829 :       for (auto i : _txns) {
     356     1949829 :         auto * txn = i.second;
     357     1949829 :         assert(i.first == txn->_key);
     358     1949829 :         if (txn->_key == ROOT_KEY) {
     359      102003 :           assert(txn->_parent == NULL);
     360     1847826 :         } else {
     361     1847826 :           assert(txn->_parent->_children.find(txn->_key)->second == txn);
     362     1847826 :         }
     363     1949829 :         txn->_touched = false;
     364     1949829 :       }
     365             : 
     366      102003 :       {
     367             :         // Root transaction
     368      102003 :         auto * txn2 = _txns[ROOT_KEY];
     369      102003 :         assert(!txn2->_touched);
     370      102003 :         txn2->_touched = true;
     371             : 
     372      102003 :         auto& recs = txn2->_recs;
     373      102003 :         for( auto const * rec = fd_funk_txn_first_rec(_real, NULL);
     374      102003 :              rec;
     375      102003 :              rec = fd_funk_txn_next_rec(_real, rec) ) {
     376           0 :           auto const * key = fd_funk_rec_key( rec );
     377           0 :           auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     378           0 :           assert(j != recs.end());
     379           0 :         }
     380      102003 :       }
     381             : 
     382      102003 :       fd_funk_txn_pool_t * txn_pool = fd_funk_txn_pool( _real );
     383             : 
     384      102003 :       fd_funk_txn_all_iter_t txn_iter[1];
     385     1949829 :       for( fd_funk_txn_all_iter_new( _real, txn_iter ); !fd_funk_txn_all_iter_done( txn_iter ); fd_funk_txn_all_iter_next( txn_iter ) ) {
     386     1847826 :         fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
     387             : 
     388     1847826 :         auto i = _txns.find(txn->xid.ul[0]);
     389     1847826 :         assert(i != _txns.end());
     390     1847826 :         auto * txn2 = i->second;
     391     1847826 :         assert(!txn2->_touched);
     392     1847826 :         txn2->_touched = true;
     393             : 
     394     1847826 :         auto * parent = fd_funk_txn_parent(txn, txn_pool);
     395     1847826 :         if (parent == NULL)
     396     1847826 :           assert(ROOT_KEY == txn2->_parent->_key);
     397     1504497 :         else
     398     1847826 :           assert(parent->xid.ul[0] == txn2->_parent->_key);
     399             : 
     400     1847826 :         auto& recs = txn2->_recs;
     401     1847826 :         for( auto const * rec = fd_funk_txn_first_rec(_real, txn);
     402    10482315 :              rec;
     403     8634489 :              rec = fd_funk_txn_next_rec(_real, rec) ) {
     404     8634489 :           auto const * key = fd_funk_rec_key( rec );
     405    48779472 :           auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     406     8634489 :           assert(j != recs.end());
     407     8634489 :         }
     408     1847826 :       }
     409             : 
     410     1949829 :       for (auto i : _txns) {
     411             :         assert(i.second->_touched);
     412     1949829 :       }
     413      102003 :     }
     414             : };

Generated by: LCOV version 1.14