Add cache for image info list
Preparing the images list involves reading a directory (which might be slow on non-local directories) and opening every file to read the exif data. To avoid having to do this on every request, we do it every 60s in a background job and only read from the cache on requests.
This commit is contained in:
parent
6206d0ea58
commit
9af4831efa
52
src/main.rs
52
src/main.rs
@ -24,11 +24,12 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||
// * Polish the css
|
||||
// * Add configuration file
|
||||
// * Support for headlines
|
||||
// * Cache generated images (+ cache cleanup)
|
||||
// * Image cache cleanup
|
||||
|
||||
type TemplateEngine = Engine<minijinja::Environment<'static>>;
|
||||
type ImageDir = PathBuf;
|
||||
type SecretKey = [u8; 64];
|
||||
type ImageListCache = Arc<RwLock<Vec<ImageInfo>>>;
|
||||
type ImageCache = Arc<RwLock<HashMap<OsString, (DateTime<Utc>, Vec<u8>)>>>;
|
||||
|
||||
#[derive(Clone, extract::FromRef)]
|
||||
@ -36,6 +37,7 @@ struct ApplicationState {
|
||||
engine: TemplateEngine,
|
||||
image_cache: ImageCache,
|
||||
image_dir: ImageDir,
|
||||
image_list_cache: ImageListCache,
|
||||
secret_key: SecretKey,
|
||||
}
|
||||
|
||||
@ -56,6 +58,15 @@ async fn main() {
|
||||
.with(tracing_formatter)
|
||||
.init();
|
||||
|
||||
let image_dir = PathBuf::from(image_path);
|
||||
let image_cache = ImageCache::default();
|
||||
let image_list_cache = ImageListCache::default();
|
||||
|
||||
tokio::spawn(update_image_list_cache_job(
|
||||
image_dir.clone(),
|
||||
image_list_cache.clone()
|
||||
));
|
||||
|
||||
let mut jinja = minijinja::Environment::new();
|
||||
jinja.add_template("base", include_str!("../templates/base.html")).unwrap();
|
||||
jinja.add_template("index", include_str!("../templates/index.html")).unwrap();
|
||||
@ -76,8 +87,9 @@ async fn main() {
|
||||
.layer(session_layer)
|
||||
.with_state(ApplicationState {
|
||||
engine: Engine::from(jinja),
|
||||
image_cache: Default::default(),
|
||||
image_dir: PathBuf::from(image_path),
|
||||
image_cache,
|
||||
image_dir,
|
||||
image_list_cache,
|
||||
secret_key,
|
||||
});
|
||||
|
||||
@ -89,7 +101,33 @@ async fn main() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Default)]
|
||||
async fn update_image_list_cache_job(image_dir: ImageDir, image_cache: ImageListCache) {
|
||||
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(60));
|
||||
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
let image_dir = image_dir.clone();
|
||||
let images = task::spawn_blocking(move || {
|
||||
// TODO: Only update images with changed modification times
|
||||
read_images(&image_dir)
|
||||
}).await
|
||||
.unwrap_or_else(|error| {
|
||||
tracing::error!("Could not read images due to panic: {:#}", error);
|
||||
Ok(Vec::new())
|
||||
})
|
||||
.unwrap_or_else(|error| {
|
||||
tracing::error!("Could not read images: {:#}", error);
|
||||
Vec::new()
|
||||
});
|
||||
|
||||
tracing::debug!("{} images in the image list cache", images.len());
|
||||
*image_cache.write().unwrap() = images;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Default, Clone)]
|
||||
pub struct ImageInfo {
|
||||
width: u32,
|
||||
height: u32,
|
||||
@ -108,19 +146,19 @@ pub struct IndexTempalte {
|
||||
|
||||
async fn index(
|
||||
engine: TemplateEngine,
|
||||
State(image_dir): State<ImageDir>,
|
||||
State(image_list_cache): State<ImageListCache>,
|
||||
State(secret_key): State<SecretKey>,
|
||||
session: ReadableSession,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
let logged_in = session.get::<()>("logged_in").is_some();
|
||||
|
||||
if logged_in {
|
||||
let images = read_images(&image_dir)?;
|
||||
let images = image_list_cache.read().unwrap();
|
||||
|
||||
// Encrypt image names
|
||||
let chacha_key = chacha20poly1305::Key::from_slice(&secret_key[0..32]);
|
||||
let chacha = XChaCha20Poly1305::new(chacha_key);
|
||||
let mut images = images.into_iter().map(|mut image_info| {
|
||||
let mut images = images.iter().cloned().map(|mut image_info| {
|
||||
let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng);
|
||||
let mut ciphertext = chacha.encrypt(&nonce, image_info.name.as_bytes())?;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user