put rpcs in a rwlock again
This commit is contained in:
parent
670b64de31
commit
31f772591b
@ -72,6 +72,7 @@ RUN --mount=type=cache,target=/root/.cargo/git \
|
||||
--mount=type=cache,target=/app/target \
|
||||
set -eux; \
|
||||
\
|
||||
[ -e "$(pwd)/payment-contracts/src/contracts/mod.rs" ] || touch "$(pwd)/payment-contracts/build.rs"; \
|
||||
cargo --locked --verbose fetch
|
||||
|
||||
# build tests (done its in own FROM so that it can run in parallel)
|
||||
@ -86,6 +87,7 @@ RUN --mount=type=cache,target=/root/.cargo/git \
|
||||
--mount=type=cache,target=/app/target \
|
||||
set -eux; \
|
||||
\
|
||||
[ -e "$(pwd)/payment-contracts/src/contracts/mod.rs" ] || touch "$(pwd)/payment-contracts/build.rs"; \
|
||||
RUST_LOG=web3_proxy=trace,info \
|
||||
cargo \
|
||||
--frozen \
|
||||
@ -106,6 +108,7 @@ RUN --mount=type=cache,target=/root/.cargo/git \
|
||||
--mount=type=cache,target=/app/target \
|
||||
set -eux; \
|
||||
\
|
||||
[ -e "$(pwd)/payment-contracts/src/contracts/mod.rs" ] || touch "$(pwd)/payment-contracts/build.rs"; \
|
||||
cargo install \
|
||||
--features "$WEB3_PROXY_FEATURES" \
|
||||
--frozen \
|
||||
|
@ -654,6 +654,7 @@ impl Web3ProxyApp {
|
||||
|
||||
pub async fn apply_top_config(&self, new_top_config: TopConfig) -> Web3ProxyResult<()> {
|
||||
// TODO: also update self.config from new_top_config.app
|
||||
info!("applying new config");
|
||||
|
||||
// connect to the backends
|
||||
self.balanced_rpcs
|
||||
@ -685,6 +686,8 @@ impl Web3ProxyApp {
|
||||
}
|
||||
}
|
||||
|
||||
info!("config applied successfully");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::errors::{Web3ProxyErrorContext, Web3ProxyResult};
|
||||
use crate::frontend::users::referral;
|
||||
use entities::{
|
||||
admin_increase_balance_receipt, increase_on_chain_balance_receipt, referee, referrer,
|
||||
rpc_accounting_v2, rpc_key, stripe_increase_balance_receipt,
|
||||
@ -147,7 +146,7 @@ impl Balance {
|
||||
.into_tuple()
|
||||
.one(db_conn)
|
||||
.await
|
||||
.web3_context("fetching rpc_accounting_v2")?
|
||||
.web3_context("fetching total_spent_paid_credits and total_spent")?
|
||||
.unwrap_or_default();
|
||||
|
||||
let one_time_referee_bonus = referee::Entity::find()
|
||||
@ -160,7 +159,7 @@ impl Balance {
|
||||
.into_tuple()
|
||||
.one(db_conn)
|
||||
.await
|
||||
.web3_context("fetching referee")?
|
||||
.web3_context("fetching one time referee bonus")?
|
||||
.unwrap_or_default();
|
||||
|
||||
let referal_bonus = referee::Entity::find()
|
||||
@ -177,7 +176,7 @@ impl Balance {
|
||||
.into_tuple()
|
||||
.one(db_conn)
|
||||
.await
|
||||
.web3_context("fetching referee and referral_codes")?
|
||||
.web3_context("fetching referal bonus")?
|
||||
.unwrap_or_default();
|
||||
|
||||
let balance = Self {
|
||||
|
@ -20,7 +20,8 @@ use futures::StreamExt;
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use migration::sea_orm::DatabaseConnection;
|
||||
use moka::future::{Cache, CacheBuilder, ConcurrentCacheExt};
|
||||
use moka::future::{Cache, CacheBuilder};
|
||||
use parking_lot::RwLock;
|
||||
use serde::ser::{SerializeStruct, Serializer};
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
@ -44,7 +45,7 @@ pub struct Web3Rpcs {
|
||||
pub(crate) block_sender: flume::Sender<(Option<Web3ProxyBlock>, Arc<Web3Rpc>)>,
|
||||
/// any requests will be forwarded to one (or more) of these connections
|
||||
/// TODO: hopefully this not being an async lock will be okay. if you need it across awaits, clone the arc
|
||||
pub(crate) by_name: Cache<String, Arc<Web3Rpc>>,
|
||||
pub(crate) by_name: RwLock<HashMap<String, Arc<Web3Rpc>>>,
|
||||
/// all providers with the same consensus head block. won't update if there is no `self.watch_consensus_head_sender`
|
||||
/// TODO: document that this is a watch sender and not a broadcast! if things get busy, blocks might get missed
|
||||
/// TODO: why is watch_consensus_head_sender in an Option, but this one isn't?
|
||||
@ -113,7 +114,7 @@ impl Web3Rpcs {
|
||||
watch::channel(Default::default());
|
||||
|
||||
// by_name starts empty. self.apply_server_configs will add to it
|
||||
let by_name = Cache::builder().build();
|
||||
let by_name = RwLock::new(HashMap::new());
|
||||
|
||||
let max_head_block_lag = max_head_block_lag.unwrap_or(5.into());
|
||||
|
||||
@ -225,18 +226,20 @@ impl Web3Rpcs {
|
||||
|
||||
while let Some(x) = spawn_handles.next().await {
|
||||
match x {
|
||||
Ok(Ok((rpc, _handle))) => {
|
||||
Ok(Ok((new_rpc, _handle))) => {
|
||||
// web3 connection worked
|
||||
|
||||
let old_rpc = self.by_name.read().get(&new_rpc.name).map(Arc::clone);
|
||||
|
||||
// clean up the old rpc if it exists
|
||||
if let Some(old_rpc) = self.by_name.get(&rpc.name) {
|
||||
if let Some(old_rpc) = old_rpc {
|
||||
trace!("old_rpc: {}", old_rpc);
|
||||
|
||||
// if the old rpc was synced, wait for the new one to sync
|
||||
if old_rpc.head_block.as_ref().unwrap().borrow().is_some() {
|
||||
let mut new_head_receiver =
|
||||
rpc.head_block.as_ref().unwrap().subscribe();
|
||||
trace!("waiting for new {} connection to sync", rpc);
|
||||
new_rpc.head_block.as_ref().unwrap().subscribe();
|
||||
trace!("waiting for new {} connection to sync", new_rpc);
|
||||
|
||||
// TODO: maximum wait time
|
||||
while new_head_receiver.borrow_and_update().is_none() {
|
||||
@ -248,8 +251,7 @@ impl Web3Rpcs {
|
||||
|
||||
// new rpc is synced (or old one was not synced). update the local map
|
||||
// make sure that any new requests use the new connection
|
||||
self.by_name.insert(rpc.name.clone(), rpc).await;
|
||||
self.by_name.sync();
|
||||
self.by_name.write().insert(new_rpc.name.clone(), new_rpc);
|
||||
|
||||
// tell the old rpc to disconnect
|
||||
if let Some(ref disconnect_sender) = old_rpc.disconnect_watch {
|
||||
@ -257,8 +259,7 @@ impl Web3Rpcs {
|
||||
disconnect_sender.send_replace(true);
|
||||
}
|
||||
} else {
|
||||
self.by_name.insert(rpc.name.clone(), rpc).await;
|
||||
self.by_name.sync();
|
||||
self.by_name.write().insert(new_rpc.name.clone(), new_rpc);
|
||||
}
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
@ -287,17 +288,15 @@ impl Web3Rpcs {
|
||||
}
|
||||
|
||||
pub fn get(&self, conn_name: &str) -> Option<Arc<Web3Rpc>> {
|
||||
self.by_name.get(conn_name)
|
||||
self.by_name.read().get(conn_name).cloned()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
// // TODO: this seems not great. investigate better ways to do this
|
||||
// self.by_name.sync();
|
||||
self.by_name.entry_count() as usize
|
||||
self.by_name.read().len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() > 0
|
||||
self.by_name.read().is_empty()
|
||||
}
|
||||
|
||||
/// TODO: rename to be consistent between "head" and "synced"
|
||||
@ -652,7 +651,7 @@ impl Web3Rpcs {
|
||||
let mut earliest_retry_at = None;
|
||||
|
||||
// TODO: filter the rpcs with Ranked.will_work_now
|
||||
let mut all_rpcs: Vec<_> = self.by_name.iter().map(|x| x.1).collect();
|
||||
let mut all_rpcs: Vec<_> = self.by_name.read().values().cloned().collect();
|
||||
|
||||
let mut max_count = if let Some(max_count) = max_count {
|
||||
max_count
|
||||
@ -1291,8 +1290,6 @@ impl Display for Web3Rpcs {
|
||||
impl fmt::Debug for Web3Rpcs {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: the default formatter takes forever to write. this is too quiet though
|
||||
self.by_name.sync();
|
||||
|
||||
let consensus_rpcs = self.watch_ranked_rpcs.borrow().is_some();
|
||||
|
||||
f.debug_struct("Web3Rpcs")
|
||||
@ -1310,10 +1307,8 @@ impl Serialize for Web3Rpcs {
|
||||
let mut state = serializer.serialize_struct("Web3Rpcs", 6)?;
|
||||
|
||||
{
|
||||
// TODO: i didn't think we'd need this, but it shouldn't hurt
|
||||
self.by_name.sync();
|
||||
|
||||
let rpcs: Vec<Arc<Web3Rpc>> = self.by_name.iter().map(|x| x.1).collect();
|
||||
let by_name = self.by_name.read();
|
||||
let rpcs: Vec<&Arc<Web3Rpc>> = by_name.values().collect();
|
||||
// TODO: coordinate with frontend team to rename "conns" to "rpcs"
|
||||
state.serialize_field("conns", &rpcs)?;
|
||||
}
|
||||
@ -1528,19 +1523,14 @@ mod tests {
|
||||
|
||||
let chain_id = 1;
|
||||
|
||||
let by_name = Cache::builder().build();
|
||||
by_name
|
||||
.insert(head_rpc.name.clone(), head_rpc.clone())
|
||||
.await;
|
||||
by_name
|
||||
.insert(lagged_rpc.name.clone(), lagged_rpc.clone())
|
||||
.await;
|
||||
by_name.sync();
|
||||
let mut by_name = HashMap::new();
|
||||
by_name.insert(head_rpc.name.clone(), head_rpc.clone());
|
||||
by_name.insert(lagged_rpc.name.clone(), lagged_rpc.clone());
|
||||
|
||||
// TODO: make a Web3Rpcs::new
|
||||
let rpcs = Web3Rpcs {
|
||||
block_sender: block_sender.clone(),
|
||||
by_name,
|
||||
by_name: RwLock::new(by_name),
|
||||
chain_id,
|
||||
name: "test".to_string(),
|
||||
watch_head_block: Some(watch_consensus_head_sender),
|
||||
@ -1803,18 +1793,13 @@ mod tests {
|
||||
|
||||
let chain_id = 1;
|
||||
|
||||
let by_name = Cache::builder().build();
|
||||
by_name
|
||||
.insert(pruned_rpc.name.clone(), pruned_rpc.clone())
|
||||
.await;
|
||||
by_name
|
||||
.insert(archive_rpc.name.clone(), archive_rpc.clone())
|
||||
.await;
|
||||
by_name.sync();
|
||||
let mut by_name = HashMap::new();
|
||||
by_name.insert(pruned_rpc.name.clone(), pruned_rpc.clone());
|
||||
by_name.insert(archive_rpc.name.clone(), archive_rpc.clone());
|
||||
|
||||
let rpcs = Web3Rpcs {
|
||||
block_sender,
|
||||
by_name,
|
||||
by_name: RwLock::new(by_name),
|
||||
chain_id,
|
||||
name: "test".to_string(),
|
||||
watch_head_block: Some(watch_consensus_head_sender),
|
||||
@ -1992,22 +1977,17 @@ mod tests {
|
||||
|
||||
let chain_id = 1;
|
||||
|
||||
let by_name = Cache::builder().build();
|
||||
by_name
|
||||
.insert(mock_geth.name.clone(), mock_geth.clone())
|
||||
.await;
|
||||
by_name
|
||||
.insert(
|
||||
mock_erigon_archive.name.clone(),
|
||||
mock_erigon_archive.clone(),
|
||||
)
|
||||
.await;
|
||||
by_name.sync();
|
||||
let mut by_name = HashMap::new();
|
||||
by_name.insert(mock_geth.name.clone(), mock_geth.clone());
|
||||
by_name.insert(
|
||||
mock_erigon_archive.name.clone(),
|
||||
mock_erigon_archive.clone(),
|
||||
);
|
||||
|
||||
// TODO: make a Web3Rpcs::new
|
||||
let rpcs = Web3Rpcs {
|
||||
block_sender,
|
||||
by_name,
|
||||
by_name: RwLock::new(by_name),
|
||||
chain_id,
|
||||
name: "test".to_string(),
|
||||
watch_head_block: Some(watch_consensus_head_sender),
|
||||
|
Loading…
Reference in New Issue
Block a user