From 02d6b276aaa1e5533a79e7b60bb0dab9e6d60e09 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 2 Oct 2023 10:09:35 -0400 Subject: [PATCH 1/3] Optimize iteration No need to make lists before iteration given possible early termination --- lib/galaxy/model/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 04d4552ae620..a82e41786aa1 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -19,6 +19,7 @@ from collections.abc import Callable from datetime import timedelta from enum import Enum +from itertools import chain from string import Template from typing import ( Any, @@ -4487,10 +4488,9 @@ def ok_to_edit_metadata(self): # prevent modifying metadata when dataset is queued or running as input/output # This code could be more efficient, i.e. by using mappers, but to prevent slowing down loading a History panel, we'll leave the code here for now sa_session = object_session(self) - for job_to_dataset_association in ( - sa_session.query(JobToInputDatasetAssociation).filter_by(dataset_id=self.id).all() - + sa_session.query(JobToOutputDatasetAssociation).filter_by(dataset_id=self.id).all() - ): + stmt1 = select(JobToInputDatasetAssociation).filter_by(dataset_id=self.id) + stmt2 = select(JobToOutputDatasetAssociation).filter_by(dataset_id=self.id) + for job_to_dataset_association in chain(sa_session.scalars(stmt1), sa_session.scalars(stmt2)): if job_to_dataset_association.job.state not in Job.terminal_states: return False return True From 0e5b56fbc69f0f9ea4774212a873c87f5862ebc8 Mon Sep 17 00:00:00 2001 From: John Davis Date: Mon, 2 Oct 2023 11:40:01 -0400 Subject: [PATCH 2/3] Don't select everything but the kitchen sink --- lib/galaxy/model/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index a82e41786aa1..d3c9866e1589 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -4486,12 +4486,12 @@ def set_dbkey(self, value): def ok_to_edit_metadata(self): # prevent modifying metadata when dataset is queued or running as input/output - # This code could be more efficient, i.e. by using mappers, but to prevent slowing down loading a History panel, we'll leave the code here for now sa_session = object_session(self) - stmt1 = select(JobToInputDatasetAssociation).filter_by(dataset_id=self.id) - stmt2 = select(JobToOutputDatasetAssociation).filter_by(dataset_id=self.id) - for job_to_dataset_association in chain(sa_session.scalars(stmt1), sa_session.scalars(stmt2)): - if job_to_dataset_association.job.state not in Job.terminal_states: + stmt1 = select(JobToInputDatasetAssociation.job_id).filter_by(dataset_id=self.id) + stmt2 = select(JobToOutputDatasetAssociation.job_id).filter_by(dataset_id=self.id) + for job_id in chain(sa_session.scalars(stmt1), sa_session.scalars(stmt2)): + state = sa_session.scalar(select(Job.state).where(Job.id == job_id)) + if state not in Job.terminal_states: return False return True From 003ae50cac68fb02d401bbd67c2710ffc6e0d238 Mon Sep 17 00:00:00 2001 From: John Davis Date: Tue, 3 Oct 2023 11:04:30 -0400 Subject: [PATCH 3/3] Reduce check to one query --- lib/galaxy/model/__init__.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index d3c9866e1589..e82ee44244bc 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -19,7 +19,6 @@ from collections.abc import Callable from datetime import timedelta from enum import Enum -from itertools import chain from string import Template from typing import ( Any, @@ -4485,15 +4484,27 @@ def set_dbkey(self, value): dbkey = property(get_dbkey, set_dbkey) def ok_to_edit_metadata(self): - # prevent modifying metadata when dataset is queued or running as input/output - sa_session = object_session(self) - stmt1 = select(JobToInputDatasetAssociation.job_id).filter_by(dataset_id=self.id) - stmt2 = select(JobToOutputDatasetAssociation.job_id).filter_by(dataset_id=self.id) - for job_id in chain(sa_session.scalars(stmt1), sa_session.scalars(stmt2)): - state = sa_session.scalar(select(Job.state).where(Job.id == job_id)) - if state not in Job.terminal_states: - return False - return True + """ + Prevent modifying metadata when dataset is queued or running as input/output: + return `False` if there exists an associated job with a non-terminal state. + """ + + def exists_clause(assoc_model): + return ( + select(assoc_model.job_id) + .join(Job) + .where(assoc_model.dataset_id == self.id) + .where(Job.state.not_in(Job.terminal_states)) + .exists() + ) + + stmt = select( + or_( + exists_clause(JobToInputDatasetAssociation), + exists_clause(JobToOutputDatasetAssociation), + ) + ) + return not object_session(self).scalar(stmt) def change_datatype(self, new_ext): self.clear_associated_files()