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

Code before:
    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 */
    writePointerWithPadding(buf_out + sizeof(uint64_t), node);
}

/* 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
 * 'sender' node.
 *
 * Returns 0:
 *   - Early exit when the node is already in FAIL state
 *   - The same sender has already reported within the same time bucket
 *   - An existing report from 'sender' was refreshed (timestamp updated)
 * Returns 1 if a brand new failure report entry is created. */
int clusterNodeAddFailureReport(clusterNode *failing, clusterNode *sender) {
    unsigned char buf[FAILURE_REPORT_KEYLEN];
    mstime_t now = mstime();
    const mstime_t bucketed_time = (now / SEC_IN_MS) * SEC_IN_MS + SEC_IN_MS;
    int is_new = 1;

    /* This avoids unnecessary iteration and memory ops, improving performance
     * when handling repeated reports for already failed nodes. */
    if (nodeFailed(failing)) return 0;

    /* Look for any existing entry from this sender and remove it */
    raxIterator ri;
    raxStart(&ri, failing->fail_reports);
    raxSeek(&ri, "^", NULL, 0);
    while (raxNext(&ri)) {
        mstime_t reported_time;
        clusterNode *reported_node;
        decodeFailureReportKey(ri.key, &reported_time, &reported_node);
        if (reported_node == sender) {
            if (reported_time == bucketed_time) {
                /* Early exit if it is the exact same sender and bucket */
                raxStop(&ri);

Review comment (bug):
```suggestion
 *   - The node is already in FAIL state
```

Code after:
 * -------------------------------------------------------------------------- */

/* Create a new cluster node, with the specified flags.
 * If "nodename" is NULL this is considered a first handshake and a random
 * node name is assigned to this node (it will be fixed later when we'll
 * receive the first pong).
 *
 * The node is created and returned to the user, but it is not automatically
 * added to the nodes hash table. */
clusterNode *createClusterNode(char *nodename, int flags) {
    clusterNode *node = zmalloc(sizeof(*node));

    if (nodename)
        memcpy(node->name, nodename, CLUSTER_NAMELEN);
    else
        getRandomHexChars(node->name, CLUSTER_NAMELEN);
    getRandomHexChars(node->shard_id, CLUSTER_NAMELEN);
    node->ctime = mstime();
    node->configEpoch = 0;
    node->flags = flags;
    memset(node->slots, 0, sizeof(node->slots));
    node->slot_info_pairs = NULL;
    node->slot_info_pairs_count = 0;
    node->numslots = 0;
    node->num_replicas = 0;
    node->replicas = NULL;
    node->replicaof = NULL;
    node->last_in_ping_gossip = 0;
    node->ping_sent = node->pong_received = 0;
    node->outbound_link_attempt_time = 0;
    node->data_received = 0;
    node->meet_sent = 0;
    node->fail_time = 0;
    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 = raxNew();
    node->orphaned_time = 0;
    node->repl_offset = 0;
    node->is_node_healthy = 0;
    return node;
}