Limit the event response size
This commit is contained in:
parent
51a9436fd7
commit
3d71abcca1
3 changed files with 34 additions and 5 deletions
|
|
@ -7,6 +7,8 @@ use super::{
|
||||||
};
|
};
|
||||||
use anyhow::{Context as _, Result, ensure};
|
use anyhow::{Context as _, Result, ensure};
|
||||||
|
|
||||||
|
use crate::consts::MAX_EVENTS_PAGE_BYTES;
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
pub async fn get_missing_document(
|
pub async fn get_missing_document(
|
||||||
tx: &mut Transaction<'_>,
|
tx: &mut Transaction<'_>,
|
||||||
|
|
@ -172,7 +174,12 @@ impl Database {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn events_after(&self, vault: &VaultId, after: VaultUpdateId) -> Result<EventBatch> {
|
pub async fn events_after(
|
||||||
|
&self,
|
||||||
|
vault: &VaultId,
|
||||||
|
after: VaultUpdateId,
|
||||||
|
page_size: usize,
|
||||||
|
) -> Result<EventBatch> {
|
||||||
let mut tx = self.create_readonly_transaction(vault).await?;
|
let mut tx = self.create_readonly_transaction(vault).await?;
|
||||||
let head_event_id = Self::latest_event_id(&mut tx).await?;
|
let head_event_id = Self::latest_event_id(&mut tx).await?;
|
||||||
|
|
||||||
|
|
@ -182,23 +189,36 @@ impl Database {
|
||||||
);
|
);
|
||||||
|
|
||||||
let rows: Vec<(VaultUpdateId, String)> = sqlx::query_as(
|
let rows: Vec<(VaultUpdateId, String)> = sqlx::query_as(
|
||||||
"SELECT event_id, request_id FROM events WHERE event_id > ? ORDER BY event_id",
|
"SELECT event_id, request_id FROM events WHERE event_id > ? ORDER BY event_id LIMIT ?",
|
||||||
)
|
)
|
||||||
.bind(after)
|
.bind(after)
|
||||||
|
.bind(i64::try_from(page_size)?)
|
||||||
.fetch_all(&mut *tx)
|
.fetch_all(&mut *tx)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut events = Vec::with_capacity(rows.len());
|
let mut events = Vec::with_capacity(rows.len());
|
||||||
|
let mut bytes = 0;
|
||||||
|
let mut end_event_id = after;
|
||||||
for (event_id, request_id) in rows {
|
for (event_id, request_id) in rows {
|
||||||
events.push(EventRecord {
|
let event = EventRecord {
|
||||||
event_id,
|
event_id,
|
||||||
request_id: request_id.parse()?,
|
request_id: request_id.parse()?,
|
||||||
event: Self::event_by_id(&mut tx, event_id).await?,
|
event: Self::event_by_id(&mut tx, event_id).await?,
|
||||||
});
|
};
|
||||||
|
let size = serde_json::to_vec(&event)?.len();
|
||||||
|
// One manifest is atomic and may exceed this budget. Always allow
|
||||||
|
// one event so even such a page advances; never accumulate history.
|
||||||
|
if !events.is_empty() && bytes + size > MAX_EVENTS_PAGE_BYTES {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bytes += size;
|
||||||
|
end_event_id = event_id;
|
||||||
|
events.push(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EventBatch {
|
Ok(EventBatch {
|
||||||
head_event_id,
|
head_event_id,
|
||||||
|
end_event_id: Some(end_event_id),
|
||||||
events,
|
events,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,7 @@ pub const WEBSOCKET_SEND_TIMEOUT: Duration = Duration::from_secs(10);
|
||||||
|
|
||||||
pub const DEFAULT_MERGEABLE_FILE_EXTENSIONS: &[&str] = &["md", "txt"];
|
pub const DEFAULT_MERGEABLE_FILE_EXTENSIONS: &[&str] = &["md", "txt"];
|
||||||
|
|
||||||
|
pub const DEFAULT_EVENTS_PAGE_SIZE: usize = 64;
|
||||||
|
pub const MAX_EVENTS_PAGE_BYTES: usize = 1024 * 1024;
|
||||||
|
|
||||||
pub const SUPPORTED_API_VERSION: u32 = 4;
|
pub const SUPPORTED_API_VERSION: u32 = 4;
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,14 @@ use serde::Deserialize;
|
||||||
use super::VaultPath;
|
use super::VaultPath;
|
||||||
use crate::{
|
use crate::{
|
||||||
app_state::{AppState, database::models::EventBatch},
|
app_state::{AppState, database::models::EventBatch},
|
||||||
|
consts::DEFAULT_EVENTS_PAGE_SIZE,
|
||||||
errors::{SyncServerError, client_error, server_error},
|
errors::{SyncServerError, client_error, server_error},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct EventsQuery {
|
pub struct EventsQuery {
|
||||||
pub after: i64,
|
pub after: i64,
|
||||||
|
pub page_size: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
|
|
@ -33,7 +35,11 @@ pub async fn events(
|
||||||
|
|
||||||
state
|
state
|
||||||
.database
|
.database
|
||||||
.events_after(&vault_id, query.after)
|
.events_after(
|
||||||
|
&vault_id,
|
||||||
|
query.after,
|
||||||
|
query.page_size.unwrap_or(DEFAULT_EVENTS_PAGE_SIZE),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.map(Json)
|
.map(Json)
|
||||||
.map_err(server_error)
|
.map_err(server_error)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue