Как ORM CakePHP 3.x знает, из какого кеш-файла читать, не определяя его явно?

CakePHP 3.5.13

В методе контроллера я кэширую запрос к базе данных следующим образом:

$substances = TableRegistry::get('Substances');
$query = $substances->find()->limit($limit)->offset($offset);
$query->cache(function ($query) {
    return 'substance_results_' . md5(serialize($query->sql()));
});
$this->set('data', $query->all());

Это создает кешированный файл (мы используем Redis для кеширования в этом приложении), содержащий результаты запроса. Например, в Redis я вижу следующее:

127.0.0.1:6379> keys *
3) "cake_redis_substance_results_cb799f6526c148d133ad9ce9245b23be"
4) "cake_redis_substance_results_dbc7b0b99dff3ab6a20cbdfbbd09be8c"

Если тот же запрос ($query) будет выполнен еще раз, Cake прочитает содержимое соответствующего кэшированного файла. Как это возможно, учитывая, что мы не сообщаем ему, с какого ключа читать кэшированные данные? Мы сообщаем ему имя ключа для написать в, но не читать из.

Чтобы проиллюстрировать это, скажем, я сделал:

$query->cache(function ($query) {
    return 'foo_' . md5(serialize(time()));
});

Здесь я создал совершенно другой ключ, не основанный на выполняемом SQL. Никакая часть кода не сообщает ему, какой ключ соответствует какому запросу.

В документации (https://book.cakephp.org/3.0/en/orm/query-builder.html#caching-loaded-results) по кэшированию загруженных результатов это не объясняется в отношении ORM. Он говорит:

The cache method makes it simple to add cached results to your custom finders or through event listeners.

When the results for a cached query are fetched...

Он сообщает вам, как писать (добавлять) в кеш, но не читать (извлекать) из него по отношению к ORM.

Я читал разделы документации, в которых рассказывается, как читать из кеша обычным, не связанным с ORM способом (с использованием Cache::read($key)), но это совершенно не связано с автоматическим выполнением ORM. В случае Cache::read($key) вам необходимо предоставить ключ ($key), который сообщает ему, из какого файла кеша читать данные - я могу это понять, поскольку вы явно указываете ему, какой ключ читать. Но это не относится к ORM и объектам запроса.

Пожалуйста, кто-нибудь может это прояснить?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В CookBook, скорее всего, не упоминаются дополнительные требования, потому что их нет.

Ключ кеша для пишу, конечно, должен быть таким же, как ключ кеша для чтение, все остальное не будет иметь никакого смысла, т.е. запрос будет использовать все, что вы передаете методу QueryTrait::cache() как для чтения, так и для записи, что означает, что вы находятся явно определяет ключ для чтения прямо здесь, в закрытии, которое вы передаете.

Проверьте описание метода, он говорит примерно то же самое:

/**
 * Enable result caching for this query.
 *
 * If a query has caching enabled, it will do the following when executed:
 *
 * - Check the cache for $key. If there are results no SQL will be executed.
 *   Instead the cached results will be returned.
 * - When the cached data is stale/missing the result set will be cached as the query
 *   is executed.
 *
 * ### Usage
 *
 * ```
 * // Simple string key + config
 * $query->cache('my_key', 'db_results');
 *
 * // Function to generate key.
 * $query->cache(function ($q) {
 *   $key = serialize($q->clause('select'));
 *   $key .= serialize($q->clause('where'));
 *   return md5($key);
 * });
 *
 * [...]
 *
 * @param false|string|\Closure $key Either the cache key or a function to generate the 
 *   cache key. When using a function, this query instance will be supplied as an argument.
 *
 * [...]
 */

Каждый раз, когда выполняется запрос, он проверяет, передали ли вы ключ кеша, а также оценивает и использует его для чтения и записи кешированных результатов соответственно. Следовательно, вы должны убедиться, что ключ кеша является «статическим», чтобы вся вещь кеширования запросов могла быть полезной.

Вы можете использовать замыкание для динамического построения ключа, но результат должен быть статическим, т.е. для одного и того же запроса он должен генерировать один и тот же ключ каждый раз, когда он вызывается. Есть причина, по которой метод QueryTrait::cache() принимает не только замыкание, но и строку!

Я уже упоминал об этом раньше, это происходит в \Cake\Datasource\QueryTrait::all() и \Cake\Datasource\QueryCacher::fetch(), взгляните на источник, чтобы лучше понять, как это работает.

Спасибо за разъяснения. Я действительно считаю, что это следует упомянуть в документации. По сути, ответ заключается в том, что в первом наборе кода, который я опубликовал, serialize($query->sql()) имеет дело как с записью, так и с чтением из кеша, потому что $query->sql() - это строка SQL, которая должна быть выполнена, и поэтому изменяется только при изменении самого запроса. Это означает, что он может найти (прочитать) кэшированный ключ, если один и тот же SQL запускается более одного раза, потому что соответствующий ключ будет существовать.

Andy 28.09.2018 16:32

@Andy Было бы неплохо добавить дополнительное описание. Если вы создадите проблему из-за в github, кто-то, вероятно, позаботится об этом.

ndm 28.09.2018 16:38

Другие вопросы по теме