Skip to content

Fixed the private data reminder schedule and NCBI publication search failure handling - #675

Draft
vagisha wants to merge 14 commits into
release26.7-SNAPSHOTfrom
26.7_fb_panoramapublic-reminder-fixes
Draft

Fixed the private data reminder schedule and NCBI publication search failure handling#675
vagisha wants to merge 14 commits into
release26.7-SNAPSHOTfrom
26.7_fb_panoramapublic-reminder-fixes

Conversation

@vagisha

@vagisha vagisha commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Rationale

  • The Private Data Reminder job stopped firing on production after a Tomcat restart.
  • NCBI eutils can return a 5xx for requests, even when they are well under the rate limit. A failed search was indistinguishable from a dataset with no published paper.
  • The job status reported COMPLETE even when there were errors in sending reminders to one or more datasets.

Related Pull Requests

Changes

  • PanoramaPublicModule schedules the reminder job from startBackgroundThreads().
  • NcbiPublicationSearchServiceImpl.getString retries eutils requests on 5xx, read timeouts and 429.
    • Up to three attempts, waiting 500ms before the second and 1000ms before the third.
    • HttpClient's own retry layer is disabled so the attempt count means what it says.
  • searchForPublication throws NcbiSearchException only when no publication was found and at least one request failed, so an empty result still means no paper was found.
    • The reminder job records the failure against the experiment and still posts the reminder.
    • The two controller actions report an error rather than "no publications found".
  • Added an optional NCBI API key to Private Data Reminder Settings, raising the eutils rate ceiling from 3 to 10 requests / second.
    • It is held in the encrypted property store, is never rendered back into the form
    • It can only be removed through the Remove the saved key checkbox, so editing the reminder settings cannot erase it.
    • The key is redacted from the retry warning and from NCBI's 4xx response body before either reaches a log.
    • NCBI echoes the submitted key back in its 400 body, and for the reminder job the log is the pipeline job log.
  • PrivateDataReminderJob checks a configured NCBI API key before iterating over datasets.
    • The check is skipped when publication search is off or no key is saved.
    • If the key is rejected, the job is stopped with no reminders posted.
    • Only a 400 is a rejection. Any other status leaves the key unchecked. The job logs a warning and runs with the key anyway.
  • Added a Validate button that checks a key against NCBI before it is saved.
    • The result is displayed beside the field with the rejection details behind a Details link.
    • A status that is not 400 is reported as a check that could not be completed.
  • PrivateDataReminderJob.run reports a status computed from what the job recorded, rather than always reporting complete. An ERROR logged through the job's logger already sets the status to error, and the status set at the end was overwriting it.
    • ERROR means the job could not start, or a reminder that should have posted for a dataset was not.
    • A failed publication search is a WARN, since the reminder is still posted. It reports an error only when the search failed for more than half the datasets it ran for.
    • A missing support message thread is an error only when the submission has an announcement id. Older datasets, submitted before submission requests were posted to a message board, do not have an announcement id.

Tests

  • NcbiPublicationSearchServiceImpl.TestCase
    • Retries 5xx, timeouts and 429, but not other 4xx.
    • A persistent 5xx is tried 3 times with 500ms then 1000ms backoff, then rethrown.
    • An interrupted thread stops after one attempt.
    • The key is stripped from the logged URL and from an echoed error body.
    • NCBI's reason reaches the log for a 4xx but not a 5xx.
    • A 400 from NCBI is a rejected key, while a 403, 404, 429, 5xx or timeout is a check that could not be completed.
  • NcbiApiKeyTest - new Selenium test for the NCBI API key settings.
    • Fails with instructions if a key is already saved on the server, rather than overwriting one it cannot restore.
    • The saved key is never rendered into the form, and saving with the field blank keeps the stored key.
    • Validate button reports that a key was not accepted.
    • The Remove the saved key checkbox removes the key. Removes its own key in @After even if the test fails partway.
  • PrivateDataReminderSettings.TestCase - the extension and reminder boundary tests now take the current date from the expiry the settings calculate, so they no longer fail on the last days of a month.

Co-Authored-By: Claude noreply@anthropic.com

vagisha and others added 12 commits August 28, 2026 12:07
…on search

- PanoramaPublicModule.startupAfterSpringConfig now re-establishes the daily reminder's Quartz schedule on startup; it was only set when an admin saved the settings form, so a Tomcat restart silently killed the job (Quartz's in-memory job store does not survive a JVM restart).
* Added an "NCBI API key" admin field wired into buildCommonParams as &api_key= (raises NCBI rate limit 3->10 req/sec); contextual Logger threaded through getString/getJson so retry warnings follow the job log vs. server log.
- NcbiPublicationSearchServiceImpl.getString retries eutils calls (3 attempts, 500/1000ms backoff) on 5xx and read timeouts (~40% transient failure rate); 4xx fails fast, with the NCBI response body included so a bad key's "API key invalid" reaches the log instead of a bare 400.
- Added JUnit (retry/backoff, api_key, 4xx body) and Selenium API-key coverage.

Follow-up to PR #606.
…ore the bad-key search so incidental NCBI 5xx in earlier steps don't make checkExpectedErrors flaky on dev
…rm/id query params instead of substring-scanning the whole request URL
…ve-up-after-MAX_HTTP_ATTEMPTS, no-retry-on-4xx); made executeGet protected so a test subclass can drive it

- Corrected stale comments in NcbiPublicationSearchServiceImpl and its mock, and tightened the others.
* Redacted the API key from the retry warning and from NCBI's 4xx body before either reaches a log
* Moved the key to the encrypted property store, and stopped the form displaying or erasing it
* Threw NcbiSearchException from executeSearch so a rejected key no longer reads as a dataset with no paper
* Added a Validate button that checks a key against NCBI before it is saved
* Scheduled the reminder job from startBackgroundThreads so a SchedulerException cannot fail server startup
* Added redaction unit tests and reworked the Selenium NCBI API key coverage

Co-Authored-By: Claude <noreply@anthropic.com>
* Added NcbiApiKeyTest, which fails with instructions rather than skipping when a key is already saved
* Covered the Validate button on TeamCity through the mock NCBI service, so nothing is skipped there
* Removed the key coverage from PublicationSearchTest, which no longer saves or removes a site-wide key
* Removed the test's own key in @after so a failed run cannot leave one that breaks every later search

Co-Authored-By: Claude <noreply@anthropic.com>
… error bodies

* Retried 429, which is NCBI's answer when the request rate is exceeded and was failing fast as a 4xx
* Disabled HttpClient's own retries, which doubled the requests MAX_HTTP_ATTEMPTS names on a persistent 503
* Stopped retrying once the thread is interrupted, since every later sleep throws at once and drops the backoff
* Stopped the reminder job starting another experiment after interruption, which would run with no rate limit
* Bounded the error body read and caught its ParseException, which could otherwise discard the HTTP status

Co-Authored-By: Claude <noreply@anthropic.com>
* Took the current date from the expiry the settings calculate, rather than from today plus an offset
* On 31 August, six months back then forward again gives 28 August, so the expiry fell before the date checked
* Removed the currentDate and minutesOffset parameters that the change left always null and zero
* Left the production arithmetic alone, since adding months and clamping to a shorter month is correct
* Corrected two test comments, one naming a map key that was renamed and one naming the wrong cleanup method

Co-Authored-By: Claude <noreply@anthropic.com>
* PrivateDataReminderJob checks a configured key before any dataset, and errors without posting when NCBI rejects it
* checkApiKey replaces validateApiKey and reports VALID, REJECTED or UNCONFIRMED, so an NCBI outage does not stop the run
* The Validate button now reports a request that never reached NCBI separately from a rejected key
* The end-of-run message no longer tells an admin to check the key when the failure was something else
* Added a unit test for the classification, driven through an executeGet override so it needs no network

Co-Authored-By: Claude <noreply@anthropic.com>
* sleepMs and rateLimit became instance methods, so a test subclass can replace the waiting
* The retry tests now assert the delays the loop used, 500ms then 1000ms, which retryDelayMs alone cannot show
* The 4xx test asserts the loop did not wait at all

Co-Authored-By: Claude <noreply@anthropic.com>
* checkApiKey reports a 429 as unconfirmed rather than rejected

Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves Panorama Public reminder scheduling and NCBI publication-search reliability.

Changes:

  • Restores reminder scheduling during application startup.
  • Adds NCBI retries and explicit search-failure handling.
  • Adds encrypted API-key configuration, validation, and tests.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
PublicationSearchTest.java Preserves reminder settings during testing.
PanoramaPublicBaseTest.java Adds API-key settings test helpers.
NcbiApiKeyTest.java Tests API-key management and validation.
privateDataRemindersSettingsForm.jsp Adds API-key controls and feedback.
PrivateDataReminderJob.java Handles key checks and search failures.
PanoramaPublicModule.java Schedules reminders during startup.
PanoramaPublicController.java Adds key validation and persistence.
NcbiSearchException.java Distinguishes failed searches.
NcbiPublicationSearchServiceImpl.java Adds retries, redaction, and key support.
NcbiPublicationSearchService.java Extends the NCBI service contract.
NcbiApiKeyCheck.java Models key-validation outcomes.
MockNcbiPublicationSearchService.java Updates mocked request handling.
PrivateDataReminderSettings.java Stores encrypted keys and stabilizes date tests.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +553 to +555
// 500ms after the first failure, then doubling. Jitter is not needed. Both callers, the daily
// reminder job and the UI search, issue NCBI requests sequentially, so a retry never collides
// with a sibling request.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. The UI search actions are request handlers and can overlap each other or the reminder job, so the comment claiming a retry never collides with a sibling request was wrong and has been corrected in bf13cb4

Not adding jitter. These are site-admin actions so collisions will be rare.

vagisha and others added 2 commits September 7, 2026 15:24
… job

* A failed NCBI request no longer abandons the remaining PMC strategies or the PubMed fallback
* NcbiPublicationSearchServiceImpl.checkApiKey treats only HTTP 400 as a rejected key
* PrivateDataReminderJob.run reports cancelled or error instead of complete when it stops early
* Failed NCBI requests log at WARN, leaving ERROR for the per-dataset and run summary
* NcbiApiKeyTest reads a new data-key-saved attribute rather than the field placeholder
* NcbiApiKeyTest restores the settings it overwrites and no longer requires NCBI to reject a key
* Corrected comments and javadoc, and small consistency cleanups

Co-Authored-By: Claude <noreply@anthropic.com>
…om log levels

* An ERROR through the job's logger sets the pipeline status, and the status set at the end
  overwrote it. run now computes it from what ProcessingResults recorded
* ERROR means the job could not start or a dataset was not sent a reminder. A failed publication
  search is WARN, since the reminder still goes out
* A publication search failing for more than half the searches that reached NCBI is an ERROR
* A missing support thread is an error only when the submission has an announcement id. The rest
  predate the message board and can never be reminded
* An unexpected exception on a dataset is now recorded and counted
* A citation that will not parse logs at WARN, since it does not fail the match
* Added PrivateDataReminderJob.TestCase for the error count, the failure rate boundary, and the
  announcement id split

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants