bugfix: retrieve and update ticketbook in the same query (#6101)
* bugfix: retrieve and update ticketbook in the same query * bump up NS version * Update Cargo.toml * remove SKIP LOCKED part of the query --------- Co-authored-by: benedetta davico <46782255+benedettadavico@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
6a9a767ab4
commit
ca0c9898f0
Generated
+1
-1
@@ -6597,7 +6597,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-node-status-api"
|
||||
version = "4.0.9"
|
||||
version = "4.0.10"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"anyhow",
|
||||
|
||||
-14
@@ -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"
|
||||
}
|
||||
+65
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Postgres>;
|
||||
|
||||
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<Transaction<'a, Postgres>> 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,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// 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<StorageTransaction<'_>, sqlx::Error> {
|
||||
self.pool.begin().await.map(Into::into)
|
||||
}
|
||||
|
||||
pub(crate) async fn available_tickets_of_type(&self, typ: &str) -> Result<i64, sqlx::Error> {
|
||||
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<Option<StoredIssuedTicketbook>, 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(())
|
||||
}
|
||||
|
||||
@@ -91,15 +91,14 @@ impl TicketbookManagerStorage {
|
||||
testrun_id: i32,
|
||||
) -> anyhow::Result<Option<RetrievedTicketbook>> {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user