From b67cdd942409ce47b771aefbfe4ed6bbbd8f7541 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Tue, 6 Jan 2026 19:34:21 +0000 Subject: [PATCH 1/3] runtime: safe account reads during reclaims Makes fd_funk_get_acc_meta_readonly robust to concurrent reclaims/rooting. (fd_accdb APIs are already robust) Replay and exec tile interactions generally guarantee that account records are never removed/moved from the account database if those records are still reachable via the index. These is only one exception: When an account deletion gets rooted, the underlying account record gets removed (reclaimed). Rooting happens concurrent with replay. This is theoretically problematic for account database users (use-after-free). Fortunately, the account database APIs (acc_mgr and accdb) do not create references to accounts susceptible to removal (zero lamport accounts). Only the API implementations themselves have to recover from overruns, with no changes to runtime code required. --- src/flamenco/progcache/fd_prog_load.c | 4 +- src/flamenco/runtime/fd_acc_mgr.c | 39 +++++++------------ src/flamenco/runtime/fd_acc_mgr.h | 20 +--------- src/flamenco/runtime/fd_core_bpf_migration.c | 26 +++---------- src/flamenco/runtime/fd_executor.c | 25 +++--------- .../runtime/program/fd_system_program_nonce.c | 4 +- .../test_deprecate_rent_exemption_threshold.c | 11 ++---- .../runtime/tests/fd_harness_common.c | 2 +- src/flamenco/stakes/fd_stake_delegations.c | 7 ++-- src/funk/fd_funk_rec.h | 27 +------------ 10 files changed, 35 insertions(+), 130 deletions(-) diff --git a/src/flamenco/progcache/fd_prog_load.c b/src/flamenco/progcache/fd_prog_load.c index 04e8f599f06..79652006271 100644 --- a/src/flamenco/progcache/fd_prog_load.c +++ b/src/flamenco/progcache/fd_prog_load.c @@ -63,7 +63,7 @@ fd_get_executable_program_content_for_upgradeable_loader( fd_funk_t const * fd_pubkey_t * programdata_address = &program_account_state->inner.program.programdata_address; fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly( - funk, xid, programdata_address, NULL, NULL, out_xid ); + funk, xid, programdata_address, out_xid ); if( FD_UNLIKELY( !meta ) ) return NULL; fd_txn_account_t _rec[1]; fd_txn_account_t * programdata_acc = fd_txn_account_join( fd_txn_account_new( _rec, programdata_address, (void *)meta, 0 ) ); @@ -114,7 +114,7 @@ fd_prog_load_elf( fd_accdb_user_t * accdb, fd_funk_txn_xid_t _out_xid; if( !out_xid ) out_xid = &_out_xid; fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly( - funk, xid, &prog_addr, NULL, NULL, out_xid ); + funk, xid, &prog_addr, out_xid ); if( FD_UNLIKELY( !meta ) ) return NULL; fd_txn_account_t _rec[1]; fd_txn_account_t * rec = fd_txn_account_join( fd_txn_account_new( _rec, &prog_addr, (void *)meta, 0 ) ); diff --git a/src/flamenco/runtime/fd_acc_mgr.c b/src/flamenco/runtime/fd_acc_mgr.c index d5eb5f4205f..1c9d9009850 100644 --- a/src/flamenco/runtime/fd_acc_mgr.c +++ b/src/flamenco/runtime/fd_acc_mgr.c @@ -5,8 +5,6 @@ fd_account_meta_t const * fd_funk_get_acc_meta_readonly( fd_funk_t const * funk, fd_funk_txn_xid_t const * xid, fd_pubkey_t const * pubkey, - fd_funk_rec_t const ** orec, - int * opt_err, fd_funk_txn_xid_t * out_xid ) { fd_funk_rec_key_t id = fd_funk_acc_key( pubkey ); @@ -14,43 +12,32 @@ fd_funk_get_acc_meta_readonly( fd_funk_t const * funk, nothing else will change that account. If the account is writable in the solana txn, then we copy the data. If the account is read-only, we do not. This is safe because of the read-write locks that the solana transaction holds on the account. */ - for( ; ; ) { + + for(;;) { + + /* Locate the account record */ fd_funk_rec_query_t query[1]; fd_funk_rec_t const * rec = fd_funk_rec_query_try_global( funk, xid, &id, out_xid, query ); - if( FD_UNLIKELY( !rec ) ) { - fd_int_store_if( !!opt_err, opt_err, FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ); return NULL; } - if( NULL != orec ) - *orec = rec; - void const * raw = fd_funk_val( rec, fd_funk_wksp( funk ) ); + /* Read account balance */ + void const * raw = fd_funk_val( rec, fd_funk_wksp( funk ) ); fd_account_meta_t const * metadata = fd_type_pun_const( raw ); - if( FD_UNLIKELY( metadata->lamports==0UL ) ) { - fd_int_store_if( !!opt_err, opt_err, FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ); - if( NULL != orec ) *orec = NULL; + ulong const lamports = metadata->lamports; + if( FD_UNLIKELY( !lamports ) ) { + /* This account is awaiting deletion */ return NULL; } - fd_int_store_if( !!opt_err, opt_err, FD_ACC_MGR_SUCCESS ); - return metadata; - } + /* Recover from overruns (e.g. account rooted) */ - /* unreachable */ - return NULL; -} + if( FD_LIKELY( fd_funk_rec_map_query_test( query )==FD_MAP_SUCCESS ) ) { + return metadata; + } -FD_FN_CONST char const * -fd_acc_mgr_strerror( int err ) { - switch( err ) { - case FD_ACC_MGR_SUCCESS: - return "success"; - case FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT: - return "unknown account"; - default: - return "unknown"; } } diff --git a/src/flamenco/runtime/fd_acc_mgr.h b/src/flamenco/runtime/fd_acc_mgr.h index c9bf9e41574..541e315d724 100644 --- a/src/flamenco/runtime/fd_acc_mgr.h +++ b/src/flamenco/runtime/fd_acc_mgr.h @@ -112,9 +112,6 @@ fd_funk_acc_key( fd_pubkey_t const * pubkey ) { On success: - loads the account data into in-memory cache - returns a pointer to it in the caller's local address space - - if out_rec!=NULL, sets *out_rec to a pointer to the funk rec. - This handle is suitable as opt_con_rec for fd_funk_get_acc_meta_readonly. - - notably, leaves *opt_err untouched, even if opt_err!=NULL First byte of returned pointer is first byte of fd_account_meta_t. To find data region of account, add sizeof(fd_account_meta_t). @@ -122,12 +119,7 @@ fd_funk_acc_key( fd_pubkey_t const * pubkey ) { Lifetime of returned fd_funk_rec_t and account record pointers ends when user calls modify_data for same account, or tranasction ends. - On failure, returns NULL, and sets *opt_err if opt_err!=NULL. - Reasons for error include - - account not found - - internal database or user error (out of memory, attempting to view - record which has an active modify_data handle, etc.) - - the account is a tombstone (0 lamports) + If the account was not found, returns NULL. It is always wrong to cast return value to a non-const pointer. Instead, use fd_funk_get_acc_meta_mutable to acquire a mutable handle. @@ -142,18 +134,8 @@ fd_account_meta_t const * fd_funk_get_acc_meta_readonly( fd_funk_t const * funk, fd_funk_txn_xid_t const * xid, fd_pubkey_t const * pubkey, - fd_funk_rec_t const ** orec, - int * opt_err, fd_funk_txn_xid_t * xid_out ); -/* fd_acc_mgr_strerror converts an fd_acc_mgr error code into a human - readable cstr. The lifetime of the returned pointer is infinite and - the call itself is thread safe. The returned pointer is always to a - non-NULL cstr. */ - -FD_FN_CONST char const * -fd_acc_mgr_strerror( int err ); - FD_PROTOTYPES_END #endif /* HEADER_fd_src_flamenco_runtime_fd_acc_mgr_h */ diff --git a/src/flamenco/runtime/fd_core_bpf_migration.c b/src/flamenco/runtime/fd_core_bpf_migration.c index cea0cf4f5d3..0ffd23bdb16 100644 --- a/src/flamenco/runtime/fd_core_bpf_migration.c +++ b/src/flamenco/runtime/fd_core_bpf_migration.c @@ -33,18 +33,12 @@ tmp_account_read( fd_tmp_account_t * acc, fd_funk_t * funk, fd_funk_txn_xid_t const * xid, fd_pubkey_t const * addr ) { - int opt_err = 0; fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly( funk, xid, addr, - NULL, - &opt_err, NULL ); - if( FD_UNLIKELY( opt_err!=FD_ACC_MGR_SUCCESS ) ) { - if( FD_LIKELY( opt_err==FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) ) return NULL; - FD_LOG_CRIT(( "fd_funk_get_acc_meta_readonly failed (%d)", opt_err )); - } + if( FD_LIKELY( !meta ) ) return NULL; tmp_account_new( acc, meta->dlen ); acc->meta = *meta; acc->addr = *addr; @@ -136,19 +130,14 @@ target_builtin_new_checked( target_builtin_t * target_builtin, break; case FD_CORE_BPF_MIGRATION_TARGET_STATELESS: { /* Program account should not exist */ - int opt_err = 0; - fd_funk_get_acc_meta_readonly( + int progdata_exists = !!fd_funk_get_acc_meta_readonly( funk, xid, program_address, - NULL, - &opt_err, NULL ); - if( opt_err==FD_ACC_MGR_SUCCESS ) { + if( progdata_exists ) { /* CoreBpfMigrationError::AccountAlreadyExists(*program_address) */ return NULL; - } else if( opt_err!=FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) { - FD_LOG_ERR(( "database error: %d", opt_err )); } break; } @@ -164,19 +153,14 @@ target_builtin_new_checked( target_builtin_t * target_builtin, do { /* Program data account should not exist */ - int opt_err = 0; - fd_funk_get_acc_meta_readonly( + int progdata_exists = !!fd_funk_get_acc_meta_readonly( funk, xid, &program_data_address, - NULL, - &opt_err, NULL ); - if( opt_err==FD_ACC_MGR_SUCCESS ) { + if( progdata_exists ) { /* CoreBpfMigrationError::AccountAlreadyExists(*program_address) */ return NULL; - } else if( opt_err!=FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) { - FD_LOG_ERR(( "database error: %d", opt_err )); } } while(0); diff --git a/src/flamenco/runtime/fd_executor.c b/src/flamenco/runtime/fd_executor.c index a764085d112..1493c5c5360 100644 --- a/src/flamenco/runtime/fd_executor.c +++ b/src/flamenco/runtime/fd_executor.c @@ -606,8 +606,8 @@ fd_executor_load_transaction_accounts_old( fd_runtime_t * runtime, https://github.com/anza-xyz/agave/blob/v2.2.0/svm/src/account_loader.rs#L496-L517 */ fd_pubkey_t const * owner_pubkey = (fd_pubkey_t const *)program_meta->owner; - fd_account_meta_t const * owner_account = fd_funk_get_acc_meta_readonly( runtime->funk, &xid, owner_pubkey, NULL, &err, NULL ); - if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) { + fd_account_meta_t const * owner_account = fd_funk_get_acc_meta_readonly( runtime->funk, &xid, owner_pubkey, NULL ); + if( FD_UNLIKELY( !owner_account ) ) { /* https://github.com/anza-xyz/agave/blob/v2.2.0/svm/src/account_loader.rs#L520 */ return FD_RUNTIME_TXN_ERR_PROGRAM_ACCOUNT_NOT_FOUND; } @@ -717,11 +717,9 @@ fd_collect_loaded_account( fd_runtime_t * runtime, runtime->funk, &xid, &loader_state->inner.program.programdata_address, - NULL, - &err, NULL ); - if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) { + if( FD_UNLIKELY( !programdata_meta ) ) { return FD_RUNTIME_EXECUTE_SUCCESS; } @@ -975,15 +973,12 @@ fd_executor_create_rollback_fee_payer_account( fd_runtime_t * runtime, } } - int err = FD_ACC_MGR_SUCCESS; if( !meta ) { fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( bank ), bank->idx } }; meta = fd_funk_get_acc_meta_readonly( runtime->funk, &xid, fee_payer_key, - NULL, - &err, NULL ); if( FD_UNLIKELY( !meta ) ) FD_LOG_CRIT(( "fd_funk_get_acc_meta_readonly failed" )); } @@ -1444,22 +1439,14 @@ fd_executor_setup_txn_account( fd_runtime_t * runtime, } } - int err = FD_ACC_MGR_SUCCESS; if( FD_LIKELY( !meta ) ) { fd_funk_txn_xid_t xid = { .ul = { fd_bank_slot_get( bank ), bank->idx } }; meta = fd_funk_get_acc_meta_readonly( runtime->funk, &xid, acc, - NULL, - &err, NULL ); } - /* If there is an error with a read from the accounts database, it is - unexpected unless the account does not exist. */ - if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS && err!=FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) ) { - FD_LOG_CRIT(( "fd_txn_account_init_from_funk_readonly err=%d", err )); - } fd_account_meta_t * account_meta = NULL; @@ -1488,7 +1475,7 @@ fd_executor_setup_txn_account( fd_runtime_t * runtime, if the account does not exist, we need to initialize a new metadata. */ - if( FD_LIKELY( err==FD_ACC_MGR_SUCCESS ) ) { + if( FD_LIKELY( meta ) ) { account_meta = (fd_account_meta_t *)meta; } else if( fd_pubkey_eq( acc, &fd_sysvar_instructions_id ) ) { account_meta = fd_type_pun( runtime->accounts.sysvar_instructions_mem ); @@ -1530,10 +1517,8 @@ fd_executor_setup_executable_account( fd_runtime_t * runtime, runtime->funk, &xid, programdata_acc, - NULL, - &err, NULL ); - if( FD_LIKELY( err==FD_ACC_MGR_SUCCESS ) ) { + if( FD_LIKELY( meta ) ) { runtime->accounts.executable_pubkeys[*executable_idx] = *programdata_acc; runtime->accounts.executables_meta[*executable_idx] = (fd_account_meta_t *)meta; (*executable_idx)++; diff --git a/src/flamenco/runtime/program/fd_system_program_nonce.c b/src/flamenco/runtime/program/fd_system_program_nonce.c index 6698c185353..939dff04fb2 100644 --- a/src/flamenco/runtime/program/fd_system_program_nonce.c +++ b/src/flamenco/runtime/program/fd_system_program_nonce.c @@ -1019,12 +1019,10 @@ fd_check_transaction_age( fd_runtime_t * runtime, runtime->funk, &xid, &txn_out->accounts.keys[ instr_accts[ 0UL ] ], - NULL, - &err, NULL ); ulong acc_data_len = meta->dlen; - if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) { + if( FD_UNLIKELY( !meta ) ) { return FD_RUNTIME_TXN_ERR_BLOCKHASH_FAIL_ADVANCE_NONCE_INSTR; } diff --git a/src/flamenco/runtime/test_deprecate_rent_exemption_threshold.c b/src/flamenco/runtime/test_deprecate_rent_exemption_threshold.c index 2b0e0df76bc..a689e61cc0c 100644 --- a/src/flamenco/runtime/test_deprecate_rent_exemption_threshold.c +++ b/src/flamenco/runtime/test_deprecate_rent_exemption_threshold.c @@ -221,14 +221,9 @@ verify_rent_values( test_env_t * env, static int rent_was_modified_in_txn( test_env_t * env, fd_funk_txn_xid_t const * xid ) { - fd_funk_t * funk = fd_accdb_user_v1_funk( env->accdb ); - fd_funk_rec_key_t key; - fd_memcpy( key.uc, fd_sysvar_rent_id.key, FD_PUBKEY_FOOTPRINT ); - fd_funk_txn_xid_t xid_out; - fd_funk_rec_query_t query[1]; - - fd_funk_rec_query_try_global( funk, xid, &key, &xid_out, query ); - return fd_funk_txn_xid_eq( &xid_out, xid ); + fd_accdb_peek_t peek[1]; + FD_TEST( fd_accdb_peek( env->accdb, peek, xid, &fd_sysvar_rent_id ) ); + return fd_funk_txn_xid_eq( peek->acc->rec->pair.xid, xid ); } static int diff --git a/src/flamenco/runtime/tests/fd_harness_common.c b/src/flamenco/runtime/tests/fd_harness_common.c index 24896316237..83e6852597e 100644 --- a/src/flamenco/runtime/tests/fd_harness_common.c +++ b/src/flamenco/runtime/tests/fd_harness_common.c @@ -25,7 +25,7 @@ fd_solfuzz_pb_load_account( fd_runtime_t * runtime, /* Account must not yet exist */ fd_funk_t * funk = fd_accdb_user_v1_funk( accdb ); - if( FD_UNLIKELY( fd_funk_get_acc_meta_readonly( funk, xid, pubkey, NULL, NULL, NULL) ) ) { + if( FD_UNLIKELY( fd_funk_get_acc_meta_readonly( funk, xid, pubkey, NULL ) ) ) { return 0; } diff --git a/src/flamenco/stakes/fd_stake_delegations.c b/src/flamenco/stakes/fd_stake_delegations.c index a3977d3f325..8efc63b46e8 100644 --- a/src/flamenco/stakes/fd_stake_delegations.c +++ b/src/flamenco/stakes/fd_stake_delegations.c @@ -356,16 +356,15 @@ fd_stake_delegations_refresh( fd_stake_delegations_t * stake_delegations, continue; } - int err = 0; - fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly( funk, xid, &stake_delegation->stake_account, NULL, &err, NULL ); - if( FD_UNLIKELY( err ) ) { + fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly( funk, xid, &stake_delegation->stake_account, NULL ); + if( FD_UNLIKELY( !meta ) ) { fd_stake_delegation_map_idx_remove( stake_delegation_map, &stake_delegation->stake_account, ULONG_MAX, stake_delegation_pool ); fd_stake_delegation_pool_idx_release( stake_delegation_pool, i ); continue; } fd_stake_state_v2_t stake_state; - err = fd_stake_get_state( meta, &stake_state ); + int err = fd_stake_get_state( meta, &stake_state ); if( FD_UNLIKELY( err ) ) { fd_stake_delegation_map_idx_remove( stake_delegation_map, &stake_delegation->stake_account, ULONG_MAX, stake_delegation_pool ); fd_stake_delegation_pool_idx_release( stake_delegation_pool, i ); diff --git a/src/funk/fd_funk_rec.h b/src/funk/fd_funk_rec.h index 115056a5bfb..68580f81efb 100644 --- a/src/funk/fd_funk_rec.h +++ b/src/funk/fd_funk_rec.h @@ -107,32 +107,7 @@ FD_FN_CONST static inline int fd_funk_rec_idx_is_null( uint idx ) { return idx== /* Accessors */ -/* fd_funk_rec_query_try queries the in-preparation transaction pointed to - by txn for the record whose key matches the key pointed to by key. - If txn is NULL, the query will be done for the funk's last published - transaction. Returns a pointer to current record on success and NULL - on failure. Reasons for failure include txn is neither NULL nor a - pointer to a in-preparation transaction, key is NULL or not a record - in the given transaction. - - The returned pointer is in the caller's address space if the - return value is non-NULL. - - Assumes funk is a current local join (NULL returns NULL), txn is NULL - or points to an in-preparation transaction in the caller's address - space, key points to a record key in the caller's address space (NULL - returns NULL), and no concurrent operations on funk, txn or key. - funk retains no interest in key. The funk retains ownership of any - returned record. - - The query argument remembers the query for later validity testing. - - This is reasonably fast O(1). - - Important safety tip! This function can encounter records - that have the ERASE flag set (i.e. are tombstones of erased - records). fd_funk_rec_query_try will still return the record in this - case, and the application should check for the flag. */ +/* FIXME deprecate */ fd_funk_rec_t * fd_funk_rec_query_try( fd_funk_t * funk, From a84b68f0708f6e845e3de383d8671f09c01c41bd Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Thu, 18 Dec 2025 16:38:04 +0000 Subject: [PATCH 2/3] accdb: remove unused guard structs --- src/flamenco/accdb/fd_accdb_ref.h | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/flamenco/accdb/fd_accdb_ref.h b/src/flamenco/accdb/fd_accdb_ref.h index c2865c7ff8e..a1fe53b26ea 100644 --- a/src/flamenco/accdb/fd_accdb_ref.h +++ b/src/flamenco/accdb/fd_accdb_ref.h @@ -170,26 +170,6 @@ fd_accdb_ref_slot_set( fd_accdb_rw_t * rw, FD_PROTOTYPES_END -/* fd_accdb_guardr_t tracks a rwlock being held as read-only. - Destroying this guard object detaches the caller's thread from the - rwlock. */ - -struct fd_accbd_guardr { - fd_rwlock_t * rwlock; -}; - -typedef struct fd_accdb_guardr fd_accdb_guardr_t; - -/* fd_accdb_guardw_t tracks an rwlock being held exclusively. - Destroying this guard object detaches the caller's thread from the - lock. */ - -struct fd_accdb_guardw { - fd_rwlock_t * rwlock; -}; - -typedef struct fd_accdb_guardw fd_accdb_guardw_t; - /* fd_accdb_spec_t tracks a speculative access to a shared resource. Destroying this guard object marks the end of a speculative access. */ From b9971f297ef1b2ae3ac14ca1b0f78c0fedb56333 Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Thu, 18 Dec 2025 16:38:17 +0000 Subject: [PATCH 3/3] accdb: implement record deletions / reclaims Release account database records when an account deletion gets rooted (aka reclaims) (Fixes a memory leak that will cause Firedancer nodes to crash over a long enough time horizon.) --- book/api/metrics-generated.md | 1 + .../metrics/generated/fd_metrics_replay.c | 1 + .../metrics/generated/fd_metrics_replay.h | 8 ++- src/disco/metrics/metrics.xml | 1 + src/discof/replay/fd_replay_tile.c | 9 +-- src/flamenco/accdb/fd_accdb_admin.c | 55 ++++++++++++++++--- src/flamenco/accdb/fd_accdb_admin.h | 7 ++- src/flamenco/progcache/fd_progcache_admin.c | 1 + 8 files changed, 68 insertions(+), 15 deletions(-) diff --git a/book/api/metrics-generated.md b/book/api/metrics-generated.md index 34ab16797b2..10e5885ea53 100644 --- a/book/api/metrics-generated.md +++ b/book/api/metrics-generated.md @@ -542,6 +542,7 @@ | replay_​accdb_​reverted | counter | Number of account database records reverted | | replay_​accdb_​rooted | counter | Number of account database entries rooted | | replay_​accdb_​gc_​root | counter | Number of account database entries garbage collected | +| replay_​accdb_​reclaimed | counter | Number of account database entries reclaimed (deletion rooted) | diff --git a/src/disco/metrics/generated/fd_metrics_replay.c b/src/disco/metrics/generated/fd_metrics_replay.c index e3373c7b36a..09469392ec3 100644 --- a/src/disco/metrics/generated/fd_metrics_replay.c +++ b/src/disco/metrics/generated/fd_metrics_replay.c @@ -31,4 +31,5 @@ const fd_metrics_meta_t FD_METRICS_REPLAY[FD_METRICS_REPLAY_TOTAL] = { DECLARE_METRIC( REPLAY_ACCDB_REVERTED, COUNTER ), DECLARE_METRIC( REPLAY_ACCDB_ROOTED, COUNTER ), DECLARE_METRIC( REPLAY_ACCDB_GC_ROOT, COUNTER ), + DECLARE_METRIC( REPLAY_ACCDB_RECLAIMED, COUNTER ), }; diff --git a/src/disco/metrics/generated/fd_metrics_replay.h b/src/disco/metrics/generated/fd_metrics_replay.h index 2a682e6c4b0..453f38b0ebf 100644 --- a/src/disco/metrics/generated/fd_metrics_replay.h +++ b/src/disco/metrics/generated/fd_metrics_replay.h @@ -192,7 +192,13 @@ #define FD_METRICS_COUNTER_REPLAY_ACCDB_GC_ROOT_DESC "Number of account database entries garbage collected" #define FD_METRICS_COUNTER_REPLAY_ACCDB_GC_ROOT_CVT (FD_METRICS_CONVERTER_NONE) -#define FD_METRICS_REPLAY_TOTAL (29UL) +#define FD_METRICS_COUNTER_REPLAY_ACCDB_RECLAIMED_OFF (141UL) +#define FD_METRICS_COUNTER_REPLAY_ACCDB_RECLAIMED_NAME "replay_accdb_reclaimed" +#define FD_METRICS_COUNTER_REPLAY_ACCDB_RECLAIMED_TYPE (FD_METRICS_TYPE_COUNTER) +#define FD_METRICS_COUNTER_REPLAY_ACCDB_RECLAIMED_DESC "Number of account database entries reclaimed (deletion rooted)" +#define FD_METRICS_COUNTER_REPLAY_ACCDB_RECLAIMED_CVT (FD_METRICS_CONVERTER_NONE) + +#define FD_METRICS_REPLAY_TOTAL (30UL) extern const fd_metrics_meta_t FD_METRICS_REPLAY[FD_METRICS_REPLAY_TOTAL]; #endif /* HEADER_fd_src_disco_metrics_generated_fd_metrics_replay_h */ diff --git a/src/disco/metrics/metrics.xml b/src/disco/metrics/metrics.xml index ed293dbba30..c54a3a07012 100644 --- a/src/disco/metrics/metrics.xml +++ b/src/disco/metrics/metrics.xml @@ -814,6 +814,7 @@ metric introduced. + diff --git a/src/discof/replay/fd_replay_tile.c b/src/discof/replay/fd_replay_tile.c index 4365a0cb40e..7fea63fbccf 100644 --- a/src/discof/replay/fd_replay_tile.c +++ b/src/discof/replay/fd_replay_tile.c @@ -498,10 +498,11 @@ metrics_write( fd_replay_tile_t * ctx ) { FD_MCNT_SET( REPLAY, PROGCACHE_ROOTED, ctx->progcache_admin->metrics.root_cnt ); FD_MCNT_SET( REPLAY, PROGCACHE_GC_ROOT, ctx->progcache_admin->metrics.gc_root_cnt ); - FD_MCNT_SET( REPLAY, ACCDB_CREATED, ctx->accdb->base.created_cnt ); - FD_MCNT_SET( REPLAY, ACCDB_REVERTED, ctx->accdb_admin->metrics.revert_cnt ); - FD_MCNT_SET( REPLAY, ACCDB_ROOTED, ctx->accdb_admin->metrics.root_cnt ); - FD_MCNT_SET( REPLAY, ACCDB_GC_ROOT, ctx->accdb_admin->metrics.gc_root_cnt ); + FD_MCNT_SET( REPLAY, ACCDB_CREATED, ctx->accdb->base.created_cnt ); + FD_MCNT_SET( REPLAY, ACCDB_REVERTED, ctx->accdb_admin->metrics.revert_cnt ); + FD_MCNT_SET( REPLAY, ACCDB_ROOTED, ctx->accdb_admin->metrics.root_cnt ); + FD_MCNT_SET( REPLAY, ACCDB_GC_ROOT, ctx->accdb_admin->metrics.gc_root_cnt ); + FD_MCNT_SET( REPLAY, ACCDB_RECLAIMED, ctx->accdb_admin->metrics.reclaim_cnt ); } static inline ulong diff --git a/src/flamenco/accdb/fd_accdb_admin.c b/src/flamenco/accdb/fd_accdb_admin.c index faed5426182..79ddb1ba66d 100644 --- a/src/flamenco/accdb/fd_accdb_admin.c +++ b/src/flamenco/accdb/fd_accdb_admin.c @@ -1,4 +1,5 @@ #include "fd_accdb_admin.h" +#include "../fd_flamenco_base.h" fd_accdb_admin_t * fd_accdb_admin_join( fd_accdb_admin_t * ljoin, @@ -214,6 +215,36 @@ fd_accdb_cancel( fd_accdb_admin_t * accdb, fd_accdb_txn_cancel_tree( accdb, txn ); } +/* fd_accdb_chain_reclaim "reclaims" a zero-lamport account by removing + its underlying record. */ + +static void +fd_accdb_chain_reclaim( fd_accdb_admin_t * accdb, + fd_funk_rec_t * rec ) { + fd_funk_t * funk = accdb->funk; + + /* Phase 1: Remove record from map */ + + fd_funk_xid_key_pair_t pair = rec->pair; + fd_funk_rec_query_t query[1]; + int rm_err = fd_funk_rec_map_remove( funk->rec_map, &pair, NULL, query, FD_MAP_FLAG_BLOCKING ); + if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", rm_err, fd_map_strerror( rm_err ) )); + FD_COMPILER_MFENCE(); + + /* Phase 2: Invalidate record */ + + fd_funk_rec_t * old_rec = query->ele; + memset( &old_rec->pair, 0, sizeof(fd_funk_xid_key_pair_t) ); + FD_COMPILER_MFENCE(); + + /* Phase 3: Free record */ + + old_rec->map_next = FD_FUNK_REC_IDX_NULL; + fd_funk_val_flush( old_rec, funk->alloc, funk->wksp ); + fd_funk_rec_pool_release( funk->rec_pool, old_rec, 1 ); + accdb->metrics.reclaim_cnt++; +} + /* fd_accdb_chain_gc_root cleans up a stale "rooted" version of a record. */ @@ -227,7 +258,8 @@ fd_accdb_chain_gc_root( fd_accdb_admin_t * accdb, fd_funk_rec_query_t query[1]; int rm_err = fd_funk_rec_map_remove( funk->rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING ); if( rm_err==FD_MAP_ERR_KEY ) return; - if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) )); + if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed (%i-%s)", rm_err, fd_map_strerror( rm_err ) )); + FD_COMPILER_MFENCE(); /* Phase 2: Invalidate record */ @@ -257,6 +289,7 @@ fd_accdb_publish_recs( fd_accdb_admin_t * accdb, uint head = txn->rec_head_idx; txn->rec_head_idx = FD_FUNK_REC_IDX_NULL; txn->rec_tail_idx = FD_FUNK_REC_IDX_NULL; + fd_wksp_t * funk_wksp = accdb->funk->wksp; while( !fd_funk_rec_idx_is_null( head ) ) { fd_funk_rec_t * rec = &accdb->funk->rec_pool->ele[ head ]; @@ -266,13 +299,21 @@ fd_accdb_publish_recs( fd_accdb_admin_t * accdb, fd_funk_txn_xid_set_root( pair->xid ); fd_accdb_chain_gc_root( accdb, pair ); - /* Migrate record to root */ + /* Root or reclaim record */ uint next = rec->next_idx; - rec->prev_idx = FD_FUNK_REC_IDX_NULL; - rec->next_idx = FD_FUNK_REC_IDX_NULL; - fd_funk_txn_xid_t const root = { .ul = { ULONG_MAX, ULONG_MAX } }; - fd_funk_txn_xid_st_atomic( rec->pair.xid, &root ); - accdb->metrics.root_cnt++; + fd_account_meta_t const * meta = fd_funk_val( rec, funk_wksp ); + FD_CRIT( meta && rec->val_sz>=sizeof(fd_account_meta_t), "invalid funk record value" ); + if( FD_LIKELY( meta->lamports ) ) { + /* Migrate record to root */ + rec->prev_idx = FD_FUNK_REC_IDX_NULL; + rec->next_idx = FD_FUNK_REC_IDX_NULL; + fd_funk_txn_xid_t const root = { .ul = { ULONG_MAX, ULONG_MAX } }; + fd_funk_txn_xid_st_atomic( rec->pair.xid, &root ); + accdb->metrics.root_cnt++; + } else { + /* Remove record */ + fd_accdb_chain_reclaim( accdb, rec ); + } head = next; /* next record */ } diff --git a/src/flamenco/accdb/fd_accdb_admin.h b/src/flamenco/accdb/fd_accdb_admin.h index 24fe44891bf..37adf1365c4 100644 --- a/src/flamenco/accdb/fd_accdb_admin.h +++ b/src/flamenco/accdb/fd_accdb_admin.h @@ -7,9 +7,10 @@ struct fd_accdb_admin { fd_funk_t funk[1]; struct { - ulong root_cnt; - ulong gc_root_cnt; - ulong revert_cnt; + ulong root_cnt; /* moved to database root */ + ulong reclaim_cnt; /* 0 lamport account removed while rooting */ + ulong gc_root_cnt; /* stale rooted revisions removed while rooting */ + ulong revert_cnt; /* abandoned by consensus */ } metrics; }; diff --git a/src/flamenco/progcache/fd_progcache_admin.c b/src/flamenco/progcache/fd_progcache_admin.c index 9b154fcca51..f5b2d2e92d8 100644 --- a/src/flamenco/progcache/fd_progcache_admin.c +++ b/src/flamenco/progcache/fd_progcache_admin.c @@ -220,6 +220,7 @@ fd_progcache_gc_root( fd_progcache_admin_t * cache, int rm_err = fd_funk_rec_map_remove( funk->rec_map, pair, NULL, query, FD_MAP_FLAG_BLOCKING ); if( rm_err==FD_MAP_ERR_KEY ) return; if( FD_UNLIKELY( rm_err!=FD_MAP_SUCCESS ) ) FD_LOG_CRIT(( "fd_funk_rec_map_remove failed: %i-%s", rm_err, fd_map_strerror( rm_err ) )); + FD_COMPILER_MFENCE(); /* Phase 2: Invalidate record */