From 443ceac40f5b645098235074a273bdef54548ad2 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Tue, 13 Aug 2024 07:39:37 -0600 Subject: [PATCH] server: Upsert object row on conflict Upsert instead of doing delete+insert or ignoring the specific error. Fixes #132. --- server/src/api/v1/upload_path.rs | 23 ++++------------------- server/src/database/entity/object.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/server/src/api/v1/upload_path.rs b/server/src/api/v1/upload_path.rs index 3673797..4b04601 100644 --- a/server/src/api/v1/upload_path.rs +++ b/server/src/api/v1/upload_path.rs @@ -46,7 +46,7 @@ use crate::database::entity::cache; use crate::database::entity::chunk::{self, ChunkState, Entity as Chunk}; use crate::database::entity::chunkref::{self, Entity as ChunkRef}; use crate::database::entity::nar::{self, Entity as Nar, NarState}; -use crate::database::entity::object::{self, Entity as Object}; +use crate::database::entity::object::{self, Entity as Object, InsertExt}; use crate::database::entity::Json as DbJson; use crate::database::{AtticDatabase, ChunkGuard, NarGuard}; @@ -257,12 +257,6 @@ async fn upload_path_dedup( .map_err(ServerError::database_error)?; // Create a mapping granting the local cache access to the NAR - Object::delete_many() - .filter(object::Column::CacheId.eq(cache.id)) - .filter(object::Column::StorePathHash.eq(upload_info.store_path_hash.to_string())) - .exec(&txn) - .await - .map_err(ServerError::database_error)?; Object::insert({ let mut new_object = upload_info.to_active_model(); new_object.cache_id = Set(cache.id); @@ -271,6 +265,7 @@ async fn upload_path_dedup( new_object.created_by = Set(username); new_object }) + .on_conflict_do_update() .exec(&txn) .await .map_err(ServerError::database_error)?; @@ -487,12 +482,6 @@ async fn upload_path_new_chunked( .map_err(ServerError::database_error)?; // Create a mapping granting the local cache access to the NAR - Object::delete_many() - .filter(object::Column::CacheId.eq(cache.id)) - .filter(object::Column::StorePathHash.eq(upload_info.store_path_hash.to_string())) - .exec(&txn) - .await - .map_err(ServerError::database_error)?; Object::insert({ let mut new_object = upload_info.to_active_model(); new_object.cache_id = Set(cache.id); @@ -501,6 +490,7 @@ async fn upload_path_new_chunked( new_object.created_by = Set(username); new_object }) + .on_conflict_do_update() .exec(&txn) .await .map_err(ServerError::database_error)?; @@ -594,12 +584,6 @@ async fn upload_path_new_unchunked( .map_err(ServerError::database_error)?; // Create a mapping granting the local cache access to the NAR - Object::delete_many() - .filter(object::Column::CacheId.eq(cache.id)) - .filter(object::Column::StorePathHash.eq(upload_info.store_path_hash.to_string())) - .exec(&txn) - .await - .map_err(ServerError::database_error)?; Object::insert({ let mut new_object = upload_info.to_active_model(); new_object.cache_id = Set(cache.id); @@ -608,6 +592,7 @@ async fn upload_path_new_unchunked( new_object.created_by = Set(username); new_object }) + .on_conflict_do_update() .exec(&txn) .await .map_err(ServerError::database_error)?; diff --git a/server/src/database/entity/object.rs b/server/src/database/entity/object.rs index 37ad893..69b5a62 100644 --- a/server/src/database/entity/object.rs +++ b/server/src/database/entity/object.rs @@ -6,6 +6,8 @@ use std::path::PathBuf; use std::str::FromStr; use sea_orm::entity::prelude::*; +use sea_orm::sea_query::OnConflict; +use sea_orm::Insert; use super::nar::NarModel; use super::Json; @@ -15,6 +17,10 @@ use attic::hash::Hash; pub type ObjectModel = Model; +pub trait InsertExt { + fn on_conflict_do_update(self) -> Self; +} + /// An object in a binary cache. #[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "object")] @@ -87,6 +93,27 @@ pub enum Relation { Nar, } +impl InsertExt for Insert { + fn on_conflict_do_update(self) -> Self { + self.on_conflict( + OnConflict::columns([Column::CacheId, Column::StorePathHash]) + .update_columns([ + Column::NarId, + Column::StorePath, + Column::References, + Column::System, + Column::Deriver, + Column::Sigs, + Column::Ca, + Column::CreatedAt, + Column::LastAccessedAt, + Column::CreatedBy, + ]) + .to_owned(), + ) + } +} + impl Model { /// Converts this object to a NarInfo. pub fn to_nar_info(&self, nar: &NarModel) -> ServerResult {