From: Mike Bayer Date: Sun, 18 Apr 2010 18:11:44 +0000 (-0400) Subject: added detailed documentation regarding ORM performance in conjunction with mutabletype. X-Git-Tag: rel_0_6_0~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=72915f1e8c5d07ca82e38dec9fb072707b5ffc2a;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git added detailed documentation regarding ORM performance in conjunction with mutabletype. --- diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 72251c8d57..7de5cafad1 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -142,6 +142,15 @@ class UUID(sqltypes.TypeEngine): PGUuid = UUID class ARRAY(sqltypes.MutableType, sqltypes.Concatenable, sqltypes.TypeEngine): + """Postgresql ARRAY type. + + Represents values as Python lists. + + **Note:** be sure to read the notes for + :class:`~sqlalchemy.types.MutableType` regarding ORM + performance implications. + + """ __visit_name__ = 'ARRAY' def __init__(self, item_type, mutable=True): diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 198835562f..1fdf643c75 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -459,10 +459,71 @@ class TypeDecorator(AbstractType): class MutableType(object): - """A mixin that marks a Type as holding a mutable object. - - :meth:`copy_value` and :meth:`compare_values` should be customized - as needed to match the needs of the object. + """A mixin that marks a :class:`TypeEngine` as representing + a mutable Python object type. + + "mutable" means that changes can occur in place to a value + of this type. Examples includes Python lists, dictionaries, + and sets, as well as user-defined objects. The primary + need for identification of "mutable" types is by the ORM, + which applies special rules to such values in order to guarantee + that changes are detected. These rules may have a significant + performance impact, described below. + + A :class:`MutableType` usually allows a flag called + ``mutable=True`` to enable/disable the "mutability" flag, + represented on this class by :meth:`is_mutable`. Examples + include :class:`PickleType` and + :class:`~sqlalchemy.dialects.postgresql.base.ARRAY`. Setting + this flag to ``False`` effectively disables any mutability- + specific behavior by the ORM. + + :meth:`copy_value` and :meth:`compare_values` represent a copy + and compare function for values of this type - implementing + subclasses should override these appropriately. + + The usage of mutable types has significant performance + implications when using the ORM. In order to detect changes, the + ORM must create a copy of the value when it is first + accessed, so that changes to the current value can be compared + against the "clean" database-loaded value. Additionally, when the + ORM checks to see if any data requires flushing, it must scan + through all instances in the session which are known to have + "mutable" attributes and compare the current value of each + one to its "clean" + value. So for example, if the Session contains 6000 objects (a + fairly large amount) and autoflush is enabled, every individual + execution of :class:`Query` will require a full scan of that subset of + the 6000 objects that have mutable attributes, possibly resulting + in tens of thousands of additional method calls for every query. + + Note that for small numbers (< 100 in the Session at a time) + of objects with "mutable" values, the performance degradation is + negligible. In most cases it's likely that the convenience allowed + by "mutable" change detection outweighs the performance penalty. + + It is perfectly fine to represent "mutable" data types with the + "mutable" flag set to False, which eliminates any performance + issues. It means that the ORM will only reliably detect changes + for values of this type if a newly modified value is of a different + identity (i.e., ``id(value)``) than what was present before - + i.e., instead of operations like these:: + + myobject.somedict['foo'] = 'bar' + myobject.someset.add('bar') + myobject.somelist.append('bar') + + You'd instead say:: + + myobject.somevalue = {'foo':'bar'} + myobject.someset = myobject.someset.union(['bar']) + myobject.somelist = myobject.somelist + ['bar'] + + A future release of SQLAlchemy will include instrumented + collection support for mutable types, such that at least usage of + plain Python datastructures will be able to emit events for + in-place changes, removing the need for pessimistic scanning for + changes. """ @@ -1365,13 +1426,16 @@ class Enum(String, SchemaType): return super(Enum, self).adapt(impltype) class PickleType(MutableType, TypeDecorator): - """Holds Python objects. + """Holds Python objects, which are serialized using pickle. PickleType builds upon the Binary type to apply Python's ``pickle.dumps()`` to incoming objects, and ``pickle.loads()`` on the way out, allowing any pickleable Python object to be stored as a serialized binary field. + **Note:** be sure to read the notes for :class:`MutableType` regarding + ORM performance implications. + """ impl = LargeBinary