diff --git a/book/api/metrics-generated.md b/book/api/metrics-generated.md
index 6e7ac2612b..16c8981024 100644
--- a/book/api/metrics-generated.md
+++ b/book/api/metrics-generated.md
@@ -541,6 +541,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 83d3f42484..74e5ce9354 100644
--- a/src/disco/metrics/generated/fd_metrics_replay.c
+++ b/src/disco/metrics/generated/fd_metrics_replay.c
@@ -30,4 +30,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 47298e92e5..ffcd466ab3 100644
--- a/src/disco/metrics/generated/fd_metrics_replay.h
+++ b/src/disco/metrics/generated/fd_metrics_replay.h
@@ -186,7 +186,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 (28UL)
+#define FD_METRICS_COUNTER_REPLAY_ACCDB_RECLAIMED_OFF (140UL)
+#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 (29UL)
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 ba58bc91b9..4f3bfe0ee4 100644
--- a/src/disco/metrics/metrics.xml
+++ b/src/disco/metrics/metrics.xml
@@ -813,6 +813,7 @@ metric introduced.
+
diff --git a/src/discof/replay/fd_replay_tile.c b/src/discof/replay/fd_replay_tile.c
index 0a5ae60ef6..576a5e9349 100644
--- a/src/discof/replay/fd_replay_tile.c
+++ b/src/discof/replay/fd_replay_tile.c
@@ -495,10 +495,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 ec3c072c14..9798348c63 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,
@@ -209,6 +210,35 @@ 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 ) ));
+
+ /* 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. */
@@ -222,7 +252,7 @@ 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 ) ));
/* Phase 2: Invalidate record */
@@ -252,6 +282,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 ];
@@ -261,13 +292,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 24fe44891b..37adf1365c 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/accdb/fd_accdb_ref.h b/src/flamenco/accdb/fd_accdb_ref.h
index c2865c7ff8..a1fe53b26e 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. */