-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Limiting Max Child Processes Via an Exec Env Var API #69
base: 4.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Neighborhoods\Kojo\Environment; | ||
|
||
use InvalidArgumentException; | ||
use LogicException; | ||
|
||
class Memory implements MemoryInterface | ||
{ | ||
protected $EnvironmentMaximumMemoryValue; | ||
protected $EnvironmentMaximumMemoryInputUnit; | ||
protected $WorkerMaximumMemoryValue; | ||
protected $WorkerMaximumMemoryInputUnit; | ||
protected $MaximumNumberOfWorkers; | ||
|
||
public function getMaximumNumberOfWorkers(): int | ||
{ | ||
if ($this->MaximumNumberOfWorkers === null) { | ||
$environmentMaximumMemoryValue = $this->getEnvironmentMaximumMemoryValue(); | ||
$workerMaximumMemoryValue = $this->getWorkerMaximumMemoryValue(); | ||
$maximumNumberOfWorkers = (int)floor($environmentMaximumMemoryValue / $workerMaximumMemoryValue); | ||
$this->MaximumNumberOfWorkers = $maximumNumberOfWorkers; | ||
} | ||
|
||
return $this->MaximumNumberOfWorkers; | ||
} | ||
|
||
public function setEnvironmentMaximumMemoryValue(int $EnvironmentMaximumMemoryValue): MemoryInterface | ||
{ | ||
if ($this->EnvironmentMaximumMemoryValue !== null) { | ||
throw new LogicException('Environment Maximum Memory Value is already set.'); | ||
} | ||
|
||
$this->EnvironmentMaximumMemoryValue = $this->getBytes( | ||
$EnvironmentMaximumMemoryValue, | ||
$this->getEnvironmentMaximumMemoryInputUnit() | ||
); | ||
|
||
return $this; | ||
} | ||
|
||
protected function getEnvironmentMaximumMemoryValue(): int | ||
{ | ||
if ($this->EnvironmentMaximumMemoryValue === null) { | ||
throw new LogicException('Environment Maximum Memory Value has not been set.'); | ||
} | ||
|
||
return $this->EnvironmentMaximumMemoryValue; | ||
} | ||
|
||
public function setEnvironmentMaximumMemoryInputUnit(string $EnvironmentMaximumMemoryUnit): MemoryInterface | ||
{ | ||
if ($this->EnvironmentMaximumMemoryInputUnit !== null) { | ||
throw new LogicException('Environment Maximum Memory Input Unit is already set.'); | ||
} | ||
|
||
$this->assertValidMemoryUnit($EnvironmentMaximumMemoryUnit); | ||
$this->EnvironmentMaximumMemoryInputUnit = $EnvironmentMaximumMemoryUnit; | ||
|
||
return $this; | ||
} | ||
|
||
protected function getEnvironmentMaximumMemoryInputUnit(): string | ||
{ | ||
if ($this->EnvironmentMaximumMemoryInputUnit === null) { | ||
throw new LogicException('Environment Maximum Memory Input Unit has not been set.'); | ||
} | ||
|
||
return $this->EnvironmentMaximumMemoryInputUnit; | ||
} | ||
|
||
public function setWorkerMaximumMemoryValue(int $WorkerMaximumMemoryValue): MemoryInterface | ||
{ | ||
if ($this->WorkerMaximumMemoryValue !== null) { | ||
throw new LogicException('Worker Maximum Memory Value is already set.'); | ||
} | ||
|
||
$this->WorkerMaximumMemoryValue = $this->getBytes( | ||
$WorkerMaximumMemoryValue, | ||
$this->getWorkerMaximumMemoryInputUnit() | ||
); | ||
|
||
return $this; | ||
} | ||
|
||
public function getWorkerMaximumMemoryValue(): int | ||
{ | ||
if ($this->WorkerMaximumMemoryValue === null) { | ||
throw new LogicException('Worker Maximum Memory Value has not been set.'); | ||
} | ||
|
||
return $this->WorkerMaximumMemoryValue; | ||
} | ||
|
||
public function setWorkerMaximumMemoryInputUnit(string $WorkerMaximumMemoryUnit): MemoryInterface | ||
{ | ||
if ($this->WorkerMaximumMemoryInputUnit !== null) { | ||
throw new LogicException('Worker Maximum Memory Input Unit is already set.'); | ||
} | ||
|
||
$this->assertValidMemoryUnit($WorkerMaximumMemoryUnit); | ||
|
||
$this->WorkerMaximumMemoryInputUnit = $WorkerMaximumMemoryUnit; | ||
|
||
return $this; | ||
} | ||
|
||
protected function getWorkerMaximumMemoryInputUnit(): string | ||
{ | ||
if ($this->WorkerMaximumMemoryInputUnit === null) { | ||
throw new LogicException('Worker Maximum Memory Input Unit has not been set.'); | ||
} | ||
|
||
return $this->WorkerMaximumMemoryInputUnit; | ||
} | ||
|
||
protected function getBytes(int $Value, string $Unit): int | ||
{ | ||
switch ($Unit) { | ||
/** @noinspection PhpMissingBreakStatementInspection */ | ||
case self::Gibibyte: | ||
$Value *= 1024; | ||
/** @noinspection PhpMissingBreakStatementInspection */ | ||
case self::Mebibyte: | ||
$Value *= 1024; | ||
case self::Kibibyte: | ||
$Value *= 1024; | ||
} | ||
|
||
return $Value; | ||
} | ||
|
||
protected function assertValidMemoryUnit(string $MemoryUnit): MemoryInterface | ||
{ | ||
switch ($MemoryUnit) { | ||
case self::Kibibyte: | ||
case self::Mebibyte: | ||
case self::Gibibyte: | ||
break; | ||
default: | ||
throw new InvalidArgumentException(sprintf('Memory Unit [%s] is not KiB, MiB, or GiB.', $MemoryUnit)); | ||
} | ||
|
||
return $this; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Neighborhoods\Kojo\Environment\Memory; | ||
|
||
use Neighborhoods\Kojo\Environment\MemoryInterface; | ||
|
||
/** @codeCoverageIgnore */ | ||
trait AwareTrait | ||
{ | ||
protected $NeighborhoodsKojoEnvironmentMemory; | ||
|
||
public function setEnvironmentMemory(MemoryInterface $environmentMemory): self | ||
{ | ||
assert(!$this->hasEnvironmentMemory(), | ||
new \LogicException('NeighborhoodsKojoEnvironmentMemory is already set.')); | ||
$this->NeighborhoodsKojoEnvironmentMemory = $environmentMemory; | ||
|
||
return $this; | ||
} | ||
|
||
protected function getEnvironmentMemory(): MemoryInterface | ||
{ | ||
assert($this->hasEnvironmentMemory(), new \LogicException('NeighborhoodsKojoEnvironmentMemory is not set.')); | ||
|
||
return $this->NeighborhoodsKojoEnvironmentMemory; | ||
} | ||
|
||
protected function hasEnvironmentMemory(): bool | ||
{ | ||
return isset($this->NeighborhoodsKojoEnvironmentMemory); | ||
} | ||
|
||
protected function unsetEnvironmentMemory(): self | ||
{ | ||
assert($this->hasEnvironmentMemory(), new \LogicException('NeighborhoodsKojoEnvironmentMemory is not set.')); | ||
unset($this->NeighborhoodsKojoEnvironmentMemory); | ||
|
||
return $this; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
declare(strict_types=1); | ||
|
||
namespace Neighborhoods\Kojo\Environment; | ||
|
||
interface MemoryInterface | ||
{ | ||
public const Gibibyte = 'GiB'; | ||
public const Mebibyte = 'MiB'; | ||
public const Kibibyte = 'KiB'; | ||
|
||
public function setEnvironmentMaximumMemoryValue(int $EnvironmentMaximumMemoryValue): MemoryInterface; | ||
|
||
public function setWorkerMaximumMemoryInputUnit(string $WorkerMaximumMemoryUnit): MemoryInterface; | ||
|
||
public function getMaximumNumberOfWorkers(): int; | ||
|
||
public function setWorkerMaximumMemoryValue(int $WorkerMaximumMemoryValue): MemoryInterface; | ||
|
||
public function setEnvironmentMaximumMemoryInputUnit(string $EnvironmentMaximumMemoryUnit): MemoryInterface; | ||
|
||
public function getWorkerMaximumMemoryValue(): int; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
parameters: | ||
Neighborhoods\Kojo\Environment\MemoryInterface.EnvironmentMaximumMemoryInputValue: '%env(ENVIRONMENT_MAXIMUM_MEMORY_INPUT_VALUE)%' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allows missing values since this will be a 4.x release. |
||
Neighborhoods\Kojo\Environment\MemoryInterface.EnvironmentMaximumMemoryInputUnit: '%env(ENVIRONMENT_MAXIMUM_MEMORY_INPUT_UNIT)%' | ||
Neighborhoods\Kojo\Environment\MemoryInterface.WorkerMaximumMemoryValue: '%env(WORKER_MAXIMUM_MEMORY_INPUT_VALUE)%' | ||
Neighborhoods\Kojo\Environment\MemoryInterface.WorkerMaximumMemoryUnit: '%env(WORKER_MAXIMUM_MEMORY_INPUT_UNIT)%' | ||
services: | ||
Neighborhoods\Kojo\Environment\MemoryInterface: | ||
class: Neighborhoods\Kojo\Environment\Memory | ||
shared: true | ||
public: false | ||
calls: | ||
- [setEnvironmentMaximumMemoryValue, ['%Neighborhoods\Kojo\Environment\MemoryInterface.EnvironmentMaximumMemoryInputValue%']] | ||
- [setEnvironmentMaximumMemoryInputUnit, ['%Neighborhoods\Kojo\Environment\MemoryInterface.EnvironmentMaximumMemoryInputUnit%']] | ||
- [setWorkerMaximumMemoryValue, ['%Neighborhoods\Kojo\Environment\MemoryInterface.WorkerMaximumMemoryValue%']] | ||
- [setWorkerMaximumMemoryInputUnit, ['%Neighborhoods\Kojo\Environment\MemoryInterface.WorkerMaximumMemoryUnit%']] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,8 @@ | |
use Neighborhoods\Kojo\Process; | ||
use Neighborhoods\Kojo\Scheduler; | ||
use Neighborhoods\Kojo\Selector; | ||
use Neighborhoods\Kojo\Environment; | ||
use Throwable; | ||
|
||
class Job extends Forked implements JobInterface | ||
{ | ||
|
@@ -16,6 +18,7 @@ class Job extends Forked implements JobInterface | |
use Scheduler\AwareTrait; | ||
use Selector\AwareTrait; | ||
use Process\Pool\Factory\AwareTrait; | ||
use Environment\Memory\AwareTrait; | ||
|
||
protected function _run(): Forked | ||
{ | ||
|
@@ -26,12 +29,22 @@ protected function _run(): Forked | |
$this->_getScheduler()->scheduleStaticJobs(); | ||
$this->_getMaintainer()->updatePendingJobs(); | ||
$this->_getMaintainer()->deleteCompletedJobs(); | ||
$this->applyMemoryLimit(); | ||
$this->_getForeman()->workWorker(); | ||
} catch (\Throwable $throwable) { | ||
} catch (Throwable $throwable) { | ||
$this->_getLogger()->critical($throwable->getMessage(), ['exception' => $throwable]); | ||
$this->_setOrReplaceExitCode(255); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
protected function applyMemoryLimit(): JobInterface | ||
{ | ||
$processMaximumMemoryValue = $this->getEnvironmentMemory()->getWorkerMaximumMemoryValue(); | ||
$this->_getLogger()->debug(sprintf('Applying memory limit of [%s] bytes.', $processMaximumMemoryValue)); | ||
ini_set('memory_limit', $processMaximumMemoryValue); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that PHP will set |
||
|
||
return $this; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should have a default exceptional case here.