LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_impl_v2.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 563 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 16 0.0 %

          Line data    Source code
       1             : #include "fd_accdb_impl_v2.h"
       2             : #include "fd_accdb_funk.h"
       3             : #include "fd_vinyl_req_pool.h"
       4             : #include <stdatomic.h>
       5             : 
       6             : FD_STATIC_ASSERT( alignof(fd_accdb_user_v2_t)<=alignof(fd_accdb_user_t), layout );
       7             : FD_STATIC_ASSERT( sizeof (fd_accdb_user_v2_t)<=sizeof(fd_accdb_user_t),  layout );
       8             : 
       9             : /* Record synchronization *********************************************/
      10             : 
      11             : /* fd_funk_rec_write_{lock,unlock} acquire/release a record write lock.
      12             : 
      13             :    These are assumed to never fail because writes to accdb records are
      14             :    externally coordinated to be non-conflicting at the record level.
      15             :    In other words, it is assumed that, when the caller attempts to
      16             :    write to a record:
      17             :    - no admin attempts to root or cancel the DB txn this write targets
      18             :      (admin may only root/cancel txns after they are frozen, and users
      19             :      may only write to txns before they are frozen)
      20             :    - no other user attempts to read/write to the same record until the
      21             :      current thread is done writing.  (Other threads wait for the
      22             :      current thread to signal completion before attempting to access) */
      23             : 
      24             : static void
      25             : fd_funk_rec_write_lock( fd_funk_t const * funk,
      26           0 :                         fd_funk_rec_t *   rec ) {
      27           0 :   ulong rec_idx = (ulong)( rec - funk->rec_pool->ele );
      28           0 :   ulong volatile * vl = &funk->rec_lock[ rec_idx ];
      29           0 :   ulong val = FD_VOLATILE_CONST( *vl );
      30           0 :   if( FD_UNLIKELY( fd_funk_rec_lock_bits( val ) ) ) {
      31           0 :     FD_LOG_CRIT(( "fd_funk_rec_write_lock(" FD_FUNK_REC_PAIR_FMT ") failed: record has active readers",
      32           0 :                   FD_FUNK_REC_PAIR_FMT_ARGS( rec->pair ) ));
      33           0 :   }
      34           0 :   ulong val_new = fd_funk_rec_ver_lock( fd_funk_rec_ver_bits( val ), FD_FUNK_REC_LOCK_MASK );
      35           0 :   if( FD_UNLIKELY( FD_ATOMIC_CAS( vl, val, val_new )!=val ) ) {
      36           0 :     FD_LOG_CRIT(( "fd_funk_rec_write_lock(" FD_FUNK_REC_PAIR_FMT ") failed: data race detected",
      37           0 :                   FD_FUNK_REC_PAIR_FMT_ARGS( rec->pair ) ));
      38           0 :   }
      39           0 : }
      40             : 
      41             : static void
      42             : fd_funk_rec_write_lock_uncontended( fd_funk_t const * funk,
      43           0 :                                     fd_funk_rec_t *   rec ) {
      44           0 :   ulong rec_idx = (ulong)( rec - funk->rec_pool->ele );
      45           0 :   ulong val     = funk->rec_lock[ rec_idx ];
      46           0 :   ulong val_ver = fd_funk_rec_ver_bits( val );
      47           0 :   funk->rec_lock[ rec_idx ] = fd_funk_rec_ver_lock( val_ver, FD_FUNK_REC_LOCK_MASK );
      48           0 : }
      49             : 
      50             : static void
      51             : fd_funk_rec_write_unlock( fd_funk_t const * funk,
      52           0 :                           fd_funk_rec_t *   rec ) {
      53           0 :   ulong rec_idx = (ulong)( rec - funk->rec_pool->ele );
      54           0 :   ulong volatile * vl = &funk->rec_lock[ rec_idx ];
      55           0 :   ulong val = FD_VOLATILE_CONST( *vl );
      56           0 :   if( FD_UNLIKELY( fd_funk_rec_lock_bits( val )!=FD_FUNK_REC_LOCK_MASK ) ) {
      57           0 :     FD_LOG_CRIT(( "fd_funk_rec_write_unlock(" FD_FUNK_REC_PAIR_FMT ") failed: record is not write locked",
      58           0 :                   FD_FUNK_REC_PAIR_FMT_ARGS( rec->pair ) ));
      59           0 :   }
      60           0 :   ulong val_new = fd_funk_rec_ver_lock( fd_funk_rec_ver_bits( val ), 0UL );
      61           0 :   if( FD_UNLIKELY( FD_ATOMIC_CAS( vl, val, val_new )!=val ) ) {
      62           0 :     FD_LOG_CRIT(( "fd_funk_rec_write_unlock(" FD_FUNK_REC_PAIR_FMT ") failed: data race detected",
      63           0 :                   FD_FUNK_REC_PAIR_FMT_ARGS( rec->pair ) ));
      64           0 :   }
      65           0 : }
      66             : 
      67             : /* fd_funk_rec_read_{lock_try,unlock} try-acquire/release a record read
      68             :    lock.
      69             : 
      70             :    Read lock acquires may fail for frozen txn.  This is because an admin
      71             :    may concurrently root the txn as we are querying. */
      72             : 
      73             : static int
      74             : fd_funk_rec_read_lock( fd_funk_t const * funk,
      75           0 :                        fd_funk_rec_t *   rec ) {
      76           0 :   ulong rec_idx = (ulong)( rec - funk->rec_pool->ele );
      77           0 :   ulong volatile * vl = &funk->rec_lock[ rec_idx ];
      78           0 :   for(;;) {
      79           0 :     ulong val      = FD_VOLATILE_CONST( *vl );
      80           0 :     ulong val_ver  = fd_funk_rec_ver_bits ( val );
      81           0 :     ulong val_lock = fd_funk_rec_lock_bits( val );
      82           0 :     if( FD_UNLIKELY( !fd_funk_rec_ver_alive( val_ver ) ) ) {
      83           0 :       return FD_MAP_ERR_AGAIN;
      84           0 :     }
      85           0 :     if( FD_UNLIKELY( val_lock>=FD_FUNK_REC_LOCK_MASK-1 ) ) {
      86           0 :       FD_LOG_CRIT(( "fd_funk_rec_read_lock(" FD_FUNK_REC_PAIR_FMT ") failed: val_lock=%#lx (too many readers or already write locked)",
      87           0 :                     FD_FUNK_REC_PAIR_FMT_ARGS( rec->pair ), val_lock ));
      88           0 :     }
      89           0 :     ulong val_new = fd_funk_rec_ver_lock( val_ver, val_lock+1UL );
      90           0 :     if( FD_LIKELY( FD_ATOMIC_CAS( vl, val, val_new )==val ) ) {
      91           0 :       return FD_MAP_SUCCESS;
      92           0 :     }
      93           0 :   }
      94           0 : }
      95             : 
      96             : static void
      97             : fd_funk_rec_read_unlock( fd_funk_t const * funk,
      98           0 :                          fd_funk_rec_t *   rec ) {
      99           0 :   ulong rec_idx = (ulong)( rec - funk->rec_pool->ele );
     100           0 :   ulong volatile * vl = &funk->rec_lock[ rec_idx ];
     101           0 :   for(;;) {
     102           0 :     ulong val      = FD_VOLATILE_CONST( *vl );
     103           0 :     ulong val_ver  = fd_funk_rec_ver_bits ( val );
     104           0 :     ulong val_lock = fd_funk_rec_lock_bits( val );
     105           0 :     if( FD_UNLIKELY( val_lock==0UL || val_lock==FD_FUNK_REC_LOCK_MASK ) ) {
     106           0 :       FD_LOG_CRIT(( "fd_funk_rec_read_unlock(" FD_FUNK_REC_PAIR_FMT ") failed: val_lock=%#lx (cannot unlock)",
     107           0 :                     FD_FUNK_REC_PAIR_FMT_ARGS( rec->pair ), val_lock ));
     108           0 :     }
     109           0 :     ulong val_new = fd_funk_rec_ver_lock( val_ver, val_lock-1UL );
     110           0 :     if( FD_LIKELY( FD_ATOMIC_CAS( vl, val, val_new )==val ) ) {
     111           0 :       return;
     112           0 :     }
     113           0 :   }
     114           0 : }
     115             : 
     116             : /* Record acquisition *************************************************/
     117             : 
     118             : /* funk_rec_acquire finds the newest revision of 'key' in the funk hash
     119             :    chain at index chain_idx.  Only considers records on the current fork
     120             :    (fork nodes stored in accdb).
     121             : 
     122             :    On success, returns ACQUIRE_{READ,WRITE} and points *out_rec to the
     123             :    record found.  If is_write, a write-lock is acquired for this record,
     124             :    otherwise a read lock.  It is the caller's responsibility to release
     125             :    this lock.
     126             : 
     127             :    If no record was found, returns ACQUIRE_NOT_FOUND.
     128             : 
     129             :    Returns ACQUIRE_FAILED on index contention (hash map locks) or
     130             :    record contention (admin started deleting the record just as we were
     131             :    about to start the access).  The caller should retry in this case. */
     132             : 
     133           0 : #define ACQUIRE_READ      0
     134           0 : #define ACQUIRE_WRITE     1
     135           0 : #define ACQUIRE_NOT_FOUND 2
     136           0 : #define ACQUIRE_FAILED    3
     137             : 
     138             : static int
     139             : funk_rec_acquire( fd_accdb_user_v2_t const * accdb,
     140             :                   ulong                      chain_idx,
     141             :                   fd_funk_rec_key_t const *  key,
     142             :                   fd_funk_rec_t **           out_rec,
     143           0 :                   _Bool                      is_write ) {
     144           0 :   *out_rec = NULL;
     145             : 
     146           0 :   fd_accdb_lineage_t const *                    lineage   = accdb->lineage;
     147           0 :   fd_funk_rec_map_shmem_t const *               shmap     = accdb->funk->rec_map->map;
     148           0 :   fd_funk_rec_map_shmem_private_chain_t const * chain_tbl = fd_funk_rec_map_shmem_private_chain_const( shmap, 0UL );
     149           0 :   fd_funk_rec_map_shmem_private_chain_t const * chain     = chain_tbl + chain_idx;
     150           0 :   fd_funk_rec_t *                               rec_tbl   = accdb->funk->rec_pool->ele;
     151           0 :   ulong                                         rec_max   = fd_funk_rec_pool_ele_max( accdb->funk->rec_pool );
     152             : 
     153           0 :   uint            ele_idx   = chain->head_cidx;
     154           0 :   ulong _Atomic * ver_cnt_p = (ulong _Atomic *)&chain->ver_cnt;
     155           0 :   ulong           ver_cnt   = atomic_load_explicit( ver_cnt_p, memory_order_acquire );
     156             : 
     157             :   /* Start a speculative transaction for the chain containing revisions
     158             :      of the account key we are looking for. */
     159           0 :   ulong cnt = fd_funk_rec_map_private_vcnt_cnt( ver_cnt );
     160           0 :   if( FD_UNLIKELY( fd_funk_rec_map_private_vcnt_ver( ver_cnt )&1 ) ) {
     161           0 :     return ACQUIRE_FAILED; /* chain is locked */
     162           0 :   }
     163             : 
     164             :   /* Walk the map chain, bail at the first entry
     165             :      (Each chain is sorted newest-to-oldest) */
     166           0 :   fd_funk_rec_t * best = NULL;
     167           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     168           0 :     uint ele_next = rec_tbl[ ele_idx ].map_next;
     169           0 :     if( FD_UNLIKELY( atomic_load_explicit( ver_cnt_p, memory_order_acquire )!=ver_cnt ) ) {
     170           0 :       return ACQUIRE_FAILED;
     171           0 :     }
     172             : 
     173           0 :     if( FD_UNLIKELY( ele_idx>=rec_max ) ) {
     174           0 :       FD_LOG_CRIT(( "funk_rec_acquire detected memory corruption: invalid ele_idx at node %lu:%u (rec_max %lu)",
     175           0 :                     chain_idx, ele_idx, rec_max ));
     176           0 :     }
     177             : 
     178           0 :     fd_funk_rec_t * rec = &rec_tbl[ ele_idx ];
     179             : 
     180             :     /* Skip over unrelated records (hash collision) */
     181           0 :     if( FD_UNLIKELY( !fd_funk_rec_key_eq( rec->pair.key, key ) ) ) goto next;
     182             : 
     183             :     /* Confirm that record is part of the current fork */
     184           0 :     if( FD_UNLIKELY( !fd_accdb_lineage_has_xid( lineage, rec->pair.xid ) ) ) goto next;
     185             : 
     186           0 :     if( FD_UNLIKELY( ele_next==ele_idx ) ) {
     187           0 :       FD_LOG_CRIT(( "funk_rec_acquire detected cycle" ));
     188           0 :     }
     189           0 :     best = rec;
     190           0 :     break;
     191             : 
     192           0 : next:
     193           0 :     ele_idx = ele_next;
     194           0 :   }
     195           0 :   if( FD_UNLIKELY( !best && ele_idx!=FD_FUNK_REC_IDX_NULL ) ) {
     196           0 :     FD_LOG_CRIT(( "funk_rec_acquire detected malformed chain (%lu): found more nodes than chain header indicated (%lu)", chain_idx, cnt ));
     197           0 :   }
     198             : 
     199             :   /* Found a record, acquire a lock */
     200           0 :   if( best ) {
     201             :     /* If the write does not target the current transaction, demote it */
     202           0 :     if( is_write && !fd_accdb_lineage_is_tip( accdb->lineage, best->pair.xid ) ) {
     203           0 :       is_write = 0;
     204           0 :     }
     205           0 :     if( is_write ) {
     206           0 :       fd_funk_rec_write_lock( accdb->funk, best );
     207           0 :     } else {
     208           0 :       if( fd_funk_rec_read_lock( accdb->funk, best )!=FD_MAP_SUCCESS ) {
     209           0 :         return ACQUIRE_FAILED; /* record about to be moved to vinyl */
     210           0 :       }
     211           0 :     }
     212           0 :   }
     213             : 
     214             :   /* Retry if there was contention at the hash map */
     215           0 :   if( FD_UNLIKELY( atomic_load_explicit( ver_cnt_p, memory_order_acquire )!=ver_cnt ) ) {
     216           0 :     if( best ) {
     217           0 :       if( is_write ) fd_funk_rec_write_unlock( accdb->funk, best );
     218           0 :       else           fd_funk_rec_read_unlock ( accdb->funk, best );
     219           0 :     }
     220           0 :     return ACQUIRE_FAILED;
     221           0 :   }
     222             : 
     223           0 :   *out_rec = best;
     224           0 :   return best ? ( is_write ? ACQUIRE_WRITE : ACQUIRE_READ ) : ACQUIRE_NOT_FOUND;
     225           0 : }
     226             : 
     227             : static int
     228             : funk_open_ref( fd_accdb_user_v2_t *      accdb,
     229             :                fd_accdb_ref_t *          ref,
     230             :                fd_funk_txn_xid_t const * xid,
     231             :                void const *              address,
     232           0 :                _Bool                     is_write ) {
     233           0 :   fd_funk_t const * funk = accdb->funk;
     234           0 :   fd_funk_rec_key_t key[1]; memcpy( key->uc, address, 32UL );
     235             : 
     236             :   /* Hash key to chain */
     237           0 :   fd_funk_xid_key_pair_t pair[1];
     238           0 :   fd_funk_txn_xid_copy( pair->xid, xid );
     239           0 :   fd_funk_rec_key_copy( pair->key, key );
     240           0 :   fd_funk_rec_map_t const * rec_map = funk->rec_map;
     241           0 :   ulong hash      = fd_funk_rec_map_key_hash( pair, rec_map->map->seed );
     242           0 :   ulong chain_idx = (hash & (rec_map->map->chain_cnt-1UL) );
     243             : 
     244             :   /* Traverse chain for candidate */
     245           0 :   fd_funk_rec_t * rec = NULL;
     246           0 :   int err;
     247           0 :   for(;;) {
     248           0 :     err = funk_rec_acquire( accdb, chain_idx, key, &rec, is_write );
     249           0 :     if( FD_LIKELY( err!=ACQUIRE_FAILED ) ) break;
     250           0 :     FD_SPIN_PAUSE();
     251             :     /* FIXME backoff */
     252           0 :   }
     253           0 :   if( rec ) {
     254           0 :     memcpy( ref->address, address, 32UL );
     255           0 :     ref->accdb_type = FD_ACCDB_TYPE_V1;
     256           0 :     ref->ref_type   = err==ACQUIRE_WRITE ? FD_ACCDB_REF_RW : FD_ACCDB_REF_RO;
     257           0 :     ref->user_data  = (ulong)rec;
     258           0 :     ref->user_data2 = 0UL;
     259           0 :     ref->meta_laddr = (ulong)fd_funk_val( rec, funk->wksp );
     260           0 :   } else {
     261           0 :     memset( ref, 0, sizeof(fd_accdb_rw_t) );
     262           0 :   }
     263           0 :   return err;
     264           0 : }
     265             : 
     266             : /* Read method ********************************************************/
     267             : 
     268             : void
     269             : fd_accdb_user_v2_open_ro_multi( fd_accdb_user_t *         accdb,
     270             :                                 fd_accdb_ro_t *           ro0,
     271             :                                 fd_funk_txn_xid_t const * xid,
     272             :                                 void const *              addr0,
     273           0 :                                 ulong                     cnt ) {
     274           0 :   fd_accdb_user_v2_t *  v2        = (fd_accdb_user_v2_t *)accdb;
     275           0 :   fd_vinyl_rq_t *       rq        = v2->vinyl_rq;        /* "request queue "*/
     276           0 :   fd_vinyl_req_pool_t * req_pool  = v2->vinyl_req_pool;  /* "request pool" */
     277           0 :   fd_wksp_t *           req_wksp  = v2->vinyl_req_wksp;  /* shm workspace containing request buffer */
     278           0 :   fd_wksp_t *           data_wksp = v2->vinyl_data_wksp; /* shm workspace containing vinyl data cache */
     279           0 :   ulong                 link_id   = v2->vinyl_link_id;   /* vinyl client ID */
     280             : 
     281           0 :   if( FD_UNLIKELY( cnt>fd_vinyl_req_batch_key_max( req_pool ) ) ) {
     282           0 :     FD_LOG_CRIT(( "open_ro_multi cnt %lu exceeds vinyl request batch max %lu",
     283           0 :                   cnt, fd_vinyl_req_batch_key_max( req_pool ) ));
     284           0 :   }
     285             : 
     286             :   /* Open accounts from funk
     287             : 
     288             :      (FIXME this is a potentially slow operation, might want to fire off
     289             :      a 'prefetch' instruction to vinyl asynchronously before doing this,
     290             :      so that the vinyl data is in cache by the time v1_open_rw_multi
     291             :      finishes) */
     292             : 
     293           0 :   fd_accdb_lineage_set_fork( v2->lineage, v2->funk, xid );
     294           0 :   ulong addr_laddr = (ulong)addr0;
     295           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     296           0 :     void const * addr_i = (void const *)( (ulong)addr_laddr + i*32UL );
     297           0 :     if( funk_open_ref( v2, ro0[i].ref, xid, addr_i, 0 )==ACQUIRE_READ ) {
     298           0 :       v2->base.ro_active++;
     299           0 :     } else {
     300           0 :       fd_accdb_ro_init_empty( &ro0[i], addr_i );
     301           0 :     }
     302           0 :   }
     303             : 
     304             :   /* For the accounts that were not found in funk, open vinyl records */
     305             : 
     306           0 :   ulong batch_idx = fd_vinyl_req_pool_acquire( req_pool );
     307             :   /* req_pool_release called before returning */
     308           0 :   fd_vinyl_comp_t * comp           = fd_vinyl_req_batch_comp     ( req_pool, batch_idx );
     309           0 :   fd_vinyl_key_t *  req_key0       = fd_vinyl_req_batch_key      ( req_pool, batch_idx );
     310           0 :   schar *           req_err0       = fd_vinyl_req_batch_err      ( req_pool, batch_idx );
     311           0 :   ulong *           req_val_gaddr0 = fd_vinyl_req_batch_val_gaddr( req_pool, batch_idx );
     312             : 
     313             :   /* Create a read-only vinyl "ACQUIRE" batch */
     314             : 
     315           0 :   ulong req_cnt = 0UL;
     316           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     317           0 :     if( ro0[i].ref->accdb_type!=FD_ACCDB_TYPE_NONE ) continue;
     318             :     /* At this point, addr0[i] not found in funk, load from vinyl */
     319           0 :     void const * addr_i = (void const *)( (ulong)addr0 + i*32UL );
     320             : 
     321           0 :     fd_vinyl_key_init( req_key0+req_cnt, addr_i, 32UL );
     322           0 :     req_err0      [ req_cnt ] = 0;
     323           0 :     req_val_gaddr0[ req_cnt ] = 0UL;
     324           0 :     req_cnt++;
     325           0 :   }
     326           0 :   if( !req_cnt ) {
     327             :     /* All records were found in funk, bail early */
     328           0 :     fd_vinyl_req_pool_release( req_pool, batch_idx );
     329           0 :     return;
     330           0 :   }
     331             : 
     332             :   /* Send read-only "ACQUIRE" batch to vinyl and wait for response */
     333             : 
     334           0 :   ulong req_id = v2->vinyl_req_id++;
     335           0 :   memset( comp, 0, sizeof(fd_vinyl_comp_t) );
     336           0 :   fd_vinyl_req_send_batch( rq, req_pool, req_wksp, req_id, link_id, FD_VINYL_REQ_TYPE_ACQUIRE, 0UL, batch_idx, req_cnt );
     337             : 
     338           0 :   while( FD_VOLATILE_CONST( comp->seq )!=1UL ) FD_SPIN_PAUSE();
     339           0 :   FD_COMPILER_MFENCE();
     340           0 :   int comp_err = FD_VOLATILE_CONST( comp->err );
     341           0 :   if( FD_UNLIKELY( comp_err!=FD_VINYL_SUCCESS ) ) {
     342           0 :     FD_LOG_CRIT(( "vinyl tile rejected my ACQUIRE request: %i-%s", comp_err, fd_vinyl_strerror( comp_err ) ));
     343           0 :   }
     344             : 
     345             :   /* For the accounts that were newly found in vinyl, create accdb
     346             :      handles */
     347             : 
     348           0 :   req_cnt = 0UL;
     349           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     350           0 :     if( ro0[i].ref->accdb_type!=FD_ACCDB_TYPE_NONE ) continue;
     351           0 :     void const * addr_i = (void const *)( (ulong)addr0 + i*32UL );
     352             : 
     353           0 :     int   req_err   = FD_VOLATILE_CONST( req_err0      [ req_cnt ] );
     354           0 :     ulong val_gaddr = FD_VOLATILE_CONST( req_val_gaddr0[ req_cnt ] );
     355             : 
     356           0 :     fd_accdb_ro_t * ro = &ro0[ i ];
     357           0 :     if( req_err==0 ) {
     358             :       /* Record found in vinyl, create reference */
     359           0 :       fd_account_meta_t const * meta = fd_wksp_laddr_fast( data_wksp, val_gaddr );
     360             : 
     361           0 :       accdb->base.ro_active++;
     362           0 :       *ro = (fd_accdb_ro_t) {0};
     363           0 :       memcpy( ro->ref->address, addr_i, 32UL );
     364           0 :       ro->ref->accdb_type = FD_ACCDB_TYPE_V2;
     365           0 :       ro->ref->ref_type   = FD_ACCDB_REF_RO;
     366           0 :       ro->meta            = meta;
     367           0 :     } else if( FD_UNLIKELY( req_err!=FD_VINYL_ERR_KEY ) ) {
     368           0 :       FD_LOG_CRIT(( "vinyl tile ACQUIRE request failed: %i-%s", req_err, fd_vinyl_strerror( req_err ) ));
     369           0 :     }
     370           0 :     req_cnt++;
     371           0 :   }
     372             : 
     373           0 :   fd_vinyl_req_pool_release( req_pool, batch_idx );
     374             : 
     375             :   /* At this point, ownership of vinyl records transitions to caller.
     376             :      (Released using close_ro_multi) */
     377           0 : }
     378             : 
     379             : /* Write method *******************************************************/
     380             : 
     381             : void
     382             : fd_accdb_user_v2_open_rw_multi( fd_accdb_user_t *         accdb,
     383             :                                 fd_accdb_rw_t *           rw0,
     384             :                                 fd_funk_txn_xid_t const * xid,
     385             :                                 void const *              addr0,
     386             :                                 ulong const *             data_max0,
     387             :                                 int                       flags,
     388           0 :                                 ulong                     cnt ) {
     389           0 :   fd_accdb_user_v2_t *  v2        = (fd_accdb_user_v2_t *)accdb;
     390           0 :   fd_funk_t *           funk      = v2->funk;
     391           0 :   fd_vinyl_rq_t *       rq        = v2->vinyl_rq;        /* "request queue "*/
     392           0 :   fd_vinyl_req_pool_t * req_pool  = v2->vinyl_req_pool;  /* "request pool" */
     393           0 :   fd_wksp_t *           req_wksp  = v2->vinyl_req_wksp;  /* shm workspace containing request buffer */
     394           0 :   fd_wksp_t *           data_wksp = v2->vinyl_data_wksp; /* shm workspace containing vinyl data cache */
     395           0 :   ulong                 link_id   = v2->vinyl_link_id;   /* vinyl client ID */
     396             : 
     397           0 :   fd_accdb_lineage_set_fork( v2->lineage, v2->funk, xid );
     398           0 :   fd_funk_txn_t * txn = fd_accdb_lineage_write_check( v2->lineage, v2->funk );
     399             : 
     400           0 :   int const flag_truncate = !!( flags & FD_ACCDB_FLAG_TRUNCATE );
     401           0 :   int const flag_create   = !!( flags & FD_ACCDB_FLAG_CREATE   );
     402           0 :   if( FD_UNLIKELY( flags & ~(FD_ACCDB_FLAG_CREATE|FD_ACCDB_FLAG_TRUNCATE) ) ) {
     403           0 :     FD_LOG_CRIT(( "invalid flags for open_rw: %#02x", (uint)flags ));
     404           0 :   }
     405             : 
     406           0 :   if( FD_UNLIKELY( cnt>fd_vinyl_req_batch_key_max( req_pool ) ) ) {
     407           0 :     FD_LOG_CRIT(( "open_rw_multi cnt %lu exceeds vinyl request batch max %lu",
     408           0 :                   cnt, fd_vinyl_req_batch_key_max( req_pool ) ));
     409           0 :   }
     410             : 
     411             :   /* Query for existing funk records
     412             : 
     413             :      (FIXME this is a potentially slow operation, might want to fire off
     414             :      a 'prefetch' instruction to vinyl asynchronously before doing this,
     415             :      so that the vinyl data is in cache by the time v1_open_rw_multi
     416             :      finishes) */
     417             : 
     418           0 :   ulong addr_laddr = (ulong)addr0;
     419           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     420           0 :     void const * addr_i = (void const *)( (ulong)addr_laddr + i*32UL );
     421           0 :     funk_open_ref( v2, rw0[ i ].ref, xid, addr_i, 1 );
     422           0 :   }
     423             : 
     424             :   /* For the accounts that were not found in funk, create writable funk
     425             :      records from elements in vinyl. */
     426             : 
     427           0 :   ulong batch_idx = fd_vinyl_req_pool_acquire( req_pool );
     428             :   /* req_pool_release called before returning */
     429           0 :   fd_vinyl_comp_t * comp           = fd_vinyl_req_batch_comp     ( req_pool, batch_idx );
     430           0 :   fd_vinyl_key_t *  req_key0       = fd_vinyl_req_batch_key      ( req_pool, batch_idx );
     431           0 :   schar *           req_err0       = fd_vinyl_req_batch_err      ( req_pool, batch_idx );
     432           0 :   ulong *           req_val_gaddr0 = fd_vinyl_req_batch_val_gaddr( req_pool, batch_idx );
     433             : 
     434             :   /* Create a read-only vinyl "ACQUIRE" batch */
     435             : 
     436           0 :   ulong req_cnt = 0UL;
     437           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     438           0 :     if( rw0[i].ref->ref_type!=FD_ACCDB_REF_INVAL ) continue;
     439             :     /* At this point, addr0[i] not found in funk, load from vinyl */
     440           0 :     void const * addr_i = (void const *)( (ulong)addr0 + i*32UL );
     441             : 
     442           0 :     fd_vinyl_key_init( req_key0+req_cnt, addr_i, 32UL );
     443           0 :     req_err0      [ req_cnt ] = 0;
     444           0 :     req_val_gaddr0[ req_cnt ] = 0UL;
     445           0 :     req_cnt++;
     446           0 :   }
     447             : 
     448             :   /* Send read-only "ACQUIRE" batch to vinyl and wait for response */
     449             : 
     450           0 :   if( req_cnt ) {
     451           0 :     ulong req_id = v2->vinyl_req_id++;
     452           0 :     memset( fd_vinyl_req_batch_comp( req_pool, batch_idx ), 0, sizeof(fd_vinyl_comp_t) );
     453           0 :     fd_vinyl_req_send_batch( rq, req_pool, req_wksp, req_id, link_id, FD_VINYL_REQ_TYPE_ACQUIRE, 0UL, batch_idx, req_cnt );
     454             : 
     455           0 :     while( FD_VOLATILE_CONST( comp->seq )!=1UL ) FD_SPIN_PAUSE();
     456           0 :     FD_COMPILER_MFENCE();
     457           0 :     int comp_err = FD_VOLATILE_CONST( comp->err );
     458           0 :     if( FD_UNLIKELY( comp_err!=FD_VINYL_SUCCESS ) ) {
     459           0 :       FD_LOG_CRIT(( "vinyl tile rejected my ACQUIRE request: %i-%s", comp_err, fd_vinyl_strerror( comp_err ) ));
     460           0 :     }
     461           0 :   }
     462             : 
     463             :   /* Promote any found accounts to writable accounts */
     464             : 
     465           0 :   req_cnt = 0UL;
     466           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     467           0 :     void const *    addr_i   = (void const *)( (ulong)addr0 + i*32UL );
     468           0 :     fd_accdb_rw_t * rw       = &rw0[ i ];
     469           0 :     fd_funk_rec_t * rec      = (fd_funk_rec_t *)rw->ref->user_data;
     470           0 :     ulong           data_max = data_max0[ i ];
     471             : 
     472           0 :     if( rw->ref->ref_type==FD_ACCDB_REF_RW ) {
     473             :       /* Mutable record found, modify in-place */
     474             : 
     475           0 :       if( FD_UNLIKELY( !flag_create && fd_accdb_ref_lamports( rw->ro )==0UL ) ) {
     476             :         /* Tombstone */
     477           0 :         fd_funk_rec_write_unlock( v2->funk, rec );
     478           0 :         goto not_found;
     479           0 :       }
     480             : 
     481           0 :       ulong  acc_orig_sz = fd_accdb_ref_data_sz( rw->ro );
     482           0 :       ulong  val_sz_min  = sizeof(fd_account_meta_t)+fd_ulong_max( data_max, acc_orig_sz );
     483           0 :       void * val         = fd_funk_val_truncate( rec, funk->alloc, funk->wksp, 16UL, val_sz_min, NULL );
     484           0 :       if( FD_UNLIKELY( !val ) ) {
     485           0 :         FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
     486           0 :       }
     487           0 :       fd_accdb_funk_prep_inplace( rw, funk, rec );
     488           0 :       if( flag_truncate ) {
     489           0 :         rec->val_sz = sizeof(fd_account_meta_t);
     490           0 :         rw->meta->dlen = 0;
     491           0 :       }
     492           0 :       accdb->base.rw_active++;
     493             :       /* Retain write lock */
     494             : 
     495           0 :       continue; /* next account */
     496           0 :     }
     497             : 
     498           0 :     if( rw->ref->ref_type==FD_ACCDB_REF_RO ) {
     499             :       /* Frozen record found, copy out to new object */
     500             : 
     501           0 :       fd_accdb_ro_t * ro = rw->ro;
     502           0 :       if( FD_UNLIKELY( !flag_create && fd_accdb_ref_lamports( ro )==0UL ) ) {
     503             :         /* Tombstone */
     504           0 :         fd_funk_rec_read_unlock( v2->funk, rec );
     505           0 :         goto not_found;
     506           0 :       }
     507             : 
     508           0 :       ulong  acc_orig_sz = fd_accdb_ref_data_sz( ro );
     509           0 :       ulong  val_sz_min  = sizeof(fd_account_meta_t)+fd_ulong_max( data_max, acc_orig_sz );
     510           0 :       ulong  val_sz      = flag_truncate ? sizeof(fd_account_meta_t) : rec->val_sz;
     511           0 :       ulong  val_max     = 0UL;
     512           0 :       void * val         = fd_alloc_malloc_at_least( funk->alloc, 16UL, val_sz_min, &val_max );
     513           0 :       if( FD_UNLIKELY( !val ) ) {
     514           0 :         FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
     515           0 :       }
     516             : 
     517           0 :       fd_account_meta_t * meta            = val;
     518           0 :       uchar *             data            = (uchar *)( meta+1 );
     519           0 :       ulong               data_max_actual = val_max - sizeof(fd_account_meta_t);
     520           0 :       if( flag_truncate ) fd_accdb_funk_copy_truncated( meta,       ro->meta );
     521           0 :       else                fd_accdb_funk_copy_account  ( meta, data, ro->meta, fd_account_data( ro->meta ) );
     522           0 :       if( acc_orig_sz<data_max_actual ) {
     523             :         /* Zero out trailing data */
     524           0 :         uchar * tail    = data           +acc_orig_sz;
     525           0 :         ulong   tail_sz = data_max_actual-acc_orig_sz;
     526           0 :         fd_memset( tail, 0, tail_sz );
     527           0 :       }
     528             : 
     529           0 :       fd_accdb_funk_prep_create( rw, funk, txn, addr_i, val, val_sz, val_max );
     530           0 :       accdb->base.rw_active++;
     531           0 :       accdb->base.created_cnt++;
     532             : 
     533           0 :       FD_COMPILER_MFENCE();
     534           0 :       fd_funk_rec_read_unlock( v2->funk, rec );
     535             : 
     536           0 :       continue; /* next account */
     537           0 :     }
     538             : 
     539             :     /* Record not found in funk */
     540             : 
     541           0 :     int req_err = req_err0[ req_cnt ];
     542           0 :     if( req_err ) {
     543             :       /* Record not found in vinyl either (truly does not exist)
     544             :          If CREATE flag was requested, create it in funk */
     545           0 :       if( FD_UNLIKELY( req_err!=FD_VINYL_ERR_KEY ) ) {
     546           0 :         FD_LOG_CRIT(( "vinyl tile ACQUIRE request failed: %i-%s", req_err, fd_vinyl_strerror( req_err ) ));
     547           0 :       }
     548           0 : not_found:
     549           0 :       if( flag_create ) {
     550           0 :         fd_accdb_funk_create( v2->funk, rw, txn, addr_i, data_max0[ i ] );
     551           0 :         fd_funk_rec_write_lock_uncontended( v2->funk, (fd_funk_rec_t *)rw->ref->user_data );
     552           0 :         accdb->base.rw_active++;
     553           0 :       } else {
     554           0 :         memset( rw, 0, sizeof(fd_accdb_ref_t) );
     555           0 :       }
     556           0 :       req_cnt++;
     557           0 :       continue;
     558           0 :     }
     559             : 
     560             :     /* Record found in vinyl */
     561             : 
     562           0 :     ulong               req_val_gaddr = req_val_gaddr0[ req_cnt ];
     563           0 :     fd_account_meta_t * src_meta      = fd_wksp_laddr_fast( data_wksp, req_val_gaddr );
     564           0 :     uchar const *       src_data      = (uchar *)( src_meta+1 );
     565             : 
     566           0 :     if( FD_UNLIKELY( src_meta->lamports==0UL ) ) goto not_found; /* tombstone */
     567             : 
     568           0 :     ulong  acc_orig_sz = src_meta->dlen;
     569           0 :     ulong  val_sz_min  = sizeof(fd_account_meta_t)+fd_ulong_max( data_max0[ i ], acc_orig_sz );
     570           0 :     ulong  acc_sz      = flag_truncate ? 0UL : acc_orig_sz;
     571           0 :     ulong  val_sz      = sizeof(fd_account_meta_t)+acc_sz;
     572           0 :     ulong  val_max     = 0UL;
     573           0 :     void * val         = fd_alloc_malloc_at_least( v2->funk->alloc, 16UL, val_sz_min, &val_max );
     574           0 :     if( FD_UNLIKELY( !val ) ) {
     575           0 :       FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
     576           0 :     }
     577             : 
     578           0 :     fd_account_meta_t * dst_meta        = val;
     579           0 :     uchar *             dst_data        = (uchar *)( dst_meta+1 );
     580           0 :     ulong               data_max_actual = val_max - sizeof(fd_account_meta_t);
     581           0 :     if( flag_truncate ) fd_accdb_funk_copy_truncated( dst_meta,           src_meta           );
     582           0 :     else                fd_accdb_funk_copy_account  ( dst_meta, dst_data, src_meta, src_data );
     583           0 :     if( acc_orig_sz<data_max_actual ) {
     584             :       /* Zero out trailing data */
     585           0 :       uchar * tail    = dst_data       +acc_orig_sz;
     586           0 :       ulong   tail_sz = data_max_actual-acc_orig_sz;
     587           0 :       fd_memset( tail, 0, tail_sz );
     588           0 :     }
     589             : 
     590           0 :     fd_accdb_funk_prep_create( rw, v2->funk, txn, addr_i, val, val_sz, val_max );
     591           0 :     fd_funk_rec_write_lock_uncontended( v2->funk, (fd_funk_rec_t *)rw->ref->user_data );
     592             : 
     593           0 :     req_cnt++;
     594           0 :     accdb->base.rw_active++;
     595           0 :     accdb->base.created_cnt++;
     596           0 :   }
     597             : 
     598             :   /* Send "RELEASE" batch (reuse val_gaddr values),
     599             :      and wait for response */
     600             : 
     601           0 :   if( req_cnt ) {
     602           0 :     ulong req_id = v2->vinyl_req_id++;
     603           0 :     memset( fd_vinyl_req_batch_comp( req_pool, batch_idx ), 0, sizeof(fd_vinyl_comp_t) );
     604           0 :     fd_vinyl_req_send_batch( rq, req_pool, req_wksp, req_id, link_id, FD_VINYL_REQ_TYPE_RELEASE, 0UL, batch_idx, req_cnt );
     605             : 
     606           0 :     while( FD_VOLATILE_CONST( comp->seq )!=1UL ) FD_SPIN_PAUSE();
     607           0 :     FD_COMPILER_MFENCE();
     608           0 :     int comp_err = FD_VOLATILE_CONST( comp->err );
     609           0 :     if( FD_UNLIKELY( comp_err!=FD_VINYL_SUCCESS ) ) {
     610           0 :       FD_LOG_CRIT(( "vinyl tile rejected my RELEASE request: %i-%s", comp_err, fd_vinyl_strerror( comp_err ) ));
     611           0 :     }
     612           0 :   }
     613             : 
     614           0 :   fd_vinyl_req_pool_release( req_pool, batch_idx );
     615           0 : }
     616             : 
     617             : void
     618             : funk_close_rw( fd_accdb_user_v2_t * accdb,
     619           0 :                fd_accdb_rw_t *      write ) {
     620           0 :   fd_funk_rec_t * rec = (fd_funk_rec_t *)write->ref->user_data;
     621             : 
     622           0 :   if( FD_UNLIKELY( !accdb->base.rw_active ) ) {
     623           0 :     FD_LOG_CRIT(( "Failed to modify account: ref count underflow" ));
     624           0 :   }
     625             : 
     626           0 :   if( write->ref->user_data2 ) {
     627           0 :     fd_funk_txn_t * txn = (fd_funk_txn_t *)write->ref->user_data2;
     628           0 :     fd_funk_rec_prepare_t prepare = {
     629           0 :       .rec          = rec,
     630           0 :       .rec_head_idx = &txn->rec_head_idx,
     631           0 :       .rec_tail_idx = &txn->rec_tail_idx
     632           0 :     };
     633           0 :     fd_funk_rec_publish( accdb->funk, &prepare );
     634           0 :   }
     635             : 
     636           0 :   fd_funk_rec_write_unlock( accdb->funk, rec );
     637           0 :   accdb->base.rw_active--;
     638           0 : }
     639             : 
     640             : void
     641             : fd_accdb_user_v2_close_ref_multi( fd_accdb_user_t * accdb,
     642             :                                   fd_accdb_ref_t *  ref0,
     643           0 :                                   ulong             cnt ) {
     644           0 :   fd_accdb_user_v2_t *  v2        = (fd_accdb_user_v2_t *)accdb;
     645           0 :   fd_vinyl_rq_t *       rq        = v2->vinyl_rq;        /* "request queue "*/
     646           0 :   fd_vinyl_req_pool_t * req_pool  = v2->vinyl_req_pool;  /* "request pool" */
     647           0 :   fd_wksp_t *           req_wksp  = v2->vinyl_req_wksp;  /* shm workspace containing request buffer */
     648           0 :   fd_wksp_t *           data_wksp = v2->vinyl_data_wksp; /* shm workspace containing vinyl data cache */
     649           0 :   ulong                 link_id   = v2->vinyl_link_id;   /* vinyl client ID */
     650             : 
     651           0 :   if( FD_UNLIKELY( cnt>fd_vinyl_req_batch_key_max( req_pool ) ) ) {
     652           0 :     FD_LOG_CRIT(( "close_ref_multi cnt %lu exceeds vinyl request batch max %lu",
     653           0 :                   cnt, fd_vinyl_req_batch_key_max( req_pool ) ));
     654           0 :   }
     655             : 
     656             :   /* First, release all references to vinyl records
     657             :      (This is a prefetch friendly / fast loop) */
     658             : 
     659           0 :   ulong batch_idx = fd_vinyl_req_pool_acquire( req_pool );
     660             :   /* req_pool_release called before returning */
     661           0 :   fd_vinyl_comp_t * comp           = fd_vinyl_req_batch_comp     ( req_pool, batch_idx );
     662           0 :   schar *           req_err0       = fd_vinyl_req_batch_err      ( req_pool, batch_idx );
     663           0 :   ulong *           req_val_gaddr0 = fd_vinyl_req_batch_val_gaddr( req_pool, batch_idx );
     664             : 
     665           0 :   ulong ro_close_cnt = 0UL;
     666           0 :   ulong rw_close_cnt = 0UL;
     667           0 :   ulong req_cnt      = 0UL;
     668           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     669           0 :     fd_accdb_ref_t * ref = &ref0[ i ];
     670           0 :     if( ref->accdb_type!=FD_ACCDB_TYPE_V2 ) continue;
     671           0 :     ref->ref_type==FD_ACCDB_REF_RO ? ro_close_cnt++ : rw_close_cnt++;
     672           0 :     req_err0      [ req_cnt ] = 0;
     673           0 :     req_val_gaddr0[ req_cnt ] = fd_wksp_gaddr_fast( data_wksp, (void *)ref->meta_laddr );
     674           0 :     memset( ref, 0, sizeof(fd_accdb_ref_t) );
     675           0 :     req_cnt++;
     676           0 :   }
     677           0 :   if( req_cnt ) {
     678           0 :     if( FD_UNLIKELY( ro_close_cnt > accdb->base.ro_active ) ) {
     679           0 :       FD_LOG_CRIT(( "attempted to close more accdb_ro (%lu) than are open (%lu)",
     680           0 :                     ro_close_cnt, accdb->base.ro_active ));
     681           0 :     }
     682           0 :     if( FD_UNLIKELY( rw_close_cnt > accdb->base.rw_active ) ) {
     683           0 :       FD_LOG_CRIT(( "attempted to close more accdb_rw (%lu) than are open (%lu)",
     684           0 :                     rw_close_cnt, accdb->base.rw_active ));
     685           0 :     }
     686           0 :     ulong req_id = v2->vinyl_req_id++;
     687           0 :     memset( fd_vinyl_req_batch_comp( req_pool, batch_idx ), 0, sizeof(fd_vinyl_comp_t) );
     688           0 :     fd_vinyl_req_send_batch( rq, req_pool, req_wksp, req_id, link_id, FD_VINYL_REQ_TYPE_RELEASE, 0UL, batch_idx, req_cnt );
     689           0 :   }
     690             : 
     691             :   /* While our vinyl request is inflight, release funk records
     692             :      (This does expensive DRAM accesses, which are convenient to do when
     693             :      we are waiting for the database to asynchronously respond) */
     694             : 
     695           0 :   for( ulong i=0UL; i<cnt; i++ ) {
     696           0 :     fd_accdb_ref_t * ref = &ref0[ i ];
     697           0 :     if( ref->accdb_type!=FD_ACCDB_TYPE_V1 ) continue;
     698           0 :     switch( ref0[ i ].ref_type ) {
     699           0 :     case FD_ACCDB_REF_RO:
     700           0 :       accdb->base.ro_active--;
     701           0 :       fd_funk_rec_read_unlock( v2->funk, (fd_funk_rec_t *)ref->user_data );
     702           0 :       break;
     703           0 :     case FD_ACCDB_REF_RW:
     704           0 :       funk_close_rw( v2, (fd_accdb_rw_t *)ref );
     705           0 :       break;
     706           0 :     default:
     707           0 :       FD_LOG_CRIT(( "invalid ref_type %u in fd_accdb_user_v1_close_ref", (uint)ref->ref_type ));
     708           0 :     }
     709           0 :     memset( ref, 0, sizeof(fd_accdb_ref_t) );
     710           0 :   }
     711             : 
     712             :   /* Wait for response from vinyl */
     713             : 
     714           0 :   if( req_cnt ) {
     715           0 :     while( FD_VOLATILE_CONST( comp->seq )!=1UL ) FD_SPIN_PAUSE();
     716           0 :     FD_COMPILER_MFENCE();
     717           0 :     int comp_err = FD_VOLATILE_CONST( comp->err );
     718           0 :     if( FD_UNLIKELY( comp_err!=FD_VINYL_SUCCESS ) ) {
     719           0 :       FD_LOG_CRIT(( "vinyl tile rejected my RELEASE request: %i-%s", comp_err, fd_vinyl_strerror( comp_err ) ));
     720           0 :     }
     721           0 :     for( ulong i=0UL; i<req_cnt; i++ ) {
     722           0 :       int req_err = req_err0[ i ];
     723           0 :       if( FD_UNLIKELY( req_err!=FD_VINYL_SUCCESS ) ) {
     724           0 :         FD_LOG_CRIT(( "vinyl tile RELEASE request failed: %i-%s", req_err, fd_vinyl_strerror( req_err ) ));
     725           0 :       }
     726           0 :     }
     727             : 
     728           0 :     accdb->base.ro_active -= ro_close_cnt;
     729           0 :     accdb->base.rw_active -= rw_close_cnt;
     730           0 :   }
     731             : 
     732           0 :   fd_vinyl_req_pool_release( req_pool, batch_idx );
     733           0 : }
     734             : 
     735             : ulong
     736             : fd_accdb_user_v2_rw_data_max( fd_accdb_user_t *     accdb,
     737           0 :                               fd_accdb_rw_t const * rw ) {
     738           0 :   (void)accdb;
     739           0 :   if( rw->ref->accdb_type==FD_ACCDB_TYPE_NONE ) {
     740           0 :     return rw->ref->user_data; /* data_max */
     741           0 :   }
     742           0 :   fd_funk_rec_t * rec = (fd_funk_rec_t *)rw->ref->user_data;
     743           0 :   return (ulong)( rec->val_max - sizeof(fd_account_meta_t) );
     744           0 : }
     745             : 
     746             : void
     747             : fd_accdb_user_v2_rw_data_sz_set( fd_accdb_user_t * accdb,
     748             :                                  fd_accdb_rw_t *   rw,
     749             :                                  ulong             data_sz,
     750           0 :                                  int               flags ) {
     751           0 :   int flag_dontzero = !!( flags & FD_ACCDB_FLAG_DONTZERO );
     752           0 :   if( FD_UNLIKELY( flags & ~(FD_ACCDB_FLAG_DONTZERO) ) ) {
     753           0 :     FD_LOG_CRIT(( "invalid flags for rw_data_sz_set: %#02x", (uint)flags ));
     754           0 :   }
     755             : 
     756           0 :   ulong prev_sz = rw->meta->dlen;
     757           0 :   if( data_sz>prev_sz ) {
     758           0 :     ulong data_max = fd_accdb_user_v2_rw_data_max( accdb, rw );
     759           0 :     if( FD_UNLIKELY( data_sz>data_max ) ) {
     760           0 :       FD_LOG_CRIT(( "attempted to write %lu bytes into a rec with only %lu bytes of data space",
     761           0 :                     data_sz, data_max ));
     762           0 :     }
     763           0 :     if( !flag_dontzero ) {
     764           0 :       void * tail = (uchar *)fd_accdb_ref_data( rw ) + prev_sz;
     765           0 :       fd_memset( tail, 0, data_sz-prev_sz );
     766           0 :     }
     767           0 :   }
     768           0 :   rw->meta->dlen = (uint)data_sz;
     769             : 
     770           0 :   if( rw->ref->accdb_type==FD_ACCDB_TYPE_V1 ) {
     771           0 :     fd_funk_rec_t * rec = (fd_funk_rec_t *)rw->ref->user_data;
     772           0 :     rec->val_sz = (uint)( sizeof(fd_account_meta_t)+data_sz ) & FD_FUNK_REC_VAL_MAX;
     773           0 :   }
     774           0 : }
     775             : 
     776             : fd_accdb_user_t *
     777             : fd_accdb_user_v2_init( fd_accdb_user_t * accdb_,
     778             :                        void *            shfunk,
     779             :                        void *            shlocks,
     780             :                        void *            vinyl_rq,
     781             :                        void *            vinyl_data,
     782             :                        void *            vinyl_req_pool,
     783             :                        ulong             vinyl_link_id,
     784           0 :                        ulong             max_depth ) {
     785           0 :   if( FD_UNLIKELY( !accdb_ ) ) {
     786           0 :     FD_LOG_WARNING(( "NULL ljoin" ));
     787           0 :     return NULL;
     788           0 :   }
     789           0 :   if( FD_UNLIKELY( !shfunk ) ) {
     790           0 :     FD_LOG_WARNING(( "NULL shfunk" ));
     791           0 :     return NULL;
     792           0 :   }
     793           0 :   if( FD_UNLIKELY( !vinyl_data ) ) {
     794           0 :     FD_LOG_WARNING(( "NULL vinyl_data" ));
     795           0 :     return NULL;
     796           0 :   }
     797             : 
     798           0 :   fd_accdb_user_v2_t * accdb = fd_type_pun( accdb_ );
     799           0 :   memset( accdb, 0, sizeof(fd_accdb_user_v2_t) );
     800             : 
     801           0 :   if( FD_UNLIKELY( !fd_funk_join( accdb->funk, shfunk, shlocks ) ) ) {
     802           0 :     FD_LOG_CRIT(( "fd_funk_join failed" ));
     803           0 :   }
     804             : 
     805           0 :   fd_vinyl_rq_t *       rq       = fd_vinyl_rq_join( vinyl_rq );
     806           0 :   fd_vinyl_req_pool_t * req_pool = fd_vinyl_req_pool_join( vinyl_req_pool );
     807           0 :   if( FD_UNLIKELY( !rq || !req_pool ) ) {
     808             :     /* component joins log warning if this is reached */
     809           0 :     FD_LOG_WARNING(( "Failed to initialize database client" ));
     810           0 :     return NULL;
     811           0 :   }
     812             : 
     813           0 :   accdb->lineage->max_depth = max_depth;
     814           0 :   accdb->vinyl_req_id       = 0UL;
     815           0 :   accdb->vinyl_rq           = rq;
     816           0 :   accdb->vinyl_link_id      = vinyl_link_id;
     817           0 :   accdb->vinyl_data_wksp    = vinyl_data;
     818           0 :   accdb->vinyl_req_wksp     = fd_wksp_containing( req_pool );
     819           0 :   accdb->vinyl_req_pool     = req_pool;
     820           0 :   accdb->base.accdb_type    = FD_ACCDB_TYPE_V2;
     821           0 :   accdb->base.vt            = &fd_accdb_user_v2_vt;
     822           0 :   return accdb_;
     823           0 : }
     824             : 
     825             : void
     826           0 : fd_accdb_user_v2_fini( fd_accdb_user_t * accdb ) {
     827           0 :   fd_accdb_user_v2_t * user = (fd_accdb_user_v2_t *)accdb;
     828             : 
     829           0 :   fd_vinyl_rq_leave( user->vinyl_rq );
     830             : 
     831           0 :   if( FD_UNLIKELY( !fd_funk_leave( user->funk, NULL, NULL ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
     832           0 : }
     833             : 
     834             : ulong
     835           0 : fd_accdb_user_v2_batch_max( fd_accdb_user_t * accdb ) {
     836           0 :   fd_accdb_user_v2_t * user = (fd_accdb_user_v2_t *)accdb;
     837           0 :   return fd_vinyl_req_batch_key_max( user->vinyl_req_pool );
     838           0 : }
     839             : 
     840             : 
     841             : fd_accdb_user_vt_t const fd_accdb_user_v2_vt = {
     842             :   .fini            = fd_accdb_user_v2_fini,
     843             :   .batch_max       = fd_accdb_user_v2_batch_max,
     844             :   .open_ro_multi   = fd_accdb_user_v2_open_ro_multi,
     845             :   .open_rw_multi   = fd_accdb_user_v2_open_rw_multi,
     846             :   .close_ref_multi = fd_accdb_user_v2_close_ref_multi,
     847             :   .rw_data_max     = fd_accdb_user_v2_rw_data_max,
     848             :   .rw_data_sz_set  = fd_accdb_user_v2_rw_data_sz_set
     849             : };

Generated by: LCOV version 1.14