What is Sailfish?
+
Sailfish is a Laravel package that keeps a long-term history of MySQL's performance_schema counters: index usage, full table scans, lock waits, errors, statement digests, finished transactions and the server's global status. It keeps that history across the server restarts that reset those counters, serves a dashboard of index, table and server activity, and sends events by mail, Slack or webhook when something changes. It is made by Boring Observability, who also make Skyline and Requizon.
Why not just query performance_schema directly?
+
performance_schema only ever tells you what happened since the server booted. After a restart or a failover, "this index has zero reads" means nothing, and you cannot ask whether scans on a table started after last Tuesday's deploy. Sailfish stores what moved in each five-minute interval, so those questions still have answers.
What events does Sailfish send?
+
Eighteen, in three groups. On the server: connections running out, surges in row-lock waits, slow queries, errors and temporary tables on disk, a growing InnoDB history list, and buffer pool misses. On transactions: a surge in the share rolled back, and transactions that ran long. On tables and indexes: table-scan surges, indexes that went cold, were never used or are redundant, unusual table growth, unindexed *_id columns, tables without a primary key, reclaimable space and schema changes. The urgent ones are sent as they open; the rest go in a daily digest.
Can Sailfish tell me which indexes are unused?
+
Yes. An index with no fetch in 30 days of observed history opens a "never used" event, which says how big the index is, what its writes cost and whether another index covers it. An index that was read in the 14 days before the last 7 and not since opens an "index went cold" event. PRIMARY keys are left out of both.
Does Sailfish send data anywhere?
+
No. It is a package running inside your application. It reads performance_schema over your own connection, writes to sailfish_* tables in your own database and serves its dashboard from your own domain. Notifications go only to the mail addresses, Slack channel and webhook you configure.
What does Sailfish require?
+
MySQL 8.0, Laravel 12 or 13 and PHP 8.4 or newer, and a database user that can SELECT from performance_schema. An application user limited to its own schema cannot, and sailfish:collect names the grant it needs. SELECT on mysql.innodb_index_stats adds index sizes, and PROCESS adds InnoDB's history list length. Both are optional.
How much does Sailfish cost?
+
Sailfish is a one-time purchase of $99 per application. The purchase includes twelve months of upgrades. Everything published during those twelve months is yours permanently: you can keep running it and reinstall it whenever you rebuild. Renewing is optional and buys only the next year of releases. The checkout is hosted by Anystack, which also issues the license key.
Do you offer a refund?
+
Yes. Every purchase comes with a 30-day money-back guarantee. Email tech@boring-observability.dev within 30 days of being charged and we refund it in full, with no questions asked.
Why are my statement digests empty?
+
MySQL 8.0 gives no digest to statements sent through the binary prepared-statement protocol, and Laravel's MySQL connection uses native prepared statements by default. Index and table counters see every statement either way. For digests of application queries, set PDO::ATTR_EMULATE_PREPARES to true on the connection.
Who can see the dashboard?
+
Whoever your viewSailfish gate allows. Outside the local environment nobody gets in until you publish the service provider and define that gate. A second gate, viewSailfishQueryText, decides who sees running statements with their values rather than with each literal replaced by "?".