diff --git a/src/discof/restore/Local.mk b/src/discof/restore/Local.mk index ec6d8e6a8a..d739c88ebf 100644 --- a/src/discof/restore/Local.mk +++ b/src/discof/restore/Local.mk @@ -7,6 +7,10 @@ ifdef FD_HAS_ZSTD $(call add-objs,fd_snapdc_tile,fd_discof) endif # FD_HAS_ZSTD $(call add-objs,fd_snapin_tile fd_snapin_tile_funk fd_snapin_tile_vinyl,fd_discof) +ifdef FD_HAS_HOSTED +$(call make-unit-test,test_snapin_tile,test_snapin_tile,fd_discof fd_disco fd_flamenco fd_vinyl fd_funk fd_tango fd_ballet fd_util) +$(call run-unit-test,test_snapin_tile) +endif # FD_HAS_HOSTED endif # FD_HAS_SSE $(call add-objs,fd_snapwh_tile,fd_discof) $(call add-objs,fd_snapwr_tile,fd_discof) diff --git a/src/discof/restore/fd_snapin_tile.c b/src/discof/restore/fd_snapin_tile.c index 83fa7c1ff7..b6429f96ab 100644 --- a/src/discof/restore/fd_snapin_tile.c +++ b/src/discof/restore/fd_snapin_tile.c @@ -891,6 +891,7 @@ unprivileged_init( fd_topo_t * topo, #include "../../disco/stem/fd_stem.c" +#ifndef FD_TILE_TEST fd_topo_run_tile_t fd_tile_snapin = { .name = NAME, .populate_allowed_fds = populate_allowed_fds, @@ -901,5 +902,6 @@ fd_topo_run_tile_t fd_tile_snapin = { .unprivileged_init = unprivileged_init, .run = stem_run, }; +#endif #undef NAME diff --git a/src/discof/restore/test_snapin_tile.c b/src/discof/restore/test_snapin_tile.c new file mode 100644 index 0000000000..35fb16a581 --- /dev/null +++ b/src/discof/restore/test_snapin_tile.c @@ -0,0 +1,337 @@ +#include "fd_snapin_tile.c" +#include "../../disco/topo/fd_topob.h" +#include "utils/fd_ssctrl.h" +#include /* aligned_alloc */ +#define WKSP_TAG 1UL + +struct snapin_topo { + fd_topo_t * topo; + + /* Shared memory objects */ + void * accdb_funk; + void * txncache; + + /* Topo links */ + fd_topo_link_t * in_link; + fd_topo_link_t * out_mf_link; + fd_topo_link_t * out_ct_link; + + /* Tile context */ + void * scratch; + + /* Stem bits */ + fd_frag_meta_t * stem_mcaches [2]; + ulong stem_seqs [2]; + ulong stem_depths [2]; + ulong stem_cr_avail[2]; + ulong stem_min_cr_avail; +}; +typedef struct snapin_topo snapin_topo_t; + +static void +snapin_topo_init( snapin_topo_t * t, + fd_wksp_t * wksp ) { + fd_topo_t * topo = fd_wksp_alloc_laddr( wksp, alignof(fd_topo_t), sizeof(fd_topo_t), WKSP_TAG ); + FD_TEST( topo ); + t->topo = topo; + fd_topob_new( topo, "snapin" ); + + fd_topo_wksp_t * topo_wksp = fd_topob_wksp( topo, "snapin" ); + topo_wksp->wksp = wksp; + + ulong const funk_rec_max = 32UL; + ulong const funk_txn_max = 16UL; + t->accdb_funk = fd_wksp_alloc_laddr( wksp, fd_funk_align(), fd_funk_footprint( funk_txn_max, funk_rec_max ), WKSP_TAG ); + FD_TEST( fd_funk_new( t->accdb_funk, WKSP_TAG, 1UL, funk_txn_max, funk_rec_max ) ); + fd_topo_obj_t * funk_obj = fd_topob_obj( topo, "funk", "snapin" ); + funk_obj->offset = fd_wksp_gaddr_fast( wksp, t->accdb_funk ); + + ulong const txncache_max_live_slots = 4UL; + ulong const txncache_max_txn_per_slot = 4UL; + t->txncache = fd_wksp_alloc_laddr( wksp, fd_txncache_shmem_align(), fd_txncache_shmem_footprint( txncache_max_live_slots, txncache_max_txn_per_slot ), WKSP_TAG ); + FD_TEST( fd_txncache_shmem_new( t->txncache, txncache_max_live_slots, txncache_max_txn_per_slot ) ); + fd_topo_obj_t * txncache_obj = fd_topob_obj( topo, "txncache", "snapin" ); + txncache_obj->offset = fd_wksp_gaddr_fast( wksp, t->txncache ); + + ulong const in_depth = 4UL; + ulong const in_mtu = USHORT_MAX; + void * in_mcache = fd_wksp_alloc_laddr( wksp, fd_mcache_align(), fd_mcache_footprint( in_depth, 0UL ), WKSP_TAG ); + /* */fd_mcache_new( in_mcache, in_depth, 0UL, 0UL ); + fd_topo_obj_t * in_mcache_obj = fd_topob_obj( topo, "mcache", "snapin" ); + in_mcache_obj->offset = fd_wksp_gaddr_fast( wksp, in_mcache ); + ulong const in_data_sz = fd_dcache_req_data_sz( in_mtu, in_depth, 0UL, 1 ); + void * in_dcache = fd_wksp_alloc_laddr( wksp, fd_dcache_align(), fd_dcache_footprint( in_data_sz, 0UL ), WKSP_TAG ); + FD_TEST( fd_dcache_new( in_dcache, in_data_sz, 0UL ) ); + fd_topo_obj_t * in_dcache_obj = fd_topob_obj( topo, "dcache", "snapin" ); + in_dcache_obj->offset = fd_wksp_gaddr_fast( wksp, in_dcache ); + fd_topo_link_t * in_link = fd_topob_link( topo, "snapdc_in", "snapin", 4UL, in_mtu, 0UL ); + in_link->mcache_obj_id = in_mcache_obj->id; + in_link->dcache_obj_id = in_dcache_obj->id; + in_link->mcache = fd_mcache_join( in_mcache ); + in_link->dcache = fd_dcache_join( in_dcache ); + t->in_link = in_link; + + ulong const out_ct_depth = 4UL; + void * out_ct_mcache = fd_wksp_alloc_laddr( wksp, fd_mcache_align(), fd_mcache_footprint( out_ct_depth, 0UL ), WKSP_TAG ); + FD_TEST( fd_mcache_new( out_ct_mcache, out_ct_depth, 0UL, 0UL ) ); + fd_topo_obj_t * out_ct_mcache_obj = fd_topob_obj( topo, "mcache", "snapin" ); + out_ct_mcache_obj->offset = fd_wksp_gaddr_fast( wksp, out_ct_mcache ); + fd_topo_link_t * out_ct_link = fd_topob_link( topo, "snapin_ct", "snapin", 4UL, 0UL, 0UL ); + out_ct_link->mcache_obj_id = out_ct_mcache_obj->id; + out_ct_link->mcache = fd_mcache_join( out_ct_mcache ); + t->out_ct_link = out_ct_link; + + ulong const out_mf_depth = 4UL; + void * out_mf_mcache = fd_wksp_alloc_laddr( wksp, fd_mcache_align(), fd_mcache_footprint( out_mf_depth, 0UL ), WKSP_TAG ); + FD_TEST( fd_mcache_new( out_mf_mcache, out_mf_depth, 0UL, 0UL ) ); + fd_topo_obj_t * out_mf_mcache_obj = fd_topob_obj( topo, "mcache", "snapin" ); + out_mf_mcache_obj->offset = fd_wksp_gaddr_fast( wksp, out_mf_mcache ); + ulong const out_mf_data_sz = fd_dcache_req_data_sz( USHORT_MAX, out_mf_depth, 0UL, 1 ); + void * out_mf_dcache = fd_wksp_alloc_laddr( wksp, fd_dcache_align(), fd_dcache_footprint( out_mf_data_sz, 0UL ), WKSP_TAG ); + FD_TEST( fd_dcache_new( out_mf_dcache, out_mf_data_sz, 0UL ) ); + fd_topo_obj_t * out_mf_dcache_obj = fd_topob_obj( topo, "dcache", "snapin" ); + out_mf_dcache_obj->offset = fd_wksp_gaddr_fast( wksp, out_mf_dcache ); + fd_topo_link_t * out_mf_link = fd_topob_link( topo, "snapin_manif", "snapin", 4UL, 0UL, 0UL ); + out_mf_link->mcache_obj_id = out_mf_mcache_obj->id; + out_mf_link->dcache_obj_id = out_mf_dcache_obj->id; + out_mf_link->mcache = fd_mcache_join( out_mf_mcache ); + out_mf_link->dcache = fd_dcache_join( out_mf_dcache ); + t->out_mf_link = out_mf_link; + + fd_topo_tile_t * topo_tile = fd_topob_tile( topo, "snapin", "snapin", "snapin", 0UL, 0, 0 ); + topo_tile->snapin.use_vinyl = 0; + topo_tile->snapin.lthash_disabled = 1; + topo_tile->snapin.max_live_slots = 32UL; + topo_tile->snapin.funk_obj_id = funk_obj->id; + topo_tile->snapin.txncache_obj_id = txncache_obj->id; + + fd_topob_tile_in ( topo, "snapin", 0UL, "snapin", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); + fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL ); + fd_topob_tile_out( topo, "snapin", 0UL, "snapin_manif", 0UL ); + + ulong const tile_footprint = scratch_footprint( topo_tile ); + FD_LOG_INFO(( "snapin tile has %lu bytes scratch region", tile_footprint )); + void * tile_scratch = fd_wksp_alloc_laddr( wksp, scratch_align(), tile_footprint, WKSP_TAG ); + FD_TEST( tile_scratch ); + t->scratch = tile_scratch; + fd_topo_obj_t * tile_obj = fd_topob_obj( topo, "tile", "snapin" ); + tile_obj->offset = fd_wksp_gaddr_fast( wksp, tile_scratch ); + topo_tile->tile_obj_id = tile_obj->id; + + t->stem_mcaches [0] = t->out_ct_link->mcache; + t->stem_mcaches [1] = t->out_mf_link->mcache; + t->stem_seqs [0] = 0UL; + t->stem_seqs [1] = 0UL; + t->stem_depths [0] = fd_mcache_depth( t->out_ct_link->mcache ); + t->stem_depths [1] = fd_mcache_depth( t->out_mf_link->mcache ); + t->stem_cr_avail[0] = ULONG_MAX; + t->stem_cr_avail[1] = ULONG_MAX; + t->stem_min_cr_avail = ULONG_MAX; + + unprivileged_init( topo, topo_tile ); +} + +static void +snapin_topo_fini( snapin_topo_t * topo ) { + fd_wksp_free_laddr( fd_funk_delete( topo->accdb_funk ) ); + fd_wksp_free_laddr( topo->txncache ); + fd_wksp_free_laddr( fd_mcache_delete( fd_mcache_leave( topo->in_link->mcache ) ) ); + fd_wksp_free_laddr( fd_dcache_delete( fd_dcache_leave( topo->in_link->dcache ) ) ); + fd_wksp_free_laddr( fd_mcache_delete( fd_mcache_leave( topo->out_ct_link->mcache ) ) ); + fd_wksp_free_laddr( fd_mcache_delete( fd_mcache_leave( topo->out_mf_link->mcache ) ) ); + fd_wksp_free_laddr( fd_dcache_delete( fd_dcache_leave( topo->out_mf_link->dcache ) ) ); + fd_wksp_free_laddr( topo->scratch ); + fd_wksp_free_laddr( topo->topo ); +} + +static void +test_funk_incremental( fd_wksp_t * wksp ) { + snapin_topo_t t; + snapin_topo_init( &t, wksp ); + + fd_stem_context_t stem = { + .mcaches = t.stem_mcaches, + .seqs = t.stem_seqs, + .depths = t.stem_depths, + .cr_avail = t.stem_cr_avail, + .min_cr_avail = &t.stem_min_cr_avail + }; + + fd_funk_t funk[1]; + FD_TEST( fd_funk_join( funk, t.accdb_funk ) ); + fd_funk_txn_xid_t root_xid = { .ul={ ULONG_MAX, ULONG_MAX } }; + FD_TEST( fd_funk_txn_xid_eq( fd_funk_last_publish( funk ), &root_xid ) ); + + /* Mark full snapshot download as finished */ + + fd_snapin_tile_t * ctx = t.scratch; + ctx->state = FD_SNAPSHOT_STATE_FINISHING; + FD_TEST( 0==returnable_frag( + ctx, + 0UL, + /* seq */ 0UL, + /* sig */ FD_SNAPSHOT_MSG_CTRL_NEXT, + /* chunk */ 0UL, + /* sz */ 0UL, + /* ctl */ 0UL, + /* tsorig */ 0UL, + /* tspub */ 0UL, + /* stem */ &stem + ) ); + FD_TEST( ctx->state==FD_SNAPSHOT_STATE_IDLE ); + + /* Start an incremental snapshot */ + + FD_TEST( 0==returnable_frag( + ctx, + 0UL, + /* seq */ 1UL, + /* sig */ FD_SNAPSHOT_MSG_CTRL_INIT_INCR, + /* chunk */ 0UL, + /* sz */ 0UL, + /* ctl */ 0UL, + /* tsorig */ 0UL, + /* tspub */ 0UL, + /* stem */ &stem + ) ); + FD_TEST( ctx->state==FD_SNAPSHOT_STATE_PROCESSING ); + + fd_funk_txn_xid_t incremental_xid = { .ul={ LONG_MAX, LONG_MAX } }; + FD_TEST( fd_funk_txn_query( &incremental_xid, funk->txn_map ) ); + + /* Inject a SlotHistory account to pass end-of-snapshot verification */ + + fd_accdb_user_t accdb[1]; + FD_TEST( fd_accdb_user_v1_init( accdb, t.accdb_funk ) ); + { + uchar const data[17] = {0UL}; + fd_accdb_rw_t rw[1]; + FD_TEST( fd_accdb_open_rw( accdb, rw, &root_xid, &fd_sysvar_slot_history_id, sizeof(data), FD_ACCDB_FLAG_CREATE|FD_ACCDB_FLAG_ROOT ) ); + fd_accdb_ref_owner_set ( rw, &fd_sysvar_owner_id ); + fd_accdb_ref_data_set ( rw, data, sizeof(data) ); + fd_accdb_ref_lamports_set( rw, (ulong)1e6 ); + fd_accdb_close_rw( accdb, rw ); + } + fd_accdb_user_fini( accdb ); + + /* Finish incremental */ + + ctx->state = FD_SNAPSHOT_STATE_FINISHING; + ctx->bank_slot = 3UL; + FD_TEST( 0==returnable_frag( + ctx, + 0UL, + /* seq */ 1UL, + /* sig */ FD_SNAPSHOT_MSG_CTRL_DONE, + /* chunk */ 0UL, + /* sz */ 0UL, + /* ctl */ 0UL, + /* tsorig */ 0UL, + /* tspub */ 0UL, + /* stem */ &stem + ) ); + FD_TEST( ctx->state==FD_SNAPSHOT_STATE_IDLE ); + fd_funk_txn_xid_t incr_xid = { .ul={ 3UL, 0UL } }; + FD_TEST( fd_funk_txn_xid_eq( fd_funk_last_publish( funk ), &incr_xid ) ); + + /* Clean up */ + + fd_funk_leave( funk, NULL ); + snapin_topo_fini( &t ); +} + +static void +test_funk_fail_full( fd_wksp_t * wksp ) { + snapin_topo_t t; + snapin_topo_init( &t, wksp ); + + fd_stem_context_t stem = { + .mcaches = t.stem_mcaches, + .seqs = t.stem_seqs, + .depths = t.stem_depths, + .cr_avail = t.stem_cr_avail, + .min_cr_avail = &t.stem_min_cr_avail + }; + + fd_snapin_tile_t * ctx = t.scratch; + ctx->state = FD_SNAPSHOT_STATE_PROCESSING; + + fd_funk_t funk[1]; + FD_TEST( fd_funk_join( funk, t.accdb_funk ) ); + fd_funk_txn_xid_t root_xid = { .ul={ ULONG_MAX, ULONG_MAX } }; + FD_TEST( fd_funk_txn_xid_eq( fd_funk_last_publish( funk ), &root_xid ) ); + + /* React to an error while downloading */ + + FD_TEST( 0==returnable_frag( + ctx, + 0UL, + /* seq */ 0UL, + /* sig */ FD_SNAPSHOT_MSG_CTRL_ERROR, + /* chunk */ 0UL, + /* sz */ 0UL, + /* ctl */ 0UL, + /* tsorig */ 0UL, + /* tspub */ 0UL, + /* stem */ &stem + ) ); + FD_TEST( ctx->state==FD_SNAPSHOT_STATE_ERROR ); + + /* React to a reset signal */ + + FD_TEST( 0==returnable_frag( + ctx, + 0UL, + /* seq */ 1UL, + /* sig */ FD_SNAPSHOT_MSG_CTRL_FAIL, + /* chunk */ 0UL, + /* sz */ 0UL, + /* ctl */ 0UL, + /* tsorig */ 0UL, + /* tspub */ 0UL, + /* stem */ &stem + ) ); + FD_TEST( ctx->state==FD_SNAPSHOT_STATE_IDLE ); + + /* Clean up */ + + fd_funk_leave( funk, NULL ); + snapin_topo_fini( &t ); +} + +int +main( int argc, + char ** argv ) { + fd_boot( &argc, &argv ); + + ulong cpu_idx = fd_tile_cpu_id( fd_tile_idx() ); + if( cpu_idx>fd_shmem_cpu_cnt() ) cpu_idx = 0UL; + + /* The snapin tile requires a large workspace (multiple GiB). Since + we are just doing functional testing here, not performance testing, + use a demand-paged/slow normal page backed workspace via malloc. + (Instead of requiring huge pages or wasting time zero-initializing + upfront). */ + + ulong const mem_req = 5UL<<30; + ulong wksp_part_max = fd_wksp_part_max_est( mem_req, 1UL<<20 ); + ulong wksp_data_max = fd_wksp_data_max_est( mem_req, wksp_part_max ); + void * wksp_mem = aligned_alloc( FD_SHMEM_NORMAL_PAGE_SZ, mem_req ); FD_TEST( wksp_mem ); + fd_wksp_t * wksp = fd_wksp_new( wksp_mem, "snapin", 1U, wksp_part_max, wksp_data_max ); FD_TEST( wksp ); + fd_shmem_join_anonymous( "snapin", FD_SHMEM_JOIN_MODE_READ_WRITE, wksp, wksp_mem, FD_SHMEM_NORMAL_PAGE_SZ, mem_req>>FD_SHMEM_NORMAL_LG_PAGE_SZ ); + + test_funk_incremental( wksp ); + test_funk_fail_full ( wksp ); + + /* Check for memory leaks */ + fd_wksp_usage_t wksp_usage; + FD_TEST( fd_wksp_usage( wksp, NULL, 0UL, &wksp_usage ) ); + FD_TEST( wksp_usage.free_cnt==wksp_usage.total_cnt ); + + fd_shmem_leave_anonymous( wksp, NULL ); + free( fd_wksp_delete( wksp ) ); + + FD_LOG_NOTICE(( "pass" )); + fd_halt(); + return 0; +} diff --git a/src/flamenco/accdb/fd_accdb_impl_v1.c b/src/flamenco/accdb/fd_accdb_impl_v1.c index 03d7f3db43..56951d5379 100644 --- a/src/flamenco/accdb/fd_accdb_impl_v1.c +++ b/src/flamenco/accdb/fd_accdb_impl_v1.c @@ -218,6 +218,45 @@ fd_accdb_peek_funk( fd_accdb_user_v1_t * accdb, return peek; } +fd_accdb_peek_t * +fd_accdb_peek_root( fd_accdb_user_v1_t * accdb, + fd_accdb_peek_t * peek, + void const * address ) { + fd_funk_t const * funk = accdb->funk; + fd_funk_rec_key_t key[1]; memcpy( key->uc, address, 32UL ); + + /* Hash key to chain */ + fd_funk_xid_key_pair_t pair[1]; + fd_funk_txn_xid_set_root( pair->xid ); + fd_funk_rec_key_copy( pair->key, key ); + fd_funk_rec_t * rec = NULL; + for(;;) { + fd_funk_rec_query_t query[1]; + int err = fd_funk_rec_map_query_try( funk->rec_map, pair, NULL, query, 0 ); + if( FD_UNLIKELY( err==FD_MAP_ERR_AGAIN ) ) { + /* FIXME: random backoff */ + FD_SPIN_PAUSE(); + continue; + } + if( err==FD_MAP_ERR_KEY ) return NULL; + FD_CRIT( err==FD_MAP_SUCCESS, "fd_funk_rec_map_query_try failed" ); + rec = fd_funk_rec_map_query_ele( query ); + break; + } + + *peek = (fd_accdb_peek_t) { + .acc = {{ + .rec = rec, + .meta = fd_funk_val( rec, funk->wksp ) + }}, + .spec = {{ + .key = *key, + .keyp = rec->pair.key + }} + }; + return peek; +} + fd_accdb_peek_t * fd_accdb_user_v1_peek( fd_accdb_user_t * accdb, fd_accdb_peek_t * peek, @@ -293,7 +332,7 @@ fd_accdb_prep_create( fd_accdb_rw_t * rw, *rw = (fd_accdb_rw_t) { .rec = rec, .meta = meta, - .published = 0 + .published = !fd_funk_txn_xid_eq_root( xid ) }; return rw; } @@ -365,34 +404,54 @@ fd_accdb_user_v1_open_rw( fd_accdb_user_t * accdb, int const flag_create = !!( flags & FD_ACCDB_FLAG_CREATE ); int const flag_truncate = !!( flags & FD_ACCDB_FLAG_TRUNCATE ); - if( FD_UNLIKELY( flags & ~(FD_ACCDB_FLAG_CREATE|FD_ACCDB_FLAG_TRUNCATE) ) ) { + int const flag_root = !!( flags & FD_ACCDB_FLAG_ROOT ); + if( FD_UNLIKELY( flags & ~(FD_ACCDB_FLAG_CREATE|FD_ACCDB_FLAG_TRUNCATE|FD_ACCDB_FLAG_ROOT) ) ) { FD_LOG_CRIT(( "invalid flags for open_rw: %#02x", (uint)flags )); } - /* Pivot to different fork */ - fd_accdb_load_fork( v1, xid ); - ulong txn_idx = v1->tip_txn_idx; - if( FD_UNLIKELY( txn_idx==ULONG_MAX ) ) { - FD_LOG_CRIT(( "fd_accdb_user_v1_open_rw failed: XID %lu:%lu is rooted", xid->ul[0], xid->ul[1] )); - } - if( FD_UNLIKELY( txn_idx >= fd_funk_txn_pool_ele_max( v1->funk->txn_pool ) ) ) { - FD_LOG_CRIT(( "memory corruption detected: invalid txn_idx %lu (max %lu)", - txn_idx, fd_funk_txn_pool_ele_max( v1->funk->txn_pool ) )); - } - fd_funk_txn_t * txn = &v1->funk->txn_pool->ele[ txn_idx ]; - if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) { - FD_LOG_CRIT(( "Failed to modify account: data race detected on fork node (expected XID %lu:%lu, found %lu:%lu)", - xid->ul[0], xid->ul[1], - txn->xid.ul[0], txn->xid.ul[1] )); - } - if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) { - FD_LOG_CRIT(( "Failed to modify account: XID %lu:%lu has children/is frozen", xid->ul[0], xid->ul[1] )); - } + /* Query old value */ - /* Query old record value */ + fd_accdb_peek_t peek_[1]; + fd_accdb_peek_t * peek = NULL; + fd_funk_txn_xid_t key_xid = *xid; + if( FD_UNLIKELY( flag_root ) ) { - fd_accdb_peek_t peek[1]; - if( FD_UNLIKELY( !fd_accdb_peek_funk( v1, peek, xid, address ) ) ) { + /* Modifying rooted record in-place */ + fd_funk_txn_xid_t const * last_publish = fd_funk_last_publish( v1->funk ); + if( FD_UNLIKELY( !fd_funk_txn_xid_eq( xid, last_publish ) ) ) { + FD_LOG_CRIT(( "fd_accdb_user_v1_open_rw failed: user requested ROOT mode, but XID %lu:%lu is not the last published XID (%lu:%lu)", + xid->ul[0], xid->ul[1], + last_publish->ul[0], last_publish->ul[1] )); + } + fd_funk_txn_xid_set_root( &key_xid ); + peek = fd_accdb_peek_root( v1, peek_, address ); + + } else { + + /* Normal transactional update, pivot to different fork */ + fd_accdb_load_fork( v1, xid ); + ulong txn_idx = v1->tip_txn_idx; + if( FD_UNLIKELY( txn_idx==ULONG_MAX ) ) { + FD_LOG_CRIT(( "fd_accdb_user_v1_open_rw failed: XID %lu:%lu is rooted", xid->ul[0], xid->ul[1] )); + } + if( FD_UNLIKELY( txn_idx >= fd_funk_txn_pool_ele_max( v1->funk->txn_pool ) ) ) { + FD_LOG_CRIT(( "memory corruption detected: invalid txn_idx %lu (max %lu)", + txn_idx, fd_funk_txn_pool_ele_max( v1->funk->txn_pool ) )); + } + fd_funk_txn_t * txn = &v1->funk->txn_pool->ele[ txn_idx ]; + if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) { + FD_LOG_CRIT(( "Failed to modify account: data race detected on fork node (expected XID %lu:%lu, found %lu:%lu)", + xid->ul[0], xid->ul[1], + txn->xid.ul[0], txn->xid.ul[1] )); + } + if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) { + FD_LOG_CRIT(( "Failed to modify account: XID %lu:%lu has children/is frozen", xid->ul[0], xid->ul[1] )); + } + peek = fd_accdb_peek_funk( v1, peek_, xid, address ); + + } + + if( FD_UNLIKELY( !peek ) ) { /* Record not found */ if( !flag_create ) return NULL; @@ -405,7 +464,7 @@ fd_accdb_user_v1_open_rw( fd_accdb_user_t * accdb, memset( val, 0, sizeof(fd_account_meta_t) ); return fd_accdb_prep_create( rw, v1, xid, address, val, sizeof(fd_account_meta_t), val_max ); - } else if( fd_funk_txn_xid_eq( peek->acc->rec->pair.xid, xid ) ) { + } else if( fd_funk_txn_xid_eq( peek->acc->rec->pair.xid, &key_xid ) ) { /* Mutable record found, modify in-place */ fd_funk_rec_t * rec = (void *)( peek->acc->ref->rec_laddr ); @@ -449,7 +508,7 @@ fd_accdb_user_v1_open_rw( fd_accdb_user_t * accdb, FD_LOG_CRIT(( "Failed to modify account: data race detected, account was removed while being read" )); } - return fd_accdb_prep_create( rw, v1, xid, address, val, val_sz, val_max ); + return fd_accdb_prep_create( rw, v1, &key_xid, address, val, val_sz, val_max ); } } diff --git a/src/flamenco/accdb/fd_accdb_user.h b/src/flamenco/accdb/fd_accdb_user.h index 75bf2452bd..89eb935684 100644 --- a/src/flamenco/accdb/fd_accdb_user.h +++ b/src/flamenco/accdb/fd_accdb_user.h @@ -8,8 +8,9 @@ #define FD_ACCDB_DEPTH_MAX (128UL) -#define FD_ACCDB_FLAG_CREATE (1) -#define FD_ACCDB_FLAG_TRUNCATE (2) +#define FD_ACCDB_FLAG_CREATE (1) /* create if not exists */ +#define FD_ACCDB_FLAG_TRUNCATE (2) /* truncate if exists */ +#define FD_ACCDB_FLAG_ROOT (4) /* modify DB root, bypass transactional layer */ /* fd_accdb_user_vt_t specifies the interface (vtable) for the account DB client. */