in-memory cache featured item list

This commit is contained in:
Brandon Thomas committed 2024-07-12 01:29:58 -04:00
1 parent a4023278bc
commit 8889299cc7
6 files changed
+90 -72

No files matched your search

@@ -7,11 +7,11 @@ use sieve_cache::SieveCache;
use errors::AnyhowResult;
#[derive(Clone)]
struct Sieve<K: Eq + Hash + Clone, V: Clone + ?Sized> {
pub struct ArcSieve<K: Eq + Hash + Clone, V: Clone + ?Sized> {
cache: Arc<Mutex<SieveCache<K, V>>>,
}
impl <K: Eq + Hash + Clone, V: Clone + ?Sized> Sieve<K, V> {
impl <K: Eq + Hash + Clone, V: Clone + ?Sized> ArcSieve<K, V> {
pub fn with_capacity(capacity: usize) -> AnyhowResult<Self> {
let cache = SieveCache::new(capacity)
@@ -54,11 +54,11 @@ impl <K: Eq + Hash + Clone, V: Clone + ?Sized> Sieve<K, V> {
#[cfg(test)]
mod tests {
use crate::sieve::Sieve;
use crate::arc_sieve::ArcSieve;
#[test]
fn test_get_copy() {
let sieve : Sieve<String, String> = Sieve::with_capacity(1).unwrap();
let sieve : ArcSieve<String, String> = ArcSieve::with_capacity(1).unwrap();
let key = "key".to_string();
let value = "value".to_string();
+1 -1
View File
@@ -14,7 +14,7 @@
#![allow(dead_code)]
#![allow(non_snake_case)]
pub mod arc_sieve;
pub mod multi_item_ttl_cache;
pub mod single_item_ttl_cache;
pub mod ttl_key_counter;
mod sieve;
@@ -20,6 +20,8 @@ use tokens::tokens::users::UserToken;
use crate::payloads::prompt_args::prompt_inner_payload::PromptInnerPayload;
// NB: Clone is for caches.
#[derive(Clone)]
pub struct FeaturedMediaFileListPage {
pub records: Vec<FeaturedMediaFileListItem>,
@@ -32,6 +34,7 @@ pub struct FeaturedMediaFileListPage {
pub last_id: Option<i64>,
}
#[derive(Clone)]
pub struct FeaturedMediaFileListItem {
pub token: MediaFileToken,
@@ -20,21 +20,21 @@ use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::view_as::ViewAs;
use enums::no_table::style_transfer::style_transfer_name::StyleTransferName;
use mysql_queries::queries::media_files::get::batch_get_media_files_by_tokens::batch_get_media_files_by_tokens;
use mysql_queries::queries::media_files::list::list_featured_media_files::{list_featured_media_files, ListFeaturedMediaFilesArgs};
use mysql_queries::queries::media_files::list::list_featured_media_files::{FeaturedMediaFileListPage, list_featured_media_files, ListFeaturedMediaFilesArgs};
use tokens::tokens::media_files::MediaFileToken;
use crate::http_server::common_responses::user_details_lite::UserDetailsLight;
use crate::http_server::common_responses::media_file_cover_image_details::{MediaFileCoverImageDetails, MediaFileDefaultCover};
use crate::http_server::common_responses::media_file_origin_details::MediaFileOriginDetails;
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats;
use crate::http_server::common_responses::user_details_lite::UserDetailsLight;
use crate::http_server::endpoints::media_files::helpers::get_scoped_engine_categories::get_scoped_engine_categories;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_classes::get_scoped_media_classes;
use crate::http_server::endpoints::media_files::helpers::get_scoped_media_types::get_scoped_media_types;
use crate::state::server_state::ServerState;
use crate::util::allowed_explore_media_access::allowed_explore_media_access;
#[derive(Deserialize, ToSchema, IntoParams)]
#[derive(Deserialize, ToSchema, IntoParams, Hash, Clone, PartialEq, Eq)]
pub struct ListFeaturedMediaFilesQueryParams {
pub sort_ascending: Option<bool>,
pub page_size: Option<usize>,
@@ -181,72 +181,19 @@ pub async fn list_featured_media_files_handler(
http_request: HttpRequest,
query: Query<ListFeaturedMediaFilesQueryParams>,
server_state: web::Data<Arc<ServerState>>
) -> Result<HttpResponse, impl ResponseError> {
) -> Result<HttpResponse, ListFeaturedMediaFilesError> {
let maybe_user_session = server_state
.session_checker
.maybe_get_user_session(&http_request, &server_state.mysql_pool)
.await
.map_err(|e| {
warn!("Session checker error: {:?}", e);
ListFeaturedMediaFilesError::ServerError
})?;
let maybe_cached_results = server_state
.caches
.ephemeral
.featured_media_files_sieve
.get_copy(&query)
.ok()
.flatten();
let mut is_mod = false;
match maybe_user_session {
None => {},
Some(session) => {
is_mod = session.can_ban_users;
},
};
// TODO(bt,2023-12-04): Enforce real maximums and defaults
let limit = query.page_size.unwrap_or(25);
let sort_ascending = query.sort_ascending.unwrap_or(false);
let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false);
let cursor = if let Some(cursor) = query.cursor.as_deref() {
let cursor = server_state.sort_key_crypto.decrypt_id(cursor)
.map_err(|e| {
warn!("crypto error: {:?}", e);
ListFeaturedMediaFilesError::ServerError
})?;
Some(cursor as usize)
} else {
None
};
let view_as = if is_mod {
ViewAs::Moderator
} else {
ViewAs::AnotherUser
};
let mut maybe_filter_media_types = get_scoped_media_types(query.filter_media_type.as_deref());
let mut maybe_filter_media_classes = get_scoped_media_classes(query.filter_media_classes.as_deref());
let mut maybe_filter_engine_categories = get_scoped_engine_categories(query.filter_engine_categories.as_deref());
let query_results =
list_featured_media_files(ListFeaturedMediaFilesArgs {
limit,
maybe_offset: cursor,
cursor_is_reversed,
sort_ascending,
view_as,
maybe_filter_media_types: maybe_filter_media_types.as_ref(),
maybe_filter_media_classes: maybe_filter_media_classes.as_ref(),
maybe_filter_engine_categories: maybe_filter_engine_categories.as_ref(),
mysql_pool: &server_state.mysql_pool,
}).await;
let results_page = match query_results {
Ok(results) => results,
Err(e) => {
warn!("Query error: {:?}", e);
return Err(ListFeaturedMediaFilesError::ServerError);
}
let results_page = match maybe_cached_results {
Some(cached_results) => cached_results,
None => database_lookup(&query, &server_state).await?,
};
let cursor_next = if let Some(id) = results_page.last_id {
@@ -271,6 +218,12 @@ pub async fn list_featured_media_files_handler(
None
};
// TODO(bt,2023-12-04): Enforce real maximums and defaults
let limit = query.page_size.unwrap_or(25);
let sort_ascending = query.sort_ascending.unwrap_or(false);
let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false);
let results = results_page.records.into_iter()
.map(|m| {
let public_bucket_path = MediaFileBucketPath::from_object_hash(
@@ -342,3 +295,58 @@ pub async fn list_featured_media_files_handler(
.content_type("application/json")
.body(body))
}
async fn database_lookup(
query: &ListFeaturedMediaFilesQueryParams,
server_state: &ServerState,
) -> Result<FeaturedMediaFileListPage, ListFeaturedMediaFilesError> {
// TODO(bt,2023-12-04): Enforce real maximums and defaults
let limit = query.page_size.unwrap_or(25);
let sort_ascending = query.sort_ascending.unwrap_or(false);
let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false);
let cursor = if let Some(cursor) = query.cursor.as_deref() {
let cursor = server_state.sort_key_crypto.decrypt_id(cursor)
.map_err(|e| {
warn!("crypto error: {:?}", e);
ListFeaturedMediaFilesError::ServerError
})?;
Some(cursor as usize)
} else {
None
};
let mut maybe_filter_media_types = get_scoped_media_types(query.filter_media_type.as_deref());
let mut maybe_filter_media_classes = get_scoped_media_classes(query.filter_media_classes.as_deref());
let mut maybe_filter_engine_categories = get_scoped_engine_categories(query.filter_engine_categories.as_deref());
// NB: No reason to show deleted or non-public featured items to mods or authors.
// That's just confusing and wastes an extra query.
const VIEW_AS: ViewAs = ViewAs::AnotherUser;
let results = list_featured_media_files(ListFeaturedMediaFilesArgs {
limit,
maybe_offset: cursor,
cursor_is_reversed,
sort_ascending,
view_as: VIEW_AS,
maybe_filter_media_types: maybe_filter_media_types.as_ref(),
maybe_filter_media_classes: maybe_filter_media_classes.as_ref(),
maybe_filter_engine_categories: maybe_filter_engine_categories.as_ref(),
mysql_pool: &server_state.mysql_pool,
}).await.map_err(|err| {
error!("DB error: {:?}", err);
ListFeaturedMediaFilesError::ServerError
})?;
// NB: Fail open.
let _result = server_state
.caches
.ephemeral
.featured_media_files_sieve
.store_copy(&query, &results);
Ok(results)
}
@@ -58,6 +58,7 @@ use config::shared_constants::DEFAULT_MYSQL_CONNECTION_STRING;
use config::shared_constants::DEFAULT_RUST_LOG;
use email_sender::smtp_email_sender::SmtpEmailSender;
use errors::AnyhowResult;
use memory_caching::arc_sieve::ArcSieve;
use memory_caching::single_item_ttl_cache::SingleItemTtlCache;
use mysql_queries::mediators::badge_granter::BadgeGranter;
use mysql_queries::mediators::firehose_publisher::FirehosePublisher;
@@ -428,6 +429,7 @@ async fn main() -> AnyhowResult<()> {
easyenv::get_env_duration_seconds_or_default(
"QUEUE_STATS_CACHE_TTL_SECONDS",
Duration::from_secs(60))),
featured_media_files_sieve: ArcSieve::with_capacity(100)?,
}
},
ip_ban_list,
@@ -7,10 +7,12 @@ use actix_helpers::middleware::banned_ip_filter::ip_ban_list::ip_ban_list::IpBan
use billing_component::stripe::stripe_config::StripeConfig;
use cloud_storage::bucket_client::BucketClient;
use email_sender::smtp_email_sender::SmtpEmailSender;
use memory_caching::arc_sieve::ArcSieve;
use memory_caching::single_item_ttl_cache::SingleItemTtlCache;
use mysql_queries::mediators::badge_granter::BadgeGranter;
use mysql_queries::mediators::firehose_publisher::FirehosePublisher;
use mysql_queries::queries::generic_inference::web::get_pending_inference_job_count::InferenceQueueLengthResult;
use mysql_queries::queries::media_files::list::list_featured_media_files::FeaturedMediaFileListPage;
use mysql_queries::queries::model_categories::list_categories_query_builder::CategoryList;
use mysql_queries::queries::tts::tts_inference_jobs::get_pending_tts_inference_job_count::TtsQueueLengthResult;
use mysql_queries::queries::w2l::w2l_templates::list_w2l_templates::W2lTemplateRecordForList;
@@ -23,6 +25,7 @@ use crate::configs::static_api_tokens::StaticApiTokenSet;
use crate::http_server::cookies::anonymous_visitor_tracking::avt_cookie_manager::AvtCookieManager;
use crate::http_server::endpoints::categories::tts::list_fully_computed_assigned_tts_categories::list_fully_computed_assigned_tts_categories::ModelTokensByCategoryToken;
use crate::http_server::endpoints::leaderboard::get_leaderboard::LeaderboardInfo;
use crate::http_server::endpoints::media_files::list::list_featured_media_files_handler::ListFeaturedMediaFilesQueryParams;
use crate::http_server::endpoints::stats::result_transformer::CacheableQueueStats;
use crate::http_server::endpoints::tts::list_tts_models::TtsModelRecordForResponse;
use crate::http_server::endpoints::voice_conversion::models::list_voice_conversion_models::VoiceConversionModel;
@@ -191,6 +194,8 @@ pub struct EphemeralInMemoryCaches {
pub tts_queue_length: SingleItemTtlCache<TtsQueueLengthResult>,
pub leaderboard: SingleItemTtlCache<LeaderboardInfo>,
pub featured_media_files_sieve: ArcSieve<ListFeaturedMediaFilesQueryParams, FeaturedMediaFileListPage>,
}
#[derive(Clone)]