Use database transactions where appropriate

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-11-06 12:40:19 +01:00
parent 609d05fff2
commit a8dc065dce
No known key found for this signature in database
3 changed files with 64 additions and 37 deletions

View File

@ -11,6 +11,7 @@ use crate::address::{KnownAddress, Source};
use crate::clock::Timestamp; use crate::clock::Timestamp;
use crate::prelude::Address; use crate::prelude::Address;
use crate::service::NodeId; use crate::service::NodeId;
use crate::sql::transaction;
use crate::wire::message::AddressType; use crate::wire::message::AddressType;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -120,7 +121,8 @@ impl Store for Book {
timestamp: Timestamp, timestamp: Timestamp,
addrs: impl IntoIterator<Item = KnownAddress>, addrs: impl IntoIterator<Item = KnownAddress>,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let mut stmt = self.db.prepare( transaction(&self.db, move |db| {
let mut stmt = db.prepare(
"INSERT INTO nodes (id, features, alias, timestamp) "INSERT INTO nodes (id, features, alias, timestamp)
VALUES (?1, ?2, ?3, ?4) VALUES (?1, ?2, ?3, ?4)
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
@ -135,7 +137,7 @@ impl Store for Book {
stmt.next()?; stmt.next()?;
for addr in addrs { for addr in addrs {
let mut stmt = self.db.prepare( let mut stmt = db.prepare(
"INSERT INTO addresses (node, type, value, source, timestamp) "INSERT INTO addresses (node, type, value, source, timestamp)
VALUES (?1, ?2, ?3, ?4, ?5) VALUES (?1, ?2, ?3, ?4, ?5)
ON CONFLICT DO UPDATE ON CONFLICT DO UPDATE
@ -149,23 +151,26 @@ impl Store for Book {
stmt.bind(5, timestamp as i64)?; stmt.bind(5, timestamp as i64)?;
stmt.next()?; stmt.next()?;
} }
Ok(self.db.change_count() > 0) Ok(db.change_count() > 0)
})
.map_err(Error::from)
} }
fn remove(&mut self, node: &NodeId) -> Result<bool, Error> { fn remove(&mut self, node: &NodeId) -> Result<bool, Error> {
self.db transaction(&self.db, move |db| {
.prepare("DELETE FROM nodes WHERE id = ?")? db.prepare("DELETE FROM nodes WHERE id = ?")?
.into_cursor() .into_cursor()
.bind(&[(*node).into()])? .bind(&[(*node).into()])?
.next(); .next();
self.db db.prepare("DELETE FROM addresses WHERE node = ?")?
.prepare("DELETE FROM addresses WHERE node = ?")?
.into_cursor() .into_cursor()
.bind(&[(*node).into()])? .bind(&[(*node).into()])?
.next(); .next();
Ok(self.db.change_count() > 0) Ok(db.change_count() > 0)
})
.map_err(Error::from)
} }
fn entries(&self) -> Result<Box<dyn Iterator<Item = (NodeId, KnownAddress)>>, Error> { fn entries(&self) -> Result<Box<dyn Iterator<Item = (NodeId, KnownAddress)>>, Error> {

View File

@ -5,6 +5,7 @@ pub mod control;
pub mod decoder; pub mod decoder;
pub mod logger; pub mod logger;
pub mod service; pub mod service;
pub mod sql;
#[cfg(test)] #[cfg(test)]
pub mod test; pub mod test;
pub mod transport; pub mod transport;

21
radicle-node/src/sql.rs Normal file
View File

@ -0,0 +1,21 @@
use sqlite as sql;
/// Run an SQL query inside a transaction.
/// Commits the transaction on success, and rolls back on error.
pub fn transaction<T>(
db: &sql::Connection,
query: impl FnOnce(&sql::Connection) -> Result<T, sql::Error>,
) -> Result<T, sql::Error> {
db.execute("BEGIN")?;
match query(db) {
Ok(result) => {
db.execute("COMMIT")?;
Ok(result)
}
Err(err) => {
db.execute("ROLLBACK")?;
Err(err)
}
}
}