diff --git a/CHANGELOG.md b/CHANGELOG.md index 1990d95..c0662c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * (feature) Add `task-manager:run-worker` command as wrapper for Symfony messengers `consume` command. * (improvement) Default to `limit` of 5 messages in run worker command, if no other limit is given. * (improvement) Validate unique task ids to a specific format. +* (feature) Also purge log entries with a max number entry. 2.0.3 diff --git a/src/DependencyInjection/TaskManagerBundleConfiguration.php b/src/DependencyInjection/TaskManagerBundleConfiguration.php index 360b0eb..602ca2f 100644 --- a/src/DependencyInjection/TaskManagerBundleConfiguration.php +++ b/src/DependencyInjection/TaskManagerBundleConfiguration.php @@ -24,9 +24,23 @@ public function getConfigTreeBuilder () : TreeBuilder ->scalarPrototype()->end() ->end() ->integerNode("log_ttl") + ->setDeprecated("21torr/task-manager", "2.2.0", "Use the `log.ttl` configuration instead.") ->info("The max age of log entries, before they are automatically cleaned.") ->defaultValue(28) ->end() + ->arrayNode("log") + ->addDefaultsIfNotSet() + ->children() + ->integerNode("ttl") + ->defaultValue(28) + ->info("The max age of log entries, before they are automatically cleaned.") + ->end() + ->integerNode("max_entries") + ->defaultValue(1000) + ->info("The max number of log entries, before they are automatically cleaned.") + ->end() + ->end() + ->end() ->end(); return $treeBuilder; diff --git a/src/Log/LogCleaner.php b/src/Log/LogCleaner.php index 074e732..854babf 100644 --- a/src/Log/LogCleaner.php +++ b/src/Log/LogCleaner.php @@ -8,6 +8,7 @@ { public function __construct ( private int $logTtlInDays, + private int $logMaxEntries, private TaskLogModel $model, ) {} @@ -18,7 +19,9 @@ public function cleanLogEntries () : array { $deleted = []; - foreach ($this->model->fetchOutdatedTasks($this->logTtlInDays) as $logEntry) + $outdatedTasks = $this->model->fetchOutdatedTasks($this->logTtlInDays, $this->logMaxEntries); + + foreach ($outdatedTasks as $logEntry) { $deleted[] = \sprintf( "%s (%s)", @@ -41,4 +44,12 @@ public function getMaxLogEntryAge () : int { return $this->logTtlInDays; } + + /** + * Returns the maximum number of log entries to keep + */ + public function getMaxLogEntryNumber () : int + { + return $this->logMaxEntries; + } } diff --git a/src/Log/Task/CleanOutdatedLogsTaskHandler.php b/src/Log/Task/CleanOutdatedLogsTaskHandler.php index 24e9d60..516ade6 100644 --- a/src/Log/Task/CleanOutdatedLogsTaskHandler.php +++ b/src/Log/Task/CleanOutdatedLogsTaskHandler.php @@ -25,8 +25,9 @@ public function onCleanOutdatedLogs (CleanOutdatedLogsTask $task) : void $io->title("Task Manager: Cleaning Outdated Log Entries"); $io->comment(\sprintf( - "Cleaning log entries older than %d days", + "Cleaning log entries older than %d days and keeping at most %d entries", $this->logCleaner->getMaxLogEntryAge(), + $this->logCleaner->getMaxLogEntryNumber(), )); $deletedEntries = $this->logCleaner->cleanLogEntries(); diff --git a/src/Model/TaskLogModel.php b/src/Model/TaskLogModel.php index 7875c6c..e216bae 100644 --- a/src/Model/TaskLogModel.php +++ b/src/Model/TaskLogModel.php @@ -88,17 +88,30 @@ public function createRunForTask (TaskLog $log) : TaskRun /** * @return list */ - public function fetchOutdatedTasks (int $maxAgeInDays) : array + public function fetchOutdatedTasks ( + int $maxAgeInDays, + int $maxEntries, + ) : array { - $oldestTimeQueued = $this->clock->now() + // start with a fixed TTL + $purgeBefore = $this->clock->now() ->sub(new \DateInterval("P{$maxAgeInDays}D")); + // check whether the last entry at "max entries" would be newer than the + // TTL. If so, then adjust the purge date, to fulfill both + $cutOffEntry = $this->getCutoffEntry($maxEntries); + + if (null !== $cutOffEntry && $cutOffEntry->getTimeQueued() > $purgeBefore) + { + $purgeBefore = $cutOffEntry->getTimeQueued(); + } + /** @var TaskLog[] $entries */ $entries = $this->repository->createQueryBuilder("task") ->select("task, run") ->leftJoin("task.runs", "run") ->where("task.timeQueued <= :oldestTimestamp") - ->setParameter("oldestTimestamp", $oldestTimeQueued) + ->setParameter("oldestTimestamp", $purgeBefore) ->getQuery() ->getResult(); @@ -115,6 +128,22 @@ public function fetchOutdatedTasks (int $maxAgeInDays) : array return $filtered; } + /** + * + */ + private function getCutoffEntry (int $maxEntries) : ?TaskLog + { + /** @var TaskLog[] $result */ + $result = $this->repository->createQueryBuilder("task") + ->addOrderBy("task.timeQueued", "DESC") + ->setFirstResult($maxEntries) + ->setMaxResults(1) + ->getQuery() + ->getResult(); + + return $result[0] ?? null; + } + /** * @return $this */ diff --git a/src/TaskManagerBundle.php b/src/TaskManagerBundle.php index f3784be..05508f5 100644 --- a/src/TaskManagerBundle.php +++ b/src/TaskManagerBundle.php @@ -26,8 +26,16 @@ static function (array $config, ContainerBuilder $container) : void $container->getDefinition(BundleConfig::class) ->setArgument('$sortedQueues', $config["queues"]); + // if the new value was customized, use it. Otherwise keep using + // the old value. If none is set, they use the same default, so everything + // is fine. + $logTtl = 28 !== $config["log"]["ttl"] + ? $config["log"]["ttl"] + : $config["log_ttl"]; + $container->getDefinition(LogCleaner::class) - ->setArgument('$logTtlInDays', $config["log_ttl"]); + ->setArgument('$logTtlInDays', $logTtl) + ->setArgument('$logMaxEntries', $config["log"]["max_entries"]); }, ); }