-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: improve console wallet responsiveness (#3304)
Description --- 1. Improved console wallet responsiveness by introducing a [**edit** _debouncer with a_] cooldown period for calculating the balance, as this is a database call that grows linearly with the amount of UTXOs in the wallet and does not need to be repeated multiple times per second in certain scenarios. [**Edit** _All events that can change the balance and are captured in the wallet event monitor are followed by the `trigger_balance_refresh` command that will send an event to the debouncer, which in turn will collect all such events and only perform one balance update database query after the end of the cooldown period._] ~~A new base node state change (metadata) event will ensure the last request to calculate balance will be fulfilled.~~ 2. Submit connectivity failed event if the metadata RPC requests time out so that 'Waiting for data...' will be displayed in the wallet. 3. Removed the RPC client ping call as a method when monitoring the base node to calculate latency and instead replaced it with the time measured to obtain metadata via the RPC client. Motivation and Context --- 1. Wallets with growing numbers of UTXOs become more and more sluggish due to repeated database calls to calculate the balance; this is much more pronounced when many broadcast monitoring protocols are running. 2. When the metadata RPC requests time out previous base node state would be displayed; this could carry on for 30 minutes or more with consecutive timeouts where the correct user feedback would be that the wallet is waiting for data. 3. The time measured for the ping call always follows the time it takes for the metadata call, which is evident as well when the base node gets busy and will often take longer than the metadata call. Having two longish RPC calls with no added value results in more RPC request timeouts than is necessary. See time measurement plots below: ![image](https://user-images.githubusercontent.com/39146854/132186041-5afd5e15-8a3b-4635-82f4-800b2e6b750a.png) ![image](https://user-images.githubusercontent.com/39146854/132186070-c9913a23-98e8-4461-8b86-bcd0ebbfb4f0.png) ![image](https://user-images.githubusercontent.com/39146854/132186089-fe4eff9b-bb22-4de2-b9fa-93f5accf3a8f.png) ![image](https://user-images.githubusercontent.com/39146854/132186110-12eec3af-e363-4802-a2e7-c9ce44062892.png) ![image](https://user-images.githubusercontent.com/39146854/132186520-f533078e-907e-4407-96f2-d5f64e4c55c4.png) How Has This Been Tested? --- System-level testing during a stressed coin split
- Loading branch information
1 parent
e126033
commit 73017a4
Showing
10 changed files
with
236 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 139 additions & 0 deletions
139
applications/tari_console_wallet/src/ui/state/debouncer.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// Copyright 2020. The Tari Project | ||
// | ||
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the | ||
// following conditions are met: | ||
// | ||
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following | ||
// disclaimer. | ||
// | ||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the | ||
// following disclaimer in the documentation and/or other materials provided with the distribution. | ||
// | ||
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote | ||
// products derived from this software without specific prior written permission. | ||
// | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | ||
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
use crate::ui::state::AppStateInner; | ||
use log::*; | ||
use std::{ | ||
sync::Arc, | ||
time::{Duration, Instant}, | ||
}; | ||
use tari_wallet::output_manager_service::handle::OutputManagerHandle; | ||
use tokio::{ | ||
sync::{broadcast, RwLock}, | ||
time, | ||
}; | ||
|
||
const LOG_TARGET: &str = "wallet::console_wallet::debouncer"; | ||
|
||
#[derive(Clone)] | ||
pub(crate) struct BalanceEnquiryDebouncer { | ||
app_state_inner: Arc<RwLock<AppStateInner>>, | ||
output_manager_service: OutputManagerHandle, | ||
balance_enquiry_cooldown_period: Duration, | ||
tx: broadcast::Sender<()>, | ||
} | ||
|
||
impl BalanceEnquiryDebouncer { | ||
pub fn new( | ||
app_state_inner: Arc<RwLock<AppStateInner>>, | ||
balance_enquiry_cooldown_period: Duration, | ||
output_manager_service: OutputManagerHandle, | ||
) -> Self { | ||
// This channel must only be size 1; the debouncer will ensure that the balance is updated timeously | ||
let (tx, _) = broadcast::channel(1); | ||
Self { | ||
app_state_inner, | ||
output_manager_service, | ||
balance_enquiry_cooldown_period, | ||
tx, | ||
} | ||
} | ||
|
||
pub async fn run(mut self) { | ||
let balance_enquiry_events = &mut self.tx.subscribe(); | ||
let mut shutdown_signal = self.app_state_inner.read().await.get_shutdown_signal(); | ||
let delay = time::sleep(self.balance_enquiry_cooldown_period); | ||
tokio::pin!(delay); | ||
|
||
debug!(target: LOG_TARGET, "Balance enquiry debouncer starting"); | ||
if let Ok(balance) = self.output_manager_service.get_balance().await { | ||
trace!( | ||
target: LOG_TARGET, | ||
"Initial balance: available {}, incoming {}, outgoing {}", | ||
balance.available_balance, | ||
balance.pending_incoming_balance, | ||
balance.pending_outgoing_balance | ||
); | ||
let mut inner = self.app_state_inner.write().await; | ||
if let Err(e) = inner.refresh_balance(balance).await { | ||
warn!(target: LOG_TARGET, "Error refresh app_state: {}", e); | ||
} | ||
} | ||
loop { | ||
tokio::select! { | ||
_ = &mut delay => { | ||
if let Ok(result) = time::timeout( | ||
self.balance_enquiry_cooldown_period, | ||
balance_enquiry_events.recv() | ||
).await { | ||
match result { | ||
Ok(_) => { | ||
let start_time = Instant::now(); | ||
match self.output_manager_service.get_balance().await { | ||
Ok(balance) => { | ||
trace!( | ||
target: LOG_TARGET, | ||
"Updating balance ({} ms): available {}, incoming {}, outgoing {}", | ||
start_time.elapsed().as_millis(), | ||
balance.available_balance, | ||
balance.pending_incoming_balance, | ||
balance.pending_outgoing_balance | ||
); | ||
let mut inner = self.app_state_inner.write().await; | ||
if let Err(e) = inner.refresh_balance(balance).await { | ||
warn!(target: LOG_TARGET, "Error refresh app_state: {}", e); | ||
} | ||
} | ||
Err(e) => { | ||
warn!(target: LOG_TARGET, "Could not obtain balance ({})", e); | ||
} | ||
} | ||
} | ||
Err(broadcast::error::RecvError::Lagged(n)) => { | ||
trace!(target: LOG_TARGET, "Balance enquiry debouncer lagged {} update requests", n); | ||
continue; | ||
} | ||
Err(broadcast::error::RecvError::Closed) => { | ||
info!( | ||
target: LOG_TARGET, | ||
"Balance enquiry debouncer shutting down because the channel was closed" | ||
); | ||
break; | ||
} | ||
} | ||
} | ||
}, | ||
_ = shutdown_signal.wait() => { | ||
info!( | ||
target: LOG_TARGET, | ||
"Balance enquiry debouncer shutting down because the shutdown signal was received" | ||
); | ||
break; | ||
}, | ||
} | ||
} | ||
} | ||
|
||
pub fn get_sender(self) -> broadcast::Sender<()> { | ||
self.tx | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.