app/housekeeping/MarkOrphanedBakesForDeletion.scala (49 lines of code) (raw):
package housekeeping
import data.{Bakes, Dynamo, Recipes}
import models.{Bake, BakeId, RecipeId}
import org.quartz.SimpleScheduleBuilder
import services.{Loggable, PrismData}
/*
If a recipe has been deleted from a table but not the associated bake, then these
lonely bakes will linger on and never get picked up by the other housekeeping tasks
*/
object MarkOrphanedBakesForDeletion {
val FAULT_TOLERANCE = 0
def findOrphanedBakeIds(
recipeIds: Set[RecipeId],
bakes: List[Bake.DbModel]
): List[BakeId] = {
val orphanedBakes =
bakes.filterNot(bake => recipeIds.contains(bake.recipeId))
orphanedBakes.map(bake => BakeId(bake.recipeId, bake.buildNumber))
}
}
class MarkOrphanedBakesForDeletion(prismAgents: PrismData, dynamo: Dynamo)
extends HousekeepingJob
with Loggable {
override val schedule = SimpleScheduleBuilder.repeatHourlyForever(24)
override def housekeep(): Unit = {
implicit val implicitPrismAgents: PrismData = prismAgents
implicit val implicitDynamo: Dynamo = dynamo
val (errors, recipes) = Recipes.recipesWithErrors()
errors match {
case _ if errors.length > MarkOrphanedBakesForDeletion.FAULT_TOLERANCE =>
log.info(
s"Housekeeping found ${errors.length} database errors while searching for orphaned bakes"
)
log.warn(
s"${errors.length} errors exceeds the limit so orphaned bake deletion will not continue"
)
case _ =>
val bakes = Bakes.scanForAll()
val orphanedBakeIds = MarkOrphanedBakesForDeletion.findOrphanedBakeIds(
recipes.map(_.id).toSet,
bakes
)
if (orphanedBakeIds.nonEmpty)
log.info(
s"Marking ${orphanedBakeIds.size} orphaned bakes for deletion"
)
orphanedBakeIds.foreach { bakeId =>
Bakes.markToDelete(bakeId)
log.info(s"Marked ${bakeId.toString} for deletion")
}
}
}
}