Fix storing empty-trash task

It was wrongly stored using RPeriodicTask directly, but the higher
level `UserTask` must be used instead, because this ensures a
correctly scoped periodic task using the `updateOneTask` method. Since
this is a system task, it can be given a fixed ID which makes it now
safe even if stored using RPeriodicTask directly.

The bug resulted in multiple empty-trash tasks to be inserted (on each
restart).

Refs: #347
This commit is contained in:
eikek 2021-08-19 00:24:10 +02:00
parent e85bd1267c
commit 90421599ea
7 changed files with 37 additions and 18 deletions

View File

@ -25,6 +25,8 @@ case class EmptyTrashArgs(
def makeSubject: String = def makeSubject: String =
s"Empty Trash: Remove older than ${minAge.toJava}" s"Empty Trash: Remove older than ${minAge.toJava}"
def periodicTaskId: Ident =
EmptyTrashArgs.periodicTaskId(collective)
} }
object EmptyTrashArgs { object EmptyTrashArgs {
@ -33,6 +35,9 @@ object EmptyTrashArgs {
val defaultSchedule = CalEvent.unsafe("*-*-1/7 03:00:00") val defaultSchedule = CalEvent.unsafe("*-*-1/7 03:00:00")
def periodicTaskId(coll: Ident): Ident =
Ident.unsafe(s"docspell") / taskName / coll
implicit val jsonEncoder: Encoder[EmptyTrashArgs] = implicit val jsonEncoder: Encoder[EmptyTrashArgs] =
deriveEncoder[EmptyTrashArgs] deriveEncoder[EmptyTrashArgs]
implicit val jsonDecoder: Decoder[EmptyTrashArgs] = implicit val jsonDecoder: Decoder[EmptyTrashArgs] =

View File

@ -40,6 +40,8 @@ import docspell.store.records.{REmptyTrashSetting, RJobLog}
import emil.javamail._ import emil.javamail._
import org.http4s.blaze.client.BlazeClientBuilder import org.http4s.blaze.client.BlazeClientBuilder
import org.http4s.client.Client import org.http4s.client.Client
import docspell.store.usertask.UserTaskStore
import docspell.store.usertask.UserTaskScope
final class JoexAppImpl[F[_]: Async]( final class JoexAppImpl[F[_]: Async](
cfg: Config, cfg: Config,
@ -91,9 +93,15 @@ final class JoexAppImpl[F[_]: Async](
REmptyTrashSetting.findForAllCollectives(OCollective.EmptyTrash.default, 50) REmptyTrashSetting.findForAllCollectives(OCollective.EmptyTrash.default, 50)
) )
.evalMap(es => .evalMap(es =>
EmptyTrashTask.periodicTask(EmptyTrashArgs(es.cid, es.minAge), es.schedule) UserTaskStore(store).use { uts =>
val args = EmptyTrashArgs(es.cid, es.minAge)
uts.updateOneTask(
UserTaskScope(args.collective),
args.makeSubject.some,
EmptyTrashTask.userTask(args, es.schedule)
)
}
) )
.evalMap(pstore.insert)
.compile .compile
.drain .drain

View File

@ -13,8 +13,8 @@ import fs2.Stream
import docspell.backend.ops.{OItem, OItemSearch} import docspell.backend.ops.{OItem, OItemSearch}
import docspell.common._ import docspell.common._
import docspell.joex.scheduler._ import docspell.joex.scheduler._
import docspell.store.records.{RItem, RPeriodicTask} import docspell.store.records.RItem
import docspell.store.usertask.{UserTask, UserTaskScope} import docspell.store.usertask.UserTask
import com.github.eikek.calev.CalEvent import com.github.eikek.calev.CalEvent
@ -26,19 +26,15 @@ object EmptyTrashTask {
private val pageSize = 20 private val pageSize = 20
def periodicTask[F[_]: Sync](args: EmptyTrashArgs, ce: CalEvent): F[RPeriodicTask] = def userTask(args: EmptyTrashArgs, ce: CalEvent): UserTask[EmptyTrashArgs] =
Ident UserTask(
.randomId[F] args.periodicTaskId,
.flatMap(id => EmptyTrashArgs.taskName,
UserTask( true,
id, ce,
EmptyTrashArgs.taskName, None,
true, args
ce, )
None,
args
).encode.toPeriodicTask(UserTaskScope(args.collective), args.makeSubject.some)
)
def apply[F[_]: Async]( def apply[F[_]: Async](
itemOps: OItem[F], itemOps: OItem[F],

View File

@ -0,0 +1,3 @@
-- note this is only for users of nightly releases
DELETE FROM "periodic_task"
WHERE "task" = 'empty-trash';

View File

@ -0,0 +1,3 @@
-- note this is only for users of nightly releases
DELETE FROM `periodic_task`
WHERE `task` = 'empty-trash';

View File

@ -0,0 +1,3 @@
-- note this is only for users of nightly releases
DELETE FROM "periodic_task"
WHERE "task" = 'empty-trash';

View File

@ -81,7 +81,8 @@ trait UserTaskStore[F[_]] {
* *
* Unlike `updateTask`, this ensures that there is at most one task of some name in the * Unlike `updateTask`, this ensures that there is at most one task of some name in the
* db. Multiple same tasks (task with same name) may not be allowed to run, depending * db. Multiple same tasks (task with same name) may not be allowed to run, depending
* on what they do. This is not ensured by the database, though. * on what they do. This is not ensured by the database, though. The task is identified
* by task name, submitter and group.
* *
* If there are currently multiple tasks with same name as `ut` for the user `account`, * If there are currently multiple tasks with same name as `ut` for the user `account`,
* they will all be removed and the given task inserted! * they will all be removed and the given task inserted!