feat: long running queue with 1 hour of timeout

This commit is contained in:
Andras Bacsai 2023-06-28 18:20:01 +02:00
parent ba18c589f0
commit 80af200c9f
9 changed files with 57 additions and 19 deletions

View File

@ -17,12 +17,12 @@
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Spatie\Activitylog\Models\Activity; use Spatie\Activitylog\Models\Activity;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Spatie\Url\Url; use Spatie\Url\Url;
use Throwable;
use Visus\Cuid2\Cuid2; use Visus\Cuid2\Cuid2;
class ApplicationDeploymentJob implements ShouldQueue class ApplicationDeploymentJob implements ShouldQueue
@ -46,7 +46,7 @@ class ApplicationDeploymentJob implements ShouldQueue
private ApplicationPreview|null $preview = null; private ApplicationPreview|null $preview = null;
public static int $batch_counter = 0; public static int $batch_counter = 0;
public $timeout = 10200; public $failOnTimeout = true;
public function __construct( public function __construct(
public int $application_deployment_queue_id, public int $application_deployment_queue_id,
@ -128,7 +128,8 @@ public function handle(): void
"echo '\nOops something is not okay, are you okay? 😢'", "echo '\nOops something is not okay, are you okay? 😢'",
"echo '\n\n{$e->getMessage()}'", "echo '\n\n{$e->getMessage()}'",
]); ]);
$this->fail(); ray($e);
$this->fail($e->getMessage());
} finally { } finally {
if (isset($this->docker_compose)) { if (isset($this->docker_compose)) {
Storage::disk('deployments')->put(Str::kebab($this->application->name) . '/docker-compose.yml', $this->docker_compose); Storage::disk('deployments')->put(Str::kebab($this->application->name) . '/docker-compose.yml', $this->docker_compose);
@ -142,9 +143,15 @@ private function start_builder_image()
$this->execute_now([ $this->execute_now([
"echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '", "echo -n 'Pulling latest version of the builder image (ghcr.io/coollabsio/coolify-builder)... '",
]); ]);
$this->execute_now([ if (isDev()) {
"docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder", $this->execute_now([
], isDebuggable: true); "docker run -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock coolify-builder",
], isDebuggable: true);
} else {
$this->execute_now([
"docker run --pull=always -d --name {$this->deployment_uuid} --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/coollabsio/coolify-builder",
], isDebuggable: true);
}
$this->execute_now([ $this->execute_now([
"echo 'Done.'" "echo 'Done.'"
]); ]);
@ -291,8 +298,9 @@ private function deploy()
$this->next(ProcessStatus::FINISHED->value); $this->next(ProcessStatus::FINISHED->value);
} }
public function failed(): void public function failed(Throwable $exception): void
{ {
ray($exception);
$this->next(ProcessStatus::ERROR->value); $this->next(ProcessStatus::ERROR->value);
} }
@ -329,7 +337,7 @@ private function next(string $status)
} }
private function execute_in_builder(string $command) private function execute_in_builder(string $command)
{ {
return "docker exec {$this->deployment_uuid} bash -c '{$command}'"; return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1'";
} }
private function generate_environment_variables($ports) private function generate_environment_variables($ports)
{ {

View File

@ -34,7 +34,7 @@ function queue_application_deployment(int $application_id, string $deployment_uu
force_rebuild: $force_rebuild, force_rebuild: $force_rebuild,
rollback_commit: $commit, rollback_commit: $commit,
pull_request_id: $pull_request_id, pull_request_id: $pull_request_id,
)); ))->onConnection('long-running')->onQueue('long-running');
} }
function queue_next_deployment(Application $application) function queue_next_deployment(Application $application)
@ -47,6 +47,6 @@ function queue_next_deployment(Application $application)
deployment_uuid: $next_found->deployment_uuid, deployment_uuid: $next_found->deployment_uuid,
force_rebuild: $next_found->force_rebuild, force_rebuild: $next_found->force_rebuild,
pull_request_id: $next_found->pull_request_id pull_request_id: $next_found->pull_request_id
)); ))->onConnection('long-running')->onQueue('long-running');
} }
} }

View File

@ -64,7 +64,7 @@ function git_api(GithubApp|GitlabApp $source, string $endpoint, string $method =
} }
$json = $response->json(); $json = $response->json();
if ($response->failed() && $throwError) { if ($response->failed() && $throwError) {
throw new \Exception("Failed to get data from {$source->name} with error: " . $json['message']); throw new \Exception("Failed to get data from {$source->name} with error:<br><br>" . $json['message']);
} }
return [ return [
'rate_limit_remaining' => $response->header('X-RateLimit-Remaining'), 'rate_limit_remaining' => $response->header('X-RateLimit-Remaining'),

View File

@ -190,7 +190,18 @@
'maxJobs' => 0, 'maxJobs' => 0,
'memory' => 128, 'memory' => 128,
'tries' => 1, 'tries' => 1,
'timeout' => 3600, 'timeout' => 300,
'nice' => 0,
],
'long-running' => [
'connection' => 'redis',
'queue' => ['long-running'],
'balance' => 'auto',
'maxTime' => 0,
'maxJobs' => 0,
'memory' => 128,
'tries' => 1,
'timeout' => 3560,
'nice' => 0, 'nice' => 0,
], ],
], ],
@ -203,6 +214,12 @@
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1), 'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1), 'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
], ],
'long-running' => [
'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 10),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
],
], ],
'local' => [ 'local' => [
@ -212,6 +229,12 @@
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1), 'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1), 'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
], ],
'long-running' => [
'autoScalingStrategy' => 'size',
'maxProcesses' => env('HORIZON_MAX_PROCESSES', 10),
'balanceMaxShift' => env('HORIZON_BALANCE_MAX_SHIFT', 1),
'balanceCooldown' => env('HORIZON_BALANCE_COOLDOWN', 1),
],
], ],
], ],
]; ];

View File

@ -33,7 +33,14 @@
'sync' => [ 'sync' => [
'driver' => 'sync', 'driver' => 'sync',
], ],
'long-running' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'long-running',
'retry_after' => 3600,
'block_for' => null,
'after_commit' => false,
],
'database' => [ 'database' => [
'driver' => 'database', 'driver' => 'database',
'table' => 'jobs', 'table' => 'jobs',
@ -66,7 +73,7 @@
'driver' => 'redis', 'driver' => 'redis',
'connection' => 'default', 'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'), 'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90, 'retry_after' => 300,
'block_for' => null, 'block_for' => null,
'after_commit' => false, 'after_commit' => false,
], ],

View File

@ -35,5 +35,5 @@ RUN if [[ ${TARGETPLATFORM} == 'linux/arm64' ]]; then \
;fi ;fi
ENTRYPOINT ["/sbin/tini", "--"] ENTRYPOINT ["/sbin/tini", "--"]
CMD ["sh", "-c", "while true; do sleep 3600 && exit 0; done"] CMD ["sh", "-c", "while true; do sleep 1; done"]

View File

@ -1,6 +1,6 @@
<div class="flex items-center gap-2 pb-4"> <div class="flex items-center gap-2 pb-4">
<h2>Logs</h2> <h2>Logs</h2>
@if (data_get($activity, 'properties.status') === 'in_progress') @if (data_get($activity, 'properties.status') === 'in_progress' || data_get($activity, 'properties.status') === 'queued')
<x-forms.button wire:click.prevent="cancel">Cancel deployment</x-forms.button> <x-forms.button wire:click.prevent="cancel">Cancel deployment</x-forms.button>
@endif @endif
</div> </div>

View File

@ -67,8 +67,8 @@ class="hover:no-underline">
dayjs.extend(window.dayjs_plugin_relativeTime); dayjs.extend(window.dayjs_plugin_relativeTime);
Alpine.data('elapsedTime', (uuid, status, created_at, updated_at) => ({ Alpine.data('elapsedTime', (uuid, status, created_at, updated_at) => ({
finished_time: '0s', finished_time: 'calculating...',
started_time: '0s', started_time: 'calculating...',
init() { init() {
if (timers[uuid]) { if (timers[uuid]) {
clearInterval(timers[uuid]); clearInterval(timers[uuid]);

View File

@ -71,7 +71,7 @@ class="relative flex duration-300 transform transition ease-in-out max-w-md w-fu
</svg> </svg>
</div> </div>
</template> </template>
<span x-text="toast.message" /> <span x-html="toast.message" />
</i> </i>
@if ($closeable) @if ($closeable)