LCOV - code coverage report
Current view: top level - funk - test_funk_common.hpp (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 283 317 89.3 %
Date: 2025-08-05 05:04:49 Functions: 25 27 92.6 %

          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 long ROOT_KEY = 0;
      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     1324362 :     fake_rec(ulong key) : _key(key) {
      25     1324362 :       assert(_all.count(this) == 0);
      26     1324362 :       _all.insert(this);
      27     1324362 :     }
      28     1324362 :     ~fake_rec() {
      29     1324362 :       assert(_all.count(this) == 1);
      30     1324362 :       _all.erase(this);
      31     1324362 :     }
      32             : 
      33     1324362 :     static fake_rec * make_random() {
      34     1324362 :       auto * rec = new fake_rec(((ulong)lrand48())%MAX_CHILDREN);
      35     1324362 :       auto len = ((ulong)lrand48())%8UL;
      36     1324362 :       rec->_data.resize(len);
      37     5948328 :       for (ulong i = 0; i < len; ++i)
      38     4623966 :         rec->_data[i] = lrand48();
      39     1324362 :       return rec;
      40     1324362 :     }
      41             : 
      42     1545966 :     fd_funk_rec_key_t real_id() const {
      43     1545966 :       fd_funk_rec_key_t i;
      44     1545966 :       memset(&i, 0, sizeof(i));
      45     1545966 :       i.ul[0] = _key;
      46     1545966 :       return i;
      47     1545966 :     }
      48             : 
      49    33917163 :     ulong size() const {
      50    33917163 :       return _data.size()*sizeof(long);
      51    33917163 :     }
      52             : 
      53    16329843 :     const uchar* data() const {
      54    16329843 :       return (const uchar*)_data.data();
      55    16329843 :     }
      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     1171947 :       for (auto i : _recs) {
      70     1171947 :         delete i;
      71     1171947 :       }
      72      240003 :     }
      73             : 
      74     2023092 :     fd_funk_txn_xid_t real_id() const {
      75     2023092 :       fd_funk_txn_xid_t i;
      76     2023092 :       memset(&i, 0, sizeof(i));
      77     2023092 :       i.ul[0] = _key;
      78     2023092 :       return i;
      79     2023092 :     }
      80             : 
      81     1459113 :     bool insert(fake_rec* rec) {
      82     1459113 :       for (auto i : _recs)
      83    15016125 :         if( i->_key == rec->_key ) {
      84       66885 :           delete rec;
      85       66885 :           return false; /* Error */
      86       66885 :         }
      87     1392228 :       auto sz = _recs.size();
      88     1392228 :       _recs.resize(sz+1);
      89     1392228 :       _recs[sz] = rec;
      90     1392228 :       return true;
      91     1459113 :     }
      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             : #ifdef FUNK_RECONNECT_TEST
     109             :       struct stat x;
     110             :       if( stat("/mnt/.fd/.gigantic/reconnect_test", &x) == 0) {
     111             :         void * shmem = fd_shmem_join( "reconnect_test", FD_SHMEM_JOIN_MODE_READ_WRITE, NULL, NULL, NULL, 1 ); /* logs details */
     112             :         FD_TEST( shmem != NULL );
     113             :         _wksp = fd_wksp_join( shmem );
     114             :         FD_TEST( _wksp != NULL );
     115             :         FD_TEST( fd_wksp_owner( _wksp ) == ULONG_MAX );
     116             :         ulong tag = FD_FUNK_MAGIC;
     117             :         fd_wksp_tag_query_info_t info;
     118             :         FD_TEST( fd_wksp_tag_query( _wksp, &tag, 1, &info, 1 ) > 0 );
     119             :         assert(info.tag == tag);
     120             :         FD_TEST( fd_funk_purify( (uchar*)shmem + info.gaddr_lo ) == FD_FUNK_SUCCESS );
     121             :         FD_TEST( fd_funk_join( _real, (uchar*)shmem + info.gaddr_lo ) );
     122             :         fd_funk_all_iter_t iter[1];
     123             :         for( fd_funk_all_iter_new( _real, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
     124             :           fd_funk_rec_t const * rec = fd_funk_all_iter_ele_const( iter );
     125             :           void * val = fd_funk_val( rec, _wksp );
     126             :           ulong sz = fd_funk_val_sz( rec );
     127             :           fake_rec * rec2 = new fake_rec( fd_funk_rec_key( rec )->ul[0] );
     128             :           rec2->_data.resize( sz/8UL );
     129             :           memcpy( rec2->_data.data(), val, sz );
     130             :           _txns[ROOT_KEY]->_recs.push_back( rec2 );
     131             :         }
     132             : 
     133             :       } else {
     134             :         FD_TEST( !fd_shmem_create( "reconnect_test", FD_SHMEM_GIGANTIC_PAGE_SZ, 1U, fd_shmem_cpu_idx( numa_idx ), 0600 ) );
     135             :         void * shmem = fd_shmem_join( "reconnect_test", FD_SHMEM_JOIN_MODE_READ_WRITE, NULL, NULL, NULL, 1 ); /* logs details */
     136             :         void * wkspmem = fd_wksp_new( shmem, "reconect_test", 0U, 1024, FD_SHMEM_GIGANTIC_PAGE_SZ ); /* logs details */
     137             :         _wksp = fd_wksp_join( wkspmem );
     138             :         void * mem = fd_wksp_alloc_laddr( _wksp, fd_funk_align(), fd_funk_footprint( txn_max, rec_max ), FD_FUNK_MAGIC );
     139             :         FD_TEST( fd_funk_join( _real, fd_funk_new( mem, 1, 1234U, txn_max, rec_max ) ) );
     140             :       }
     141             : #else
     142           3 :       _wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, 1U, fd_shmem_cpu_idx( numa_idx ), "wksp", 0UL );
     143           3 :       void * mem = fd_wksp_alloc_laddr( _wksp, fd_funk_align(), fd_funk_footprint( txn_max, rec_max ), FD_FUNK_MAGIC );
     144           3 :       FD_TEST( fd_funk_join( _real, fd_funk_new( mem, 1, 1234U, txn_max, rec_max ) ) );
     145           3 : #endif
     146           3 :     }
     147           3 :     ~fake_funk() {
     148           3 :       for (auto i : _txns)
     149          42 :         delete i.second;
     150           3 :       for( auto i : fake_rec::_all )
     151           0 :         FD_LOG_NOTICE(( "leaked record 0x%lx!", (ulong)i ));
     152             : 
     153           3 :     }
     154             : 
     155     1560000 :     fake_txn * pick_unfrozen_txn() {
     156     1560000 :       fake_txn* list[MAX_TXNS];
     157     1560000 :       uint listlen = 0;
     158     1560000 :       for (auto i : _txns)
     159    33998520 :         if (i.second->_children.size() == 0)
     160    17102070 :           list[listlen++] = i.second;
     161     1560000 :       return list[((uint)lrand48())%listlen];
     162     1560000 :     }
     163             : 
     164     1809966 :     fd_funk_txn_t * get_real_txn(fake_txn * txn) {
     165     1809966 :       if (txn->_key == ROOT_KEY)
     166       26874 :         return NULL;
     167     1783092 :       fd_funk_txn_map_t * txn_map = fd_funk_txn_map( _real );
     168     1783092 :       auto xid = txn->real_id();
     169     1783092 :       return fd_funk_txn_query(&xid, txn_map);
     170     1809966 :     }
     171             : 
     172     1260000 :     void random_insert() {
     173     1260000 :       fake_txn * txn = pick_unfrozen_txn();
     174     1260000 :       if( txn->_recs.size() == MAX_CHILDREN ) return;
     175     1257477 :       fake_rec * rec = NULL;
     176     1324362 :       do {
     177     1324362 :         rec = fake_rec::make_random();
     178             :         /* Prevent duplicate keys */
     179     1324362 :       } while (!txn->insert(rec));
     180             : 
     181     1257477 :       fd_funk_txn_t * txn2 = get_real_txn(txn);
     182     1257477 :       auto key = rec->real_id();
     183     1257477 :       fd_funk_rec_prepare_t prepare[1];
     184     1257477 :       fd_funk_rec_t * rec2 = fd_funk_rec_prepare(_real, txn2, &key, prepare, NULL);
     185     1257477 :       void * val = fd_funk_val_truncate(rec2, fd_funk_alloc( _real ), _wksp, 0UL, rec->size(), NULL);
     186     1257477 :       memcpy(val, rec->data(), rec->size());
     187     1257477 :       fd_funk_rec_publish( _real, prepare );
     188     1257477 :       assert(fd_funk_val_sz(rec2) == rec->size());
     189     1257477 :     }
     190             : 
     191      300000 :     void random_remove() {
     192      300000 :       fake_txn * txn = pick_unfrozen_txn();
     193      300000 :       auto& recs = txn->_recs;
     194      300000 :       fake_rec* list[MAX_CHILDREN];
     195      300000 :       uint listlen = 0;
     196      300000 :       for (auto i : recs)
     197     2205690 :         if (!i->_erased)
     198     1903635 :           list[listlen++] = i;
     199      300000 :       if (!listlen) return;
     200      288489 :       auto* rec = list[((uint)lrand48())%listlen];
     201             : 
     202      288489 :       fd_funk_txn_t * txn2 = get_real_txn(txn);
     203      288489 :       auto key = rec->real_id();
     204      288489 :       assert(fd_funk_rec_remove(_real, txn2, &key, NULL, 0UL) == FD_FUNK_SUCCESS);
     205             : 
     206      288489 :       rec->_erased = true;
     207      288489 :       rec->_data.clear();
     208      288489 :     }
     209             : 
     210      240000 :     void random_new_txn() {
     211      240000 :       if (_txns.size() == MAX_TXNS)
     212           0 :         return;
     213             : 
     214      240000 :       fake_txn* list[MAX_TXNS];
     215      240000 :       uint listlen = 0;
     216      240000 :       for (auto i : _txns)
     217     3865830 :         list[listlen++] = i.second;
     218      240000 :       auto * parent = list[((uint)lrand48())%listlen];
     219             : 
     220      240000 :       ulong key = ++_lastxid;
     221      240000 :       auto * txn = _txns[key] = new fake_txn(key);
     222             : 
     223      240000 :       txn->_parent = parent;
     224      240000 :       parent->_children[key] = txn;
     225             : 
     226      240000 :       fd_funk_txn_t * parent2 = get_real_txn(parent);
     227      240000 :       auto xid = txn->real_id();
     228      240000 :       assert(fd_funk_txn_prepare(_real, parent2, &xid, 1) != NULL);
     229      240000 :     }
     230             : 
     231      210366 :     void fake_cancel_family(fake_txn* txn) {
     232      210366 :       assert(txn->_key != ROOT_KEY);
     233      358005 :       while (!txn->_children.empty())
     234      147639 :         fake_cancel_family(txn->_children.begin()->second);
     235      210366 :       txn->_parent->_children.erase(txn->_key);
     236      210366 :       _txns.erase(txn->_key);
     237      210366 :       delete txn;
     238      210366 :     }
     239             : 
     240       29595 :     void fake_publish_to_parent(fake_txn* txn) {
     241             :       // Move records into parent
     242       29595 :       auto* parent = txn->_parent;
     243      134751 :       for (auto i : txn->_recs) {
     244      134751 :         uint p = 0;
     245     4364649 :         for (auto j : parent->_recs) {
     246     4364649 :           if( i->_key == j->_key ) {
     247       85530 :             delete j;
     248       85530 :             parent->_recs.erase(parent->_recs.begin()+p);
     249       85530 :             break;
     250       85530 :           }
     251     4279119 :           p++;
     252     4279119 :         }
     253      134751 :         parent->insert(i);
     254      134751 :       }
     255       29595 :       txn->_recs.clear();
     256             : 
     257             :       // Cancel siblings
     258       86322 :       for (;;) {
     259       86322 :         bool repeat = false;
     260       86322 :         for (auto i : parent->_children)
     261      120537 :           if (txn != i.second) {
     262       56727 :             fake_cancel_family(i.second);
     263       56727 :             repeat = true;
     264       56727 :             break;
     265       56727 :           }
     266       86322 :         if (!repeat) break;
     267       86322 :       }
     268       29595 :       assert(parent->_children.size() == 1 && parent->_children[txn->_key] == txn);
     269             : 
     270             :       // Move children up
     271       29595 :       parent->_children.clear();
     272       44826 :       for (auto i : txn->_children) {
     273       44826 :         auto* child = i.second;
     274       44826 :         child->_parent = parent;
     275       44826 :         parent->_children[child->_key] = child;
     276       44826 :       }
     277             : 
     278       29595 :       _txns.erase(txn->_key);
     279       29595 :       delete txn;
     280       29595 :     }
     281             : 
     282       17595 :     void fake_publish(fake_txn* txn) {
     283       17595 :       assert(txn->_key != ROOT_KEY);
     284       17595 :       if (txn->_parent->_key != ROOT_KEY)
     285       11595 :         fake_publish(txn->_parent);
     286       17595 :       assert(txn->_parent->_key == ROOT_KEY);
     287       17595 :       fake_publish_to_parent(txn);
     288       17595 :     }
     289             : 
     290           0 :     void random_publish() {
     291           0 :       fake_txn* list[MAX_TXNS];
     292           0 :       uint listlen = 0;
     293           0 :       for (auto i : _txns)
     294           0 :         if (i.second->_key != ROOT_KEY)
     295           0 :           list[listlen++] = i.second;
     296           0 :       if (!listlen) return;
     297           0 :       auto * txn = list[((uint)lrand48())%listlen];
     298           0 : 
     299           0 :       fd_funk_txn_t * txn2 = get_real_txn(txn);
     300           0 :       assert(fd_funk_txn_publish(_real, txn2, 1) > 0);
     301           0 : 
     302           0 :       // Simulate publication
     303           0 :       fake_publish(txn);
     304           0 :     }
     305             : 
     306       12000 :     void random_publish_into_parent() {
     307       12000 :       fake_txn* list[MAX_TXNS];
     308       12000 :       uint listlen = 0;
     309       12000 :       for (auto i : _txns)
     310      233724 :         if (i.second->_key != ROOT_KEY)
     311      221724 :           list[listlen++] = i.second;
     312       12000 :       if (!listlen) {
     313           0 :         return;
     314           0 :       }
     315       12000 :       auto * txn = list[((uint)lrand48())%listlen];
     316             : 
     317       12000 :       fd_funk_txn_t * txn2 = get_real_txn(txn);
     318       12000 :       assert(fd_funk_txn_publish_into_parent(_real, txn2, 1) == FD_FUNK_SUCCESS);
     319             : 
     320             :       // Simulate publication
     321       12000 :       fake_publish_to_parent(txn);
     322       12000 :     }
     323             : 
     324           0 :     void random_cancel() {
     325           0 :       fake_txn* list[MAX_TXNS];
     326           0 :       uint listlen = 0;
     327           0 :       for (auto i : _txns)
     328           0 :         if (i.second->_key != ROOT_KEY)
     329           0 :           list[listlen++] = i.second;
     330           0 :       if (!listlen) return;
     331           0 :       auto * txn = list[((uint)lrand48())%listlen];
     332           0 : 
     333           0 :       fd_funk_txn_t * txn2 = get_real_txn(txn);
     334           0 :       assert(fd_funk_txn_cancel(_real, txn2, 1) > 0);
     335           0 : 
     336           0 :       // Simulate cancel
     337           0 :       fake_cancel_family(txn);
     338           0 :     }
     339             : 
     340      102003 :     void verify() {
     341             : #ifdef FD_FUNK_HANDHOLDING
     342             :       assert(fd_funk_verify(_real) == FD_FUNK_SUCCESS);
     343             : #endif
     344             : 
     345     1948551 :       for (auto i : _txns) {
     346     1948551 :         assert(i.first == i.second->_key);
     347    18799170 :         for (auto j : i.second->_recs) {
     348    18799170 :           j->_touched = false;
     349    18799170 :         }
     350     1948551 :       }
     351             : 
     352      102003 :       fd_funk_all_iter_t iter[1];
     353    18901173 :       for( fd_funk_all_iter_new( _real, iter ); !fd_funk_all_iter_done( iter ); fd_funk_all_iter_next( iter ) ) {
     354    18799170 :         fd_funk_rec_t const * rec = fd_funk_all_iter_ele_const( iter );
     355    18799170 :         auto const * xid = fd_funk_rec_xid( rec );
     356    18799170 :         auto i = _txns.find(xid->ul[0]);
     357    18799170 :         assert(i != _txns.end());
     358    18799170 :         auto const * key = fd_funk_rec_key( rec );
     359    18799170 :         auto& recs = i->second->_recs;
     360   562273650 :         auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     361    18799170 :         assert(j != recs.end());
     362    18799170 :         auto * rec2 = *j;
     363    18799170 :         if (rec2->_erased) {
     364     3726804 :           assert(rec->flags & FD_FUNK_REC_FLAG_ERASE);
     365     3726804 :           assert(fd_funk_val_sz(rec) == 0);
     366    15072366 :         } else {
     367    15072366 :           assert(!(rec->flags & FD_FUNK_REC_FLAG_ERASE));
     368    15072366 :           assert(fd_funk_val_sz(rec) == rec2->size());
     369    15072366 :           assert(memcmp(fd_funk_val(rec, _wksp), rec2->data(), rec2->size()) == 0);
     370    15072366 :         }
     371             : 
     372    18799170 :         fd_funk_txn_map_t * txn_map = fd_funk_txn_map( _real );
     373    18799170 :         fd_funk_txn_t * txn = fd_funk_txn_query( xid, txn_map );
     374    18799170 :         fd_funk_rec_query_t query[1];
     375    18799170 :         auto* rec3 = fd_funk_rec_query_try_global(_real, txn, rec->pair.key, NULL, query);
     376    18799170 :         if( ( rec->flags & FD_FUNK_REC_FLAG_ERASE ) )
     377    18799170 :           assert(rec3 == NULL);
     378    15072366 :         else
     379    18799170 :           assert(rec == rec3);
     380    18799170 :         assert(!fd_funk_rec_query_test( query ));
     381             : 
     382    18799170 :         assert(!rec2->_touched);
     383    18799170 :         rec2->_touched = true;
     384    18799170 :       }
     385             : 
     386     1948551 :       for (auto i : _txns) {
     387    18799170 :         for (auto j : i.second->_recs) {
     388    18799170 :           assert(j->_touched || j->_erased);
     389    18799170 :         }
     390     1948551 :       }
     391             : 
     392     1948551 :       for (auto i : _txns) {
     393     1948551 :         auto * txn = i.second;
     394     1948551 :         assert(i.first == txn->_key);
     395     1948551 :         if (txn->_key == ROOT_KEY) {
     396      102003 :           assert(txn->_parent == NULL);
     397     1846548 :         } else {
     398     1846548 :           assert(txn->_parent->_children.find(txn->_key)->second == txn);
     399     1846548 :         }
     400     1948551 :         txn->_touched = false;
     401     1948551 :       }
     402             : 
     403      102003 :       {
     404             :         // Root transaction
     405      102003 :         auto * txn2 = _txns[ROOT_KEY];
     406      102003 :         assert(!txn2->_touched);
     407      102003 :         txn2->_touched = true;
     408             : 
     409      102003 :         auto& recs = txn2->_recs;
     410      102003 :         for( auto const * rec = fd_funk_txn_first_rec(_real, NULL);
     411    10279047 :              rec;
     412    10177044 :              rec = fd_funk_txn_next_rec(_real, rec) ) {
     413    10177044 :           auto const * key = fd_funk_rec_key( rec );
     414   513436308 :           auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     415    10177044 :           assert(j != recs.end());
     416    10177044 :         }
     417      102003 :       }
     418             : 
     419      102003 :       fd_funk_txn_pool_t * txn_pool = fd_funk_txn_pool( _real );
     420             : 
     421      102003 :       fd_funk_txn_all_iter_t txn_iter[1];
     422     1948551 :       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 ) ) {
     423     1846548 :         fd_funk_txn_t const * txn = fd_funk_txn_all_iter_ele_const( txn_iter );
     424             : 
     425     1846548 :         auto i = _txns.find(txn->xid.ul[0]);
     426     1846548 :         assert(i != _txns.end());
     427     1846548 :         auto * txn2 = i->second;
     428     1846548 :         assert(!txn2->_touched);
     429     1846548 :         txn2->_touched = true;
     430             : 
     431     1846548 :         auto * parent = fd_funk_txn_parent(txn, txn_pool);
     432     1846548 :         if (parent == NULL)
     433     1846548 :           assert(ROOT_KEY == txn2->_parent->_key);
     434     1502640 :         else
     435     1846548 :           assert(parent->xid.ul[0] == txn2->_parent->_key);
     436             : 
     437     1846548 :         auto& recs = txn2->_recs;
     438     1846548 :         for( auto const * rec = fd_funk_txn_first_rec(_real, txn);
     439    10468674 :              rec;
     440     8622126 :              rec = fd_funk_txn_next_rec(_real, rec) ) {
     441     8622126 :           auto const * key = fd_funk_rec_key( rec );
     442    48837342 :           auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     443     8622126 :           assert(j != recs.end());
     444     8622126 :         }
     445     1846548 :       }
     446             : 
     447     1948551 :       for (auto i : _txns) {
     448             :         assert(i.second->_touched);
     449     1948551 :       }
     450      102003 :     }
     451             : };

Generated by: LCOV version 1.14