-
Notifications
You must be signed in to change notification settings - Fork 195
Description
Pulse Version
1.0.0-beta8
Laravel Version
10.39.0
PHP Version
8.2.4
Livewire Version
3.3.5
Database Driver & Version
MySQL Ver 8.0.32 for Linux on x86_64 (MySQL Community Server - GPL)
Description
On our current setup, we regularly get these deadlock issues reported to Sentry. Happened 14 times now since we added Pulse on the 14th of December. The more visitors, the more deadlocks happen. It's not limited to one type of recorder, but happens for all. And we do have extra plugins/cards added, but it already happened once before we added any extras.
Small note: I've set up a dedicated new MySQL database just for Pulse so the app queries wouldn't interfere with those of Pulse. Check further down for the configuration.
Stack trace
An example exception:
Illuminate\Database\QueryException
SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction (Connection: mysql-pulse, SQL: insert into `pulse_aggregates` (`aggregate`, `bucket`, `key`, `period`, `type`, `value`) values (count, 1703171580, users:1:roles, 60, cache_hit, 1), (count, 1703171520, users:1:roles, 360, cache_hit, 1), (count, 1703171520, users:1:roles, 1440, cache_hit, 1), (count, 1703167200, users:1:roles, 10080, cache_hit, 1) on duplicate key update `value` = `value` + values(`value`))
Raw stack trace:
PDOException: SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction
#18 /vendor/laravel/framework/src/Illuminate/Database/Connection.php(605): PDOStatement::execute
#17 /vendor/laravel/framework/src/Illuminate/Database/Connection.php(605): Illuminate\Database\Connection::Illuminate\Database\{closure}
#16 /vendor/laravel/framework/src/Illuminate/Database/Connection.php(809): Illuminate\Database\Connection::runQueryCallback
#15 /vendor/laravel/framework/src/Illuminate/Database/Connection.php(776): Illuminate\Database\Connection::run
#14 /vendor/laravel/framework/src/Illuminate/Database/Connection.php(593): Illuminate\Database\Connection::affectingStatement
#13 /vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(3506): Illuminate\Database\Query\Builder::upsert
#12 /vendor/laravel/pulse/src/Storage/DatabaseStorage.php(166): Laravel\Pulse\Storage\DatabaseStorage::upsertCount
#11 /vendor/laravel/pulse/src/Storage/DatabaseStorage.php(72): Laravel\Pulse\Storage\DatabaseStorage::Laravel\Pulse\Storage\{closure}
#10 /vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php(240): Illuminate\Support\Collection::each
#9 /vendor/laravel/pulse/src/Storage/DatabaseStorage.php(72): Laravel\Pulse\Storage\DatabaseStorage::Laravel\Pulse\Storage\{closure}
#8 /vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php(30): Illuminate\Database\Connection::transaction
#7 /vendor/laravel/pulse/src/Storage/DatabaseStorage.php(48): Laravel\Pulse\Storage\DatabaseStorage::store
#6 /vendor/laravel/pulse/src/Ingests/StorageIngest.php(29): Laravel\Pulse\Ingests\StorageIngest::ingest
#5 /vendor/laravel/pulse/src/Pulse.php(308): Laravel\Pulse\Pulse::Laravel\Pulse\{closure}
#4 /vendor/laravel/pulse/src/Pulse.php(535): Laravel\Pulse\Pulse::rescue
#3 /vendor/laravel/pulse/src/Pulse.php(307): Laravel\Pulse\Pulse::store
#2 /vendor/laravel/pulse/src/PulseServiceProvider.php(130): Laravel\Pulse\PulseServiceProvider::Laravel\Pulse\{closure}
#1 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(227): Illuminate\Foundation\Http\Kernel::terminate
#0 /public/index.php(55): null
Illuminate\Database\QueryException: SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction (Connection: mysql-pulse, SQL: insert into `pulse_aggregates` (`aggregate`, `bucket`, `key`, `period`, `type`, `value`) values (count, 1703171580, users:1:roles, 60, cache_hit, 1), (count, 1703171520, users:1:roles, 360, cache_hit, 1), (count, 1703171520, users:1:roles, 1440, cache_hit, 1), (count, 1703167200, users:1:roles, 10080, cache_hit, 1) on duplicate key update `value` = `value` + values(`value`))
#16 /vendor/laravel/framework/src/Illuminate/Database/Connection.php(822): Illuminate\Database\Connection::runQueryCallback
#15 /vendor/laravel/framework/src/Illuminate/Database/Connection.php(776): Illuminate\Database\Connection::run
#14 /vendor/laravel/framework/src/Illuminate/Database/Connection.php(593): Illuminate\Database\Connection::affectingStatement
#13 /vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(3506): Illuminate\Database\Query\Builder::upsert
#12 /vendor/laravel/pulse/src/Storage/DatabaseStorage.php(166): Laravel\Pulse\Storage\DatabaseStorage::upsertCount
#11 /vendor/laravel/pulse/src/Storage/DatabaseStorage.php(72): Laravel\Pulse\Storage\DatabaseStorage::Laravel\Pulse\Storage\{closure}
#10 /vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php(240): Illuminate\Support\Collection::each
#9 /vendor/laravel/pulse/src/Storage/DatabaseStorage.php(72): Laravel\Pulse\Storage\DatabaseStorage::Laravel\Pulse\Storage\{closure}
#8 /vendor/laravel/framework/src/Illuminate/Database/Concerns/ManagesTransactions.php(30): Illuminate\Database\Connection::transaction
#7 /vendor/laravel/pulse/src/Storage/DatabaseStorage.php(48): Laravel\Pulse\Storage\DatabaseStorage::store
#6 /vendor/laravel/pulse/src/Ingests/StorageIngest.php(29): Laravel\Pulse\Ingests\StorageIngest::ingest
#5 /vendor/laravel/pulse/src/Pulse.php(308): Laravel\Pulse\Pulse::Laravel\Pulse\{closure}
#4 /vendor/laravel/pulse/src/Pulse.php(535): Laravel\Pulse\Pulse::rescue
#3 /vendor/laravel/pulse/src/Pulse.php(307): Laravel\Pulse\Pulse::store
#2 /vendor/laravel/pulse/src/PulseServiceProvider.php(130): Laravel\Pulse\PulseServiceProvider::Laravel\Pulse\{closure}
#1 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(227): Illuminate\Foundation\Http\Kernel::terminate
#0 /public/index.php(55): null
Configuration
config/database.php excerpt:
'mysql-pulse' => [
'driver' => 'mysql',
'url' => env('PULSE_DATABASE_URL', env('DATABASE_URL')),
'host' => env('PULSE_DB_HOST', env('DB_HOST', '127.0.0.1')),
'port' => env('PULSE_DB_PORT', env('DB_PORT', '3306')),
'database' => env('PULSE_DB_DATABASE', env('DB_DATABASE', 'forge')),
'username' => env('PULSE_DB_USERNAME', env('DB_USERNAME', 'forge')),
'password' => env('PULSE_DB_PASSWORD', env('DB_PASSWORD', '')),
'unix_socket' => env('PULSE_DB_SOCKET', env('DB_SOCKET', '')),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_520_ci',
'timezone' => '+00:00',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('PULSE_MYSQL_ATTR_SSL_CA', env('MYSQL_ATTR_SSL_CA')),
]) : [],
'dump' => [
'timeout' => 60,
],
],config/pulse.php
<?php
declare(strict_types=1);
use EuSonLito\LaravelPulse\AppsLoad\Recorders\AppsLoadRecorder;
use Geow\CommandHistory\Recorders\CommandHistoryRecorder;
use Geow\DiskMetrics\Recorders\DiskRecorder;
use Laravel\Pulse\Http\Middleware\Authorize;
use Laravel\Pulse\Recorders\CacheInteractions;
use Laravel\Pulse\Recorders\Exceptions;
use Laravel\Pulse\Recorders\Queues;
use Laravel\Pulse\Recorders\Servers;
use Laravel\Pulse\Recorders\SlowJobs;
use Laravel\Pulse\Recorders\SlowOutgoingRequests;
use Laravel\Pulse\Recorders\SlowQueries;
use Laravel\Pulse\Recorders\SlowRequests;
use Laravel\Pulse\Recorders\UserJobs;
use Laravel\Pulse\Recorders\UserRequests;
use Maantje\Pulse\Database\Recorders\DatabaseRecorder;
use Morrislaptop\LaravelPulse4xx\FourXxRecorder;
return [
/*
|--------------------------------------------------------------------------
| Pulse Domain
|--------------------------------------------------------------------------
|
| This is the subdomain which the Pulse dashboard will be accessible from.
| When set to null, the dashboard will reside under the same domain as
| the application. Remember to configure your DNS entries correctly.
|
*/
'domain' => env('PULSE_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Pulse Path
|--------------------------------------------------------------------------
|
| This is the path which the Pulse dashboard will be accessible from. Feel
| free to change this path to anything you'd like. Note that this won't
| affect the path of the internal API that is never exposed to users.
|
*/
'path' => env('PULSE_PATH', 'debug/pulse'),
/*
|--------------------------------------------------------------------------
| Pulse Master Switch
|--------------------------------------------------------------------------
|
| This configuration option may be used to completely disable all Pulse
| data recorders regardless of their individual configurations. This
| provides a single option to quickly disable all Pulse recording.
|
*/
'enabled' => env('PULSE_ENABLED', true),
/*
|--------------------------------------------------------------------------
| Pulse Storage Driver
|--------------------------------------------------------------------------
|
| This configuration option determines which storage driver will be used
| while storing entries from Pulse's recorders. In addition, you also
| may provide any options to configure the selected storage driver.
|
*/
'storage' => [
'driver' => env('PULSE_STORAGE_DRIVER', 'database'),
'database' => [
'connection' => env('PULSE_DB_CONNECTION', null),
'chunk' => 1000,
],
],
/*
|--------------------------------------------------------------------------
| Pulse Ingest Driver
|--------------------------------------------------------------------------
|
| This configuration options determines the ingest driver that will be used
| to capture entries from Pulse's recorders. Ingest drivers are great to
| free up your request workers quickly by offloading the data storage.
|
*/
'ingest' => [
'driver' => env('PULSE_INGEST_DRIVER', 'storage'),
'trim_lottery' => [1, 1_000],
'redis' => [
'connection' => env('PULSE_REDIS_CONNECTION'),
'chunk' => 1000,
],
],
/*
|--------------------------------------------------------------------------
| Pulse Cache Driver
|--------------------------------------------------------------------------
|
| This configuration option determines the cache driver that will be used
| for various tasks, including caching dashboard results, establishing
| locks for events that should only occur on one server and signals.
|
*/
'cache' => env('PULSE_CACHE_DRIVER'),
/*
|--------------------------------------------------------------------------
| Pulse Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will be assigned to every Pulse route, giving you the
| chance to add your own middleware to this list or change any of the
| existing middleware. Of course, reasonable defaults are provided.
|
*/
'middleware' => [
'web',
Authorize::class,
],
/*
|--------------------------------------------------------------------------
| Pulse Recorders
|--------------------------------------------------------------------------
|
| The following array lists the "recorders" that will be registered with
| Pulse, along with their configuration. Recorders gather application
| event data from requests and tasks to pass to your ingest driver.
|
*/
'recorders' => [
CacheInteractions::class => [
'enabled' => env('PULSE_CACHE_INTERACTIONS_ENABLED', true),
'sample_rate' => env('PULSE_CACHE_INTERACTIONS_SAMPLE_RATE', .5),
'ignore' => [
'/\b[a-f0-9]{32}\b/', // Session IDs or something
'/^.+@.+\|(?:(?:\d+\.\d+\.\d+\.\d+)|[0-9a-fA-F:]+)(?::timer)?$/', // Breeze / Jetstream authentication rate limiting
'/^[a-zA-Z0-9]{40}$/', // Session IDs
'/^health:*/',
'/^illuminate:/', // Internal Laravel keys
'/^laravel-health:check-.*/',
'/^laravel:pulse:/', // Internal Pulse keys
'/^nova/', // Internal Nova keys
'/^telescope:/', // Internal Telescope keys
'/^about_application/', // Internal Telescope keys
],
'groups' => [
'/^job-exceptions:.*/' => 'job-exceptions:*',
'/^myclic\.content\.participant-dashboard-content\.\d+/' => 'myclic.content.participant-dashboard-content.*',
'/^myclic\.feed\.\d+/' => 'myclic.feed.*',
'/^throttle-event-myclic\.questionnaire\.started-\d+/' => 'throttle-event-myclic.questionnaire.started-*',
'/^users:\d+:roles/' => 'users:*:roles',
],
],
Exceptions::class => [
'enabled' => env('PULSE_EXCEPTIONS_ENABLED', true),
'sample_rate' => env('PULSE_EXCEPTIONS_SAMPLE_RATE', 1),
'location' => env('PULSE_EXCEPTIONS_LOCATION', true),
'ignore' => [
// '/^Package\\\\Exceptions\\\\/',
],
],
Queues::class => [
'enabled' => env('PULSE_QUEUES_ENABLED', true),
'sample_rate' => env('PULSE_QUEUES_SAMPLE_RATE', 1),
'ignore' => [
// '/^Package\\\\Jobs\\\\/',
'/^Spatie\\\\Health\\\\Jobs\\\\/',
],
],
Servers::class => [
'server_name' => env('PULSE_SERVER_NAME', gethostname()),
'directories' => explode(':', (string) env('PULSE_SERVER_DIRECTORIES', '/')),
],
SlowJobs::class => [
'enabled' => env('PULSE_SLOW_JOBS_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_JOBS_SAMPLE_RATE', 1),
'threshold' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
'ignore' => [
// '/^Package\\\\Jobs\\\\/',
],
],
SlowOutgoingRequests::class => [
'enabled' => env('PULSE_SLOW_OUTGOING_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_OUTGOING_REQUESTS_SAMPLE_RATE', 1),
'threshold' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
'ignore' => [
// '#^http://127\.0\.0\.1:13714#', // Inertia SSR
],
'groups' => [
// '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*',
// '#^https?://([^/]*).*$#' => '\1',
// '#/\d+#' => '/*',
],
],
SlowQueries::class => [
'enabled' => env('PULSE_SLOW_QUERIES_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_QUERIES_SAMPLE_RATE', 1),
'threshold' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
'location' => env('PULSE_SLOW_QUERIES_LOCATION', true),
'ignore' => [
'/(["`])pulse_[\w]+?\1/',
],
],
SlowRequests::class => [
'enabled' => env('PULSE_SLOW_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_SLOW_REQUESTS_SAMPLE_RATE', 1),
'threshold' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
'ignore' => [
'#^/_debugbar#',
'#^/debug/health#',
'#^/debug/horizon#',
'#^/debug/pulse$#',
'#^/livewire/update#',
],
],
UserJobs::class => [
'enabled' => env('PULSE_USER_JOBS_ENABLED', true),
'sample_rate' => env('PULSE_USER_JOBS_SAMPLE_RATE', 1),
'ignore' => [
// '/^Package\\\\Jobs\\\\/',
],
],
UserRequests::class => [
'enabled' => env('PULSE_USER_REQUESTS_ENABLED', true),
'sample_rate' => env('PULSE_USER_REQUESTS_SAMPLE_RATE', 1),
'ignore' => [
'#^/debug/pulse$#',
'#^/debug/horizon#',
'#^/debug/health$#',
],
],
/*
* Third-party
*/
AppsLoadRecorder::class => [
'enabled' => env('PULSE_APPS_LOAD_ENABLED', true),
'sample_rate' => env('PULSE_APPS_LOAD_SAMPLE_RATE', 1),
'limit' => env('PULSE_APPS_LOAD_LIMIT', 10),
'ignore' => [
'#^/pulse$#',
],
],
FourXxRecorder::class => [
'enabled' => env('PULSE_4XX_ENABLED', true),
'sample_rate' => env('PULSE_4XX_SAMPLE_RATE', 1),
'ignore' => [
// '#^/wp-admin#',
],
],
DiskRecorder::class => [
'enabled' => env('GEOW_DISK_METRICS', true),
],
DatabaseRecorder::class => [
'connections' => [
'mysql' => [
'values' => [
'Connections',
'Threads_connected',
'Threads_running',
'Max_used_connections',
],
'aggregates' => [
'avg' => [
'Threads_connected',
'Threads_running',
],
'max' => [],
'count' => [],
],
],
],
],
CommandHistoryRecorder::class => [
'enabled' => env('GEOW_COMMAND_HISTORY', true),
'ignore' => [
'#^backup:monitor#',
'#^cache:prune-stale-tags#',
'#^config:cache#',
'#^config:clear#',
'#^event:cache#',
'#^event:clear#',
'#^health:check#',
'#^health:queue-check-heartbeat#',
'#^health:schedule-check-heartbeat#',
'#^horizon:pause#',
'#^horizon:purge#',
'#^horizon:snapshot#',
'#^horizon:status#',
'#^horizon:work#',
'#^icons:cache#',
'#^icons:clear#',
'#^migrate#',
'#^modules:cache#',
'#^modules:clear#',
'#^operations:process#',
'#^package:discover#',
'#^pulse:check#',
'#^pulse:restart#',
'#^purifier:clear#',
'#^route:cache#',
'#^route:clear#',
'#^scout:flush#',
'#^scout:import#',
'#^storage:link#',
'#^view:cache#',
'#^view:clear#',
],
],
],
];
Steps To Reproduce
Can't really provide a reproducible repository here since it's about a database deadlock issue, but let me know how else I can provide information.
What we did:
- Install Pulse in our existing app
- Set up a new production database just for Pulse
- Publish the Pulse migration and config
- Configure things
- Deploy the app and run migrations
- Issues started appearing soon after, more in the last week