diff --git a/Cargo.lock b/Cargo.lock index 01206ee22f..ad8b4d9064 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6597,7 +6597,7 @@ dependencies = [ [[package]] name = "nym-node-status-api" -version = "4.0.9" +version = "4.0.10" dependencies = [ "ammonia", "anyhow", diff --git a/nym-node-status-api/nym-node-status-api/.sqlx/query-8c92a413a2853a2508c0e8a17ae8723c400930663c4c76e96dfdc7e8c98501ca.json b/nym-node-status-api/nym-node-status-api/.sqlx/query-8c92a413a2853a2508c0e8a17ae8723c400930663c4c76e96dfdc7e8c98501ca.json deleted file mode 100644 index 25937a2a47..0000000000 --- a/nym-node-status-api/nym-node-status-api/.sqlx/query-8c92a413a2853a2508c0e8a17ae8723c400930663c4c76e96dfdc7e8c98501ca.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE ecash_ticketbook SET used_tickets = used_tickets + 1 WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4" - ] - }, - "nullable": [] - }, - "hash": "8c92a413a2853a2508c0e8a17ae8723c400930663c4c76e96dfdc7e8c98501ca" -} diff --git a/nym-node-status-api/nym-node-status-api/.sqlx/query-af5c78ef980e38d81f58f72f21c9cd410f83b8750196e0cf5fa5af23883e76df.json b/nym-node-status-api/nym-node-status-api/.sqlx/query-af5c78ef980e38d81f58f72f21c9cd410f83b8750196e0cf5fa5af23883e76df.json new file mode 100644 index 0000000000..2246b63dee --- /dev/null +++ b/nym-node-status-api/nym-node-status-api/.sqlx/query-af5c78ef980e38d81f58f72f21c9cd410f83b8750196e0cf5fa5af23883e76df.json @@ -0,0 +1,65 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE ecash_ticketbook\n SET used_tickets = used_tickets + 1\n WHERE id = (\n SELECT id\n FROM ecash_ticketbook\n WHERE used_tickets < total_tickets\n AND expiration_date >= $1\n AND ticketbook_type = $2\n ORDER BY expiration_date ASC\n LIMIT 1\n FOR UPDATE\n )\n RETURNING *\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "serialization_revision", + "type_info": "Int2" + }, + { + "ordinal": 2, + "name": "ticketbook_type", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "ticketbook_data", + "type_info": "Bytea" + }, + { + "ordinal": 4, + "name": "expiration_date", + "type_info": "Date" + }, + { + "ordinal": 5, + "name": "epoch_id", + "type_info": "Int4" + }, + { + "ordinal": 6, + "name": "total_tickets", + "type_info": "Int4" + }, + { + "ordinal": 7, + "name": "used_tickets", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Date", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "af5c78ef980e38d81f58f72f21c9cd410f83b8750196e0cf5fa5af23883e76df" +} diff --git a/nym-node-status-api/nym-node-status-api/Cargo.toml b/nym-node-status-api/nym-node-status-api/Cargo.toml index a54d942237..a1fefcdcc5 100644 --- a/nym-node-status-api/nym-node-status-api/Cargo.toml +++ b/nym-node-status-api/nym-node-status-api/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "nym-node-status-api" -version = "4.0.9" +version = "4.0.10" authors.workspace = true repository.workspace = true homepage.workspace = true diff --git a/nym-node-status-api/nym-node-status-api/src/db/mod.rs b/nym-node-status-api/nym-node-status-api/src/db/mod.rs index fc948bee81..0205b4b64e 100644 --- a/nym-node-status-api/nym-node-status-api/src/db/mod.rs +++ b/nym-node-status-api/nym-node-status-api/src/db/mod.rs @@ -1,5 +1,4 @@ use anyhow::{Result, anyhow}; -use std::ops::{Deref, DerefMut}; use std::{str::FromStr, time::Duration}; pub(crate) mod models; @@ -8,9 +7,7 @@ pub(crate) mod queries; #[cfg(test)] mod tests; -use sqlx::{ - ConnectOptions, PgPool, Postgres, Transaction, migrate::Migrator, postgres::PgConnectOptions, -}; +use sqlx::{ConnectOptions, PgPool, Postgres, migrate::Migrator, postgres::PgConnectOptions}; static MIGRATOR: Migrator = sqlx::migrate!("./migrations_pg"); @@ -18,35 +15,6 @@ pub(crate) type DbPool = PgPool; pub(crate) type DbConnection = sqlx::pool::PoolConnection; -pub(crate) struct StorageTransaction<'a> { - inner: Transaction<'a, Postgres>, -} - -impl<'a> StorageTransaction<'a> { - pub(crate) async fn commit(self) -> Result<(), sqlx::Error> { - self.inner.commit().await - } -} - -impl<'a> From> for StorageTransaction<'a> { - fn from(inner: Transaction<'a, Postgres>) -> Self { - Self { inner } - } -} - -impl<'a> Deref for StorageTransaction<'a> { - type Target = Transaction<'a, Postgres>; - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl<'a> DerefMut for StorageTransaction<'a> { - fn deref_mut(&mut self) -> &mut Transaction<'a, Postgres> { - &mut self.inner - } -} - #[derive(Clone)] pub(crate) struct Storage { pool: DbPool, diff --git a/nym-node-status-api/nym-node-status-api/src/db/queries/ecash_data.rs b/nym-node-status-api/nym-node-status-api/src/db/queries/ecash_data.rs index 0e92935f3d..0f90e5884a 100644 --- a/nym-node-status-api/nym-node-status-api/src/db/queries/ecash_data.rs +++ b/nym-node-status-api/nym-node-status-api/src/db/queries/ecash_data.rs @@ -1,7 +1,7 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::db::{Storage, StorageTransaction}; +use crate::db::Storage; use crate::ticketbook_manager::storage::auxiliary_models::StoredIssuedTicketbook; use nym_credential_proxy_lib::storage::models::{ RawCoinIndexSignatures, RawExpirationDateSignatures, RawVerificationKey, @@ -10,10 +10,6 @@ use time::Date; use tracing::error; impl Storage { - pub(crate) async fn begin_storage_tx(&self) -> Result, sqlx::Error> { - self.pool.begin().await.map(Into::into) - } - pub(crate) async fn available_tickets_of_type(&self, typ: &str) -> Result { let count = sqlx::query!( r#" @@ -217,46 +213,38 @@ impl Storage { .await?; Ok(()) } -} -impl<'a> StorageTransaction<'a> { pub(crate) async fn get_next_unspent_ticketbook( - &mut self, + &self, ticket_type: String, deadline: Date, ) -> Result, sqlx::Error> { - sqlx::query_as( + sqlx::query_as!( + StoredIssuedTicketbook, r#" - SELECT * - FROM ecash_ticketbook - WHERE used_tickets + 1 <= total_tickets - AND expiration_date >= $1 - AND ticketbook_type = $2 - ORDER BY expiration_date ASC - LIMIT 1 + UPDATE ecash_ticketbook + SET used_tickets = used_tickets + 1 + WHERE id = ( + SELECT id + FROM ecash_ticketbook + WHERE used_tickets < total_tickets + AND expiration_date >= $1 + AND ticketbook_type = $2 + ORDER BY expiration_date ASC + LIMIT 1 + FOR UPDATE + ) + RETURNING * "#, + deadline, + ticket_type ) - .bind(deadline) - .bind(ticket_type) - .fetch_optional(&mut ***self) + .fetch_optional(&self.pool) .await } - pub(crate) async fn increase_used_ticketbook_tickets( - &mut self, - ticketbook_id: i32, - ) -> Result<(), sqlx::Error> { - sqlx::query!( - "UPDATE ecash_ticketbook SET used_tickets = used_tickets + 1 WHERE id = $1", - ticketbook_id - ) - .execute(&mut ***self) - .await?; - Ok(()) - } - pub(crate) async fn set_distributed_ticketbook( - &mut self, + &self, testrun_id: i32, ticketbook_id: i32, assigned_index: i32, @@ -271,7 +259,7 @@ impl<'a> StorageTransaction<'a> { ticketbook_id, assigned_index ) - .execute(&mut ***self) + .execute(&self.pool) .await?; Ok(()) } diff --git a/nym-node-status-api/nym-node-status-api/src/ticketbook_manager/storage/mod.rs b/nym-node-status-api/nym-node-status-api/src/ticketbook_manager/storage/mod.rs index bf4da00b3c..e8f2091448 100644 --- a/nym-node-status-api/nym-node-status-api/src/ticketbook_manager/storage/mod.rs +++ b/nym-node-status-api/nym-node-status-api/src/ticketbook_manager/storage/mod.rs @@ -91,15 +91,14 @@ impl TicketbookManagerStorage { testrun_id: i32, ) -> anyhow::Result> { let deadline = ecash_today().ecash_date(); - let mut tx = self.storage.begin_storage_tx().await?; // we don't want ticketbooks with expiration in the past - let Some(raw) = tx + // note: this query updates the spent tickets atomically + let Some(raw) = self + .storage .get_next_unspent_ticketbook(ticket_type.to_string(), deadline) .await? else { - // make sure to finish our tx - tx.commit().await?; return Ok(None); }; @@ -110,10 +109,9 @@ impl TicketbookManagerStorage { ) .map_err(|err| anyhow!("failed to deserialise stored ticketbook: {err}"))?; - tx.set_distributed_ticketbook(testrun_id, raw.id, raw.used_tickets) + self.storage + .set_distributed_ticketbook(testrun_id, raw.id, raw.used_tickets) .await?; - tx.increase_used_ticketbook_tickets(raw.id).await?; - tx.commit().await?; deserialised.update_spent_tickets(raw.used_tickets as u64); Ok(Some(RetrievedTicketbook {