LCOV - code coverage report
Current view: top level - funkier - test_funkier_common.hpp (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 96 233 41.2 %
Date: 2025-03-20 12:08:36 Functions: 14 22 63.6 %

          Line data    Source code
       1             : extern "C" {
       2             :   #include "fd_funkier.h"
       3             : }
       4             : #include <map>
       5             : #include <vector>
       6             : #include <set>
       7             : #include <algorithm>
       8             : #include <stdlib.h>
       9             : #include <assert.h>
      10             : #include <unistd.h>
      11             : 
      12             : static const long ROOT_KEY = 0;
      13             : static const ulong MAX_TXNS = 100;
      14             : static const ulong MAX_CHILDREN = 100;
      15             : static const uint MAX_PARTS = 8;
      16             : 
      17             : struct fake_rec {
      18             :     ulong _key;
      19             :     std::vector<long> _data;
      20             :     bool _erased = false;
      21             :     bool _touched = false;
      22             :     static std::set<fake_rec*> _all;
      23             : 
      24             :     fake_rec() = delete;
      25             :     fake_rec(ulong key) : _key(key) {
      26             :       assert(_all.count(this) == 0);
      27             :       _all.insert(this);
      28             :     }
      29           0 :     ~fake_rec() {
      30           0 :       assert(_all.count(this) == 1);
      31           0 :       _all.erase(this);
      32           0 :     }
      33             : 
      34     3150000 :     static fake_rec * make_random() {
      35     3150000 :       auto * rec = new fake_rec(((ulong)lrand48())%MAX_CHILDREN);
      36     3150000 :       auto len = ((ulong)lrand48())%8UL;
      37     3150000 :       rec->_data.resize(len);
      38    14163387 :       for (ulong i = 0; i < len; ++i)
      39    11013387 :         rec->_data[i] = lrand48();
      40     3150000 :       return rec;
      41     3150000 :     }
      42             : 
      43     3871584 :     fd_funkier_rec_key_t real_id() const {
      44     3871584 :       fd_funkier_rec_key_t i;
      45     3871584 :       memset(&i, 0, sizeof(i));
      46     3871584 :       i.ul[0] = _key;
      47     3871584 :       return i;
      48     3871584 :     }
      49             : 
      50    83717706 :     ulong size() const {
      51    83717706 :       return _data.size()*sizeof(long);
      52    83717706 :     }
      53             : 
      54    39746673 :     const uchar* data() const {
      55    39746673 :       return (const uchar*)_data.data();
      56    39746673 :     }
      57             : };
      58             : 
      59             : std::set<fake_rec*> fake_rec::_all;
      60             : 
      61             : struct fake_txn {
      62             :     ulong _key;
      63             :     std::vector<fake_rec*> _recs;
      64             :     std::map<ulong,fake_txn*> _children;
      65             :     fake_txn * _parent = NULL;
      66             :     bool _touched = false;
      67             : 
      68      600003 :     fake_txn(ulong key) : _key(key) { }
      69      600003 :     ~fake_txn() {
      70     2798130 :       for (auto i : _recs) {
      71     2798130 :         delete i;
      72     2798130 :       }
      73      600003 :     }
      74             : 
      75     5058495 :     fd_funkier_txn_xid_t real_id() const {
      76     5058495 :       fd_funkier_txn_xid_t i;
      77     5058495 :       memset(&i, 0, sizeof(i));
      78     5058495 :       i.ul[0] = _key;
      79     5058495 :       return i;
      80     5058495 :     }
      81             : 
      82             :     bool insert(fake_rec* rec) {
      83             :       for (auto i : _recs)
      84             :         if( i->_key == rec->_key ) {
      85             :           delete rec;
      86             :           return false; /* Error */
      87             :         }
      88             :       auto sz = _recs.size();
      89             :       _recs.resize(sz+1);
      90             :       _recs[sz] = rec;
      91             :       return true;
      92             :     }
      93             : };
      94             : 
      95             : struct fake_funk {
      96             :     fd_wksp_t * _wksp;
      97             :     fd_funkier_t * _real;
      98             :     std::map<ulong,fake_txn*> _txns;
      99             :     ulong _lastxid = 0;
     100             : #ifdef TEST_FUNKIER_FILE
     101             :     fd_funkier_close_file_args_t close_args;
     102             : #endif
     103             : 
     104           6 :     fake_funk(int * argc, char *** argv) {
     105           6 :       fd_boot( argc, argv );
     106           6 :       ulong txn_max = 128;
     107           6 :       ulong rec_max = 1<<16;
     108             : 
     109             : #ifdef TEST_FUNKIER_FILE
     110           3 :       _real = fd_funkier_open_file( "funk_test_file", 1, 1234U, txn_max, rec_max, FD_SHMEM_GIGANTIC_PAGE_SZ, FD_FUNKIER_OVERWRITE, &close_args );
     111             :       _wksp = fd_funkier_wksp( _real );
     112             : 
     113             : #else
     114             :       ulong  numa_idx = fd_shmem_numa_idx( 0 );
     115           3 :       _wksp = fd_wksp_new_anonymous( FD_SHMEM_GIGANTIC_PAGE_SZ, 1U, fd_shmem_cpu_idx( numa_idx ), "wksp", 0UL );
     116           3 :       void * mem = fd_wksp_alloc_laddr( _wksp, fd_funkier_align(), fd_funkier_footprint( txn_max, rec_max ), FD_FUNKIER_MAGIC );
     117             :       _real = fd_funkier_join( fd_funkier_new( mem, 1, 1234U, txn_max, rec_max ) );
     118             : #endif
     119             : 
     120           6 :       _txns[ROOT_KEY] = new fake_txn(ROOT_KEY);
     121           6 :     }
     122             :     ~fake_funk() {
     123             :       for (auto i : _txns)
     124             :         delete i.second;
     125             : #ifdef TEST_FUNKIER_FILE
     126             :       fd_funkier_close_file( &close_args );
     127             :       unlink( "funk_test_file" );
     128             : #endif
     129             :       for( auto i : fake_rec::_all )
     130             :         FD_LOG_NOTICE(( "leaked record 0x%lx!", (ulong)i ));
     131             : 
     132             :     }
     133             : 
     134             : #ifdef TEST_FUNKIER_FILE
     135           0 :     void reopen_file() {
     136           0 :       fd_funkier_close_file( &close_args );
     137           0 :       _real = fd_funkier_open_file( "funk_test_file", 1, 0, 0, 0, 0, FD_FUNKIER_READ_WRITE, &close_args );
     138           0 :       _wksp = fd_funkier_wksp( _real );
     139           0 :     }
     140             : #endif
     141             : 
     142     3900000 :     fake_txn * pick_unfrozen_txn() {
     143     3900000 :       fake_txn* list[MAX_TXNS];
     144     3900000 :       uint listlen = 0;
     145     3900000 :       for (auto i : _txns)
     146    84843900 :         if (i.second->_children.size() == 0)
     147    42647100 :           list[listlen++] = i.second;
     148     3900000 :       return list[((uint)lrand48())%listlen];
     149     3900000 :     }
     150             : 
     151     4531584 :     fd_funkier_txn_t * get_real_txn(fake_txn * txn) {
     152     4531584 :       if (txn->_key == ROOT_KEY)
     153       73089 :         return NULL;
     154     4458495 :       fd_funkier_txn_map_t txn_map = fd_funkier_txn_map( _real, _wksp );
     155     4458495 :       auto xid = txn->real_id();
     156     4458495 :       return fd_funkier_txn_query(&xid, &txn_map);
     157     4531584 :     }
     158             : 
     159             :     void random_insert() {
     160             :       fake_txn * txn = pick_unfrozen_txn();
     161             :       if( txn->_recs.size() == MAX_CHILDREN ) return;
     162             :       fake_rec * rec = NULL;
     163             :       do {
     164             :         rec = fake_rec::make_random();
     165             :         /* Prevent duplicate keys */
     166             :       } while (!txn->insert(rec));
     167             : 
     168             :       fd_funkier_txn_t * txn2 = get_real_txn(txn);
     169             :       auto key = rec->real_id();
     170             :       fd_funkier_rec_prepare_t prepare[1];
     171             :       fd_funkier_rec_t * rec2 = fd_funkier_rec_prepare(_real, txn2, &key, prepare, NULL);
     172             :       void * val = fd_funkier_val_truncate(rec2, rec->size(), fd_funkier_alloc(_real, _wksp), _wksp, NULL);
     173             :       memcpy(val, rec->data(), rec->size());
     174             :       fd_funkier_rec_publish( prepare );
     175             :       assert(fd_funkier_val_sz(rec2) == rec->size());
     176             :     }
     177             : 
     178             :     void random_remove() {
     179             :       fake_txn * txn = pick_unfrozen_txn();
     180             :       auto& recs = txn->_recs;
     181             :       fake_rec* list[MAX_CHILDREN];
     182             :       uint listlen = 0;
     183             :       for (auto i : recs)
     184             :         if (!i->_erased)
     185             :           list[listlen++] = i;
     186             :       if (!listlen) return;
     187             :       auto* rec = list[((uint)lrand48())%listlen];
     188             : 
     189             :       fd_funkier_txn_t * txn2 = get_real_txn(txn);
     190             :       auto key = rec->real_id();
     191             :       assert(fd_funkier_rec_remove(_real, txn2, &key, NULL, 0UL) == FD_FUNKIER_SUCCESS);
     192             : 
     193             :       rec->_erased = true;
     194             :       rec->_data.clear();
     195             :     }
     196             : 
     197      600000 :     void random_new_txn() {
     198      600000 :       if (_txns.size() == MAX_TXNS)
     199           0 :         return;
     200             : 
     201      600000 :       fake_txn* list[MAX_TXNS];
     202      600000 :       uint listlen = 0;
     203      600000 :       for (auto i : _txns)
     204     9638400 :         list[listlen++] = i.second;
     205      600000 :       auto * parent = list[((uint)lrand48())%listlen];
     206             : 
     207      600000 :       ulong key = ++_lastxid;
     208      600000 :       auto * txn = _txns[key] = new fake_txn(key);
     209             : 
     210      600000 :       txn->_parent = parent;
     211      600000 :       parent->_children[key] = txn;
     212             : 
     213      600000 :       fd_funkier_txn_t * parent2 = get_real_txn(parent);
     214      600000 :       auto xid = txn->real_id();
     215      600000 :       assert(fd_funkier_txn_prepare(_real, parent2, &xid, 1) != NULL);
     216      600000 :     }
     217             : 
     218      525633 :     void fake_cancel_family(fake_txn* txn) {
     219      525633 :       assert(txn->_key != ROOT_KEY);
     220      894990 :       while (!txn->_children.empty())
     221      369357 :         fake_cancel_family(txn->_children.begin()->second);
     222      525633 :       txn->_parent->_children.erase(txn->_key);
     223      525633 :       _txns.erase(txn->_key);
     224      525633 :       delete txn;
     225      525633 :     }
     226             : 
     227             :     void fake_publish_to_parent(fake_txn* txn) {
     228             :       // Move records into parent
     229             :       auto* parent = txn->_parent;
     230             :       for (auto i : txn->_recs) {
     231             :         uint p = 0;
     232             :         for (auto j : parent->_recs) {
     233             :           if( i->_key == j->_key ) {
     234             :             delete j;
     235             :             parent->_recs.erase(parent->_recs.begin()+p);
     236             :             break;
     237             :           }
     238             :           p++;
     239             :         }
     240             :         parent->insert(i);
     241             :       }
     242             :       txn->_recs.clear();
     243             : 
     244             :       // Cancel siblings
     245             :       for (;;) {
     246             :         bool repeat = false;
     247             :         for (auto i : parent->_children)
     248             :           if (txn != i.second) {
     249             :             fake_cancel_family(i.second);
     250             :             repeat = true;
     251             :             break;
     252             :           }
     253             :         if (!repeat) break;
     254             :       }
     255             :       assert(parent->_children.size() == 1 && parent->_children[txn->_key] == txn);
     256             : 
     257             :       // Move children up
     258             :       parent->_children.clear();
     259             :       for (auto i : txn->_children) {
     260             :         auto* child = i.second;
     261             :         child->_parent = parent;
     262             :         parent->_children[child->_key] = child;
     263             :       }
     264             : 
     265             :       _txns.erase(txn->_key);
     266             :       delete txn;
     267             :     }
     268             : 
     269       44352 :     void fake_publish(fake_txn* txn) {
     270       44352 :       assert(txn->_key != ROOT_KEY);
     271       44352 :       if (txn->_parent->_key != ROOT_KEY)
     272       29352 :         fake_publish(txn->_parent);
     273       44352 :       assert(txn->_parent->_key == ROOT_KEY);
     274           0 :       fake_publish_to_parent(txn);
     275       44352 :     }
     276             : 
     277           0 :     void random_publish() {
     278           0 :       fake_txn* list[MAX_TXNS];
     279           0 :       uint listlen = 0;
     280           0 :       for (auto i : _txns)
     281           0 :         if (i.second->_key != ROOT_KEY)
     282           0 :           list[listlen++] = i.second;
     283           0 :       if (!listlen) return;
     284           0 :       auto * txn = list[((uint)lrand48())%listlen];
     285           0 : 
     286           0 :       fd_funkier_txn_t * txn2 = get_real_txn(txn);
     287           0 :       assert(fd_funkier_txn_publish(_real, txn2, 1) > 0);
     288           0 : 
     289           0 :       // Simulate publication
     290           0 :       fake_publish(txn);
     291           0 :     }
     292             : 
     293       30000 :     void random_publish_into_parent() {
     294       30000 :       fake_txn* list[MAX_TXNS];
     295       30000 :       uint listlen = 0;
     296       30000 :       for (auto i : _txns)
     297      582405 :         if (i.second->_key != ROOT_KEY)
     298      552405 :           list[listlen++] = i.second;
     299       30000 :       if (!listlen) {
     300           0 :         return;
     301           0 :       }
     302       30000 :       auto * txn = list[((uint)lrand48())%listlen];
     303             : 
     304       30000 :       fd_funkier_txn_t * txn2 = get_real_txn(txn);
     305       30000 :       assert(fd_funkier_txn_publish_into_parent(_real, txn2, 1) == FD_FUNKIER_SUCCESS);
     306             : 
     307             :       // Simulate publication
     308           0 :       fake_publish_to_parent(txn);
     309       30000 :     }
     310             : 
     311           0 :     void random_cancel() {
     312           0 :       fake_txn* list[MAX_TXNS];
     313           0 :       uint listlen = 0;
     314           0 :       for (auto i : _txns)
     315           0 :         if (i.second->_key != ROOT_KEY)
     316           0 :           list[listlen++] = i.second;
     317           0 :       if (!listlen) return;
     318           0 :       auto * txn = list[((uint)lrand48())%listlen];
     319           0 : 
     320           0 :       fd_funkier_txn_t * txn2 = get_real_txn(txn);
     321           0 :       assert(fd_funkier_txn_cancel(_real, txn2, 1) > 0);
     322           0 : 
     323           0 :       // Simulate cancel
     324           0 :       fake_cancel_family(txn);
     325           0 :     }
     326             : 
     327           0 :     void verify() {
     328             : #ifdef FD_FUNKIER_HANDHOLDING
     329             :       assert(fd_funkier_verify(_real) == FD_FUNKIER_SUCCESS);
     330             : #endif
     331             : 
     332           0 :       for (auto i : _txns) {
     333           0 :         assert(i.first == i.second->_key);
     334           0 :         for (auto j : i.second->_recs) {
     335           0 :           j->_touched = false;
     336           0 :         }
     337           0 :       }
     338             : 
     339           0 :       fd_funkier_all_iter_t iter[1];
     340           0 :       for( fd_funkier_all_iter_new( _real, iter ); !fd_funkier_all_iter_done( iter ); fd_funkier_all_iter_next( iter ) ) {
     341           0 :         fd_funkier_rec_t const * rec = fd_funkier_all_iter_ele_const( iter );
     342           0 :         auto const * xid = fd_funkier_rec_xid( rec );
     343           0 :         auto i = _txns.find(xid->ul[0]);
     344           0 :         assert(i != _txns.end());
     345           0 :         auto const * key = fd_funkier_rec_key( rec );
     346           0 :         auto& recs = i->second->_recs;
     347           0 :         auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     348           0 :         assert(j != recs.end());
     349           0 :         auto * rec2 = *j;
     350           0 :         if (rec2->_erased) {
     351           0 :           assert(rec->flags & FD_FUNKIER_REC_FLAG_ERASE);
     352           0 :           assert(fd_funkier_val_sz(rec) == 0);
     353           0 :         } else {
     354           0 :           assert(!(rec->flags & FD_FUNKIER_REC_FLAG_ERASE));
     355           0 :           assert(fd_funkier_val_sz(rec) == rec2->size());
     356           0 :           assert(memcmp(fd_funkier_val(rec, _wksp), rec2->data(), rec2->size()) == 0);
     357           0 :         }
     358             : 
     359           0 :         fd_funkier_txn_map_t txn_map = fd_funkier_txn_map( _real, fd_funkier_wksp( _real ) );
     360           0 :         fd_funkier_txn_t * txn = fd_funkier_txn_query( xid, &txn_map );
     361           0 :         fd_funkier_rec_query_t query[1];
     362           0 :         auto* rec3 = fd_funkier_rec_query_try_global(_real, txn, rec->pair.key, NULL, query);
     363           0 :         if( ( rec->flags & FD_FUNKIER_REC_FLAG_ERASE ) )
     364           0 :           assert(rec3 == NULL);
     365           0 :         else
     366           0 :           assert(rec == rec3);
     367           0 :         assert(!fd_funkier_rec_query_test( query ));
     368             : 
     369           0 :         assert(!rec2->_touched);
     370           0 :         rec2->_touched = true;
     371           0 :       }
     372             : 
     373           0 :       for (auto i : _txns) {
     374           0 :         for (auto j : i.second->_recs) {
     375           0 :           assert(j->_touched || j->_erased);
     376           0 :         }
     377           0 :       }
     378             : 
     379           0 :       for (auto i : _txns) {
     380           0 :         auto * txn = i.second;
     381           0 :         assert(i.first == txn->_key);
     382           0 :         if (txn->_key == ROOT_KEY) {
     383           0 :           assert(txn->_parent == NULL);
     384           0 :         } else {
     385           0 :           assert(txn->_parent->_children.find(txn->_key)->second == txn);
     386           0 :         }
     387           0 :         txn->_touched = false;
     388           0 :       }
     389             : 
     390           0 :       {
     391             :         // Root transaction
     392           0 :         auto * txn2 = _txns[ROOT_KEY];
     393           0 :         assert(!txn2->_touched);
     394           0 :         txn2->_touched = true;
     395             : 
     396           0 :         auto& recs = txn2->_recs;
     397           0 :         for( auto const * rec = fd_funkier_txn_first_rec(_real, NULL);
     398           0 :              rec;
     399           0 :              rec = fd_funkier_txn_next_rec(_real, rec) ) {
     400           0 :           auto const * key = fd_funkier_rec_key( rec );
     401           0 :           auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     402           0 :           assert(j != recs.end());
     403           0 :         }
     404           0 :       }
     405             : 
     406           0 :       fd_funkier_txn_pool_t txn_pool = fd_funkier_txn_pool( _real, _wksp );
     407             : 
     408           0 :       fd_funkier_txn_all_iter_t txn_iter[1];
     409           0 :       for( fd_funkier_txn_all_iter_new( _real, txn_iter ); !fd_funkier_txn_all_iter_done( txn_iter ); fd_funkier_txn_all_iter_next( txn_iter ) ) {
     410           0 :         fd_funkier_txn_t const * txn = fd_funkier_txn_all_iter_ele_const( txn_iter );
     411             : 
     412           0 :         auto i = _txns.find(txn->xid.ul[0]);
     413           0 :         assert(i != _txns.end());
     414           0 :         auto * txn2 = i->second;
     415           0 :         assert(!txn2->_touched);
     416           0 :         txn2->_touched = true;
     417             : 
     418           0 :         auto * parent = fd_funkier_txn_parent(txn, &txn_pool);
     419           0 :         if (parent == NULL)
     420           0 :           assert(ROOT_KEY == txn2->_parent->_key);
     421           0 :         else
     422           0 :           assert(parent->xid.ul[0] == txn2->_parent->_key);
     423             : 
     424           0 :         auto& recs = txn2->_recs;
     425           0 :         for( auto const * rec = fd_funkier_txn_first_rec(_real, txn);
     426           0 :              rec;
     427           0 :              rec = fd_funkier_txn_next_rec(_real, rec) ) {
     428           0 :           auto const * key = fd_funkier_rec_key( rec );
     429           0 :           auto j = std::find_if(recs.begin(), recs.end(), [key](auto * rec2) {return rec2->_key == key->ul[0];});
     430           0 :           assert(j != recs.end());
     431           0 :         }
     432           0 :       }
     433             : 
     434           0 :       for (auto i : _txns) {
     435           0 :         assert(i.second->_touched);
     436           0 :       }
     437           0 :     }
     438             : };

Generated by: LCOV version 1.14