Problem/Motivation
When user entity base fields 'access' and 'login' are changing their values cache associated with user cache tags is not cleared. There's a good reason for this. If the cache associated with user cache tags would be invalidated on every change of 'access', this will degrade the performance because that cache will be cleared too often.
However, for this reason, the pages where these values are displayed (like /admin/people
view page) are displaying stale content.
Proposed resolution
Display the 2 fields using formatters that are lazy rendering their values.
Remaining tasks
None.
User interface changes
None.
API changes
New field type 'user_timestamp' — an unmodified extension of 'timestamp'.
New field formatters 'user_timestamp' (extending 'timestamp') and 'user_timestamp_ago' (extending 'user_timestamp_ago'). The new formatters have the same output bu are using lazy rendering.
Data model changes
The user entity base fields are no more fields of type 'timestamp' but of the new 'user_timestamp' type.
Views or displays having user entity 'access' or 'login' fields and using the 'timestamp' or 'timestamp_ago' formatters will use the new 'user_timestamp' and 'user_timestamp_ago' new formatters.
Comment | File | Size | Author |
---|---|---|---|
#25 | user_timestamp_cache-2650768-25.patch | 1.1 KB | marcvangend |
#13 | 2650768-13.patch | 16.99 KB | claudiu.cristea |
#13 | interdiff.txt | 4.94 KB | claudiu.cristea |
Comments
Comment #2
proxiss CreditAttribution: proxiss as a volunteer commentedComment #3
cilefen CreditAttribution: cilefen commentedComment #4
claudiu.cristeaThis should fix the issue, can you try?
But keep in mind that the "last access" time is refreshing NO often than 180 sec (3 mins). You can lower the value by adding this in settings.php:
For testing purposes you can do this.
While it's a caching issue I would like to pass this issue to @Wim Leers. Also a question for Wim: Shouldn't we trigger the cache invalidation [Cache::invalidateTags()] inside the entity internal cache invalidation? I mean when $this->resetCache(array($account->id())) is called, subsequently, the internal cache mechanism can also call Cache::invalidateTags(), instead of:
Comment #5
claudiu.cristeaFixed title, summary, version. and yes, needs tests.
Comment #6
claudiu.cristeaComment #8
claudiu.cristeaDefinitely, cacheability it's not an easy topic :)
So I guess that #4 is wrong because will invalidate the user cache too often. That's why I think this case fits better to auto-placeholdering. Here's a experimental patch but take into consideration following notes:
The new 'user_timestamp' is still cached. It doesn't work as expected.The patch is missing an upgrade path for user entity definition and a post_update to change the formatters for user 'access' and 'login' base fields in all entity view displays and all views.\Drupal\Core\Render\Renderer
. Without that it doesn't work. I don't understand how to debug that. Is that a bug in Renderer?A side-note: I think formatters and widgets should receive (maybe from their $this->fieldDefinition?) a flag that tells formatter if to build normally or lazy the output. For example, the definition for user 'access' base field could be:
Then the same formatter can handle both cases depending on this bool without needing for a new formatter.
Comment #10
claudiu.cristeaFixed more points, added upgrade path (with test).
Comment #12
claudiu.cristeaThe IS should reflect #10.
Comment #13
claudiu.cristeaFixed IS. Fixed tests.
Comment #14
Wim LeersThis seems like it's a duplicate of #2500525: Time ago/hence date/time formatting breaks caching; needs appropriate max age.
Comment #15
dawehnerSounds like that indeed, with the formatter being fixed, we would not need those workarounds in the first place.
Comment #16
Wim LeersThanks for confirming. @claudiu.cristea, could you please review the patch on the other issue? You having worked on this patch makes you one of the best people possible to review the other patch :)
Comment #17
claudiu.cristea@Wim Leers, @dawehner, I don't think they are the same issue. In this case the 'access' and 'login' values of user are cached forever (because their update doesn't pass trough entity save). The other patch seems to be the opposite. Here's not only about "time ago" (aka interval calculated on current time). I would review the other but I want first that we decide if these are the same issue.
Comment #18
mpdonadioEven if this isn't a duplicate, I don't think the approach of adding a new formatter is correct (the same issue will come up lots pf places timestamps). It seems like a too specific solution to a generic problem. On the other patch, we are going down the road of calculating the cache-max age based on what the timeago will present back to the user. Another option I contemplated was whether we actually need a "request_time" cache context, which I think it the correct solution there (and yes, it would kill performance but would guarantee correct data being rendered).
Comment #19
Wim LeersIn this particular case, why not just allow log in/user creation to indeed invalidate the user entity's cache tag? That'd be the more consistent/simpler solution, wouldn't it?
It seems like these are simply wrong:
Logging in definitely doesn't happen very often. Access does. But:
It'll happen at most every 180 seconds.
Not sure at all, just trying to make sure we properly discuss that option, because it'd be by far the simplest.
Comment #20
mpdonadio#19 sounds like the proper thing to do, and is one of the whole points of why we introduced cache contexts (so we don't need to manually muck around with invalidating caches).
Comment #22
marcvangendI'm trying to understand what needs to be done here. At least comment #19sounds reasonable to me. But what would that mean for the patch in #13?
Also I doubt if we really need a user_timestamp formatter. If this formatter solves something, can we be sure it is only useful in the context of a user? If not, let's name it after what it does, not what it was created for initially.
Comment #24
marcvangendI talked with @claudiu.cristea about this at the Drupal Dev Days. We concluded that the first patch from #4 is exactly (or pretty close to) the solution that is proposed in #19... Or did we miss something, @WimLeers?
The patch from #4 does not apply anymore so it would need a re-roll.
Comment #25
marcvangendHere's a re-roll of the patch in #4. It works fine as far as I can see (I mainly tested with the "Member for" timestamp on user/[uid]).
That said I'm still not sure if it's the best solution. It seems wrong to invalidate a cache tag when the data itself didn't change, but only the representation of that data is stale. Thoughts?
Comment #26
marcvangendComment #29
Wim LeersI was specifically referring to logging in and creating users being infrequent actions. For those use cases, invalidating cache tags seems acceptable. So IMO that's a good solution for the "login timestamp" mentioned in the issue title.
For the "last access timestamp", I'd use https://www.drupal.org/developing/api/8/cache/max-age — just specify a max-age that matches:
$build['#cache']['max-age'] = Settings::get('session_write_interval', 180);
. This will ensure that the last access timestamp is at most 3 minutes old… which is fine, since it's also updated at most every 3 minutes. Worst case, this means it will be 5 minutes and 59 seconds old.This view is not designed to give a real-time dashboard. It's meant to administer users, and get a sense of which users have never logged in, have not logged in since months, etc. So somewhat stale data seems fine to me.
Of course, the ideal would be to never have stale data for any of this. But AFAIK Views retrieves the last access timestamp in an actual DB query. Which means that it's not possible to cache all fields in each row except that one, and have just that one be queried when displaying the view. That may be the ideal.
IMO this issue needs feedback/guidance from a Views maintainer. I've given my opinion as a de facto cache system maintainer.
Comment #30
Anonymous (not verified) CreditAttribution: Anonymous at Ashday Interactive Systems commentedI just came across this bug on one of our sites. It seems to me like if Drupal is going to display a "Member For" and "Last Access", they should be kept current. As-is, it can be rather confusing, and it misled us while we were debugging issues related to user activity earlier in the day.
Right now the page looks like a real-time dashboard, so either the numbers (including "Last Access") should be accurate, or the page shouldn't be displaying them in a format which makes them look real-time but which falls out of date. Would it help perhaps to show the actual last access date & time, rather than simply "time ago"? That might have the related benefit of giving minute-precision to times a day or longer ago, which it doesn't currently show on this page. I'm not super well-versed in this level of caching, though, so I'm not certain if that would work.
Comment #41
catchI think we should open a separate issue to look at moving these fields out of base fields into a separate service. If we were adding them now I don't think we'd have them in the base table at all.
edit: here it is #3299948: Move access and login fields out of the user entity.