Какой предлагаемый способ хранения ресурса ETag?

Где мне хранить ETag для данного ресурса?

Подход A: вычисления на лету

Получите ресурс и вычисляйте ETag на лету по каждому запросу:

$resource = $repository->findByPK($id); // query

// Compute ETag
$etag = md5($resource->getUpdatedAt());

$response = new Response();
$response->setETag($etag);
$response->setLastModified($resource->getUpdatedAt());

if($response->isNotModified($this->getRequest())) {
    return $response; // 304
}

Подход B: хранение на уровне базы данных

Экономия процессорного времени при выполнении операторов INSERT и UPDATE немного медленнее (мы используем триггеры для обновления ETag):

$resource = $repository->findByPK($id); // query

$response = new Response();
$response->setETag($resource->getETag());
$response->setLastModified($resource->getUpdatedAt());

if ($response->isNotModified($this->getRequest())) {
    return $response;
}

Подход C: кэширование ETag

Это похоже на подход B, но ETag хранится в некотором промежуточном программном обеспечении кеширования.


person gremo    schedule 21.08.2012    source источник
comment
Я не совсем понимаю, вы ведь говорите о серверной стороне? Почему бы не вычислить его всякий раз, когда на ресурсе есть PUT / POST / DELETE, и не кешировать это? У вас всегда будет последняя версия ETag. Или внешние процессы изменяют ваши файлы?   -  person WhyNotHugo    schedule 21.08.2012
comment
@ Хьюго, извини, что сбил тебя с толку. Имеется дело не с файлами, а с ресурсами / объектами. У меня вопрос о том, где хранить etags: с помощью базы данных + триггеров или любого другого механизма.   -  person gremo    schedule 21.08.2012


Ответы (3)


Я полагаю, это будет зависеть от стоимости доступа к предметам, входящим в сам ETag.

Я имею в виду, что пользователь отправляет запрос на данный ресурс; это должно вызвать операцию извлечения в базе данных (или другую операцию).

Если поиск - это что-то простое, например, получение файла, то запрос статистики файла выполняется быстро, и нет необходимости нигде хранить: MD5 пути к файлу плюс время его обновления достаточно.

Если извлечение подразумевает запрос к базе данных, то это зависит от того, сможете ли вы разложить запрос без потери производительности (например, пользователь запрашивает статью по идентификатору. Вы можете получить соответствующие данные только из таблицы статей. Таким образом, "попадание" в кеш будет влечет за собой один SELECT для первичного ключа. Но «промах» кеша означает, что вам придется снова запросить базу данных, тратя впустую первый запрос - или нет - в зависимости от вашей модели).

Если запрос (или последовательность запросов) хорошо разложим (и полученный код можно поддерживать), я бы снова выбрал динамический ETag.

Если это не так, то большая часть зависит от стоимости запроса и общей стоимости обслуживания решения с сохраненным ETag. Если запрос является дорогостоящим (или вывод громоздкий) и INSERT / UPDATE мало, то (и, я думаю, только тогда) будет выгодно сохранить вторичный столбец (или таблицу) с помощью ETag.

Что касается промежуточного программного обеспечения для кеширования, я не знаю. Если бы у меня был фреймворк, отслеживающий все за меня, я бы сказал «действуйте» - промежуточное ПО должно заботиться и реализовывать перечисленные выше моменты. Если промежуточное ПО не зависит от реализации (маловероятно, если только это не вырезание и вставка ... что не является чем-то неслыханным), тогда будет либо риск того, что оно «скроет» обновления ресурса, либо, возможно, чрезмерная неудобство при вызове API очистки кеша после обновлений. Оба фактора должны быть оценены в сравнении с улучшением нагрузки, предлагаемым поддержкой ETag.

Не думаю, что в этом случае существует «серебряная пуля».

Изменить: в вашем случае разница между случаями A и B небольшая или даже отсутствует. Чтобы иметь возможность реализовать getUpdatedAt (), вам необходимо сохранить время обновления в модели.

В этом конкретном случае я думаю, что было бы проще и легче поддерживать динамический, явный расчет ETag (случай A). Затраты на извлечение возникают в любом случае, а явные затраты на вычисление - это затраты на вычисление MD5, что действительно быстро и полностью зависит от ЦП. На мой взгляд, преимущества в ремонтопригодности и простоте огромны.

Что касается частично связанного с этим примечания, мне приходит в голову, что в некоторых случаях (нечастые обновления базы данных и гораздо более частые запросы к той же базе) может быть выгодно и почти прозрачно реализовать глобальное Last-Modified время для всей базы данных . Если база данных не изменилась, то ни один запрос к базе данных не может возвращать различные ресурсы, независимо от того, что это за запрос. В такой ситуации нужно всего лишь сохранить глобальный флаг Last-Modified в каком-нибудь удобном и быстром извлекаемом месте (не обязательно в базе данных). Например

function dbModified() {
    touch('.last-update'); // creates the file, or updates its modification time
}

в любом UPDATE/DELETE коде. Ресурс затем добавит заголовок

function sendModified() {
    $tsstring = gmdate('D, d M Y H:i:s ', filemtime('.last-update')) . 'GMT';
    Header("Last-Modified: " . $tsstring);
}

чтобы сообщить браузеру о времени модификации ресурса.

Затем любой запрос ресурса, включая If-Modified-Since, может быть возвращен с помощью 304 без доступа к уровню постоянства (или, по крайней мере, с сохранением всего постоянного доступа к ресурсам). Время обновления на уровне записи не потребуется (не обязательно):

function ifNotModified() {
    // Check out timezone settings. The GMT helps but it's not always the ticket
    $ims = isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
        ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])
        : -1; // This ensures the test will FAIL

   if (filemtime('.last-update') <= $ims) {
       // The database was never updated after the resource retrieval.
       // There's no way the resource may have changed.
       exit(Header('HTTP/1.1 304 Not Modified'));
   }
}

Можно было бы разместить вызов ifNotModified () как можно раньше в маршруте предоставления ресурсов, sendModified как можно раньше в коде вывода ресурсов, а dbModified () везде, где база данных существенно изменяется в отношении ресурсов (т. Е. вы можете и, вероятно, должны избегать этого при регистрации статистики доступа к базе данных, если она не влияет на содержимое ресурсов).

person LSerni    schedule 21.08.2012

На мой взгляд, сохраняющиеся ETags - это ПЛОХАЯ ИДЕЯ, если ваша бизнес-логика НЕ ​​ПРОФЕССИОНАЛЬНЫЕ ETags. Например, когда вы пишете приложение для отслеживания пользователей по ETags, а это бизнес-фича :).

Потенциальная экономия времени выполнения будет небольшой или отсутствующей. Плохие стороны этого решения очевидны и растут по мере роста вашего приложения.

Согласно спецификации Ресурс в одной и той же версии должен выдавать разные E-теги в зависимости от конечной точки, из которой была получена.

Из http://en.wikipedia.org/wiki/HTTP_ETag:

«Сравнение ETags имеет смысл только в отношении одного URL-адреса - ETags для ресурсов, полученных из разных URL-адресов, могут быть или не быть равными, поэтому из их сравнения нельзя вывести никакого смысла».

Из этого вы можете сделать вывод, что вам следует сохранять не только ETags, но и его конечную точку и хранить столько ETags, сколько у вас есть enpoints. Звучит безумно?

Даже если вы хотите игнорировать спецификацию HTTP и просто предоставить один Etag для Entity без каких-либо метаданных о его конечных точках. Вы по-прежнему связываете как минимум 2 уровня (кеширование и бизнес-логику), которые в идеале не следует смешивать. Идея наличия Entity (по сравнению с некоторыми потерянными данными) заключается в том, чтобы иметь в них отдельную, а не связанную бизнес-логику и не засорять их вещами о сети, данными уровня просмотра или ... кешированием.

person smentek    schedule 24.01.2014

IHMO, это зависит от того, как часто обновляются ресурсы и как часто ресурсы читаются.

Если каждый ETag читается 1 или 2 раза между модификациями, просто вычисляйте их на лету.
Если ваши ресурсы читаются гораздо чаще, чем обновляются, вам лучше кэшировать их, вычисляя ETag каждый раз. ресурс изменен (поэтому вам не нужно беспокоиться об устаревших кэшированных ETag).

Если ETags изменяются почти так же часто, как они читаются, я все равно кэширую их, тем более что кажется, что ваши ресурсы хранятся в базе данных.

person WhyNotHugo    schedule 21.08.2012