Skip to content

Commit

Permalink
Account metadata caching
Browse files Browse the repository at this point in the history
Signed-off-by: Tobias de Bruijn <[email protected]>
  • Loading branch information
TobiasDeBruijn committed Mar 13, 2024
1 parent 28f16f9 commit 7c61fdf
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 58 deletions.
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "chroma",
"version": "0.1.20",
"version": "0.1.21",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --port 8008",
Expand Down
2 changes: 1 addition & 1 deletion server/chroma/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "chroma"
version = "0.1.20"
version = "0.1.21"
edition = "2021"

[dependencies]
Expand Down
11 changes: 7 additions & 4 deletions server/chroma/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
extern crate core;

use crate::config::Config;
use crate::routes::appdata::{AppData, SessionIdCache, WebData};
use crate::routes::appdata::{AlbumIdCache, AppData, SessionIdCache, WebData};
use crate::routes::routable::Routable;
use actix_cors::Cors;
use actix_web::{App, HttpServer};
use actix_web::{web, App, HttpServer};
use cabbage::KoalaApi;
use color_eyre::eyre::Error;
use color_eyre::Result;
Expand Down Expand Up @@ -76,12 +76,15 @@ async fn main() -> Result<()> {
.wrap(Cors::permissive())
.wrap(TracingLogger::<NoiselessRootSpanBuilder>::new())
.app_data(WebData::new(appdata.clone()))
.app_data(
.app_data(web::Data::new(
SessionIdCache::builder()
.max_capacity(10000)
.time_to_live(Duration::from_secs(30))
.build(),
)
))
.app_data(web::Data::new(
AlbumIdCache::builder().max_capacity(10000).build(),
))
.configure(routes::Router::configure)
})
.bind(&format!(
Expand Down
3 changes: 2 additions & 1 deletion server/chroma/src/routes/appdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ use crate::config::Config;
use crate::routes::authorization::Authorization;
use actix_web::web;
use cabbage::KoalaApi;
use dal::database::Database;
use dal::database::{Album, Database};
use dal::storage_engine::StorageEngine;
use moka::future::Cache;

pub type WebData = web::Data<AppData>;
pub type SessionIdCache = Cache<String, Authorization>;
pub type AlbumIdCache = Cache<String, Album>;

#[derive(Debug, Clone)]
pub struct AppData {
Expand Down
2 changes: 1 addition & 1 deletion server/chroma/src/routes/v1/album/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub async fn delete(
.await?;
}

album.delete().await?;
album.delete(&data.db).await?;

Ok(Empty)
}
14 changes: 9 additions & 5 deletions server/chroma/src/routes/v1/album/get.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::routes::appdata::WebData;
use crate::routes::appdata::{AlbumIdCache, WebData};
use crate::routes::authorization::Authorization;
use crate::routes::error::{Error, WebResult};
use actix_multiresponse::Payload;
Expand Down Expand Up @@ -29,11 +29,15 @@ pub struct Query {
pub async fn get(
_: Authorization,
data: WebData,
album_id_cache: web::Data<AlbumIdCache>,
query: web::Query<Query>,
) -> WebResult<Payload<GetAlbumResponse>> {
let album = Album::get_by_id(&data.db, &query.id)
.await?
.ok_or(Error::NotFound)?;
let album = match album_id_cache.get(&query.id).await {
Some(v) => v,
None => Album::get_by_id(&data.db, &query.id)
.await?
.ok_or(Error::NotFound)?,
};

// If the user requests that photos are not returned, return an empty list.
let photos = match query.without_photos {
Expand Down Expand Up @@ -91,7 +95,7 @@ pub async fn get(
Ok(Payload(GetAlbumResponse {
photos,
album: Some(AlbumWithCoverPhoto {
album: Some(album.to_proto().await?),
album: Some(album.to_proto(&data.db).await?),
cover_photo,
}),
}))
Expand Down
53 changes: 49 additions & 4 deletions server/chroma/src/routes/v1/album/list.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::routes::appdata::WebData;
use crate::routes::appdata::{AlbumIdCache, WebData};
use crate::routes::authorization::Authorization;
use crate::routes::error::{Error, WebResult};
use crate::routes::v1::PhotoQuality;
Expand All @@ -7,7 +7,7 @@ use actix_web::web;
use dal::database::{Album, Photo};
use dal::storage_engine::EngineType;
use dal::DalError;
use futures::future::join_all;
use futures::future::{join_all, try_join_all};
use proto::{AlbumWithCoverPhoto, ListAlbumsResponse};
use serde::Deserialize;

Expand All @@ -26,11 +26,54 @@ pub struct Query {
/// - If something went wrong
pub async fn list(
auth: Authorization,
album_id_cache: web::Data<AlbumIdCache>,
data: WebData,
query: web::Query<Query>,
) -> WebResult<Payload<ListAlbumsResponse>> {
let mut albums = Album::list(&data.db).await?;
// Fetch only IDs, so we can grab the rest from cache
let ids = Album::list_ids(&data.db).await?;

// Fetch cached albums
let cached_albums = join_all(ids.into_iter().map(|f| {
let cache = &**album_id_cache;
async move {
let album = cache.get(&f).await;
(f, album)
}
}))
.await;

// Fetch albums from database of which there is nothing cached
let fetched_albums = try_join_all(
cached_albums
.iter()
.filter(|(_, cached)| cached.is_none())
.map(|(id, _)| Album::get_by_id(&data.db, id)),
)
.await?
.into_iter()
.filter_map(|f| f)
.collect::<Vec<_>>();

// Insert the newly fetched into the cache
join_all(fetched_albums.iter().map(|album| {
let cache = &**album_id_cache;
async move { cache.insert(album.id.clone(), album.clone()).await }
}))
.await;

// Merge the two sets
let mut albums = vec![
fetched_albums,
cached_albums
.into_iter()
.map(|(_, v)| v)
.filter_map(|v| v)
.collect::<Vec<_>>(),
]
.concat();

// Check if we should include draft albums
let include_draft = auth.is_admin
|| auth
.has_scope(&data.db, "nl.svsticky.chroma.album.list.draft")
Expand All @@ -40,13 +83,15 @@ pub async fn list(
albums.retain(|f| !f.is_draft);
}

// Transform them all to the proto response
let albums = join_all(albums.into_iter().map(|album| {
let storage = data.storage.clone();
let database = data.db.clone();
let qpref: dal::storage_engine::PhotoQuality = query.quality_preference.clone().into();
let include_cover_photo = query.include_cover_photo;

async move {
// Fetch the cover photo if it is requested
let cover_photo = if include_cover_photo {
if let Some(id) = &album.cover_photo_id {
let photo = Photo::get_by_id(&database, id).await?.unwrap();
Expand All @@ -71,7 +116,7 @@ pub async fn list(
};

Ok(AlbumWithCoverPhoto {
album: Some(album.to_proto().await?),
album: Some(album.to_proto(&database).await?),
cover_photo,
})
}
Expand Down
14 changes: 9 additions & 5 deletions server/chroma/src/routes/v1/album/update.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::routes::appdata::WebData;
use crate::routes::appdata::{AlbumIdCache, WebData};
use crate::routes::authorization::Authorization;
use crate::routes::empty::Empty;
use crate::routes::error::{Error, WebResult};
use actix_multiresponse::Payload;
use actix_web::web;
use dal::database::{Album, Photo};
use proto::UpdateAlbumRequest;

Expand All @@ -21,6 +22,7 @@ use proto::UpdateAlbumRequest;
pub async fn update(
auth: Authorization,
data: WebData,
album_id_cache: web::Data<AlbumIdCache>,
payload: Payload<UpdateAlbumRequest>,
) -> WebResult<Empty> {
if !auth.is_admin
Expand Down Expand Up @@ -48,7 +50,7 @@ pub async fn update(
)));
}

album.update_name(name).await?;
album.update_name(name, &data.db).await?;
}

if let Some(cover_photo_id) = &payload.cover_photo_id {
Expand All @@ -65,7 +67,7 @@ pub async fn update(
)));
}

album.update_cover_photo(&photo).await?;
album.update_cover_photo(&photo, &data.db).await?;
}

if let Some(draft_settings) = &payload.draft_settings {
Expand All @@ -76,16 +78,18 @@ pub async fn update(

match draft_settings {
proto::update_album_request::DraftSettings::SetDraft(v) if *v => {
album.set_draft().await?;
album.set_draft(&data.db).await?;
}
proto::update_album_request::DraftSettings::SetPublished(v) if *v => {
album
.set_published(auth.to_dal_user_type(&data.db).await?)
.set_published(auth.to_dal_user_type(&data.db).await?, &data.db)
.await?;
}
_ => {}
}
}

album_id_cache.insert(album.id.clone(), album).await;

Ok(Empty)
}
9 changes: 7 additions & 2 deletions server/chroma/src/routes/v1/photo/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub async fn create(
}

// TODO Update actix-multiresponse to support moving out the payload, avoids another clone
let photo_id = image_pipeline(&data, payload.photo_data.clone(), &album).await?;
let photo_id = image_pipeline(&data, payload.photo_data.clone(), &album, &data.db).await?;

Ok(Payload(CreatePhotoResponse { photo_id }))
}
Expand All @@ -57,7 +57,12 @@ pub async fn create(
///
/// If any step in the pipeline fails
#[instrument(skip(data, image))]
async fn image_pipeline(data: &WebData, image: Vec<u8>, album: &Album<'_>) -> WebResult<String> {
async fn image_pipeline(
data: &WebData,
image: Vec<u8>,
album: &Album,
db: &Database,
) -> WebResult<String> {
// This pipeline modifies the image. The idea is that each 'step' outputs
// a variable 'image', which the next step can then use.

Expand Down
Loading

0 comments on commit 7c61fdf

Please sign in to comment.