diff --git a/lib/galaxy/tool_source_store/database.py b/lib/galaxy/tool_source_store/database.py index 4e42be66398..ebc9816440a 100644 --- a/lib/galaxy/tool_source_store/database.py +++ b/lib/galaxy/tool_source_store/database.py @@ -310,7 +310,11 @@ class DatabaseToolSourceStore(ToolSourceStore): if index is None: index = ToolIndex() - index.entries[entry.id] = entry + # add_entry keeps ``entries_by_version`` in step with ``entries`` — + # versioned lookups (``get(tool_id, tool_version)``, the job-time + # materialise path) read the per-version map, and the two maps are + # serialized independently. + index.add_entry(entry) index.invalidate_caches() # Update section mapping diff --git a/lib/galaxy/tools/lazy_toolbox.py b/lib/galaxy/tools/lazy_toolbox.py index e1d9ad75d27..9a868534956 100644 --- a/lib/galaxy/tools/lazy_toolbox.py +++ b/lib/galaxy/tools/lazy_toolbox.py @@ -801,6 +801,13 @@ class LazyToolBox(ToolBox): # id and misses the registry. Persist so job-handler processes # materialising from the shared index see it too. entry.data_manager_id = data_manager_id + if self._tool_index is not None: + # After a from_dict reload the default and per-version maps + # hold distinct objects; the job-time materialise resolves + # through the per-version map, so stamp its twin too. + twin = self._tool_index.entries_by_version.get(entry.id, {}).get(entry.version or "") + if twin is not None and twin is not entry: + twin.data_manager_id = data_manager_id if self._store is not None: try: self._store.update_index_entry(entry) diff --git a/test/unit/app/tools/test_lazy_tool.py b/test/unit/app/tools/test_lazy_tool.py index 289ef621b52..538e7b4d805 100644 --- a/test/unit/app/tools/test_lazy_tool.py +++ b/test/unit/app/tools/test_lazy_tool.py @@ -360,9 +360,14 @@ def test_create_tool_stamps_data_manager_conf_id_on_entry(): box = _seam_box() e = _entry(id="dm_tool", tool_type="manage_data") box._tool_index.entries["dm_tool"] = e + # After a from_dict reload the per-version map holds a distinct object — + # the one job-time versioned lookups resolve. It must get the stamp too. + twin = _entry(id="dm_tool", tool_type="manage_data") + box._tool_index.entries_by_version["dm_tool"] = {twin.version or "": twin} tool = box.create_tool(config_file=None, guid="dm_tool", data_manager_id="test_data_manager") assert isinstance(tool, LazyTool) assert e.data_manager_id == "test_data_manager" + assert twin.data_manager_id == "test_data_manager" box._store.update_index_entry.assert_called_once_with(e) diff --git a/test/unit/tool_source_store/test_stores.py b/test/unit/tool_source_store/test_stores.py index 8f062cd0fc5..5136323a578 100644 --- a/test/unit/tool_source_store/test_stores.py +++ b/test/unit/tool_source_store/test_stores.py @@ -213,6 +213,24 @@ class TestDatabaseBackendPathRows: assert reloaded.get("removable") is None assert "removable" not in reloaded.by_section.get("sec1", []) + def test_update_index_entry_reaches_versioned_lookups(self): + app = MockApp() + store = DatabaseToolSourceStore(app.model.context) # type: ignore[arg-type] + index = ToolIndex() + index.add_entry(ToolIndexEntry(id="dm_tool", version="1.0", name="DM")) + store.store_index(index) + app.model.context.commit() + + store.update_index_entry(ToolIndexEntry(id="dm_tool", version="1.0", name="DM", data_manager_id="conf_id")) + app.model.context.commit() + store.invalidate_index_cache() + + reloaded = store.load_index() + assert reloaded is not None + versioned = reloaded.get("dm_tool", "1.0") + assert versioned is not None + assert versioned.data_manager_id == "conf_id" + def test_pathless_sources_dedupe_on_hash(self): app = MockApp() store = DatabaseToolSourceStore(app.model.context) # type: ignore[arg-type]