Memcache-based Buffer-Object for Global Link-Target-Caching#475
Memcache-based Buffer-Object for Global Link-Target-Caching#475drfho wants to merge 13 commits intofb_refactor_repofrom
Conversation
remoteFiles → get_modelfileset_from_disk localFiles → get_modelfileset_from_zodb read_remote_file → get_file_from_disk readRepository → get_models_from_disk parse_artefact → parse_modelfile read_artefact → read_file_from_disk init_artefacts → create_modelfileset
|
A test-page with about 70 LinkElement objects in a local test with about 1000 Contentnodes gets about 20% faster (0.90s to .65s)
@zmsdev: I added log-writing for the ReqBuff-cache accesses, too. ZMS/Products/zms/_cachemanager.py Lines 122 to 124 in 201fa39 Here more than 1000x fetching ZMSMetaobjManager and MultiLanguageManager. |
Performance: Memcache may save 1ms per LinkTo avoid link-target redundancy the the backlink-object-pathes are cached for the entire portal. Change: 864a2e5 Discussion @zmsdev: At this dev-stage the shared-cache contains only the object-path of the link-target but not the object itself. For this the traversing is still needed, especially because the resulting values (e.g. url, visibility etc.) are determined by the object's context. To cache a jsonified version of the object may not be complete!? Left: Cached linktarget-list ist created by user1 by requesting link-rendering. Right: user2 needs not to create the list again but gets it from memcache. Left: memcache-derived objects-paths. Right: zmsindex-derived object-paths. Result: About 70 links create about 800 getLinkObj-calls and with 200 index-calls consuming in sum about 50ms; at the moment only these 50ms may be saved via memcache if the target-object is still cached by request-buffer. The time consumed by the Snippet: Output performance data in a ZMSTextarea/Code-Block <pre style="white-space:pre-wrap !important">
counter_zmsindex_requests = <dtml-var "REQUEST.get('counter_zmsindex_requests')">
time_consumed_by_zmsindex_requests_in_ms = <dtml-var "REQUEST.get('time_consumed_by_zmsindex_requests_in_ms')">
counter_getlinkobj = <dtml-var "REQUEST.get('counter_getlinkobj')">
time_consumed_by_getlinkobj_in_ms = <dtml-var "REQUEST.get('time_consumed_by_getlinkobj_in_ms')">
time_consumed_by_getlinkobj_datalist = <dtml-var "REQUEST.get('time_consumed_by_getlinkobj_datalist')">
</pre>BTW: All-time favorite
|
090a863 to
5770bd3
Compare







To implement a shared in-memory buffer for link data in ZMS that persists across requests and users, switching to a Memcached or RAMCache approach may be a significant performance opportunity.
Why Memcache/RAMCache Improves Performance Over ReqBuff and ZMSIndex
Cross-Request Persistence: Unlike ReqBuff (which is cleared after every HTTP request), a shared buffer allows expensive visibility and metadata lookups (like isVisible, isActive, getPhysicalPath) to be reused across different users and sessions.
Avoids ZODB/Catalog Overhead: While ZMSIndex is fast, it still involves querying a persistent ZCatalog. An in-memory cache (especially Memcached) is several orders of magnitude faster for frequent "key -> blob" lookups.
Handling "Invisible" Content: the ZMSIndex doesn't inherently know about isVisible. By caching the result of these checks in a shared buffer, you avoid re-evaluating complex logic (like translations, commit status, and trashcan checks) on every render.
Implementation Suggestion
We can extend the ReqBuff mixin logic in _cachemanager.py to include a "Global" or "Distributed" mode.
Below is how to modify getLinkObj in _zreferableitem.py:543 to utilize a shared cache manager if configured:
Challenges to Consider
Cache Invalidation: biggest hurdle. When an object is moved, renamed, or its visibility changes (e.g., it is deactivated), the shared cache must be invalidated. We would need to hook into Manage_afterAdd, Manage_afterClone, and onChangeObjEvt.
Object Pickling: We cannot easily store live ZODB objects in Memcached, but store "Value Objects" (dictionaries containing the path, UID, and visibility status) and resolve the object via unrestrictedTraverse when needed.
Visibility Context: isVisible can depend on the current user's roles or the selected language. Your cache key might need to be f"{url}:{lang}:{user_roles}".
Configuration
To activate the shared buffer, you need to set a ZMS configuration property:
ZMS.cache.path