Problem
NamedFile and NamedBlobFile objects currently lack an __eq__ implementation, falling back to Python's default identity comparison. In the context of plone.restapi (or any code that replaces field values), this causes a major performance and storage regression:
When an image is PATCHed with the exact same bytes, the deserializer creates a new object instance. Because new_instance != old_instance, Plone perceives a change, triggers a ZODB write, fires an ObjectModifiedEvent, re-indexes the catalog, and invalidates all existing image scales.
Impact
In high-frequency update scenarios (like automated content imports), this leads to:
- Massive Data.fs growth due to redundant transaction history.
- Blob storage bloat from unnecessary scale regeneration.
- Increased CPU load from redundant indexing and image processing.
Proposed Solution
- Implement
__eq__ in NamedFile and NamedBlobFile comparing contentType, filename, and a content checksum.
- Introduce a private
_hash attribute (SHA-256) calculated during _setData using chunked reading to save memory.
- Update the internal _modified timestamp ONLY if the content hash actually changes.
- ZODB Safety: For legacy objects lacking a
_hash, the comparison should compute it in memory but MUST NOT save it back to the object during a read operation (avoiding "write-on-read").
Problem
NamedFile and NamedBlobFile objects currently lack an
__eq__implementation, falling back to Python's default identity comparison. In the context of plone.restapi (or any code that replaces field values), this causes a major performance and storage regression:When an image is PATCHed with the exact same bytes, the deserializer creates a new object instance. Because
new_instance != old_instance, Plone perceives a change, triggers a ZODB write, fires an ObjectModifiedEvent, re-indexes the catalog, and invalidates all existing image scales.Impact
In high-frequency update scenarios (like automated content imports), this leads to:
Proposed Solution
__eq__inNamedFileandNamedBlobFilecomparingcontentType,filename, and a content checksum._hashattribute (SHA-256) calculated during_setDatausing chunked reading to save memory._hash, the comparison should compute it in memory but MUST NOT save it back to the object during a read operation (avoiding "write-on-read").