Conversation
This comment has been minimized.
This comment has been minimized.
fb9c438 to
7977d5a
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
| }; | ||
| assertNoQueriesPerItem("dataset deep", smallRegularVersion, largeRegularVersion, findDeep, readFiles); | ||
| assertNoQueriesPerItem("dataset deep tabular", smallTabularVersion, largeTabularVersion, findDeep, readFiles); | ||
| } |
There was a problem hiding this comment.
@ErykKul I'm just putting this here but please investigate this failing test: at edu.harvard.iq.dataverse.api.DatasetsIT.testSummaryDatasetVersionsDifferencesAPI(DatasetsIT.java:6765)
qqmyers
left a comment
There was a problem hiding this comment.
Looks good w.r.t. code review, so I'll approve so we can move to QA. I made a couple comments but the only change I suggest is to simplify the release note.
The one concern from AI was that this change means findDeep calls no longer assure everything is loaded so if we use the object outside the initial context, some relations may not be loaded. I don't think we do that in general, but it's possible that we do that in workflows or some onSuccess and/or async calls. I'm going to do some testing/QA in those areas at QDR. (It doesn't sound to hard to fix that - either you iterate though to sure things load, or otherwise adapt to not rely on everything already being loaded in those, hopefully rare, cases.)
| private List<IngestReport> ingestReports; | ||
|
|
||
| @OneToOne(mappedBy = "dataFile", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) | ||
| @BatchFetch(BatchFetchType.IN) |
There was a problem hiding this comment.
Just a note that AI says BatchFetch isn't optimal for OneToOne fields, but this is better than no annotation given that we don't have weaving on to allow lazy fetch, when most values are null, etc. So we might want to revisit these if/when we get weaving working (I tried it once pre-AI and didn't manage to fix the issues that arose.)
|
|
||
| @ManyToOne | ||
| @JoinColumn(name="embargo_id") | ||
| @BatchFetch(BatchFetchType.IN) |
There was a problem hiding this comment.
Another AI note about batchfetch - it suggests using it as hints on specific queries instead of as an annotation. I don't think that's worth worrying about right now though.
| @OneToMany(mappedBy="dataFile", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) | ||
| private List<FileMetadata> fileMetadatas; | ||
|
|
||
| @OneToMany(mappedBy="dataFile", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) |
There was a problem hiding this comment.
Why aren't fields like this made batchfetch? Is it just scope of the PR or are these fields somehow different? (I note this one was dropped from the findDeep hints in previous PRs).
There was a problem hiding this comment.
Deliberate. No code reads DataFile.getGuestbookResponses(); responses are queried through the service bean, and the collection is only loaded by the cascade when a file is deleted. It is one row per download, so batching it would only add queries and memory. Same for auxiliary files, ingest reports and access requests. The annotated relations are the ones the page, API, export and indexing read per file.
| @@ -0,0 +1,5 @@ | |||
| ## Bug Fixes | |||
There was a problem hiding this comment.
FWIW: I'd call this an improvement rather than bug fix, but my main comment would be a suggestion to simplify the note, e.g.: The number of queries required to load datasets and files has been greatly reduced for large datasets. An additional change improves the loading time for solr facets on the dataset page. The result is significantly faster dataset and file page and api response times. See #12739.
(The suggested phrasing above makes it sound like the file API is also faster like the dataset one - is that the case? I was guessing it might be but didn't check. If not - faster dataset and file page loading and dataset api response times.)
There was a problem hiding this comment.
Done, with your wording. The file listing API benefits too: 7 queries per file before, none now (new IT case).
This comment has been minimized.
This comment has been minimized.
|
@qqmyers |
This comment has been minimized.
This comment has been minimized.
|
|
📦 Pushed preview images as 🚢 See on GHCR. Use by referencing with full name as printed above, mind the registry name. |
| * batch fetching (EclipseLink error 6169). | ||
| * @return the dataset | ||
| */ | ||
| public Dataset findDeep(Object pk) { |
There was a problem hiding this comment.
I see that this appears to work as intended.
But, dumb question:
What is the difference between find() and findDeep() at this point, and do we really need the latter at all? - I may be missing something that it adds...
|
This prod. dataset with 1,577 files has been used as the benchmark for the total query count behind rendering the dataset page: The result stayed virtually unchanged, at ~2,100+ queries since at least 6.7.1; was last measured 10 days ago for 6.12 (2,136 queries). Testing with this PR reduces the number to 591. A decent bulk of the savings comes from eliminating (N = number of files) queries on the 1:1 relationship to |
|
Locust test.
This is our standard, heavier-load test - 250 pseudousers bombarding the site, with 5-20 sec. sleeps between calls. |


What this PR does / why we need it:
Loading and reading the files of a dataset version ran database queries per file, and for tabular files per variable. For a dataset with 64,278 files, one file page took about 17 seconds.
LAZYis ignored):FileMetadata.dataFile,DataFile.ingestRequest,DataFile.thumbnailForDataset,DvObject.storageQuota.FileMetadata.fileCategories,FileMetadata.varGroups,DataFile.dataTables,DataFile.dataFileTags.DataTable.dataVariables,DataVariable.invalidRanges,summaryStatistics,categories,variableMetadatas.These get EclipseLink
@BatchFetch(BatchFetchType.IN), so each is loaded for up to 500 items per query.INrather than the defaultJOIN, so that paged file queries still load only their page.The dataset page had two more costs that grow with the number of files:
DatasetVersionServiceBean.findDeeploaded the version with all files and ten of their relations in one joined query, which EclipseLink turns into objects slowly. It now loads the version alone; the files come with the batch fetching above. The named queryDatasetVersion.findByIdno longer joins the files either: EclipseLink refuses to batch fetch for objects built from a join (error 6169).DataFile.embargoandDataFile.retentionget@BatchFetchtoo.Dataset.findByIdandDatasetServiceBean.findDeep, used byGET /api/datasets/{id}and by indexing, joined all files of the dataset with 18 of their relations in the same way. With the batch fetching above, this failed with EclipseLink 6169 (found in review). They now load the dataset alone. The API response is unchanged: compared byte for byte with develop for datasets with 10,000 and 64,000 files, by id and by persistent id, as admin and anonymous. For 64,000 files the response starts after 1.7 s instead of 3.5 s.Which issue(s) this PR closes:
Special notes for your reviewer:
DatasetVersionFileMetadatasPerformanceITruns five operations on a smaller and a larger dataset and fails if the select queries grow with the number of files or variables: checking for restricted files, export file details (regular and tabular), the file listing JSON, and the schema.org JSON-LD. All five fail on develop (for example 409 vs 809 queries for the export of 50 vs 100 files) and pass here (17 vs 17).TabularDataExportITgoes from 8,037 select queries to 34,HugeDatasetExportPerformanceITfrom 8,009 to 25. A sixth check loads a version like the dataset page and reads the relationsfindDeepused to join (16 queries for both sizes); it fails with EclipseLink 6169 if the files are joined in the query again. A seventh does the same for a dataset loaded likeDatasetServiceBean.findDeep(10 queries for both sizes). The test fixtures now set the dataset as the owner of its files, as in a real installation; without that, the dataset had no files to load.VariableMetadata.categoriesMetadatais also read per variable, but the test fixtures cannot create variable metadata yet, so it is left out.Suggestions on how to test this:
mvn test-compile failsafe:integration-test -DskipUnitTests=true -Dit.test='DatasetVersionFileMetadatasPerformanceIT,TabularDataExportIT,HugeDatasetExportPerformanceIT'(needs Docker). On an installation with a large dataset, open a file page and the dataset page before and after. Locally, for a published dataset with 10,000 files, the file page took 0.73 to 1.06 s on develop (36d1f0f) and 0.25 to 0.34 s with this PR. For a published dataset with 64,000 files, the dataset page took 4.2 s (median, time to first byte) with only the file page changes, 3.0 s with the Solr change and 1.8 s with both. On our production installation (6.7.1 with these commits), the dataset page of a dataset with 64,278 files went from 9.5 s with only the file page changes to about 3 s.Does this PR introduce a user interface change? If mockups are available, please link/include them here:
No.
Is there a release notes update needed for this change?:
Included.
Additional documentation:
None.