fix(client): bug-1436 count-aware failover macro for xce_get-family calls

xcecli_joblist/reslist/workerlist/resval/jobinfo return a non-negative ROW
COUNT on success (rc = getreply.nrows), not a 0/status code. They were
wrapped in XCECLI_FAILOVER_CALL, which unconditionally classifies the raw
return value against fixed constants (E_XCE_* and POSIX errno magnitudes
accepted in either sign) — a row count that happens to equal one of those
constants (e.g. exactly 111 jobs == ECONNREFUSED) was silently
misclassified as a transport failure, forcing a spurious
xce_failover_rotate() on every call with that exact count. In
/xce-test-ha's arithmetic progression (3 + 3*round) this hit at round 36,
burning the retry budget on self-inflicted churn until it coincided with
a genuine transient and surfaced a real ECONNRESET to the caller.

Add XCECLI_FAILOVER_CALL_COUNT: only enters the retry/classify loop when
EXPR's raw result is negative; any non-negative result (0 or a genuine
row count of any magnitude) is unconditionally success. Switch the five
xce_get-family wrappers to it. Verified via 40-round sequential and
40-round chaotic HA soak past round 36, zero committed-job loss.
This commit is contained in:
Rongsong Shen 2026-07-13 07:18:52 +00:00
parent 510b566bbf
commit 40c2351578
2 changed files with 79 additions and 10 deletions

View file

@ -283,8 +283,9 @@ int xcecli_reslist(xcecli_ctx_t c, xce_resource_t*** res)
return -EINVAL;
}
return XCECLI_FAILOVER_CALL(ctx, /*is_write=*/false,
xce_reslist(&(ctx->ctx), res));
/* xce_reslist returns a non-negative ROW COUNT on success, not a 0/status
* code — must go through the count-aware macro (bug-1436). */
return XCECLI_FAILOVER_CALL_COUNT(ctx, xce_reslist(&(ctx->ctx), res));
}
int xcecli_join(xcecli_ctx_t c, int role, hostattrs_t* attrs)
@ -305,8 +306,9 @@ int xcecli_workerlist(xcecli_ctx_t c, xce_worker_t*** workers)
return -EINVAL;
}
return XCECLI_FAILOVER_CALL(ctx, /*is_write=*/false,
xce_workerlist(&(ctx->ctx), workers));
/* xce_workerlist returns a non-negative ROW COUNT on success, not a
* 0/status code — must go through the count-aware macro (bug-1436). */
return XCECLI_FAILOVER_CALL_COUNT(ctx, xce_workerlist(&(ctx->ctx), workers));
}
int xcecli_jobdispatch(xcecli_ctx_t c, xce_job_dispatch_t* info)
@ -341,8 +343,14 @@ int xcecli_joblist(xcecli_ctx_t c, xce_job_t*** jobs)
return -EINVAL;
}
return XCECLI_FAILOVER_CALL(ctx, /*is_write=*/false,
xce_joblist(&(ctx->ctx), jobs));
/* xce_joblist returns a non-negative ROW COUNT on success, not a 0/status
* code — a count that happens to equal a retriable errno's magnitude
* (e.g. exactly 111 jobs == ECONNREFUSED) would be misclassified as a
* transport failure by the generic macro, forcing a spurious reconnect
* storm that eventually surfaces a real "Connection reset by peer" once
* it coincides with a genuine transient (bug-1436). Must go through the
* count-aware macro instead. */
return XCECLI_FAILOVER_CALL_COUNT(ctx, xce_joblist(&(ctx->ctx), jobs));
}
int xcecli_clusterconf(xcecli_ctx_t c, xce_clusterconf_t** conf)
@ -583,8 +591,9 @@ int xcecli_resval(xcecli_ctx_t c, char* host, char* res, xce_resval_t*** val)
return -EINVAL;
}
return XCECLI_FAILOVER_CALL(ctx, /*is_write=*/false,
xce_resval(&(ctx->ctx), host, res, val));
/* xce_resval returns a non-negative ROW COUNT on success, not a 0/status
* code — must go through the count-aware macro (bug-1436). */
return XCECLI_FAILOVER_CALL_COUNT(ctx, xce_resval(&(ctx->ctx), host, res, val));
}
int xcecli_notify(xcecli_ctx_t c, xce_notify_data_t* data)
@ -913,7 +922,12 @@ int xcecli_jobinfo(xcecli_ctx_t c, uint64_t jobid, xce_job_t** job)
return -EINVAL;
}
rc = XCECLI_FAILOVER_CALL(ctx, /*is_write=*/false,
xce_jobinfo(&(ctx->ctx), jobid, job));
/* xce_jobinfo returns 1 (found) / 0 (not found) / negative (error) — a
* fixed non-zero success value, not a 0/status code. 1 never happens to
* collide with a recognized retriable magnitude today, but it is the same
* class of bug as bug-1436 (xcecli_joblist) — go through the count-aware
* macro for correctness and consistency rather than relying on that
* coincidence. */
rc = XCECLI_FAILOVER_CALL_COUNT(ctx, xce_jobinfo(&(ctx->ctx), jobid, job));
return rc;
}