"new" and "updated" marker no longer breaking render cache
The comment "new" marker is based on the user's last viewed timestamp, which means it's forced to be per-user. That made comments only cacheable per-user. Hence it broke the render cache.
This was solved by the following measures:
- Embed metadata that is the same for all users in the HTML as
data-
attributes: the node's last comment timestamp, the node's ID, the comment user ID, each comment timestamp. - Attach JavaScript that adds the "new" and "updated" markers, which has to perform a HTTP request to know the latest time the current user read a node (and hence also its comments). Use heuristics (i.e. any content >30 days old doesn't get a "new" or "updated" marker) and client-side caching (
localStorage
) to avoid performing a HTTP request in most scenarios. - See #1991684-40: Node history markers (comment & node "new" indicator, "x new comments" links) forces render caching to be per user for a comprehensive overview of the heuristics used to minimize HTTP requests.
There are quite a few different places where this is used: comment "new" indicators, node "new comments" links, tracker "new/updated" node indicators, tracker "x new replies" links, plus use cases in the forum module. All of them should use the same heuristics and client-side caching, to prevent duplicate logic and maximize client-side cache hit ratio.
This enables entity render caching: #1605290: Enable entity render caching with cache tag support.
History JS API
That's why there's now a new History JS API, in the drupal.history
library. It has the following methods:
needsServerCheck(nodeID, contentTimestamp
: determines whether a server check is necessary. This can be used to consistently apply the heuristics. Any content that is >30 days old never gets a "new" or "updated" indicator, because it is assumed to have been read already. Any content that was published before the known "last read" timestamp (as cached inlocalStorage
) also is assumed to have been read already.
In other words: old content and content that's already been read can be ignored completely if this function returnsfalse
.fetchTimestamps(nodeIDs, callback)
: fetches the "last read" timestamps for the given nodes, stores them inlocalStorage
and calls the callback, which can then callgetLastRead(nodeID)
. You should only pass in node IDs for whichneedsServerCheck()
returnedtrue
— i.e. the node IDs that need a server check.getLastRead(nodeID)
: gets the "last read" timestamp for the given node, as stored inlocalStorage
. Should be called afterfetchTimestamps()
has done its thing and called the callback.markAsRead(nodeID)
: to mark the given node as read on the server side; automatically updates the client-side cache (to minimize HTTP requests further).
Look at the drupal.comment-new-indicator
or drupal.node-new-comments-link
libraries for sample code that uses the History JS API.
User-specific styling ("comment by viewer") no longer breaking render cache
Similarly, the by-viewer
class was being set in PHP, and is user-specific, hence also broke the render cache. The solution there was to add some JavaScript (see the drupal.comment-by-viewer
library), that combined with a data-comment-user-id
attribute on each comment and a new drupalSettings.user.uid
setting knows whether the current user is also the commenter.
Comments
Code examples?
Do we typically include code examples for new APIs? If so it would be great to distill the above information into a few code examples of the API in use.