Skip to content

Commit 75ced5e

Browse files
fanatidleafaar
authored andcommitted
rpc: add config option to getRPF (#217)
1 parent 2deff8a commit 75ced5e

File tree

4 files changed

+160
-71
lines changed

4 files changed

+160
-71
lines changed

rpc-client-api/src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,9 @@ pub struct RpcContextConfig {
352352
pub commitment: Option<CommitmentConfig>,
353353
pub min_context_slot: Option<Slot>,
354354
}
355+
356+
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
357+
#[serde(rename_all = "camelCase")]
358+
pub struct RpcRecentPrioritizationFeesConfig {
359+
pub percentile: Option<u16>,
360+
}

rpc/src/rpc.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,10 +2394,18 @@ impl JsonRpcRequestProcessor {
23942394
fn get_recent_prioritization_fees(
23952395
&self,
23962396
pubkeys: Vec<Pubkey>,
2397+
percentile: Option<u16>,
23972398
) -> Result<Vec<RpcPrioritizationFee>> {
2398-
Ok(self
2399-
.prioritization_fee_cache
2400-
.get_prioritization_fees(&pubkeys)
2399+
let cache = match percentile {
2400+
Some(percentile) => self
2401+
.prioritization_fee_cache
2402+
.get_prioritization_fees2(&pubkeys, percentile),
2403+
None => self
2404+
.prioritization_fee_cache
2405+
.get_prioritization_fees(&pubkeys),
2406+
};
2407+
2408+
Ok(cache
24012409
.into_iter()
24022410
.map(|(slot, prioritization_fee)| RpcPrioritizationFee {
24032411
slot,
@@ -3647,6 +3655,7 @@ pub mod rpc_full {
36473655
&self,
36483656
meta: Self::Metadata,
36493657
pubkey_strs: Option<Vec<String>>,
3658+
config: Option<RpcRecentPrioritizationFeesConfig>,
36503659
) -> Result<Vec<RpcPrioritizationFee>>;
36513660
}
36523661

@@ -4340,6 +4349,7 @@ pub mod rpc_full {
43404349
&self,
43414350
meta: Self::Metadata,
43424351
pubkey_strs: Option<Vec<String>>,
4352+
config: Option<RpcRecentPrioritizationFeesConfig>,
43434353
) -> Result<Vec<RpcPrioritizationFee>> {
43444354
let pubkey_strs = pubkey_strs.unwrap_or_default();
43454355
debug!(
@@ -4355,7 +4365,17 @@ pub mod rpc_full {
43554365
.into_iter()
43564366
.map(|pubkey_str| verify_pubkey(&pubkey_str))
43574367
.collect::<Result<Vec<_>>>()?;
4358-
meta.get_recent_prioritization_fees(pubkeys)
4368+
4369+
let RpcRecentPrioritizationFeesConfig { percentile } = config.unwrap_or_default();
4370+
if let Some(percentile) = percentile {
4371+
if percentile > 10_000 {
4372+
return Err(Error::invalid_params(
4373+
"Percentile is too big; max value is 10000".to_owned(),
4374+
));
4375+
}
4376+
}
4377+
4378+
meta.get_recent_prioritization_fees(pubkeys, percentile)
43594379
}
43604380
}
43614381
}

runtime/src/prioritization_fee.rs

Lines changed: 92 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,13 @@ pub enum PrioritizationFeeError {
136136
/// block; and the minimum fee for each writable account in all transactions in this block. The only relevant
137137
/// write account minimum fees are those greater than the block minimum transaction fee, because the minimum fee needed to land
138138
/// a transaction is determined by Max( min_transaction_fee, min_writable_account_fees(key), ...)
139-
#[derive(Debug)]
139+
#[derive(Debug, Default)]
140140
pub struct PrioritizationFee {
141-
// The minimum prioritization fee of transactions that landed in this block.
142-
min_transaction_fee: u64,
141+
// Prioritization fee of transactions that landed in this block.
142+
transaction_fees: Vec<u64>,
143143

144-
// The minimum prioritization fee of each writable account in transactions in this block.
145-
min_writable_account_fees: HashMap<Pubkey, u64>,
144+
// Prioritization fee of each writable account in transactions in this block.
145+
writable_account_fees: HashMap<Pubkey, Vec<u64>>,
146146

147147
// Default to `false`, set to `true` when a block is completed, therefore the minimum fees recorded
148148
// are finalized, and can be made available for use (e.g., RPC query)
@@ -152,33 +152,18 @@ pub struct PrioritizationFee {
152152
metrics: PrioritizationFeeMetrics,
153153
}
154154

155-
impl Default for PrioritizationFee {
156-
fn default() -> Self {
157-
PrioritizationFee {
158-
min_transaction_fee: u64::MAX,
159-
min_writable_account_fees: HashMap::new(),
160-
is_finalized: false,
161-
metrics: PrioritizationFeeMetrics::default(),
162-
}
163-
}
164-
}
165-
166155
impl PrioritizationFee {
167156
/// Update self for minimum transaction fee in the block and minimum fee for each writable account.
168157
pub fn update(&mut self, transaction_fee: u64, writable_accounts: Vec<Pubkey>) {
169158
let (_, update_us) = measure_us!({
170159
if !self.is_finalized {
171-
if transaction_fee < self.min_transaction_fee {
172-
self.min_transaction_fee = transaction_fee;
173-
}
160+
self.transaction_fees.push(transaction_fee);
174161

175162
for write_account in writable_accounts {
176-
self.min_writable_account_fees
163+
self.writable_account_fees
177164
.entry(write_account)
178-
.and_modify(|write_lock_fee| {
179-
*write_lock_fee = std::cmp::min(*write_lock_fee, transaction_fee)
180-
})
181-
.or_insert(transaction_fee);
165+
.or_default()
166+
.push(transaction_fee);
182167
}
183168

184169
self.metrics
@@ -193,38 +178,54 @@ impl PrioritizationFee {
193178
self.metrics.accumulate_total_update_elapsed_us(update_us);
194179
}
195180

196-
/// Accounts that have minimum fees lesser or equal to the minimum fee in the block are redundant, they are
197-
/// removed to reduce memory footprint when mark_block_completed() is called.
198-
fn prune_irrelevant_writable_accounts(&mut self) {
199-
self.metrics.total_writable_accounts_count = self.get_writable_accounts_count() as u64;
200-
self.min_writable_account_fees
201-
.retain(|_, account_fee| account_fee > &mut self.min_transaction_fee);
202-
self.metrics.relevant_writable_accounts_count = self.get_writable_accounts_count() as u64;
203-
}
204-
205181
pub fn mark_block_completed(&mut self) -> Result<(), PrioritizationFeeError> {
206182
if self.is_finalized {
207183
return Err(PrioritizationFeeError::BlockIsAlreadyFinalized);
208184
}
209-
self.prune_irrelevant_writable_accounts();
210185
self.is_finalized = true;
186+
187+
self.transaction_fees.sort();
188+
for fees in self.writable_account_fees.values_mut() {
189+
fees.sort()
190+
}
191+
192+
self.metrics.total_writable_accounts_count = self.get_writable_accounts_count() as u64;
193+
self.metrics.relevant_writable_accounts_count = self.get_writable_accounts_count() as u64;
194+
211195
Ok(())
212196
}
213197

214198
pub fn get_min_transaction_fee(&self) -> Option<u64> {
215-
(self.min_transaction_fee != u64::MAX).then_some(self.min_transaction_fee)
199+
self.transaction_fees.first().copied()
200+
}
201+
202+
fn get_percentile(fees: &[u64], percentile: u16) -> Option<u64> {
203+
let index = (percentile as usize).min(9_999) * fees.len() / 10_000;
204+
fees.get(index).copied()
205+
}
206+
207+
pub fn get_transaction_fee(&self, percentile: u16) -> Option<u64> {
208+
Self::get_percentile(&self.transaction_fees, percentile)
209+
}
210+
211+
pub fn get_min_writable_account_fee(&self, key: &Pubkey) -> Option<u64> {
212+
self.writable_account_fees
213+
.get(key)
214+
.and_then(|fees| fees.first().copied())
216215
}
217216

218-
pub fn get_writable_account_fee(&self, key: &Pubkey) -> Option<u64> {
219-
self.min_writable_account_fees.get(key).copied()
217+
pub fn get_writable_account_fee(&self, key: &Pubkey, percentile: u16) -> Option<u64> {
218+
self.writable_account_fees
219+
.get(key)
220+
.and_then(|fees| Self::get_percentile(fees, percentile))
220221
}
221222

222-
pub fn get_writable_account_fees(&self) -> impl Iterator<Item = (&Pubkey, &u64)> {
223-
self.min_writable_account_fees.iter()
223+
pub fn get_writable_account_fees(&self) -> impl Iterator<Item = (&Pubkey, &Vec<u64>)> {
224+
self.writable_account_fees.iter()
224225
}
225226

226227
pub fn get_writable_accounts_count(&self) -> usize {
227-
self.min_writable_account_fees.len()
228+
self.writable_account_fees.len()
228229
}
229230

230231
pub fn is_finalized(&self) -> bool {
@@ -236,20 +237,28 @@ impl PrioritizationFee {
236237

237238
// report this slot's min_transaction_fee and top 10 min_writable_account_fees
238239
let min_transaction_fee = self.get_min_transaction_fee().unwrap_or(0);
239-
let mut accounts_fees: Vec<_> = self.get_writable_account_fees().collect();
240-
accounts_fees.sort_by(|lh, rh| rh.1.cmp(lh.1));
241240
datapoint_info!(
242241
"block_min_prioritization_fee",
243242
("slot", slot as i64, i64),
244243
("entity", "block", String),
245244
("min_prioritization_fee", min_transaction_fee as i64, i64),
246245
);
247-
for (account_key, fee) in accounts_fees.iter().take(10) {
246+
247+
let mut accounts_fees: Vec<(&Pubkey, u64)> = self
248+
.get_writable_account_fees()
249+
.filter_map(|(account, fees)| {
250+
fees.first()
251+
.copied()
252+
.map(|min_account_fee| (account, min_transaction_fee.min(min_account_fee)))
253+
})
254+
.collect();
255+
accounts_fees.sort_by(|lh, rh| rh.1.cmp(&lh.1));
256+
for (account_key, fee) in accounts_fees.into_iter().take(10) {
248257
datapoint_trace!(
249258
"block_min_prioritization_fee",
250259
("slot", slot as i64, i64),
251260
("entity", account_key.to_string(), String),
252-
("min_prioritization_fee", **fee as i64, i64),
261+
("min_prioritization_fee", fee as i64, i64),
253262
);
254263
}
255264
}
@@ -275,22 +284,26 @@ mod tests {
275284
// [5, a, b ] --> [5, 5, 5, nil ]
276285
{
277286
prioritization_fee.update(5, vec![write_account_a, write_account_b]);
287+
assert!(prioritization_fee.mark_block_completed().is_ok());
288+
278289
assert_eq!(5, prioritization_fee.get_min_transaction_fee().unwrap());
279290
assert_eq!(
280291
5,
281292
prioritization_fee
282-
.get_writable_account_fee(&write_account_a)
293+
.get_min_writable_account_fee(&write_account_a)
283294
.unwrap()
284295
);
285296
assert_eq!(
286297
5,
287298
prioritization_fee
288-
.get_writable_account_fee(&write_account_b)
299+
.get_min_writable_account_fee(&write_account_b)
289300
.unwrap()
290301
);
291302
assert!(prioritization_fee
292-
.get_writable_account_fee(&write_account_c)
303+
.get_min_writable_account_fee(&write_account_c)
293304
.is_none());
305+
306+
prioritization_fee.is_finalized = false;
294307
}
295308

296309
// Assert for second transaction:
@@ -299,25 +312,29 @@ mod tests {
299312
// [9, b, c ] --> [5, 5, 5, 9 ]
300313
{
301314
prioritization_fee.update(9, vec![write_account_b, write_account_c]);
315+
assert!(prioritization_fee.mark_block_completed().is_ok());
316+
302317
assert_eq!(5, prioritization_fee.get_min_transaction_fee().unwrap());
303318
assert_eq!(
304319
5,
305320
prioritization_fee
306-
.get_writable_account_fee(&write_account_a)
321+
.get_min_writable_account_fee(&write_account_a)
307322
.unwrap()
308323
);
309324
assert_eq!(
310325
5,
311326
prioritization_fee
312-
.get_writable_account_fee(&write_account_b)
327+
.get_min_writable_account_fee(&write_account_b)
313328
.unwrap()
314329
);
315330
assert_eq!(
316331
9,
317332
prioritization_fee
318-
.get_writable_account_fee(&write_account_c)
333+
.get_min_writable_account_fee(&write_account_c)
319334
.unwrap()
320335
);
336+
337+
prioritization_fee.is_finalized = false;
321338
}
322339

323340
// Assert for third transaction:
@@ -326,44 +343,56 @@ mod tests {
326343
// [2, a, c ] --> [2, 2, 5, 2 ]
327344
{
328345
prioritization_fee.update(2, vec![write_account_a, write_account_c]);
346+
assert!(prioritization_fee.mark_block_completed().is_ok());
347+
329348
assert_eq!(2, prioritization_fee.get_min_transaction_fee().unwrap());
330349
assert_eq!(
331350
2,
332351
prioritization_fee
333-
.get_writable_account_fee(&write_account_a)
352+
.get_min_writable_account_fee(&write_account_a)
334353
.unwrap()
335354
);
336355
assert_eq!(
337356
5,
338357
prioritization_fee
339-
.get_writable_account_fee(&write_account_b)
358+
.get_min_writable_account_fee(&write_account_b)
340359
.unwrap()
341360
);
342361
assert_eq!(
343362
2,
344363
prioritization_fee
345-
.get_writable_account_fee(&write_account_c)
364+
.get_min_writable_account_fee(&write_account_c)
346365
.unwrap()
347366
);
367+
368+
prioritization_fee.is_finalized = false;
348369
}
349370

350-
// assert after prune, account a and c should be removed from cache to save space
371+
// assert after sort
351372
{
352-
prioritization_fee.prune_irrelevant_writable_accounts();
353-
assert_eq!(1, prioritization_fee.min_writable_account_fees.len());
373+
prioritization_fee.update(2, vec![write_account_a, write_account_c]);
374+
assert!(prioritization_fee.mark_block_completed().is_ok());
375+
354376
assert_eq!(2, prioritization_fee.get_min_transaction_fee().unwrap());
355-
assert!(prioritization_fee
356-
.get_writable_account_fee(&write_account_a)
357-
.is_none());
377+
assert_eq!(3, prioritization_fee.writable_account_fees.len());
378+
assert_eq!(
379+
2,
380+
prioritization_fee
381+
.get_min_writable_account_fee(&write_account_a)
382+
.unwrap()
383+
);
358384
assert_eq!(
359385
5,
360386
prioritization_fee
361-
.get_writable_account_fee(&write_account_b)
387+
.get_min_writable_account_fee(&write_account_b)
388+
.unwrap()
389+
);
390+
assert_eq!(
391+
2,
392+
prioritization_fee
393+
.get_min_writable_account_fee(&write_account_c)
362394
.unwrap()
363395
);
364-
assert!(prioritization_fee
365-
.get_writable_account_fee(&write_account_c)
366-
.is_none());
367396
}
368397
}
369398

0 commit comments

Comments
 (0)