Difference between revisions of "Persistence predicates"
Colin-adams (Talk | contribs) |
Colin-adams (Talk | contribs) (Restore keyword added. persistence keyword removed.) |
||
Line 10: | Line 10: | ||
Another is that too much data may be stored, resulting in large files (or database occupancy, or whatever) and slow reload times. Some data, such as internal caches, are probably best not serialized at all. For other data, it may be better just to store a shorthand identifier to enable the data to be rebuilt (maybe on demand) at retrieval time. | Another is that too much data may be stored, resulting in large files (or database occupancy, or whatever) and slow reload times. Some data, such as internal caches, are probably best not serialized at all. For other data, it may be better just to store a shorthand identifier to enable the data to be rebuilt (maybe on demand) at retrieval time. | ||
− | This article explores ways to address the second problem. | + | This article explores ways to address the second problem. The first problem is best tackled by only using <e>STORABLE</e>s as a medium-term caching mechanism. |
==Transient data== | ==Transient data== | ||
Line 29: | Line 29: | ||
If a persistence predicate returns <e>False</e>, it may be that the programmer does not need this data at all (such as the case of an internal cache). But in some cases the programmer will want the data to be recreated at restoration time (this may be necessary to restore the class invariant), rather than retrieved from the persistence mechanism. | If a persistence predicate returns <e>False</e>, it may be that the programmer does not need this data at all (such as the case of an internal cache). But in some cases the programmer will want the data to be recreated at restoration time (this may be necessary to restore the class invariant), rather than retrieved from the persistence mechanism. | ||
− | Take an example of a class which has an attribute named <e>`gadget'</e> of type <e>GADGET_FROM_DATABASE</e> having an attribute named <e>`id'</e> of type <e>INTEGER</e>. The class invariant requires that<e>`gadget'</e> is not <e>Void</e>, but the size required to store <e>`gadget'</e> is very large. The class also has a secret routine <e>` | + | Take an example of a class which has an attribute named <e>`gadget'</e> of type <e>GADGET_FROM_DATABASE</e> having an attribute named <e>`id'</e> of type <e>INTEGER</e>. The class invariant requires that<e>`gadget'</e> is not <e>Void</e>, but the size required to store <e>`gadget'</e> is very large. The class also has a secret routine <e>`create_gadget_from_identifier (a_id: INTEGER)</e> which can be used to create a <e>GADGET_FROM_DATABASE</e> given its <e>`id'</e> value. In such a case, rather than store <e>`gadget'</e> it is probably better to just store the <e>`id'</e> value, and then call <e>`create_gadget_from_identifier'</e> at retrieval time. |
− | A restoration routine is a | + | A restoration routine is similar to a creation procedure, in as much as it cannot reply on the class invariant holding. It can rely on attributes for which the persistence predicate evaluates to <e>True </e> as having been restored, though. |
==Syntax== | ==Syntax== | ||
− | + | One new keyword <e>restore</e> will be required. This can occur in several places: | |
− | + | <ul> | |
+ | <li>After the <e>create</e> clause in the class prolog. What follows is a list of restoration routines, just like the list of creation procedures. Typically this will occur in the form: | ||
+ | <e> | ||
+ | restore {STORABLE} | ||
+ | recreate_attributes | ||
+ | </e> | ||
+ | </li> | ||
+ | <li>After the <e>attribute</e> keyword and optional body. What follows is: | ||
+ | |||
+ | <p>1. The tag <e>persistence_predicate:</e> followed by one of:</p> | ||
+ | |||
+ | <p>a) <e>True</e> meaning always persist.</p> | ||
+ | <p>b) <e>False</e> meaning never persist.</p> | ||
+ | <p>c) The keyword <e>agent</e> followed by a declaration of a <e>PREDICATE</e>.</p> | ||
+ | |||
+ | <p>2. An optional body (introduced by the tag <e>body:</e>) like the attribute body, and this is code that is executed only if the attribute was not persisted. These routines run after all persisted attributes have been restored, but before the restoration procedure is run. It is only used for initialization of attributes that do not depend | ||
+ | upon other restoration attribute bodies having been run. | ||
+ | </p> | ||
+ | </li> | ||
+ | <li> | ||
+ | Within a restoration routine as an instruction. Here it is used analagously to a <e>create</e> instruction, to run one of the named restoration routines specified in the class of one of the attributes that was not persisted. | ||
+ | </li> | ||
+ | </ul> | ||
===Example=== | ===Example=== | ||
Line 46: | Line 68: | ||
attribute | attribute | ||
... | ... | ||
− | + | restore | |
persistance_predicate: agent is_gadget_persisted (attribute.id) -- `True' and `False' would also be acceptable values, | persistance_predicate: agent is_gadget_persisted (attribute.id) -- `True' and `False' would also be acceptable values, | ||
-- with `True' being the default | -- with `True' being the default | ||
− | + | body: create_gadget_from_identifier (attribute.id) | |
</e> | </e> | ||
− | Here the ECMA <e>attribute</e> keyword is reused to denote the instance of `gadget'. | + | Here the ECMA <e>attribute</e> keyword is reused to denote the instance of `gadget'. This is only available within the attribute restore clause, not in a restoration routine. |
==Redefinition== | ==Redefinition== | ||
Line 64: | Line 86: | ||
==Class correctness conditions== | ==Class correctness conditions== | ||
− | The possibility of the persistance_predicate evaluating to (or statically specified as) <e>False</e> on an attribute that is required to be non-Void (or non-default for an expanded attribute) implies that | + | The possibility of the persistance_predicate evaluating to (or statically specified as) <e>False</e> on an attribute that is required to be non-Void (or non-default for an expanded attribute) implies that either inline restoration code must be supplied, or the restoration routine has an appropriate postcondition clause. |
Revision as of 22:08, 27 September 2007
Research: This page describes research about Eiffel, not the actual language specification.
Contents
Introduction
Using STORABLE
s in an application has at least two significant disadvantages.
One is that using them for long-term persistence is vulnerable to changes in the class.
Another is that too much data may be stored, resulting in large files (or database occupancy, or whatever) and slow reload times. Some data, such as internal caches, are probably best not serialized at all. For other data, it may be better just to store a shorthand identifier to enable the data to be rebuilt (maybe on demand) at retrieval time.
This article explores ways to address the second problem. The first problem is best tackled by only using STORABLE
s as a medium-term caching mechanism.
Transient data
Java tackles this problem with the keyword transient
. An attribute so marked is not stored at serialization time. When the object is retrieved the attribute will be void.
This seems rather limited to me. It can't be used if the class invariant constrains the attribute to be non-void. And I don't think there is a way for a descendant class to override this behaviour (it's a long time since I've written any Java, so I could be very wrong here).
My thinking is that there should be a way to specify whether or not a particular attribute should be stored on any given occaision, and a way to specify what to do at retrieval time.
Persistence predicates
A persistence predicate is a routine of type PREDICATE [ANY, TUPLE [!<attribute_type>]]
associated with an attribute of type <attribute_type>.
At storage time, the predicate will be called with the instance of the attribute as its argument (if the attribute is non-Void). If it returns True
then the attribute is stored. If it returns False
then the attribute is not stored, but it may be re-created at retrieval time.
Restoration routines
If a persistence predicate returns False
, it may be that the programmer does not need this data at all (such as the case of an internal cache). But in some cases the programmer will want the data to be recreated at restoration time (this may be necessary to restore the class invariant), rather than retrieved from the persistence mechanism.
Take an example of a class which has an attribute named `gadget'
of type GADGET_FROM_DATABASE
having an attribute named `id'
of type INTEGER
. The class invariant requires that`gadget'
is not Void
, but the size required to store `gadget'
is very large. The class also has a secret routine `create_gadget_from_identifier (a_id: INTEGER)
which can be used to create a GADGET_FROM_DATABASE
given its `id'
value. In such a case, rather than store `gadget'
it is probably better to just store the `id'
value, and then call `create_gadget_from_identifier'
at retrieval time.
A restoration routine is similar to a creation procedure, in as much as it cannot reply on the class invariant holding. It can rely on attributes for which the persistence predicate evaluates to True
as having been restored, though.
Syntax
One new keyword restore
will be required. This can occur in several places:
- After the
create
clause in the class prolog. What follows is a list of restoration routines, just like the list of creation procedures. Typically this will occur in the form:restore {STORABLE} recreate_attributes
- After the
attribute
keyword and optional body. What follows is:1. The tag
persistence_predicate:
followed by one of:a)
True
meaning always persist.b)
False
meaning never persist.c) The keyword
agent
followed by a declaration of aPREDICATE
.2. An optional body (introduced by the tag
body:
) like the attribute body, and this is code that is executed only if the attribute was not persisted. These routines run after all persisted attributes have been restored, but before the restoration procedure is run. It is only used for initialization of attributes that do not depend upon other restoration attribute bodies having been run. -
Within a restoration routine as an instruction. Here it is used analagously to a
create
instruction, to run one of the named restoration routines specified in the class of one of the attributes that was not persisted.
Example
gadget: GADGET_FROM_DATABASE -- Gadget retrieved from DB via its `id' attribute attribute ... restore persistance_predicate: agent is_gadget_persisted (attribute.id) -- `True' and `False' would also be acceptable values, -- with `True' being the default body: create_gadget_from_identifier (attribute.id)
Here the ECMA attribute
keyword is reused to denote the instance of `gadget'. This is only available within the attribute restore clause, not in a restoration routine.
Redefinition
The open/closed principle is honoured (I think). The author of the class can determine that an attribute must always be stored (this is the default, and is the current situation), or that it is never stored (for a pure cache attribute), or provide flexibility by specifying an agent.
The agent's routine could be deferred, frozen (yuk!) or just a normal routine, in which case descendant classes can redefine it.
The actual instance data that is passed to the persistance_predicate and the restoration_routine could be specified via an agent, which allows for further flexibility. For restoration, it might even involve an EV_DIALOG
(heaven forfend)!
Class correctness conditions
The possibility of the persistance_predicate evaluating to (or statically specified as) False
on an attribute that is required to be non-Void (or non-default for an expanded attribute) implies that either inline restoration code must be supplied, or the restoration routine has an appropriate postcondition clause.