Skip to content

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

Description

@balhar-jakub

Problem

The peer-node-read-timeout-ms is set to 15,000ms (15 seconds) in discovery-service/src/main/resources/application.yml. Under load — especially on z/OS — peer batch replication calls can exceed this timeout, triggering a cascading retry loop that the code itself warns about.

Root cause

ApimlPeerEurekaNode.ReplicationTaskProcessor catches read timeouts via maybeReadTimeOut() and returns ProcessingResult.Congestion. The Netflix batcher then waits only 1,000ms (SERVER_UNAVAILABLE_SLEEP_TIME_MS) before retrying. Read timeouts are never escalated to permanent errors — unlike network connection failures, which are gated by maxPeerRetries.

Retry cycle: 15s timeout → 1s delay → 15s timeout → 1s delay → … forever

With max-threads-for-peer-replication: 6, all six threads can be stuck in this loop simultaneously, leaving no capacity for replication to other peers.

The code explicitly warns about this

// ApimlPeerEurekaNode.java lines 421 and 458
log.error("It seems to be a socket read timeout exception, it will retry later. "
    + "if it continues to happen and some eureka node occupied all the cpu time, "
    + "you should set property 'eureka.server.peer-node-read-timeout-ms' to a bigger value", e);

The authors anticipated this exact scenario.

Why read timeouts never stop

// Lines 418-431
} catch (Throwable e) {
    networkIssueCounter.fail(e.getLocalizedMessage());
    if (maybeReadTimeOut(e)) {
        return ProcessingResult.Congestion;  // ← ALWAYS retries, never PermanentError
    } else if (isNetworkConnectException(e) && !networkIssueCounter.hasReachedMax()) {
        return ProcessingResult.TransientError;
    } else {
        return ProcessingResult.PermanentError;  // ← other errors DO escalate
    }
}

Affected properties

Property Default Location
eureka.server.peer-node-read-timeout-ms 15000 discovery-service/src/main/resources/application.yml:97, apiml/src/main/resources/application.yml:19
eureka.server.max-threads-for-peer-replication 6 Same files

Workaround (zowe.yaml)

These properties can be overridden through zowe.environments in zowe.yaml, since Spring Boot picks up environment variables via relaxed binding:

zowe:
  environments:
    EUREKA_SERVER_PEER_NODE_READ_TIMEOUT_MS: 30000
    EUREKA_SERVER_MAX_THREADS_FOR_PEER_REPLICATION: 10

Note: zowe.environments is global to all Zowe components. There is no component-specific escape hatch for arbitrary environment variables. However, only the Discovery Service consumes eureka.server.* properties, so the global scope has no unintended effects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status
    Planned In Future

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions