Skip to content

Commit

Permalink
Refactor RuntimeCache
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-rueegg committed Aug 11, 2023
1 parent 3c7d8ed commit 8df0534
Show file tree
Hide file tree
Showing 28 changed files with 3,933 additions and 162 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-DEV.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ HumHub Changelog

1.15.0-beta.2 (Unreleased)
--------------------------
- Enh #6482: New RuntimeCache implementation (See #6375, #6457, & #6463)
- Fix #6472: Initialization of account profile field type "Markdown"
- Fix #6471: Wording "Default Homepage" in Space Default Settings
- Fix #6468: Module Administration - Marketplace Links broken without Pretty URLs
Expand Down
60 changes: 54 additions & 6 deletions protected/humhub/components/ActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@

namespace humhub\components;

use humhub\exceptions\InvalidArgumentTypeException;
use humhub\interfaces\UniqueIdentifiersInterface;
use Yii;
use humhub\modules\user\models\User;
use humhub\modules\file\components\FileManager;
use yii\base\InvalidConfigException;
use yii\db\ActiveRecord as DbActiveRecord;
use yii\db\ColumnSchema;
use yii\db\Expression;
use yii\validators\Validator;
Expand All @@ -24,7 +27,7 @@
* @property User $updatedBy
* @author luke
*/
class ActiveRecord extends \yii\db\ActiveRecord
class ActiveRecord extends DbActiveRecord implements UniqueIdentifiersInterface
{

/**
Expand Down Expand Up @@ -85,15 +88,60 @@ public function afterSave($insert, $changedAttributes)
}

/**
* Returns a unique id for this record/model
*
* @return String Unique Id of this record
* @inheritdoc
*/
public function getUniqueId()
public function getUniqueId(): string
{
return str_replace('\\', '', get_class($this)) . "_" . $this->primaryKey;
return RuntimeBaseCache::normaliseObjectIdentifier($this, $this->getPrimaryKey(true));
}

/**
* @inheritdoc
* @since 1.15
*/
public function getUniqueIDs(?array $keys = null): array
{
$uniqueIDs[] = $this->getUniqueId();

$tableSchema = static::getTableSchema();

if ($keys === null) {
$keys = array_intersect(['id', 'guid'], $tableSchema->getColumnNames());
}

foreach ($keys as $i => &$key) {
if (!is_array($key)) {
if (!is_string($key)) {
throw new InvalidArgumentTypeException(__METHOD__, [1 => "\$keys[$i]"], ['array', 'string'], $key);
}

if ([$key] === $tableSchema->primaryKey) {
continue;
}

$uniqueIDs[] = RuntimeBaseCache::normaliseObjectIdentifier($this, $this->$key);

continue;
}

if ($key === $tableSchema->primaryKey) {
continue;
}

$key = array_flip($key);

foreach ($key as $property => &$value) {
$value = $this->$property;
}
unset($value);

$uniqueIDs[] = RuntimeBaseCache::normaliseObjectIdentifier($this, $key);
}

return $uniqueIDs;
}


/**
* Relation to User defined in created_by attribute
*
Expand Down
83 changes: 1 addition & 82 deletions protected/humhub/components/CacheableActiveQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

namespace humhub\components;

use humhub\exceptions\InvalidArgumentTypeException;
use Yii;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
Expand All @@ -28,88 +27,8 @@ public function findFor($name, $model)
return $result;
}

static::cacheProcessVariants('set', $result);
Yii::$app->runtimeCache->set(null, $result);

return $result;
}

public static function normaliseObjectIdentifier($classOrObject, $id = null, ?string &$idWasUsed = null): string
{
if (is_object($classOrObject)) {
$classOrObject = get_class($classOrObject);
} elseif (!is_string($classOrObject)) {
throw new InvalidArgumentTypeException(
__METHOD__,
[1 => '$classOrObject'],
['object', 'string'],
$classOrObject
);
}

$idWasUsed = '';
$id = $id === 0 || !empty($id) ? "__" . ($idWasUsed = implode('_', (array)$id)) : '';

return str_replace('\\', '_', ltrim($classOrObject, '\\')) . $id;
}

/**
* @param string $action
* @param ActiveRecord $record
* @param array $properties
*
* @return void
*/
public static function cacheProcessVariants(string $action, ActiveRecord $record, array $properties = ['id', 'guid']): void
{
static $runtimeCache;

$runtimeCache ??= Yii::$app->runtimeCache;

$done = [];
$class = get_class($record);

$pk = $record->getPrimaryKey(true);

$runtimeCache->$action(self::normaliseObjectIdentifier($class, $pk, $identifier), $record);

$done[] = $identifier;

foreach ($properties as $identifier) {
if ($record->hasAttribute($identifier)) {
$identifier = (string)$record->$identifier;
if (!in_array($identifier, $done, true)) {
$runtimeCache->$action(self::normaliseObjectIdentifier($class, $identifier, $identifier), $record);
$done[] = $identifier;
}
}
}

/**
* Check if we have the related record cached in the polymorphic behavior, so we can delete the cache by ID.
* (This is not fully bullet-proof, as the object might still be saved in the cache, but only under the guid key.)
*/
if ($action !== 'delete' || !$record->hasMethod('getPolymorphicRelation')) {
return;
}

if ($model = $record->getPolymorphicRelation(false) ?? $runtimeCache->get(self::normaliseObjectIdentifier($record->{$record->classAttribute}, $record->{$record->pkAttribute}))) {
static::cacheProcessVariants($action, $model, $properties);
}
}

public static function cacheDeleteByClass($class, $condition)
{
$cache = Yii::$app->runtimeCache;

if (is_array($condition) && $class::isPrimaryKey(array_keys($condition))) {
$key = self::normaliseObjectIdentifier($class, $condition);

if ($record = $cache->get($key)) {
self::cacheProcessVariants('delete', $record);
$cache->delete($key);
}
} else {
$cache->flush();
}
}
}
16 changes: 8 additions & 8 deletions protected/humhub/components/FindInstanceTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ protected static function findInstanceHelper($identifier, array $config = []): ?
$find = static fn(): ?self => static::findOne([$key => $id]);

if ($config['cached'] ?? true) {
return Yii::$app->runtimeCache->getOrSet(CacheableActiveQuery::normaliseObjectIdentifier(static::class, $id), $find);
return Yii::$app->runtimeCache->getOrSet(RuntimeBaseCache::normaliseObjectIdentifier(static::class, $id), $find);
}

$identifier = $find();
Yii::$app->runtimeCache->set(CacheableActiveQuery::normaliseObjectIdentifier(static::class, $id), $identifier);
Yii::$app->runtimeCache->set(RuntimeBaseCache::normaliseObjectIdentifier(static::class, $id), $identifier);

return $identifier;
}
Expand Down Expand Up @@ -158,7 +158,7 @@ public function hasOneCached(string $class, array $condition, array $identifiers
}
unset($value);

$identifier = CacheableActiveQuery::normaliseObjectIdentifier($class, $identifiers + $fields, $idUsed);
$identifier = RuntimeBaseCache::normaliseObjectIdentifier($class, $identifiers + $fields, $idUsed);

return (!$idUsed || empty($record = Yii::$app->runtimeCache->get($identifier)))
? $this->hasOne($class, $condition)
Expand All @@ -167,35 +167,35 @@ public function hasOneCached(string $class, array $condition, array $identifiers

public function afterDelete()
{
CacheableActiveQuery::cacheProcessVariants('delete', $this);
Yii::$app->runtimeCache->delete($this);

parent::afterDelete();
}

public function afterSave($insert, $changedAttributes)
{
CacheableActiveQuery::cacheProcessVariants('delete', $this);
Yii::$app->runtimeCache->delete($this);

parent::afterSave($insert, $changedAttributes);
}

public static function deleteAll($condition = null, $params = [])
{
CacheableActiveQuery::cacheDeleteByClass(static::class, $condition);
RuntimeBaseCache::cacheDeleteByClass(static::class, $condition);

return parent::deleteAll($condition, $params);
}

public static function updateAll($attributes, $condition = '', $params = [])
{
CacheableActiveQuery::cacheDeleteByClass(static::class, $condition);
RuntimeBaseCache::cacheDeleteByClass(static::class, $condition);

return parent::updateAll($attributes, $condition, $params);
}

public static function updateAllCounters($counters, $condition = '', $params = [])
{
CacheableActiveQuery::cacheDeleteByClass(static::class, $condition);
RuntimeBaseCache::cacheDeleteByClass(static::class, $condition);

return parent::updateAllCounters($counters, $condition, $params);
}
Expand Down

0 comments on commit 8df0534

Please sign in to comment.