activegraph
activegraph copied to clipboard
Lazy deserialization
In #1490 I found a few micro-optimizations to apply for ActiveNode
's deserialization of nodes. It got me thinking about how this could be improved further.
The primary way I could think of to improve here would be to lazily convert node properties. For example, given the following model:
class Person
include Neo4j::ActiveNode
property :created_at, type: Time
end
When we fetch a Person
, the properties are currently deserialized immediately. This is trivial most of the time, especially if you're using simple, serializable attribute values and the default conversions for Time
values, for example. However, it means we still copy over entire hashes for each model.
What I'm thinking about is, instead of eagerly converting when reading the objects from the DB, we leave @attributes
empty and instead define the attribute methods to proxy the persisted Node
. If we only use 1-2 properties from each node, we could save almost 50% CPU time on deserialization (not counting time spent at protocol level).
A naive example might look something like this:
class Person
def created_at
@attributes.fetch('created_at') do |attr|
value = @_persisted_obj&.properties[:created_at]
@attributes[attr] = unless value.nil?
Neo4j::Shared::TypeConverters
.converter_for(self.class.attributes[:created_at].type)
.to_ruby(value)
end
end
end
end
This way, @attributes
becomes a caching proxy for the persisted Node
. We still read from @attributes
, but fall back to the Node
and promote the value from the Node
to @attributes
only when we need it.
I think this will require significant changes because it appears that some internals depend on attributes being eagerly populated — when I tried to implement this myself, at least, I got NoMethodError
for calling things on nil
, so I'm assuming something was trying to read from @attributes
directly. I'll probably try to play with this a bit more when I have time to understand it more deeply.
Runtime information:
Neo4j database version: 3.3.4 (via Homebrew)
neo4j
gem version: 9.1.5
neo4j-core
gem version: 8.1.1
Yeah, this definitely seems like a fantastic idea! But yeah, the attributes code can be a bit hairy. But if you can get it working, by all means! ;) 🎉