-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathMaintenance.php
More file actions
108 lines (90 loc) · 3.15 KB
/
Maintenance.php
File metadata and controls
108 lines (90 loc) · 3.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<?php
namespace OpenRuntimes\Executor\Runner;
use OpenRuntimes\Executor\Runner\Repository\Runtimes;
use Swoole\Timer;
use Utopia\Console;
use Utopia\Orchestration\Orchestration;
use Utopia\Storage\Device\Local;
use Utopia\System\System;
use function Swoole\Coroutine\batch;
/**
* Handles periodic cleanup of inactive runtimes and orphaned temp directories.
*/
class Maintenance
{
private int|false $timerId = false;
public function __construct(
private Orchestration $orchestration,
private Runtimes $runtimes
) {
}
/**
* Starts the maintenance loop. No-op if already running.
*/
public function start(int $intervalSeconds, int $inactiveSeconds): void
{
if ($this->timerId !== false) {
return;
}
$intervalMs = $intervalSeconds * 1000;
$this->timerId = Timer::tick($intervalMs, fn () => $this->tick($inactiveSeconds));
Console::info("[Maintenance] Started task on interval $intervalSeconds seconds.");
}
/**
* Stops the maintenance loop. No-op if already stopped.
*/
public function stop(): void
{
if ($this->timerId === false) {
return;
}
Timer::clear($this->timerId);
$this->timerId = false;
Console::info("[Maintenance] Stopped task.");
}
/**
* Removes runtimes inactive beyond the threshold and cleans up temporary files.
*/
private function tick(int $inactiveSeconds): void
{
Console::info("[Maintenance] Running task with threshold $inactiveSeconds seconds.");
$threshold = \time() - $inactiveSeconds;
$candidates = array_filter(
$this->runtimes->list(),
fn ($runtime) => $runtime->updated < $threshold
);
// Remove from in-memory state before removing the container.
// Ensures availability, otherwise we would route requests to terminating runtimes.
$keys = array_keys($candidates);
foreach ($keys as $key) {
$this->runtimes->remove($key);
}
// Then, remove forcefully terminate the associated running container.
$jobs = array_map(
fn ($candidate) => fn () => $this->orchestration->remove($candidate->name, force: true),
$candidates
);
$results = batch($jobs);
$removed = \count(array_filter($results));
Console::info("[Maintenance] Removed {$removed}/" . \count($candidates) . " inactive runtimes.");
$this->cleanupTmp();
}
private function cleanupTmp(): void
{
$localDevice = new Local();
$tmpPath = DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
$prefix = $tmpPath . System::getHostname() . '-';
foreach ($localDevice->getFiles($tmpPath) as $entry) {
if (!\str_starts_with($entry, $prefix)) {
continue;
}
$runtimeName = substr($entry, \strlen($tmpPath));
if ($this->runtimes->exists($runtimeName)) {
continue;
}
if ($localDevice->deletePath($entry)) {
Console::info("[Maintenance] Removed {$entry}.");
}
}
}
}