From 8af09939ff69af5be85a5a09e307fe3302c1be2a Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Wed, 28 Jun 2023 22:54:18 +0530 Subject: [PATCH] feat: pagination on performance logs fetches --- db/db-core/src/lib.rs | 17 ++++- db/db-core/src/tests.rs | 15 +++- db/db-sqlx-maria/sqlx-data.json | 115 ++++++++++++++++------------- db/db-sqlx-maria/src/lib.rs | 25 ++++--- db/db-sqlx-postgres/sqlx-data.json | 72 ++++++++++-------- db/db-sqlx-postgres/src/lib.rs | 14 +++- 6 files changed, 160 insertions(+), 98 deletions(-) diff --git a/db/db-core/src/lib.rs b/db/db-core/src/lib.rs index b30e5e1a..4a3a0ccc 100644 --- a/db/db-core/src/lib.rs +++ b/db/db-core/src/lib.rs @@ -255,19 +255,34 @@ pub trait MCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase { async fn analysis_save( &self, captcha_id: &str, - d: &PerformanceAnalytics, + d: &CreatePerformanceAnalytics, ) -> DBResult<()>; /// fetch PoW analytics async fn analytics_fetch( &self, captcha_id: &str, + limit: usize, + offset: usize, ) -> DBResult>; } +#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)] +/// Log Proof-of-Work CAPTCHA performance analytics +pub struct CreatePerformanceAnalytics { + /// time taken to generate proof + pub time: u32, + /// difficulty factor for which the proof was generated + pub difficulty_factor: u32, + /// worker/client type: wasm, javascript, python, etc. + pub worker_type: String, +} + #[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)] /// Proof-of-Work CAPTCHA performance analytics pub struct PerformanceAnalytics { + /// log ID + pub id: usize, /// time taken to generate proof pub time: u32, /// difficulty factor for which the proof was generated diff --git a/db/db-core/src/tests.rs b/db/db-core/src/tests.rs index a2d19f03..b8699596 100644 --- a/db/db-core/src/tests.rs +++ b/db/db-core/src/tests.rs @@ -260,13 +260,24 @@ pub async fn database_works<'a, T: MCDatabase>( db.record_solve(c.key).await.unwrap(); db.record_confirm(c.key).await.unwrap(); - let analytics = PerformanceAnalytics { + let analytics = CreatePerformanceAnalytics { time: 0, difficulty_factor: 0, worker_type: "wasm".into(), }; db.analysis_save(c.key, &analytics).await.unwrap(); - assert_eq!(db.analytics_fetch(c.key).await.unwrap(), vec![analytics]); + let limit = 50; + let mut offset = 0; + let a = db.analytics_fetch(c.key, limit, offset).await.unwrap(); + assert_eq!(a[0].time, analytics.time); + assert_eq!(a[0].difficulty_factor, analytics.difficulty_factor); + assert_eq!(a[0].worker_type, analytics.worker_type); + offset += 1; + assert!(db + .analytics_fetch(c.key, limit, offset) + .await + .unwrap() + .is_empty()); assert_eq!(db.fetch_solve(p.username, c.key).await.unwrap().len(), 1); assert_eq!( diff --git a/db/db-sqlx-maria/sqlx-data.json b/db/db-sqlx-maria/sqlx-data.json index 9a6934cd..bb5ad114 100644 --- a/db/db-sqlx-maria/sqlx-data.json +++ b/db/db-sqlx-maria/sqlx-data.json @@ -277,57 +277,6 @@ }, "query": "DELETE FROM mcaptcha_levels \n WHERE config_id = (\n SELECT config_id FROM mcaptcha_config where captcha_key= (?) \n AND user_id = (\n SELECT ID from mcaptcha_users WHERE name = ?\n )\n )" }, - "7fde24630da96b8c4fd9d6120a54b4fae9db4f3c1c1bc15ebb2c0e94831ff9af": { - "describe": { - "columns": [ - { - "name": "time", - "ordinal": 0, - "type_info": { - "char_set": 63, - "flags": { - "bits": 4097 - }, - "max_size": 11, - "type": "Long" - } - }, - { - "name": "difficulty_factor", - "ordinal": 1, - "type_info": { - "char_set": 63, - "flags": { - "bits": 4097 - }, - "max_size": 11, - "type": "Long" - } - }, - { - "name": "worker_type", - "ordinal": 2, - "type_info": { - "char_set": 224, - "flags": { - "bits": 4101 - }, - "max_size": 400, - "type": "VarString" - } - } - ], - "nullable": [ - false, - false, - false - ], - "parameters": { - "Right": 1 - } - }, - "query": "SELECT time, difficulty_factor, worker_type FROM mcaptcha_pow_analytics\n WHERE \n config_id = (\n SELECT \n config_id FROM mcaptcha_config \n WHERE \n captcha_key = ?\n )\n ORDER BY ID" - }, "89386c46668a2653a54687e65958af5cf7a8da268339a1f5a379ede47b3c6d2a": { "describe": { "columns": [], @@ -457,6 +406,70 @@ }, "query": "UPDATE mcaptcha_users set email = ?\n WHERE name = ?" }, + "9f10afb0f242f11c58389803c5e85e244cc59102b8929a21e3fcaa852d57a52c": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": { + "char_set": 63, + "flags": { + "bits": 515 + }, + "max_size": 11, + "type": "Long" + } + }, + { + "name": "time", + "ordinal": 1, + "type_info": { + "char_set": 63, + "flags": { + "bits": 4097 + }, + "max_size": 11, + "type": "Long" + } + }, + { + "name": "difficulty_factor", + "ordinal": 2, + "type_info": { + "char_set": 63, + "flags": { + "bits": 4097 + }, + "max_size": 11, + "type": "Long" + } + }, + { + "name": "worker_type", + "ordinal": 3, + "type_info": { + "char_set": 224, + "flags": { + "bits": 4101 + }, + "max_size": 400, + "type": "VarString" + } + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Right": 3 + } + }, + "query": "SELECT\n id, time, difficulty_factor, worker_type\n FROM\n mcaptcha_pow_analytics\n WHERE\n config_id = (\n SELECT config_id FROM mcaptcha_config WHERE captcha_key = ?\n ) \n ORDER BY ID\n LIMIT ? OFFSET ?" + }, "a89c066db044cddfdebee6a0fd0d80a5a26dcb7ecc00a9899f5634b72ea0a952": { "describe": { "columns": [ diff --git a/db/db-sqlx-maria/src/lib.rs b/db/db-sqlx-maria/src/lib.rs index b57befde..b71cc1f3 100644 --- a/db/db-sqlx-maria/src/lib.rs +++ b/db/db-sqlx-maria/src/lib.rs @@ -900,7 +900,7 @@ impl MCDatabase for Database { async fn analysis_save( &self, captcha_id: &str, - d: &PerformanceAnalytics, + d: &CreatePerformanceAnalytics, ) -> DBResult<()> { let _ = sqlx::query!( "INSERT INTO mcaptcha_pow_analytics @@ -921,8 +921,11 @@ impl MCDatabase for Database { async fn analytics_fetch( &self, captcha_id: &str, + limit: usize, + offset: usize, ) -> DBResult> { struct P { + id: i32, time: i32, difficulty_factor: i32, worker_type: String, @@ -931,6 +934,7 @@ impl MCDatabase for Database { impl From

for PerformanceAnalytics { fn from(v: P) -> Self { Self { + id: v.id as usize, time: v.time as u32, difficulty_factor: v.difficulty_factor as u32, worker_type: v.worker_type, @@ -940,16 +944,19 @@ impl MCDatabase for Database { let mut c = sqlx::query_as!( P, - "SELECT time, difficulty_factor, worker_type FROM mcaptcha_pow_analytics - WHERE + "SELECT + id, time, difficulty_factor, worker_type + FROM + mcaptcha_pow_analytics + WHERE config_id = ( - SELECT - config_id FROM mcaptcha_config - WHERE - captcha_key = ? - ) - ORDER BY ID", + SELECT config_id FROM mcaptcha_config WHERE captcha_key = ? + ) + ORDER BY ID + LIMIT ? OFFSET ?", &captcha_id, + limit as i64, + offset as i64, ) .fetch_all(&self.pool) .await diff --git a/db/db-sqlx-postgres/sqlx-data.json b/db/db-sqlx-postgres/sqlx-data.json index 917c5c78..ae93fcc8 100644 --- a/db/db-sqlx-postgres/sqlx-data.json +++ b/db/db-sqlx-postgres/sqlx-data.json @@ -1,5 +1,45 @@ { "db": "PostgreSQL", + "017576128f1c63aee062799a33f872457fe19f5d6429d0af312dc00c244b31cb": { + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Int4" + }, + { + "name": "time", + "ordinal": 1, + "type_info": "Int4" + }, + { + "name": "difficulty_factor", + "ordinal": 2, + "type_info": "Int4" + }, + { + "name": "worker_type", + "ordinal": 3, + "type_info": "Varchar" + } + ], + "nullable": [ + false, + false, + false, + false + ], + "parameters": { + "Left": [ + "Text", + "Int8", + "Int8" + ] + } + }, + "query": "SELECT id, time, difficulty_factor, worker_type FROM mcaptcha_pow_analytics\n WHERE \n config_id = (\n SELECT \n config_id FROM mcaptcha_config \n WHERE \n key = $1\n )\n ORDER BY ID\n OFFSET $2 LIMIT $3\n " + }, "02deb524bb12632af9b7883975f75fdc30d6775d836aff647add1dffd1a4bc00": { "describe": { "columns": [ @@ -626,38 +666,6 @@ }, "query": "DELETE FROM mcaptcha_users WHERE name = ($1)" }, - "d4ca28f0d895ef832d5cc1bc56c17e94c33efdfe63e10f29bbe54b6841a43320": { - "describe": { - "columns": [ - { - "name": "time", - "ordinal": 0, - "type_info": "Int4" - }, - { - "name": "difficulty_factor", - "ordinal": 1, - "type_info": "Int4" - }, - { - "name": "worker_type", - "ordinal": 2, - "type_info": "Varchar" - } - ], - "nullable": [ - false, - false, - false - ], - "parameters": { - "Left": [ - "Text" - ] - } - }, - "query": "SELECT time, difficulty_factor, worker_type FROM mcaptcha_pow_analytics\n WHERE \n config_id = (\n SELECT \n config_id FROM mcaptcha_config \n WHERE \n key = $1\n )\n ORDER BY ID" - }, "d7dd6cd6a7626e79c62377b2d59115067c5851ec044911ff8833779a08bbb8f7": { "describe": { "columns": [], diff --git a/db/db-sqlx-postgres/src/lib.rs b/db/db-sqlx-postgres/src/lib.rs index 103a6e06..75fb4d26 100644 --- a/db/db-sqlx-postgres/src/lib.rs +++ b/db/db-sqlx-postgres/src/lib.rs @@ -906,7 +906,7 @@ impl MCDatabase for Database { async fn analysis_save( &self, captcha_id: &str, - d: &PerformanceAnalytics, + d: &CreatePerformanceAnalytics, ) -> DBResult<()> { let _ = sqlx::query!( "INSERT INTO mcaptcha_pow_analytics @@ -927,8 +927,11 @@ impl MCDatabase for Database { async fn analytics_fetch( &self, captcha_id: &str, + limit: usize, + offset: usize, ) -> DBResult> { struct P { + id: i32, time: i32, difficulty_factor: i32, worker_type: String, @@ -940,13 +943,14 @@ impl MCDatabase for Database { time: v.time as u32, difficulty_factor: v.difficulty_factor as u32, worker_type: v.worker_type, + id: v.id as usize, } } } let mut c = sqlx::query_as!( P, - "SELECT time, difficulty_factor, worker_type FROM mcaptcha_pow_analytics + "SELECT id, time, difficulty_factor, worker_type FROM mcaptcha_pow_analytics WHERE config_id = ( SELECT @@ -954,8 +958,12 @@ impl MCDatabase for Database { WHERE key = $1 ) - ORDER BY ID", + ORDER BY ID + OFFSET $2 LIMIT $3 + ", &captcha_id, + offset as i32, + limit as i32 ) .fetch_all(&self.pool) .await