server: Upsert object row on conflict

Upsert instead of doing delete+insert or ignoring the specific error.

Fixes #132.
This commit is contained in:
Zhaofeng Li 2024-08-13 07:39:37 -06:00
parent e127acbf9a
commit 443ceac40f
2 changed files with 31 additions and 19 deletions

View file

@ -46,7 +46,7 @@ use crate::database::entity::cache;
use crate::database::entity::chunk::{self, ChunkState, Entity as Chunk}; use crate::database::entity::chunk::{self, ChunkState, Entity as Chunk};
use crate::database::entity::chunkref::{self, Entity as ChunkRef}; use crate::database::entity::chunkref::{self, Entity as ChunkRef};
use crate::database::entity::nar::{self, Entity as Nar, NarState}; 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::entity::Json as DbJson;
use crate::database::{AtticDatabase, ChunkGuard, NarGuard}; use crate::database::{AtticDatabase, ChunkGuard, NarGuard};
@ -257,12 +257,6 @@ async fn upload_path_dedup(
.map_err(ServerError::database_error)?; .map_err(ServerError::database_error)?;
// Create a mapping granting the local cache access to the NAR // 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({ Object::insert({
let mut new_object = upload_info.to_active_model(); let mut new_object = upload_info.to_active_model();
new_object.cache_id = Set(cache.id); new_object.cache_id = Set(cache.id);
@ -271,6 +265,7 @@ async fn upload_path_dedup(
new_object.created_by = Set(username); new_object.created_by = Set(username);
new_object new_object
}) })
.on_conflict_do_update()
.exec(&txn) .exec(&txn)
.await .await
.map_err(ServerError::database_error)?; .map_err(ServerError::database_error)?;
@ -487,12 +482,6 @@ async fn upload_path_new_chunked(
.map_err(ServerError::database_error)?; .map_err(ServerError::database_error)?;
// Create a mapping granting the local cache access to the NAR // 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({ Object::insert({
let mut new_object = upload_info.to_active_model(); let mut new_object = upload_info.to_active_model();
new_object.cache_id = Set(cache.id); 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.created_by = Set(username);
new_object new_object
}) })
.on_conflict_do_update()
.exec(&txn) .exec(&txn)
.await .await
.map_err(ServerError::database_error)?; .map_err(ServerError::database_error)?;
@ -594,12 +584,6 @@ async fn upload_path_new_unchunked(
.map_err(ServerError::database_error)?; .map_err(ServerError::database_error)?;
// Create a mapping granting the local cache access to the NAR // 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({ Object::insert({
let mut new_object = upload_info.to_active_model(); let mut new_object = upload_info.to_active_model();
new_object.cache_id = Set(cache.id); 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.created_by = Set(username);
new_object new_object
}) })
.on_conflict_do_update()
.exec(&txn) .exec(&txn)
.await .await
.map_err(ServerError::database_error)?; .map_err(ServerError::database_error)?;

View file

@ -6,6 +6,8 @@ use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
use sea_orm::sea_query::OnConflict;
use sea_orm::Insert;
use super::nar::NarModel; use super::nar::NarModel;
use super::Json; use super::Json;
@ -15,6 +17,10 @@ use attic::hash::Hash;
pub type ObjectModel = Model; pub type ObjectModel = Model;
pub trait InsertExt {
fn on_conflict_do_update(self) -> Self;
}
/// An object in a binary cache. /// An object in a binary cache.
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)] #[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "object")] #[sea_orm(table_name = "object")]
@ -87,6 +93,27 @@ pub enum Relation {
Nar, Nar,
} }
impl InsertExt for Insert<ActiveModel> {
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 { impl Model {
/// Converts this object to a NarInfo. /// Converts this object to a NarInfo.
pub fn to_nar_info(&self, nar: &NarModel) -> ServerResult<NarInfo> { pub fn to_nar_info(&self, nar: &NarModel) -> ServerResult<NarInfo> {