Repository: valkey-io/valkey
File: src/cluster_legacy.c (C)

Code before:
    node->link = NULL;
    node->inbound_link = NULL;
    node->inbound_link_freed_time = node->ctime;
    memset(node->ip, 0, sizeof(node->ip));
    node->announce_client_ipv4 = sdsempty();
    node->announce_client_ipv6 = sdsempty();
    node->hostname = sdsempty();
    node->human_nodename = sdsempty();
    node->tcp_port = 0;
    node->cport = 0;
    node->tls_port = 0;
    node->fail_reports = listCreate();
    node->orphaned_time = 0;
    node->repl_offset = 0;
    listSetFreeMethod(node->fail_reports, zfree);
    node->is_node_healthy = 0;
    return node;
}

/* This function is called every time we get a failure report from a node.
 * The side effect is to populate the fail_reports list (or to update
 * the timestamp of an existing report).
 *
 * 'failing' is the node that is in failure state according to the
 * 'sender' node.
 *
 * The function returns 0 if it just updates a timestamp of an existing
 * failure report from the same sender. 1 is returned if a new failure
 * report is created. */
int clusterNodeAddFailureReport(clusterNode *failing, clusterNode *sender) {
    list *l = failing->fail_reports;
    listNode *ln;
    listIter li;
    clusterNodeFailReport *fr;

    /* If a failure report from the same sender already exists, just update
     * the timestamp. */
    listRewind(l, &li);
    mstime_t now = mstime();
    while ((ln = listNext(&li)) != NULL) {
        fr = ln->value;
        if (fr->node == sender) {
            fr->time = now;
            return 0;
        }
    }

    /* Otherwise create a new report. */
    fr = zmalloc(sizeof(*fr));
    fr->node = sender;
    fr->time = now;

Review comment (bug):
I am thinking if we can ignore the hour part and tens of minutes in the timestamp (HH:MM:SS) to create fewer nodes in the radix tree. In some corner cases, say if the report for a failed node is received at 12:59:59, then next report will have a lot of digits changed. if it sounds valid, I can create an issue for this as well.

wdyt @hpatro @sungming2?



Code after:
    node->announce_client_ipv6 = sdsempty();
    node->hostname = sdsempty();
    node->human_nodename = sdsempty();
    node->tcp_port = 0;
    node->cport = 0;
    node->tls_port = 0;
    node->fail_reports = raxNew();
    node->orphaned_time = 0;
    node->repl_offset = 0;
    node->is_node_healthy = 0;
    return node;
}

/* 8 bytes mstime + 8 bytes cluster node pointer (padded with zeros on 32-bit systems). */
#define FAILURE_REPORT_KEYLEN 16
#define SEC_IN_MS 1000ULL
#define CEIL_OFFSET_MS (SEC_IN_MS - 1)

/* The report time is rounded up (ceiled) to the nearest second in order to bucket
 * timestamps and keep the radix‐tree keys compact.  The resulting timestamp is stored
 * in big‑endian format, followed by the pointer to the clusterNode. */
static void encodeFailureReportKey(clusterNode *node, mstime_t report_time, unsigned char *buf_out) {
    const size_t node_ptr_pad_bytes = (sizeof(clusterNode *) == 4) ? 4 : 0; // pad on 32-bit

    /* Round up to the next second for fewer key splits and quorum grace */
    mstime_t bucketed_time = (report_time / SEC_IN_MS) * SEC_IN_MS + SEC_IN_MS;

    /* Big endian timestamp for numeric time order in rax */
    uint64_t big_endian_time = htonu64((uint64_t)bucketed_time);
    memcpy(buf_out, &big_endian_time, sizeof(uint64_t));

    /* Append the node pointer, plus padding if necessary */
    memcpy(buf_out + sizeof(uint64_t), &node, sizeof(node));
    if (node_ptr_pad_bytes) memset(buf_out + sizeof(uint64_t) + sizeof(node), 0, node_ptr_pad_bytes);
}

/* Reverses the operation of encodeFailureReportKey, reading back a timestamp
 * and the pointer to the associated clusterNode. */
static void decodeFailureReportKey(unsigned char *buf, mstime_t *report_time, clusterNode **node_ptr) {
    uint64_t big_endian_time;
    memcpy(&big_endian_time, buf, sizeof(uint64_t));
    *report_time = (mstime_t)ntohu64(big_endian_time);
    memcpy(node_ptr, buf + sizeof(uint64_t), sizeof(*node_ptr));
}

/* This function is called every time we get a failure report from a node.
 * We maintain fail_reports as a radix tree keyed by bucketed timestamp
 * (ceiled to the next second) and the sender pointer, which naturally orders
 * entries by time.
 *
 * 'failing' is the node that is in failure state according to the