2
0
Fork 0
mirror of https://git.asonix.dog/asonix/pict-rs synced 2024-12-22 11:21:24 +00:00

Add more poll timers, spawn process from background threads

This commit is contained in:
asonix 2024-03-09 15:19:13 -06:00
parent 9fe586b9dd
commit e302df7e39
15 changed files with 62 additions and 41 deletions

View file

@ -98,7 +98,8 @@ async fn read_rgba_command<S>(
(MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()), (MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()),
]; ];
let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)? let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)
.await?
.add_extras(temporary_path); .add_extras(temporary_path);
Ok(process) Ok(process)

View file

@ -40,7 +40,8 @@ pub(super) async fn check_reorient(
#[tracing::instrument(level = "trace", skip_all)] #[tracing::instrument(level = "trace", skip_all)]
async fn needs_reorienting(input: BytesStream, timeout: u64) -> Result<bool, ExifError> { async fn needs_reorienting(input: BytesStream, timeout: u64) -> Result<bool, ExifError> {
let buf = Process::run("exiftool", &["-n", "-Orientation", "-"], &[], timeout)? let buf = Process::run("exiftool", &["-n", "-Orientation", "-"], &[], timeout)
.await?
.drive_with_async_read(input.into_reader()) .drive_with_async_read(input.into_reader())
.into_string() .into_string()
.await?; .await?;

View file

@ -197,7 +197,8 @@ pub(super) async fn discover_bytes_stream<S>(
], ],
&[], &[],
state.config.media.process_timeout, state.config.media.process_timeout,
)? )
.await?
.read() .read()
.into_vec() .into_vec()
.await .await
@ -242,7 +243,8 @@ async fn alpha_pixel_formats(timeout: u64) -> Result<HashSet<String>, FfMpegErro
], ],
&[], &[],
timeout, timeout,
)? )
.await?
.read() .read()
.into_vec() .into_vec()
.await?; .await?;

View file

@ -72,7 +72,8 @@ async fn discover<S>(state: &State<S>, stream: BytesStream) -> Result<Discovery,
], ],
&envs, &envs,
state.config.media.process_timeout, state.config.media.process_timeout,
)? )
.await?
.drive_with_async_read(stream.into_reader()) .drive_with_async_read(stream.into_reader())
.into_string() .into_string()
.await; .await;

View file

@ -142,7 +142,8 @@ where
let elapsed = start.elapsed(); let elapsed = start.elapsed();
if elapsed > Duration::from_micros(10) { if elapsed > Duration::from_micros(10) {
metrics::counter!(crate::init_metrics::FUTURE_POLL_TIMER_EXCEEDED, "timer" => this.name.to_string()); metrics::counter!(crate::init_metrics::FUTURE_POLL_TIMER_EXCEEDED, "timer" => this.name.to_string()).increment(1);
metrics::histogram!(crate::init_metrics::FUTURE_POLL_TIMER_EXCEEDED_SECONDS, "timer" => this.name.to_string()).record(elapsed.as_secs_f64());
} }
if elapsed > Duration::from_secs(1) { if elapsed > Duration::from_secs(1) {

View file

@ -74,11 +74,12 @@ pub(crate) async fn generate<S: Store + 'static>(
thumbnail_args, thumbnail_args,
original_details, original_details,
hash.clone(), hash.clone(),
); )
.with_poll_timer("process-future");
let (details, identifier) = process_map let (details, identifier) = process_map
.process(hash, thumbnail_path, process_fut) .process(hash, thumbnail_path, process_fut)
.with_poll_timer("process-future") .with_poll_timer("process-map-future")
.with_timeout(Duration::from_secs(state.config.media.process_timeout * 4)) .with_timeout(Duration::from_secs(state.config.media.process_timeout * 4))
.with_metrics(crate::init_metrics::GENERATE_PROCESS) .with_metrics(crate::init_metrics::GENERATE_PROCESS)
.await .await

View file

@ -97,7 +97,8 @@ pub(super) async fn thumbnail<S: Store>(
], ],
&[], &[],
state.config.media.process_timeout, state.config.media.process_timeout,
)? )
.await?
.wait() .wait()
.await .await
.map_err(FfMpegError::Process)?; .map_err(FfMpegError::Process)?;

View file

@ -43,7 +43,8 @@ pub(super) async fn thumbnail_command<S>(
(MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()), (MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()),
]; ];
let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)? let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)
.await?
.add_extras(temporary_path); .add_extras(temporary_path);
Ok(process) Ok(process)

View file

@ -32,9 +32,15 @@ fn describe_future() {
FUTURE_POLL_TIMER_EXCEEDED, FUTURE_POLL_TIMER_EXCEEDED,
"How many times a given poll operation has lasted longer than 10 microseconds" "How many times a given poll operation has lasted longer than 10 microseconds"
); );
metrics::describe_histogram!(
FUTURE_POLL_TIMER_EXCEEDED_SECONDS,
"Durations for polls lasting longer than 10 microseconds"
);
} }
pub(crate) const FUTURE_POLL_TIMER_EXCEEDED: &str = "pict-rs.future.poll-timer.exceeded"; pub(crate) const FUTURE_POLL_TIMER_EXCEEDED: &str = "pict-rs.future.poll-timer.exceeded";
pub(crate) const FUTURE_POLL_TIMER_EXCEEDED_SECONDS: &str =
"pict-rs.future.poll-timer.exceeded.seconds";
fn describe_queue_cleanup() { fn describe_queue_cleanup() {
metrics::describe_counter!( metrics::describe_counter!(

View file

@ -106,7 +106,8 @@ pub(crate) async fn process_image_command<S>(
(MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()), (MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()),
]; ];
let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)? let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)
.await?
.add_extras(temporary_path); .add_extras(temporary_path);
Ok(process) Ok(process)

View file

@ -155,7 +155,7 @@ impl ProcessError {
} }
impl Process { impl Process {
pub(crate) fn run<T>( pub(crate) async fn run<T>(
command: &str, command: &str,
args: &[T], args: &[T],
envs: &[(&str, &OsStr)], envs: &[(&str, &OsStr)],
@ -168,15 +168,10 @@ impl Process {
tracing::debug!("{envs:?} {command} {args:?}"); tracing::debug!("{envs:?} {command} {args:?}");
let res = tracing::trace_span!(parent: None, "Create command", %command).in_scope(|| { let mut cmd = Command::new(command.as_ref());
Self::spawn( cmd.args(args).envs(envs.iter().copied());
command.clone(),
Command::new(command.as_ref()) let res = Self::spawn(command.clone(), cmd, timeout).await;
.args(args)
.envs(envs.iter().copied()),
timeout,
)
});
match res { match res {
Ok(this) => Ok(this), Ok(this) => Ok(this),
@ -191,16 +186,17 @@ impl Process {
} }
} }
fn spawn(command: Arc<str>, cmd: &mut Command, timeout: u64) -> std::io::Result<Self> { async fn spawn(command: Arc<str>, mut cmd: Command, timeout: u64) -> std::io::Result<Self> {
tracing::trace_span!(parent: None, "Spawn command", %command).in_scope(|| { let guard = MetricsGuard::guard(command.clone());
let guard = MetricsGuard::guard(command.clone());
let cmd = cmd cmd.stdin(Stdio::piped())
.stdin(Stdio::piped()) .stdout(Stdio::piped())
.stdout(Stdio::piped()) .kill_on_drop(true);
.kill_on_drop(true);
cmd.spawn().map(|child| Process { crate::sync::spawn_blocking("spawn-command", move || cmd.spawn())
.await
.expect("spawn panicked")
.map(|child| Process {
child, child,
command, command,
guard, guard,
@ -208,7 +204,6 @@ impl Process {
extras: Box::new(()), extras: Box::new(()),
id: Uuid::now_v7(), id: Uuid::now_v7(),
}) })
})
} }
pub(crate) fn add_extras(self, extra: impl Extras + 'static) -> Self { pub(crate) fn add_extras(self, extra: impl Extras + 'static) -> Self {
@ -282,6 +277,7 @@ impl Process {
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => break, Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => break,
Err(e) => return Err(e), Err(e) => return Err(e),
} }
tokio::task::yield_now().await;
} }
Ok(()) Ok(())
@ -454,6 +450,16 @@ impl ProcessRead {
self, self,
f: impl FnOnce(BoxRead<'static>) -> Fut, f: impl FnOnce(BoxRead<'static>) -> Fut,
) -> Result<Fut::Output, ProcessError> ) -> Result<Fut::Output, ProcessError>
where
Fut: Future,
{
self.with_stdout_inner(f).await
}
async fn with_stdout_inner<Fut>(
self,
f: impl FnOnce(BoxRead<'static>) -> Fut,
) -> Result<Fut::Output, ProcessError>
where where
Fut: Future, Fut: Future,
{ {

View file

@ -133,7 +133,7 @@ async fn process_image_command<S>(
magick::convert_image_command(state, input.format, format, quality).await? magick::convert_image_command(state, input.format, format, quality).await?
} else { } else {
exiftool::clear_metadata_command(state.config.media.process_timeout)? exiftool::clear_metadata_command(state.config.media.process_timeout).await?
}; };
Ok((InternalFormat::Image(format), process)) Ok((InternalFormat::Image(format), process))
@ -188,7 +188,7 @@ async fn process_animation_command<S>(
magick::convert_animation_command(state, input, format, quality).await? magick::convert_animation_command(state, input, format, quality).await?
} else { } else {
exiftool::clear_metadata_command(state.config.media.process_timeout)? exiftool::clear_metadata_command(state.config.media.process_timeout).await?
}; };
Ok((InternalFormat::Animation(format), process)) Ok((InternalFormat::Animation(format), process))

View file

@ -1,11 +1,6 @@
use crate::{exiftool::ExifError, process::Process}; use crate::{exiftool::ExifError, process::Process};
#[tracing::instrument(level = "trace", skip_all)] #[tracing::instrument(level = "trace", skip_all)]
pub(super) fn clear_metadata_command(timeout: u64) -> Result<Process, ExifError> { pub(super) async fn clear_metadata_command(timeout: u64) -> Result<Process, ExifError> {
Ok(Process::run( Ok(Process::run("exiftool", &["-all=", "-", "-out", "-"], &[], timeout).await?)
"exiftool",
&["-all=", "-", "-out", "-"],
&[],
timeout,
)?)
} }

View file

@ -132,7 +132,10 @@ async fn transcode_files(
output_path, output_path,
]); ]);
Process::run("ffmpeg", &args, &[], timeout)?.wait().await?; Process::run("ffmpeg", &args, &[], timeout)
.await?
.wait()
.await?;
Ok(()) Ok(())
} }

View file

@ -80,7 +80,8 @@ async fn convert<S>(
(MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()), (MAGICK_CONFIGURE_PATH, state.policy_dir.as_os_str()),
]; ];
let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)? let process = Process::run("magick", &args, &envs, state.config.media.process_timeout)
.await?
.add_extras(temporary_path); .add_extras(temporary_path);
Ok(process) Ok(process)