diff --git a/Cargo.lock b/Cargo.lock index 6d454ea4..8c28b54a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,7 +653,7 @@ dependencies = [ [[package]] name = "auction-server" -version = "0.34.2" +version = "0.34.3" dependencies = [ "anchor-lang", "anchor-lang-idl", @@ -670,6 +670,7 @@ dependencies = [ "borsh 1.5.5", "clap", "clickhouse", + "dashmap 6.1.0", "email_address", "express-relay", "express-relay-api-types", @@ -1641,6 +1642,20 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -2252,7 +2267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" dependencies = [ "cfg-if", - "dashmap", + "dashmap 5.5.3", "futures", "futures-timer", "no-std-compat", @@ -5228,7 +5243,7 @@ checksum = "1e25b7073890561a6b7875a921572fc4a9a2c78b3e60fb8e0a7ee4911961f8bd" dependencies = [ "async-trait", "bincode", - "dashmap", + "dashmap 5.5.3", "futures", "futures-util", "indexmap 2.8.0", @@ -6902,7 +6917,7 @@ dependencies = [ "async-channel", "bytes", "crossbeam-channel", - "dashmap", + "dashmap 5.5.3", "futures", "futures-util", "governor", diff --git a/auction-server/Cargo.toml b/auction-server/Cargo.toml index 2830f996..0f57e4cc 100644 --- a/auction-server/Cargo.toml +++ b/auction-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auction-server" -version = "0.34.2" +version = "0.34.3" edition = "2021" license-file = "license.txt" @@ -63,6 +63,7 @@ tokio-metrics = { version = "0.4.2", features = ["rt"] } clickhouse = { version = "0.13.2", features = ["time", "uuid", "native-tls", "inserter"] } sha2 = "0.10.9" tokio-tungstenite = { workspace = true, features = ["native-tls"] } +dashmap = "6.1.0" [dev-dependencies] mockall = "0.13.1" diff --git a/auction-server/src/auction/repository/add_auction.rs b/auction-server/src/auction/repository/add_auction.rs index d8e4c07e..5ae48b92 100644 --- a/auction-server/src/auction/repository/add_auction.rs +++ b/auction-server/src/auction/repository/add_auction.rs @@ -6,11 +6,7 @@ use { impl Repository { #[tracing::instrument(skip_all)] async fn add_in_memory_auction(&self, auction: entities::Auction) { - self.in_memory_store - .auctions - .write() - .await - .insert(auction.id, auction); + self.in_memory_store.auctions.insert(auction.id, auction); } // NOTE: Do not call this function directly. Instead call `add_auction` from `Service`. diff --git a/auction-server/src/auction/repository/conclude_auction.rs b/auction-server/src/auction/repository/conclude_auction.rs index 62adee7d..836a5ce4 100644 --- a/auction-server/src/auction/repository/conclude_auction.rs +++ b/auction-server/src/auction/repository/conclude_auction.rs @@ -10,7 +10,7 @@ impl Repository { pub async fn conclude_auction(&self, auction_id: entities::AuctionId) -> anyhow::Result<()> { tracing::Span::current().record("auction_id", auction_id.to_string()); self.db.conclude_auction(auction_id).await?; - self.remove_in_memory_auction(auction_id).await; + self.remove_in_memory_auction(auction_id); Ok(()) } } diff --git a/auction-server/src/auction/repository/get_in_memory_auction_by_bid_id.rs b/auction-server/src/auction/repository/get_in_memory_auction_by_bid_id.rs index d5b52eed..b5118f56 100644 --- a/auction-server/src/auction/repository/get_in_memory_auction_by_bid_id.rs +++ b/auction-server/src/auction/repository/get_in_memory_auction_by_bid_id.rs @@ -4,12 +4,11 @@ use { }; impl Repository { - pub async fn get_in_memory_auction_bid_by_bid_id( + pub fn get_in_memory_auction_bid_by_bid_id( &self, bid_id: entities::BidId, ) -> Option { self.get_in_memory_auctions() - .await .into_iter() .find_map(|auction| auction.bids.iter().find(|bid| bid.id == bid_id).cloned()) } diff --git a/auction-server/src/auction/repository/get_in_memory_auction_by_id.rs b/auction-server/src/auction/repository/get_in_memory_auction_by_id.rs index d4d8d821..80d291be 100644 --- a/auction-server/src/auction/repository/get_in_memory_auction_by_id.rs +++ b/auction-server/src/auction/repository/get_in_memory_auction_by_id.rs @@ -4,15 +4,13 @@ use { }; impl Repository { - pub async fn get_in_memory_auction_by_id( + pub fn get_in_memory_auction_by_id( &self, auction_id: entities::AuctionId, ) -> Option { self.in_memory_store .auctions - .read() - .await .get(&auction_id) - .cloned() + .map(|auction_ref| auction_ref.clone()) } } diff --git a/auction-server/src/auction/repository/get_in_memory_auctions.rs b/auction-server/src/auction/repository/get_in_memory_auctions.rs index 4c22509b..079b3902 100644 --- a/auction-server/src/auction/repository/get_in_memory_auctions.rs +++ b/auction-server/src/auction/repository/get_in_memory_auctions.rs @@ -4,13 +4,11 @@ use { }; impl Repository { - pub async fn get_in_memory_auctions(&self) -> Vec { + pub fn get_in_memory_auctions(&self) -> Vec { self.in_memory_store .auctions - .read() - .await - .values() - .cloned() + .iter() + .map(|entry| entry.value().clone()) .collect() } } diff --git a/auction-server/src/auction/repository/mod.rs b/auction-server/src/auction/repository/mod.rs index c8bebf9d..e5bd7995 100644 --- a/auction-server/src/auction/repository/mod.rs +++ b/auction-server/src/auction/repository/mod.rs @@ -5,6 +5,7 @@ use { entities::ChainId, }, axum_prometheus::metrics, + dashmap::DashMap, solana_sdk::pubkey::Pubkey, std::collections::{ HashMap, @@ -66,7 +67,7 @@ pub struct ChainStoreEvm {} #[derive(Debug)] pub struct InMemoryStore { pub pending_bids: RwLock>>, - pub auctions: RwLock>, + pub auctions: DashMap, pub auction_lock: Mutex>, pub bid_lock: Mutex>, @@ -78,7 +79,7 @@ impl Default for InMemoryStore { fn default() -> Self { Self { pending_bids: RwLock::new(HashMap::new()), - auctions: RwLock::new(HashMap::new()), + auctions: DashMap::new(), auction_lock: Mutex::new(HashMap::new()), bid_lock: Mutex::new(HashMap::new()), chain_store: ChainStoreSvm::default(), @@ -110,7 +111,7 @@ impl Repository { pub(super) async fn update_metrics(&self) { let label = [("chain_id", self.chain_id.to_string())]; let store = &self.in_memory_store; - metrics::gauge!("in_memory_auctions", &label).set(store.auctions.read().await.len() as f64); + metrics::gauge!("in_memory_auctions", &label).set(store.auctions.len() as f64); metrics::gauge!("in_memory_pending_bids", &label) .set(store.pending_bids.read().await.len() as f64); metrics::gauge!("in_memory_auction_locks", &label) diff --git a/auction-server/src/auction/repository/remove_in_memory_auction.rs b/auction-server/src/auction/repository/remove_in_memory_auction.rs index 9787cab6..3d32d857 100644 --- a/auction-server/src/auction/repository/remove_in_memory_auction.rs +++ b/auction-server/src/auction/repository/remove_in_memory_auction.rs @@ -5,12 +5,8 @@ use { impl Repository { #[tracing::instrument(skip_all, fields(auction_id))] - pub async fn remove_in_memory_auction(&self, auction_id: entities::AuctionId) { + pub fn remove_in_memory_auction(&self, auction_id: entities::AuctionId) { tracing::Span::current().record("auction_id", auction_id.to_string()); - self.in_memory_store - .auctions - .write() - .await - .remove(&auction_id); + self.in_memory_store.auctions.remove(&auction_id); } } diff --git a/auction-server/src/auction/repository/update_bid_status.rs b/auction-server/src/auction/repository/update_bid_status.rs index 55b01237..a875d1c5 100644 --- a/auction-server/src/auction/repository/update_bid_status.rs +++ b/auction-server/src/auction/repository/update_bid_status.rs @@ -10,14 +10,14 @@ use { impl Repository { // Find the in memory auction which contains the bid and update the bid status #[tracing::instrument(skip_all)] - async fn update_in_memory_auction_bid( + fn update_in_memory_auction_bid( &self, bid: &entities::Bid, new_status: entities::BidStatusSvm, ) { if let Some(auction_id) = new_status.get_auction_id() { - let mut write_guard = self.in_memory_store.auctions.write().await; - if let Some(auction) = write_guard.get_mut(&auction_id) { + if let Some(mut auction_ref) = self.in_memory_store.auctions.get_mut(&auction_id) { + let auction = auction_ref.value_mut(); let bid_index = auction.bids.iter().position(|b| b.id == bid.id); if let Some(index) = bid_index { auction.bids[index].status = new_status; @@ -36,7 +36,7 @@ impl Repository { self.db.update_bid_status(&bid, &new_status).await?; if is_updated && !new_status.is_pending() { self.remove_in_memory_pending_bids(&[bid.clone()]).await; - self.update_in_memory_auction_bid(&bid, new_status).await; + self.update_in_memory_auction_bid(&bid, new_status); } Ok((is_updated, conclusion_time_new)) } diff --git a/auction-server/src/auction/repository/update_in_memory_auction.rs b/auction-server/src/auction/repository/update_in_memory_auction.rs index dbc64a63..943896ba 100644 --- a/auction-server/src/auction/repository/update_in_memory_auction.rs +++ b/auction-server/src/auction/repository/update_in_memory_auction.rs @@ -7,9 +7,8 @@ impl Repository { #[tracing::instrument(skip_all, fields(auction_id))] pub async fn update_in_memory_auction(&self, auction: entities::Auction) { tracing::Span::current().record("auction_id", auction.id.to_string()); - let mut write_guard = self.in_memory_store.auctions.write().await; - match write_guard.get_mut(&auction.id) { - Some(a) => { + match self.in_memory_store.auctions.get_mut(&auction.id) { + Some(mut a) => { *a = auction; } None => { diff --git a/auction-server/src/auction/service/cancel_bid.rs b/auction-server/src/auction/service/cancel_bid.rs index 8f2a9478..d4c707d1 100644 --- a/auction-server/src/auction/service/cancel_bid.rs +++ b/auction-server/src/auction/service/cancel_bid.rs @@ -27,7 +27,6 @@ impl Service { let bid = self .repo .get_in_memory_auction_bid_by_bid_id(input.bid_id) - .await .ok_or(RestError::BadParameters( "Bid is only cancellable in awaiting_signature state".to_string(), ))?; diff --git a/auction-server/src/auction/service/conclude_auction.rs b/auction-server/src/auction/service/conclude_auction.rs index 9185fe62..d0cc516d 100644 --- a/auction-server/src/auction/service/conclude_auction.rs +++ b/auction-server/src/auction/service/conclude_auction.rs @@ -41,11 +41,7 @@ impl Service { .await; // Refetch the auction from the in-memory store to check if all bids are finalized - if let Some(auction) = self - .repo - .get_in_memory_auction_by_id(input.auction.id) - .await - { + if let Some(auction) = self.repo.get_in_memory_auction_by_id(input.auction.id) { if auction.bids.iter().all(|bid| bid.status.is_concluded()) { self.repo .conclude_auction(auction.id) @@ -122,7 +118,7 @@ impl Service { let mut interval = Self::get_conclusion_interval(); loop { interval.tick().await; - if let Some(auction) = self.repo.get_in_memory_auction_by_id(auction_id).await { + if let Some(auction) = self.repo.get_in_memory_auction_by_id(auction_id) { if let Err(e) = self .conclude_auction(ConcludeAuctionInput { auction }) .await diff --git a/auction-server/src/auction/service/get_auction_by_id.rs b/auction-server/src/auction/service/get_auction_by_id.rs index 7a1d5e2c..e012ba53 100644 --- a/auction-server/src/auction/service/get_auction_by_id.rs +++ b/auction-server/src/auction/service/get_auction_by_id.rs @@ -8,9 +8,7 @@ pub struct GetAuctionByIdInput { } impl Service { - pub async fn get_auction_by_id(&self, input: GetAuctionByIdInput) -> Option { - self.repo - .get_in_memory_auction_by_id(input.auction_id) - .await + pub fn get_auction_by_id(&self, input: GetAuctionByIdInput) -> Option { + self.repo.get_in_memory_auction_by_id(input.auction_id) } } diff --git a/auction-server/src/auction/service/submit_quote.rs b/auction-server/src/auction/service/submit_quote.rs index d485d456..e868ecbf 100644 --- a/auction-server/src/auction/service/submit_quote.rs +++ b/auction-server/src/auction/service/submit_quote.rs @@ -34,13 +34,12 @@ const MIN_DEADLINE_BUFFER_SECS: i64 = 2; impl Service { #[tracing::instrument(skip_all)] - async fn get_winner_bid_for_submission( + fn get_winner_bid_for_submission( &self, auction_id: entities::AuctionId, ) -> Result<(entities::Auction, entities::Bid), RestError> { let auction: entities::Auction = self .get_auction_by_id(GetAuctionByIdInput { auction_id }) - .await .ok_or(RestError::BadParameters("Quote not found. The provided reference ID may be invalid, already finalized on-chain, or canceled.".to_string()))?; let winner_bid = auction @@ -66,7 +65,7 @@ impl Service { let mut bid = bid; self.add_relayer_signature(&mut bid); let auction = self.get_auction_by_id(GetAuctionByIdInput {auction_id: auction.id, - }).await.ok_or_else(|| { + }).ok_or_else(|| { tracing::error!(auction_id = %auction.id, "Auction not found when getting most recent version"); RestError::TemporarilyUnavailable })?; @@ -95,7 +94,8 @@ impl Service { let _lock = lock.lock().await; // Make sure the bid is still not cancelled, we also get the latest saved version of the auction - let (auction, bid_latest_version) = self.get_winner_bid_for_submission(auction.id).await?; + // TODO: we probably don't need to get the auction and bid again, can probably use the one we have from the call to get_winner_bid_for_submission in submit_quote + let (auction, bid_latest_version) = self.get_winner_bid_for_submission(auction.id)?; if bid_latest_version.status.is_submitted() { return Ok(()); } @@ -200,7 +200,7 @@ impl Service { &self, input: SubmitQuoteInput, ) -> Result { - let (auction, winner_bid) = self.get_winner_bid_for_submission(input.auction_id).await?; + let (auction, winner_bid) = self.get_winner_bid_for_submission(input.auction_id)?; let mut bid = winner_bid.clone(); tracing::Span::current().record("bid_id", bid.id.to_string()); diff --git a/auction-server/src/auction/service/workers.rs b/auction-server/src/auction/service/workers.rs index 9daf194f..ddde3e46 100644 --- a/auction-server/src/auction/service/workers.rs +++ b/auction-server/src/auction/service/workers.rs @@ -141,7 +141,7 @@ impl Service { self.task_tracker.spawn({ let service = self.clone(); async move { - let in_memory_auctions = service.repo.get_in_memory_auctions().await; + let in_memory_auctions = service.repo.get_in_memory_auctions(); let auctions = in_memory_auctions.iter().filter(|auction| { auction.bids.iter().any(|bid| { bid.chain_data.transaction.signatures[0] == signature