From 6e45e71f41f6e5f62f056197241c0f72b2701b58 Mon Sep 17 00:00:00 2001 From: marvin-j97 Date: Fri, 2 Feb 2024 13:36:58 +0100 Subject: [PATCH] feat: add count route --- server/src/api/count.rs | 80 ++++++++++++++++++++++++++++++++++ server/src/api/delete_row.rs | 4 +- server/src/api/delete_table.rs | 4 +- server/src/api/get_rows.rs | 4 +- server/src/api/metrics.rs | 4 +- server/src/api/mod.rs | 1 + server/src/api/scan.rs | 4 +- server/src/api/write.rs | 4 +- server/src/main.rs | 1 + 9 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 server/src/api/count.rs diff --git a/server/src/api/count.rs b/server/src/api/count.rs new file mode 100644 index 0000000..840cd05 --- /dev/null +++ b/server/src/api/count.rs @@ -0,0 +1,80 @@ +use crate::app_state::AppState; +use crate::error::CustomRouteResult; +use crate::identifier::is_valid_table_identifier; +use crate::response::build_response; +use actix_web::http::StatusCode; +use actix_web::{ + post, + web::{self, Path}, + HttpResponse, +}; +use serde_json::json; + +#[post("/v1/table/{name}/count")] +pub async fn handler( + path: Path, + app_state: web::Data, +) -> CustomRouteResult { + let before = std::time::Instant::now(); + + let table_name = path.into_inner(); + + if table_name.starts_with('_') { + return Ok(build_response( + before.elapsed(), + StatusCode::BAD_REQUEST, + "Invalid table name", + &json!(null), + )); + } + + if !is_valid_table_identifier(&table_name) { + return Ok(build_response( + before.elapsed(), + StatusCode::BAD_REQUEST, + "Invalid table name", + &json!(null), + )); + } + + let tables = app_state.tables.read().await; + + if let Some(table) = tables.get(&table_name) { + let (row_count, cell_count) = { + let table = table.clone(); + + tokio::task::spawn_blocking(move || table.count()) + .await + .expect("should join") + }?; + + let dur = before.elapsed(); + + let micros_total = dur.as_micros(); + + let micros_per_row = if row_count == 0 { + None + } else { + Some(micros_total / row_count as u128) + }; + + Ok(build_response( + dur, + StatusCode::OK, + "Count successful", + &json!({ + "row_count": row_count, + "cell_count": cell_count, + "micros": micros_total, + "micros_per_row": micros_per_row, + }), + )) + } else { + Ok(build_response( + before.elapsed(), + StatusCode::NOT_FOUND, + "Table not found", + &json!(null), + )) + } +} diff --git a/server/src/api/delete_row.rs b/server/src/api/delete_row.rs index 24979cb..dbfed92 100644 --- a/server/src/api/delete_row.rs +++ b/server/src/api/delete_row.rs @@ -29,8 +29,6 @@ pub async fn handler( ) -> CustomRouteResult { let before = std::time::Instant::now(); - let tables = app_state.tables.read().await; - let table_name = path.into_inner(); if table_name.starts_with('_') { @@ -53,6 +51,8 @@ pub async fn handler( let req_body = req_body.into_inner(); + let tables = app_state.tables.read().await; + if let Some(table) = tables.get(&table_name) { let count = { let table = table.clone(); diff --git a/server/src/api/delete_table.rs b/server/src/api/delete_table.rs index 7469326..3502780 100644 --- a/server/src/api/delete_table.rs +++ b/server/src/api/delete_table.rs @@ -17,8 +17,6 @@ pub async fn handler( ) -> CustomRouteResult { let before = std::time::Instant::now(); - let mut tables = app_state.tables.write().await; - let table_name = path.into_inner(); if table_name.starts_with('_') { @@ -39,6 +37,8 @@ pub async fn handler( )); } + let mut tables = app_state.tables.write().await; + if let Some(table) = tables.get(&table_name).cloned() { app_state.manifest_table.delete_user_table(&table_name)?; tables.remove(&table_name); diff --git a/server/src/api/get_rows.rs b/server/src/api/get_rows.rs index 8540f1e..92776e0 100644 --- a/server/src/api/get_rows.rs +++ b/server/src/api/get_rows.rs @@ -26,8 +26,6 @@ pub async fn handler( ) -> CustomRouteResult { let before = std::time::Instant::now(); - let tables = app_state.tables.read().await; - let table_name = path.into_inner(); if table_name.starts_with('_') { @@ -48,6 +46,8 @@ pub async fn handler( )); } + let tables = app_state.tables.read().await; + if let Some(table) = tables.get(&table_name) { let result = { let table = table.clone(); diff --git a/server/src/api/metrics.rs b/server/src/api/metrics.rs index f9e976d..8316844 100644 --- a/server/src/api/metrics.rs +++ b/server/src/api/metrics.rs @@ -16,12 +16,12 @@ pub async fn handler( ) -> CustomRouteResult { let before = std::time::Instant::now(); - let tables = app_state.tables.write().await; - let table_name = path.into_inner(); let actual_name = format!("usr_{table_name}"); + let tables = app_state.tables.write().await; + if tables.get(&actual_name).is_some() { /* let rows = app_state .metrics_table diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 20c2eb8..1aeca4b 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -1,3 +1,4 @@ +pub mod count; pub mod create_column_family; pub mod create_table; pub mod delete_row; diff --git a/server/src/api/scan.rs b/server/src/api/scan.rs index ed27cdd..cfdf80c 100644 --- a/server/src/api/scan.rs +++ b/server/src/api/scan.rs @@ -20,8 +20,6 @@ pub async fn handler( ) -> CustomRouteResult { let before = std::time::Instant::now(); - let tables = app_state.tables.read().await; - let table_name = path.into_inner(); if table_name.starts_with('_') { @@ -42,6 +40,8 @@ pub async fn handler( )); } + let tables = app_state.tables.read().await; + if let Some(table) = tables.get(&table_name) { let result = { let table = table.clone(); diff --git a/server/src/api/write.rs b/server/src/api/write.rs index bd78542..6617984 100644 --- a/server/src/api/write.rs +++ b/server/src/api/write.rs @@ -32,8 +32,6 @@ pub async fn handler( return bad_request(before, "Items array should not be empty"); } - let tables = app_state.tables.read().await; - let table_name = path.into_inner(); if table_name.starts_with('_') { @@ -54,6 +52,8 @@ pub async fn handler( )); } + let tables = app_state.tables.read().await; + if let Some(table) = tables.get(&table_name).cloned() { // TODO: use spawn_blocking diff --git a/server/src/main.rs b/server/src/main.rs index f1b8745..14138fa 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -135,6 +135,7 @@ async fn main() -> smoltable::Result<()> { .service(api::list_tables::handler) .service(api::create_table::handler) .service(api::write::handler) + .service(api::count::handler) .service(api::get_rows::handler) .service(api::delete_row::handler) .service(api::scan::handler)