LCOV - code coverage report
Current view: top level - app/shredcap - fd_rocksdb_src.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 264 0.0 %
Date: 2026-08-15 04:34:25 Functions: 0 7 0.0 %

          Line data    Source code
       1             : #include "../../discof/backtest/fd_backtest_src.h"
       2             : #include <stdlib.h>
       3             : #include <rocksdb/c.h>
       4             : 
       5             : extern fd_backt_src_vt_t const fd_rocksdb_src_vt;
       6             : 
       7             : #define CF_IDX_DEFAULT    0
       8           0 : #define CF_IDX_CODE_SHRED 1
       9           0 : #define CF_IDX_DATA_SHRED 2
      10           0 : #define CF_IDX_ROOT       3
      11           0 : #define CF_IDX_BANK_HASH  4
      12           0 : #define CF_IDX_DEAD_SLOT  5
      13           0 : #define CF_CNT            6
      14             : 
      15             : struct fd_rocksdb_src {
      16             :   fd_backt_src_t src[1];
      17             :   rocksdb_t * db;
      18             :   rocksdb_readoptions_t * ro;
      19             :   rocksdb_column_family_handle_t * code_shred_cf;
      20             :   rocksdb_column_family_handle_t * data_shred_cf;
      21             :   rocksdb_column_family_handle_t * root_cf;
      22             :   rocksdb_column_family_handle_t * bank_hash_cf;
      23             :   rocksdb_column_family_handle_t * dead_slot_cf;
      24             :   rocksdb_column_family_handle_t ** all_cfs; /* every column family in the db */
      25             :   ulong all_cf_cnt;
      26             :   rocksdb_iterator_t * code_shred_iter;
      27             :   rocksdb_iterator_t * data_shred_iter;
      28             :   rocksdb_iterator_t * root_iter;
      29             : 
      30             :   uint rooted_only : 1;
      31             :   uint code_shreds : 1;
      32             :   uint code_iter_done : 1;
      33             :   uint data_iter_done : 1;
      34             :   uint code_slot_done : 1;
      35             :   uint data_slot_done : 1;
      36             : 
      37             :   ulong current_slot; /* ULONG_MAX if slot not started */
      38             : };
      39             : 
      40             : typedef struct fd_rocksdb_src fd_rocksdb_src_t;
      41             : 
      42             : static ulong
      43           0 : iter_cur_slot( rocksdb_iterator_t * iter ) {
      44           0 :   rocksdb_slice_t key = rocksdb_iter_key_slice( iter );
      45           0 :   if( FD_UNLIKELY( key.size < sizeof(ulong) ) ) {
      46           0 :     FD_LOG_HEXDUMP_WARNING(( "invalid key", key.data, key.size ));
      47           0 :     FD_LOG_ERR(( "corrupt RocksDB: invalid key in iterator" ));
      48           0 :   }
      49           0 :   return fd_ulong_bswap( FD_LOAD( ulong, key.data ) );
      50           0 : }
      51             : 
      52             : fd_backt_src_t *
      53           0 : fd_rocksdb_src_create( fd_backtest_src_opts_t const * opts ) {
      54           0 :   FD_TEST( opts );
      55             :   /* opts->format ignored */
      56             : 
      57           0 :   static char const * cf_names[ CF_CNT ] = {
      58           0 :     [ CF_IDX_DEFAULT    ] = "default",
      59           0 :     [ CF_IDX_CODE_SHRED ] = "code_shred",
      60           0 :     [ CF_IDX_DATA_SHRED ] = "data_shred",
      61           0 :     [ CF_IDX_ROOT       ] = "root",
      62           0 :     [ CF_IDX_BANK_HASH  ] = "bank_hashes",
      63           0 :     [ CF_IDX_DEAD_SLOT  ] = "dead_slots"
      64           0 :   };
      65             : 
      66           0 :   rocksdb_options_t * options = rocksdb_options_create();
      67           0 :   if( FD_UNLIKELY( !options ) ) FD_LOG_ERR(( "rocksdb_options_create failed" ));
      68             : 
      69             :   /* Enumerate and open ALL column families, not just the ones this
      70             :      reader uses: a read-only open with a subset of column families
      71             :      makes rocksdb delete the unopened families' SST files at close,
      72             :      corrupting the blockstore for other readers (eg. agave-ledger-tool). */
      73           0 :   char *  err        = NULL;
      74           0 :   ulong   all_cf_cnt = 0UL;
      75           0 :   char ** all_cf_names = rocksdb_list_column_families( options, opts->path, &all_cf_cnt, &err );
      76           0 :   if( FD_UNLIKELY( !all_cf_names ) ) {
      77           0 :     FD_LOG_WARNING(( "rocksdb_list_column_families failed: %s", err ));
      78           0 :     rocksdb_free( err );
      79           0 :     rocksdb_options_destroy( options );
      80           0 :     return NULL;
      81           0 :   }
      82             : 
      83           0 :   rocksdb_options_t const **         cf_options = calloc( all_cf_cnt, sizeof(rocksdb_options_t const *)         );
      84           0 :   rocksdb_column_family_handle_t ** cfs         = calloc( all_cf_cnt, sizeof(rocksdb_column_family_handle_t *) );
      85           0 :   if( FD_UNLIKELY( !cf_options || !cfs ) ) FD_LOG_ERR(( "out of memory" ));
      86           0 :   for( ulong i=0UL; i<all_cf_cnt; i++ ) cf_options[ i ] = options;
      87             : 
      88           0 :   rocksdb_t * db = rocksdb_open_for_read_only_column_families(
      89           0 :     options,
      90           0 :     opts->path,
      91           0 :     (int)all_cf_cnt,
      92           0 :     (char const * const *)all_cf_names,
      93           0 :     cf_options,
      94           0 :     cfs,
      95           0 :     0,
      96           0 :     &err
      97           0 :   );
      98           0 :   rocksdb_options_destroy( options );
      99           0 :   free( cf_options );
     100           0 :   if( FD_UNLIKELY( !db ) ) {
     101           0 :     FD_LOG_WARNING(( "rocksdb_open_for_read_only_column_families failed: %s", err ));
     102           0 :     rocksdb_free( err );
     103           0 :     rocksdb_list_column_families_destroy( all_cf_names, all_cf_cnt );
     104           0 :     free( cfs );
     105           0 :     return NULL;
     106           0 :   }
     107             : 
     108             :   /* Pick out the handles this reader uses. */
     109           0 :   rocksdb_column_family_handle_t * named[ CF_CNT ] = {0};
     110           0 :   for( ulong i=0UL; i<all_cf_cnt; i++ ) {
     111           0 :     for( ulong j=0UL; j<CF_CNT; j++ ) {
     112           0 :       if( 0==strcmp( all_cf_names[ i ], cf_names[ j ] ) ) named[ j ] = cfs[ i ];
     113           0 :     }
     114           0 :   }
     115           0 :   rocksdb_list_column_families_destroy( all_cf_names, all_cf_cnt );
     116           0 :   for( ulong j=0UL; j<CF_CNT; j++ ) {
     117           0 :     if( FD_UNLIKELY( !named[ j ] ) ) {
     118           0 :       FD_LOG_WARNING(( "column family \"%s\" not found in %s", cf_names[ j ], opts->path ));
     119           0 :       for( ulong i=0UL; i<all_cf_cnt; i++ ) if( cfs[ i ] ) rocksdb_column_family_handle_destroy( cfs[ i ] );
     120           0 :       free( cfs );
     121           0 :       rocksdb_close( db );
     122           0 :       return NULL;
     123           0 :     }
     124           0 :   }
     125             : 
     126           0 :   rocksdb_readoptions_t * ro = rocksdb_readoptions_create();
     127           0 :   if( FD_UNLIKELY( !ro ) ) {
     128           0 :     FD_LOG_ERR(( "rocksdb_readoptions_create failed" ));
     129           0 :   }
     130             : 
     131           0 :   fd_rocksdb_src_t * src = calloc( 1UL, sizeof(fd_rocksdb_src_t) );
     132           0 :   if( FD_UNLIKELY( !src ) ) FD_LOG_ERR(( "out of memory" ));
     133             : 
     134           0 :   rocksdb_iterator_t * code_shred_iter = rocksdb_create_iterator_cf( db, ro, named[ CF_IDX_CODE_SHRED ] );
     135           0 :   rocksdb_iterator_t * data_shred_iter = rocksdb_create_iterator_cf( db, ro, named[ CF_IDX_DATA_SHRED ] );
     136           0 :   rocksdb_iterator_t * root_iter       = rocksdb_create_iterator_cf( db, ro, named[ CF_IDX_ROOT       ] );
     137           0 :   if( FD_UNLIKELY( !code_shred_iter || !data_shred_iter || !root_iter ) ) {
     138           0 :     FD_LOG_ERR(( "rocksdb_create_iterator_cf failed" ));
     139           0 :   }
     140             : 
     141           0 :   rocksdb_iter_seek_to_first( code_shred_iter );
     142           0 :   rocksdb_iter_seek_to_first( data_shred_iter );
     143           0 :   rocksdb_iter_seek_to_first( root_iter );
     144             : 
     145           0 :   *src = (fd_rocksdb_src_t){
     146           0 :     .src = {{
     147           0 :       .vt = &fd_rocksdb_src_vt
     148           0 :     }},
     149           0 :     .db = db,
     150           0 :     .ro = ro,
     151             : 
     152           0 :     .code_shred_cf   = named[ CF_IDX_CODE_SHRED ],
     153           0 :     .data_shred_cf   = named[ CF_IDX_DATA_SHRED ],
     154           0 :     .root_cf         = named[ CF_IDX_ROOT       ],
     155           0 :     .bank_hash_cf    = named[ CF_IDX_BANK_HASH  ],
     156           0 :     .dead_slot_cf    = named[ CF_IDX_DEAD_SLOT  ],
     157           0 :     .all_cfs         = cfs,
     158           0 :     .all_cf_cnt      = all_cf_cnt,
     159           0 :     .code_shred_iter = code_shred_iter,
     160           0 :     .data_shred_iter = data_shred_iter,
     161           0 :     .root_iter       = root_iter,
     162             : 
     163           0 :     .rooted_only     = !!opts->rooted_only,
     164           0 :     .code_shreds     = !!opts->code_shreds,
     165           0 :     .code_iter_done  = !opts->code_shreds,
     166           0 :     .data_iter_done  = 0,
     167           0 :     .code_slot_done  = !opts->code_shreds,
     168           0 :     .data_slot_done  = 0,
     169             : 
     170           0 :     .current_slot = ULONG_MAX
     171           0 :   };
     172           0 :   return src->src;
     173           0 : }
     174             : 
     175             : void
     176           0 : fd_rocksdb_src_destroy( fd_backt_src_t * this ) {
     177           0 :   if( FD_UNLIKELY( !this ) ) return;
     178           0 :   fd_rocksdb_src_t * src = (fd_rocksdb_src_t *)this;
     179           0 :   rocksdb_iter_destroy( src->code_shred_iter );
     180           0 :   rocksdb_iter_destroy( src->data_shred_iter );
     181           0 :   rocksdb_iter_destroy( src->root_iter );
     182           0 :   for( ulong i=0UL; i<src->all_cf_cnt; i++ ) rocksdb_column_family_handle_destroy( src->all_cfs[ i ] );
     183           0 :   free( src->all_cfs );
     184           0 :   rocksdb_readoptions_destroy( src->ro );
     185           0 :   rocksdb_close( src->db );
     186           0 :   free( src );
     187           0 : }
     188             : 
     189             : ulong
     190             : fd_rocksdb_src_first_shred( fd_backt_src_t * this,
     191             :                             uchar *          buf,
     192           0 :                             ulong            buf_sz ) {
     193           0 :   fd_rocksdb_src_t * src = (fd_rocksdb_src_t *)this;
     194           0 :   rocksdb_iterator_t * iter = rocksdb_create_iterator_cf(
     195           0 :       src->db, src->ro, src->data_shred_cf );
     196           0 :   if( FD_UNLIKELY( !iter ) ) {
     197           0 :     FD_LOG_WARNING(( "rocksdb_create_iterator_cf(data_shred) failed" ));
     198           0 :     return 0UL;
     199           0 :   }
     200           0 :   rocksdb_iter_seek_to_first( iter );
     201           0 :   ulong sz = 0UL;
     202           0 :   if( FD_LIKELY( rocksdb_iter_valid( iter ) ) ) {
     203           0 :     size_t vlen = 0UL;
     204           0 :     char const * value = rocksdb_iter_value( iter, &vlen );
     205           0 :     if( FD_LIKELY( value && vlen<=buf_sz ) ) {
     206           0 :       fd_memcpy( buf, value, vlen );
     207           0 :       sz = (ulong)vlen;
     208           0 :     } else {
     209           0 :       FD_LOG_WARNING(( "RocksDB contains oversz data shred (sz=%lu buf_sz=%lu)", (ulong)vlen, buf_sz ));
     210           0 :     }
     211           0 :   } else {
     212           0 :     FD_LOG_WARNING(( "RocksDB does not contain any data shreds" ));
     213           0 :   }
     214           0 :   rocksdb_iter_destroy( iter );
     215           0 :   return sz;
     216           0 : }
     217             : 
     218             : ulong
     219             : fd_rocksdb_src_shred( fd_backt_src_t * this,
     220             :                       uchar *          buf,
     221           0 :                       ulong            buf_sz ) {
     222           0 :   fd_rocksdb_src_t * src = (fd_rocksdb_src_t *)this;
     223             : 
     224           0 :   for(;;) {
     225             : 
     226           0 :     if( FD_UNLIKELY( src->current_slot==ULONG_MAX && src->rooted_only ) ) {
     227           0 :       if( FD_UNLIKELY( !rocksdb_iter_valid( src->root_iter ) ) ) {
     228           0 :         src->data_iter_done = 1;
     229           0 :         src->code_iter_done = 1;
     230           0 :         return ULONG_MAX;
     231           0 :       }
     232           0 :       src->current_slot = iter_cur_slot( src->root_iter );
     233           0 :     }
     234             : 
     235           0 :     if( !src->data_slot_done ) {
     236           0 :       rocksdb_iterator_t * iter = src->data_shred_iter;
     237           0 :       if( FD_UNLIKELY( !rocksdb_iter_valid( iter ) ) ) {
     238           0 :         src->data_slot_done = 1;
     239           0 :         src->data_iter_done = 1;
     240           0 :         continue;
     241           0 :       }
     242           0 :       ulong found_slot = iter_cur_slot( iter );
     243           0 :       if( FD_UNLIKELY( src->current_slot == ULONG_MAX ) ) {
     244           0 :         src->current_slot = found_slot;
     245           0 :       } else if( FD_UNLIKELY( found_slot > src->current_slot ) ) {
     246           0 :         src->data_slot_done = 1;
     247           0 :         continue;
     248           0 :       } else if( FD_UNLIKELY( found_slot < src->current_slot ) ) {
     249           0 :         char key[8]; FD_STORE( ulong, key, fd_ulong_bswap( src->current_slot ) );
     250           0 :         rocksdb_iter_seek( iter, key, sizeof(ulong) );
     251           0 :         continue;
     252           0 :       }
     253           0 :       rocksdb_slice_t value = rocksdb_iter_value_slice( iter );
     254           0 :       ulong sz = fd_ulong_min( value.size, buf_sz );
     255           0 :       fd_memcpy( buf, value.data, sz );
     256           0 :       rocksdb_iter_next( iter );
     257           0 :       return sz;
     258           0 :     }
     259             : 
     260           0 :     if( !src->code_slot_done ) {
     261           0 :       rocksdb_iterator_t * iter = src->code_shred_iter;
     262           0 :       if( FD_UNLIKELY( !rocksdb_iter_valid( iter ) ) ) {
     263           0 :         src->code_slot_done = 1;
     264           0 :         src->code_iter_done = 1;
     265           0 :         continue;
     266           0 :       }
     267           0 :       ulong found_slot = iter_cur_slot( iter );
     268           0 :       if( FD_UNLIKELY( found_slot > src->current_slot ) ) {
     269           0 :         src->code_slot_done = 1;
     270           0 :         continue;
     271           0 :       } else if( FD_UNLIKELY( found_slot < src->current_slot ) ) {
     272           0 :         char key[8]; FD_STORE( ulong, key, fd_ulong_bswap( src->current_slot ) );
     273           0 :         rocksdb_iter_seek( iter, key, sizeof(ulong) );
     274           0 :         continue;
     275           0 :       }
     276           0 :       rocksdb_slice_t value = rocksdb_iter_value_slice( iter );
     277           0 :       ulong sz = fd_ulong_min( value.size, buf_sz );
     278           0 :       fd_memcpy( buf, value.data, sz );
     279           0 :       rocksdb_iter_next( iter );
     280           0 :       return sz;
     281           0 :     }
     282             : 
     283           0 :     if( FD_UNLIKELY( src->data_iter_done && src->code_iter_done ) ) {
     284           0 :       return ULONG_MAX;
     285           0 :     }
     286           0 :     if( src->current_slot!=ULONG_MAX ) {
     287           0 :       char next_root_key[8]; FD_STORE( ulong, next_root_key, fd_ulong_bswap( src->current_slot+1UL ) );
     288           0 :       rocksdb_iter_seek( src->root_iter, next_root_key, sizeof(ulong) );
     289           0 :     }
     290           0 :     src->current_slot   = ULONG_MAX;
     291           0 :     src->code_slot_done = !src->code_shreds;
     292           0 :     src->data_slot_done = 0;
     293           0 :   }
     294             : 
     295           0 : }
     296             : 
     297             : fd_backt_slot_info_t *
     298             : fd_rocksdb_src_slot_info( fd_backt_src_t *       this,
     299             :                           fd_backt_slot_info_t * out,
     300           0 :                           ulong                  slot ) {
     301           0 :   fd_rocksdb_src_t * src = (fd_rocksdb_src_t *)this;
     302             : 
     303           0 :   char key[8]; FD_STORE( ulong, key, fd_ulong_bswap( slot ) );
     304             : 
     305           0 :   char * err = NULL;
     306           0 :   ulong  root_val_len;
     307           0 :   char * root_val = rocksdb_get_cf( src->db, src->ro, src->root_cf, key, sizeof(ulong), &root_val_len, &err );
     308           0 :   if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "rocksdb_get_cf(root) failed: %s", err ));
     309             : 
     310           0 :   ulong  bank_hash_len;
     311           0 :   char * bank_hash_val = rocksdb_get_cf( src->db, src->ro, src->bank_hash_cf, key, sizeof(ulong), &bank_hash_len, &err );
     312           0 :   if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "rocksdb_get_cf(bank_hashes) failed: %s", err ));
     313             : 
     314           0 :   ulong  dead_slot_len;
     315           0 :   char * dead_slot_val = rocksdb_get_cf( src->db, src->ro, src->dead_slot_cf, key, sizeof(ulong), &dead_slot_len, &err );
     316           0 :   if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "rocksdb_get_cf(dead_slot) failed: %s", err ));
     317             : 
     318           0 :   memset( out, 0, sizeof(fd_backt_slot_info_t) );
     319           0 :   out->slot = slot;
     320             : 
     321           0 :   if( bank_hash_val && bank_hash_len>=36UL ) {
     322           0 :     fd_memcpy( &out->bank_hash, bank_hash_val+4UL, sizeof(fd_hash_t) );
     323           0 :     out->bank_hash_set = 1;
     324           0 :   }
     325             : 
     326           0 :   if( root_val && root_val_len>=1UL ) {
     327           0 :     out->rooted = !!root_val[ 0 ];
     328           0 :   }
     329             : 
     330           0 :   if( dead_slot_val && dead_slot_len>=1UL ) {
     331           0 :     out->dead = !!dead_slot_val[ 0 ];
     332           0 :   }
     333             : 
     334           0 :   rocksdb_free( root_val );
     335           0 :   rocksdb_free( bank_hash_val );
     336           0 :   rocksdb_free( dead_slot_val );
     337             : 
     338           0 :   return out;
     339           0 : }
     340             : 
     341             : void
     342             : fd_rocksdb_src_seek( fd_backt_src_t * this,
     343           0 :                      ulong            slot ) {
     344           0 :   fd_rocksdb_src_t * src = (fd_rocksdb_src_t *)this;
     345             : 
     346           0 :   char key[8]; FD_STORE( ulong, key, fd_ulong_bswap( slot ) );
     347           0 :   rocksdb_iter_seek( src->root_iter,       key, sizeof(ulong) );
     348           0 :   rocksdb_iter_seek( src->data_shred_iter, key, sizeof(ulong) );
     349           0 :   if( src->code_shreds ) {
     350           0 :     rocksdb_iter_seek( src->code_shred_iter, key, sizeof(ulong) );
     351           0 :   }
     352             : 
     353             :   src->current_slot   = ULONG_MAX;
     354           0 :   src->data_slot_done = 0;
     355           0 :   src->code_slot_done = !src->code_shreds;
     356           0 :   src->data_iter_done = 0;
     357           0 :   src->code_iter_done = !src->code_shreds;
     358           0 : }
     359             : 
     360             : fd_backt_src_vt_t const fd_rocksdb_src_vt = {
     361             :   .destroy      = fd_rocksdb_src_destroy,
     362             :   .first_shred  = fd_rocksdb_src_first_shred,
     363             :   .shred        = fd_rocksdb_src_shred,
     364             :   .slot_info    = fd_rocksdb_src_slot_info,
     365             :   .seek         = fd_rocksdb_src_seek
     366             : };

Generated by: LCOV version 1.14