mirror of
https://git.asonix.dog/asonix/pict-rs.git
synced 2025-01-06 01:28:45 +00:00
More streme
This commit is contained in:
parent
1b97ac1c5a
commit
b2674f06d0
5 changed files with 92 additions and 174 deletions
|
@ -107,7 +107,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hashes are read in a consistent order
|
// Hashes are read in a consistent order
|
||||||
let mut stream = repo.hashes().into_streamer();
|
let stream = std::pin::pin!(repo.hashes());
|
||||||
|
let mut stream = stream.into_streamer();
|
||||||
|
|
||||||
let state = Rc::new(MigrateState {
|
let state = Rc::new(MigrateState {
|
||||||
repo: repo.clone(),
|
repo: repo.clone(),
|
||||||
|
|
|
@ -138,7 +138,8 @@ async fn alias(repo: &ArcRepo, alias: Alias, token: DeleteToken) -> Result<(), E
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn all_variants(repo: &ArcRepo) -> Result<(), Error> {
|
async fn all_variants(repo: &ArcRepo) -> Result<(), Error> {
|
||||||
let mut hash_stream = repo.hashes().into_streamer();
|
let hash_stream = std::pin::pin!(repo.hashes());
|
||||||
|
let mut hash_stream = hash_stream.into_streamer();
|
||||||
|
|
||||||
while let Some(res) = hash_stream.next().await {
|
while let Some(res) = hash_stream.next().await {
|
||||||
let hash = res?;
|
let hash = res?;
|
||||||
|
|
82
src/repo.rs
82
src/repo.rs
|
@ -8,10 +8,10 @@ use crate::{
|
||||||
config,
|
config,
|
||||||
details::Details,
|
details::Details,
|
||||||
error_code::{ErrorCode, OwnedErrorCode},
|
error_code::{ErrorCode, OwnedErrorCode},
|
||||||
future::LocalBoxFuture,
|
|
||||||
stream::LocalBoxStream,
|
stream::LocalBoxStream,
|
||||||
};
|
};
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
|
use futures_core::Stream;
|
||||||
use std::{fmt::Debug, sync::Arc};
|
use std::{fmt::Debug, sync::Arc};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -572,81 +572,29 @@ impl HashPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageFuture = LocalBoxFuture<'static, Result<HashPage, RepoError>>;
|
impl dyn FullRepo {
|
||||||
|
pub(crate) fn hashes(self: &Arc<Self>) -> impl Stream<Item = Result<Hash, RepoError>> {
|
||||||
|
let repo = self.clone();
|
||||||
|
|
||||||
pub(crate) struct HashStream {
|
streem::try_from_fn(|yielder| async move {
|
||||||
repo: Option<ArcRepo>,
|
let mut slug = None;
|
||||||
page_future: Option<PageFuture>,
|
|
||||||
page: Option<HashPage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl futures_core::Stream for HashStream {
|
|
||||||
type Item = Result<Hash, RepoError>;
|
|
||||||
|
|
||||||
fn poll_next(
|
|
||||||
self: std::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut std::task::Context<'_>,
|
|
||||||
) -> std::task::Poll<Option<Self::Item>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let Some(repo) = &this.repo else {
|
let page = repo.hash_page(slug, 100).await?;
|
||||||
return std::task::Poll::Ready(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
let slug = if let Some(page) = &mut this.page {
|
slug = page.next();
|
||||||
// popping last in page is fine - we reversed them
|
|
||||||
if let Some(hash) = page.hashes.pop() {
|
for hash in page.hashes {
|
||||||
return std::task::Poll::Ready(Some(Ok(hash)));
|
yielder.yield_ok(hash).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let slug = page.next();
|
if slug.is_none() {
|
||||||
this.page.take();
|
break;
|
||||||
|
|
||||||
if let Some(slug) = slug {
|
|
||||||
Some(slug)
|
|
||||||
} else {
|
|
||||||
this.repo.take();
|
|
||||||
return std::task::Poll::Ready(None);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(page_future) = &mut this.page_future {
|
|
||||||
let res = std::task::ready!(page_future.as_mut().poll(cx));
|
|
||||||
|
|
||||||
this.page_future.take();
|
|
||||||
|
|
||||||
match res {
|
|
||||||
Ok(mut page) => {
|
|
||||||
// reverse because we use `pop` to fetch next
|
|
||||||
page.hashes.reverse();
|
|
||||||
|
|
||||||
this.page = Some(page);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
this.repo.take();
|
|
||||||
|
|
||||||
return std::task::Poll::Ready(Some(Err(e)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let repo = repo.clone();
|
|
||||||
|
|
||||||
this.page_future = Some(Box::pin(async move { repo.hash_page(slug, 100).await }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl dyn FullRepo {
|
Ok(())
|
||||||
pub(crate) fn hashes(self: &Arc<Self>) -> HashStream {
|
})
|
||||||
HashStream {
|
|
||||||
repo: Some(self.clone()),
|
|
||||||
page_future: None,
|
|
||||||
page: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ pub(crate) async fn migrate_repo(old_repo: ArcRepo, new_repo: ArcRepo) -> Result
|
||||||
tracing::warn!("Checks complete, migrating repo");
|
tracing::warn!("Checks complete, migrating repo");
|
||||||
tracing::warn!("{total_size} hashes will be migrated");
|
tracing::warn!("{total_size} hashes will be migrated");
|
||||||
|
|
||||||
let mut hash_stream = old_repo.hashes().into_streamer();
|
let hash_stream = std::pin::pin!(old_repo.hashes());
|
||||||
|
let mut hash_stream = hash_stream.into_streamer();
|
||||||
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
while let Some(res) = hash_stream.next().await {
|
while let Some(res) = hash_stream.next().await {
|
||||||
|
|
|
@ -21,6 +21,7 @@ use diesel_async::{
|
||||||
},
|
},
|
||||||
AsyncConnection, AsyncPgConnection, RunQueryDsl,
|
AsyncConnection, AsyncPgConnection, RunQueryDsl,
|
||||||
};
|
};
|
||||||
|
use futures_core::Stream;
|
||||||
use tokio::sync::Notify;
|
use tokio::sync::Notify;
|
||||||
use tokio_postgres::{tls::NoTlsStream, AsyncMessage, Connection, NoTls, Notification, Socket};
|
use tokio_postgres::{tls::NoTlsStream, AsyncMessage, Connection, NoTls, Notification, Socket};
|
||||||
use tracing::Instrument;
|
use tracing::Instrument;
|
||||||
|
@ -30,7 +31,7 @@ use uuid::Uuid;
|
||||||
use crate::{
|
use crate::{
|
||||||
details::Details,
|
details::Details,
|
||||||
error_code::{ErrorCode, OwnedErrorCode},
|
error_code::{ErrorCode, OwnedErrorCode},
|
||||||
future::{LocalBoxFuture, WithMetrics, WithTimeout},
|
future::{WithMetrics, WithTimeout},
|
||||||
serde_str::Serde,
|
serde_str::Serde,
|
||||||
stream::LocalBoxStream,
|
stream::LocalBoxStream,
|
||||||
};
|
};
|
||||||
|
@ -1477,13 +1478,10 @@ impl AliasAccessRepo for PostgresRepo {
|
||||||
&self,
|
&self,
|
||||||
timestamp: time::OffsetDateTime,
|
timestamp: time::OffsetDateTime,
|
||||||
) -> Result<LocalBoxStream<'static, Result<Alias, RepoError>>, RepoError> {
|
) -> Result<LocalBoxStream<'static, Result<Alias, RepoError>>, RepoError> {
|
||||||
Ok(Box::pin(PageStream {
|
Ok(Box::pin(page_stream(
|
||||||
inner: self.inner.clone(),
|
self.inner.clone(),
|
||||||
future: None,
|
to_primitive(timestamp),
|
||||||
current: Vec::new(),
|
|inner, older_than| async move {
|
||||||
older_than: to_primitive(timestamp),
|
|
||||||
next: Box::new(|inner, older_than| {
|
|
||||||
Box::pin(async move {
|
|
||||||
use schema::proxies::dsl::*;
|
use schema::proxies::dsl::*;
|
||||||
|
|
||||||
let mut conn = inner.get_connection().await?;
|
let mut conn = inner.get_connection().await?;
|
||||||
|
@ -1501,9 +1499,8 @@ impl AliasAccessRepo for PostgresRepo {
|
||||||
.map_err(PostgresError::Diesel)?;
|
.map_err(PostgresError::Diesel)?;
|
||||||
|
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
})
|
},
|
||||||
}),
|
)))
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_alias_access(&self, _: Alias) -> Result<(), RepoError> {
|
async fn remove_alias_access(&self, _: Alias) -> Result<(), RepoError> {
|
||||||
|
@ -1570,13 +1567,10 @@ impl VariantAccessRepo for PostgresRepo {
|
||||||
&self,
|
&self,
|
||||||
timestamp: time::OffsetDateTime,
|
timestamp: time::OffsetDateTime,
|
||||||
) -> Result<LocalBoxStream<'static, Result<(Hash, String), RepoError>>, RepoError> {
|
) -> Result<LocalBoxStream<'static, Result<(Hash, String), RepoError>>, RepoError> {
|
||||||
Ok(Box::pin(PageStream {
|
Ok(Box::pin(page_stream(
|
||||||
inner: self.inner.clone(),
|
self.inner.clone(),
|
||||||
future: None,
|
to_primitive(timestamp),
|
||||||
current: Vec::new(),
|
|inner, older_than| async move {
|
||||||
older_than: to_primitive(timestamp),
|
|
||||||
next: Box::new(|inner, older_than| {
|
|
||||||
Box::pin(async move {
|
|
||||||
use schema::variants::dsl::*;
|
use schema::variants::dsl::*;
|
||||||
|
|
||||||
let mut conn = inner.get_connection().await?;
|
let mut conn = inner.get_connection().await?;
|
||||||
|
@ -1594,9 +1588,8 @@ impl VariantAccessRepo for PostgresRepo {
|
||||||
.map_err(PostgresError::Diesel)?;
|
.map_err(PostgresError::Diesel)?;
|
||||||
|
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
})
|
},
|
||||||
}),
|
)))
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_variant_access(&self, _: Hash, _: String) -> Result<(), RepoError> {
|
async fn remove_variant_access(&self, _: Hash, _: String) -> Result<(), RepoError> {
|
||||||
|
@ -1778,62 +1771,36 @@ impl FullRepo for PostgresRepo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type NextFuture<I> = LocalBoxFuture<'static, Result<Vec<(time::PrimitiveDateTime, I)>, RepoError>>;
|
fn page_stream<I, F, Fut>(
|
||||||
|
|
||||||
struct PageStream<I> {
|
|
||||||
inner: Arc<Inner>,
|
inner: Arc<Inner>,
|
||||||
future: Option<NextFuture<I>>,
|
mut older_than: time::PrimitiveDateTime,
|
||||||
current: Vec<I>,
|
next: F,
|
||||||
older_than: time::PrimitiveDateTime,
|
) -> impl Stream<Item = Result<I, RepoError>>
|
||||||
next: Box<dyn Fn(Arc<Inner>, time::PrimitiveDateTime) -> NextFuture<I>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> futures_core::Stream for PageStream<I>
|
|
||||||
where
|
where
|
||||||
I: Unpin,
|
F: Fn(Arc<Inner>, time::PrimitiveDateTime) -> Fut,
|
||||||
|
Fut: std::future::Future<Output = Result<Vec<(time::PrimitiveDateTime, I)>, RepoError>>
|
||||||
|
+ 'static,
|
||||||
|
I: 'static,
|
||||||
{
|
{
|
||||||
type Item = Result<I, RepoError>;
|
streem::try_from_fn(|yielder| async move {
|
||||||
|
|
||||||
fn poll_next(
|
|
||||||
self: std::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut std::task::Context<'_>,
|
|
||||||
) -> std::task::Poll<Option<Self::Item>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Pop because we reversed the list
|
let mut page = (next)(inner.clone(), older_than).await?;
|
||||||
if let Some(alias) = this.current.pop() {
|
|
||||||
return std::task::Poll::Ready(Some(Ok(alias)));
|
if let Some((last_time, last_item)) = page.pop() {
|
||||||
|
for (_, item) in page {
|
||||||
|
yielder.yield_ok(item).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(future) = this.future.as_mut() {
|
yielder.yield_ok(last_item).await;
|
||||||
let res = std::task::ready!(future.as_mut().poll(cx));
|
|
||||||
|
|
||||||
this.future.take();
|
older_than = last_time;
|
||||||
|
|
||||||
match res {
|
|
||||||
Ok(page) if page.is_empty() => {
|
|
||||||
return std::task::Poll::Ready(None);
|
|
||||||
}
|
|
||||||
Ok(page) => {
|
|
||||||
let (mut timestamps, mut aliases): (Vec<_>, Vec<_>) =
|
|
||||||
page.into_iter().unzip();
|
|
||||||
// reverse because we use .pop() to get next
|
|
||||||
aliases.reverse();
|
|
||||||
|
|
||||||
this.current = aliases;
|
|
||||||
this.older_than = timestamps.pop().expect("Verified nonempty");
|
|
||||||
}
|
|
||||||
Err(e) => return std::task::Poll::Ready(Some(Err(e))),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let inner = this.inner.clone();
|
break;
|
||||||
let older_than = this.older_than;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.future = Some((this.next)(inner, older_than));
|
Ok(())
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for PostgresRepo {
|
impl std::fmt::Debug for PostgresRepo {
|
||||||
|
|
Loading…
Reference in a new issue