myxce/src/include/request.h
Rongsong Shen 7b39650229 refactor(ha): passive standby model — primary connects outbound to all peers
Remove fetch_config_from_primary, ha_find_and_connect_to_primary, and the
tier-1/2/3 discovery path. Standbys never initiate outbound connections to
the primary; the primary owns all inter-controller channels.

Key changes:
- ha.c: add primary heartbeat loop (ha_start_primary_heartbeat) that sends
  OPCode_nop to connected standbys every heartbeat_interval_ms; loop
  self-cancels on demotion via role check before re-arm.
- ha.c: add outbound reconnect in primary_heartbeat_fn — for peers absent
  from standby_endpoints, open fresh outbound TLS connection (on_standby_connected
  sends ctrlhello; standby_ep_ctx_t mirrors vote_ep_ctx_t layout).
- ha.c: remove ha_find_and_connect_to_primary + helpers (ha_raw_tcp_connect,
  ha_tcp_connect_hook); remove election_timeout_fn (superseded by
  raft_election_timeout_fn); remove fetch_config_from_primary call from
  ha_elect_evaluate_and_signal STANDBY branch.
- cluster.c: OPCode_requestVote denial no longer reuses inbound connection;
  late joiners are bootstrapped via primary heartbeat outbound connect.
- config_repl.c: remove connect_ssl_raw, do_one_config_fetch,
  fetch_config_from_primary (~250 LOC).
- myapp.c: remove my_tag > 0 fetch branch; all standbys wait passively.
- ha.h: remove ha_find_and_connect_to_primary declaration + macros +
  ha_tcp_connect_hook seam; add ha_start_primary_heartbeat declaration.
- meson.build: remove test_ha_connect.c (tests deleted function).
- controller_test.c: remove test_ha_connect registration.
- CLAUDE.md, src/controller/CLAUDE.md: document passive standby model,
  inbound-vs-outbound connection ownership, async I/O constraint,
  heartbeat model.
- request.h: add xce_request declaration (removes scattered local externs).
2026-06-01 04:17:05 +00:00

177 lines
7.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#if !defined(_REQUEST_H_)
#define _REQUEST_H_ 1
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <openssl/ssl.h>
#include "netio.h"
#include "typemap.h"
#include "utils.h"
#include "xce/types.h"
typedef struct {
int wait;
} reqbar_t;
typedef int (*xce_check_fn_t)(void*);
extern int xce_request(endpoint_t *ep, xce_request_t *req);
extern int xce_encode(xce_request_t*, io_packet_t*);
extern int xce_decode(io_packet_t*, xce_request_t*);
extern int xce_reply(endpoint_t*, xce_peerinfo_t*, xce_reply_t*);
extern int xce_hello_req(endpoint_t*, char*);
extern int xce_nop_req(endpoint_t*);
extern int xce_shutdown_req(endpoint_t*);
extern void init_reply_by_req(xce_reply_t*, xce_request_t*);
extern int xce_join_req(endpoint_t*, int, hostattrs_t*);
extern int xce_pushconfig_req(endpoint_t*, xce_peerinfo_t*, xce_configdata_t*);
extern int xce_push_data_req(endpoint_t*, xce_peerinfo_t*, xce_data_t*,
xce_check_fn_t, void*, int);
extern int xce_workerlist_req(endpoint_t*, xce_peerinfo_t*);
extern int xce_reply_workerlist(endpoint_t*, uint64_t, int, xce_peerinfo_t*,
xce_workerlist_t*);
extern int xce_jobdispatch_req(endpoint_t*, xce_job_dispatch_t*);
extern int xce_jobsubmit_req(endpoint_t*, xce_job_submit_t*);
extern int xce_reply_jobdispatch(endpoint_t*, uint64_t, int32_t,
xce_peerinfo_t*);
extern int xce_reply_jobid(endpoint_t*, int, uint64_t, uint64_t,
xce_peerinfo_t*);
extern int xce_joblist_req(endpoint_t*, xce_peerinfo_t*);
extern int xce_reply_joblist(endpoint_t*, uint64_t, int, xce_peerinfo_t*,
xce_job_list_t*);
extern int xce_startjob_req(endpoint_t*, xce_job_exec_t*);
extern int xce_clusterconf_req(endpoint_t*);
extern int xce_reply_clusterconf(endpoint_t*, uint64_t, int, xce_peerinfo_t*,
xce_clusterconf_t*);
extern int xce_get_req(endpoint_t*, xce_get_t*);
extern int xce_reply_get(endpoint_t*, xce_peerinfo_t*, xce_reply_t*,
xce_check_fn_t, void*, int);
extern int xce_notify_req(endpoint_t*, xce_notify_data_t*);
extern int extract_data(dataset_t*, xce_data_t*);
extern int xce_update_req(endpoint_t*, xce_update_data_t*);
extern int xce_jobctrl_req(endpoint_t*, xce_jobctrl_t*);
extern int xce_queuectrl_req(endpoint_t*, xce_queuectrl_t*);
extern int xce_hostctrl_req(endpoint_t*, xce_hostctrl_t*);
extern int xce_runtask_req(endpoint_t*, xce_taskrun_t*);
extern int xce_set_pend_reason_req(endpoint_t*, xce_preason_set_t*);
extern int xce_clear_reason_req(endpoint_t*, xce_clear_reason_t*);
/*
* xce_replconfigpush_req_raw: send OP_replConfigPush carrying raw byte arrays.
* Bypasses the encode_ReplConfigPushRequest_ptr path (which maps sha256/bytes
* through xce_data_t — wrong type for raw bytes) by constructing the capnp
* message directly via capn_new_list8 + capn_setv8 + write_Request.
*/
extern int xce_replconfigpush_req_raw(endpoint_t *ep, xce_peerinfo_t *from,
uint64_t version_tag,
const uint8_t *sha256, size_t sha256_len,
const uint8_t *config_bytes,
size_t config_len);
/*
* xce_replconfig_reply_raw: primary sends an OP_replConfig reply with
* actual config bytes via endpoint (iomgr queued). Bypasses
* encode_ReplConfigReply_ptr (which maps sha256/bytes through xce_data_t).
*/
extern int xce_replconfig_reply_raw(endpoint_t *ep, xce_peerinfo_t *from,
uint64_t seq, uint64_t tag,
const uint8_t *sha256, size_t sha256_len,
const uint8_t *config, size_t config_len);
/*
* xce_replconfig_req_ssl: standby sends OP_replConfig over a raw SSL
* socket (no iomgr). Wire framing: uint64_t size + capnp payload
* (matches netio.c io_fsm_perform).
*/
extern int xce_replconfig_req_ssl(SSL *ssl, uint64_t version_tag);
/*
* xce_replconfig_reply_ssl_decode: standby reads + parses an
* OP_replConfig reply from a raw SSL socket. Output buffers are
* malloc'd and owned by the caller. Both *out_sha256 and *out_bytes
* may be NULL on the up_to_date == 1 fast path.
*/
extern int xce_replconfig_reply_ssl_decode(SSL *ssl,
int *out_up_to_date,
uint64_t *out_tag,
uint8_t **out_sha256,
uint8_t **out_bytes,
size_t *out_len);
/*
* xce_free_request: free an xce_request_t and all owned sub-fields,
* including the raw_sha256/raw_bytes fields on upgrade_config_req_t
* that the generated free_Request() does not know about.
* Use this everywhere instead of calling free_Request() directly.
*/
extern void xce_free_request(xce_request_t *req);
/*
* xce_upgrade_config_req_extract: extract sha256 + bytes from a decoded
* OP_upgradeConfig request. xce_decode() stores malloc'd copies in
* req->body.upgradeconfigrequestmsg->raw_sha256 / raw_bytes before
* capn_free() runs. Returns shallow pointers (owned by req); caller
* must NOT free them. Returns -EINVAL if fields are missing.
*/
extern int xce_upgrade_config_req_extract(const xce_request_t *req,
uint8_t **out_bytes,
size_t *out_len,
uint8_t **out_sha256);
/*
* xce_upgrade_config_req_extract_raw: parse OP_upgradeConfig request
* bytes from a raw on-wire packet buffer. Output buffers (*out_bytes,
* *out_sha256) are malloc'd and owned by the caller.
*/
extern int xce_upgrade_config_req_extract_raw(const uint8_t *pkt_data,
size_t pkt_sz,
uint8_t **out_bytes,
size_t *out_len,
uint8_t **out_sha256);
/* ── Task 10.2: Client-Side Transparent Retry (Req 7.1–7.6) ─────────────────
*
* xce_retry_config_t: configurable retry parameters with safe production defaults.
* Pass NULL to xce_request_with_retry() to use defaults.
*/
typedef struct {
int max_retries; /* default 20 */
int initial_retry_ms; /* default 50 */
int max_retry_ms; /* default 5000 */
double backoff_multiplier; /* default 2.0 (exponential) */
} xce_retry_config_t;
extern const xce_retry_config_t XCE_RETRY_DEFAULTS;
/*
* xce_request_with_retry: transparent retry wrapper for transient controller errors.
*
* - XCE_ERR_NOT_READY → backoff, retry same endpoint (Req 7.1)
* - XCE_ERR_STANDBY with redirect_host/port → reconnect, retry (Req 7.2)
* - XCE_ERR_STANDBY without redirect → backoff, retry (Req 7.2 fallback)
* - ECONNREFUSED / ETIMEDOUT → reconnect, retry (Req 7.3)
* - EPERM / EINVAL / ENOENT / EACCES → surface immediately (Req 7.4)
* - Exhausted max_retries → surface last error (Req 7.5)
* - Backoff jitter via rand_r() to prevent thundering herd (Req 7.6)
*
* NULL cfg → uses XCE_RETRY_DEFAULTS.
*/
extern int xce_request_with_retry(endpoint_t *ep,
xce_request_t *req,
const xce_retry_config_t *cfg);
/*
* xce_retry_send_hook: test-only injection point for the send+receive round-trip.
* When non-NULL, xce_request_with_retry() calls this instead of the real network path.
* Returns 0 on success (reply_out populated), -errno on transport failure.
*/
extern int (*xce_retry_send_hook)(endpoint_t *ep, xce_request_t *req,
xce_reply_t *reply_out);
#endif