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.
This commit is contained in:
Franz Liedke 2017-12-21 12:23:34 +01:00
parent c8a1a5fcfa
commit 56231d61be
No known key found for this signature in database
GPG Key ID: 9A0231A879B055F4
3 changed files with 57 additions and 24 deletions

View File

@ -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));

View File

@ -0,0 +1,55 @@
<?php
/*
* This file is part of Flarum.
*
* (c) Toby Zerner <toby.zerner@gmail.com>
*
* 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;
}
}

View File

@ -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';