-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor exchange rate caching #370
Conversation
WalkthroughThe changes in this pull request involve updates to the Changes
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (1)
src/currency/fiat.rs (1)
11-17
: Consider usingonce_cell
instead oflazy_static
for lazy initializationWhile
lazy_static
provides lazy initialization, theonce_cell
crate offers a more modern and flexible approach withOnceCell
andLazy
. Since yourrust-version
is "1.61.0", you can use the externalonce_cell
crate, which provides similar functionality without macros and can improve readability.To refactor using
once_cell
, first add it to your dependencies inCargo.toml
:+ once_cell = "1.18.0"
Then, update your imports and replace the
lazy_static!
block:-use lazy_static::lazy_static; +use once_cell::sync::Lazy; -lazy_static! { - static ref EXCHANGE_RATE_PROVIDER: Mutex<ExchangeRateProvider<blockchain_info_consumer::ApiConsumer>> = - Mutex::new(ExchangeRateProvider { - data_source: blockchain_info_consumer::ApiConsumer, - data: None, - }); -} +static EXCHANGE_RATE_PROVIDER: Lazy<Mutex<ExchangeRateProvider<blockchain_info_consumer::ApiConsumer>>> = Lazy::new(|| { + Mutex::new(ExchangeRateProvider { + data_source: blockchain_info_consumer::ApiConsumer, + data: None, + }) +});This approach eliminates the need for a macro and aligns with modern Rust practices.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
Cargo.lock
is excluded by!**/*.lock
📒 Files selected for processing (2)
Cargo.toml
(1 hunks)src/currency/fiat.rs
(3 hunks)
🔇 Additional comments (2)
src/currency/fiat.rs (1)
1-2
: Appropriate imports added for thread-safe synchronization
The imports of Mutex
and lazy_static
are necessary for implementing thread-safe access to shared resources and lazy initialization.
Cargo.toml (1)
30-30
:
Verify the version of lazy_static
dependency
The specified version 1.5.0
for lazy_static
may not exist. As of October 2023, the latest version available on crates.io is 1.4.0
. Using a non-existent version will cause dependency resolution errors.
Please verify and update the version accordingly. You can check the latest version with the following command:
Alternatively, consider replacing lazy_static
with once_cell
as suggested earlier.
src/currency/fiat.rs
Outdated
EXCHANGE_RATE_PROVIDER | ||
.lock() | ||
.expect("Failed to lock EXCHANGE_RATE_PROVIDER") | ||
.btc_value(self) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Handle potential mutex poisoning to prevent unexpected panics
Using .expect("Failed to lock EXCHANGE_RATE_PROVIDER")
will cause the program to panic if the mutex is poisoned (e.g., if another thread panicked while holding the lock). In a production environment, it's better to handle this case gracefully.
Consider handling the PoisonError
by recovering or propagating an appropriate error:
- EXCHANGE_RATE_PROVIDER
- .lock()
- .expect("Failed to lock EXCHANGE_RATE_PROVIDER")
- .btc_value(self)
+ match EXCHANGE_RATE_PROVIDER.lock() {
+ Ok(provider) => provider.btc_value(self),
+ Err(poisoned) => {
+ // Handle the poisoned mutex, possibly by recovering the data
+ // or logging the error and returning a default value
+ eprintln!("Mutex poisoned: {}", poisoned);
+ // Example: Continue using the poisoned mutex
+ poisoned.into_inner().btc_value(self)
+ }
+ }
Alternatively, you might consider using RwLock
if read operations are more frequent than writes, which can improve concurrency performance.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
EXCHANGE_RATE_PROVIDER | |
.lock() | |
.expect("Failed to lock EXCHANGE_RATE_PROVIDER") | |
.btc_value(self) | |
match EXCHANGE_RATE_PROVIDER.lock() { | |
Ok(provider) => provider.btc_value(self), | |
Err(poisoned) => { | |
// Handle the poisoned mutex, possibly by recovering the data | |
// or logging the error and returning a default value | |
eprintln!("Mutex poisoned: {}", poisoned); | |
// Example: Continue using the poisoned mutex | |
poisoned.into_inner().btc_value(self) | |
} | |
} |
1894f2c
to
7a46877
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (2)
src/currency/fiat.rs (2)
11-17
: Add documentation for thread-safety guaranteesWhile the implementation using
lazy_static!
andMutex
is correct, it would be beneficial to document the thread-safety guarantees and usage patterns.Consider adding documentation like this:
// Static to have an easy way of caching the exchange rates. +// Thread-safe singleton implementation using Mutex to ensure +// safe concurrent access to exchange rates across multiple threads. +// The lazy_static ensures one-time initialization when first accessed. lazy_static! { static ref EXCHANGE_RATE_PROVIDER: Mutex<ExchangeRateProvider<blockchain_info_consumer::ApiConsumer>> =
Line range hint
87-102
: Add concurrent access testsThe current test only verifies caching behavior but not thread-safety. Given this refactoring focuses on thread-safe access, consider adding concurrent tests.
Example test to add:
#[test] fn test_concurrent_access() { use std::thread; let handles: Vec<_> = (0..10) .map(|_| { thread::spawn(|| { // Access from multiple threads let btc_value = Fiat::USD.btc_value(); assert!(btc_value > 0.0); }) }) .collect(); // Ensure all threads complete successfully handles.into_iter().for_each(|h| h.join().unwrap()); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
⛔ Files ignored due to path filters (1)
Cargo.lock
is excluded by!**/*.lock
📒 Files selected for processing (2)
Cargo.toml
(1 hunks)src/currency/fiat.rs
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- Cargo.toml
🔇 Additional comments (2)
src/currency/fiat.rs (2)
2-2
: LGTM! Appropriate imports added for thread-safe implementation
The new imports for lazy_static
and Mutex
are correctly added and follow Rust's import conventions.
Also applies to: 4-4
54-57
:
Improve error handling for mutex lock operations
The current implementation using expect()
could cause panics in production. This issue was previously raised and remains valid.
As mentioned in the previous review, consider handling potential mutex poisoning:
- EXCHANGE_RATE_PROVIDER
- .lock()
- .expect("Failed to lock EXCHANGE_RATE_PROVIDER")
- .btc_value(self)
+ match EXCHANGE_RATE_PROVIDER.lock() {
+ Ok(provider) => provider.btc_value(self),
+ Err(poisoned) => {
+ // Log the error and recover the inner value
+ log::error!("Exchange rate provider mutex poisoned: {}", poisoned);
+ poisoned.into_inner().btc_value(self)
+ }
+ }
Additionally, consider adding error propagation to allow callers to handle failures:
fn btc_value(&self) -> Result<f64, Box<dyn std::error::Error>> {
let provider = EXCHANGE_RATE_PROVIDER.lock()
.map_err(|e| format!("Failed to acquire exchange rate lock: {}", e))?;
Ok(provider.btc_value(self))
}
Summary by CodeRabbit
New Features
Bug Fixes
These updates ensure a more reliable and secure experience when accessing exchange rates.