mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Fix cancelling jobs
A request to cancel a job was not processed correctly. The cancelling routine of a task must run, regardless of the (non-final) state. Now it works like this: if a job is currently running, it is interrupted and its cancel routine is invoked. It then enters "cancelled" state. If it is stuck, it is loaded and only its cancel routine is run. If it is in a final state or waiting, it is removed from the queue.
This commit is contained in:
@ -2,6 +2,7 @@ package docspell.backend.ops
|
||||
|
||||
import cats.implicits._
|
||||
import cats.effect._
|
||||
import cats.data.OptionT
|
||||
import docspell.backend.ops.OJob.{CollectiveQueueState, JobCancelResult}
|
||||
import docspell.common.{Ident, JobState}
|
||||
import docspell.store.Store
|
||||
@ -22,6 +23,10 @@ object OJob {
|
||||
case object Removed extends JobCancelResult
|
||||
case object CancelRequested extends JobCancelResult
|
||||
case object JobNotFound extends JobCancelResult
|
||||
|
||||
def removed: JobCancelResult = Removed
|
||||
def cancelRequested: JobCancelResult = CancelRequested
|
||||
def jobNotFound: JobCancelResult = JobNotFound
|
||||
}
|
||||
|
||||
case class JobDetail(job: RJob, logs: Vector[RJobLog])
|
||||
@ -49,43 +54,30 @@ object OJob {
|
||||
.map(CollectiveQueueState)
|
||||
|
||||
def cancelJob(id: Ident, collective: Ident): F[JobCancelResult] = {
|
||||
def mustCancel(job: Option[RJob]): Option[(RJob, Ident)] =
|
||||
for {
|
||||
worker <- job.flatMap(_.worker)
|
||||
job <- job.filter(j =>
|
||||
j.state == JobState.Scheduled || j.state == JobState.Running
|
||||
)
|
||||
} yield (job, worker)
|
||||
def remove(job: RJob): F[JobCancelResult] =
|
||||
store.transact(RJob.delete(job.id)) *> JobCancelResult.removed.pure[F]
|
||||
|
||||
def canDelete(j: RJob): Boolean =
|
||||
mustCancel(j.some).isEmpty
|
||||
|
||||
val tryDelete = for {
|
||||
job <- RJob.findByIdAndGroup(id, collective)
|
||||
jobm = job.filter(canDelete)
|
||||
del <- jobm.traverse(j => RJob.delete(j.id))
|
||||
} yield del match {
|
||||
case Some(_) => Right(JobCancelResult.Removed: JobCancelResult)
|
||||
case None => Left(mustCancel(job))
|
||||
}
|
||||
|
||||
def tryCancel(job: RJob, worker: Ident): F[JobCancelResult] =
|
||||
joex
|
||||
.cancelJob(job.id, worker)
|
||||
.map(flag =>
|
||||
if (flag) JobCancelResult.CancelRequested else JobCancelResult.JobNotFound
|
||||
)
|
||||
|
||||
for {
|
||||
tryDel <- store.transact(tryDelete)
|
||||
result <- tryDel match {
|
||||
case Right(r) => r.pure[F]
|
||||
case Left(Some((job, worker))) =>
|
||||
tryCancel(job, worker)
|
||||
case Left(None) =>
|
||||
(JobCancelResult.JobNotFound: OJob.JobCancelResult).pure[F]
|
||||
def tryCancel(job: RJob): F[JobCancelResult] =
|
||||
job.worker match {
|
||||
case Some(worker) =>
|
||||
for {
|
||||
flag <- joex.cancelJob(job.id, worker)
|
||||
res <-
|
||||
if (flag) JobCancelResult.cancelRequested.pure[F]
|
||||
else remove(job)
|
||||
} yield res
|
||||
case None =>
|
||||
remove(job)
|
||||
}
|
||||
} yield result
|
||||
|
||||
(for {
|
||||
job <- OptionT(store.transact(RJob.findByIdAndGroup(id, collective)))
|
||||
result <- OptionT.liftF(
|
||||
if (job.isInProgress) tryCancel(job)
|
||||
else remove(job)
|
||||
)
|
||||
} yield result)
|
||||
.getOrElse(JobCancelResult.jobNotFound)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package docspell.backend.ops
|
||||
|
||||
import cats.implicits._
|
||||
import cats.effect._
|
||||
import cats.data.OptionT
|
||||
import docspell.common.{Ident, NodeType}
|
||||
import docspell.joexapi.client.JoexClient
|
||||
import docspell.store.Store
|
||||
@ -28,10 +29,10 @@ object OJoex {
|
||||
} yield ()
|
||||
|
||||
def cancelJob(job: Ident, worker: Ident): F[Boolean] =
|
||||
for {
|
||||
node <- store.transact(RNode.findById(worker))
|
||||
cancel <- node.traverse(n => client.cancelJob(n.url, job))
|
||||
} yield cancel.isDefined
|
||||
(for {
|
||||
node <- OptionT(store.transact(RNode.findById(worker)))
|
||||
cancel <- OptionT.liftF(client.cancelJob(node.url, job))
|
||||
} yield cancel.success).getOrElse(false)
|
||||
})
|
||||
|
||||
def create[F[_]: ConcurrentEffect](
|
||||
|
Reference in New Issue
Block a user