Skip to content

Commit

Permalink
Clone inner objects when cloning DirectButler
Browse files Browse the repository at this point in the history
When cloning a DirectButler instance, clone all of its inner objects instead of just copying references to the top-level properties.  This makes it safe to use the cloned objects concurrently from different threads.  This is necessary for the Butler server and other services using DirectButler as a backend, since they have a separate thread for each incoming request.
  • Loading branch information
dhirving committed Jan 9, 2024
1 parent d0bfb3f commit 4879269
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 11 deletions.
5 changes: 3 additions & 2 deletions python/lsst/daf/butler/direct_butler.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,12 @@ def _clone(
) -> DirectButler:
# Docstring inherited
defaults = RegistryDefaults(collections=collections, run=run, infer=inferDefaults, **kwargs)
registry = self._registry.copy(defaults)

return DirectButler(
registry=self._registry.copy(defaults),
registry=registry,
config=self._config,
datastore=self._datastore,
datastore=self._datastore.clone(registry.getDatastoreBridgeManager()),
storageClasses=self.storageClasses,
)

Expand Down
12 changes: 3 additions & 9 deletions python/lsst/daf/butler/registry/sql_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def isWriteable(self) -> bool:

def copy(self, defaults: RegistryDefaults | None = None) -> SqlRegistry:
"""Create a new `SqlRegistry` backed by the same data repository
and connection as this one, but independent defaults.
as this one, but independent defaults and database connection.
Parameters
----------
Expand All @@ -288,19 +288,13 @@ def copy(self, defaults: RegistryDefaults | None = None) -> SqlRegistry:
-------
copy : `SqlRegistry`
A new `SqlRegistry` instance with its own defaults.
Notes
-----
Because the new registry shares a connection with the original, they
also share transaction state (despite the fact that their `transaction`
context manager methods do not reflect this), and must be used with
care.
"""
if defaults is None:
# No need to copy, because `RegistryDefaults` is immutable; we
# effectively copy on write.
defaults = self.defaults
result = SqlRegistry(self._db, defaults, self._managers)
db = self._db.clone()
result = SqlRegistry(db, defaults, self._managers.clone(db))
result.dimension_record_cache.load_from(self.dimension_record_cache)
return result

Expand Down

0 comments on commit 4879269

Please sign in to comment.