Skip to content

Commit

Permalink
NDEV-2309: Implement NEON_REVISION caching (#226)
Browse files Browse the repository at this point in the history
  • Loading branch information
Deniskore authored Nov 22, 2023
1 parent 3c903ab commit bcfd04c
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 26 deletions.
48 changes: 24 additions & 24 deletions evm_loader/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions evm_loader/lib/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,68 @@ pub struct GetHolderRequest {
pub pubkey: Pubkey,
pub slot: Option<u64>,
}

#[cfg(test)]
mod tests {
use crate::types::tracer_ch_common::RevisionMap;

#[test]
fn test_build_ranges_empty() {
let results = Vec::new();
let exp = Vec::new();
let res = RevisionMap::build_ranges(results);
assert_eq!(res, exp);
}

#[test]
fn test_build_ranges_single_element() {
let results = vec![(1u64, String::from("Rev1"))];
let exp = vec![(1u64, 2u64, String::from("Rev1"))];
let res = RevisionMap::build_ranges(results);
assert_eq!(res, exp);
}

#[test]
fn test_build_ranges_multiple_elements_different_revision() {
let results = vec![
(222222222u64, String::from("Rev1")),
(333333333u64, String::from("Rev2")),
(444444444u64, String::from("Rev3")),
];

let exp = vec![
(222222222u64, 333333333u64, String::from("Rev1")),
(333333334u64, 444444444u64, String::from("Rev2")),
(444444445u64, 444444445u64, String::from("Rev3")),
];
let res = RevisionMap::build_ranges(results);

assert_eq!(res, exp);
}

#[test]
fn test_rangemap() {
let ranges = vec![
(123456780, 123456788, String::from("Rev1")),
(123456789, 123456793, String::from("Rev2")),
(123456794, 123456799, String::from("Rev3")),
];
let map = RevisionMap::new(ranges);

assert_eq!(map.get(123456779), None); // Below the bottom bound of the first range

assert_eq!(map.get(123456780), Some(String::from("Rev1"))); // The bottom bound of the first range
assert_eq!(map.get(123456785), Some(String::from("Rev1"))); // Within the first range
assert_eq!(map.get(123456788), Some(String::from("Rev1"))); // The top bound of the first range

assert_eq!(map.get(123456793), Some(String::from("Rev2"))); // The bottom bound of the second range
assert_eq!(map.get(123456790), Some(String::from("Rev2"))); // Within the second range
assert_eq!(map.get(123456793), Some(String::from("Rev2"))); // The top bound of the second range

assert_eq!(map.get(123456799), Some(String::from("Rev3"))); // The bottom bound of the third range
assert_eq!(map.get(123456795), Some(String::from("Rev3"))); // Within the third range
assert_eq!(map.get(123456799), Some(String::from("Rev3"))); // The top bound of the third range

assert_eq!(map.get(123456800), None); // Beyond the top end of the last range
}
}
67 changes: 67 additions & 0 deletions evm_loader/lib/src/types/tracer_ch_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::fmt;
use clickhouse::Row;
use serde::{Deserialize, Serialize};
use solana_sdk::{account::Account, pubkey::Pubkey};
use std::collections::BTreeMap;
use std::time::Instant;
use thiserror::Error;

pub const ROOT_BLOCK_DELAY: u8 = 100;
Expand Down Expand Up @@ -52,6 +54,13 @@ impl SlotParent {
}
}

// NEON_REVISION row
#[derive(Row, Deserialize)]
pub struct RevisionRow {
pub slot: u64,
pub data: Vec<u8>,
}

#[derive(Row, serde::Deserialize, Clone)]
pub struct AccountRow {
pub owner: Vec<u8>,
Expand Down Expand Up @@ -129,3 +138,61 @@ pub struct EthSyncing {
pub current_block: u64,
pub highest_block: u64,
}

pub struct RevisionMap {
map: BTreeMap<u64, String>,
pub last_update: Instant,
}

impl RevisionMap {
pub fn new(neon_revision_ranges: Vec<(u64, u64, String)>) -> Self {
let mut map = BTreeMap::new();

for (start, end, value) in neon_revision_ranges {
map.insert(start, value.clone());
map.insert(end, value);
}

let last_update = std::time::Instant::now();

RevisionMap { map, last_update }
}

// When deploying a program for the first time it is now only available in the next slot (the slot after the one the deployment transaction landed in).
// When undeploying / closing a program the change is visible immediately and the very next instruction even within the transaction can not access it anymore.
// When redeploying the program becomes temporarily closed immediately and will reopen with the new version in the next slot.
pub fn build_ranges(input: Vec<(u64, String)>) -> Vec<(u64, u64, String)> {
let mut ranges = Vec::new();

for i in 0..input.len() {
let (start, rev) = input[i].clone();
let end = if i < input.len() - 1 {
input[i + 1].0 - 1
} else {
start
};

match i {
0 => ranges.push((start, end + 1, rev.clone())),
_ if i == input.len() - 1 => ranges.push((start + 1, end + 1, rev.clone())),
_ => ranges.push((start + 1, end + 1, rev.clone())),
}
}
ranges
}

pub fn get(&self, slot: u64) -> Option<String> {
// Check if slot is less than the starting range or
// greater than the ending range
let (start, _) = self.map.iter().next()?;
let (end, _) = self.map.iter().last()?;

if slot < *start || slot > *end {
return None;
}

let value = self.map.range(..=slot).rev().next();

value.map(|(_, v)| v.clone())
}
}
37 changes: 35 additions & 2 deletions evm_loader/lib/src/types/tracer_ch_db.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::{
commands::get_neon_elf::get_elf_parameter,
types::tracer_ch_common::{AccountRow, ChError, SlotParent, ROOT_BLOCK_DELAY},
types::tracer_ch_common::{AccountRow, ChError, RevisionRow, SlotParent, ROOT_BLOCK_DELAY},
};

use super::{
tracer_ch_common::{ChResult, EthSyncStatus, EthSyncing, SlotParentRooted},
tracer_ch_common::{ChResult, EthSyncStatus, EthSyncing, RevisionMap, SlotParentRooted},
ChDbConfig,
};

Expand Down Expand Up @@ -586,6 +586,39 @@ impl ClickHouseDb {
}
}

pub async fn get_neon_revisions(&self, pubkey: &Pubkey) -> ChResult<RevisionMap> {
let query = r#"SELECT slot, data
FROM events.update_account_distributed
WHERE
pubkey = ?
ORDER BY
slot ASC,
write_version ASC"#;

let pubkey_str = format!("{:?}", pubkey.to_bytes());
let rows: Vec<RevisionRow> = self
.client
.query(query)
.bind(pubkey_str)
.fetch_all()
.await?;

let mut results: Vec<(u64, String)> = Vec::new();

for row in rows {
let neon_revision = get_elf_parameter(&row.data, "NEON_REVISION").map_err(|e| {
ChError::Db(clickhouse::error::Error::Custom(format!(
"Failed to get NEON_REVISION, error: {:?}",
e
)))
})?;
results.push((row.slot, neon_revision));
}
let ranges = RevisionMap::build_ranges(results);

Ok(RevisionMap::new(ranges))
}

pub async fn get_slot_by_blockhash(&self, blockhash: &str) -> ChResult<u64> {
let query = r#"SELECT slot
FROM events.notify_block_distributed
Expand Down

0 comments on commit bcfd04c

Please sign in to comment.