From b242d39f53a5c1eac89b209ede086137ac3794d6 Mon Sep 17 00:00:00 2001 From: Najman Husaini Date: Tue, 14 Jan 2025 15:48:59 -0700 Subject: [PATCH] layout changes and batching --- Cargo.lock | 6 +- Cargo.toml | 2 +- usr-backend-runner/Cargo.toml | 9 - usr-backend-runner/src/main.rs | 26 --- usr-backend/src/manifest.rs | 110 ++++++++--- .../src/routes/(apps)/manifest/+page.svelte | 164 ++++++++-------- .../src/routes/(apps)/scheduler/+page.svelte | 184 +++++++----------- 7 files changed, 240 insertions(+), 261 deletions(-) delete mode 100644 usr-backend-runner/Cargo.toml delete mode 100644 usr-backend-runner/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index cc9dc2d..fc991f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1891,7 +1891,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3260,10 +3260,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "usr-backend-runner" -version = "0.1.0" - [[package]] name = "utf16_iter" version = "1.0.5" diff --git a/Cargo.toml b/Cargo.toml index 14958c8..6987e1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["usr-backend", "usr-backend-runner"] \ No newline at end of file +members = ["usr-backend"] \ No newline at end of file diff --git a/usr-backend-runner/Cargo.toml b/usr-backend-runner/Cargo.toml deleted file mode 100644 index d8aeeff..0000000 --- a/usr-backend-runner/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "usr-backend-runner" -version = "0.1.0" -edition = "2021" - -[dependencies] - -[profile.release] -lto = true \ No newline at end of file diff --git a/usr-backend-runner/src/main.rs b/usr-backend-runner/src/main.rs deleted file mode 100644 index 7e22003..0000000 --- a/usr-backend-runner/src/main.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::process::Stdio; - -fn main() { - loop { - let result = std::process::Command::new("cargo") - .args([ - "run", - "-p", - "usr-backend", - #[cfg(not(debug_assertions))] - "--release" - ]) - .stderr(Stdio::piped()) - .stdout(Stdio::null()) - .output(); - match result { - Ok(output) => { - let _ = std::fs::write("runner.log", format!("{}\n{}", String::from_utf8(output.stderr).unwrap(), output.status)); - } - Err(e) => { - let _ = std::fs::write("runner.log", e.to_string()); - } - } - std::thread::sleep(std::time::Duration::from_secs(5)); - } -} diff --git a/usr-backend/src/manifest.rs b/usr-backend/src/manifest.rs index 4a2b78a..ac43b96 100644 --- a/usr-backend/src/manifest.rs +++ b/usr-backend/src/manifest.rs @@ -1,11 +1,11 @@ -use std:: - sync::Arc +use std::{collections::{hash_map::Entry, HashMap}, sync::{Arc, LazyLock}, time::Instant} ; use axum::{ extract::State, http::StatusCode, response::{IntoResponse, Response}, routing::{delete, get, post}, Json, Router }; use discord_webhook2::message::Message; +use parking_lot::Mutex; use sea_orm::{ prelude::Decimal, sea_query::Table, sqlx::types::chrono::Local, ActiveModelTrait, ActiveValue, ConnectionTrait, DatabaseConnection, EntityTrait, Schema }; @@ -15,6 +15,14 @@ use tracing::error; use crate::{scheduler, UsrState}; mod order; +struct BatchedTask { + queue: HashMap, + deadline: Option, +} +static BATCHED: LazyLock> = LazyLock::new(|| Mutex::new(BatchedTask { + queue: HashMap::new(), + deadline: None, +})); #[derive(Deserialize)] pub struct PendingOrder { @@ -57,20 +65,67 @@ async fn new_order( vendor: ActiveValue::Set(pending_order.vendor), link: ActiveValue::Set(pending_order.link), }; - if let Err(e) = active_model.insert(&state.db).await { - error!("Failed to create new order: {e}"); - (StatusCode::INTERNAL_SERVER_ERROR, "") - } else { - tokio::spawn(async move { - if let Err(e) = state - .new_orders_webhook - .send(&Message::new(|message| message.content(webhook_msg))) - .await - { - error!("Failed to trigger new-order webhook: {e}"); + match active_model.insert(&state.db).await { + Ok(m) => { + let mut guard = BATCHED.lock(); + guard.queue.insert(m.id, webhook_msg); + let was_none = guard.deadline.is_none(); + guard.deadline = Some(Instant::now() + std::time::Duration::from_secs(60 * 5)); + + if was_none { + drop(guard); + + tokio::spawn(async move { + loop { + let deadline = BATCHED.lock().deadline.unwrap(); + tokio::time::sleep_until(deadline.into()).await; + let queue; + { + let mut guard = BATCHED.lock(); + if guard.deadline.unwrap() != deadline { + continue; + } + let replacement = HashMap::with_capacity(guard.queue.capacity()); + queue = std::mem::replace(&mut guard.queue, replacement); + } + let mut running = String::new(); + for (_, msg) in queue { + if running.len() + msg.len() + 1 < 2000 { + running.push_str(&msg); + running.push_str("\n"); + } else { + if let Err(e) = state + .new_orders_webhook + .send(&Message::new(|message| message.content(running))) + .await + { + error!("Failed to trigger new-order webhook: {e}"); + } + running = msg; + } + } + if let Err(e) = state + .new_orders_webhook + .send(&Message::new(|message| message.content(running))) + .await + { + error!("Failed to trigger new-order webhook: {e}"); + } + let mut guard = BATCHED.lock(); + if guard.queue.is_empty() { + guard.deadline = None; + break; + } + } + }); } - }); - (StatusCode::OK, "") + + (StatusCode::OK, "") + } + Err(e) => { + error!("Failed to create new order: {e}"); + (StatusCode::INTERNAL_SERVER_ERROR, "") + } } } @@ -134,15 +189,24 @@ async fn change_order( error!("Failed to change order: {e}"); (StatusCode::INTERNAL_SERVER_ERROR, "") } else { - tokio::spawn(async move { - if let Err(e) = state - .new_orders_webhook - .send(&Message::new(|message| message.content(webhook_msg))) - .await - { - error!("Failed to trigger new-order webhook: {e}"); + let mut guard = BATCHED.lock(); + match guard.queue.entry(change_order.id) { + Entry::Occupied(mut entry) => { + entry.insert(webhook_msg); } - }); + Entry::Vacant(_) => { + tokio::spawn(async move { + if let Err(e) = state + .new_orders_webhook + .send(&Message::new(|message| message.content(webhook_msg))) + .await + { + error!("Failed to trigger new-order webhook: {e}"); + } + }); + } + } + (StatusCode::OK, "") } } diff --git a/usr-web/src/routes/(apps)/manifest/+page.svelte b/usr-web/src/routes/(apps)/manifest/+page.svelte index 724679c..6150a51 100644 --- a/usr-web/src/routes/(apps)/manifest/+page.svelte +++ b/usr-web/src/routes/(apps)/manifest/+page.svelte @@ -97,7 +97,11 @@ } -
+ + USR Manifest + + +
-
+
-
-
+
{#if tabIndex === 0} @@ -428,108 +413,86 @@ Admin
- {:else if tabIndex === 2} - - - {advancedFilterParseError} {/if}
- - - - - {#each DAYS as day} - - {/each} - - - - {#each { length: 32 } as _, y} +
+ {#if tabIndex !== 0} +
+ {#if selectedCellPeople !== null} + {#each selectedCellPeople as name} +
{name}
+ {/each} + {:else} + {#each maxPresent as name} +
{name}
+ {/each} + {/if} +
+ {/if} + +
{day}
+ - - {#each DAYS as _, x} - {#if tabIndex === 0} - + {#each DAYS as day} + + {/each} + + + + {#each { length: 32 } as _, y} + + + {#each DAYS as _, x} + {#if tabIndex === 0} + - {:else} - {#snippet scheduleCell(people: Set)} - - {/snippet} - {@render scheduleCell(filterFn(x, y))} - {/if} - {/each} - - {/each} - -
{timeString(y)} { - if (name === '') { - return; - } - event.currentTarget.releasePointerCapture(event.pointerId); - deleting = isPositionInsideAvailabilities(x, y, name); - updateDrag = true; - dragStartX = x; - dragStartY = y; - dragEndX = x; - dragEndY = y; - }} - onpointerenter={(event) => { - if (updateDrag) { - event.stopPropagation(); + {day}
{timeString(y)} { + if (name === '') { + return; + } + event.currentTarget.releasePointerCapture(event.pointerId); + deleting = isPositionInsideAvailabilities(x, y, name); + updateDrag = true; + dragStartX = x; + dragStartY = y; dragEndX = x; dragEndY = y; - } - }} - > - { - selectedCellPeople = people; - selectedCell = [x, y]; }} - id={selectedCell !== null && selectedCell[0] === x && selectedCell[1] === y ? 'selected-cell' : ''} + onpointerenter={(event) => { + if (updateDrag) { + event.stopPropagation(); + dragEndX = x; + dragEndY = y; + } + }} >
- -{#if tabIndex !== 0} -
- {#if selectedCellPeople !== null} - {#each selectedCellPeople as name} -
{name}
- {/each} - {:else} - {#each maxPresent as name} -
{name}
+ {:else} + {#snippet scheduleCell(people: Set)} + { + selectedCellPeople = people; + selectedCell = [x, y]; + }} + id={selectedCell !== null && selectedCell[0] === x && selectedCell[1] === y ? 'selected-cell' : ''} + > + + {/snippet} + {@render scheduleCell(filterFn(x, y))} + {/if} + {/each} + {/each} - {/if} -
-{/if} + + +