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

Code before:
    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 */
#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(unsigned char *buf, mstime_t report_time, clusterNode *node) {
    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 + CEIL_OFFSET_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, &big_endian_time, sizeof(uint64_t));

    /* Append the node pointer, plus padding if necessary */
    memcpy(buf + sizeof(uint64_t), &node, sizeof(node));
    if (node_ptr_pad_bytes) memset(buf + 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

Review comment (suggestion):
```suggestion
    mstime_t bucketed_time = report_time / SEC_IN_MS * SEC_IN_MS + 1;
```
I don't understand why we chose to round up, as opposed to down then plus one to force it into the next bucket for the grace. This is mostly a nitpick, it's just a bit more compact.

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