activegraph icon indicating copy to clipboard operation
activegraph copied to clipboard

Lazy deserialization

Open jgaskins opened this issue 6 years ago • 1 comments

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

jgaskins avatar Mar 26 '18 04:03 jgaskins

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! ;) 🎉

cheerfulstoic avatar Mar 27 '18 23:03 cheerfulstoic