Report transaction signature on update account (#25726)

There's no direct linkage between account-update events and transaction-events

Issue is linked with geyser plugin interface
Currently, there's no straightforward way to determine what notify_transaction call is related to particular update_account call. So, there's no simple way to determine what transaction caused what changes in accounts. This issue is especially valuable in case when several transactions modifies single account in the same slot.

Add txn_signature field to ReplicaTransactionInfo structure

This additional field will contain the signature of transaction which caused this account update event. This modification is not bringing significant overhead because all necessary information for such linkage already available inside Accounts::collect_accounts_to_store method
This commit is contained in:
ivandzen 2022-06-10 20:24:35 +03:00 committed by GitHub
parent 29b597cea5
commit 2251aa2809
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 179 additions and 68 deletions

View File

@ -38,12 +38,46 @@ pub struct ReplicaAccountInfo<'a> {
pub write_version: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
/// Information about an account being updated
/// (extended with transaction signature doing this update)
pub struct ReplicaAccountInfoV2<'a> {
/// The Pubkey for the account
pub pubkey: &'a [u8],
/// The lamports for the account
pub lamports: u64,
/// The Pubkey of the owner program account
pub owner: &'a [u8],
/// This account's data contains a loaded program (and is now read-only)
pub executable: bool,
/// The epoch at which this account will next owe rent
pub rent_epoch: u64,
/// The data held in this account.
pub data: &'a [u8],
/// A global monotonically increasing atomic number, which can be used
/// to tell the order of the account update. For example, when an
/// account is updated in the same slot multiple times, the update
/// with higher write_version should supersede the one with lower
/// write_version.
pub write_version: u64,
/// First signature of the transaction caused this account modification
pub txn_signature: Option<&'a Signature>,
}
/// A wrapper to future-proof ReplicaAccountInfo handling.
/// If there were a change to the structure of ReplicaAccountInfo,
/// there would be new enum entry for the newer version, forcing
/// plugin implementations to handle the change.
pub enum ReplicaAccountInfoVersions<'a> {
V0_0_1(&'a ReplicaAccountInfo<'a>),
V0_0_2(&'a ReplicaAccountInfoV2<'a>),
}
/// Information about a transaction

View File

@ -3,7 +3,7 @@ use {
crate::geyser_plugin_manager::GeyserPluginManager,
log::*,
solana_geyser_plugin_interface::geyser_plugin_interface::{
ReplicaAccountInfo, ReplicaAccountInfoVersions,
ReplicaAccountInfoV2, ReplicaAccountInfoVersions,
},
solana_measure::measure::Measure,
solana_metrics::*,
@ -14,6 +14,7 @@ use {
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
clock::Slot,
signature::Signature,
},
std::sync::{Arc, RwLock},
};
@ -23,8 +24,16 @@ pub(crate) struct AccountsUpdateNotifierImpl {
}
impl AccountsUpdateNotifierInterface for AccountsUpdateNotifierImpl {
fn notify_account_update(&self, slot: Slot, meta: &StoredMeta, account: &AccountSharedData) {
if let Some(account_info) = self.accountinfo_from_shared_account_data(meta, account) {
fn notify_account_update(
&self,
slot: Slot,
meta: &StoredMeta,
account: &AccountSharedData,
txn_signature: &Option<&Signature>,
) {
if let Some(account_info) =
self.accountinfo_from_shared_account_data(meta, account, txn_signature)
{
self.notify_plugins_of_account_update(account_info, slot, false);
}
}
@ -97,8 +106,9 @@ impl AccountsUpdateNotifierImpl {
&self,
meta: &'a StoredMeta,
account: &'a AccountSharedData,
) -> Option<ReplicaAccountInfo<'a>> {
Some(ReplicaAccountInfo {
txn_signature: &'a Option<&'a Signature>,
) -> Option<ReplicaAccountInfoV2<'a>> {
Some(ReplicaAccountInfoV2 {
pubkey: meta.pubkey.as_ref(),
lamports: account.lamports(),
owner: account.owner().as_ref(),
@ -106,14 +116,15 @@ impl AccountsUpdateNotifierImpl {
rent_epoch: account.rent_epoch(),
data: account.data(),
write_version: meta.write_version,
txn_signature: *txn_signature,
})
}
fn accountinfo_from_stored_account_meta<'a>(
&self,
stored_account_meta: &'a StoredAccountMeta,
) -> Option<ReplicaAccountInfo<'a>> {
Some(ReplicaAccountInfo {
) -> Option<ReplicaAccountInfoV2<'a>> {
Some(ReplicaAccountInfoV2 {
pubkey: stored_account_meta.meta.pubkey.as_ref(),
lamports: stored_account_meta.account_meta.lamports,
owner: stored_account_meta.account_meta.owner.as_ref(),
@ -121,12 +132,13 @@ impl AccountsUpdateNotifierImpl {
rent_epoch: stored_account_meta.account_meta.rent_epoch,
data: stored_account_meta.data,
write_version: stored_account_meta.meta.write_version,
txn_signature: None,
})
}
fn notify_plugins_of_account_update(
&self,
account: ReplicaAccountInfo,
account: ReplicaAccountInfoV2,
slot: Slot,
is_startup: bool,
) {
@ -139,7 +151,7 @@ impl AccountsUpdateNotifierImpl {
for plugin in plugin_manager.plugins.iter_mut() {
let mut measure = Measure::start("geyser-plugin-update-account");
match plugin.update_account(
ReplicaAccountInfoVersions::V0_0_1(&account),
ReplicaAccountInfoVersions::V0_0_2(&account),
slot,
is_startup,
) {

View File

@ -45,6 +45,7 @@ use {
State as NonceState,
},
pubkey::Pubkey,
signature::Signature,
slot_hashes::SlotHashes,
system_program,
sysvar::{self, epoch_schedule::EpochSchedule, instructions::construct_instructions_data},
@ -1200,7 +1201,7 @@ impl Accounts {
lamports_per_signature: u64,
leave_nonce_on_success: bool,
) {
let accounts_to_store = self.collect_accounts_to_store(
let (accounts_to_store, txn_signatures) = self.collect_accounts_to_store(
txs,
res,
loaded,
@ -1209,11 +1210,12 @@ impl Accounts {
lamports_per_signature,
leave_nonce_on_success,
);
self.accounts_db.store_cached(slot, &accounts_to_store);
self.accounts_db
.store_cached(slot, &accounts_to_store, Some(&txn_signatures));
}
pub fn store_accounts_cached(&self, slot: Slot, accounts: &[(&Pubkey, &AccountSharedData)]) {
self.accounts_db.store_cached(slot, accounts)
self.accounts_db.store_cached(slot, accounts, None)
}
/// Add a slot to root. Root slots cannot be purged
@ -1231,8 +1233,12 @@ impl Accounts {
durable_nonce: &(DurableNonce, /*separate_domains:*/ bool),
lamports_per_signature: u64,
leave_nonce_on_success: bool,
) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
) -> (
Vec<(&'a Pubkey, &'a AccountSharedData)>,
Vec<Option<&'a Signature>>,
) {
let mut accounts = Vec::with_capacity(load_results.len());
let mut signatures = Vec::with_capacity(load_results.len());
for (i, ((tx_load_result, nonce), tx)) in load_results.iter_mut().zip(txs).enumerate() {
if tx_load_result.is_err() {
// Don't store any accounts if tx failed to load
@ -1301,11 +1307,12 @@ impl Accounts {
// Add to the accounts to store
accounts.push((&*address, &*account));
signatures.push(Some(tx.signature()));
}
}
}
}
accounts
(accounts, signatures)
}
}
@ -2971,6 +2978,7 @@ mod tests {
(message.account_keys[1], account2.clone()),
];
let tx0 = new_sanitized_tx(&[&keypair0], message, Hash::default());
let tx0_sign = *tx0.signature();
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
let message = Message::new_with_compiled_instructions(
@ -2986,6 +2994,7 @@ mod tests {
(message.account_keys[1], account2),
];
let tx1 = new_sanitized_tx(&[&keypair1], message, Hash::default());
let tx1_sign = *tx1.signature();
let loaded0 = (
Ok(LoadedTransaction {
@ -3025,7 +3034,7 @@ mod tests {
}
let txs = vec![tx0, tx1];
let execution_results = vec![new_execution_result(Ok(()), None); 2];
let collected_accounts = accounts.collect_accounts_to_store(
let (collected_accounts, txn_signatures) = accounts.collect_accounts_to_store(
&txs,
&execution_results,
loaded.as_mut_slice(),
@ -3042,6 +3051,14 @@ mod tests {
.iter()
.any(|(pubkey, _account)| *pubkey == &keypair1.pubkey()));
assert_eq!(txn_signatures.len(), 2);
assert!(txn_signatures
.iter()
.any(|signature| signature.unwrap().to_string().eq(&tx0_sign.to_string())));
assert!(txn_signatures
.iter()
.any(|signature| signature.unwrap().to_string().eq(&tx1_sign.to_string())));
// Ensure readonly_lock reflects lock
assert_eq!(
*accounts
@ -3524,7 +3541,7 @@ mod tests {
)),
nonce.as_ref(),
)];
let collected_accounts = accounts.collect_accounts_to_store(
let (collected_accounts, _) = accounts.collect_accounts_to_store(
&txs,
&execution_results,
loaded.as_mut_slice(),
@ -3652,7 +3669,7 @@ mod tests {
)),
nonce.as_ref(),
)];
let collected_accounts = accounts.collect_accounts_to_store(
let (collected_accounts, _) = accounts.collect_accounts_to_store(
&txs,
&execution_results,
loaded.as_mut_slice(),

View File

@ -74,6 +74,7 @@ use {
hash::Hash,
pubkey::Pubkey,
rent::Rent,
signature::Signature,
timing::AtomicInterval,
},
std::{
@ -5774,11 +5775,12 @@ impl AccountsDb {
}
}
fn write_accounts_to_cache(
fn write_accounts_to_cache<'a>(
&self,
slot: Slot,
hashes: Option<&[impl Borrow<Hash>]>,
accounts_and_meta_to_store: &[(StoredMeta, Option<&impl ReadableAccount>)],
txn_signatures_iter: Box<dyn std::iter::Iterator<Item = &Option<&Signature>> + 'a>,
) -> Vec<AccountInfo> {
let len = accounts_and_meta_to_store.len();
let hashes = hashes.map(|hashes| {
@ -5788,8 +5790,9 @@ impl AccountsDb {
accounts_and_meta_to_store
.iter()
.zip(txn_signatures_iter)
.enumerate()
.map(|(i, (meta, account))| {
.map(|(i, ((meta, account), signature))| {
let hash = hashes.map(|hashes| hashes[i].borrow());
let account = account
@ -5801,7 +5804,7 @@ impl AccountsDb {
account.lamports(),
);
self.notify_account_at_accounts_update(slot, meta, &account);
self.notify_account_at_accounts_update(slot, meta, &account, signature);
let cached_account = self.accounts_cache.store(slot, &meta.pubkey, account, hash);
// hash this account in the bg
@ -5828,6 +5831,7 @@ impl AccountsDb {
storage_finder: F,
mut write_version_producer: P,
is_cached_store: bool,
txn_signatures: Option<&'a [Option<&'a Signature>]>,
) -> Vec<AccountInfo> {
let mut calc_stored_meta_time = Measure::start("calc_stored_meta");
let slot = accounts.target_slot();
@ -5858,7 +5862,18 @@ impl AccountsDb {
.fetch_add(calc_stored_meta_time.as_us(), Ordering::Relaxed);
if self.caching_enabled && is_cached_store {
self.write_accounts_to_cache(slot, hashes, &accounts_and_meta_to_store)
let signature_iter: Box<dyn std::iter::Iterator<Item = &Option<&Signature>>> =
match txn_signatures {
Some(txn_signatures) => {
assert_eq!(txn_signatures.len(), accounts_and_meta_to_store.len());
Box::new(txn_signatures.iter())
}
None => {
Box::new(std::iter::repeat(&None).take(accounts_and_meta_to_store.len()))
}
};
self.write_accounts_to_cache(slot, hashes, &accounts_and_meta_to_store, signature_iter)
} else {
match hashes {
Some(hashes) => self.write_accounts_to_storage(
@ -7359,20 +7374,26 @@ impl AccountsDb {
.fetch_add(measure.as_us(), Ordering::Relaxed);
}
pub fn store_cached(&self, slot: Slot, accounts: &[(&Pubkey, &AccountSharedData)]) {
self.store((slot, accounts), self.caching_enabled);
pub fn store_cached(
&self,
slot: Slot,
accounts: &[(&Pubkey, &AccountSharedData)],
txn_signatures: Option<&[Option<&Signature>]>,
) {
self.store((slot, accounts), self.caching_enabled, txn_signatures);
}
/// Store the account update.
/// only called by tests
pub fn store_uncached(&self, slot: Slot, accounts: &[(&Pubkey, &AccountSharedData)]) {
self.store((slot, accounts), false)
self.store((slot, accounts), false, None);
}
fn store<'a, T: ReadableAccount + Sync + ZeroLamport>(
&self,
accounts: impl StorableAccounts<'a, T>,
is_cached_store: bool,
txn_signatures: Option<&'a [Option<&'a Signature>]>,
) {
// If all transactions in a batch are errored,
// it's possible to get a store with no accounts.
@ -7402,7 +7423,7 @@ impl AccountsDb {
}
// we use default hashes for now since the same account may be stored to the cache multiple times
self.store_accounts_unfrozen(accounts, None, is_cached_store);
self.store_accounts_unfrozen(accounts, None, is_cached_store, txn_signatures);
self.report_store_timings();
}
@ -7529,6 +7550,7 @@ impl AccountsDb {
accounts: impl StorableAccounts<'a, T>,
hashes: Option<&[&Hash]>,
is_cached_store: bool,
txn_signatures: Option<&'a [Option<&'a Signature>]>,
) {
// This path comes from a store to a non-frozen slot.
// If a store is dead here, then a newer update for
@ -7545,6 +7567,7 @@ impl AccountsDb {
None::<Box<dyn Iterator<Item = u64>>>,
is_cached_store,
reset_accounts,
txn_signatures,
);
}
@ -7567,6 +7590,7 @@ impl AccountsDb {
write_version_producer,
is_cached_store,
reset_accounts,
None,
)
}
@ -7578,6 +7602,7 @@ impl AccountsDb {
write_version_producer: Option<Box<dyn Iterator<Item = u64>>>,
is_cached_store: bool,
reset_accounts: bool,
txn_signatures: Option<&'b [Option<&'b Signature>]>,
) -> StoreAccountsTiming {
let storage_finder = Box::new(move |slot, size| {
storage
@ -7605,6 +7630,7 @@ impl AccountsDb {
storage_finder,
write_version_producer,
is_cached_store,
txn_signatures,
);
store_accounts_time.stop();
self.stats
@ -9708,7 +9734,7 @@ pub mod tests {
let account0 = AccountSharedData::new(1, 0, &key);
let ancestors = vec![(unrooted_slot, 1)].into_iter().collect();
if is_cached {
db.store_cached(unrooted_slot, &[(&key, &account0)]);
db.store_cached(unrooted_slot, &[(&key, &account0)], None);
} else {
db.store_uncached(unrooted_slot, &[(&key, &account0)]);
}
@ -11267,6 +11293,7 @@ pub mod tests {
(some_slot, &[(&key, &account)][..]),
Some(&[&Hash::default()]),
false,
None,
);
db.add_root(some_slot);
let check_hash = true;
@ -11505,7 +11532,7 @@ pub mod tests {
}
// provide bogus account hashes
let some_hash = Hash::new(&[0xca; HASH_BYTES]);
db.store_accounts_unfrozen((some_slot, accounts), Some(&[&some_hash]), false);
db.store_accounts_unfrozen((some_slot, accounts), Some(&[&some_hash]), false, None);
db.add_root(some_slot);
assert_matches!(
db.verify_bank_hash_and_lamports(
@ -12676,13 +12703,13 @@ pub mod tests {
let account = AccountSharedData::new(1, 16 * 4096, &Pubkey::default());
let pubkey1 = solana_sdk::pubkey::new_rand();
accounts.store_cached(0, &[(&pubkey1, &account)]);
accounts.store_cached(0, &[(&pubkey1, &account)], None);
let pubkey2 = solana_sdk::pubkey::new_rand();
accounts.store_cached(0, &[(&pubkey2, &account)]);
accounts.store_cached(0, &[(&pubkey2, &account)], None);
let zero_account = AccountSharedData::new(0, 1, &Pubkey::default());
accounts.store_cached(1, &[(&pubkey1, &zero_account)]);
accounts.store_cached(1, &[(&pubkey1, &zero_account)], None);
// Add root 0 and flush separately
accounts.get_accounts_delta_hash(0);
@ -12854,7 +12881,7 @@ pub mod tests {
let key = Pubkey::default();
let account0 = AccountSharedData::new(1, 0, &key);
let slot = 0;
db.store_cached(slot, &[(&key, &account0)]);
db.store_cached(slot, &[(&key, &account0)], None);
// Load with no ancestors and no root will return nothing
assert!(db
@ -12887,7 +12914,7 @@ pub mod tests {
let key = Pubkey::default();
let account0 = AccountSharedData::new(1, 0, &key);
let slot = 0;
db.store_cached(slot, &[(&key, &account0)]);
db.store_cached(slot, &[(&key, &account0)], None);
db.mark_slot_frozen(slot);
// No root was added yet, requires an ancestor to find
@ -12920,9 +12947,9 @@ pub mod tests {
let unrooted_key = solana_sdk::pubkey::new_rand();
let key5 = solana_sdk::pubkey::new_rand();
let key6 = solana_sdk::pubkey::new_rand();
db.store_cached(unrooted_slot, &[(&unrooted_key, &account0)]);
db.store_cached(root5, &[(&key5, &account0)]);
db.store_cached(root6, &[(&key6, &account0)]);
db.store_cached(unrooted_slot, &[(&unrooted_key, &account0)], None);
db.store_cached(root5, &[(&key5, &account0)], None);
db.store_cached(root6, &[(&key6, &account0)], None);
for slot in &[unrooted_slot, root5, root6] {
db.mark_slot_frozen(*slot);
}
@ -12985,7 +13012,7 @@ pub mod tests {
let num_slots = 2 * max_cache_slots();
for i in 0..num_roots + num_unrooted {
let key = Pubkey::new_unique();
db.store_cached(i as Slot, &[(&key, &account0)]);
db.store_cached(i as Slot, &[(&key, &account0)], None);
keys.push(key);
db.mark_slot_frozen(i as Slot);
if i < num_roots {
@ -13054,8 +13081,8 @@ pub mod tests {
let zero_lamport_account =
AccountSharedData::new(0, 0, AccountSharedData::default().owner());
let slot1_account = AccountSharedData::new(1, 1, AccountSharedData::default().owner());
db.store_cached(0, &[(&account_key, &zero_lamport_account)]);
db.store_cached(1, &[(&account_key, &slot1_account)]);
db.store_cached(0, &[(&account_key, &zero_lamport_account)], None);
db.store_cached(1, &[(&account_key, &slot1_account)], None);
db.add_root(0);
db.add_root(1);
@ -13077,7 +13104,7 @@ pub mod tests {
.unwrap();
assert_eq!(account.lamports(), 1);
assert_eq!(db.read_only_accounts_cache.cache_len(), 1);
db.store_cached(2, &[(&account_key, &zero_lamport_account)]);
db.store_cached(2, &[(&account_key, &zero_lamport_account)], None);
assert_eq!(db.read_only_accounts_cache.cache_len(), 1);
let account = db
.load_with_fixed_root(&Ancestors::default(), &account_key)
@ -13102,8 +13129,8 @@ pub mod tests {
let zero_lamport_account =
AccountSharedData::new(0, 0, AccountSharedData::default().owner());
let slot1_account = AccountSharedData::new(1, 1, AccountSharedData::default().owner());
db.store_cached(0, &[(&account_key, &zero_lamport_account)]);
db.store_cached(1, &[(&account_key, &slot1_account)]);
db.store_cached(0, &[(&account_key, &zero_lamport_account)], None);
db.store_cached(1, &[(&account_key, &slot1_account)], None);
db.add_root(0);
db.add_root(1);
@ -13157,16 +13184,20 @@ pub mod tests {
AccountSharedData::new(0, 0, AccountSharedData::default().owner());
// Store into slot 0, and then flush the slot to storage
db.store_cached(0, &[(&zero_lamport_account_key, &slot0_account)]);
db.store_cached(0, &[(&zero_lamport_account_key, &slot0_account)], None);
// Second key keeps other lamport account entry for slot 0 alive,
// preventing clean of the zero_lamport_account in slot 1.
db.store_cached(0, &[(&other_account_key, &slot0_account)]);
db.store_cached(0, &[(&other_account_key, &slot0_account)], None);
db.add_root(0);
db.flush_accounts_cache(true, None);
assert!(!db.storage.get_slot_storage_entries(0).unwrap().is_empty());
// Store into slot 1, a dummy slot that will be dead and purged before flush
db.store_cached(1, &[(&zero_lamport_account_key, &zero_lamport_account)]);
db.store_cached(
1,
&[(&zero_lamport_account_key, &zero_lamport_account)],
None,
);
// Store into slot 2, which makes all updates from slot 1 outdated.
// This means slot 1 is a dead slot. Later, slot 1 will be cleaned/purged
@ -13174,7 +13205,11 @@ pub mod tests {
// the refcount of `zero_lamport_account_key` because cached keys do not bump
// the refcount in the index. This means clean should *not* remove
// `zero_lamport_account_key` from slot 2
db.store_cached(2, &[(&zero_lamport_account_key, &zero_lamport_account)]);
db.store_cached(
2,
&[(&zero_lamport_account_key, &zero_lamport_account)],
None,
);
db.add_root(1);
db.add_root(2);
@ -13296,11 +13331,11 @@ pub mod tests {
/ \
1 2 (root)
*/
db.store_cached(0, &[(&account_key, &zero_lamport_account)]);
db.store_cached(1, &[(&account_key, &slot1_account)]);
db.store_cached(0, &[(&account_key, &zero_lamport_account)], None);
db.store_cached(1, &[(&account_key, &slot1_account)], None);
// Fodder for the scan so that the lock on `account_key` is not held
db.store_cached(1, &[(&account_key2, &slot1_account)]);
db.store_cached(2, &[(&account_key, &slot2_account)]);
db.store_cached(1, &[(&account_key2, &slot1_account)], None);
db.store_cached(2, &[(&account_key, &slot2_account)], None);
db.get_accounts_delta_hash(0);
let max_scan_root = 0;
@ -13394,7 +13429,7 @@ pub mod tests {
for data_size in 0..num_keys {
let account = AccountSharedData::new(1, data_size, &Pubkey::default());
accounts_db.store_cached(slot, &[(&Pubkey::new_unique(), &account)]);
accounts_db.store_cached(slot, &[(&Pubkey::new_unique(), &account)], None);
}
accounts_db.add_root(slot);
@ -13462,6 +13497,7 @@ pub mod tests {
&scan_stall_key,
&AccountSharedData::new(1, 0, &Pubkey::default()),
)],
None,
);
}
@ -13473,6 +13509,7 @@ pub mod tests {
accounts_db.store_cached(
*slot,
&[(key, &AccountSharedData::new(1, space, &Pubkey::default()))],
None,
);
}
accounts_db.add_root(*slot as Slot);
@ -13517,6 +13554,7 @@ pub mod tests {
accounts_db.store_cached(
alive_slot,
&[(key, &AccountSharedData::new(1, 0, &Pubkey::default()))],
None,
);
accounts_db.add_root(alive_slot);
}
@ -13854,8 +13892,8 @@ pub mod tests {
let account1 = AccountSharedData::new(1, 0, AccountSharedData::default().owner());
// Store into slot 0
db.store_cached(0, &[(&account_key1, &account1)]);
db.store_cached(0, &[(&account_key2, &account1)]);
db.store_cached(0, &[(&account_key1, &account1)], None);
db.store_cached(0, &[(&account_key2, &account1)], None);
db.add_root(0);
if !do_intra_cache_clean {
// If we don't want the cache doing purges before flush,
@ -13869,7 +13907,7 @@ pub mod tests {
}
// Make account_key1 in slot 0 outdated by updating in rooted slot 1
db.store_cached(1, &[(&account_key1, &account1)]);
db.store_cached(1, &[(&account_key1, &account1)], None);
db.add_root(1);
// Flushes all roots
db.flush_accounts_cache(true, None);
@ -13893,7 +13931,7 @@ pub mod tests {
db.shrink_candidate_slots();
// Make slot 0 dead by updating the remaining key
db.store_cached(2, &[(&account_key2, &account1)]);
db.store_cached(2, &[(&account_key2, &account1)], None);
db.add_root(2);
// Flushes all roots
@ -14122,6 +14160,7 @@ pub mod tests {
&pubkey,
&AccountSharedData::new(1, 0, AccountSharedData::default().owner()),
)],
None,
);
db.add_root(0);
db.flush_accounts_cache(true, None);
@ -14140,7 +14179,7 @@ pub mod tests {
return;
}
account.set_lamports(slot + 1);
db.store_cached(slot, &[(&pubkey, &account)]);
db.store_cached(slot, &[(&pubkey, &account)], None);
db.add_root(slot);
sleep(Duration::from_millis(RACY_SLEEP_MS));
db.flush_accounts_cache(true, None);
@ -14308,7 +14347,7 @@ pub mod tests {
let num_trials = 10;
for _ in 0..num_trials {
let pubkey = Pubkey::new_unique();
db.store_cached(slot, &[(&pubkey, &account)]);
db.store_cached(slot, &[(&pubkey, &account)], None);
// Wait for both threads to finish
flush_trial_start_sender.send(()).unwrap();
remove_trial_start_sender.send(()).unwrap();
@ -14399,7 +14438,7 @@ pub mod tests {
let slot_to_pubkey_map: HashMap<Slot, Pubkey> = (0..num_cached_slots)
.map(|slot| {
let pubkey = Pubkey::new_unique();
db.store_cached(slot, &[(&pubkey, &account)]);
db.store_cached(slot, &[(&pubkey, &account)], None);
(slot, pubkey)
})
.collect();
@ -14830,19 +14869,19 @@ pub mod tests {
let slot1 = 1;
let account = AccountSharedData::new(111, space, &owner);
accounts_db.store_cached(slot1, &[(&pubkey, &account)]);
accounts_db.store_cached(slot1, &[(&pubkey, &account)], None);
accounts_db.get_accounts_delta_hash(slot1);
accounts_db.add_root(slot1);
let slot2 = 2;
let account = AccountSharedData::new(222, space, &owner);
accounts_db.store_cached(slot2, &[(&pubkey, &account)]);
accounts_db.store_cached(slot2, &[(&pubkey, &account)], None);
accounts_db.get_accounts_delta_hash(slot2);
accounts_db.add_root(slot2);
let slot3 = 3;
let account = AccountSharedData::new(0, space, &owner);
accounts_db.store_cached(slot3, &[(&pubkey, &account)]);
accounts_db.store_cached(slot3, &[(&pubkey, &account)], None);
accounts_db.get_accounts_delta_hash(slot3);
accounts_db.add_root(slot3);

View File

@ -5,7 +5,7 @@ use {
},
solana_measure::measure::Measure,
solana_metrics::*,
solana_sdk::{account::AccountSharedData, clock::Slot, pubkey::Pubkey},
solana_sdk::{account::AccountSharedData, clock::Slot, pubkey::Pubkey, signature::Signature},
std::collections::{hash_map::Entry, HashMap, HashSet},
};
@ -64,10 +64,11 @@ impl AccountsDb {
slot: Slot,
meta: &StoredMeta,
account: &AccountSharedData,
txn_signature: &Option<&Signature>,
) {
if let Some(accounts_update_notifier) = &self.accounts_update_notifier {
let notifier = &accounts_update_notifier.read().unwrap();
notifier.notify_account_update(slot, meta, account);
notifier.notify_account_update(slot, meta, account, txn_signature);
}
}
@ -160,6 +161,7 @@ pub mod tests {
account::{AccountSharedData, ReadableAccount},
clock::Slot,
pubkey::Pubkey,
signature::Signature,
},
std::sync::{
atomic::{AtomicBool, Ordering},
@ -186,6 +188,7 @@ pub mod tests {
slot: Slot,
meta: &StoredMeta,
account: &AccountSharedData,
_txn_signature: &Option<&Signature>,
) {
self.accounts_notified
.entry(meta.pubkey)
@ -337,24 +340,24 @@ pub mod tests {
let account1 =
AccountSharedData::new(account1_lamports1, 1, AccountSharedData::default().owner());
let slot0 = 0;
accounts.store_cached(slot0, &[(&key1, &account1)]);
accounts.store_cached(slot0, &[(&key1, &account1)], None);
let key2 = solana_sdk::pubkey::new_rand();
let account2_lamports: u64 = 200;
let account2 =
AccountSharedData::new(account2_lamports, 1, AccountSharedData::default().owner());
accounts.store_cached(slot0, &[(&key2, &account2)]);
accounts.store_cached(slot0, &[(&key2, &account2)], None);
let account1_lamports2 = 2;
let slot1 = 1;
let account1 = AccountSharedData::new(account1_lamports2, 1, account1.owner());
accounts.store_cached(slot1, &[(&key1, &account1)]);
accounts.store_cached(slot1, &[(&key1, &account1)], None);
let key3 = solana_sdk::pubkey::new_rand();
let account3_lamports: u64 = 300;
let account3 =
AccountSharedData::new(account3_lamports, 1, AccountSharedData::default().owner());
accounts.store_cached(slot1, &[(&key3, &account3)]);
accounts.store_cached(slot1, &[(&key3, &account3)], None);
let notifier = notifier.write().unwrap();
assert_eq!(notifier.accounts_notified.get(&key1).unwrap().len(), 2);

View File

@ -1,12 +1,18 @@
use {
crate::append_vec::{StoredAccountMeta, StoredMeta},
solana_sdk::{account::AccountSharedData, clock::Slot},
solana_sdk::{account::AccountSharedData, clock::Slot, signature::Signature},
std::sync::{Arc, RwLock},
};
pub trait AccountsUpdateNotifierInterface: std::fmt::Debug {
/// Notified when an account is updated at runtime, due to transaction activities
fn notify_account_update(&self, slot: Slot, meta: &StoredMeta, account: &AccountSharedData);
fn notify_account_update(
&self,
slot: Slot,
meta: &StoredMeta,
account: &AccountSharedData,
txn_signature: &Option<&Signature>,
);
/// Notified when the AccountsDb is initialized at start when restored
/// from a snapshot.