Skip to content

Commit

Permalink
[Feature] Measure Retrieve data (#1393)
Browse files Browse the repository at this point in the history
* Add MeasureRetrieveData event

* phpstan

* add laravel pulse card

* add config enable / disable

* fix cypress
  • Loading branch information
luanfreitasdev authored Feb 17, 2024
1 parent c640aac commit 2643640
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 9 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
"laravel/prompts": "^0.1.15"
},
"require-dev": {
"composer/composer": "^2.6.6",
"composer/composer": "^2.7.1",
"laravel/pint": "^1.13.10",
"laradumps/laradumps-core": "^1.1",
"larastan/larastan": "^2.8.1",
"pestphp/pest": "2.28.0",
"pestphp/pest": "^2.33.0",
"orchestra/testbench": "8.19|^9.0"
},
"suggest": {
Expand Down
2 changes: 2 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ parameters:
- './src/Components/Exports/OpenSpout/*'
- './src/Components/Actions/Macros.php'
- './src/Helpers/Actions.php'
- './src/Livewire/MeasurementCard.php'
- './src/Recorders/PowerGridRecorder.php'
87 changes: 87 additions & 0 deletions resources/views/livewire/measurement-card.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<x-pulse::card :cols="$cols" :rows="$rows" :class="$class" wire:poll.5s="">
<x-pulse::card-header name="PowerGrid" details="10 most recent records">
<x-slot:icon>⚡</x-slot:icon>
</x-pulse::card-header>

<x-pulse::scroll :expand="$expand">
<div class="grid grid-cols-1 gap-2">
@if ($measurements->isEmpty())
<x-pulse::no-results />
@elseif (!$config['enabled'])
<div class="h-full flex flex-col items-center justify-center p-4">
<x-pulse::icons.no-pulse class="h-8 w-8 stroke-gray-300 dark:stroke-gray-700" />
<p class="mt-2 text-sm text-gray-400 dark:text-gray-600">
PowerGrid metering has been disabled.
</p>
</div>

@else
<div class="grid grid-cols-2 gap-3 text-center">
<div class="flex flex-col justify-center sm:block">
<div class="font-bold text-gray-700 dark:text-gray-300 tabular-nums">
<span class="uppercase text-xl">{{ round($averageRetrieveData, 2) }}</span> <span class="!text-sm">ms</span>
</div>

<span class="text-xs uppercase font-bold text-gray-500 dark:text-gray-400">
retrieve Data
</span>
</div>
<div class="flex flex-col justify-center sm:block">
<div class="font-bold text-gray-700 dark:text-gray-300 tabular-nums">
<span class="uppercase text-xl">{{ round($averageQueriesTime, 2) }}</span> <span class="!text-sm">ms</span>
</div>

<span class="text-xs uppercase font-bold text-gray-500 dark:text-gray-400">
query Time
</span>
</div>
</div>
<div>
<x-pulse::table>
<colgroup>
<col />
<col />
<col />
</colgroup>
<x-pulse::thead>
<tr>
<x-pulse::th class="text-left">Table</x-pulse::th>
<x-pulse::th class="text-right">Time</x-pulse::th>
<x-pulse::th class="text-right">Query Time</x-pulse::th>
<x-pulse::th class="text-right">Total Queries</x-pulse::th>
<x-pulse::th class="text-right">Created at</x-pulse::th>
</tr>
</x-pulse::thead>
<tbody>
@foreach ($measurements as $measurement)
<tr wire:key="{{ $loop->index }}-spacer" class="h-2 first:h-0"></tr>
<tr wire:key="{{ $loop->index }}-row">
<x-pulse::td class="max-w-[1px]">
<code class="block text-xs text-gray-900 dark:text-gray-100 truncate" title="{{ data_get($measurement, 'tableName') }}">
{{ data_get($measurement, 'tableName') }}
</code>
</x-pulse::td>
<x-pulse::td numeric class="text-gray-700 dark:text-gray-300 font-bold">
{{ data_get($measurement, 'retrieveData') }} ms
</x-pulse::td>
<x-pulse::td numeric class="text-gray-700 dark:text-gray-300 font-bold">
{{ data_get($measurement, 'queriesTime') }} ms
</x-pulse::td>
<x-pulse::td numeric class="text-gray-700 dark:text-gray-300 font-bold">
{{ count(data_get($measurement, 'queries') ) }}
</x-pulse::td>
<x-pulse::td numeric class="text-gray-700 text-sm dark:text-gray-300 font-bold">
@php
$createdAt = \Illuminate\Support\Carbon::createFromTimestamp(data_get($measurement, 'timestamp'));
@endphp
{{ $createdAt->diffForHumans() }}
</x-pulse::td>
</tr>
@endforeach
</tbody>
</x-pulse::table>
@endif
</div>
</div>
</x-pulse::scroll>
</x-pulse::card>
22 changes: 22 additions & 0 deletions src/Events/MeasureRetrieveData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace PowerComponents\LivewirePowerGrid\Events;

class MeasureRetrieveData
{
/**
* @param string $tableName Name of the table where the data was retrieved.
* @param float $retrieveData Total time spent on the data retrieval operation.
* @param float $queriesTime Total time spent on executing queries.
* @param bool $cached Indicates whether the data was retrieved from the cache.
* @param array $queries List of queries executed (query, binding, time).
*/
public function __construct(
public string $tableName,
public float $retrieveData,
public float $queriesTime = 0,
public bool $cached = false,
public array $queries = [],
) {
}
}
53 changes: 53 additions & 0 deletions src/Livewire/MeasurementCard.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace PowerComponents\LivewirePowerGrid\Livewire;

use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\Config;
use Livewire\Attributes\Lazy;
use PowerComponents\LivewirePowerGrid\Recorders\PowerGridRecorder;

#[Lazy]
class MeasurementCard extends \Laravel\Pulse\Livewire\Card
{
public function render(): View
{
$config = Config::get('pulse.recorders.' . PowerGridRecorder::class);

$averageRetrieveData = 0;
$averageQueriesTime = 0;
$measurements = collect();

if (data_get($config, 'enabled')) {
$measurements = \Laravel\Pulse\Facades\Pulse::values('powergrid-measurements')
->map(function ($item) {
/** @var array $value */
$value = json_decode($item->value, true);
$item->tableName = $value['tableName'];
$item->retrieveData = $value['retrieveData'];
$item->queriesTime = $value['queriesTime'];
$item->cached = $value['cached'];
$item->queries = $value['queries'];
unset($item->value);

return $item;
})
->sort(fn ($a, $b) => $b->timestamp <=> $a->timestamp)
->take(10)
->values();

$averageRetrieveData = $measurements->avg(fn ($item) => $item->retrieveData);
$averageQueriesTime = $measurements->avg(fn ($item) => $item->queriesTime);
}

return view(
'livewire-powergrid::livewire.measurement-card',
compact(
'measurements',
'averageQueriesTime',
'averageRetrieveData',
'config'
)
);
}
}
41 changes: 37 additions & 4 deletions src/PowerGridComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
namespace PowerComponents\LivewirePowerGrid;

use Exception;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\{Factory, View};
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Pagination\{LengthAwarePaginator, Paginator};
use Illuminate\Support\{Arr, Collection as BaseCollection, Facades\Cache};

use Livewire\{Attributes\Computed, Component, Features\SupportQueryString\BaseUrl, WithPagination};
use Livewire\{Attributes\Computed, Component, WithPagination};

use function Livewire\{invade, store};
use PowerComponents\LivewirePowerGrid\Events\MeasureRetrieveData;

use PowerComponents\LivewirePowerGrid\Themes\ThemeBase;
use Throwable;
Expand Down Expand Up @@ -157,8 +158,28 @@ public function visibleColumns(): BaseCollection
#[Computed]
protected function getCachedData(): mixed
{
$start = microtime(true);

if (!Cache::supportsTags() || !boolval(data_get($this->setUp, 'cache.enabled'))) {
return $this->readyToLoad ? $this->fillData() : collect();
$results = $this->readyToLoad ? $this->fillData() : collect();

$retrieveData = round((microtime(true) - $start) * 1000);

$queries = $this->processDataSourceInstance?->queryLog() ?? [];

/** @var float $queriesTime */
$queriesTime = collect($queries)->sum('time');

app(Dispatcher::class)->dispatch(
new MeasureRetrieveData(
$this->tableName,
retrieveData: $retrieveData,
queriesTime: $queriesTime,
queries: $queries,
)
);

return $results;
}

if (!$this->readyToLoad) {
Expand All @@ -173,9 +194,21 @@ protected function getCachedData(): mixed
$tag = $prefix . ($customTag ?: 'powergrid-' . $this->datasource()->getModel()->getTable() . '-' . $this->tableName);
$cacheKey = join('-', $this->getCacheKeys());

return $forever
$results = $forever
? Cache::tags($tag)->rememberForever($cacheKey, fn () => $this->fillData())
: Cache::tags($tag)->remember($cacheKey, $ttl, fn () => $this->fillData());

$time = round((microtime(true) - $start) * 1000);

app(Dispatcher::class)->dispatch(
new MeasureRetrieveData(
$this->tableName,
$time,
cached: true,
)
);

return $results;
}

protected function getCacheKeys(): array
Expand Down
19 changes: 17 additions & 2 deletions src/ProcessDataSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Illuminate\Database\Eloquent\{Builder as EloquentBuilder, Model};
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\{Collection as BaseCollection, Str};
use Illuminate\Support\{Collection as BaseCollection, Facades\DB, Str};
use PowerComponents\LivewirePowerGrid\Components\Actions\ActionsController;
use PowerComponents\LivewirePowerGrid\Components\Rules\{RulesController};
use PowerComponents\LivewirePowerGrid\DataSource\{Builder, Collection};
Expand All @@ -20,6 +20,8 @@ class ProcessDataSource

public bool $isCollection = false;

private array $queryLog = [];

public function __construct(
public PowerGridComponent $component,
public array $properties = [],
Expand All @@ -31,6 +33,11 @@ public static function fillData(PowerGridComponent $powerGridComponent, array $p
return new self($powerGridComponent, $properties);
}

public function queryLog(): array
{
return $this->queryLog;
}

/**
* @throws Throwable
*/
Expand Down Expand Up @@ -97,6 +104,8 @@ private function processCollection(mixed $datasource): \Illuminate\Pagination\Le
*/
private function processModel(EloquentBuilder|MorphToMany|QueryBuilder|BaseCollection|null $datasource): Paginator|LengthAwarePaginator
{
DB::enableQueryLog();

if (is_null($datasource)) {
$datasource = $this->component->datasource();
}
Expand Down Expand Up @@ -130,7 +139,13 @@ private function processModel(EloquentBuilder|MorphToMany|QueryBuilder|BaseColle
$collection = $results->getCollection();

/** @phpstan-ignore-next-line */
return $results->setCollection($this->transform($collection, $this->component));
$results = $results->setCollection($this->transform($collection, $this->component));

$this->queryLog = DB::getQueryLog();

DB::disableQueryLog();

return $results;
}

/**
Expand Down
6 changes: 5 additions & 1 deletion src/Providers/PowerGridServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use PowerComponents\LivewirePowerGrid\Components\Filters\FilterManager;
use PowerComponents\LivewirePowerGrid\Components\Rules\RuleManager;
use PowerComponents\LivewirePowerGrid\Themes\ThemeManager;
use PowerComponents\LivewirePowerGrid\{Livewire\LazyChild, PowerGridManager};
use PowerComponents\LivewirePowerGrid\{Livewire\LazyChild, Livewire\MeasurementCard, PowerGridManager};

/** @codeCoverageIgnore */
class PowerGridServiceProvider extends ServiceProvider
Expand Down Expand Up @@ -56,6 +56,10 @@ public function register(): void

Livewire::component('lazy-child', LazyChild::class);

if (class_exists(\Laravel\Pulse\Facades\Pulse::class)) {
Livewire::component('powergrid-measurement-card', MeasurementCard::class);
}

Macros::boot();
}

Expand Down
33 changes: 33 additions & 0 deletions src/Recorders/PowerGridRecorder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace PowerComponents\LivewirePowerGrid\Recorders;

use Carbon\CarbonImmutable;
use Illuminate\Config\Repository;
use Laravel\Pulse\Pulse;
use PowerComponents\LivewirePowerGrid\Events\MeasureRetrieveData;

class PowerGridRecorder
{
public string $listen = MeasureRetrieveData::class;

public function __construct(
protected Pulse $pulse,
protected Repository $config
) {
}

public function record(MeasureRetrieveData $class): void
{
$now = CarbonImmutable::now();

$measurement = collect($class);

$this->pulse->set(
type: 'powergrid-measurements',
key: uniqid(),
value: $measurement,
timestamp: $now->getTimestamp()
);
}
}

0 comments on commit 2643640

Please sign in to comment.