Repository navigation
feat!(prepro): validate user-submitted external accessions - #7413
maverbiest wants to merge 20 commits into
Conversation
ca74192 to
feb5038
Compare
|
Claude finished @maverbiest's task in 1m 3s —— View job Code review: validate user-submitted external accessions
Overall this is clean and easy to follow. The accession regexes, the INSDC-ingest bypass and the umbrella-project detection look correct. The parametrised tests cover the main paths well: wrong type, comma-separated values, case, 404, 5xx, retries exhausted and ingest bypass. Main concern (inline): if ENA is down, the check can hold up the pipeline badly. Each lookup can take about 3.5 minutes because of 5 retries with a 30s timeout plus backoff. Failures aren't cached and there can be 3 lookups per entry, so a whole batch could stall even though the result is only a warning. I suggest fewer retries, a shorter timeout and a simple circuit breaker. Other inline points:
General:
|
| # Successful ENA responses are small XML documents (a few KB). Cache hits refresh an entry's | ||
| # position; a bioproject shared across a batch stays cached while unique biosamples and runs | ||
| # cycle through. | ||
| ena_cache = RequestCache(max_size=16) |
There was a problem hiding this comment.
Performance when ENA is slow or down: this reuses RequestCache's default retries=5 with backoff_factor=1, and uses a 30s timeout. If ENA hangs or keeps returning 5xx/429, one lookup can block for about 6×30s plus ~31s of backoff, so roughly 3.5 minutes. Failed responses are never cached, and each entry can have up to 3 accessions, so every entry in the batch waits the full time again. An ENA outage could stall preprocessing for hours, even though the result is only a warning.
Suggestions:
- Use fewer retries for this cache (e.g.
RequestCache(max_size=16, retries=1)) and a shorter timeout (5–10s). - Add a small circuit breaker: after an "unreachable" result, skip ENA for N seconds and return the warning straight away.
There was a problem hiding this comment.
I'll reduce retries and timeout as suggested, let's skip the circuit breaker for now, we can revisit if it's problematic
| return RawProcessingResult(datum=accession) | ||
|
|
||
| def _is_umbrella_project(self, xml_bytes) -> bool: | ||
| for _, elem in ET.iterparse(io.BytesIO(xml_bytes), events=("start",)): # noqa: S314 |
There was a problem hiding this comment.
If ENA returns a 200 with a body that isn't valid XML (truncated body, an HTML maintenance page, etc.), ET.ParseError isn't caught here. call_function then turns it into an internal error, so the submission is blocked. Every other ENA problem only produces a warning. Consider catching ET.ParseError and returning the "unable to check" warning instead, and add a test case for it. Using defusedxml would also remove the need for the S314 noqa.
There was a problem hiding this comment.
It's probably a bit overboard to guard against ENA returning malformed XML on a 200, we also don't handle this specifically elsewhere
2874df9 to
c569c8a
Compare
| RAW_READS = "raw_reads" | ||
|
|
||
|
|
||
| # Also guards against comma-separated lists, which the ENA browser API |
There was a problem hiding this comment.
we should probably link to the docs here: https://ena-docs.readthedocs.io/en/latest/submit/general-guide/accessions.html
| f"expected a value matching '{pattern.pattern}'." | ||
| ) | ||
| try: | ||
| response = ena_cache.get_or_fetch( |
There was a problem hiding this comment.
Hmm... if the bioproject was once public and then is removed we cache the successful query... I guess in practice this is a small cache however as we probably wont get many bioprojects I wonder if this could be an issue
There was a problem hiding this comment.
We could add some time limit to the cache that makes entries stale after a while?
But then again, a bioproject could also be removed in the interim between PPX submission and ENA deposition, so we can't guard against this type of thing completely in any case
There was a problem hiding this comment.
yeah I think its ok to ignore for now, just good to be aware (we might want to add a commetn)
There was a problem hiding this comment.
This also raises the question: what do we do if a bioproject exists when someone submits and when we propagate to ENA, but then three months later we bump the pipeline version and discover the bioproject has been deleted?
This would block reprossessing, right? What would we do in that situation, a DB surgery to remove the external accessions from the entry?
There was a problem hiding this comment.
yeah it would block reprocessing but I think we can just ignore that case for now as I think its unlikely (and tbf would be good for us to know if this happened lol)
| accession: str | ||
| accession_type: str | None = "bioproject" | ||
| # How ENA responds: an HTTP status code, or an exception raised by the session. | ||
| # None means ENA must not be contacted at all. |
There was a problem hiding this comment.
I think this is actually checked by checking if expected_url is actually null
Co-authored-by: Anna (Anya) Parker <50943381+anna-parker@users.noreply.github.com>
anna-parker
left a comment
There was a problem hiding this comment.
This is amazing thanks!
Before we merge this could you quickly confirm that for the existing bioproject, biosamples and runs that have been directly submitted to PPX none are not live? (should hopefully not be the case)
resolves #6859, #7250
Breaking change
Non-INSDC ingested entries that provide accessions of umbrella projects in
bioprojectAccessionwill be rejected after this change. Also accessions that don't match the following patterns will error:Summary
This PR adds a validation for values provided by direct submitters in the
bioprojectAccession,biosampleAccession, andinsdcRawReadsAccessionfields (validation is skipped for INSDC-ingested data). This is done by checking if the provided accessions are public on ENA. The ENA checking implementation is based on theENAVisibilityCheckerinena-submission/src/ena_deposition/check_external_visibility.pyNew behaviour summary:
Most of the LOC are from a parametrized test I asked Claude to add. I thought it was quite nice, but let me know if it's too exhaustive and I will cut it down.
Will this have a big impact on reprocessing?
This change means also that we will re-validate each non-INSDC-ingested value in these three fields when we do a full reprocessing of our data. Luckily, it seems we only have 448 direct-submission entries in the DB where one or more of these fields are set.
I found this out by running this query against the staging DB:
Naively, this is 448 * 3 = 1344 calls to ENA. Claude times these between 0.1 and 0.4 seconds, so ~538 seconds added to reprocessing all of PPX at most. This does not take into account caching, timeouts, and retries, though.
We could in theory record in the DB whether an accession has passed validation in the past so we never check it again, but this would be quite a big change for little gain IMO.
Screenshot
PR Checklist
🚀 Preview: https://perpro-check-accessions.loculus.org