perf(cache): use deleteMatching method to prevent forgetting cached elements in models

This commit is contained in:
Yassine Doghri 2021-05-31 13:32:33 +00:00
parent 7bcbfb32f7
commit 76afc0cfa2
No known key found for this signature in database
GPG Key ID: 3E7F89498B960C9F
9 changed files with 68 additions and 115 deletions

View File

@ -26,7 +26,7 @@ class EpisodeController extends BaseController
{ {
protected Podcast $podcast; protected Podcast $podcast;
protected ?Episode $episode; protected Episode $episode;
public function _remap(string $method, string ...$params): mixed public function _remap(string $method, string ...$params): mixed
{ {
@ -253,7 +253,7 @@ class EpisodeController extends BaseController
$this->episode->is_blocked = $this->request->getPost('block') === 'yes'; $this->episode->is_blocked = $this->request->getPost('block') === 'yes';
$this->episode->custom_rss_string = $this->request->getPost('custom_rss',); $this->episode->custom_rss_string = $this->request->getPost('custom_rss',);
$this->episode->updated_by = user_id(); $this->episode->updated_by = (int) user_id();
$audioFile = $this->request->getFile('audio_file'); $audioFile = $this->request->getFile('audio_file');
if ($audioFile !== null && $audioFile->isValid()) { if ($audioFile !== null && $audioFile->isValid()) {
@ -268,7 +268,7 @@ class EpisodeController extends BaseController
$transcriptChoice = $this->request->getPost('transcript-choice'); $transcriptChoice = $this->request->getPost('transcript-choice');
if ($transcriptChoice === 'upload-file') { if ($transcriptChoice === 'upload-file') {
$transcriptFile = $this->request->getFile('transcript_file'); $transcriptFile = $this->request->getFile('transcript_file');
if ($transcriptFile->isValid()) { if ($transcriptFile !== null && $transcriptFile->isValid()) {
$this->episode->transcript_file = $transcriptFile; $this->episode->transcript_file = $transcriptFile;
$this->episode->transcript_file_remote_url = null; $this->episode->transcript_file_remote_url = null;
} }
@ -287,7 +287,7 @@ class EpisodeController extends BaseController
$chaptersChoice = $this->request->getPost('chapters-choice'); $chaptersChoice = $this->request->getPost('chapters-choice');
if ($chaptersChoice === 'upload-file') { if ($chaptersChoice === 'upload-file') {
$chaptersFile = $this->request->getFile('chapters_file'); $chaptersFile = $this->request->getFile('chapters_file');
if ($chaptersFile->isValid()) { if ($chaptersFile !== null && $chaptersFile->isValid()) {
$this->episode->chapters_file = $chaptersFile; $this->episode->chapters_file = $chaptersFile;
$this->episode->chapters_file_remote_url = null; $this->episode->chapters_file_remote_url = null;
} }
@ -411,13 +411,11 @@ class EpisodeController extends BaseController
if ($publishMethod === 'schedule') { if ($publishMethod === 'schedule') {
$scheduledPublicationDate = $this->request->getPost('scheduled_publication_date',); $scheduledPublicationDate = $this->request->getPost('scheduled_publication_date',);
if ($scheduledPublicationDate) { if ($scheduledPublicationDate) {
$scheduledDateUTC = Time::createFromFormat( $this->episode->published_at = Time::createFromFormat(
'Y-m-d H:i', 'Y-m-d H:i',
$scheduledPublicationDate, $scheduledPublicationDate,
$this->request->getPost('client_timezone'), $this->request->getPost('client_timezone'),
)->setTimezone('UTC'); )->setTimezone('UTC');
$this->episode->published_at = $scheduledDateUTC;
$newNote->published_at = $scheduledDateUTC;
} else { } else {
$db->transRollback(); $db->transRollback();
return redirect() return redirect()
@ -426,11 +424,11 @@ class EpisodeController extends BaseController
->with('error', 'Schedule date must be set!'); ->with('error', 'Schedule date must be set!');
} }
} else { } else {
$dateNow = Time::now(); $this->episode->published_at = Time::now();
$this->episode->published_at = $dateNow;
$newNote->published_at = $dateNow;
} }
$newNote->published_at = $this->episode->published_at;
$noteModel = new NoteModel(); $noteModel = new NoteModel();
if (! $noteModel->addNote($newNote)) { if (! $noteModel->addNote($newNote)) {
$db->transRollback(); $db->transRollback();
@ -498,20 +496,15 @@ class EpisodeController extends BaseController
$db = Database::connect(); $db = Database::connect();
$db->transStart(); $db->transStart();
$note = (new NoteModel())->getNoteById($this->request->getPost('note_id'),);
$note->message = $this->request->getPost('message');
$publishMethod = $this->request->getPost('publication_method'); $publishMethod = $this->request->getPost('publication_method');
if ($publishMethod === 'schedule') { if ($publishMethod === 'schedule') {
$scheduledPublicationDate = $this->request->getPost('scheduled_publication_date',); $scheduledPublicationDate = $this->request->getPost('scheduled_publication_date',);
if ($scheduledPublicationDate) { if ($scheduledPublicationDate) {
$scheduledDateUTC = Time::createFromFormat( $this->episode->published_at = Time::createFromFormat(
'Y-m-d H:i', 'Y-m-d H:i',
$scheduledPublicationDate, $scheduledPublicationDate,
$this->request->getPost('client_timezone'), $this->request->getPost('client_timezone'),
)->setTimezone('UTC'); )->setTimezone('UTC');
$this->episode->published_at = $scheduledDateUTC;
$note->published_at = $scheduledDateUTC;
} else { } else {
$db->transRollback(); $db->transRollback();
return redirect() return redirect()
@ -520,11 +513,15 @@ class EpisodeController extends BaseController
->with('error', 'Schedule date must be set!'); ->with('error', 'Schedule date must be set!');
} }
} else { } else {
$dateNow = Time::now(); $this->episode->published_at = Time::now();
$this->episode->published_at = $dateNow;
$note->published_at = $dateNow;
} }
$note = (new NoteModel())->getNoteById($this->request->getPost('note_id'),);
if ($note !== null) {
$note->message = $this->request->getPost('message');
$note->published_at = $this->episode->published_at;
$noteModel = new NoteModel(); $noteModel = new NoteModel();
if (! $noteModel->editNote($note)) { if (! $noteModel->editNote($note)) {
$db->transRollback(); $db->transRollback();
@ -533,6 +530,7 @@ class EpisodeController extends BaseController
->withInput() ->withInput()
->with('errors', $noteModel->errors()); ->with('errors', $noteModel->errors());
} }
}
$episodeModel = new EpisodeModel(); $episodeModel = new EpisodeModel();
if (! $episodeModel->update($this->episode->id, $this->episode)) { if (! $episodeModel->update($this->episode->id, $this->episode)) {

View File

@ -39,7 +39,7 @@ class EpisodeController extends BaseController
} }
if ( if (
($this->episode = (new EpisodeModel())->getEpisodeBySlug($this->podcast->id, $params[1],)) !== null ($this->episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1],)) !== null
) { ) {
unset($params[1]); unset($params[1]);
unset($params[0]); unset($params[0]);

View File

@ -57,10 +57,8 @@ class FakePodcastsAnalyticsSeeder extends Seeder
$analyticsPodcastsByRegion = []; $analyticsPodcastsByRegion = [];
$episodes = (new EpisodeModel()) $episodes = (new EpisodeModel())
->where([ ->where('podcast_id', $podcast->id)
'podcast_id' => $podcast->id, ->where('`published_at` <= NOW()', null, false)
'DATE(published_at) <=' => date('Y-m-d', $date),
])
->findAll(); ->findAll();
foreach ($episodes as $episode) { foreach ($episodes as $episode) {
$age = floor(($date - strtotime($episode->published_at)) / 86400,); $age = floor(($date - strtotime($episode->published_at)) / 86400,);
@ -110,7 +108,7 @@ class FakePodcastsAnalyticsSeeder extends Seeder
? 'N/A' ? 'N/A'
: $city->country->isoCode; : $city->country->isoCode;
$regionCode = $city->subdivisions[0]->isoCode === null $regionCode = $city->subdivisions === []
? 'N/A' ? 'N/A'
: $city->subdivisions[0]->isoCode; : $city->subdivisions[0]->isoCode;
$latitude = round($city->location->latitude, 3); $latitude = round($city->location->latitude, 3);

View File

@ -196,10 +196,8 @@ class FakeWebsiteAnalyticsSeeder extends Seeder
$websiteByReferer = []; $websiteByReferer = [];
$episodes = (new EpisodeModel()) $episodes = (new EpisodeModel())
->where([ ->where('podcast_id', $podcast->id)
'podcast_id' => $podcast->id, ->where('`published_at` <= NOW()', null, false)
'DATE(published_at) <=' => date('Y-m-d', $date),
])
->findAll(); ->findAll();
foreach ($episodes as $episode) { foreach ($episodes as $episode) {
$age = floor(($date - strtotime($episode->published_at)) / 86400,); $age = floor(($date - strtotime($episode->published_at)) / 86400,);

View File

@ -39,7 +39,7 @@ class EpisodeController extends BaseController
} }
if ( if (
($this->episode = (new EpisodeModel())->getEpisodeBySlug($this->podcast->id, $params[1],)) !== null ($this->episode = (new EpisodeModel())->getEpisodeBySlug($params[0], $params[1],)) !== null
) { ) {
unset($params[1]); unset($params[1]);
unset($params[0]); unset($params[0]);

View File

@ -13,9 +13,9 @@ use CodeIgniter\Model;
class EpisodeModel extends Model class EpisodeModel extends Model
{ {
// TODO: remove
/** /**
* TODO: remove, shouldn't be here
*
* @var array<string, array<string, string>> * @var array<string, array<string, string>>
*/ */
public static $themes = [ public static $themes = [
@ -135,45 +135,26 @@ class EpisodeModel extends Model
*/ */
protected $afterInsert = ['writeEnclosureMetadata', 'clearCache']; protected $afterInsert = ['writeEnclosureMetadata', 'clearCache'];
// clear cache beforeUpdate because if slug changes, so will the episode link
/** /**
* @var string[] * @var string[]
*/ */
protected $beforeUpdate = ['clearCache']; protected $afterUpdate = ['clearCache', 'writeEnclosureMetadata'];
/**
* @var string[]
*/
protected $afterUpdate = ['writeEnclosureMetadata'];
/** /**
* @var string[] * @var string[]
*/ */
protected $beforeDelete = ['clearCache']; protected $beforeDelete = ['clearCache'];
/** public function getEpisodeBySlug(string $podcastName, string $episodeSlug): ?Episode
* @param int|string $podcastId may be the id or podcast name
*/
public function getEpisodeBySlug(int | string $podcastId, string $episodeSlug): ?Episode
{ {
$cacheName = "podcast#{$podcastId}_episode-{$episodeSlug}"; $cacheName = "podcast-{$podcastName}_episode-{$episodeSlug}";
if (! ($found = cache($cacheName))) { if (! ($found = cache($cacheName))) {
$builder = $this->select('episodes.*') $found = $this->select('episodes.*')
->where('slug', $episodeSlug)
->where('`published_at` <= NOW()', null, false);
if (is_numeric($podcastId)) {
// passed argument is the podcast id
$builder->where('podcast_id', $podcastId);
} else {
// passed argument is the podcast name, must perform join
$builder
->join('podcasts', 'podcasts.id = episodes.podcast_id') ->join('podcasts', 'podcasts.id = episodes.podcast_id')
->where('podcasts.name', $podcastId); ->where('slug', $episodeSlug)
} ->where('podcasts.name', $podcastName)
->where('`published_at` <= NOW()', null, false)
$found = $builder->first(); ->first();
cache() cache()
->save($cacheName, $found, DECADE); ->save($cacheName, $found, DECADE);
@ -278,9 +259,9 @@ class EpisodeModel extends Model
* Returns the timestamp difference in seconds between the next episode to publish and the current timestamp Returns * Returns the timestamp difference in seconds between the next episode to publish and the current timestamp Returns
* false if there's no episode to publish * false if there's no episode to publish
* *
* @return int|bool seconds * @return int|false seconds
*/ */
public function getSecondsToNextUnpublishedEpisode(int $podcastId): int | bool public function getSecondsToNextUnpublishedEpisode(int $podcastId): int | false
{ {
$result = $this->select('TIMESTAMPDIFF(SECOND, NOW(), `published_at`) as timestamp_diff',) $result = $this->select('TIMESTAMPDIFF(SECOND, NOW(), `published_at`) as timestamp_diff',)
->where([ ->where([
@ -291,7 +272,7 @@ class EpisodeModel extends Model
->get() ->get()
->getResultArray(); ->getResultArray();
return count($result) !== 0 return $result !== []
? (int) $result[0]['timestamp_diff'] ? (int) $result[0]['timestamp_diff']
: false; : false;
} }
@ -305,46 +286,18 @@ class EpisodeModel extends Model
{ {
$episode = (new self())->find(is_array($data['id']) ? $data['id'][0] : $data['id'],); $episode = (new self())->find(is_array($data['id']) ? $data['id'][0] : $data['id'],);
// delete cache for rss feed // delete podcast cache
cache() cache()
->deleteMatching("podcast#{$episode->podcast_id}_feed*"); ->deleteMatching("podcast#{$episode->podcast_id}*");
// delete model requests cache
cache() cache()
->delete("podcast#{$episode->podcast_id}_episodes"); ->deleteMatching("podcast-{$episode->podcast->name}*");
cache() cache()
->delete("podcast_episode#{$episode->id}"); ->delete("podcast_episode#{$episode->id}");
cache() cache()
->deleteMatching("podcast#{$episode->podcast_id}_episode#{$episode->id}*"); ->deleteMatching("page_podcast#{$episode->podcast_id}*");
cache()
->delete("podcast#{$episode->podcast_id}_episode-{$episode->slug}");
cache()
->deleteMatching("page_podcast#{$episode->podcast_id}_activity*");
cache()
->deleteMatching("page_podcast#{$episode->podcast_id}_episode#{$episode->id}_*",);
cache() cache()
->deleteMatching('page_credits_*'); ->deleteMatching('page_credits_*');
if ($episode->season_number) {
cache()->deleteMatching("podcast#{$episode->podcast_id}_season*");
cache()
->deleteMatching("page_podcast#{$episode->podcast_id}_episodes_season*",);
} else {
cache()->deleteMatching("podcast#{$episode->podcast_id}_year*");
cache()
->deleteMatching("page_podcast#{$episode->podcast_id}_episodes_year*",);
}
// delete query cache
cache()
->delete("podcast#{$episode->podcast_id}_defaultQuery");
cache()
->delete("podcast#{$episode->podcast_id}_years");
cache()
->delete("podcast#{$episode->podcast_id}_seasons");
return $data; return $data;
} }

View File

@ -171,7 +171,7 @@ class PersonModel extends Model
->getLocale(); ->getLocale();
$cacheName = "taxonomy_options_{$locale}"; $cacheName = "taxonomy_options_{$locale}";
/** @var array<string, array<string, string|array<string, array<string, string>>>> $personsTaxonomy */ /** @var array<string, mixed> $personsTaxonomy */
$personsTaxonomy = lang('PersonsTaxonomy.persons'); $personsTaxonomy = lang('PersonsTaxonomy.persons');
if (! ($options = cache($cacheName))) { if (! ($options = cache($cacheName))) {
@ -285,14 +285,14 @@ class PersonModel extends Model
/** /**
* Add persons to podcast * Add persons to podcast
* *
* @param array<string> $persons * @param array<string> $personIds
* @param array<string, string> $roles * @param array<string, string> $roles
* *
* @return bool|int Number of rows inserted or FALSE on failure * @return bool|int Number of rows inserted or FALSE on failure
*/ */
public function addPodcastPersons(int $podcastId, array $persons = [], array $roles = []): int | bool public function addPodcastPersons(int $podcastId, array $personIds = [], array $roles = []): int | bool
{ {
if ($persons === []) { if ($personIds === []) {
return 0; return 0;
} }
@ -303,11 +303,11 @@ class PersonModel extends Model
]); ]);
$data = []; $data = [];
foreach ($persons as $person) { foreach ($personIds as $personId) {
if ($roles === []) { if ($roles === []) {
$data[] = [ $data[] = [
'podcast_id' => $podcastId, 'podcast_id' => $podcastId,
'person_id' => $person, 'person_id' => $personId,
]; ];
} }
@ -315,7 +315,7 @@ class PersonModel extends Model
$groupRole = explode(',', $role); $groupRole = explode(',', $role);
$data[] = [ $data[] = [
'podcast_id' => $podcastId, 'podcast_id' => $podcastId,
'person_id' => $person, 'person_id' => $personId,
'person_group' => $groupRole[0], 'person_group' => $groupRole[0],
'person_role' => $groupRole[1], 'person_role' => $groupRole[1],
]; ];
@ -337,6 +337,9 @@ class PersonModel extends Model
cache()->deleteMatching("podcast#{$podcastId}_person#{$personId}*"); cache()->deleteMatching("podcast#{$podcastId}_person#{$personId}*");
cache() cache()
->delete("podcast#{$podcastId}_persons"); ->delete("podcast#{$podcastId}_persons");
(new PodcastModel())->clearCache([
'id' => $podcastId,
]);
return $this->db->table('podcasts_persons') return $this->db->table('podcasts_persons')
->delete([ ->delete([
@ -399,6 +402,9 @@ class PersonModel extends Model
cache()->deleteMatching("podcast#{$podcastId}_episode#{$episodeId}_person#{$personId}*"); cache()->deleteMatching("podcast#{$podcastId}_episode#{$episodeId}_person#{$personId}*");
cache() cache()
->delete("podcast#{$podcastId}_episode#{$episodeId}_persons"); ->delete("podcast#{$podcastId}_episode#{$episodeId}_persons");
(new EpisodeModel())->clearCache([
'id' => $episodeId,
]);
return $this->db->table('episodes_persons') return $this->db->table('episodes_persons')
->delete([ ->delete([
@ -424,7 +430,7 @@ class PersonModel extends Model
// clear cache for every credits page // clear cache for every credits page
cache() cache()
->deleteMatching('page_credits_*'); ->deleteMatching('page_credits*');
return $data; return $data;
} }

View File

@ -20,9 +20,9 @@
class="text-xs text-gray-500"> class="text-xs text-gray-500">
<time <time
itemprop="published" itemprop="published"
datetime="<?= $note->created_at->format(DateTime::ATOM) ?>" datetime="<?= $note->published_at->format(DateTime::ATOM) ?>"
title="<?= $note->created_at ?>" title="<?= $note->published_at ?>"
><?= lang('Common.mediumDate', [$note->created_at]) ?></time> ><?= lang('Common.mediumDate', [$note->published_at]) ?></time>
</a> </a>
</div> </div>
</header> </header>

View File

@ -14,9 +14,9 @@
<time <time
class="flex-shrink-0 ml-auto text-xs text-gray-600" class="flex-shrink-0 ml-auto text-xs text-gray-600"
itemprop="published" itemprop="published"
datetime="<?= $reply->created_at->format(DateTime::ATOM) ?>" datetime="<?= $reply->published_at->format(DateTime::ATOM) ?>"
title="<?= $reply->created_at ?>" title="<?= $reply->published_at ?>"
><?= lang('Common.mediumDate', [$reply->created_at]) ?></time> ><?= lang('Common.mediumDate', [$reply->published_at]) ?></time>
</header> </header>
<p class="mb-2 note-content"><?= $reply->message_html ?></p> <p class="mb-2 note-content"><?= $reply->message_html ?></p>
<?php if ($reply->has_preview_card): ?> <?php if ($reply->has_preview_card): ?>