Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DatabaseSizeCheck #151

Merged
merged 21 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ jobs:
matrix:
os: [ubuntu-latest]
php: [8.1, 8.2]
laravel: [9.*, 8.*]
laravel: [9.*, 8.*, 10.*]
stability: [prefer-stable]
include:
- laravel: 10.*
testbench: 8.*
- laravel: 9.*
testbench: 7.*
- laravel: 8.*
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

All notable changes to `laravel-health` will be documented in this file.

## 1.18.3 - 2023-01-25

### What's Changed

- Fix empty output when running `artisan health:list --fresh` by @emiliopedrollo in https://github.com/spatie/laravel-health/pull/150

### New Contributors

- @emiliopedrollo made their first contribution in https://github.com/spatie/laravel-health/pull/150

**Full Changelog**: https://github.com/spatie/laravel-health/compare/1.18.2...1.18.3

## 1.18.2 - 2023-01-24

### What's Changed

- Laravel 10.x Compatibility by @laravel-shift in https://github.com/spatie/laravel-health/pull/152

### New Contributors

- @laravel-shift made their first contribution in https://github.com/spatie/laravel-health/pull/152

**Full Changelog**: https://github.com/spatie/laravel-health/compare/1.18.1...1.18.2

## 1.18.1 - 2023-01-06

### What's Changed
Expand Down
24 changes: 12 additions & 12 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,33 @@
"require": {
"php": "^8.0",
"dragonmantank/cron-expression": "^3.3.1",
"guzzlehttp/guzzle": "^6.5|^7.4.5",
"illuminate/console": "^8.75|^9.0",
"illuminate/contracts": "^8.75|^9.0",
"illuminate/database": "^8.75|^9.0",
"illuminate/notifications": "^8.75|^9.0",
"illuminate/support": "^8.75|^9.0",
"guzzlehttp/guzzle": "^6.5|^7.4.5|^7.2",
"illuminate/console": "^8.75|^9.0|^10.0",
"illuminate/contracts": "^8.75|^9.0|^10.0",
"illuminate/database": "^8.75|^9.0|^10.0",
"illuminate/notifications": "^8.75|^9.0|^10.0",
"illuminate/support": "^8.75|^9.0|^10.0",
"nunomaduro/termwind": "^1.14",
"spatie/enum": "^3.13",
"spatie/laravel-package-tools": "^1.12.1",
"spatie/regex": "^3.1.1",
"spatie/regex": "^3.1.1|^3.1",
"symfony/process": "^5.4|^6.0"
},
"require-dev": {
"laravel/horizon": "^5.9.10",
"laravel/slack-notification-channel": "^2.4",
"nunomaduro/collision": "^5.10|^6.2.1",
"nunomaduro/larastan": "^1.0.3",
"orchestra/testbench": "^6.23|^7.6",
"nunomaduro/collision": "^5.10|^6.2.1|^6.1",
"nunomaduro/larastan": "^1.0.3|^2.4",
"orchestra/testbench": "^6.23|^7.6|^8.0",
"pestphp/pest": "^1.21.3",
"pestphp/pest-plugin-laravel": "^1.2",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.1.1",
"phpunit/phpunit": "^9.5.21",
"phpunit/phpunit": "^9.5.21|^9.5.10",
"spatie/laravel-ray": "^1.30",
"spatie/pest-plugin-snapshots": "^1.1",
"spatie/pest-plugin-test-time": "^1.1.1",
"spatie/pest-plugin-test-time": "^1.1.1|^1.1",
"spatie/test-time": "^1.3"
},
"autoload": {
Expand Down
39 changes: 39 additions & 0 deletions docs/available-checks/db-size-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: DB size
weight: 7
---

This check makes sure that your database is not too big. This check supports MySQL and Postgres.

If the database is larger than the specified maximum, this check will fail.

## Requirements

You'll need to install the `doctrine/dbal` package in your project.

```bash
composer require doctrine/dbal
```


## Usage

Here's how you can register the check.

```php
use Spatie\Health\Facades\Health;
use Spatie\Health\Checks\Checks\DatabaseSizeCheck;

Health::checks([
DatabaseSizeCheck::new()
->failWhenSizeAboveGb(errorThresholdGb: 5.0)
]);
```

### Specifying the database connection

To check another database connection, call `connectionName()`

```php
DatabaseSizeCheck::new()->connectionName('another-connection-name')->failWhenSizeAboveGb(5.0),
```
2 changes: 1 addition & 1 deletion docs/available-checks/db-table-size-check.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: DB table size
weight: 7
weight: 8
---

This check makes sure the tables of your database are not too big. This check supports MySQL and Postgres.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/debug-mode.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Debug mode
weight: 8
weight: 9
---

This check will make sure that debug mode is set to `false`. It will fail when debug mode is `true`.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/environment.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Environment
weight: 9
weight: 10
---

This check will make sure your application is running used the right environment. By default, this check will fail when the environment is not equal to `production`.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/flare-error-count.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Flare error count
weight: 10
weight: 11
---

This check will monitor the amount of errors and exceptions your application throws. For this check you'll need to have an account on [Flare](https://flareapp.io).
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/horizon.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Horizon
weight: 11
weight: 12
---

This check will make sure Horizon is running. It will report a warning when Horizon is paused, and a failure when Horizon is not running.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/meilisearch.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: MeiliSearch
weight: 12
weight: 13
---

This check will verify if MeiliSearch is running. It will call MeiliSearch's [built-in health endpoint](https://docs.meilisearch.com/reference/api/health.html) and verify that its status returns `available`.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/ping.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Ping
weight: 13
weight: 14
---

This check will send a request to a given URL. It will report a failure when that URL doesn't respond with a successful response code within a second.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/queue.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Queue
weight: 14
weight: 15
---

This check will make sure that queued jobs are running. This check works by dispatching a test job (this will be done via a scheduled command), and verify if that test job is handled on time.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/redis.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Redis
weight: 15
weight: 16
---

This check will make sure Redis is running. By default, this check will make sure the `default` connection is working.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/schedule.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Schedule
weight: 16
weight: 17
---

This check will make sure the schedule is running. If the check detects that the schedule is not run every minute, it will fail.
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/security-advisories.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Security advisories
weight: 17
weight: 18
---

This check will check if the PHP packages installed in your project have known security vulnerabilities. This check works using [Packagist's security vulnerability API](https://php.watch/articles/composer-audit#packagist-vuln-list-api).
Expand Down
2 changes: 1 addition & 1 deletion docs/available-checks/used-disk-space.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Used disk space
weight: 18
weight: 19
---

This check will monitor the percentage of available disk space.
Expand Down
54 changes: 54 additions & 0 deletions src/Checks/Checks/DatabaseSizeCheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Spatie\Health\Checks\Checks;

use Illuminate\Database\ConnectionResolverInterface;
use Spatie\Health\Checks\Check;
use Spatie\Health\Checks\Result;
use Spatie\Health\Support\DbConnectionInfo;

class DatabaseSizeCheck extends Check
{
protected ?string $connectionName = null;

protected float $failWhenSizeAboveGb = 1;

public function connectionName(string $connectionName): self
{
$this->connectionName = $connectionName;

return $this;
}

public function failWhenSizeAboveGb(float $errorThresholdGb): self
{
$this->failWhenSizeAboveGb = $errorThresholdGb;

return $this;
}

public function run(): Result
{
$result = Result::make();

$databaseSizeInGb = $this->getDatabaseSizeInGb();

return $databaseSizeInGb >= $this->failWhenSizeAboveGb
? $result->failed("Database size is {$databaseSizeInGb} GB, which is above the threshold of {$this->failWhenSizeAboveGb} GB")
: $result->ok("{$databaseSizeInGb} GB");
}

protected function getDefaultConnectionName(): string
{
return config('database.default');
}

protected function getDatabaseSizeInGb(): float
{
$connectionName = $this->connectionName ?? $this->getDefaultConnectionName();

$connection = app(ConnectionResolverInterface::class)->connection($connectionName);

return round((new DbConnectionInfo())->databaseSizeInMb($connection) / 1000, 2);
}
}
2 changes: 2 additions & 0 deletions src/Commands/ListHealthChecksCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Spatie\Health\ResultStores\StoredCheckResults\StoredCheckResult;
use Spatie\Health\ResultStores\StoredCheckResults\StoredCheckResults;
use function Termwind\render;
use function Termwind\renderUsing;

class ListHealthChecksCommand extends Command
{
Expand All @@ -36,6 +37,7 @@ public function handle(): int

$checkResults = $resultStore->latestResults();

renderUsing($this->output);
render(view('health::list-cli', [
'lastRanAt' => new Carbon($checkResults?->finishedAt),
'checkResults' => $checkResults,
Expand Down
29 changes: 26 additions & 3 deletions src/Support/DbConnectionInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class DbConnectionInfo
public function connectionCount(ConnectionInterface $connection): int
{
return match (true) {
$connection instanceof MySqlConnection => (int) $connection->selectOne($connection->raw('show status where variable_name = "threads_connected"'))->Value,
$connection instanceof PostgresConnection => (int) $connection->selectOne('select count(*) as connections from pg_stat_activity')->connections,
default => throw new DatabaseNotSupported()
$connection instanceof MySqlConnection => (int)$connection->selectOne($connection->raw('show status where variable_name = "threads_connected"'))->Value,
$connection instanceof PostgresConnection => (int)$connection->selectOne('select count(*) as connections from pg_stat_activity')->connections,
default => throw DatabaseNotSupported::make($connection),
};
}

Expand All @@ -29,6 +29,15 @@ public function tableSizeInMb(ConnectionInterface $connection, string $table): f
return $sizeInBytes / 1024 / 1024;
}

public function databaseSizeInMb(ConnectionInterface $connection): float
{
return match (true) {
$connection instanceof MySqlConnection => $this->getMySQlDatabaseSize($connection),
$connection instanceof PostgresConnection => $this->getPostgresDatabaseSize($connection),
default => throw DatabaseNotSupported::make($connection),
};
}

protected function getMySQLTableSize(ConnectionInterface $connection, string $table): int
{
return $connection->selectOne('SELECT (data_length + index_length) AS size FROM information_schema.TABLES WHERE table_schema = ? AND table_name = ?', [
Expand All @@ -43,4 +52,18 @@ protected function getPostgresTableSize(ConnectionInterface $connection, string
$table,
])->size;
}

protected function getMySQLDatabaseSize(ConnectionInterface $connection): int
{
return $connection->selectOne('SELECT size from (SELECT table_schema "name", ROUND(SUM(data_length + index_length) / 1024 / 1024, 1) as size FROM information_schema.tables GROUP BY table_schema) alias_one where name = ?', [
$connection->getDatabaseName(),
])->size;
}

protected function getPostgresDatabaseSize(ConnectionInterface $connection): int
{
return $connection->selectOne('SELECT pg_size_pretty(pg_database_size(?)) AS size;', [
$connection->getDatabaseName(),
])->size;
}
}
23 changes: 23 additions & 0 deletions tests/Checks/DatabaseSizeCheckTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

use Illuminate\Support\Facades\DB;
use Spatie\Health\Checks\Checks\DatabaseSizeCheck;
use Spatie\Health\Enums\Status;

it('will determine that database size is ok if it does not cross the maximum', function () {
$result = DatabaseSizeCheck::new()
->connectionName('mysql')
->failWhenSizeAboveGb(50)
->run();

expect($result->status)->toBe(Status::ok());
});

it('will determine that database size is not ok if it does cross the maximum', function () {
$result = DatabaseSizeCheck::new()
->connectionName('mysql')
->failWhenSizeAboveGb(0)
->run();

expect($result->status)->toBe(Status::failed());
});
4 changes: 2 additions & 2 deletions tests/Commands/RunChecksCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
$this->fakeDiskSpaceCheck->fakeDiskUsagePercentage(100);
artisan(RunHealthChecksCommand::class)->assertSuccessful();

Notification::assertTimesSent(1, CheckFailedNotification::class);
Notification::assertSentTimes(CheckFailedNotification::class, 1);
});

it('has an option that will prevent notifications being sent', function () {
Expand All @@ -54,7 +54,7 @@
$this->fakeDiskSpaceCheck->fakeDiskUsagePercentage(100);
artisan('health:check --no-notification')->assertSuccessful();

Notification::assertTimesSent(0, CheckFailedNotification::class);
Notification::assertSentTimes(CheckFailedNotification::class, 0);
});

it('can store the with warnings results in the database', function () {
Expand Down
Loading