Skip to content

Commit

Permalink
Merge pull request #4 from worksome/memory
Browse files Browse the repository at this point in the history
feat(memory): JIRA-5517 Peak memory usage per job
  • Loading branch information
Oliver Nybroe authored Jan 27, 2023
2 parents d7bb64e + 7d8dd57 commit c83d362
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 2 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ The default schedule for this is `true`, to disable this event listener,
add `MeterName::ProcessedJobs->value => false` under a `horizon` key in your `telemetry.php` config file.


#### [`ProcessedJobsPeakMemoryUsageListener`](src/Listeners/ProcessedJobsPeakMemoryUsageListener.php)

The `ProcessedJobsPeakMemoryUsageListener` listener will create a histogram over peak memory usage each time a job is processed.
This metric will be registered under the name `horizon_processed_jobs_peak_memory_usage`.

Two listeners are actually registered here, the secondary listener is [`ProcessedJobsPeakMemoryUsagePreparationListener`](src/Listeners/ProcessedJobsPeakMemoryUsagePreparationListener.php)
which will take care of clearing the peak memory usage before a job starts, so for long-running queue workers, we get
the correct number.

The default schedule for this is `true`, to disable this event listener,
add `MeterName::ProcessedJobsPeakMemoryUsage->value => false` under a `horizon` key in your `telemetry.php` config file.


## Testing

```bash
Expand Down
7 changes: 7 additions & 0 deletions config/telemetry.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,12 @@
* The default is `true`, set to `false` to disable this.
*/
MeterName::ProcessedJobs->value => true,

/**
* Whether the processed job memory usage metric listener is enabled.
*
* The default is `true`, set to `false` to disable this.
*/
MeterName::ProcessedJobsPeakMemoryUsage->value => true,
],
];
1 change: 1 addition & 0 deletions src/Enums/MeterName.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum MeterName: string
case CurrentJobs = 'horizon_current_jobs';
case FailedJobs = 'horizon_failed_jobs';
case ProcessedJobs = 'horizon_processed_jobs';
case ProcessedJobsPeakMemoryUsage = 'horizon_processed_jobs_peak_memory_usage';

public function with(string ...$names): string
{
Expand Down
1 change: 1 addition & 0 deletions src/Enums/MeterUnit.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ enum MeterUnit: string
case MasterSupervisors = 'master supervisors';
case Processes = 'processes';
case Jobs = 'jobs';
case Bytes = 'bytes';
}
12 changes: 10 additions & 2 deletions src/HorizonTelemetryServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Support\ServiceProvider;
use Worksome\HorizonTelemetry\Enums\MeterName;
use Worksome\HorizonTelemetry\Listeners\FailedJobsListener;
use Worksome\HorizonTelemetry\Listeners\ProcessedJobsListener;
use Worksome\HorizonTelemetry\Listeners\ProcessedJobsPeakMemoryUsageListener;
use Worksome\HorizonTelemetry\Listeners\ProcessedJobsPeakMemoryUsagePreparationListener;
use Worksome\HorizonTelemetry\Metrics\CurrentJobsMetric;
use Worksome\HorizonTelemetry\Metrics\CurrentMasterSupervisorsMetric;
use Worksome\HorizonTelemetry\Metrics\CurrentProcessesMetric;
Expand All @@ -29,13 +32,18 @@ public function boot(): void
/** @var Repository $config */
$config = $this->app->make(Repository::class);

if ($config->get(self::CONFIG_PREFIX . MeterName::FailedJobs->value)) {
if ($config->get(self::CONFIG_PREFIX . MeterName::FailedJobs->value, true)) {
$dispatcher->listen(JobFailed::class, FailedJobsListener::class);
}

if ($config->get(self::CONFIG_PREFIX . MeterName::ProcessedJobs->value)) {
if ($config->get(self::CONFIG_PREFIX . MeterName::ProcessedJobs->value, true)) {
$dispatcher->listen(JobProcessed::class, ProcessedJobsListener::class);
}

if ($config->get(self::CONFIG_PREFIX . MeterName::ProcessedJobsPeakMemoryUsage->value, true)) {
$dispatcher->listen(JobProcessed::class, ProcessedJobsPeakMemoryUsageListener::class);
$dispatcher->listen(JobProcessing::class, ProcessedJobsPeakMemoryUsagePreparationListener::class);
}
});

$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
Expand Down
39 changes: 39 additions & 0 deletions src/Listeners/ProcessedJobsPeakMemoryUsageListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Worksome\HorizonTelemetry\Listeners;

use Illuminate\Queue\Events\JobProcessed;
use OpenTelemetry\API\Metrics\ObserverInterface;
use Worksome\HorizonTelemetry\Enums\MeterName;
use Worksome\HorizonTelemetry\Enums\MeterUnit;
use Worksome\HorizonTelemetry\MeterProvider;

readonly class ProcessedJobsPeakMemoryUsageListener
{
public function __construct(
private MeterProvider $meterProvider,
) {
}

public function __invoke(JobProcessed $event): void
{
$meter = $this->meterProvider->getMeter(MeterName::ProcessedJobsPeakMemoryUsage);

$histogram = $meter->createHistogram(
MeterName::ProcessedJobsPeakMemoryUsage->value,
MeterUnit::Bytes->value,
'The memory usage per job',
);


$histogram->record(
memory_get_peak_usage(),
[
'name' => $event->job->resolveName(),
'queue' => $event->job->getQueue(),
]
);
}
}
22 changes: 22 additions & 0 deletions src/Listeners/ProcessedJobsPeakMemoryUsagePreparationListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Worksome\HorizonTelemetry\Listeners;

use Illuminate\Queue\Events\JobProcessed;
use OpenTelemetry\API\Metrics\ObserverInterface;
use Worksome\HorizonTelemetry\Enums\MeterName;
use Worksome\HorizonTelemetry\Enums\MeterUnit;
use Worksome\HorizonTelemetry\MeterProvider;

/**
* Needed for cleaning the peak memory usage variable inside PHP between jobs.
*/
readonly class ProcessedJobsPeakMemoryUsagePreparationListener
{
public function __invoke(JobProcessed $event): void
{
memory_reset_peak_usage();
}
}

0 comments on commit c83d362

Please sign in to comment.