summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/identity.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2009-05-17 18:17:46 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2009-05-17 18:17:46 +0000
commit2be867ffac8881a4a20ca5387063ed207ac876dc (patch)
tree30b4a8d0663febea019679442c1f98360eb5ce26 /lib/sqlalchemy/orm/identity.py
parent6515e84d4c7084d9276922786291f6e047b70b84 (diff)
downloadsqlalchemy-2be867ffac8881a4a20ca5387063ed207ac876dc.tar.gz
- Significant performance enhancements regarding Sessions/flush()
in conjunction with large mapper graphs, large numbers of objects: - The Session's "weak referencing" behavior is now *full* - no strong references whatsoever are made to a mapped object or related items/collections in its __dict__. Backrefs and other cycles in objects no longer affect the Session's ability to lose all references to unmodified objects. Objects with pending changes still are maintained strongly until flush. [ticket:1398] The implementation also improves performance by moving the "resurrection" process of garbage collected items to only be relevant for mappings that map "mutable" attributes (i.e. PickleType, composite attrs). This removes overhead from the gc process and simplifies internal behavior. If a "mutable" attribute change is the sole change on an object which is then dereferenced, the mapper will not have access to other attribute state when the UPDATE is issued. This may present itself differently to some MapperExtensions. The change also affects the internal attribute API, but not the AttributeExtension interface nor any of the publically documented attribute functions. - The unit of work no longer genererates a graph of "dependency" processors for the full graph of mappers during flush(), instead creating such processors only for those mappers which represent objects with pending changes. This saves a tremendous number of method calls in the context of a large interconnected graph of mappers. - Cached a wasteful "table sort" operation that previously occured multiple times per flush, also removing significant method call count from flush(). - Other redundant behaviors have been simplified in mapper._save_obj().
Diffstat (limited to 'lib/sqlalchemy/orm/identity.py')
-rw-r--r--lib/sqlalchemy/orm/identity.py89
1 files changed, 28 insertions, 61 deletions
diff --git a/lib/sqlalchemy/orm/identity.py b/lib/sqlalchemy/orm/identity.py
index 0753ea991..aa041a585 100644
--- a/lib/sqlalchemy/orm/identity.py
+++ b/lib/sqlalchemy/orm/identity.py
@@ -15,6 +15,9 @@ class IdentityMap(dict):
self._mutable_attrs = {}
self.modified = False
self._wr = weakref.ref(self)
+
+ def replace(self, state):
+ raise NotImplementedError()
def add(self, state):
raise NotImplementedError()
@@ -102,6 +105,17 @@ class WeakInstanceDict(IdentityMap):
def contains_state(self, state):
return dict.get(self, state.key) is state
+ def replace(self, state):
+ if dict.__contains__(self, state.key):
+ existing = dict.__getitem__(self, state.key)
+ if existing is not state:
+ self._manage_removed_state(existing)
+ else:
+ return
+
+ dict.__setitem__(self, state.key, state)
+ self._manage_incoming_state(state)
+
def add(self, state):
if state.key in self:
if dict.__getitem__(self, state.key) is not state:
@@ -161,12 +175,24 @@ class StrongInstanceDict(IdentityMap):
def contains_state(self, state):
return state.key in self and attributes.instance_state(self[state.key]) is state
+ def replace(self, state):
+ if dict.__contains__(self, state.key):
+ existing = dict.__getitem__(self, state.key)
+ existing = attributes.instance_state(existing)
+ if existing is not state:
+ self._manage_removed_state(existing)
+ else:
+ return
+
+ dict.__setitem__(self, state.key, state.obj())
+ self._manage_incoming_state(state)
+
def add(self, state):
dict.__setitem__(self, state.key, state.obj())
self._manage_incoming_state(state)
def remove(self, state):
- if dict.pop(self, state.key) is not state:
+ if attributes.instance_state(dict.pop(self, state.key)) is not state:
raise AssertionError("State %s is not present in this identity map" % state)
self._manage_removed_state(state)
@@ -176,7 +202,7 @@ class StrongInstanceDict(IdentityMap):
self._manage_removed_state(state)
def remove_key(self, key):
- state = dict.__getitem__(self, key)
+ state = attributes.instance_state(dict.__getitem__(self, key))
self.remove(state)
def prune(self):
@@ -190,62 +216,3 @@ class StrongInstanceDict(IdentityMap):
self.modified = bool(dirty)
return ref_count - len(self)
-class IdentityManagedState(attributes.InstanceState):
- def _instance_dict(self):
- return None
-
- def modified_event(self, attr, should_copy, previous, passive=False):
- attributes.InstanceState.modified_event(self, attr, should_copy, previous, passive)
-
- instance_dict = self._instance_dict()
- if instance_dict:
- instance_dict.modified = True
-
- def _is_really_none(self):
- """do a check modified/resurrect.
-
- This would be called in the extremely rare
- race condition that the weakref returned None but
- the cleanup handler had not yet established the
- __resurrect callable as its replacement.
-
- """
- if self.check_modified():
- self.obj = self.__resurrect
- return self.obj()
- else:
- return None
-
- def _cleanup(self, ref):
- """weakref callback.
-
- This method may be called by an asynchronous
- gc.
-
- If the state shows pending changes, the weakref
- is replaced by the __resurrect callable which will
- re-establish an object reference on next access,
- else removes this InstanceState from the owning
- identity map, if any.
-
- """
- if self.check_modified():
- self.obj = self.__resurrect
- else:
- instance_dict = self._instance_dict()
- if instance_dict:
- instance_dict.remove(self)
- self.dispose()
-
- def __resurrect(self):
- """A substitute for the obj() weakref function which resurrects."""
-
- # store strong ref'ed version of the object; will revert
- # to weakref when changes are persisted
- obj = self.manager.new_instance(state=self)
- self.obj = weakref.ref(obj, self._cleanup)
- self._strong_obj = obj
- obj.__dict__.update(self.dict)
- self.dict = obj.__dict__
- self._run_on_load(obj)
- return obj