Skip to content

Commit ef1c7db

Browse files
committed
cache /v1/mods for up to 10 minutes
1 parent 298043f commit ef1c7db

File tree

8 files changed

+122
-25
lines changed

8 files changed

+122
-25
lines changed

Cargo.lock

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ log4rs = { version = "1.3.0", features = [
4040
"threshold_filter",
4141
] }
4242
thiserror = "2.0.12"
43+
moka = { version = "0.12.13", features = ["future"] }

src/config.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
use std::time::Duration;
2+
3+
use moka::future::Cache;
4+
5+
use crate::{
6+
endpoints::mods::IndexQueryParams,
7+
types::{
8+
api::{ApiResponse, PaginatedData},
9+
models::mod_entity::Mod,
10+
},
11+
};
12+
113
#[derive(Clone)]
214
pub struct AppData {
315
db: sqlx::postgres::PgPool,
@@ -9,6 +21,8 @@ pub struct AppData {
921
max_download_mb: u32,
1022
port: u16,
1123
debug: bool,
24+
25+
mods_cache: Cache<IndexQueryParams, ApiResponse<PaginatedData<Mod>>>,
1226
}
1327

1428
#[derive(Clone)]
@@ -37,6 +51,11 @@ pub async fn build_config() -> anyhow::Result<AppData> {
3751
.unwrap_or("250".to_string())
3852
.parse::<u32>()
3953
.unwrap_or(250);
54+
let mods_cache = Cache::builder()
55+
.max_capacity(128)
56+
.time_to_idle(Duration::from_mins(5))
57+
.time_to_live(Duration::from_mins(10))
58+
.build();
4059

4160
Ok(AppData {
4261
db: pool,
@@ -51,6 +70,7 @@ pub async fn build_config() -> anyhow::Result<AppData> {
5170
max_download_mb,
5271
port,
5372
debug,
73+
mods_cache,
5474
})
5575
}
5676

@@ -100,4 +120,8 @@ impl AppData {
100120
pub fn debug(&self) -> bool {
101121
self.debug
102122
}
123+
124+
pub fn mods_cache(&self) -> &Cache<IndexQueryParams, ApiResponse<PaginatedData<Mod>>> {
125+
&self.mods_cache
126+
}
103127
}

src/endpoints/mods.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use serde::Deserialize;
2525
use serde::Serialize;
2626
use sqlx::Acquire;
2727

28-
#[derive(Deserialize, Default)]
28+
#[derive(Deserialize, Default, Hash, Eq, PartialEq)]
2929
#[serde(rename_all = "snake_case")]
3030
pub enum IndexSortType {
3131
#[default]
@@ -37,7 +37,7 @@ pub enum IndexSortType {
3737
NameReverse,
3838
}
3939

40-
#[derive(Deserialize)]
40+
#[derive(Deserialize, Hash, Eq, PartialEq)]
4141
pub struct IndexQueryParams {
4242
pub page: Option<i64>,
4343
pub per_page: Option<i64>,
@@ -67,25 +67,32 @@ pub async fn index(
6767
query: web::Query<IndexQueryParams>,
6868
auth: Auth,
6969
) -> Result<impl Responder, ApiError> {
70-
let mut pool = data.db().acquire().await?;
70+
if let Some(s) = query.status
71+
&& s == ModVersionStatusEnum::Rejected
72+
{
73+
auth.check_admin()?;
74+
}
7175

72-
if let Some(s) = query.status {
73-
if s == ModVersionStatusEnum::Rejected {
74-
auth.check_admin()?;
75-
}
76+
if let Some(cached) = data.mods_cache().get(&query.0).await {
77+
return Ok(web::Json(cached));
7678
}
7779

78-
let mut result = Mod::get_index(&mut pool, query.0).await?;
80+
let mut pool = data.db().acquire().await?;
81+
82+
let mut result = Mod::get_index(&mut pool, &query.0).await?;
7983
for i in &mut result.data {
8084
for j in &mut i.versions {
8185
j.modify_metadata(data.app_url(), false);
8286
}
8387
}
84-
85-
Ok(web::Json(ApiResponse {
88+
let resp = ApiResponse {
8689
error: "".into(),
87-
payload: result,
88-
}))
90+
payload: result.clone(),
91+
};
92+
93+
data.mods_cache().insert(query.0, resp.clone()).await;
94+
95+
Ok(web::Json(resp))
8996
}
9097

9198
#[get("/v1/mods/{id}")]

src/types/api.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
use actix_web::{error::QueryPayloadError, HttpRequest};
1+
use actix_web::{HttpRequest, error::QueryPayloadError};
22
use serde::{Deserialize, Serialize};
33

44
use crate::endpoints::ApiError;
55

6-
#[derive(Serialize, Deserialize)]
6+
#[derive(Serialize, Deserialize, Clone)]
77
pub struct PaginatedData<T> {
88
pub data: Vec<T>,
99
pub count: i64,
1010
}
1111

12-
#[derive(Debug, Serialize, Deserialize)]
12+
#[derive(Debug, Serialize, Deserialize, Clone)]
1313
pub struct ApiResponse<T> {
1414
pub error: String,
1515
pub payload: T,

src/types/models/mod_entity.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use sqlx::{
2929
};
3030
use std::collections::HashMap;
3131

32-
#[derive(Serialize, Debug, sqlx::FromRow)]
32+
#[derive(Serialize, Debug, Clone, sqlx::FromRow)]
3333
pub struct Mod {
3434
pub id: String,
3535
pub repository: Option<String>,
@@ -141,10 +141,10 @@ impl Mod {
141141

142142
pub async fn get_index(
143143
pool: &mut PgConnection,
144-
query: IndexQueryParams,
144+
query: &IndexQueryParams,
145145
) -> Result<PaginatedData<Mod>, ApiError> {
146-
let tags = match query.tags {
147-
Some(t) => Some(Tag::parse_tags(&t, pool).await?),
146+
let tags = match &query.tags {
147+
Some(t) => Some(Tag::parse_tags(t, pool).await?),
148148
None => None,
149149
};
150150
let page: i64 = query.page.unwrap_or(1).max(1);
@@ -154,14 +154,15 @@ impl Mod {
154154
let offset = (page - 1) * per_page;
155155
let platforms = query
156156
.platforms
157-
.map(|p| VerPlatform::parse_query_string(&p))
157+
.as_ref()
158+
.map(|p| VerPlatform::parse_query_string(p))
158159
.transpose()?;
159160
let status = query.status.unwrap_or(ModVersionStatusEnum::Accepted);
160161
// We only want to filter anything if jitless is true
161162
let requires_patching = query.jitless.filter(|&j| j).map(|_| false);
162163

163-
let developer = match query.developer {
164-
Some(d) => match developers::get_one_by_username(&d, pool).await? {
164+
let developer = match &query.developer {
165+
Some(d) => match developers::get_one_by_username(d, pool).await? {
165166
Some(d) => Some(d),
166167
None => {
167168
return Ok(PaginatedData {
@@ -184,7 +185,8 @@ impl Mod {
184185

185186
let geode = query
186187
.geode
187-
.map(|x| Version::parse(&x))
188+
.as_ref()
189+
.map(|x| Version::parse(x))
188190
.transpose()
189191
.or(Err(ApiError::BadRequest("Invalid geode version".into())))?;
190192

src/types/models/mod_gd_version.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use sqlx::PgConnection;
55

66
use crate::{database::DatabaseError, types::mod_json::ModJson};
77

8-
#[derive(sqlx::Type, Debug, Deserialize, Serialize, Clone, Copy)]
8+
#[derive(sqlx::Type, Debug, Deserialize, Serialize, Clone, Copy, Hash, PartialEq, Eq)]
99
#[sqlx(type_name = "gd_version")]
1010
pub enum GDVersionEnum {
1111
#[serde(rename = "*")]

src/types/models/mod_version_status.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use serde::{Deserialize, Serialize};
22

3-
#[derive(sqlx::Type, Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
3+
#[derive(sqlx::Type, Debug, Deserialize, Serialize, Clone, Copy, Hash, PartialEq, Eq)]
44
#[serde(rename_all = "lowercase")]
55
#[sqlx(rename_all = "lowercase", type_name = "mod_version_status")]
66
pub enum ModVersionStatusEnum {

0 commit comments

Comments
 (0)