Add caching to buffers

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent bf6f50ecdd
commit 09c4776321

@ -29,11 +29,15 @@ pub async fn read_file_by_hash(
api_state: ApiAccess<'_>, api_state: ApiAccess<'_>,
buffer_state: BufferAccess<'_>, buffer_state: BufferAccess<'_>,
) -> PluginResult<String> { ) -> PluginResult<String> {
if buffer_state.reserve_entry(&hash) {
Ok(format!("once://{}", hash)) // entry has been cached
} else {
let api = api_state.api().await?; let api = api_state.api().await?;
let content = api.file.read_file_by_hash(hash.clone()).await?; let content = api.file.read_file_by_hash(hash.clone()).await?;
let uri = add_once_buffer(buffer_state, hash, mime_type, content); let uri = add_once_buffer(buffer_state, hash, mime_type, content);
Ok(uri) Ok(uri)
}
} }
#[tauri::command] #[tauri::command]
@ -54,9 +58,13 @@ pub async fn read_thumbnail(
api_state: ApiAccess<'_>, api_state: ApiAccess<'_>,
buffer_state: BufferAccess<'_>, buffer_state: BufferAccess<'_>,
) -> PluginResult<String> { ) -> PluginResult<String> {
if buffer_state.reserve_entry(&hash) {
Ok(format!("once://{}", hash)) // entry has been cached
} else {
let api = api_state.api().await?; let api = api_state.api().await?;
let content = api.file.read_thumbnail(hash.clone()).await?; let content = api.file.read_thumbnail(hash.clone()).await?;
let uri = add_once_buffer(buffer_state, hash, mime_type, content); let uri = add_once_buffer(buffer_state, hash, mime_type, content);
Ok(uri) Ok(uri)
}
} }

@ -4,7 +4,7 @@ pub use file::*;
pub use repo::*; pub use repo::*;
pub use tag::*; pub use tag::*;
use crate::tauri_plugin::state::{ApiState, AppState, BufferState, OnceBuffer}; use crate::tauri_plugin::state::{ApiState, AppState, BufferState, VolatileBuffer};
pub mod file; pub mod file;
pub mod repo; pub mod repo;
@ -22,7 +22,7 @@ fn add_once_buffer(
buf: Vec<u8>, buf: Vec<u8>,
) -> String { ) -> String {
let uri = format!("once://{}", key); let uri = format!("once://{}", key);
let once_buffer = OnceBuffer::new(mime, buf); let once_buffer = VolatileBuffer::new(mime, buf);
let mut once_buffers = buffer_state.buffer.lock(); let mut once_buffers = buffer_state.buffer.lock();
once_buffers.insert(key, once_buffer); once_buffers.insert(key, once_buffer);

@ -6,10 +6,9 @@ pub fn register_custom_uri_schemes<R: Runtime>(builder: Builder<R>) -> Builder<R
builder.register_uri_scheme_protocol("once", |app, request| { builder.register_uri_scheme_protocol("once", |app, request| {
let buf_state = app.state::<BufferState>(); let buf_state = app.state::<BufferState>();
let resource_key = request.uri().trim_start_matches("once://"); let resource_key = request.uri().trim_start_matches("once://");
let buffer = {
let mut buffers = buf_state.buffer.lock(); let buffer = buf_state.get_entry(resource_key);
buffers.remove(resource_key) buf_state.clear_expired();
};
if let Some(buffer) = buffer { if let Some(buffer) = buffer {
ResponseBuilder::new() ResponseBuilder::new()
.mimetype(&buffer.mime) .mimetype(&buffer.mime)

@ -1,9 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::mem; use std::mem;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use parking_lot::Mutex; use parking_lot::Mutex;
use tauri::async_runtime::RwLock; use tauri::async_runtime::RwLock;
use tokio::time::Instant;
use crate::client_api::ApiClient; use crate::client_api::ApiClient;
use crate::tauri_plugin::error::{PluginError, PluginResult}; use crate::tauri_plugin::error::{PluginError, PluginResult};
@ -37,20 +39,75 @@ impl ApiState {
} }
} }
pub struct OnceBuffer { #[derive(Clone)]
pub struct VolatileBuffer {
pub accessed: bool,
pub valid_until: Instant,
pub mime: String, pub mime: String,
pub buf: Vec<u8>, pub buf: Vec<u8>,
} }
impl OnceBuffer { impl VolatileBuffer {
pub fn new(mime: String, buf: Vec<u8>) -> Self { pub fn new(mime: String, buf: Vec<u8>) -> Self {
Self { mime, buf } Self {
accessed: false,
valid_until: Instant::now() + Duration::from_secs(60),
mime,
buf,
}
} }
} }
#[derive(Default)] #[derive(Default)]
pub struct BufferState { pub struct BufferState {
pub buffer: Arc<Mutex<HashMap<String, OnceBuffer>>>, pub buffer: Arc<Mutex<HashMap<String, VolatileBuffer>>>,
}
impl BufferState {
/// Checks if an entry for the specific key exists and resets
/// its state so that it can safely be accessed again.
pub fn reserve_entry(&self, key: &String) -> bool {
let mut buffers = self.buffer.lock();
let entry = buffers.get_mut(key);
if let Some(entry) = entry {
entry.accessed = false; // reset that it has been accessed so it can be reused
true
} else {
false
}
}
/// Returns the cloned buffer entry and flags it for expiration
pub fn get_entry(&self, key: &str) -> Option<VolatileBuffer> {
let mut buffers = self.buffer.lock();
let entry = buffers.get_mut(key);
if let Some(entry) = entry {
entry.accessed = true;
entry.valid_until = Instant::now() + Duration::from_secs(10); // time to live is 10 seconds
Some(entry.clone())
} else {
None
}
}
/// Clears all expired entries
pub fn clear_expired(&self) {
let mut buffer = self.buffer.lock();
let keys: Vec<String> = buffer.keys().cloned().collect();
for key in keys {
let (accessed, valid_until) = {
let entry = buffer.get(&key).unwrap();
(entry.accessed, entry.valid_until.clone())
};
if accessed && valid_until < Instant::now() {
buffer.remove(&key);
}
}
}
} }
pub struct AppState { pub struct AppState {

Loading…
Cancel
Save