use solana_sdk::transaction::VersionedTransaction;
use std::collections::VecDeque;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use super::TransactionWithSlot;
pub struct TransactionWithSlotPool {
pool: Arc<Mutex<VecDeque<Box<TransactionWithSlot>>>>,
max_size: usize,
}
impl TransactionWithSlotPool {
pub fn new(initial_size: usize, max_size: usize) -> Self {
let mut pool = VecDeque::with_capacity(initial_size);
for _ in 0..initial_size {
pool.push_back(Box::new(TransactionWithSlot::default()));
}
Self { pool: Arc::new(Mutex::new(pool)), max_size }
}
pub fn acquire(&self) -> PooledTransactionWithSlot {
let mut pool = self.pool.lock().unwrap();
let transaction = match pool.pop_front() {
Some(reused) => reused,
None => Box::new(TransactionWithSlot::default()),
};
PooledTransactionWithSlot {
transaction,
pool: Arc::clone(&self.pool),
max_size: self.max_size,
}
}
}
pub struct PooledTransactionWithSlot {
transaction: Box<TransactionWithSlot>,
pool: Arc<Mutex<VecDeque<Box<TransactionWithSlot>>>>,
max_size: usize,
}
impl PooledTransactionWithSlot {
pub fn reset_from_data(
&mut self,
transaction: VersionedTransaction,
slot: u64,
recv_us: i64,
tx_index: Option<u64>,
) {
self.transaction.transaction = transaction;
self.transaction.slot = slot;
self.transaction.recv_us = recv_us;
self.transaction.tx_index = tx_index;
}
pub fn into_transaction_with_slot(mut self) -> TransactionWithSlot {
std::mem::take(self.deref_mut())
}
}
impl Drop for PooledTransactionWithSlot {
fn drop(&mut self) {
let mut pool = self.pool.lock().unwrap();
if pool.len() < self.max_size {
self.transaction.slot = 0;
self.transaction.recv_us = 0;
self.transaction.tx_index = None;
self.transaction.transaction = VersionedTransaction::default();
pool.push_back(std::mem::take(&mut self.transaction));
}
}
}
impl std::ops::Deref for PooledTransactionWithSlot {
type Target = TransactionWithSlot;
fn deref(&self) -> &Self::Target {
&self.transaction
}
}
impl std::ops::DerefMut for PooledTransactionWithSlot {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.transaction
}
}
pub struct ShredPoolManager {
transaction_pool: TransactionWithSlotPool,
}
impl ShredPoolManager {
pub fn new() -> Self {
Self {
transaction_pool: TransactionWithSlotPool::new(
5000, 15000, ),
}
}
pub fn get_transaction_pool(&self) -> &TransactionWithSlotPool {
&self.transaction_pool
}
pub fn create_transaction_with_slot_optimized(
&self,
transaction: VersionedTransaction,
slot: u64,
recv_us: i64,
tx_index: Option<u64>,
) -> TransactionWithSlot {
let mut pooled_tx = self.transaction_pool.acquire();
pooled_tx.reset_from_data(transaction, slot, recv_us, tx_index);
pooled_tx.into_transaction_with_slot()
}
}
impl Default for ShredPoolManager {
fn default() -> Self {
Self::new()
}
}
pub static GLOBAL_SHRED_POOL_MANAGER: std::sync::LazyLock<ShredPoolManager> =
std::sync::LazyLock::new(ShredPoolManager::new);
pub mod factory {
use super::*;
pub fn create_transaction_with_slot_pooled(
transaction: VersionedTransaction,
slot: u64,
recv_us: i64,
tx_index: Option<u64>,
) -> TransactionWithSlot {
GLOBAL_SHRED_POOL_MANAGER.create_transaction_with_slot_optimized(
transaction,
slot,
recv_us,
tx_index,
)
}
}