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
