mirror of
https://git.asonix.dog/asonix/pict-rs.git
synced 2024-09-26 14:00:02 +00:00
Share notification map between sled, postgres
This commit is contained in:
parent
d9d5ac5388
commit
74885f2932
9 changed files with 245 additions and 251 deletions
|
@ -13,7 +13,6 @@ use crate::{
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
path::PathBuf,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
@ -52,7 +51,7 @@ impl Drop for MetricsGuard {
|
||||||
pub(crate) async fn generate<S: Store + 'static>(
|
pub(crate) async fn generate<S: Store + 'static>(
|
||||||
state: &State<S>,
|
state: &State<S>,
|
||||||
format: InputProcessableFormat,
|
format: InputProcessableFormat,
|
||||||
thumbnail_path: PathBuf,
|
variant: String,
|
||||||
thumbnail_args: Vec<String>,
|
thumbnail_args: Vec<String>,
|
||||||
original_details: &Details,
|
original_details: &Details,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
|
@ -66,12 +65,10 @@ pub(crate) async fn generate<S: Store + 'static>(
|
||||||
|
|
||||||
Ok((original_details.clone(), identifier))
|
Ok((original_details.clone(), identifier))
|
||||||
} else {
|
} else {
|
||||||
let variant = thumbnail_path.to_string_lossy().to_string();
|
|
||||||
|
|
||||||
let mut attempts = 0;
|
let mut attempts = 0;
|
||||||
let tup = loop {
|
let tup = loop {
|
||||||
if attempts > 4 {
|
if attempts > 2 {
|
||||||
todo!("return error");
|
return Err(UploadError::ProcessTimeout.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
match state
|
match state
|
||||||
|
@ -95,35 +92,35 @@ pub(crate) async fn generate<S: Store + 'static>(
|
||||||
.with_poll_timer("heartbeat-future")
|
.with_poll_timer("heartbeat-future")
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
state
|
||||||
|
.repo
|
||||||
|
.notify_variant(hash.clone(), variant.clone())
|
||||||
|
.await?;
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(Ok(tuple)) => break tuple,
|
Ok(Ok(tuple)) => break tuple,
|
||||||
Ok(Err(e)) | Err(e) => {
|
Ok(Err(e)) | Err(e) => return Err(e),
|
||||||
state
|
|
||||||
.repo
|
|
||||||
.fail_variant(hash.clone(), variant.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(mut entry) => {
|
||||||
match state
|
let notified = entry.notified_timeout(Duration::from_secs(20));
|
||||||
|
|
||||||
|
if let Some(identifier) = state
|
||||||
.repo
|
.repo
|
||||||
.await_variant(hash.clone(), variant.clone())
|
.variant_identifier(hash.clone(), variant.clone())
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
Some(identifier) => {
|
drop(notified);
|
||||||
let details =
|
let details = crate::ensure_details_identifier(state, &identifier).await?;
|
||||||
crate::ensure_details_identifier(state, &identifier).await?;
|
break (details, identifier);
|
||||||
|
|
||||||
break (details, identifier);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
attempts += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match notified.await {
|
||||||
|
Ok(()) => tracing::debug!("notified"),
|
||||||
|
Err(_) => tracing::warn!("timeout"),
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
50
src/lib.rs
50
src/lib.rs
|
@ -56,7 +56,6 @@ use state::State;
|
||||||
use std::{
|
use std::{
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
path::Path,
|
path::Path,
|
||||||
path::PathBuf,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{Arc, OnceLock},
|
sync::{Arc, OnceLock},
|
||||||
time::{Duration, SystemTime},
|
time::{Duration, SystemTime},
|
||||||
|
@ -774,7 +773,7 @@ fn prepare_process(
|
||||||
config: &Configuration,
|
config: &Configuration,
|
||||||
operations: Vec<(String, String)>,
|
operations: Vec<(String, String)>,
|
||||||
ext: &str,
|
ext: &str,
|
||||||
) -> Result<(InputProcessableFormat, PathBuf, Vec<String>), Error> {
|
) -> Result<(InputProcessableFormat, String, Vec<String>), Error> {
|
||||||
let operations = operations
|
let operations = operations
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(k, _)| config.media.filters.contains(&k.to_lowercase()))
|
.filter(|(k, _)| config.media.filters.contains(&k.to_lowercase()))
|
||||||
|
@ -784,10 +783,9 @@ fn prepare_process(
|
||||||
.parse::<InputProcessableFormat>()
|
.parse::<InputProcessableFormat>()
|
||||||
.map_err(|_| UploadError::UnsupportedProcessExtension)?;
|
.map_err(|_| UploadError::UnsupportedProcessExtension)?;
|
||||||
|
|
||||||
let (thumbnail_path, thumbnail_args) =
|
let (variant, thumbnail_args) = self::processor::build_chain(&operations, &format.to_string())?;
|
||||||
self::processor::build_chain(&operations, &format.to_string())?;
|
|
||||||
|
|
||||||
Ok((format, thumbnail_path, thumbnail_args))
|
Ok((format, variant, thumbnail_args))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(name = "Fetching derived details", skip(state))]
|
#[tracing::instrument(name = "Fetching derived details", skip(state))]
|
||||||
|
@ -798,7 +796,7 @@ async fn process_details<S: Store>(
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let alias = alias_from_query(source.into(), &state).await?;
|
let alias = alias_from_query(source.into(), &state).await?;
|
||||||
|
|
||||||
let (_, thumbnail_path, _) = prepare_process(&state.config, operations, ext.as_str())?;
|
let (_, variant, _) = prepare_process(&state.config, operations, ext.as_str())?;
|
||||||
|
|
||||||
let hash = state
|
let hash = state
|
||||||
.repo
|
.repo
|
||||||
|
@ -806,18 +804,16 @@ async fn process_details<S: Store>(
|
||||||
.await?
|
.await?
|
||||||
.ok_or(UploadError::MissingAlias)?;
|
.ok_or(UploadError::MissingAlias)?;
|
||||||
|
|
||||||
let thumbnail_string = thumbnail_path.to_string_lossy().to_string();
|
|
||||||
|
|
||||||
if !state.config.server.read_only {
|
if !state.config.server.read_only {
|
||||||
state
|
state
|
||||||
.repo
|
.repo
|
||||||
.accessed_variant(hash.clone(), thumbnail_string.clone())
|
.accessed_variant(hash.clone(), variant.clone())
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let identifier = state
|
let identifier = state
|
||||||
.repo
|
.repo
|
||||||
.variant_identifier(hash, thumbnail_string)
|
.variant_identifier(hash, variant)
|
||||||
.await?
|
.await?
|
||||||
.ok_or(UploadError::MissingAlias)?;
|
.ok_or(UploadError::MissingAlias)?;
|
||||||
|
|
||||||
|
@ -856,11 +852,9 @@ async fn process<S: Store + 'static>(
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let alias = proxy_alias_from_query(source.into(), &state).await?;
|
let alias = proxy_alias_from_query(source.into(), &state).await?;
|
||||||
|
|
||||||
let (format, thumbnail_path, thumbnail_args) =
|
let (format, variant, thumbnail_args) =
|
||||||
prepare_process(&state.config, operations, ext.as_str())?;
|
prepare_process(&state.config, operations, ext.as_str())?;
|
||||||
|
|
||||||
let path_string = thumbnail_path.to_string_lossy().to_string();
|
|
||||||
|
|
||||||
let (hash, alias, not_found) = if let Some(hash) = state.repo.hash(&alias).await? {
|
let (hash, alias, not_found) = if let Some(hash) = state.repo.hash(&alias).await? {
|
||||||
(hash, alias, false)
|
(hash, alias, false)
|
||||||
} else {
|
} else {
|
||||||
|
@ -874,13 +868,13 @@ async fn process<S: Store + 'static>(
|
||||||
if !state.config.server.read_only {
|
if !state.config.server.read_only {
|
||||||
state
|
state
|
||||||
.repo
|
.repo
|
||||||
.accessed_variant(hash.clone(), path_string.clone())
|
.accessed_variant(hash.clone(), variant.clone())
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let identifier_opt = state
|
let identifier_opt = state
|
||||||
.repo
|
.repo
|
||||||
.variant_identifier(hash.clone(), path_string)
|
.variant_identifier(hash.clone(), variant.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (details, identifier) = if let Some(identifier) = identifier_opt {
|
let (details, identifier) = if let Some(identifier) = identifier_opt {
|
||||||
|
@ -897,7 +891,7 @@ async fn process<S: Store + 'static>(
|
||||||
generate::generate(
|
generate::generate(
|
||||||
&state,
|
&state,
|
||||||
format,
|
format,
|
||||||
thumbnail_path,
|
variant,
|
||||||
thumbnail_args,
|
thumbnail_args,
|
||||||
&original_details,
|
&original_details,
|
||||||
hash,
|
hash,
|
||||||
|
@ -933,9 +927,8 @@ async fn process_head<S: Store + 'static>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (_, thumbnail_path, _) = prepare_process(&state.config, operations, ext.as_str())?;
|
let (_, variant, _) = prepare_process(&state.config, operations, ext.as_str())?;
|
||||||
|
|
||||||
let path_string = thumbnail_path.to_string_lossy().to_string();
|
|
||||||
let Some(hash) = state.repo.hash(&alias).await? else {
|
let Some(hash) = state.repo.hash(&alias).await? else {
|
||||||
// Invalid alias
|
// Invalid alias
|
||||||
return Ok(HttpResponse::NotFound().finish());
|
return Ok(HttpResponse::NotFound().finish());
|
||||||
|
@ -944,14 +937,11 @@ async fn process_head<S: Store + 'static>(
|
||||||
if !state.config.server.read_only {
|
if !state.config.server.read_only {
|
||||||
state
|
state
|
||||||
.repo
|
.repo
|
||||||
.accessed_variant(hash.clone(), path_string.clone())
|
.accessed_variant(hash.clone(), variant.clone())
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let identifier_opt = state
|
let identifier_opt = state.repo.variant_identifier(hash.clone(), variant).await?;
|
||||||
.repo
|
|
||||||
.variant_identifier(hash.clone(), path_string)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(identifier) = identifier_opt {
|
if let Some(identifier) = identifier_opt {
|
||||||
let details = ensure_details_identifier(&state, &identifier).await?;
|
let details = ensure_details_identifier(&state, &identifier).await?;
|
||||||
|
@ -987,10 +977,9 @@ async fn process_backgrounded<S: Store>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (target_format, process_path, process_args) =
|
let (target_format, variant, process_args) =
|
||||||
prepare_process(&state.config, operations, ext.as_str())?;
|
prepare_process(&state.config, operations, ext.as_str())?;
|
||||||
|
|
||||||
let path_string = process_path.to_string_lossy().to_string();
|
|
||||||
let Some(hash) = state.repo.hash(&source).await? else {
|
let Some(hash) = state.repo.hash(&source).await? else {
|
||||||
// Invalid alias
|
// Invalid alias
|
||||||
return Ok(HttpResponse::BadRequest().finish());
|
return Ok(HttpResponse::BadRequest().finish());
|
||||||
|
@ -998,7 +987,7 @@ async fn process_backgrounded<S: Store>(
|
||||||
|
|
||||||
let identifier_opt = state
|
let identifier_opt = state
|
||||||
.repo
|
.repo
|
||||||
.variant_identifier(hash.clone(), path_string)
|
.variant_identifier(hash.clone(), variant.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if identifier_opt.is_some() {
|
if identifier_opt.is_some() {
|
||||||
|
@ -1009,14 +998,7 @@ async fn process_backgrounded<S: Store>(
|
||||||
return Err(UploadError::ReadOnly.into());
|
return Err(UploadError::ReadOnly.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
queue_generate(
|
queue_generate(&state.repo, target_format, source, variant, process_args).await?;
|
||||||
&state.repo,
|
|
||||||
target_format,
|
|
||||||
source,
|
|
||||||
process_path,
|
|
||||||
process_args,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Accepted().finish())
|
Ok(HttpResponse::Accepted().finish())
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ impl ResizeKind {
|
||||||
pub(crate) fn build_chain(
|
pub(crate) fn build_chain(
|
||||||
args: &[(String, String)],
|
args: &[(String, String)],
|
||||||
ext: &str,
|
ext: &str,
|
||||||
) -> Result<(PathBuf, Vec<String>), Error> {
|
) -> Result<(String, Vec<String>), Error> {
|
||||||
fn parse<P: Processor>(key: &str, value: &str) -> Result<Option<P>, Error> {
|
fn parse<P: Processor>(key: &str, value: &str) -> Result<Option<P>, Error> {
|
||||||
if key == P::NAME {
|
if key == P::NAME {
|
||||||
return Ok(Some(P::parse(key, value).ok_or(UploadError::ParsePath)?));
|
return Ok(Some(P::parse(key, value).ok_or(UploadError::ParsePath)?));
|
||||||
|
@ -122,7 +122,7 @@ pub(crate) fn build_chain(
|
||||||
|
|
||||||
path.push(ext);
|
path.push(ext);
|
||||||
|
|
||||||
Ok((path, args))
|
Ok((path.to_string_lossy().to_string(), args))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Processor for Identity {
|
impl Processor for Identity {
|
||||||
|
|
|
@ -11,7 +11,6 @@ use crate::{
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
path::PathBuf,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
@ -62,7 +61,7 @@ enum Process {
|
||||||
Generate {
|
Generate {
|
||||||
target_format: InputProcessableFormat,
|
target_format: InputProcessableFormat,
|
||||||
source: Serde<Alias>,
|
source: Serde<Alias>,
|
||||||
process_path: PathBuf,
|
process_path: String,
|
||||||
process_args: Vec<String>,
|
process_args: Vec<String>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -177,13 +176,13 @@ pub(crate) async fn queue_generate(
|
||||||
repo: &ArcRepo,
|
repo: &ArcRepo,
|
||||||
target_format: InputProcessableFormat,
|
target_format: InputProcessableFormat,
|
||||||
source: Alias,
|
source: Alias,
|
||||||
process_path: PathBuf,
|
variant: String,
|
||||||
process_args: Vec<String>,
|
process_args: Vec<String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let job = serde_json::to_value(Process::Generate {
|
let job = serde_json::to_value(Process::Generate {
|
||||||
target_format,
|
target_format,
|
||||||
source: Serde::new(source),
|
source: Serde::new(source),
|
||||||
process_path,
|
process_path: variant,
|
||||||
process_args,
|
process_args,
|
||||||
})
|
})
|
||||||
.map_err(UploadError::PushJob)?;
|
.map_err(UploadError::PushJob)?;
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
||||||
store::Store,
|
store::Store,
|
||||||
UploadQuery,
|
UploadQuery,
|
||||||
};
|
};
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{JobContext, JobFuture, JobResult};
|
use super::{JobContext, JobFuture, JobResult};
|
||||||
|
|
||||||
|
@ -172,12 +172,12 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(state, process_path, process_args))]
|
#[tracing::instrument(skip(state, variant, process_args))]
|
||||||
async fn generate<S: Store + 'static>(
|
async fn generate<S: Store + 'static>(
|
||||||
state: &State<S>,
|
state: &State<S>,
|
||||||
target_format: InputProcessableFormat,
|
target_format: InputProcessableFormat,
|
||||||
source: Alias,
|
source: Alias,
|
||||||
process_path: PathBuf,
|
variant: String,
|
||||||
process_args: Vec<String>,
|
process_args: Vec<String>,
|
||||||
) -> JobResult {
|
) -> JobResult {
|
||||||
let hash = state
|
let hash = state
|
||||||
|
@ -188,10 +188,9 @@ async fn generate<S: Store + 'static>(
|
||||||
.ok_or(UploadError::MissingAlias)
|
.ok_or(UploadError::MissingAlias)
|
||||||
.abort()?;
|
.abort()?;
|
||||||
|
|
||||||
let path_string = process_path.to_string_lossy().to_string();
|
|
||||||
let identifier_opt = state
|
let identifier_opt = state
|
||||||
.repo
|
.repo
|
||||||
.variant_identifier(hash.clone(), path_string)
|
.variant_identifier(hash.clone(), variant.clone())
|
||||||
.await
|
.await
|
||||||
.retry()?;
|
.retry()?;
|
||||||
|
|
||||||
|
@ -205,7 +204,7 @@ async fn generate<S: Store + 'static>(
|
||||||
crate::generate::generate(
|
crate::generate::generate(
|
||||||
state,
|
state,
|
||||||
target_format,
|
target_format,
|
||||||
process_path,
|
variant,
|
||||||
process_args,
|
process_args,
|
||||||
&original_details,
|
&original_details,
|
||||||
hash,
|
hash,
|
||||||
|
|
27
src/repo.rs
27
src/repo.rs
|
@ -3,6 +3,7 @@ mod delete_token;
|
||||||
mod hash;
|
mod hash;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
mod migrate;
|
mod migrate;
|
||||||
|
mod notification_map;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config,
|
config,
|
||||||
|
@ -24,6 +25,8 @@ pub(crate) use delete_token::DeleteToken;
|
||||||
pub(crate) use hash::Hash;
|
pub(crate) use hash::Hash;
|
||||||
pub(crate) use migrate::{migrate_04, migrate_repo};
|
pub(crate) use migrate::{migrate_04, migrate_repo};
|
||||||
|
|
||||||
|
use self::notification_map::NotificationEntry;
|
||||||
|
|
||||||
pub(crate) type ArcRepo = Arc<dyn FullRepo>;
|
pub(crate) type ArcRepo = Arc<dyn FullRepo>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -744,17 +747,11 @@ pub(crate) trait VariantRepo: BaseRepo {
|
||||||
&self,
|
&self,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
variant: String,
|
variant: String,
|
||||||
) -> Result<Result<(), VariantAlreadyExists>, RepoError>;
|
) -> Result<Result<(), NotificationEntry>, RepoError>;
|
||||||
|
|
||||||
async fn variant_heartbeat(&self, hash: Hash, variant: String) -> Result<(), RepoError>;
|
async fn variant_heartbeat(&self, hash: Hash, variant: String) -> Result<(), RepoError>;
|
||||||
|
|
||||||
async fn fail_variant(&self, hash: Hash, variant: String) -> Result<(), RepoError>;
|
async fn notify_variant(&self, hash: Hash, variant: String) -> Result<(), RepoError>;
|
||||||
|
|
||||||
async fn await_variant(
|
|
||||||
&self,
|
|
||||||
hash: Hash,
|
|
||||||
variant: String,
|
|
||||||
) -> Result<Option<Arc<str>>, RepoError>;
|
|
||||||
|
|
||||||
async fn relate_variant_identifier(
|
async fn relate_variant_identifier(
|
||||||
&self,
|
&self,
|
||||||
|
@ -783,7 +780,7 @@ where
|
||||||
&self,
|
&self,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
variant: String,
|
variant: String,
|
||||||
) -> Result<Result<(), VariantAlreadyExists>, RepoError> {
|
) -> Result<Result<(), NotificationEntry>, RepoError> {
|
||||||
T::claim_variant_processing_rights(self, hash, variant).await
|
T::claim_variant_processing_rights(self, hash, variant).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -791,16 +788,8 @@ where
|
||||||
T::variant_heartbeat(self, hash, variant).await
|
T::variant_heartbeat(self, hash, variant).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fail_variant(&self, hash: Hash, variant: String) -> Result<(), RepoError> {
|
async fn notify_variant(&self, hash: Hash, variant: String) -> Result<(), RepoError> {
|
||||||
T::fail_variant(self, hash, variant).await
|
T::notify_variant(self, hash, variant).await
|
||||||
}
|
|
||||||
|
|
||||||
async fn await_variant(
|
|
||||||
&self,
|
|
||||||
hash: Hash,
|
|
||||||
variant: String,
|
|
||||||
) -> Result<Option<Arc<str>>, RepoError> {
|
|
||||||
T::await_variant(self, hash, variant).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn relate_variant_identifier(
|
async fn relate_variant_identifier(
|
||||||
|
|
94
src/repo/notification_map.rs
Normal file
94
src/repo/notification_map.rs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
use dashmap::DashMap;
|
||||||
|
use std::{
|
||||||
|
future::Future,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc, Weak,
|
||||||
|
},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
use tokio::sync::Notify;
|
||||||
|
|
||||||
|
use crate::future::WithTimeout;
|
||||||
|
|
||||||
|
type Map = Arc<DashMap<Arc<str>, Weak<NotificationEntryInner>>>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(super) struct NotificationMap {
|
||||||
|
map: Map,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct NotificationEntry {
|
||||||
|
inner: Arc<NotificationEntryInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NotificationEntryInner {
|
||||||
|
key: Arc<str>,
|
||||||
|
map: Map,
|
||||||
|
notify: Notify,
|
||||||
|
armed: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NotificationMap {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
map: Arc::new(DashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn register_interest(&self, key: Arc<str>) -> NotificationEntry {
|
||||||
|
let new_entry = Arc::new(NotificationEntryInner {
|
||||||
|
key: key.clone(),
|
||||||
|
map: self.map.clone(),
|
||||||
|
notify: crate::sync::bare_notify(),
|
||||||
|
armed: AtomicBool::new(false),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut key_entry = self
|
||||||
|
.map
|
||||||
|
.entry(key)
|
||||||
|
.or_insert_with(|| Arc::downgrade(&new_entry));
|
||||||
|
|
||||||
|
let upgraded_entry = key_entry.value().upgrade();
|
||||||
|
|
||||||
|
let inner = if let Some(entry) = upgraded_entry {
|
||||||
|
entry
|
||||||
|
} else {
|
||||||
|
*key_entry.value_mut() = Arc::downgrade(&new_entry);
|
||||||
|
new_entry
|
||||||
|
};
|
||||||
|
|
||||||
|
inner.armed.store(true, Ordering::Release);
|
||||||
|
|
||||||
|
NotificationEntry { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn notify(&self, key: &str) {
|
||||||
|
if let Some(notifier) = self.map.get(key).and_then(|v| v.upgrade()) {
|
||||||
|
notifier.notify.notify_waiters();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NotificationEntry {
|
||||||
|
pub(crate) fn notified_timeout(
|
||||||
|
&mut self,
|
||||||
|
duration: Duration,
|
||||||
|
) -> impl Future<Output = Result<(), tokio::time::error::Elapsed>> + '_ {
|
||||||
|
self.inner.notify.notified().with_timeout(duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NotificationMap {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for NotificationEntryInner {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.armed.load(Ordering::Acquire) {
|
||||||
|
self.map.remove(&self.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,6 @@ use diesel_async::{
|
||||||
bb8::{Pool, PooledConnection, RunError},
|
bb8::{Pool, PooledConnection, RunError},
|
||||||
AsyncDieselConnectionManager, ManagerConfig, PoolError,
|
AsyncDieselConnectionManager, ManagerConfig, PoolError,
|
||||||
},
|
},
|
||||||
scoped_futures::ScopedFutureExt,
|
|
||||||
AsyncConnection, AsyncPgConnection, RunQueryDsl,
|
AsyncConnection, AsyncPgConnection, RunQueryDsl,
|
||||||
};
|
};
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
|
@ -45,6 +44,7 @@ use self::job_status::JobStatus;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
metrics::{PopMetricsGuard, PushMetricsGuard, WaitMetricsGuard},
|
metrics::{PopMetricsGuard, PushMetricsGuard, WaitMetricsGuard},
|
||||||
|
notification_map::{NotificationEntry, NotificationMap},
|
||||||
Alias, AliasAccessRepo, AliasAlreadyExists, AliasRepo, BaseRepo, DeleteToken, DetailsRepo,
|
Alias, AliasAccessRepo, AliasAlreadyExists, AliasRepo, BaseRepo, DeleteToken, DetailsRepo,
|
||||||
FullRepo, Hash, HashAlreadyExists, HashPage, HashRepo, JobId, JobResult, OrderedHash,
|
FullRepo, Hash, HashAlreadyExists, HashPage, HashRepo, JobId, JobResult, OrderedHash,
|
||||||
ProxyRepo, QueueRepo, RepoError, SettingsRepo, StoreMigrationRepo, UploadId, UploadRepo,
|
ProxyRepo, QueueRepo, RepoError, SettingsRepo, StoreMigrationRepo, UploadId, UploadRepo,
|
||||||
|
@ -64,32 +64,7 @@ struct Inner {
|
||||||
notifier_pool: Pool<AsyncPgConnection>,
|
notifier_pool: Pool<AsyncPgConnection>,
|
||||||
queue_notifications: DashMap<String, Arc<Notify>>,
|
queue_notifications: DashMap<String, Arc<Notify>>,
|
||||||
upload_notifications: DashMap<UploadId, Weak<Notify>>,
|
upload_notifications: DashMap<UploadId, Weak<Notify>>,
|
||||||
keyed_notifications: DashMap<Arc<str>, Weak<NotificationEntry>>,
|
keyed_notifications: NotificationMap,
|
||||||
}
|
|
||||||
|
|
||||||
struct NotificationEntry {
|
|
||||||
key: Arc<str>,
|
|
||||||
inner: Arc<Inner>,
|
|
||||||
notify: Notify,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for NotificationEntry {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.inner.keyed_notifications.remove(self.key.as_ref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct KeyListener {
|
|
||||||
entry: Arc<NotificationEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyListener {
|
|
||||||
fn notified_timeout(
|
|
||||||
&self,
|
|
||||||
timeout: Duration,
|
|
||||||
) -> impl Future<Output = Result<(), tokio::time::error::Elapsed>> + '_ {
|
|
||||||
self.entry.notify.notified().with_timeout(timeout)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UploadInterest {
|
struct UploadInterest {
|
||||||
|
@ -363,7 +338,7 @@ impl PostgresRepo {
|
||||||
notifier_pool,
|
notifier_pool,
|
||||||
queue_notifications: DashMap::new(),
|
queue_notifications: DashMap::new(),
|
||||||
upload_notifications: DashMap::new(),
|
upload_notifications: DashMap::new(),
|
||||||
keyed_notifications: DashMap::new(),
|
keyed_notifications: NotificationMap::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let handle = crate::sync::abort_on_drop(crate::sync::spawn_sendable(
|
let handle = crate::sync::abort_on_drop(crate::sync::spawn_sendable(
|
||||||
|
@ -451,29 +426,8 @@ impl PostgresRepo {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn listen_on_key(&self, key: Arc<str>) -> KeyListener {
|
fn listen_on_key(&self, key: Arc<str>) -> NotificationEntry {
|
||||||
let new_entry = Arc::new(NotificationEntry {
|
self.inner.keyed_notifications.register_interest(key)
|
||||||
key: key.clone(),
|
|
||||||
inner: Arc::clone(&self.inner),
|
|
||||||
notify: crate::sync::bare_notify(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut entry = self
|
|
||||||
.inner
|
|
||||||
.keyed_notifications
|
|
||||||
.entry(key)
|
|
||||||
.or_insert_with(|| Arc::downgrade(&new_entry));
|
|
||||||
|
|
||||||
let upgraded = entry.value().upgrade();
|
|
||||||
|
|
||||||
let entry = if let Some(existing_entry) = upgraded {
|
|
||||||
existing_entry
|
|
||||||
} else {
|
|
||||||
*entry.value_mut() = Arc::downgrade(&new_entry);
|
|
||||||
new_entry
|
|
||||||
};
|
|
||||||
|
|
||||||
KeyListener { entry }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn register_interest(&self) -> Result<(), PostgresError> {
|
async fn register_interest(&self) -> Result<(), PostgresError> {
|
||||||
|
@ -658,14 +612,7 @@ impl<'a> UploadNotifierState<'a> {
|
||||||
|
|
||||||
impl<'a> KeyedNotifierState<'a> {
|
impl<'a> KeyedNotifierState<'a> {
|
||||||
fn handle(&self, key: &str) {
|
fn handle(&self, key: &str) {
|
||||||
if let Some(notification_entry) = self
|
self.inner.keyed_notifications.notify(key);
|
||||||
.inner
|
|
||||||
.keyed_notifications
|
|
||||||
.get(key)
|
|
||||||
.and_then(|weak| weak.upgrade())
|
|
||||||
{
|
|
||||||
notification_entry.notify.notify_waiters();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1150,20 +1097,23 @@ impl VariantRepo for PostgresRepo {
|
||||||
&self,
|
&self,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
variant: String,
|
variant: String,
|
||||||
) -> Result<Result<(), VariantAlreadyExists>, RepoError> {
|
) -> Result<Result<(), NotificationEntry>, RepoError> {
|
||||||
|
let key = Arc::from(format!("{}{variant}", hash.to_base64()));
|
||||||
|
let entry = self.listen_on_key(Arc::clone(&key));
|
||||||
|
|
||||||
|
self.register_interest().await?;
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.variant_identifier(hash.clone(), variant.clone())
|
.variant_identifier(hash.clone(), variant.clone())
|
||||||
.await?
|
.await?
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
return Ok(Err(VariantAlreadyExists));
|
return Ok(Err(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = format!("{}{variant}", hash.to_base64());
|
|
||||||
|
|
||||||
match self.insert_keyed_notifier(&key).await? {
|
match self.insert_keyed_notifier(&key).await? {
|
||||||
Ok(()) => Ok(Ok(())),
|
Ok(()) => Ok(Ok(())),
|
||||||
Err(AlreadyInserted) => Ok(Err(VariantAlreadyExists)),
|
Err(AlreadyInserted) => Ok(Err(entry)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1177,40 +1127,12 @@ impl VariantRepo for PostgresRepo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
async fn fail_variant(&self, hash: Hash, variant: String) -> Result<(), RepoError> {
|
async fn notify_variant(&self, hash: Hash, variant: String) -> Result<(), RepoError> {
|
||||||
let key = format!("{}{variant}", hash.to_base64());
|
let key = format!("{}{variant}", hash.to_base64());
|
||||||
|
|
||||||
self.clear_keyed_notifier(key).await.map_err(Into::into)
|
self.clear_keyed_notifier(key).await.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
|
||||||
async fn await_variant(
|
|
||||||
&self,
|
|
||||||
hash: Hash,
|
|
||||||
variant: String,
|
|
||||||
) -> Result<Option<Arc<str>>, RepoError> {
|
|
||||||
let key = Arc::from(format!("{}{variant}", hash.to_base64()));
|
|
||||||
|
|
||||||
let listener = self.listen_on_key(key);
|
|
||||||
let notified = listener.notified_timeout(Duration::from_secs(5));
|
|
||||||
|
|
||||||
self.register_interest().await?;
|
|
||||||
|
|
||||||
if let Some(identifier) = self
|
|
||||||
.variant_identifier(hash.clone(), variant.clone())
|
|
||||||
.await?
|
|
||||||
{
|
|
||||||
return Ok(Some(identifier));
|
|
||||||
}
|
|
||||||
|
|
||||||
match notified.await {
|
|
||||||
Ok(()) => tracing::debug!("notified"),
|
|
||||||
Err(_) => tracing::trace!("timeout"),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.variant_identifier(hash, variant).await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
async fn relate_variant_identifier(
|
async fn relate_variant_identifier(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1218,60 +1140,30 @@ impl VariantRepo for PostgresRepo {
|
||||||
input_variant: String,
|
input_variant: String,
|
||||||
input_identifier: &Arc<str>,
|
input_identifier: &Arc<str>,
|
||||||
) -> Result<Result<(), VariantAlreadyExists>, RepoError> {
|
) -> Result<Result<(), VariantAlreadyExists>, RepoError> {
|
||||||
|
use schema::variants::dsl::*;
|
||||||
|
|
||||||
let mut conn = self.get_connection().await?;
|
let mut conn = self.get_connection().await?;
|
||||||
|
|
||||||
conn.transaction(|conn| {
|
let res = diesel::insert_into(variants)
|
||||||
async move {
|
.values((
|
||||||
let res = async {
|
hash.eq(&input_hash),
|
||||||
use schema::variants::dsl::*;
|
variant.eq(&input_variant),
|
||||||
|
identifier.eq(input_identifier.to_string()),
|
||||||
|
))
|
||||||
|
.execute(&mut conn)
|
||||||
|
.with_metrics(crate::init_metrics::POSTGRES_VARIANTS_RELATE_VARIANT_IDENTIFIER)
|
||||||
|
.with_timeout(Duration::from_secs(5))
|
||||||
|
.await
|
||||||
|
.map_err(|_| PostgresError::DbTimeout)?;
|
||||||
|
|
||||||
diesel::insert_into(variants)
|
match res {
|
||||||
.values((
|
Ok(_) => Ok(Ok(())),
|
||||||
hash.eq(&input_hash),
|
Err(diesel::result::Error::DatabaseError(
|
||||||
variant.eq(&input_variant),
|
diesel::result::DatabaseErrorKind::UniqueViolation,
|
||||||
identifier.eq(input_identifier.to_string()),
|
_,
|
||||||
))
|
)) => Ok(Err(VariantAlreadyExists)),
|
||||||
.execute(conn)
|
Err(e) => Err(PostgresError::Diesel(e).into()),
|
||||||
.with_metrics(
|
}
|
||||||
crate::init_metrics::POSTGRES_VARIANTS_RELATE_VARIANT_IDENTIFIER,
|
|
||||||
)
|
|
||||||
.with_timeout(Duration::from_secs(5))
|
|
||||||
.await
|
|
||||||
.map_err(|_| PostgresError::DbTimeout)
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let notification_res = async {
|
|
||||||
use schema::keyed_notifications::dsl::*;
|
|
||||||
|
|
||||||
let input_key = format!("{}{input_variant}", input_hash.to_base64());
|
|
||||||
diesel::delete(keyed_notifications)
|
|
||||||
.filter(key.eq(input_key))
|
|
||||||
.execute(conn)
|
|
||||||
.with_timeout(Duration::from_secs(5))
|
|
||||||
.await
|
|
||||||
.map_err(|_| PostgresError::DbTimeout)
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match notification_res? {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => tracing::warn!("Failed to clear notifier: {e}"),
|
|
||||||
}
|
|
||||||
|
|
||||||
match res? {
|
|
||||||
Ok(_) => Ok(Ok(())),
|
|
||||||
Err(diesel::result::Error::DatabaseError(
|
|
||||||
diesel::result::DatabaseErrorKind::UniqueViolation,
|
|
||||||
_,
|
|
||||||
)) => Ok(Err(VariantAlreadyExists)),
|
|
||||||
Err(e) => Err(PostgresError::Diesel(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.scope_boxed()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.map_err(PostgresError::into)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
serde_str::Serde,
|
serde_str::Serde,
|
||||||
stream::{from_iterator, LocalBoxStream},
|
stream::{from_iterator, LocalBoxStream},
|
||||||
};
|
};
|
||||||
|
use dashmap::DashMap;
|
||||||
use sled::{transaction::TransactionError, Db, IVec, Transactional, Tree};
|
use sled::{transaction::TransactionError, Db, IVec, Transactional, Tree};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -22,6 +23,7 @@ use uuid::Uuid;
|
||||||
use super::{
|
use super::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
metrics::{PopMetricsGuard, PushMetricsGuard, WaitMetricsGuard},
|
metrics::{PopMetricsGuard, PushMetricsGuard, WaitMetricsGuard},
|
||||||
|
notification_map::{NotificationEntry, NotificationMap},
|
||||||
Alias, AliasAccessRepo, AliasAlreadyExists, AliasRepo, BaseRepo, DeleteToken, Details,
|
Alias, AliasAccessRepo, AliasAlreadyExists, AliasRepo, BaseRepo, DeleteToken, Details,
|
||||||
DetailsRepo, FullRepo, HashAlreadyExists, HashPage, HashRepo, JobId, JobResult, OrderedHash,
|
DetailsRepo, FullRepo, HashAlreadyExists, HashPage, HashRepo, JobId, JobResult, OrderedHash,
|
||||||
ProxyRepo, QueueRepo, RepoError, SettingsRepo, StoreMigrationRepo, UploadId, UploadRepo,
|
ProxyRepo, QueueRepo, RepoError, SettingsRepo, StoreMigrationRepo, UploadId, UploadRepo,
|
||||||
|
@ -113,6 +115,8 @@ pub(crate) struct SledRepo {
|
||||||
migration_identifiers: Tree,
|
migration_identifiers: Tree,
|
||||||
cache_capacity: u64,
|
cache_capacity: u64,
|
||||||
export_path: PathBuf,
|
export_path: PathBuf,
|
||||||
|
variant_process_map: DashMap<(Hash, String), time::OffsetDateTime>,
|
||||||
|
notifications: NotificationMap,
|
||||||
db: Db,
|
db: Db,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +160,8 @@ impl SledRepo {
|
||||||
migration_identifiers: db.open_tree("pict-rs-migration-identifiers-tree")?,
|
migration_identifiers: db.open_tree("pict-rs-migration-identifiers-tree")?,
|
||||||
cache_capacity,
|
cache_capacity,
|
||||||
export_path,
|
export_path,
|
||||||
|
variant_process_map: DashMap::new(),
|
||||||
|
notifications: NotificationMap::new(),
|
||||||
db,
|
db,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1453,27 +1459,61 @@ impl VariantRepo for SledRepo {
|
||||||
&self,
|
&self,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
variant: String,
|
variant: String,
|
||||||
) -> Result<Result<(), VariantAlreadyExists>, RepoError> {
|
) -> Result<Result<(), NotificationEntry>, RepoError> {
|
||||||
todo!()
|
let key = (hash.clone(), variant.clone());
|
||||||
|
let now = time::OffsetDateTime::now_utc();
|
||||||
|
let entry = self
|
||||||
|
.notifications
|
||||||
|
.register_interest(Arc::from(format!("{}{variant}", hash.to_base64())));
|
||||||
|
|
||||||
|
match self.variant_process_map.entry(key.clone()) {
|
||||||
|
dashmap::mapref::entry::Entry::Occupied(mut occupied_entry) => {
|
||||||
|
if occupied_entry
|
||||||
|
.get()
|
||||||
|
.saturating_add(time::Duration::minutes(2))
|
||||||
|
> now
|
||||||
|
{
|
||||||
|
return Ok(Err(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
occupied_entry.insert(now);
|
||||||
|
}
|
||||||
|
dashmap::mapref::entry::Entry::Vacant(vacant_entry) => {
|
||||||
|
vacant_entry.insert(now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.variant_identifier(hash, variant).await?.is_some() {
|
||||||
|
self.variant_process_map.remove(&key);
|
||||||
|
return Ok(Err(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
async fn variant_heartbeat(&self, hash: Hash, variant: String) -> Result<(), RepoError> {
|
async fn variant_heartbeat(&self, hash: Hash, variant: String) -> Result<(), RepoError> {
|
||||||
todo!()
|
let key = (hash, variant);
|
||||||
|
let now = time::OffsetDateTime::now_utc();
|
||||||
|
|
||||||
|
if let dashmap::mapref::entry::Entry::Occupied(mut occupied_entry) =
|
||||||
|
self.variant_process_map.entry(key)
|
||||||
|
{
|
||||||
|
occupied_entry.insert(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
async fn fail_variant(&self, hash: Hash, variant: String) -> Result<(), RepoError> {
|
async fn notify_variant(&self, hash: Hash, variant: String) -> Result<(), RepoError> {
|
||||||
todo!()
|
let key = (hash.clone(), variant.clone());
|
||||||
}
|
self.variant_process_map.remove(&key);
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
let key = format!("{}{variant}", hash.to_base64());
|
||||||
async fn await_variant(
|
self.notifications.notify(&key);
|
||||||
&self,
|
|
||||||
hash: Hash,
|
Ok(())
|
||||||
variant: String,
|
|
||||||
) -> Result<Option<Arc<str>>, RepoError> {
|
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
|
@ -1490,7 +1530,7 @@ impl VariantRepo for SledRepo {
|
||||||
|
|
||||||
let hash_variant_identifiers = self.hash_variant_identifiers.clone();
|
let hash_variant_identifiers = self.hash_variant_identifiers.clone();
|
||||||
|
|
||||||
crate::sync::spawn_blocking("sled-io", move || {
|
let out = crate::sync::spawn_blocking("sled-io", move || {
|
||||||
hash_variant_identifiers
|
hash_variant_identifiers
|
||||||
.compare_and_swap(key, Option::<&[u8]>::None, Some(value.as_bytes()))
|
.compare_and_swap(key, Option::<&[u8]>::None, Some(value.as_bytes()))
|
||||||
.map(|res| res.map_err(|_| VariantAlreadyExists))
|
.map(|res| res.map_err(|_| VariantAlreadyExists))
|
||||||
|
@ -1498,7 +1538,9 @@ impl VariantRepo for SledRepo {
|
||||||
.await
|
.await
|
||||||
.map_err(|_| RepoError::Canceled)?
|
.map_err(|_| RepoError::Canceled)?
|
||||||
.map_err(SledError::from)
|
.map_err(SledError::from)
|
||||||
.map_err(RepoError::from)
|
.map_err(RepoError::from)?;
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip(self))]
|
#[tracing::instrument(level = "trace", skip(self))]
|
||||||
|
|
Loading…
Reference in a new issue