Repository: apache/kafka
File: raft/src/main/java/org/apache/kafka/raft/FollowerState.java (Java)

Code before:
            .address(listener)
            .map(address -> new Node(leaderId, address.getHostString(), address.getPort()))
            .orElseThrow(() ->
                new IllegalArgumentException(
                    String.format(
                        "Unknown endpoint for leader %d and listener %s, known endpoints are %s",
                        leaderId,
                        listener,
                        leaderEndpoints
                    )
                )
            );
    }

    public boolean hasFetchTimeoutExpired(long currentTimeMs) {
        fetchTimer.update(currentTimeMs);
        return fetchTimer.isExpired();
    }

    public void resetFetchTimeoutForSuccessfulFetch(long currentTimeMs) {
        fetchTimer.update(currentTimeMs);
        fetchTimer.reset(fetchTimeoutMs);
        hasFetchedFromLeader = true;
    }

    public void resetFetchTimeoutAfterDiscoveringLeader(long currentTimeMs) {
        fetchTimer.update(currentTimeMs);
        fetchTimer.reset(fetchTimeoutMs);
    }

    public void overrideFetchTimeout(long currentTimeMs, long timeoutMs) {
        fetchTimer.update(currentTimeMs);
        fetchTimer.reset(timeoutMs);
    }

    private long updateVoterPeriodMs() {
        // Allow for a few rounds of fetch request before attempting to update
        // the voter state
        return fetchTimeoutMs;
    }

    public boolean hasUpdateVoterPeriodExpired(long currentTimeMs) {
        updateVoterPeriodTimer.update(currentTimeMs);
        return updateVoterPeriodTimer.isExpired();
    }

    public long remainingUpdateVoterPeriodMs(long currentTimeMs) {
        updateVoterPeriodTimer.update(currentTimeMs);
        return updateVoterPeriodTimer.remainingMs();
    }


Review comment (suggestion):
Let's document these 3 methods: `resetFetchTimeoutForSuccessfulFetch`, `resetFetchTimeoutAfterDiscoveringLeader` and `overrideFetchTimeout`.

How about removing code duplication with:
```java
    public void resetFetchTimeoutForSuccessfulFetch(long currentTimeMs) {
        resetFetchTimeout(currentTimeMs);
        hasFetchedFromLeader = true;
    }

    public void resetFetchTimeout(long currentTimeMs) {
       overrideFetchTimeout(currentTimeMs, fetchTimeoutMs);
    }
```

Code after:
            .address(listener)
            .map(address -> new Node(leaderId, address.getHostString(), address.getPort()))
            .orElseThrow(() ->
                new IllegalArgumentException(
                    String.format(
                        "Unknown endpoint for leader %d and listener %s, known endpoints are %s",
                        leaderId,
                        listener,
                        leaderEndpoints
                    )
                )
            );
    }

    public boolean hasFetchTimeoutExpired(long currentTimeMs) {
        fetchTimer.update(currentTimeMs);
        return fetchTimer.isExpired();
    }

    /**
     * Reset the fetch timeout after successful fetch from leader.
     */
    public void resetFetchTimeoutForSuccessfulFetch(long currentTimeMs) {
        overrideFetchTimeout(currentTimeMs, fetchTimeoutMs);
        hasFetchedFromLeader = true;
    }

    /**
     * Override the fetch timeout to a specific value. This is useful for short-circuiting followers' timeouts after
     * they receive end quorum requests
     */
    public void overrideFetchTimeout(long currentTimeMs, long timeoutMs) {
        fetchTimer.update(currentTimeMs);
        fetchTimer.reset(timeoutMs);
    }

    private long updateVoterPeriodMs() {
        // Allow for a few rounds of fetch request before attempting to update
        // the voter state
        return fetchTimeoutMs;
    }

    public boolean hasUpdateVoterPeriodExpired(long currentTimeMs) {
        updateVoterPeriodTimer.update(currentTimeMs);
        return updateVoterPeriodTimer.isExpired();
    }

    public long remainingUpdateVoterPeriodMs(long currentTimeMs) {
        updateVoterPeriodTimer.update(currentTimeMs);
        return updateVoterPeriodTimer.remainingMs();
    }