Skip to content

Commit dd8c34c

Browse files
Clear CompoundAnnotation Cache on Task Changes (#8756)
When finishing or cancelling a task annotation, clear the compound annotation caches that this annotation influences. That way, when reloading the compound annotation afterwards, the new data is visible there immediately. Note that the tracingstore-side caches don’t need to be cleared, as the wk side will decide to put new data there, containing the new merged annotationProto (with all-new tracingIds for the layers). ### URL of deployed dev instance (used for testing): - https://clearcompoundcache.webknossos.xyz ### Steps to test: - Create a few tasks for a project - After finishing a task, view the CompoundProject - in another tab, finish a second task - Reload the CompoundProject tab, should show the new data. ### Issues: - fixes #8744 - more context: https://scm.slack.com/archives/C5AKLAV0B/p1751875615927679?thread_ts=1751021208.243629&cid=C5AKLAV0B ------ - [x] Added changelog entry (create a `$PR_NUMBER.md` file in `unreleased_changes` or use `./tools/create-changelog-entry.py`) - [x] Considered [common edge cases](../blob/master/.github/common_edge_cases.md) --------- Co-authored-by: MichaelBuessemeyer <39529669+MichaelBuessemeyer@users.noreply.github.com>
1 parent 8ef593c commit dd8c34c

File tree

7 files changed

+29
-12
lines changed

7 files changed

+29
-12
lines changed

app/controllers/AnnotationController.scala

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import models.annotation.AnnotationState.Cancelled
1717
import models.annotation._
1818
import models.dataset.{DatasetDAO, DatasetService}
1919
import models.project.ProjectDAO
20-
import models.task.TaskDAO
20+
import models.task.{TaskDAO, TaskService}
2121
import models.team.{TeamDAO, TeamService}
2222
import models.user.time._
2323
import models.user.{User, UserDAO, UserService}
@@ -52,6 +52,7 @@ class AnnotationController @Inject()(
5252
tracingStoreService: TracingStoreService,
5353
provider: AnnotationInformationProvider,
5454
annotationRestrictionDefaults: AnnotationRestrictionDefaults,
55+
taskService: TaskService,
5556
analyticsService: AnalyticsService,
5657
slackNotificationService: SlackNotificationService,
5758
mailchimpClient: MailchimpClient,
@@ -158,6 +159,7 @@ class AnnotationController @Inject()(
158159
_ = logger.info(
159160
s"Reopening annotation $id, new state will be ${AnnotationState.Active.toString}, access context: ${request.identity.toStringAnonymous}")
160161
_ <- annotationDAO.updateState(annotation._id, AnnotationState.Active) ?~> "annotation.invalid"
162+
_ <- Fox.runOptional(annotation._task)(taskService.clearCompoundCache)
161163
updatedAnnotation <- provider.provideAnnotation(typ, id, request.identity) ~> NOT_FOUND
162164
json <- annotationService.publicWrites(updatedAnnotation, Some(request.identity)) ?~> "annotation.write.failed"
163165
} yield JsonOk(json, Messages("annotation.reopened"))
@@ -298,13 +300,14 @@ class AnnotationController @Inject()(
298300

299301
def cancel(typ: String, id: ObjectId): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
300302
def tryToCancel(annotation: Annotation) =
301-
annotation match {
302-
case t if t.typ == AnnotationType.Task =>
303+
(annotation._task, annotation.typ) match {
304+
case (Some(taskId), AnnotationType.Task) =>
303305
logger.info(
304-
s"Canceling annotation $id, new state will be ${AnnotationState.Cancelled.toString}, access context: ${request.identity.toStringAnonymous}")
305-
annotationDAO.updateState(annotation._id, Cancelled).map { _ =>
306-
JsonOk(Messages("task.finished"))
307-
}
306+
s"Canceling task annotation $id, new state will be ${AnnotationState.Cancelled}, access context: ${request.identity.toStringAnonymous}")
307+
for {
308+
_ <- Fox.runIf(annotation.state == AnnotationState.Finished)(taskService.clearCompoundCache(taskId))
309+
_ <- annotationDAO.updateState(annotation._id, Cancelled)
310+
} yield JsonOk(Messages("task.finished"))
308311
case _ =>
309312
Fox.successful(JsonOk(Messages("annotation.finished")))
310313
}

app/models/annotation/AnnotationService.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ class AnnotationService @Inject()(
313313
for {
314314
_ <- annotationDAO.updateModified(annotation._id, Instant.now)
315315
_ <- annotationDAO.updateState(annotation._id, AnnotationState.Finished)
316+
_ <- Fox.runOptional(annotation._task)(taskService.clearCompoundCache)
316317
} yield {
317318
if (annotation._task.isEmpty)
318319
"annotation.finished"

app/models/annotation/AnnotationStore.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class AnnotationStore @Inject()(
2525

2626
private def requestFromCache(id: AnnotationIdentifier): Option[Fox[Annotation]] = {
2727
val handler = annotationInformationHandlerSelector.informationHandlers(id.annotationType)
28-
if (handler.cache) {
28+
if (handler.useCache) {
2929
val cached = getFromCache(id)
3030
cached
3131
} else
@@ -37,7 +37,7 @@ class AnnotationStore @Inject()(
3737
for {
3838
annotation <- handler.provideAnnotation(id.identifier, user)
3939
} yield {
40-
if (handler.cache) {
40+
if (handler.useCache) {
4141
storeInCache(id, annotation)
4242
}
4343
annotation
@@ -60,4 +60,6 @@ class AnnotationStore @Inject()(
6060
case None => Empty
6161
}
6262
}
63+
64+
def removeFromCache(id: AnnotationIdentifier): Unit = temporaryAnnotationStore.remove(id.toUniqueString)
6365
}

app/models/annotation/handler/AnnotationInformationHandler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ trait AnnotationInformationHandler extends FoxImplicits {
3232

3333
implicit val ec: ExecutionContext
3434

35-
def cache: Boolean = true
35+
def useCache: Boolean = true
3636

3737
def provideAnnotation(identifier: ObjectId, user: Option[User])(implicit ctx: DBAccessContext): Fox[Annotation]
3838

app/models/annotation/handler/SavedTracingInformationHandler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class SavedTracingInformationHandler @Inject()(
2424
with Formatter
2525
with FoxImplicits {
2626

27-
override val cache = false
27+
override val useCache = false
2828

2929
override def nameForAnnotation(annotation: Annotation)(implicit ctx: DBAccessContext): Fox[String] =
3030
for {

app/models/task/TaskService.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import com.scalableminds.util.objectid.ObjectId
55
import com.scalableminds.util.tools.{Fox, FoxImplicits}
66

77
import javax.inject.Inject
8-
import models.annotation.{Annotation, AnnotationDAO, AnnotationType}
8+
import models.annotation.{Annotation, AnnotationDAO, AnnotationIdentifier, AnnotationStore, AnnotationType}
99
import models.dataset.DatasetDAO
1010
import models.project.ProjectDAO
1111
import models.team.TeamDAO
@@ -19,10 +19,12 @@ import scala.concurrent.ExecutionContext
1919
class TaskService @Inject()(conf: WkConf,
2020
datasetDAO: DatasetDAO,
2121
scriptDAO: ScriptDAO,
22+
annotationStore: AnnotationStore,
2223
userService: UserService,
2324
annotationDAO: AnnotationDAO,
2425
taskTypeDAO: TaskTypeDAO,
2526
teamDAO: TeamDAO,
27+
taskDAO: TaskDAO,
2628
scriptService: ScriptService,
2729
taskTypeService: TaskTypeService,
2830
projectDAO: ProjectDAO)(implicit ec: ExecutionContext)
@@ -88,4 +90,11 @@ class TaskService @Inject()(conf: WkConf,
8890
activeCount <- Fox.fromFuture(annotationDAO.countActiveByTask(task._id, AnnotationType.Task).getOrElse(0))
8991
} yield TaskStatus(task.pendingInstances, activeCount, task.totalInstances - (activeCount + task.pendingInstances))
9092

93+
def clearCompoundCache(taskId: ObjectId): Fox[Unit] =
94+
for {
95+
task <- taskDAO.findOne(taskId)(GlobalAccessContext)
96+
_ = annotationStore.removeFromCache(AnnotationIdentifier(AnnotationType.CompoundTask, task._id))
97+
_ = annotationStore.removeFromCache(AnnotationIdentifier(AnnotationType.CompoundProject, task._project))
98+
_ = annotationStore.removeFromCache(AnnotationIdentifier(AnnotationType.CompoundTaskType, task._taskType))
99+
} yield ()
91100
}

unreleased_changes/8756.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
### Fixed
2+
- Fixed that compound (merged) views of tasks, projects and task types would sometimes show outdated annotation data, leaving out the data from recently finished tasks annotations.

0 commit comments

Comments
 (0)