BankingStageErrorsTrackingS.../src/postgres.rs

244 lines
7.9 KiB
Rust
Raw Normal View History

use std::{
sync::{atomic::AtomicU64, Arc},
time::Duration,
};
2023-10-02 01:51:38 -07:00
use anyhow::Context;
use chrono::{DateTime, Utc};
2023-10-02 06:23:32 -07:00
use dashmap::DashMap;
use itertools::Itertools;
use tokio_postgres::{tls::MakeTlsConnect, types::ToSql, Client, NoTls, Socket};
2023-10-02 01:51:38 -07:00
use crate::{block_info::BlockInfo, transaction_info::TransactionInfo};
2023-10-02 01:51:38 -07:00
pub struct PostgresSession {
client: Client,
}
impl PostgresSession {
pub async fn new() -> anyhow::Result<Self> {
let pg_config = std::env::var("PG_CONFIG").context("env PG_CONFIG not found")?;
let pg_config = pg_config.parse::<tokio_postgres::Config>()?;
let client = Self::spawn_connection(pg_config, NoTls).await?;
2023-10-02 01:51:38 -07:00
Ok(Self { client })
}
async fn spawn_connection<T>(
pg_config: tokio_postgres::Config,
connector: T,
) -> anyhow::Result<Client>
where
T: MakeTlsConnect<Socket> + Send + 'static,
<T as MakeTlsConnect<Socket>>::Stream: Send,
{
let (client, connection) = pg_config
.connect(connector)
.await
.context("Connecting to Postgres failed")?;
tokio::spawn(async move {
log::info!("Connecting to Postgres");
if let Err(err) = connection.await {
log::error!("Connection to Postgres broke {err:?}");
return;
}
unreachable!("Postgres thread returned")
});
Ok(client)
}
pub fn multiline_query(query: &mut String, args: usize, rows: usize, types: &[&str]) {
let mut arg_index = 1usize;
for row in 0..rows {
query.push('(');
for i in 0..args {
if row == 0 && !types.is_empty() {
query.push_str(&format!("(${arg_index})::{}", types[i]));
} else {
query.push_str(&format!("${arg_index}"));
}
arg_index += 1;
if i != (args - 1) {
query.push(',');
}
}
query.push(')');
if row != (rows - 1) {
query.push(',');
}
}
}
pub async fn save_banking_transaction_results(
&self,
txs: &[TransactionInfo],
) -> anyhow::Result<()> {
2023-10-02 01:51:38 -07:00
if txs.is_empty() {
return Ok(());
}
2023-10-26 04:48:27 -07:00
const NUMBER_OF_ARGS: usize = 11;
2023-10-02 01:51:38 -07:00
let mut args: Vec<&(dyn ToSql + Sync)> = Vec::with_capacity(NUMBER_OF_ARGS * txs.len());
let txs: Vec<PostgresTransactionInfo> = txs
.iter()
.map(|x| PostgresTransactionInfo::from(x))
.collect();
2023-10-02 01:51:38 -07:00
for tx in txs.iter() {
args.push(&tx.signature);
args.push(&tx.transaction_message);
args.push(&tx.errors);
args.push(&tx.is_executed);
args.push(&tx.is_confirmed);
args.push(&tx.first_notification_slot);
args.push(&tx.cu_requested);
args.push(&tx.prioritization_fees);
args.push(&tx.utc_timestamp);
args.push(&tx.accounts_used);
2023-10-20 11:14:33 -07:00
args.push(&tx.processed_slot);
2023-10-02 01:51:38 -07:00
}
2023-10-02 06:23:32 -07:00
let mut query = String::from(
r#"
INSERT INTO banking_stage_results.transaction_infos
2023-10-20 11:14:33 -07:00
(signature, message, errors, is_executed, is_confirmed, first_notification_slot, cu_requested, prioritization_fees, utc_timestamp, accounts_used, processed_slot)
2023-10-02 06:23:32 -07:00
VALUES
"#,
);
Self::multiline_query(&mut query, NUMBER_OF_ARGS, txs.len(), &[]);
self.client.execute(&query, &args).await?;
2023-10-02 01:51:38 -07:00
Ok(())
}
pub async fn save_block(&self, block_info: BlockInfo) -> anyhow::Result<()> {
const NUMBER_OF_ARGS: usize = 9;
let mut args: Vec<&(dyn ToSql + Sync)> = Vec::with_capacity(NUMBER_OF_ARGS);
args.push(&block_info.block_hash);
args.push(&block_info.slot);
args.push(&block_info.leader_identity);
args.push(&block_info.successful_transactions);
args.push(&block_info.banking_stage_errors);
args.push(&block_info.processed_transactions);
args.push(&block_info.total_cu_used);
args.push(&block_info.total_cu_requested);
args.push(&block_info.heavily_writelocked_accounts);
let mut query = String::from(
r#"
INSERT INTO banking_stage_results.blocks
(block_hash, slot, leader_identity, successful_transactions, banking_stage_errors, processed_transactions, total_cu_used, total_cu_requested, heavily_writelocked_accounts)
VALUES
"#,
);
Self::multiline_query(&mut query, NUMBER_OF_ARGS, 1, &[]);
self.client.execute(&query, &args).await?;
Ok(())
}
2023-10-02 01:51:38 -07:00
}
pub struct Postgres {
2023-10-02 06:23:32 -07:00
session: Arc<PostgresSession>,
}
impl Postgres {
pub async fn new() -> Self {
let session = PostgresSession::new().await.unwrap();
Self {
session: Arc::new(session),
}
2023-10-02 06:23:32 -07:00
}
pub fn start_saving_transaction(
&self,
map_of_transaction: Arc<DashMap<String, TransactionInfo>>,
slots: Arc<AtomicU64>,
) {
2023-10-02 06:23:32 -07:00
let session = self.session.clone();
tokio::task::spawn(async move {
loop {
tokio::time::sleep(Duration::from_secs(60)).await;
let slot = slots.load(std::sync::atomic::Ordering::Relaxed);
let mut txs_to_store = vec![];
for tx in map_of_transaction.iter() {
if slot > tx.first_notification_slot + 300 {
txs_to_store.push(tx.clone());
}
}
if !txs_to_store.is_empty() {
println!("saving {}", txs_to_store.len());
for tx in &txs_to_store {
map_of_transaction.remove(&tx.signature);
}
let batches = txs_to_store.chunks(8).collect_vec();
for batch in batches {
session
.save_banking_transaction_results(batch)
.await
.unwrap();
2023-10-02 06:23:32 -07:00
}
}
}
});
}
pub async fn save_block_info(&self, block: BlockInfo) -> anyhow::Result<()> {
self.session.save_block(block).await
}
2023-10-02 06:23:32 -07:00
}
pub struct PostgresTransactionInfo {
pub signature: String,
pub transaction_message: Option<String>,
2023-10-21 09:40:19 -07:00
pub errors: Vec<String>,
2023-10-02 06:23:32 -07:00
pub is_executed: bool,
pub is_confirmed: bool,
pub first_notification_slot: i64,
pub cu_requested: Option<i64>,
pub prioritization_fees: Option<i64>,
pub utc_timestamp: DateTime<Utc>,
pub accounts_used: Vec<String>,
2023-10-20 11:14:33 -07:00
pub processed_slot: Option<i64>,
2023-10-02 06:23:32 -07:00
}
impl From<&TransactionInfo> for PostgresTransactionInfo {
fn from(value: &TransactionInfo) -> Self {
2023-10-21 09:40:19 -07:00
let errors = value
.errors
.iter()
2023-10-24 04:30:34 -07:00
.map(|(key, size)| format!("key:{}, slot:{}, count:{}", key.error, key.slot, size))
2023-10-21 09:40:19 -07:00
.collect_vec();
let accounts_used = value
.account_used
.iter()
2023-10-24 04:30:34 -07:00
.map(|x| format!("{}({})", x.0, x.1))
.collect();
2023-10-02 06:23:32 -07:00
Self {
signature: value.signature.clone(),
transaction_message: value
.transaction_message
.as_ref()
.map(|x| base64::encode(bincode::serialize(&x).unwrap())),
2023-10-02 06:23:32 -07:00
errors,
is_executed: value.is_executed,
is_confirmed: value.is_confirmed,
cu_requested: value.cu_requested.map(|x| x as i64),
first_notification_slot: value.first_notification_slot as i64,
prioritization_fees: value.prioritization_fees.map(|x| x as i64),
utc_timestamp: value.utc_timestamp,
accounts_used,
2023-10-20 11:14:33 -07:00
processed_slot: value.processed_slot.map(|x| x as i64),
2023-10-02 06:23:32 -07:00
}
}
}