-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Include dataset hash in job search #19112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
b3ab7ac to
ac4bba0
Compare
|
Running a Galaxy Training Academy without large computational resources - yeah, yeah! |
|
We have no clue about extra files in this context right - how can we make this conclusion without that? It isn't a -1 but I am uncomfortable with the direction this is all heading. I feel like we should be working on tightening up job search and not loosening it more and more before we've installed the guard rails it needs IMO. |
|
Maybe to clarify this - I absolutely think we should be able to hash the input to be used with job search. But I don't think this hash is what I would use - I would implement a hash of the dataset and not of the primary file of the dataset. 95% of the time they could be the same but we should verify that before using it in this context. |
If we match the transform would it still be possible to get different extra files for uploaded content (considering we already match on the datatype) ? I added 4abc6e6 because we don't record the transform for local files, but we could of course do that. There's of course also value in calculating the hash for datasets as they are written to the object store, but if we can get reliable equivalence using the upload hash + transform we could make nice progress in figuring out other edge cases for IWC workflows ? |
|
I had the realization reading your response that you only care about upload or think these fields can all only be set during uploads. I believe any tool can produce hashes and source hashes and I believe we have an API for hashing files after the fact anyway. Could we restrict all this logic to just fetch and upload1 - then it feels pretty close to being correct? This also probably explains my unease with #19110 - I think we don't validate the hashes outside the fetch tool but we can create the hashes in other tools I think - it feels like what we need is a hash validated field if we want to act on that data in this fashion but maybe it would be sufficient to be more proactive in validating the hash field whenever it is set. |
|
You may want to rebase this now that #19181 has been merged (thanks @jmchilton !), since it contained some type annotation fixes copied from here.
Not sure it's the same problem, but in #19181 I found out that a type of transform (grooming) is not reproducible (because the BAM header contains paths as part of the samtools command used to sort the data), so you'd get different hashes if you materialize twice from the same source. So I think I agree it's safer to match on the actual dataset hashes instead of the dataset source hashes. |
|
Uhm, I'm confused, is that not what I'm doing ? |
Ah, yes, I guess I was confused by the discussion above and the PR title, shouldn't it be "Include dataset hash in job search"? "Source hash" hints at the |
|
@mvdbeek Still draft? |
|
I think so ? I need to think about all the ways it could break, and extra files is a valid concern, I think ? |
Right, should we only use dataset hashes if there are no extra files for the moment? |
|
That sounds good to me. I'm hacking away on something else right now, if you want to add to the PR that would be really cool, otherwise I'll come back to it later. |
d1a7734 to
6e12ed1
Compare
177b66f to
dee9c9c
Compare
Fix the following test failure in galaxyproject#19112 : ``` FAILED lib/galaxy_test/api/test_jobs.py::TestJobsApi::test_delete_job - assert 1 == 0 + where 1 = len([{'model_class': 'Job', 'id': 'ebdef174148ecc74', 'history_id': '4dbec071fa7bdbaa', 'tool_id': 'cat1', 'state': 'ok', 'exit_code': 0, 'create_time': '2025-11-03T22:13:05.770038', 'update_time': '2025-11-03T22:13:07.098024', 'galaxy_version': '26.0', 'external_id': None, 'handler': None, 'job_runner_name': None, 'command_line': None, 'user_email': None, 'user_id': 'adb5f5c93f827949', 'command_version': '', 'params': {'queries': '[]', 'chromInfo': '"/home/runner/work/galaxy/galaxy/galaxy root/tool-data/shared/ucsc/chrom/?.len"', 'dbkey': '"?"', '__input_ext': '"input"'}, 'inputs': {'input1': {'id': 'cda8ec5455a55990', 'src': 'hda', 'uuid': 'e7979ca3-5df9-4d8d-8654-a8db8291a6c4'}}, 'outputs': {'out_file1': {'id': '3d0ca2420d1410fd', 'src': 'hda', 'uuid': 'fbeff3c3-12f5-40bf-aa6a-ea536e482938'}}, 'copied_from_job_id': None, 'output_collections': {}}]) + where [{'model_class': 'Job', 'id': 'ebdef174148ecc74', 'history_id': '4dbec071fa7bdbaa', 'tool_id': 'cat1', 'state': 'ok', 'exit_code': 0, 'create_time': '2025-11-03T22:13:05.770038', 'update_time': '2025-11-03T22:13:07.098024', 'galaxy_version': '26.0', 'external_id': None, 'handler': None, 'job_runner_name': None, 'command_line': None, 'user_email': None, 'user_id': 'adb5f5c93f827949', 'command_version': '', 'params': {'queries': '[]', 'chromInfo': '"/home/runner/work/galaxy/galaxy/galaxy root/tool-data/shared/ucsc/chrom/?.len"', 'dbkey': '"?"', '__input_ext': '"input"'}, 'inputs': {'input1': {'id': 'cda8ec5455a55990', 'src': 'hda', 'uuid': 'e7979ca3-5df9-4d8d-8654-a8db8291a6c4'}}, 'outputs': {'out_file1': {'id': '3d0ca2420d1410fd', 'src': 'hda', 'uuid': 'fbeff3c3-12f5-40bf-aa6a-ea536e482938'}}, 'copied_from_job_id': None, 'output_collections': {}}] = json() + where json = <Response [200]>.json ``` where searching for jobs has now started returning jobs with the same tool_id and input hashes but from different histories.
Fix the following test failure in galaxyproject#19112 : ``` FAILED lib/galaxy_test/api/test_jobs.py::TestJobsApi::test_delete_job - assert 1 == 0 + where 1 = len([{'model_class': 'Job', 'id': 'ebdef174148ecc74', 'history_id': '4dbec071fa7bdbaa', 'tool_id': 'cat1', 'state': 'ok', 'exit_code': 0, 'create_time': '2025-11-03T22:13:05.770038', 'update_time': '2025-11-03T22:13:07.098024', 'galaxy_version': '26.0', 'external_id': None, 'handler': None, 'job_runner_name': None, 'command_line': None, 'user_email': None, 'user_id': 'adb5f5c93f827949', 'command_version': '', 'params': {'queries': '[]', 'chromInfo': '"/home/runner/work/galaxy/galaxy/galaxy root/tool-data/shared/ucsc/chrom/?.len"', 'dbkey': '"?"', '__input_ext': '"input"'}, 'inputs': {'input1': {'id': 'cda8ec5455a55990', 'src': 'hda', 'uuid': 'e7979ca3-5df9-4d8d-8654-a8db8291a6c4'}}, 'outputs': {'out_file1': {'id': '3d0ca2420d1410fd', 'src': 'hda', 'uuid': 'fbeff3c3-12f5-40bf-aa6a-ea536e482938'}}, 'copied_from_job_id': None, 'output_collections': {}}]) + where [{'model_class': 'Job', 'id': 'ebdef174148ecc74', 'history_id': '4dbec071fa7bdbaa', 'tool_id': 'cat1', 'state': 'ok', 'exit_code': 0, 'create_time': '2025-11-03T22:13:05.770038', 'update_time': '2025-11-03T22:13:07.098024', 'galaxy_version': '26.0', 'external_id': None, 'handler': None, 'job_runner_name': None, 'command_line': None, 'user_email': None, 'user_id': 'adb5f5c93f827949', 'command_version': '', 'params': {'queries': '[]', 'chromInfo': '"/home/runner/work/galaxy/galaxy/galaxy root/tool-data/shared/ucsc/chrom/?.len"', 'dbkey': '"?"', '__input_ext': '"input"'}, 'inputs': {'input1': {'id': 'cda8ec5455a55990', 'src': 'hda', 'uuid': 'e7979ca3-5df9-4d8d-8654-a8db8291a6c4'}}, 'outputs': {'out_file1': {'id': '3d0ca2420d1410fd', 'src': 'hda', 'uuid': 'fbeff3c3-12f5-40bf-aa6a-ea536e482938'}}, 'copied_from_job_id': None, 'output_collections': {}}] = json() + where json = <Response [200]>.json ``` where searching for jobs has now started returning jobs with the same tool_id and input hashes but from different histories.
Fix the following test failure in galaxyproject#19112 : ``` FAILED lib/galaxy_test/api/test_jobs.py::TestJobsApi::test_delete_job - assert 1 == 0 + where 1 = len([{'model_class': 'Job', 'id': 'ebdef174148ecc74', 'history_id': '4dbec071fa7bdbaa', 'tool_id': 'cat1', 'state': 'ok', 'exit_code': 0, 'create_time': '2025-11-03T22:13:05.770038', 'update_time': '2025-11-03T22:13:07.098024', 'galaxy_version': '26.0', 'external_id': None, 'handler': None, 'job_runner_name': None, 'command_line': None, 'user_email': None, 'user_id': 'adb5f5c93f827949', 'command_version': '', 'params': {'queries': '[]', 'chromInfo': '"/home/runner/work/galaxy/galaxy/galaxy root/tool-data/shared/ucsc/chrom/?.len"', 'dbkey': '"?"', '__input_ext': '"input"'}, 'inputs': {'input1': {'id': 'cda8ec5455a55990', 'src': 'hda', 'uuid': 'e7979ca3-5df9-4d8d-8654-a8db8291a6c4'}}, 'outputs': {'out_file1': {'id': '3d0ca2420d1410fd', 'src': 'hda', 'uuid': 'fbeff3c3-12f5-40bf-aa6a-ea536e482938'}}, 'copied_from_job_id': None, 'output_collections': {}}]) + where [{'model_class': 'Job', 'id': 'ebdef174148ecc74', 'history_id': '4dbec071fa7bdbaa', 'tool_id': 'cat1', 'state': 'ok', 'exit_code': 0, 'create_time': '2025-11-03T22:13:05.770038', 'update_time': '2025-11-03T22:13:07.098024', 'galaxy_version': '26.0', 'external_id': None, 'handler': None, 'job_runner_name': None, 'command_line': None, 'user_email': None, 'user_id': 'adb5f5c93f827949', 'command_version': '', 'params': {'queries': '[]', 'chromInfo': '"/home/runner/work/galaxy/galaxy/galaxy root/tool-data/shared/ucsc/chrom/?.len"', 'dbkey': '"?"', '__input_ext': '"input"'}, 'inputs': {'input1': {'id': 'cda8ec5455a55990', 'src': 'hda', 'uuid': 'e7979ca3-5df9-4d8d-8654-a8db8291a6c4'}}, 'outputs': {'out_file1': {'id': '3d0ca2420d1410fd', 'src': 'hda', 'uuid': 'fbeff3c3-12f5-40bf-aa6a-ea536e482938'}}, 'copied_from_job_id': None, 'output_collections': {}}] = json() + where json = <Response [200]>.json ``` where searching for jobs has now started returning jobs with the same tool_id and input hashes but from different histories.
0fb9a09 to
790fdd9
Compare
|
A few trainers are testing the job cache with 25.1 and so far it works great. However, we see a lot of confusion. "Why people can not upload data from Zenodo etc" .... "why they need to use a shared history or DL" ... I think this PR is very important to streamline the job-cache usage and make it just easier for trainers to make use of it. |
5943450 to
09f64a4
Compare
8cddb94 to
67a4689
Compare
After some back-and-forth, I've switched to calculate dataset hashes also for the extra files and extend
It would indeed be great to have this merged for 26.0, I've marked it as ready for review. |
| compute_dataset_hash.delay(request=request) | ||
|
|
||
| # For composite datasets with extra files, hash each extra file individually | ||
| if dataset.extra_files_path_exists(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these can be thousands of files, can we at least smash this into a single task ? And maybe record a hash of hashes so the query won't have thousands of comparisons ?
| .group_by(b_hash.dataset_id) | ||
| .having( | ||
| and_( | ||
| # Number of matched hashes equals total hashes in A |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am so worried about this bringing down the database ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we calculate and store in the database (also) a "combined_hash", i.e. a hash of all the hashes of the dataset primary and extra files? We could use a similar approach also for a dataset collection (whose hash would be the combined hash of the combined hashes of its components).
Then we would be able to simplify the database query.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can extra files change when you change the dataytype ? I think they can ? I don't know if it's wise to include the extra file stuff in a first pass, it is complex and i'm not sure that even just including the hashes isn't a large query penalty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the primary file that change for some datatypes (e.g. Rgenetics) when setting metadata, not sure about the extra files.
|
I am uncertain that this is always correct and worried about the query overhead we're adding. It also won't work for collections which I guess is ok for a first pass. |
having thought for a long time about this I think that's the right thing to do ? Replace the dataset either up front if we have an expected checksum (this already works for materialization, we can add a button to the GTN for that) or after the download when we have the checksum. An implementation of this was in 5cd059a |
by computing and storing hashes for all files (primary file and extra files). Enhanced ``has_same_hash()`` to match datasets only when all hashes match, preventing partial matches.
67a4689 to
40a0820
Compare
This means we find equivalent jobs if an input hda either points at the same dataset id (existing behavior), or if the dataset
source_uri, transform andhashes match. All further restrictions still apply (same metadata etc).Builds on #19108 and #19110 .
Also:
test_run_deferred_datasetandtest_run_deferred_dataset_with_metadata_options_filtertests from
lib/galaxy_test/api/test_tools.pywhich are duplicatesof
test_deferred_with_cached_inputandtest_deferred_with_metadata_options_filterinlib/galaxy_test/api/test_tool_execute.py.How to test the changes?
(Select all options that apply)
License