Update concurrency handling

This commit is contained in:
DariusIII
2026-05-15 10:44:52 +02:00
parent 83c5624979
commit efa7b748cd
7 changed files with 15 additions and 121 deletions
@@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Providers;
use App\Services\Concurrency\TimeoutAwareProcessDriver;
use Illuminate\Concurrency\ConcurrencyManager;
use Illuminate\Process\Factory as ProcessFactory;
use Illuminate\Support\ServiceProvider;
class ConcurrencyServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
$this->app->afterResolving(ConcurrencyManager::class, function (ConcurrencyManager $manager): void {
$manager->extend('process', function (): TimeoutAwareProcessDriver {
$configuredTimeout = config('nntmux.concurrency_timeout');
$timeout = (int) ($configuredTimeout ?? config('nntmux.multiprocessing_max_child_time', 1800));
return new TimeoutAwareProcessDriver(
app(ProcessFactory::class),
$timeout
);
});
});
}
}
@@ -1,72 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Services\Concurrency;
use Closure;
use Exception;
use Illuminate\Concurrency\ProcessDriver;
use Illuminate\Console\Application;
use Illuminate\Process\Factory as ProcessFactory;
use Illuminate\Process\Pool;
use Illuminate\Support\Arr;
use Laravel\SerializableClosure\SerializableClosure;
class TimeoutAwareProcessDriver extends ProcessDriver
{
public function __construct(
ProcessFactory $processFactory,
protected int $timeout
) {
parent::__construct($processFactory);
}
/**
* Run the given tasks concurrently and return an array containing the results.
*
* @throws \Throwable
*/
public function run(Closure|array $tasks): array
{
$command = Application::formatCommandString('invoke-serialized-closure');
$results = $this->processFactory->pool(function (Pool $pool) use ($tasks, $command) {
foreach (Arr::wrap($tasks) as $key => $task) {
$pool->as((string) $key)
->timeout($this->timeout)
->path(base_path())
->env([
'LARAVEL_INVOKABLE_CLOSURE' => base64_encode(
serialize(new SerializableClosure($task))
),
])
->command($command);
}
})->start()->wait();
return $results->collect()->mapWithKeys(function ($result, $key) {
if ($result->failed()) {
throw new Exception('Concurrent process failed with exit code ['.$result->exitCode().']. Message: '.$result->errorOutput());
}
$output = $result->output();
if (($pos = strpos($output, "\x1f\x8b")) !== false) {
$output = substr($output, 0, $pos);
}
$decodedResult = json_decode($output, true);
if (! $decodedResult['successful']) {
throw new $decodedResult['exception'](
...(! empty(array_filter($decodedResult['parameters']))
? $decodedResult['parameters']
: [$decodedResult['message']])
);
}
return [$key => unserialize($decodedResult['result'])];
})->all();
}
}
+10
View File
@@ -11,6 +11,16 @@ abstract class BaseRunner
{ {
public function __construct() {} public function __construct() {}
/**
* Resolve the configured timeout (seconds) for Laravel's Concurrency::run() calls.
*/
protected function concurrencyTimeout(): int
{
$configured = config('nntmux.concurrency_timeout');
return (int) ($configured ?? config('nntmux.multiprocessing_max_child_time', 1800));
}
protected function buildDnrCommand(string $args): string protected function buildDnrCommand(string $args): string
{ {
// Convert legacy command arguments to new artisan commands // Convert legacy command arguments to new artisan commands
+2 -2
View File
@@ -71,7 +71,7 @@ class PostProcessRunner extends BaseRunner
} }
try { try {
$results = Concurrency::run($tasks); $results = Concurrency::run($tasks, $this->concurrencyTimeout());
foreach ($results as $taskIdx => $output) { foreach ($results as $taskIdx => $output) {
echo $output; echo $output;
@@ -267,7 +267,7 @@ class PostProcessRunner extends BaseRunner
} }
try { try {
$results = Concurrency::run($tasks); $results = Concurrency::run($tasks, $this->concurrencyTimeout());
foreach ($results as $taskIdx => $output) { foreach ($results as $taskIdx => $output) {
echo $output; echo $output;
+3 -3
View File
@@ -62,7 +62,7 @@ class ReleasesRunner extends BaseRunner
} }
try { try {
$results = Concurrency::run($tasks); $results = Concurrency::run($tasks, $this->concurrencyTimeout());
foreach ($results as $groupId => $output) { foreach ($results as $groupId => $output) {
echo $output; echo $output;
@@ -111,7 +111,7 @@ class ReleasesRunner extends BaseRunner
} }
try { try {
$results = Concurrency::run($tasks); $results = Concurrency::run($tasks, $this->concurrencyTimeout());
foreach ($results as $groupId => $output) { foreach ($results as $groupId => $output) {
echo $output; echo $output;
@@ -184,7 +184,7 @@ class ReleasesRunner extends BaseRunner
} }
try { try {
$results = Concurrency::run($tasks); $results = Concurrency::run($tasks, $this->concurrencyTimeout());
foreach ($results as $taskIdx => $output) { foreach ($results as $taskIdx => $output) {
echo $output; echo $output;
-2
View File
@@ -3,7 +3,6 @@
use App\Providers\AdditionalProcessingServiceProvider; use App\Providers\AdditionalProcessingServiceProvider;
use App\Providers\AppServiceProvider; use App\Providers\AppServiceProvider;
use App\Providers\CategorizationServiceProvider; use App\Providers\CategorizationServiceProvider;
use App\Providers\ConcurrencyServiceProvider;
use App\Providers\ForumServiceProvider; use App\Providers\ForumServiceProvider;
use App\Providers\HorizonServiceProvider; use App\Providers\HorizonServiceProvider;
use App\Providers\ProcessingServiceProvider; use App\Providers\ProcessingServiceProvider;
@@ -15,7 +14,6 @@ return [
AdditionalProcessingServiceProvider::class, AdditionalProcessingServiceProvider::class,
AppServiceProvider::class, AppServiceProvider::class,
CategorizationServiceProvider::class, CategorizationServiceProvider::class,
ConcurrencyServiceProvider::class,
ForumServiceProvider::class, ForumServiceProvider::class,
HorizonServiceProvider::class, HorizonServiceProvider::class,
ProcessingServiceProvider::class, ProcessingServiceProvider::class,
-11
View File
@@ -786,17 +786,6 @@ parameters:
count: 1 count: 1
path: app/Services/Categorization/CategorizationService.php path: app/Services/Categorization/CategorizationService.php
-
message: '#^Method App\\Services\\Concurrency\\TimeoutAwareProcessDriver\:\:run\(\) has parameter \$tasks with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: app/Services/Concurrency/TimeoutAwareProcessDriver.php
-
message: '#^Method App\\Services\\Concurrency\\TimeoutAwareProcessDriver\:\:run\(\) return type has no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: app/Services/Concurrency/TimeoutAwareProcessDriver.php
- -
message: '#^Strict comparison using \!\=\= between non\-empty\-array\<mixed, mixed\> and array\{\} will always evaluate to true\.$#' message: '#^Strict comparison using \!\=\= between non\-empty\-array\<mixed, mixed\> and array\{\} will always evaluate to true\.$#'