Issue Summary
When saving a node with the Purge module enabled, our content is always a version behind. In essence, we are forced to save the node changes twice in order for the updates to be seen live. The form editor also holds the old copy after the first save.
Investigation found this link describing the issue accurately: http://janezurevc.name/strange-behaviour-race-condition-during-nodesave
We have multiple servers in the mix:
1. MySQL/Memcached
2. Varnish
3. App Server
Memcached continues to hold the old copy of the edits with the Purge module enabled. Varnish cannot retrieve the updated copy because Drupal never serves it, still believing to most recent copy is the one that lives in memcache. The database does hold the newest version, however.
A restart of memcached results in Drupal serving the most recent save. Disabling the Purge module resumes normal functionality. We have resorted to using an in-house form to manually purge URLs from Varnish after a node update.
I understand that the race condition is fixed in Drupal 8 and we are waiting on a backport to Drupal 7. However, is there anything I can do that allows Purge to execute after the db transaction is complete? Since Purge uses the expire_cache hook, it might be tedious and/or difficult to implement.
| Comment | File | Size | Author |
|---|---|---|---|
| #11 | expire-use_hook_post_action-2412517-11.patch | 3.19 KB | Anonymous (not verified) |
Comments
Comment #1
fewdea commentedWorkaround
Download and install the Hook Post Action module.
Modify the Expire module code to implement hook_node_postupdate, hook_node_postinsert and hook_node_postdelete instead of the defaults.
Modify the following lines in expire.module:
To look like:
The only hooks we needed to modify were the node hooks. The Expire module has and uses many more than that, and Hook Post Action only adds node and entity hooks, but not comment or user hooks. If you were to need more hooks, you'd have to edit the Hook Post Action module to create additional hooks.
Comment #2
djbobbydrake commentedI can confirm that I was having the same issues with the race condition. Same architecture with Mysql, Memcache, Varnish. Implementing this solved the problem for us. Thanks!
Comment #3
SqyD commentedAs suggested I would see this as something that would need addressing in the Expire module as that's the component in control over what happens when. Purge will likely be just as affected as other implementors of Expires hooks such as Varnish module etc. I'm moving this issue to the Expire project.
Comment #4
spleshkaI am completely opened to solve this issue in the Expire module. I just want to get more details about why that race condition happens? Does varnish read a page from memcached or from app stack? Please, provide more details about integrations in your server stack.
Comment #5
mgzrobles commentedHi!
I think I have the same issue.
Problem:
- Edit node or nodequeu in my case.
- Expire get all urls to clear
- Call varnish to expire them
- Then a user make a request and
-- Get a Varnish MISS (correct)
-- Get a DRUPAL HIT from memcache in my case (no correct)
why can select Expire external and internal at same time?
Comment #6
joinso commentedSame problem here!
My config:
Cache Expiration (7.x-2.x-dev)
MemCache (7.x-1.5)
Recacher (7.x-1.0)
HTTP Parallel Request Library (7.x-1.14+50-dev)
When I save the node, Expires show the correct output: expires node/NID, url, home page...
Recacher shows sucessful load of urls.
However MemCache still serving the old page.
Any idea?
Regards,
JOINSO
Comment #7
zerolab commentedHere's a a code snippet that does the above for nodes:
Spleshka, are you happy with the
drupal_register_shutdown_function()approach? If yes, will work on a patch to bring this in for Expire without the need for additional modules.edit: added manual cache clear when
expire_include_base_urlis enabled.Comment #8
sgdev commentedI think we may have found the solution, and I believe this is the core issue: https://www.drupal.org/node/221081
If you look at the
saveanddeletefunctions fornode,user, andfileentities, the hooks are processed *before* runningresetCache. Because the Expire module useshook_insert,hook_update, andhook_delete, it can lead to unexpected caching results. This explains why the workaround in #1 would work.The patch in #221081 was never backported to D7, so it is necessary to execute the
resetCacheimmediately prior to runningexpire_execute_expirationcalls.Please review the attached patch, thanks.
Comment #9
sgdev commentedAfter having done more research and run patch #8 the last couple of weeks, I've decided to remove the patch I posted. It does not solve the issue, and the suggestions in #7 are a better basis for a possible patch.
Regarding the link in the original post about a race condition, I think this is probably the core of our problem. I'd recommend reviewing these posts:
* Race condition in node_save() when not using DB for cache_field: https://www.drupal.org/node/1679344
* Does not handle Race condition: https://www.drupal.org/project/memcache_storage/issues/2470063
Might want to try implementing the Cache Consistent module (https://www.drupal.org/project/cache_consistent/) and see if it helps anyone's issues. The Drupal.org site had to implement it to resolve this problem.
Comment #10
sgdev commentedUpdating issue name to be more in line with the original title.
Comment #11
Anonymous (not verified) commentedMade a patch that makes use of hook post action module. It doesn't solve all use-cases (menu links are not entities f.ex.) but for nodes, users and other entities it works.