|
1 | 1 | use actix_web::http::header::{ContentType, HeaderValue, SERVER}; |
2 | 2 | use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer}; |
| 3 | +use rusqlite::Connection; |
3 | 4 | use rustls::ServerConfig; |
4 | 5 | use serde::{Deserialize, Serialize}; |
5 | 6 | use std::collections::HashMap; |
6 | 7 | use std::io; |
7 | | -use std::sync::Arc; |
| 8 | +use std::sync::{Arc, Mutex}; |
8 | 9 |
|
9 | 10 | static SERVER_HDR: HeaderValue = HeaderValue::from_static("actix"); |
10 | 11 |
|
@@ -62,6 +63,8 @@ struct AppState { |
62 | 63 | static_files: HashMap<String, StaticFile>, |
63 | 64 | } |
64 | 65 |
|
| 66 | +struct WorkerDb(Mutex<Connection>); |
| 67 | + |
65 | 68 | fn load_dataset() -> Vec<DatasetItem> { |
66 | 69 | let path = std::env::var("DATASET_PATH").unwrap_or_else(|_| "/data/dataset.json".to_string()); |
67 | 70 | match std::fs::read_to_string(&path) { |
@@ -157,41 +160,11 @@ async fn baseline11_get(req: HttpRequest) -> HttpResponse { |
157 | 160 | .body(sum.to_string()) |
158 | 161 | } |
159 | 162 |
|
160 | | -fn crc32_compute(data: &[u8]) -> u32 { |
161 | | - static TABLE: std::sync::OnceLock<[[u32; 256]; 8]> = std::sync::OnceLock::new(); |
162 | | - let t = TABLE.get_or_init(|| { |
163 | | - let mut t = [[0u32; 256]; 8]; |
164 | | - for i in 0..256u32 { |
165 | | - let mut c = i; |
166 | | - for _ in 0..8 { c = if c & 1 != 0 { 0xEDB88320 ^ (c >> 1) } else { c >> 1 }; } |
167 | | - t[0][i as usize] = c; |
168 | | - } |
169 | | - for i in 0..256 { |
170 | | - for s in 1..8 { t[s][i] = (t[s-1][i] >> 8) ^ t[0][(t[s-1][i] & 0xFF) as usize]; } |
171 | | - } |
172 | | - t |
173 | | - }); |
174 | | - let mut crc = 0xFFFFFFFFu32; |
175 | | - let mut i = 0; |
176 | | - while i + 8 <= data.len() { |
177 | | - let a = u32::from_le_bytes([data[i], data[i+1], data[i+2], data[i+3]]) ^ crc; |
178 | | - let b = u32::from_le_bytes([data[i+4], data[i+5], data[i+6], data[i+7]]); |
179 | | - crc = t[7][(a & 0xFF) as usize] ^ t[6][((a >> 8) & 0xFF) as usize] |
180 | | - ^ t[5][((a >> 16) & 0xFF) as usize] ^ t[4][(a >> 24) as usize] |
181 | | - ^ t[3][(b & 0xFF) as usize] ^ t[2][((b >> 8) & 0xFF) as usize] |
182 | | - ^ t[1][((b >> 16) & 0xFF) as usize] ^ t[0][(b >> 24) as usize]; |
183 | | - i += 8; |
184 | | - } |
185 | | - while i < data.len() { crc = (crc >> 8) ^ t[0][((crc ^ data[i] as u32) & 0xFF) as usize]; i += 1; } |
186 | | - crc ^ 0xFFFFFFFF |
187 | | -} |
188 | | - |
189 | 163 | async fn upload(body: web::Bytes) -> HttpResponse { |
190 | | - let crc = crc32_compute(&body); |
191 | 164 | HttpResponse::Ok() |
192 | 165 | .insert_header((SERVER, SERVER_HDR.clone())) |
193 | 166 | .content_type(ContentType::plaintext()) |
194 | | - .body(format!("{:08x}", crc)) |
| 167 | + .body(body.len().to_string()) |
195 | 168 | } |
196 | 169 |
|
197 | 170 | async fn baseline11_post(req: HttpRequest, body: web::Bytes) -> HttpResponse { |
@@ -253,6 +226,43 @@ async fn compression(state: web::Data<Arc<AppState>>) -> HttpResponse { |
253 | 226 | .body(state.json_large_cache.clone()) |
254 | 227 | } |
255 | 228 |
|
| 229 | +async fn db_endpoint(req: HttpRequest, db: web::Data<WorkerDb>) -> HttpResponse { |
| 230 | + let min: f64 = req.uri().query().and_then(|q| { |
| 231 | + q.split('&').find_map(|p| p.strip_prefix("min=").and_then(|v| v.parse().ok())) |
| 232 | + }).unwrap_or(10.0); |
| 233 | + let max: f64 = req.uri().query().and_then(|q| { |
| 234 | + q.split('&').find_map(|p| p.strip_prefix("max=").and_then(|v| v.parse().ok())) |
| 235 | + }).unwrap_or(50.0); |
| 236 | + let conn = db.0.lock().unwrap(); |
| 237 | + let mut stmt = conn.prepare_cached( |
| 238 | + "SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN ?1 AND ?2 LIMIT 50" |
| 239 | + ).unwrap(); |
| 240 | + let rows = stmt.query_map(rusqlite::params![min, max], |row| { |
| 241 | + Ok(serde_json::json!({ |
| 242 | + "id": row.get::<_, i64>(0)?, |
| 243 | + "name": row.get::<_, String>(1)?, |
| 244 | + "category": row.get::<_, String>(2)?, |
| 245 | + "price": row.get::<_, f64>(3)?, |
| 246 | + "quantity": row.get::<_, i64>(4)?, |
| 247 | + "active": row.get::<_, i64>(5)? == 1, |
| 248 | + "tags": serde_json::from_str::<serde_json::Value>(&row.get::<_, String>(6)?).unwrap_or_default(), |
| 249 | + "rating": serde_json::json!({ |
| 250 | + "score": row.get::<_, f64>(7)?, |
| 251 | + "count": row.get::<_, i64>(8)? |
| 252 | + }) |
| 253 | + })) |
| 254 | + }); |
| 255 | + let items: Vec<serde_json::Value> = match rows { |
| 256 | + Ok(mapped) => mapped.filter_map(|r| r.ok()).collect(), |
| 257 | + Err(_) => Vec::new(), |
| 258 | + }; |
| 259 | + let result = serde_json::json!({"items": items, "count": items.len()}); |
| 260 | + HttpResponse::Ok() |
| 261 | + .insert_header((SERVER, SERVER_HDR.clone())) |
| 262 | + .content_type(ContentType::json()) |
| 263 | + .body(result.to_string()) |
| 264 | +} |
| 265 | + |
256 | 266 | async fn static_file( |
257 | 267 | state: web::Data<Arc<AppState>>, |
258 | 268 | path: web::Path<String>, |
@@ -307,16 +317,27 @@ async fn main() -> io::Result<()> { |
307 | 317 | let mut server = HttpServer::new({ |
308 | 318 | let state = state.clone(); |
309 | 319 | move || { |
| 320 | + let worker_db = Connection::open_with_flags( |
| 321 | + "/data/benchmark.db", |
| 322 | + rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY, |
| 323 | + ) |
| 324 | + .map(|conn| { |
| 325 | + conn.execute_batch("PRAGMA mmap_size=268435456").ok(); |
| 326 | + WorkerDb(Mutex::new(conn)) |
| 327 | + }) |
| 328 | + .expect("Failed to open database"); |
310 | 329 | App::new() |
311 | 330 | .wrap(actix_web::middleware::Compress::default()) |
312 | 331 | .app_data(web::Data::new(state.clone())) |
| 332 | + .app_data(web::Data::new(worker_db)) |
313 | 333 | .app_data(web::PayloadConfig::new(25 * 1024 * 1024)) |
314 | 334 | .route("/pipeline", web::get().to(pipeline)) |
315 | 335 | .route("/baseline11", web::get().to(baseline11_get)) |
316 | 336 | .route("/baseline11", web::post().to(baseline11_post)) |
317 | 337 | .route("/baseline2", web::get().to(baseline2)) |
318 | 338 | .route("/json", web::get().to(json_endpoint)) |
319 | 339 | .route("/compression", web::get().to(compression)) |
| 340 | + .route("/db", web::get().to(db_endpoint)) |
320 | 341 | .route("/upload", web::post().to(upload)) |
321 | 342 | .route("/static/{filename}", web::get().to(static_file)) |
322 | 343 | } |
|
0 commit comments