Skip to content

fix: escalate peer read timeouts after max retries — GH#4777#4780

Open
balhar-jakub wants to merge 2 commits into
v3.x.xfrom
hermes/gh4777
Open

fix: escalate peer read timeouts after max retries — GH#4777#4780
balhar-jakub wants to merge 2 commits into
v3.x.xfrom
hermes/gh4777

Conversation

@balhar-jakub

Copy link
Copy Markdown
Member

Closes #4777

Problem

The peer-node-read-timeout-ms was too short (15s), causing infinite retry loops under load on z/OS. Read timeouts were never escalated to permanent errors.

Fix

  • Added networkIssueCounter.hasReachedMax() gate to read timeout handling in ApimlPeerEurekaNode
  • After maxPeerRetries consecutive read timeout failures → PermanentError (matching connection-failure behavior)
  • Increased default peer-node-read-timeout-ms from 15000 → 30000ms
  • Reset counter on successful replication
  • 4 new parametrized tests covering escalation, reset, and unchanged behavior

Files changed

  • apiml-common/.../ApimlPeerEurekaNode.java: hasReachedMax() gate in both process(task) and process(tasks)
  • discovery-service/.../application.yml: timeout 15000 → 30000
  • apiml/.../application.yml: timeout 15000 → 30000
  • apiml-common/.../ReplicationTaskProcessorTest.java: 4 new tests (19 total)

Build: PASS (19/19 tests)

@balhar-jakub

Copy link
Copy Markdown
Member Author

QA + Security Review (Pavel's Lens) — APPROVED ✓

Pavel's Lens Results

Rule Check Verdict
Rule 1: Config Consistency Both application.yml files updated identically (15000→30000) ✓ PASS
Rule 2: Deduplication hasReachedMax() gate in both process(task) and process(tasks) — minimal, necessary duplication ✓ PASS
Rule 3: Null Safety networkIssueCounter is initialized field; getCountText() returns StringBuilder.toString() (never null) ✓ PASS
Rule 4: Test Parametrization 4 standalone tests covering escalation, reset, regression ✓ PASS
Rule 5: Security Boundaries No auth/authz/TLS changes; error messages don't expose sensitive data ✓ PASS
Rule 6: z/OS Awareness Timeout increase (30s) specifically for z/OS batch replication latency ✓ PASS
Rule 7: Log Quality Clear log message with retry count; existing warning preserved ✓ PASS
Rule 8: TODO Tracking No new TODOs introduced ✓ PASS

Acceptance Criteria Verification

  • AC1 ✓: hasReachedMax() gate escalates to PermanentError after N retries in both methods — covered by tests
  • AC2 ✓: Successful replication resets counter — covered by whenSuccessfulReplicationResetsReadTimeoutCounter test
  • AC3 ✓: Connection failure behavior unchanged — covered by whenConnectionFailureRepeatedMultipleTimes_thenBehaviorUnchanged test
  • AC4 ✓: 4 tests covering escalation, reset, and unchanged behavior (19/19 pass)
  • AC5 ✓: Default timeout increased to 30000ms in both config files

Security Review

  • No sensitive data in error messages (only generic "read timed out" and retry count)
  • No new endpoints or auth changes
  • No secrets/credentials exposed
  • PermanentError drops task; peer catches up via registry sync — no DoS vector

Verdict: APPROVED — Ready for merge. All acceptance criteria met, all 8 Pavel rules passed, no security concerns.

Signed-off-by: Jakub Balhar <jakub@balhar.net>
Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com>
@balhar-jakub balhar-jakub marked this pull request as draft July 7, 2026 12:47
@EvaJavornicka EvaJavornicka moved this from New to In Progress in API Mediation Layer Backlog Management Jul 8, 2026
@balhar-jakub balhar-jakub marked this pull request as ready for review July 10, 2026 06:19
@sonarqubecloud

Copy link
Copy Markdown

networkIssueCounter.fail(e.getLocalizedMessage());
if (maybeReadTimeOut(e)) {
if (networkIssueCounter.hasReachedMax()) {
log.error("Socket read timeout has occurred repeatedly and reached the maximum retry count ({}). Batch replication tasks will be dropped as a permanent error and the peer will catch up via periodic registry sync.", networkIssueCounter.getCountText());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

these 2 log messages are too similar and it would make it harder to debug. I would rephrase one of them and maybe also print the exception message, at least in the debug level

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If we use it ourselves, wouldn't it make sense to assign it the standard log id according to standard? Even for the original one.

I will change the text so it is further away as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Peer node read timeout too short (15s) — causes cascading retry loop under load

3 participants