mirror of
https://git.asonix.dog/asonix/pict-rs
synced 2024-12-22 03:11:24 +00:00
Better handle concurrent proxies
This commit is contained in:
parent
80af2b67b0
commit
9af7e01b01
4 changed files with 73 additions and 21 deletions
28
src/lib.rs
28
src/lib.rs
|
@ -74,7 +74,10 @@ use self::{
|
||||||
middleware::{Deadline, Internal, Log, Metrics, Payload},
|
middleware::{Deadline, Internal, Log, Metrics, Payload},
|
||||||
migrate_store::migrate_store,
|
migrate_store::migrate_store,
|
||||||
queue::queue_generate,
|
queue::queue_generate,
|
||||||
repo::{sled::SledRepo, Alias, ArcRepo, DeleteToken, Hash, Repo, UploadId, UploadResult},
|
repo::{
|
||||||
|
sled::SledRepo, Alias, ArcRepo, DeleteToken, Hash, ProxyAlreadyExists, Repo, UploadId,
|
||||||
|
UploadResult,
|
||||||
|
},
|
||||||
serde_str::Serde,
|
serde_str::Serde,
|
||||||
state::State,
|
state::State,
|
||||||
store::{file_store::FileStore, object_store::ObjectStore, Store},
|
store::{file_store::FileStore, object_store::ObjectStore, Store},
|
||||||
|
@ -1286,11 +1289,28 @@ async fn proxy_alias_from_query<S: Store + 'static>(
|
||||||
} else if !state.config.server.read_only {
|
} else if !state.config.server.read_only {
|
||||||
let stream = download_stream(proxy.as_str(), state).await?;
|
let stream = download_stream(proxy.as_str(), state).await?;
|
||||||
|
|
||||||
let (alias, _, _) = ingest_inline(stream, state, &Default::default()).await?;
|
// some time has passed, see if we've proxied elsewhere
|
||||||
|
if let Some(alias) = state.repo.related(proxy.clone()).await? {
|
||||||
|
alias
|
||||||
|
} else {
|
||||||
|
let (alias, token, _) =
|
||||||
|
ingest_inline(stream, state, &Default::default()).await?;
|
||||||
|
|
||||||
state.repo.relate_url(proxy, alias.clone()).await?;
|
// last check, do we succeed or fail to relate the proxy alias
|
||||||
|
if let Err(ProxyAlreadyExists) =
|
||||||
|
state.repo.relate_url(proxy.clone(), alias.clone()).await?
|
||||||
|
{
|
||||||
|
queue::cleanup_alias(&state.repo, alias, token).await?;
|
||||||
|
|
||||||
alias
|
state
|
||||||
|
.repo
|
||||||
|
.related(proxy)
|
||||||
|
.await?
|
||||||
|
.ok_or(UploadError::MissingAlias)?
|
||||||
|
} else {
|
||||||
|
alias
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(UploadError::ReadOnly.into());
|
return Err(UploadError::ReadOnly.into());
|
||||||
};
|
};
|
||||||
|
|
14
src/repo.rs
14
src/repo.rs
|
@ -46,6 +46,8 @@ pub(crate) struct HashAlreadyExists;
|
||||||
pub(crate) struct AliasAlreadyExists;
|
pub(crate) struct AliasAlreadyExists;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct VariantAlreadyExists;
|
pub(crate) struct VariantAlreadyExists;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ProxyAlreadyExists;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub(crate) struct UploadId {
|
pub(crate) struct UploadId {
|
||||||
|
@ -151,7 +153,11 @@ impl<T> BaseRepo for Arc<T> where T: BaseRepo {}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
pub(crate) trait ProxyRepo: BaseRepo {
|
pub(crate) trait ProxyRepo: BaseRepo {
|
||||||
async fn relate_url(&self, url: Url, alias: Alias) -> Result<(), RepoError>;
|
async fn relate_url(
|
||||||
|
&self,
|
||||||
|
url: Url,
|
||||||
|
alias: Alias,
|
||||||
|
) -> Result<Result<(), ProxyAlreadyExists>, RepoError>;
|
||||||
|
|
||||||
async fn related(&self, url: Url) -> Result<Option<Alias>, RepoError>;
|
async fn related(&self, url: Url) -> Result<Option<Alias>, RepoError>;
|
||||||
|
|
||||||
|
@ -163,7 +169,11 @@ impl<T> ProxyRepo for Arc<T>
|
||||||
where
|
where
|
||||||
T: ProxyRepo,
|
T: ProxyRepo,
|
||||||
{
|
{
|
||||||
async fn relate_url(&self, url: Url, alias: Alias) -> Result<(), RepoError> {
|
async fn relate_url(
|
||||||
|
&self,
|
||||||
|
url: Url,
|
||||||
|
alias: Alias,
|
||||||
|
) -> Result<Result<(), ProxyAlreadyExists>, RepoError> {
|
||||||
T::relate_url(self, url, alias).await
|
T::relate_url(self, url, alias).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,8 @@ use super::{
|
||||||
notification_map::{NotificationEntry, NotificationMap},
|
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,
|
ProxyAlreadyExists, ProxyRepo, QueueRepo, RepoError, SettingsRepo, StoreMigrationRepo,
|
||||||
UploadResult, VariantAccessRepo, VariantAlreadyExists, VariantRepo,
|
UploadId, UploadRepo, UploadResult, VariantAccessRepo, VariantAlreadyExists, VariantRepo,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -1884,21 +1884,31 @@ impl StoreMigrationRepo for PostgresRepo {
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ProxyRepo for PostgresRepo {
|
impl ProxyRepo for PostgresRepo {
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
async fn relate_url(&self, input_url: Url, input_alias: Alias) -> Result<(), RepoError> {
|
async fn relate_url(
|
||||||
|
&self,
|
||||||
|
input_url: Url,
|
||||||
|
input_alias: Alias,
|
||||||
|
) -> Result<Result<(), ProxyAlreadyExists>, RepoError> {
|
||||||
use schema::proxies::dsl::*;
|
use schema::proxies::dsl::*;
|
||||||
|
|
||||||
let mut conn = self.get_connection().await?;
|
let mut conn = self.get_connection().await?;
|
||||||
|
|
||||||
diesel::insert_into(proxies)
|
let res = diesel::insert_into(proxies)
|
||||||
.values((url.eq(input_url.as_str()), alias.eq(&input_alias)))
|
.values((url.eq(input_url.as_str()), alias.eq(&input_alias)))
|
||||||
.execute(&mut conn)
|
.execute(&mut conn)
|
||||||
.with_metrics(crate::init_metrics::POSTGRES_PROXY_RELATE_URL)
|
.with_metrics(crate::init_metrics::POSTGRES_PROXY_RELATE_URL)
|
||||||
.with_timeout(Duration::from_secs(5))
|
.with_timeout(Duration::from_secs(5))
|
||||||
.await
|
.await
|
||||||
.map_err(|_| PostgresError::DbTimeout)?
|
.map_err(|_| PostgresError::DbTimeout)?;
|
||||||
.map_err(PostgresError::Diesel)?;
|
|
||||||
|
|
||||||
Ok(())
|
match res {
|
||||||
|
Ok(_) => Ok(Ok(())),
|
||||||
|
Err(diesel::result::Error::DatabaseError(
|
||||||
|
diesel::result::DatabaseErrorKind::UniqueViolation,
|
||||||
|
_,
|
||||||
|
)) => Ok(Err(ProxyAlreadyExists)),
|
||||||
|
Err(e) => Err(PostgresError::Diesel(e).into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
|
|
|
@ -26,8 +26,8 @@ use super::{
|
||||||
notification_map::{NotificationEntry, NotificationMap},
|
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,
|
ProxyAlreadyExists, ProxyRepo, QueueRepo, RepoError, SettingsRepo, StoreMigrationRepo,
|
||||||
UploadResult, VariantAccessRepo, VariantAlreadyExists, VariantRepo,
|
UploadId, UploadRepo, UploadResult, VariantAccessRepo, VariantAlreadyExists, VariantRepo,
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! b {
|
macro_rules! b {
|
||||||
|
@ -218,20 +218,32 @@ impl FullRepo for SledRepo {
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl ProxyRepo for SledRepo {
|
impl ProxyRepo for SledRepo {
|
||||||
async fn relate_url(&self, url: Url, alias: Alias) -> Result<(), RepoError> {
|
async fn relate_url(
|
||||||
|
&self,
|
||||||
|
url: Url,
|
||||||
|
alias: Alias,
|
||||||
|
) -> Result<Result<(), ProxyAlreadyExists>, RepoError> {
|
||||||
let proxy = self.proxy.clone();
|
let proxy = self.proxy.clone();
|
||||||
let inverse_proxy = self.inverse_proxy.clone();
|
let inverse_proxy = self.inverse_proxy.clone();
|
||||||
|
|
||||||
crate::sync::spawn_blocking("sled-io", move || {
|
let res = crate::sync::spawn_blocking("sled-io", move || {
|
||||||
proxy.insert(url.as_str().as_bytes(), alias.to_bytes())?;
|
match proxy.compare_and_swap(
|
||||||
inverse_proxy.insert(alias.to_bytes(), url.as_str().as_bytes())?;
|
url.as_str().as_bytes(),
|
||||||
|
Option::<sled::IVec>::None,
|
||||||
|
Some(alias.to_bytes()),
|
||||||
|
)? {
|
||||||
|
Ok(_) => {
|
||||||
|
inverse_proxy.insert(alias.to_bytes(), url.as_str().as_bytes())?;
|
||||||
|
|
||||||
Ok(()) as Result<(), SledError>
|
Ok(Ok(())) as Result<Result<(), ProxyAlreadyExists>, SledError>
|
||||||
|
}
|
||||||
|
Err(_) => Ok(Err(ProxyAlreadyExists)),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|_| RepoError::Canceled)??;
|
.map_err(|_| RepoError::Canceled)??;
|
||||||
|
|
||||||
Ok(())
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn related(&self, url: Url) -> Result<Option<Alias>, RepoError> {
|
async fn related(&self, url: Url) -> Result<Option<Alias>, RepoError> {
|
||||||
|
|
Loading…
Reference in a new issue