Optimized code for deleting model cache when model deleted or saved in transaction. (#2438)

This commit is contained in:
李铭昕 2020-09-05 11:31:19 +08:00 committed by GitHub
parent 30218c6275
commit e06423f853
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 138 additions and 7 deletions

View File

@ -12,6 +12,7 @@
## Optimized
- [#2429](https://github.com/hyperf/hyperf/pull/2429) Optimized error message when does not set the value of `@var` for `@Inject`.
- [#2438](https://github.com/hyperf/hyperf/pull/2438) Optimized code for deleting model cache when model deleted or saved in transaction.
# v2.0.9 - 2020-08-31

View File

@ -11,6 +11,7 @@ declare(strict_types=1);
*/
namespace Hyperf\ModelCache;
use Hyperf\ModelCache\Listener\DeleteCacheInTransactionListener;
use Hyperf\ModelCache\Listener\DeleteCacheListener;
class ConfigProvider
@ -20,6 +21,7 @@ class ConfigProvider
return [
'listeners' => [
DeleteCacheListener::class,
DeleteCacheInTransactionListener::class,
],
'annotations' => [
'scan' => [

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\ModelCache;
use Hyperf\Utils\Traits\StaticInstance;
class InvalidCacheManager
{
use StaticInstance;
/**
* @var CacheableInterface[]
*/
protected $models;
public function push(CacheableInterface $model): void
{
$this->models[] = $model;
}
public function delete(): void
{
foreach ($this->models as $model) {
$model->deleteCache();
}
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\ModelCache\Listener;
use Hyperf\Database\Events\TransactionCommitted;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\ModelCache\InvalidCacheManager;
class DeleteCacheInTransactionListener implements ListenerInterface
{
public function listen(): array
{
return [
TransactionCommitted::class,
];
}
public function process(object $event)
{
if (! $event instanceof TransactionCommitted) {
return;
}
if ($event->connection->transactionLevel() === 0) {
InvalidCacheManager::instance()->delete();
}
}
}

View File

@ -16,6 +16,7 @@ use Hyperf\Database\Model\Events\Event;
use Hyperf\Database\Model\Events\Saved;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\ModelCache\CacheableInterface;
use Hyperf\ModelCache\InvalidCacheManager;
class DeleteCacheListener implements ListenerInterface
{
@ -29,11 +30,20 @@ class DeleteCacheListener implements ListenerInterface
public function process(object $event)
{
if ($event instanceof Event) {
if (! $event instanceof Event) {
return;
}
$model = $event->getModel();
if ($model instanceof CacheableInterface) {
if (! $model instanceof CacheableInterface) {
return;
}
if ($model->getConnection()->transactionLevel() > 0) {
InvalidCacheManager::instance()->push($model);
return;
}
$model->deleteCache();
}
}
}
}

View File

@ -332,4 +332,40 @@ class ModelCacheTest extends TestCase
$this->assertArrayHasKey('gender', $model->toArray());
}
public function testModelSave()
{
$container = ContainerStub::mockContainer();
/** @var \Redis $redis */
$redis = $container->make(RedisProxy::class, ['pool' => 'default']);
$id = 208;
UserModel::query()->firstOrCreate(['id' => $id], [
'name' => uniqid(),
'gender' => 1,
]);
$model = UserModel::findFromCache($id);
$name = uniqid();
$model->name = $name;
$model->save();
$this->assertSame(0, $redis->exists('{mc:default:m:user}:id:' . $id));
$model = UserModel::findFromCache($id);
$this->assertSame($name, $model->name);
$connection = $model->getConnection();
$connection->transaction(function () use ($id, $redis) {
$model = UserModel::findFromCache($id);
$name = uniqid();
$model->name = $name;
$model->save();
UserModel::findFromCache($id);
$this->assertSame(1, $redis->exists('{mc:default:m:user}:id:' . $id));
});
$this->assertSame(0, $redis->exists('{mc:default:m:user}:id:' . $id));
$model->delete();
}
}

View File

@ -17,6 +17,9 @@ use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Database\ConnectionResolverInterface;
use Hyperf\Database\Connectors\ConnectionFactory;
use Hyperf\Database\Connectors\MySqlConnector;
use Hyperf\Database\Events\TransactionCommitted;
use Hyperf\Database\Model\Events\Deleted;
use Hyperf\Database\Model\Events\Saved;
use Hyperf\DbConnection\Collector\TableCollector;
use Hyperf\DbConnection\ConnectionResolver;
use Hyperf\DbConnection\Frequency;
@ -29,6 +32,8 @@ use Hyperf\Framework\Logger\StdoutLogger;
use Hyperf\ModelCache\EagerLoad\EagerLoader;
use Hyperf\ModelCache\Handler\RedisHandler;
use Hyperf\ModelCache\Handler\RedisStringHandler;
use Hyperf\ModelCache\Listener\DeleteCacheInTransactionListener;
use Hyperf\ModelCache\Listener\DeleteCacheListener;
use Hyperf\ModelCache\Manager;
use Hyperf\ModelCache\Redis\LuaManager;
use Hyperf\Pool\Channel;
@ -59,7 +64,6 @@ class ContainerStub
'log_level' => [
LogLevel::ALERT,
LogLevel::CRITICAL,
LogLevel::DEBUG,
LogLevel::EMERGENCY,
LogLevel::ERROR,
LogLevel::INFO,
@ -122,7 +126,12 @@ class ContainerStub
$connectionFactory = new ConnectionFactory($container);
$container->shouldReceive('get')->with(ConnectionFactory::class)->andReturn($connectionFactory);
$eventDispatcher = new EventDispatcher(new ListenerProvider(), $logger);
$provider = new ListenerProvider();
$listener = new DeleteCacheListener();
$provider->on(TransactionCommitted::class, [new DeleteCacheInTransactionListener(), 'process']);
$provider->on(Saved::class, [$listener, 'process']);
$provider->on(Deleted::class, [$listener, 'process']);
$eventDispatcher = new EventDispatcher($provider, $logger);
$container->shouldReceive('get')->with(EventDispatcherInterface::class)->andReturn($eventDispatcher);
$container->shouldReceive('get')->with('db.connector.mysql')->andReturn(new MySqlConnector());