fix(app-proxy): remove the SPA document render reservation (#2447)

This commit is contained in:
Hampus
2026-09-04 15:55:59 +02:00
committed by GitHub
parent bc7f701e87
commit 7bd0d3a962
2 changed files with 3 additions and 50 deletions
+3 -41
View File
@@ -10,7 +10,7 @@ use crate::invite_meta::{
}; };
use crate::state::{ use crate::state::{
AppProxyBudgets, AppState, MAX_RENDERED_SPA_INDEX_BYTES, MAX_SPA_INDEX_BYTES, AppProxyBudgets, AppState, MAX_RENDERED_SPA_INDEX_BYTES, MAX_SPA_INDEX_BYTES,
SPA_DOCUMENT_RENDER_RESERVATION_BYTES, read_bounded_text_file, read_bounded_text_file,
}; };
use crate::time_freeze::{ use crate::time_freeze::{
load_time_freeze_config_for_request, should_serve_frozen, time_freeze_debug_header, load_time_freeze_config_for_request, should_serve_frozen, time_freeze_debug_header,
@@ -23,7 +23,6 @@ use axum::{
}; };
use std::path::Path; use std::path::Path;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::sync::{OwnedSemaphorePermit, TryAcquireError};
use super::assets_proxy::serve_local_asset; use super::assets_proxy::serve_local_asset;
use super::file_stream::stream_file; use super::file_stream::stream_file;
@@ -182,19 +181,6 @@ async fn serve_spa_index(state: &AppState, headers: &HeaderMap, request_path: &s
} }
}; };
let mut document_budget = match state
.budgets
.spa_document_memory
.clone()
.try_acquire_many_owned(SPA_DOCUMENT_RENDER_RESERVATION_BYTES)
{
Ok(permit) => permit,
Err(TryAcquireError::NoPermits) => return super::capacity_refused_response(),
Err(TryAcquireError::Closed) => {
panic!("SPA document memory budget semaphore closed unexpectedly")
}
};
let dev_buster = should_bust_dev_assets.then(current_dev_asset_cache_buster); let dev_buster = should_bust_dev_assets.then(current_dev_asset_cache_buster);
let html = match render_spa_document( let html = match render_spa_document(
&raw_html, &raw_html,
@@ -212,26 +198,7 @@ async fn serve_spa_index(state: &AppState, headers: &HeaderMap, request_path: &s
} }
}; };
let html = html.into_boxed_str(); let html = html.into_boxed_str();
let retained_bytes = u32::try_from(html.len()).expect("bounded SPA document size must fit u32"); build_spa_response(html, csp, debug_header.as_deref(), should_bust_dev_assets)
let released_bytes = SPA_DOCUMENT_RENDER_RESERVATION_BYTES
.checked_sub(retained_bytes)
.expect("rendered SPA document must fit its memory reservation");
if released_bytes > 0 {
let released_permits =
usize::try_from(released_bytes).expect("SPA document permit count must fit usize");
drop(
document_budget
.split(released_permits)
.expect("SPA document memory reservation must contain its unused permits"),
);
}
build_spa_response(
html,
csp,
debug_header.as_deref(),
should_bust_dev_assets,
document_budget,
)
} }
#[derive(Debug)] #[derive(Debug)]
@@ -438,7 +405,6 @@ async fn load_spa_index_html(state: &AppState) -> Result<String, Response> {
struct SpaDocumentBody { struct SpaDocumentBody {
html: Box<str>, html: Box<str>,
_budget: OwnedSemaphorePermit,
} }
impl AsRef<[u8]> for SpaDocumentBody { impl AsRef<[u8]> for SpaDocumentBody {
@@ -452,12 +418,8 @@ fn build_spa_response(
csp: HeaderValue, csp: HeaderValue,
time_freeze_header: Option<&str>, time_freeze_header: Option<&str>,
dev_no_store: bool, dev_no_store: bool,
document_budget: OwnedSemaphorePermit,
) -> Response { ) -> Response {
let body = Bytes::from_owner(SpaDocumentBody { let body = Bytes::from_owner(SpaDocumentBody { html });
html,
_budget: document_budget,
});
let mut response = Response::new(Body::from(body)); let mut response = Response::new(Body::from(body));
let headers = response.headers_mut(); let headers = response.headers_mut();
-9
View File
@@ -13,19 +13,11 @@ use tokio::sync::Semaphore;
pub const MAX_SPA_INDEX_BYTES: usize = 4 * 1024 * 1024; pub const MAX_SPA_INDEX_BYTES: usize = 4 * 1024 * 1024;
pub const MAX_RENDERED_SPA_INDEX_BYTES: usize = 8 * 1024 * 1024; pub const MAX_RENDERED_SPA_INDEX_BYTES: usize = 8 * 1024 * 1024;
pub const MAX_STATIC_TEXT_FILE_BYTES: usize = 4 * 1024 * 1024; pub const MAX_STATIC_TEXT_FILE_BYTES: usize = 4 * 1024 * 1024;
pub const SPA_DOCUMENT_MEMORY_BUDGET_BYTES: usize = 160 * 1024 * 1024;
pub const SPA_DOCUMENT_RENDER_RESERVATION_BYTES: u32 = 40 * 1024 * 1024;
pub const UPSTREAM_ASSET_RESPONSES_IN_FLIGHT_MAX: usize = 32; pub const UPSTREAM_ASSET_RESPONSES_IN_FLIGHT_MAX: usize = 32;
pub const LOCAL_FILE_READS_IN_FLIGHT_MAX: usize = 256; pub const LOCAL_FILE_READS_IN_FLIGHT_MAX: usize = 256;
const _: () = assert!(
MAX_RENDERED_SPA_INDEX_BYTES <= SPA_DOCUMENT_RENDER_RESERVATION_BYTES as usize,
"a rendered SPA document must fit inside the memory reserved to render it"
);
#[derive(Clone)] #[derive(Clone)]
pub struct AppProxyBudgets { pub struct AppProxyBudgets {
pub spa_document_memory: Arc<Semaphore>,
pub upstream_asset_slots: Arc<Semaphore>, pub upstream_asset_slots: Arc<Semaphore>,
pub local_read_slots: Arc<Semaphore>, pub local_read_slots: Arc<Semaphore>,
} }
@@ -33,7 +25,6 @@ pub struct AppProxyBudgets {
impl AppProxyBudgets { impl AppProxyBudgets {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
spa_document_memory: Arc::new(Semaphore::new(SPA_DOCUMENT_MEMORY_BUDGET_BYTES)),
upstream_asset_slots: Arc::new(Semaphore::new(UPSTREAM_ASSET_RESPONSES_IN_FLIGHT_MAX)), upstream_asset_slots: Arc::new(Semaphore::new(UPSTREAM_ASSET_RESPONSES_IN_FLIGHT_MAX)),
local_read_slots: Arc::new(Semaphore::new(LOCAL_FILE_READS_IN_FLIGHT_MAX)), local_read_slots: Arc::new(Semaphore::new(LOCAL_FILE_READS_IN_FLIGHT_MAX)),
} }