Stevan,

first thank you for clearing up some misunderstanding about KiokuDB.

Frost lacks documentation too, but the basic features are already
explained on the main doc page.

So let me sum up the dragons:

*No Memory Leaks by Design*

The mechanism of creating, storing and loading of objects is explained
on
http://dienstleistung-kultur.de/frost/Frost.html#The_underlying_magic_disclosed

Every time you access an object via My::Class->new, you'll get _another_
instance. This is not a complete object, only a proxy, which holds just
the attributes "id" and "asylum", all other attributes are stored in
Frost::Twilight - a cache, basically a simple hash of hashes.

The value of a not-present attribute is fetched from this cache and if
it's not found, from Frost::Cemetery, which is a wrapper around a
BerkleyDB table.

If the attribute holds an object, the object is instantiated - as proxy
object as well. Therefore all objects to be saved must inherit from a
base object, which inherits from Frost::Locum - the proxy object.

The methods in Frost::Meta::Instance named inline_* are overwritten to
manage the values as described.

You'll never get the same object's address but always the same content.

And if this proxy object goes out of scope, it's garbage collected.

So memory leaks shouldn't appear.

Here is a _simplified_ excerpt from various Frost modules illustrating,
that the attributes are not tied to the database directly. Only an
internal hash %Frost::Burial::dbm_hash is tied to DB_File, which is only
accessed via methods delivering Storable images.

At Asylum::open this is called:

sub Frost::Burial::open
{
tie %dbm_hash, "DB_File", $filename, $flags, $mode, $info;

$self->_dbm_hash ( \%dbm_hash );
}

Now the example:

$foo  isa 'Foo'
$foo  has 'bar'
$foo->id is 42

For accessing an attribute like $val = $foo->bar;
the call tree is (in pseudo code ):

# via Frost::Meta::Instance::inline_get_slot_value we come here:
foo->_evoke ( 'bar' )
   foo->asylum->_evoke ( 'Foo', 42, 'bar' )
      asylum->necromancer->evoke ( 'Foo', 42, 'bar' )   
         necromancer->mortician('Foo')->grub ( 42, 'bar' )
            mortician->cemetery('bar')->exhume ( 42 )
               burial->exhume ( 42 )

The touched methods are (bottom-up):

sub Frost::Burial::exhume ( $key )      # 42
{
my $essence = $self->_dbm_hash->{$key};

return $essence;        # is a Scalar or a Storable image
}

sub Frost::Cemetery::exhume ( $key )    # 42
# inherited from Frost::Burial...

sub Frost::Mortician::grub ( $id, $slot )       # 42, 'bar'
{
my $cemetery = $self->_cemetery->{$slot};

my $essence = $cemetery->exhume ( $id );

my $spirit = Storable::read_magic ( $essence )
                ? Storable::thaw ( $essence )
                : $essence;

return $spirit;         # is a Scalar, a Hash or an Array,
                        # thus unbound from the tied %dbm_hash !
                        # It is _not_ an object, but a hash like:
                        # { type => 'OtherClassname', ref => 123 }
}

sub Frost::Necromancer::evoke ( $class, $id, $slot ) # 'Foo', 42, 'bar'
{
my $spirit = $self->_mortician($class)->grub ( $id, $slot );

return $spirit;
}

sub Frost::Asylum::_evoke ( $class, $id, $slot_name ) # 'Foo', 42, 'bar'
{
# Frost::Twilight (cache) handling not shown...

# assumed, the spirit of 'bar' is not in cache:
#
my $spirit = $self->_necromancer()->evoke ( $class, $id, $slot_name );

# $spirit is a Scalar, a Hash or an Array, it is _not_ an object,
# but a hash like: { type => 'OtherClassname', ref => 123 }

# Frost::Asylum::_evoke_slot does the hard recursive work of creating
# the real attributes's value, which again can be a scalar, array, hash
# or a _new_ object (isa Frost::Locum !), if a classname is found in the
# spirit. Please refer to the source code.
#
my $value  = $self->_evoke_slot ( $spirit );

return $value;          # is a Scalar, a Hash, an Array or an object
}

sub Frost::Locum::_evoke ( $slot_name ) # 'bar' in our example
{
my $class = ref $self;                  # 'Foo' in our example
my $id    = $self->{id};                # 42 in our example

my $value = $self->{asylum}->_evoke ( $class, $id, $slot_name );

return $value;
}

sub Frost::Meta::Instance::inline_get_slot_value ( $invar, $slot )
{
"$invar\->_evoke ( \"$slot\" )";
}

*Mutability*

Writing the meta classes I found, that I would have to implement two
completely different ways for mutable and immutable object construction.

And according to Russ Meyer's dictum "Faster, Pussycat, Kill, Kill"
I decided to allow only immutable classes.

*Roles*

(Run-time) Roles can't be used with immutable classes, so the recipe
http://search.cpan.org/~drolsky/Moose-1.14/lib/Moose/Cookbook/Roles/Recipe3.pod
(Frost: t/400_recipes/303_lawn.t) doesn't work with Frost.

Applying a role at run-time creates an __ANON__ class with new
attributes, which can not be stored (see below inheritance).

But I admit, that I started working by diving into the source code of
Moose 0.++ - maybe the mature version 1.++ changes a lot.

So I will have a look at that again.

*Attribute Inheritance*

There is no problem with attribute inheritance, but again, at Moose 0.++
it was faster to manage my own attribute lists in Frost::Meta::Class.

Moreover 'around Frost::Meta::Class::add_attribute' was necessary to
check the extra sugar 'index', virtual' etc.

It still works, but might be easier now with Moose 1.++

This must be revisited too.

See also t/400_recipes/407_relation.t

*Weak Refs*

Matt Sergeant is cited on
http://dienstleistung-kultur.de/frost/Frost.html#Persistent_storage_even_for_circular_structures_like_trees_or_loops_is_feasible

I have nothing to add. Weak refs are evil ;-)


Frost is still 30 % far from version 1.0 and Moose itself was and is
changing fast - so please be patient.

The motivation to write Frost is my work for the GRIPS Theater, Berlin
http://grips-theater.de which uses a Repertoire Management System (RMS),
where most of the data for the web pages (event table, casting, actor
pages, shop etc.) come from.

The page management for the website uses Moose, XML::LibXML and
XML::LibXSLT and runs on Apache.mod_perl and connects to the RMS
database as well.

RMS is written in Perl too and works like a charm, but is mostly
functional programming using DBD::mysql - just as one did it in ye goode
olde days ;-)

To get rid of a bunch of m:n helper tables in SQL it is better to use
objects with referential attributes.

The projected migration from SQL to OOP influences the design of Frost
and is - beside other issues - the reason for inventing the sugar
'index' for fast access to pre-sorted event dates i.e..

So while working on RMS 2.0 I started to develop this persistence stuff
called Frost.

And to give something back to the community, I published Frost - very
early I may admit.

Let's see, where it will lead us.

Especially I will adopt more of Moose's recipes for Frost to see, if I
get them working.

In the meantime, any tests showing memory leaks, bugs, missing use cases
etc. are welcome.

Ernesto 8-)

-- 
Ernst-J. "Ernesto" Johnsdorf
mailto:[email protected]
http://search.cpan.org/~ernesto/
http://dienstleistung-kultur.de


Reply via email to