mirror of
https://github.com/terhechte/postsack
synced 2024-11-12 01:10:48 +00:00
Some performance and code improvements
This commit is contained in:
parent
6086f38a31
commit
9b4a601c8e
@ -39,6 +39,8 @@ impl Grouping {
|
|||||||
// - fix "Action".
|
// - fix "Action".
|
||||||
// - find a way to merge action, query and response in a type-safe manner...
|
// - find a way to merge action, query and response in a type-safe manner...
|
||||||
// - rename cluster_engine to model?
|
// - rename cluster_engine to model?
|
||||||
|
// - move the different operations in modules and just share the state
|
||||||
|
// - replace row_cache with the LRU crate I have open
|
||||||
|
|
||||||
/// This signifies the action we're currently evaluating
|
/// This signifies the action we're currently evaluating
|
||||||
/// It is used for sending requests and receiving responses
|
/// It is used for sending requests and receiving responses
|
||||||
@ -213,18 +215,14 @@ impl Engine {
|
|||||||
|
|
||||||
// Send the last action over the wire to be calculated
|
// Send the last action over the wire to be calculated
|
||||||
fn update(&mut self, payload: (Query, Action)) -> Result<()> {
|
fn update(&mut self, payload: (Query, Action)) -> Result<()> {
|
||||||
Ok(self.link.input_sender.send((payload.0, payload.1))?)
|
Ok(self.link.request(&payload.0, payload.1)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch the channels to see if there're any updates
|
/// Fetch the channels to see if there're any updates
|
||||||
pub fn process(&mut self) -> Result<()> {
|
pub fn process(&mut self) -> Result<()> {
|
||||||
let response = match self.link.output_receiver.try_recv() {
|
let response = match self.link.receive()? {
|
||||||
// We received something
|
Some(n) => n,
|
||||||
Ok(Ok(response)) => response,
|
None => return Ok(()),
|
||||||
// We received nothing
|
|
||||||
Err(_) => return Ok(()),
|
|
||||||
// There was an error, we forward it
|
|
||||||
Ok(Err(e)) => return Err(e),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
@ -269,43 +267,36 @@ impl Engine {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_contents(&mut self, range: &Range<usize>) -> Result<()> {
|
/// Query the contents for the current filter settings.
|
||||||
// Mark the rows as being loaded
|
/// This call will return the available data and request additional data when it is missing.
|
||||||
for index in range.clone() {
|
/// The return value indicates whether a row is loaded or loading.
|
||||||
if self.row_cache.cache_get(&index).is_none() {
|
pub fn current_contents(&mut self, range: &Range<usize>) -> Result<Vec<Option<QueryRow>>> {
|
||||||
self.row_cache.cache_set(index, LoadingState::Loading);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let request = self.make_normal_query(range.clone());
|
|
||||||
self.link
|
|
||||||
.input_sender
|
|
||||||
.send((request.clone(), Action::Mails))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Query the contents for the current filter settings
|
|
||||||
/// This is a blocking call to simplify things a great deal
|
|
||||||
/// - returns the data, and an indicator that data is missing so that we can load more data
|
|
||||||
pub fn current_contents(
|
|
||||||
&mut self,
|
|
||||||
range: &Range<usize>,
|
|
||||||
) -> Result<(Vec<Option<QueryRow>>, bool)> {
|
|
||||||
// build an array with either empty values or values from our cache.
|
// build an array with either empty values or values from our cache.
|
||||||
let mut rows = Vec::new();
|
let mut rows = Vec::new();
|
||||||
let mut data_missing = false;
|
|
||||||
|
let mut missing_data = false;
|
||||||
for index in range.clone() {
|
for index in range.clone() {
|
||||||
let entry = self.row_cache.cache_get(&index);
|
let entry = self.row_cache.cache_get(&index);
|
||||||
let entry = match entry {
|
let entry = match entry {
|
||||||
Some(LoadingState::Loaded(n)) => Some((*n).clone()),
|
Some(LoadingState::Loaded(n)) => Some((*n).clone()),
|
||||||
Some(LoadingState::Loading) => None,
|
Some(LoadingState::Loading) => None,
|
||||||
None => {
|
None => {
|
||||||
data_missing = true;
|
// for simplicity, we keep the "something is missing" state separate
|
||||||
|
missing_data = true;
|
||||||
|
|
||||||
|
// Mark the row as being loaded
|
||||||
|
self.row_cache.cache_set(index, LoadingState::Loading);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
rows.push(entry);
|
rows.push(entry);
|
||||||
}
|
}
|
||||||
Ok((rows, data_missing))
|
// Only if at least some data is missing do we perform the request
|
||||||
|
if missing_data && !range.is_empty() {
|
||||||
|
let request = self.make_normal_query(range.clone());
|
||||||
|
self.link.request(&request, Action::Mails)?;
|
||||||
|
}
|
||||||
|
Ok(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_busy(&self) -> bool {
|
pub fn is_busy(&self) -> bool {
|
||||||
@ -320,7 +311,7 @@ impl Engine {
|
|||||||
|
|
||||||
/// If we're loading mails
|
/// If we're loading mails
|
||||||
pub fn is_mail_busy(&self) -> bool {
|
pub fn is_mail_busy(&self) -> bool {
|
||||||
!self.link.input_sender.is_empty()
|
self.link.is_processing()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_group_query(&self) -> Result<Query> {
|
fn make_group_query(&self) -> Result<Query> {
|
||||||
|
@ -25,10 +25,6 @@ use super::partitions::{Partition, Partitions};
|
|||||||
// - instead of hard-coding subject/sender-domain, have a "Detail" trait
|
// - instead of hard-coding subject/sender-domain, have a "Detail" trait
|
||||||
// - consider a better logic for the cache (by row id and just fetch the smallest range that contains all missing numbers)
|
// - consider a better logic for the cache (by row id and just fetch the smallest range that contains all missing numbers)
|
||||||
|
|
||||||
pub trait Payload<O> {
|
|
||||||
fn map_response<T>(self, response: T) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Response<Context: Send + 'static> {
|
pub enum Response<Context: Send + 'static> {
|
||||||
Grouped(Query, Context, Partitions),
|
Grouped(Query, Context, Partitions),
|
||||||
@ -43,6 +39,41 @@ pub struct Link<Context: Send + 'static> {
|
|||||||
pub input_sender: InputSender<Context>,
|
pub input_sender: InputSender<Context>,
|
||||||
pub output_receiver: OutputReciever<Context>,
|
pub output_receiver: OutputReciever<Context>,
|
||||||
pub handle: Handle,
|
pub handle: Handle,
|
||||||
|
// We need to account for the brief moment where the processing channel is empty
|
||||||
|
// but we're applying the results. If there is a UI update in this window,
|
||||||
|
// the UI will not update again after the changes were applied because an empty
|
||||||
|
// channel indicates completed processing.
|
||||||
|
// There's also a delay between a request taken out of the input channel and being
|
||||||
|
// put into the output channel. In order to account for all of this, we emploty a
|
||||||
|
// request counter to know how many requests are currently in the pipeline
|
||||||
|
request_counter: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Context: Send + Sync + 'static> Link<Context> {
|
||||||
|
pub fn request(&mut self, query: &Query, context: Context) -> Result<()> {
|
||||||
|
self.request_counter += 1;
|
||||||
|
self.input_sender.send((query.clone(), context))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receive(&mut self) -> Result<Option<Response<Context>>> {
|
||||||
|
match self.output_receiver.try_recv() {
|
||||||
|
// We received something
|
||||||
|
Ok(Ok(response)) => {
|
||||||
|
// Only subtract if we successfuly received a value
|
||||||
|
self.request_counter -= 1;
|
||||||
|
Ok(Some(response))
|
||||||
|
}
|
||||||
|
// We received nothing
|
||||||
|
Err(_) => Ok(None),
|
||||||
|
// There was an error, we forward it
|
||||||
|
Ok(Err(e)) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_processing(&self) -> bool {
|
||||||
|
self.request_counter > 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<Context: Send + Sync + 'static>(config: &Config) -> Result<Link<Context>> {
|
pub fn run<Context: Send + Sync + 'static>(config: &Config) -> Result<Link<Context>> {
|
||||||
@ -54,6 +85,7 @@ pub fn run<Context: Send + Sync + 'static>(config: &Config) -> Result<Link<Conte
|
|||||||
input_sender,
|
input_sender,
|
||||||
output_receiver,
|
output_receiver,
|
||||||
handle,
|
handle,
|
||||||
|
request_counter: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +100,7 @@ fn inner_loop<Context: Send + Sync + 'static>(
|
|||||||
let response = match query {
|
let response = match query {
|
||||||
Query::Grouped { .. } => {
|
Query::Grouped { .. } => {
|
||||||
let partitions = calculate_partitions(&result)?;
|
let partitions = calculate_partitions(&result)?;
|
||||||
Response::Grouped(query, context, Partitions::new(partitions))
|
Response::Grouped(query, context, partitions)
|
||||||
}
|
}
|
||||||
Query::Normal { .. } => {
|
Query::Normal { .. } => {
|
||||||
let converted = calculate_rows(&result)?;
|
let converted = calculate_rows(&result)?;
|
||||||
@ -79,14 +111,14 @@ fn inner_loop<Context: Send + Sync + 'static>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_partitions(result: &[QueryResult]) -> Result<Vec<Partition>> {
|
fn calculate_partitions(result: &[QueryResult]) -> Result<Partitions> {
|
||||||
let mut partitions = Vec::new();
|
let mut partitions = Vec::new();
|
||||||
for r in result.iter() {
|
for r in result.iter() {
|
||||||
let partition = r.try_into()?;
|
let partition = r.try_into()?;
|
||||||
partitions.push(partition);
|
partitions.push(partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(partitions)
|
Ok(Partitions::new(partitions))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_rows(result: &[QueryResult]) -> Result<Vec<QueryRow>> {
|
fn calculate_rows(result: &[QueryResult]) -> Result<Vec<QueryRow>> {
|
||||||
|
@ -26,16 +26,18 @@ impl<'a> Widget for MailPanel<'a> {
|
|||||||
&mut selected_row,
|
&mut selected_row,
|
||||||
self.engine.current_element_count(),
|
self.engine.current_element_count(),
|
||||||
|range| {
|
|range| {
|
||||||
let (rows, load_more) = match self.engine.current_contents(&range) {
|
// we overshoot the range a bit, as otherwise somehow the bottom is always empty
|
||||||
Ok((n, load_more)) => (n, load_more),
|
let range = std::ops::Range {
|
||||||
|
start: range.start,
|
||||||
|
end: range.end + 6,
|
||||||
|
};
|
||||||
|
let rows = match self.engine.current_contents(&range) {
|
||||||
|
Ok(n) => n,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
*self.error = Some(e);
|
*self.error = Some(e);
|
||||||
(empty_vec.clone(), false)
|
empty_vec.clone()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if load_more {
|
|
||||||
*self.error = self.engine.request_contents(&range).err();
|
|
||||||
}
|
|
||||||
rows
|
rows
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user