// File: /workspace/github_c_makefile_star2_O0/nilyang[P]redis-tdd-annotation/src/cluster.c
// Function: clusterHandleSlaveFailover
void clusterHandleSlaveFailover(void) {
    mstime_t data_age;
    mstime_t auth_age = mstime() - server.cluster->failover_auth_time;
    int needed_quorum = (server.cluster->size / 2) + 1;
    int manual_failover = server.cluster->mf_end != 0 &&
                          server.cluster->mf_can_start;
    int j;
    mstime_t auth_timeout, auth_retry_time;

    server.cluster->todo_before_sleep &= ~CLUSTER_TODO_HANDLE_FAILOVER;

    /* Compute the failover timeout (the max time we have to send votes
     * and wait for replies), and the failover retry time (the time to wait
     * before waiting again.
     *
     * Timeout is MIN(NODE_TIMEOUT*2,2000) milliseconds.
     * Retry is two times the Timeout.
     */
    auth_timeout = server.cluster_node_timeout*2;
    if (auth_timeout < 2000) auth_timeout = 2000;
    auth_retry_time = auth_timeout*2;

    /* Pre conditions to run the function, that must be met both in case
     * of an automatic or manual failover:
     * 1) We are a slave.
     * 2) Our master is flagged as FAIL, or this is a manual failover.
     * 3) It is serving slots. */
    if (nodeIsMaster(myself) ||
        myself->slaveof == NULL ||
        (!nodeFailed(myself->slaveof) && !manual_failover) ||
        myself->slaveof->numslots == 0) return;

    /* Set data_age to the number of seconds we are disconnected from
     * the master. */
    // 将 data_age 设置为从节点与主节点的断开秒数
    if (server.repl_state == REDIS_REPL_CONNECTED) {
        data_age = (mstime_t)(server.unixtime - server.master->lastinteraction)
                   * 1000;
    } else {
        data_age = (mstime_t)(server.unixtime - server.repl_down_since) * 1000;
    }

    /* Remove the node timeout from the data age as it is fine that we are
     * disconnected from our master at least for the time it was down to be
     * flagged as FAIL, that's the baseline. */
    // node timeout 的时间不计入断线时间之内
    if (data_age > server.cluster_node_timeout)
        data_age -= server.cluster_node_timeout;

    /* Check if our data is recent enough. For now we just use a fixed
     * constant of ten times the node timeout since the cluster should
     * react much faster to a master down.
     *
     * Check bypassed for manual failovers. */
    // 检查这个从节点的数据是否较新：
    // 目前的检测办法是断线时间不能超过 node timeout 的十倍
    if (data_age >
        ((mstime_t)server.repl_ping_slave_period * 1000) +
        (server.cluster_node_timeout * REDIS_CLUSTER_SLAVE_VALIDITY_MULT))
    {
        if (!manual_failover) return;
    }

    /* If the previous failover attempt timedout and the retry time has
     * elapsed, we can setup a new one. */
    if (auth_age > auth_retry_time) {
        server.cluster->failover_auth_time = mstime() +
            500 + /* Fixed delay of 500 milliseconds, let FAIL msg propagate. */
            random() % 500; /* Random delay between 0 and 500 milliseconds. */
        server.cluster->failover_auth_count = 0;
        server.cluster->failover_auth_sent = 0;
        server.cluster->failover_auth_rank = clusterGetSlaveRank();
        /* We add another delay that is proportional to the slave rank.
         * Specifically 1 second * rank. This way slaves that have a probably
         * less updated replication offset, are penalized. */
        server.cluster->failover_auth_time +=
            server.cluster->failover_auth_rank * 1000;
        /* However if this is a manual failover, no delay is needed. */
        if (server.cluster->mf_end) {
            server.cluster->failover_auth_time = mstime();
            server.cluster->failover_auth_rank = 0;
        }
        redisLog(REDIS_WARNING,
            "Start of election delayed for %lld milliseconds "
            "(rank #%d, offset %lld).",
            server.cluster->failover_auth_time - mstime(),
            server.cluster->failover_auth_rank,
            replicationGetSlaveOffset());
        /* Now that we have a scheduled election, broadcast our offset
         * to all the other slaves so that they'll updated their offsets
         * if our offset is better. */
        clusterBroadcastPong(CLUSTER_BROADCAST_LOCAL_SLAVES);
        return;
    }

    /* It is possible that we received more updated offsets from other
     * slaves for the same master since we computed our election delay.
     * Update the delay if our rank changed.
     *
     * Not performed if this is a manual failover. */
    if (server.cluster->failover_auth_sent == 0 &&
        server.cluster->mf_end == 0)
    {
        int newrank = clusterGetSlaveRank();
        if (newrank > server.cluster->failover_auth_rank) {
            long long added_delay =
                (newrank - server.cluster->failover_auth_rank) * 1000;
            server.cluster->failover_auth_time += added_delay;
            server.cluster->failover_auth_rank = newrank;
            redisLog(REDIS_WARNING,
                "Slave rank updated to #%d, added %lld milliseconds of delay.",
                newrank, added_delay);
        }
    }

    /* Return ASAP if we can't still start the election. */
    // 如果执行故障转移的时间未到，先返回
    if (mstime() < server.cluster->failover_auth_time) return;

    /* Return ASAP if the election is too old to be valid. */
    // 如果距离应该执行故障转移的时间已经过了很久
    // 那么不应该再执行故障转移了（因为可能已经没有需要了）
    // 直接返回
    if (auth_age > auth_timeout) return;

    /* Ask for votes if needed. */
    // 向其他节点发送故障转移请求
    if (server.cluster->failover_auth_sent == 0) {

        // 增加配置纪元
        server.cluster->currentEpoch++;

        // 记录发起故障转移的配置纪元
        server.cluster->failover_auth_epoch = server.cluster->currentEpoch;

        redisLog(REDIS_WARNING,"Starting a failover election for epoch %llu.",
            (unsigned long long) server.cluster->currentEpoch);

        // 向其他所有节点发送信息，看它们是否支持由本节点来对下线主节点进行故障转移
        clusterRequestFailoverAuth();

        // 打开标识，表示已发送信息
        server.cluster->failover_auth_sent = 1;

        // TODO:
        // 在进入下个事件循环之前，执行：
        // 1）保存配置文件
        // 2）更新节点状态
        // 3）同步配置
        clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
                             CLUSTER_TODO_UPDATE_STATE|
                             CLUSTER_TODO_FSYNC_CONFIG);
        return; /* Wait for replies. */
    }

    /* Check if we reached the quorum. */
    // 如果当前节点获得了足够多的投票，那么对下线主节点进行故障转移
    if (server.cluster->failover_auth_count >= needed_quorum) {
        // 旧主节点
        clusterNode *oldmaster = myself->slaveof;

        redisLog(REDIS_WARNING,
            "Failover election won: I'm the new master.");

        /* We have the quorum, perform all the steps to correctly promote
         * this slave to a master.
         *
         * 1) Turn this node into a master. 
         *    将当前节点的身份由从节点改为主节点
         */
        clusterSetNodeAsMaster(myself);
        // 让从节点取消复制，成为新的主节点
        replicationUnsetMaster();

        /* 2) Claim all the slots assigned to our master. */
        // 接收所有主节点负责处理的槽
        for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) {
            if (clusterNodeGetSlotBit(oldmaster,j)) {
                // 将槽设置为未分配的
                clusterDelSlot(j);
                // 将槽的负责人设置为当前节点
                clusterAddSlot(myself,j);
            }
        }

        /* 3) Update my configEpoch to the epoch of the election. */
        // 更新集群配置纪元
        myself->configEpoch = server.cluster->failover_auth_epoch;

        /* 4) Update state and save config. */
        // 更新节点状态
        clusterUpdateState();
        // 并保存配置文件
        clusterSaveConfigOrDie(1);

        /* 5) Pong all the other nodes so that they can update the state
         *    accordingly and detect that we switched to master role. */
        // 向所有节点发送 PONG 信息
        // 让它们可以知道当前节点已经升级为主节点了
        clusterBroadcastPong(CLUSTER_BROADCAST_ALL);

        /* 6) If there was a manual failover in progress, clear the state. */
        // 如果有手动故障转移正在执行，那么清理和它有关的状态
        resetManualFailover();
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x70, %rsp
callq 0x252e0
leaq 0xa62a4(%rip), %rcx     # 0x1138f8
movq 0x7d0(%rcx), %rcx
subq 0x60030(%rcx), %rax
movq %rax, -0x10(%rbp)
leaq 0xa628b(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl 0x14(%rax), %eax
movl $0x2, %ecx
cltd
idivl %ecx
addl $0x1, %eax
movl %eax, -0x14(%rbp)
leaq 0xa626c(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rcx
xorl %eax, %eax
cmpq $0x0, 0x60050(%rcx)
movb %al, -0x49(%rbp)
je 0x6d6bd
leaq 0xa624f(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
cmpl $0x0, 0x60068(%rax)
setne %al
movb %al, -0x49(%rbp)
movb -0x49(%rbp), %al
andb $0x1, %al
movzbl %al, %eax
movl %eax, -0x18(%rbp)
leaq 0xa6229(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl 0x60078(%rax), %ecx
andl $-0x2, %ecx
movl %ecx, 0x60078(%rax)
leaq 0xa620c(%rip), %rax     # 0x1138f8
movq 0x7c0(%rax), %rax
shlq %rax
movq %rax, -0x28(%rbp)
cmpq $0x7d0, -0x28(%rbp)     # imm = 0x7D0
jge 0x6d70c
movq $0x7d0, -0x28(%rbp)     # imm = 0x7D0
movq -0x28(%rbp), %rax
shlq %rax
movq %rax, -0x30(%rbp)
movq 0xba85a(%rip), %rax     # 0x127f78
movl 0x30(%rax), %eax
andl $0x1, %eax
cmpl $0x0, %eax
jne 0x6d770
movq 0xba848(%rip), %rax     # 0x127f78
cmpq $0x0, 0x850(%rax)
je 0x6d770
movq 0xba837(%rip), %rax     # 0x127f78
movq 0x850(%rax), %rax
movl 0x30(%rax), %eax
andl $0x8, %eax
cmpl $0x0, %eax
jne 0x6d759
cmpl $0x0, -0x18(%rbp)
je 0x6d770
movq 0xba818(%rip), %rax     # 0x127f78
movq 0x850(%rax), %rax
cmpl $0x0, 0x840(%rax)
jne 0x6d775
jmp 0x6dc4f
leaq 0xa617c(%rip), %rax     # 0x1138f8
cmpl $0x5, 0x674(%rax)
jne 0x6d7b5
leaq 0xa616c(%rip), %rax     # 0x1138f8
movq 0x798(%rax), %rax
leaq 0xa615e(%rip), %rcx     # 0x1138f8
movq 0x660(%rcx), %rcx
subq 0x80(%rcx), %rax
imulq $0x3e8, %rax, %rax      # imm = 0x3E8
movq %rax, -0x8(%rbp)
jmp 0x6d7dc
leaq 0xa613c(%rip), %rax     # 0x1138f8
movq 0x798(%rax), %rax
leaq 0xa612e(%rip), %rcx     # 0x1138f8
subq 0x6b0(%rcx), %rax
imulq $0x3e8, %rax, %rax      # imm = 0x3E8
movq %rax, -0x8(%rbp)
movq -0x8(%rbp), %rax
leaq 0xa6111(%rip), %rcx     # 0x1138f8
cmpq 0x7c0(%rcx), %rax
jle 0x6d809
leaq 0xa6101(%rip), %rax     # 0x1138f8
movq 0x7c0(%rax), %rcx
movq -0x8(%rbp), %rax
subq %rcx, %rax
movq %rax, -0x8(%rbp)
movq -0x8(%rbp), %rax
leaq 0xa60e4(%rip), %rcx     # 0x1138f8
movslq 0x5f8(%rcx), %rcx
imulq $0x3e8, %rcx, %rcx      # imm = 0x3E8
leaq 0xa60cf(%rip), %rdx     # 0x1138f8
imulq $0xa, 0x7c0(%rdx), %rdx
addq %rdx, %rcx
cmpq %rcx, %rax
jle 0x6d846
cmpl $0x0, -0x18(%rbp)
jne 0x6d844
jmp 0x6dc4f
jmp 0x6d846
movq -0x10(%rbp), %rax
cmpq -0x30(%rbp), %rax
jle 0x6d9d1
callq 0x252e0
addq $0x1f4, %rax            # imm = 0x1F4
movq %rax, -0x58(%rbp)
callq 0x1f9d0
movq -0x58(%rbp), %rcx
movl $0x1f4, %esi            # imm = 0x1F4
cqto
idivq %rsi
addq %rdx, %rcx
leaq 0xa6078(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movq %rcx, 0x60030(%rax)
leaq 0xa6063(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl $0x0, 0x60038(%rax)
leaq 0xa604b(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl $0x0, 0x6003c(%rax)
callq 0x6d550
movl %eax, %ecx
leaq 0xa602c(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl %ecx, 0x60040(%rax)
leaq 0xa6018(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
imull $0x3e8, 0x60040(%rax), %eax # imm = 0x3E8
movslq %eax, %rcx
leaq 0xa5ffd(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
addq 0x60030(%rax), %rcx
movq %rcx, 0x60030(%rax)
leaq 0xa5fe1(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
cmpq $0x0, 0x60050(%rax)
je 0x6d95d
callq 0x252e0
movq %rax, %rcx
leaq 0xa5fc1(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movq %rcx, 0x60030(%rax)
leaq 0xa5fac(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl $0x0, 0x60040(%rax)
leaq 0xa5f94(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movq 0x60030(%rax), %rax
movq %rax, -0x70(%rbp)
callq 0x252e0
movq %rax, %rcx
movq -0x70(%rbp), %rax
subq %rcx, %rax
movq %rax, -0x68(%rbp)
leaq 0xa5f68(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl 0x60040(%rax), %eax
movl %eax, -0x5c(%rbp)
callq 0x42960
movq -0x68(%rbp), %rdx
movl -0x5c(%rbp), %ecx
movq %rax, %r8
movl $0x3, %edi
leaq 0x78c0a(%rip), %rsi     # 0xe65c5
movb $0x0, %al
callq 0x24fb0
movl $0x1, %edi
callq 0x6cfc0
jmp 0x6dc4f
leaq 0xa5f20(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
cmpl $0x0, 0x6003c(%rax)
jne 0x6da9f
leaq 0xa5f05(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
cmpq $0x0, 0x60050(%rax)
jne 0x6da9f
callq 0x6d550
movl %eax, -0x34(%rbp)
movl -0x34(%rbp), %eax
leaq 0xa5ede(%rip), %rcx     # 0x1138f8
movq 0x7d0(%rcx), %rcx
cmpl 0x60040(%rcx), %eax
jle 0x6da9d
movl -0x34(%rbp), %eax
leaq 0xa5ec5(%rip), %rcx     # 0x1138f8
movq 0x7d0(%rcx), %rcx
subl 0x60040(%rcx), %eax
imull $0x3e8, %eax, %eax      # imm = 0x3E8
cltq
movq %rax, -0x40(%rbp)
movq -0x40(%rbp), %rcx
leaq 0xa5ea1(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
addq 0x60030(%rax), %rcx
movq %rcx, 0x60030(%rax)
movl -0x34(%rbp), %ecx
leaq 0xa5e82(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl %ecx, 0x60040(%rax)
movl -0x34(%rbp), %edx
movq -0x40(%rbp), %rcx
movl $0x3, %edi
leaq 0x78b78(%rip), %rsi     # 0xe660e
movb $0x0, %al
callq 0x24fb0
jmp 0x6da9f
callq 0x252e0
leaq 0xa5e4d(%rip), %rcx     # 0x1138f8
movq 0x7d0(%rcx), %rcx
cmpq 0x60030(%rcx), %rax
jge 0x6dac0
jmp 0x6dc4f
movq -0x10(%rbp), %rax
cmpq -0x28(%rbp), %rax
jle 0x6dacf
jmp 0x6dc4f
leaq 0xa5e22(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
cmpl $0x0, 0x6003c(%rax)
jne 0x6db7c
leaq 0xa5e07(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movq 0x8(%rax), %rcx
addq $0x1, %rcx
movq %rcx, 0x8(%rax)
leaq 0xa5ded(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movq 0x8(%rax), %rcx
leaq 0xa5ddb(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movq %rcx, 0x60048(%rax)
leaq 0xa5dc6(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movq 0x8(%rax), %rdx
movl $0x3, %edi
leaq 0x78b02(%rip), %rsi     # 0xe664b
movb $0x0, %al
callq 0x24fb0
callq 0x6d390
leaq 0xa5d9c(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl $0x1, 0x6003c(%rax)
movl $0xe, %edi
callq 0x69b00
jmp 0x6dc4f
leaq 0xa5d75(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movl 0x60038(%rax), %eax
cmpl -0x14(%rbp), %eax
jl 0x6dc4f
movq 0xba3d8(%rip), %rax     # 0x127f78
movq 0x850(%rax), %rax
movq %rax, -0x48(%rbp)
movl $0x3, %edi
leaq 0x78ac1(%rip), %rsi     # 0xe6678
movb $0x0, %al
callq 0x24fb0
movq 0xba3b3(%rip), %rdi     # 0x127f78
callq 0x69870
callq 0x420c0
movl $0x0, -0x1c(%rbp)
cmpl $0x4000, -0x1c(%rbp)    # imm = 0x4000
jge 0x6dc14
movq -0x48(%rbp), %rdi
movl -0x1c(%rbp), %esi
callq 0x6dc60
cmpl $0x0, %eax
je 0x6dc07
movl -0x1c(%rbp), %edi
callq 0x698f0
movq 0xba379(%rip), %rdi     # 0x127f78
movl -0x1c(%rbp), %esi
callq 0x68ce0
jmp 0x6dc09
movl -0x1c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x1c(%rbp)
jmp 0x6dbd6
leaq 0xa5cdd(%rip), %rax     # 0x1138f8
movq 0x7d0(%rax), %rax
movq 0x60048(%rax), %rcx
movq 0xba348(%rip), %rax     # 0x127f78
movq %rcx, 0x38(%rax)
callq 0x6dc90
movl $0x1, %edi
callq 0x69020
xorl %edi, %edi
callq 0x6cfc0
callq 0x69650
addq $0x70, %rsp
popq %rbp
retq
nopw %cs:(%rax,%rax)