Skip to content

Commit ff32079

Browse files
author
Karsten Nilsen
committed
Add a memory threshold setting for worker restarts
Introduce `frankenphp_min_memory_mb` option to enforce a minimum memory threshold before handling new requests. This prevents running workers when memory is critically low, enhancing stability. Option is configurable via environment variables or runtime options. Default value is -1 which disables this new functionality.
1 parent 182563a commit ff32079

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

src/frankenphp-symfony/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,17 @@ return function (array $context) {
4040
## Options
4141

4242
* `frankenphp_loop_max`: the number of requests after which the worker must restart, to prevent weird memory leaks (default to `500`, set to `-1` to never restart)
43+
* `frankenphp_min_memory_mb`: the minimum amount of available memory (in MB) required before handling a new request. If available memory falls below this threshold after the first request, the worker will restart (default to `-1` to disable)
44+
45+
These options can also be configured via environment variables:
46+
47+
```
48+
docker run \
49+
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
50+
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
51+
-e FRANKENPHP_LOOP_MAX=500 \
52+
-e FRANKENPHP_MIN_MEMORY_MB=64 \
53+
-v $PWD:/app \
54+
-p 80:80 -p 443:443 \
55+
dunglas/frankenphp
56+
```

src/frankenphp-symfony/src/Runner.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class Runner implements RunnerInterface
1919
public function __construct(
2020
private HttpKernelInterface $kernel,
2121
private int $loopMax,
22+
private int $minMemoryMb = -1,
2223
) {
2324
}
2425

@@ -56,8 +57,40 @@ public function run(): int
5657
}
5758

5859
gc_collect_cycles();
59-
} while ($ret && (-1 === $this->loopMax || ++$loops < $this->loopMax));
60+
++$loops;
61+
} while ($ret && (-1 === $this->loopMax || $loops < $this->loopMax) && $this->hasAvailableMemory($loops));
6062

6163
return 0;
6264
}
65+
66+
private function hasAvailableMemory(int $loops): bool
67+
{
68+
if (-1 === $this->minMemoryMb || 1 === $loops) { // Always allow the first cycle
69+
return true;
70+
}
71+
72+
$memoryLimit = \ini_get('memory_limit');
73+
if (-1 === (int) $memoryLimit || '' === $memoryLimit) {
74+
return true;
75+
}
76+
$memoryLimit = \trim($memoryLimit);
77+
if (strlen($memoryLimit) < 2) {
78+
return true; // Unexpected value, consider enough mem available
79+
}
80+
$last = \strtolower($memoryLimit[\strlen($memoryLimit) - 1]);
81+
if (!\in_array($last, ['g', 'm', 'k'])) {
82+
return true; // Unexpected value, consider enough mem available
83+
}
84+
85+
$value = (int) $memoryLimit; // (int) stops at first non-numeric char
86+
$memoryLimitMb = match ($last) {
87+
'g' => $value * 1024,
88+
'k' => (int) ($value / 1024),
89+
default => $value,
90+
};
91+
$usedMemoryMb = \memory_get_usage(true) / 1024 / 1024;
92+
$availableMemoryMb = $memoryLimitMb - $usedMemoryMb;
93+
94+
return $availableMemoryMb >= $this->minMemoryMb;
95+
}
6396
}

src/frankenphp-symfony/src/Runtime.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,21 @@ class Runtime extends SymfonyRuntime
1818
/**
1919
* @param array{
2020
* frankenphp_loop_max?: int,
21+
* frankenphp_min_memory_mb?: int,
2122
* } $options
2223
*/
2324
public function __construct(array $options = [])
2425
{
2526
$options['frankenphp_loop_max'] = (int) ($options['frankenphp_loop_max'] ?? $_SERVER['FRANKENPHP_LOOP_MAX'] ?? $_ENV['FRANKENPHP_LOOP_MAX'] ?? 500);
27+
$options['frankenphp_min_memory_mb'] = (int) ($options['frankenphp_min_memory_mb'] ?? $_SERVER['FRANKENPHP_MIN_MEMORY_MB'] ?? $_ENV['FRANKENPHP_MIN_MEMORY_MB'] ?? -1);
2628

2729
parent::__construct($options);
2830
}
2931

3032
public function getRunner(?object $application): RunnerInterface
3133
{
3234
if ($application instanceof HttpKernelInterface && ($_SERVER['FRANKENPHP_WORKER'] ?? false)) {
33-
return new Runner($application, $this->options['frankenphp_loop_max']);
35+
return new Runner($application, $this->options['frankenphp_loop_max'], $this->options['frankenphp_min_memory_mb']);
3436
}
3537

3638
return parent::getRunner($application);

0 commit comments

Comments
 (0)