when I was debugging a new feature in my app I was surprised about
database hits that look unneccessary to me.
Here it goes:
my $r = ParentClass->new(id => 999)->load();
foreach my $child ($r->children) {
dosomething_with($child->grandparent->id);
}
in the Children-class:
sub grandparent {shift->parent->grandparent}
My expectation was to have these DB hits:
- initial get $r
- get $r->children
- get $r->grandparent
Instead I get these additional hits:
for every $child->grandparent:
- get $child->parent (the same as $r)
- get parent->grandparent (again and again)
Perhaps it can be boiled down to the question why a call to
$parent->child->parent has to get parent from the database again.
Of course there are ways to do my own caching but perhaps I am just
missing the obvious and it is all there already.
-Michael
RDBO doesn't keep any sort of global object cache. Relationship
methods cache the objects they fetch within the object that the
relationship method was called on. When you call parent() on the
child object, that method had not previously been called on that
object. The child object therefore queries the database to get the
parent object. The child object has no idea that some other code had
previously loaded the same object it's about to fetch.
-John
Since this can be quite expensive, is there a recommended way to deal
with it?
Perhaps, instead of:
sub grandparent {shift->parent->grandparent}
some way of accessor-like caching:
sub grandparent {
my $self = shift;
if (@_) {
$self->{grandparent} = $_[0]; # ???
}
elsif (not $self->{grandparent}) {
$self->{grandparent} = $self->parent->grandparent;
}
return $self->{grandparent};
}
This way I could pass the earlier fetched object whenever necessary
performance-wise.
But how about messing with the object-hash? Looks ugly but isn't that
unusual in the class file itself (I wouldn't do it from elswhere like
$child->{grandparent} = $cached).
Or are there better ways?
-Michael
> Well, at least if you're in a mod_perl environment, which I would
> assume if you're using RDBO.
Yep.
> Perl has an issue where if you have a circular reference amongst
> objects, the garbage collection process doesn't know how to clean them
> up (an object has to have NO active references on it for Perl to
> garbage collect it. When you have an object reference circle, *all* of
> the references remain active ones). This can lead to dramatic memory
> leaks that will be very unhealthy for your webserver.
Isn't it possible to weaken the reference? There must be some code for
this in RDBO already since it is almost natural to build circles with
all the relationships.
But you might be right, a first glance shows that it is very easy to get
it wrong and then the memory leaks become a real problem.
-Michael