Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/mpyw/laravel-cached-database-stickiness

Guarantee database stickiness over the same user's consecutive requests
https://github.com/mpyw/laravel-cached-database-stickiness

database eloquent laravel php redis sticky

Last synced: 12 days ago
JSON representation

Guarantee database stickiness over the same user's consecutive requests

Awesome Lists containing this project

README

        

# Laravel Cached Database Stickiness
[![Build Status](https://github.com/mpyw/laravel-cached-database-stickiness/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/mpyw/laravel-cached-database-stickiness/actions) [![Coverage Status](https://coveralls.io/repos/github/mpyw/laravel-cached-database-stickiness/badge.svg?branch=master)](https://coveralls.io/github/mpyw/laravel-cached-database-stickiness?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/mpyw/laravel-cached-database-stickiness/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/mpyw/laravel-cached-database-stickiness/?branch=master)

Guarantee database stickiness over the same user's consecutive requests.

## Requirements

- PHP: `^8.0`
- Laravel: `^9.0 || ^10.0`

## Installing

```bash
composer require mpyw/laravel-cached-database-stickiness
```

> [!IMPORTANT]
> The default implementation is provided by `ConnectionServiceProvider`, however, **package discovery is not available**.
Be careful that you MUST register it in **`config/app.php`** by yourself.

```php
[

/* ... */

Mpyw\LaravelCachedDatabaseStickiness\ConnectionServiceProvider::class,

/* ... */

],

/* ... */
];
```

Then select the proper cache driver:

| Driver | Is eligible? | Description |
|:---|:---:|:---|
| **`redis`** | 😄 | Very fast, scalable and reliable driver
**(Cluster mode must be disabled)** |
| **`memcached`** | 😄 | Alternative for Redis |
| `dynamodb` | 😧 | It works but not so suitable for short-term caching
**(`ConsistentRead` must be enabled)** |
| `apc` | 😧 | It works unless PHP processes are running in multiple machines or containers |
| `file` | 😧 | It works unless PHP processes are running in multiple machines or containers |
| `database` | 🤮 | We'll get into a chicken-or-egg situation |
| `array` | 🤮 | Just for testing |

## Features

This library provides the following features.

- Make HTTP server to take over the database sticky state from the previous user's request within the last 5 seconds.
- Make queue worker into referring to master by default.
- Make queue worker into referring to slave by implementing `ShouldAssumeFresh` on your Queueable (jobs, listeners, notifications and mailables).

## Diagrams

### Default

![default](./diagrams/default.svg)

### Sticky

![sticky](./diagrams/sticky.svg)

### Sticky Cached

![sticky-cached](./diagrams/sticky-cached.svg)

## Advanced Usage

### Customize Stickiness TTL

> [!NOTE]
> The default stickiness TTL is `5` seconds.
> You can configure this value to add **`stickiness_ttl`** directive to your `config/database.php`.

```php
env('DB_CONNECTION', 'mysql'),

/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Here are each of the database connections setup for your application.
| Of course, examples of configuring each database platform that is
| supported by Laravel is shown below to make development simple.
|
|
| All database work in Laravel is done through the PHP PDO facilities
| so make sure you have the driver for your particular database of
| choice installed on your machine before you begin development.
|
*/

'connections' => [

/* ... */

'mysql' => [
'read' => env('DB_HOST_READONLY') ? [
'host' => env('DB_HOST_READONLY'),
] : null,
'write' => [],
'sticky' => (bool)env('DB_HOST_READONLY'),
'stickiness_ttl' => 3, // Set the stickiness TTL to 3 seconds
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),

/* ... */
],

],

];
```

### Customize Connection Implementation

> [!TIP]
> You can configure Connection implementation.
> - Make sure `ConnectionServiceProvider` to be removed from `config/app.php`.
> - Extend Connection with `DispatchesConnectionEvents` trait by yourself.

```php
[!TIP]
> You can register the `StickinessResolverInterface` implementation to change the source for stickiness determination.

```php
app->bind(StickinessResolverInterface::class, AuthBasedResolver::class);
}
}
```

| | Source | Middleware |
|:---:|:---:|:---:|
| `IpBasedResolver`
**(Default)**| Remote IP address | |
| `AuthBasedResolver` | Authenticated User ID | Required |

> [!IMPORTANT]
> You must add **`ResolveStickinessOnResolvedConnections`** middleware after `Authenticate`
when you use `AuthBasedResolver`.

```diff
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
[
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],

'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
+
+ 'auth' => [
+ \App\Http\Middleware\Authenticate::class,
+ \Mpyw\LaravelCachedDatabaseStickiness\Http\Middleware\ResolveStickinessOnResolvedConnections::class,
+ ],
+
+ 'auth.basic' => [
+ \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+ \Mpyw\LaravelCachedDatabaseStickiness\Http\Middleware\ResolveStickinessOnResolvedConnections::class,
+ ],
];

/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
- 'auth' => \App\Http\Middleware\Authenticate::class,
- 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];

/* ... */
}
```

### Customize Worker Behavior

> [!TIP]
> You can register the `JobInitializerInterface` implementation to change workers' behavior.

```php
app->bind(JobInitializerInterface::class, AlwaysFreshInitializer::class);
}
}
```

| | General Queueable | `ShouldAssumeFresh` Queueable | `ShouldAssumeModified` Queueable |
|:---:|:---:|:---:|:---:|
| `AlwaysModifiedInitializer`
**(Default)**| Master | **Slave** | Master |
| `AlwaysFreshInitializer` | Slave | Slave | **Master** |

## Attention

> [!CAUTION]
> ### Don't call `Schema::defaultStringLength()` in `ServiceProvider::boot()`

#### Problem

Assume that you have the following `ServiceProvider`.

```php