From 56231d61be8e2090891cb0e93542460c41052af1 Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Thu, 21 Dec 2017 12:23:34 +0100 Subject: [PATCH] Move garbage collection into middleware This prevents garbage collection to randomly break the installer: before installation, the models that are being accessed have no database connection. Now, the middleware is only mounted into the forum's middleware stack. I want API requests to have stable performance, and the forum middleware stack is only mounted when Flarum is installed. --- src/Forum/ForumServiceProvider.php | 2 + src/Http/Middleware/CollectGarbage.php | 55 ++++++++++++++++++++++++++ src/Http/Server.php | 24 ----------- 3 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 src/Http/Middleware/CollectGarbage.php diff --git a/src/Forum/ForumServiceProvider.php b/src/Forum/ForumServiceProvider.php index 175346ad3..8e95b0072 100644 --- a/src/Forum/ForumServiceProvider.php +++ b/src/Forum/ForumServiceProvider.php @@ -17,6 +17,7 @@ use Flarum\Extension\Event\Disabled; use Flarum\Extension\Event\Enabled; use Flarum\Foundation\AbstractServiceProvider; use Flarum\Http\Middleware\AuthenticateWithSession; +use Flarum\Http\Middleware\CollectGarbage; use Flarum\Http\Middleware\DispatchRoute; use Flarum\Http\Middleware\HandleErrors; use Flarum\Http\Middleware\ParseJsonBody; @@ -54,6 +55,7 @@ class ForumServiceProvider extends AbstractServiceProvider $pipe->pipe($app->make(HandleErrors::class, ['debug' => $debugMode])); $pipe->pipe($app->make(ParseJsonBody::class)); + $pipe->pipe($app->make(CollectGarbage::class)); $pipe->pipe($app->make(StartSession::class)); $pipe->pipe($app->make(RememberFromCookie::class)); $pipe->pipe($app->make(AuthenticateWithSession::class)); diff --git a/src/Http/Middleware/CollectGarbage.php b/src/Http/Middleware/CollectGarbage.php new file mode 100644 index 000000000..0a04da16e --- /dev/null +++ b/src/Http/Middleware/CollectGarbage.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Http\Middleware; + +use Flarum\Http\AccessToken; +use Flarum\User\AuthToken; +use Flarum\User\EmailToken; +use Flarum\User\PasswordToken; +use Psr\Http\Message\ResponseInterface as Response; +use Psr\Http\Message\ServerRequestInterface as Request; +use Zend\Stratigility\MiddlewareInterface; + +class CollectGarbage implements MiddlewareInterface +{ + /** + * {@inheritdoc} + */ + public function __invoke(Request $request, Response $response, callable $out = null) + { + $this->collectGarbageSometimes(); + + return $out ? $out($request, $response) : $response; + } + + private function collectGarbageSometimes() + { + // In order to save performance, we only execute this query + // from time to time (with 50% chance). + if (! $this->hit()) { + return; + } + + AccessToken::whereRaw('last_activity <= ? - lifetime', [time()])->delete(); + + $earliestToKeep = date('Y-m-d H:i:s', time() - 24 * 60 * 60); + + EmailToken::where('created_at', '<=', $earliestToKeep)->delete(); + PasswordToken::where('created_at', '<=', $earliestToKeep)->delete(); + AuthToken::where('created_at', '<=', $earliestToKeep)->delete(); + } + + private function hit() + { + return mt_rand(1, 100) <= 2; + } +} diff --git a/src/Http/Server.php b/src/Http/Server.php index 8c50cb3e2..80e507d43 100644 --- a/src/Http/Server.php +++ b/src/Http/Server.php @@ -18,9 +18,6 @@ use Flarum\Http\Middleware\HandleErrors; use Flarum\Http\Middleware\StartSession; use Flarum\Install\InstallServiceProvider; use Flarum\Update\UpdateServiceProvider; -use Flarum\User\AuthToken; -use Flarum\User\EmailToken; -use Flarum\User\PasswordToken; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Zend\Diactoros\Response\HtmlResponse; @@ -67,9 +64,6 @@ class Server */ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $out) { - // FIXME: Only call this when app is installed. Do this via middleware? - $this->collectGarbage(); - $middleware = $this->getMiddleware($request->getUri()->getPath()); return $middleware($request, $response, $out); @@ -152,24 +146,6 @@ class Server return $pipe; } - private function collectGarbage() - { - if ($this->hitsLottery()) { - AccessToken::whereRaw('last_activity <= ? - lifetime', [time()])->delete(); - - $earliestToKeep = date('Y-m-d H:i:s', time() - 24 * 60 * 60); - - EmailToken::where('created_at', '<=', $earliestToKeep)->delete(); - PasswordToken::where('created_at', '<=', $earliestToKeep)->delete(); - AuthToken::where('created_at', '<=', $earliestToKeep)->delete(); - } - } - - private function hitsLottery() - { - return mt_rand(1, 100) <= 2; - } - private function getErrorDir() { return __DIR__.'/../../error';