#
# Copyright (c) 2009-Present, Redis Ltd.
# All rights reserved.
#
# Copyright (c) 2024-present, Valkey contributors.
# All rights reserved.
#
# Licensed under your choice of (a) the Redis Source Available License 2.0
# (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
# GNU Affero General Public License v3 (AGPLv3).
#
# Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information.
#

# Returns either main or rdbchannel client id
# Assumes there is one replica with two channels
proc get_replica_client_id {master rdbchannel} {
    set input [$master client list type replica]

    foreach line [split $input "\n"] {
        if {[regexp {id=(\d+).*flags=(\S+)} $line match id flags]} {
            if {$rdbchannel == "yes"} {
                # rdbchannel will have C flag
                if {[string match *C* $flags]} {
                    return $id
                }
            } else {
                return $id
            }
        }
    }

    error "Replica not found"
}

start_server {tags {"repl external:skip"}} {
    set replica1 [srv 0 client]

    start_server {} {
        set replica2 [srv 0 client]

        start_server {} {
            set master [srv 0 client]
            set master_host [srv 0 host]
            set master_port [srv 0 port]

            $master config set repl-diskless-sync yes
            $master config set repl-rdb-channel yes
            populate 1000 master 10

            test "Test replication with multiple replicas (rdbchannel enabled on both)" {
                $replica1 config set repl-rdb-channel yes
                $replica1 replicaof $master_host $master_port

                $replica2 config set repl-rdb-channel yes
                $replica2 replicaof $master_host $master_port

                wait_replica_online $master 0
                wait_replica_online $master 1

                $master set x 1

                # Wait until replicas catch master
                wait_for_ofs_sync $master $replica1
                wait_for_ofs_sync $master $replica2

                # Verify db's are identical
                assert_morethan [$master dbsize] 0
                assert_equal [$master get x] 1
                assert_equal [$master debug digest] [$replica1 debug digest]
                assert_equal [$master debug digest] [$replica2 debug digest]
            }

            test "Test replication with multiple replicas (rdbchannel enabled on one of them)" {
                # Allow both replicas to ask for sync
                $master config set repl-diskless-sync-delay 5

                $replica1 replicaof no one
                $replica2 replicaof no one
                $replica1 config set repl-rdb-channel yes
                $replica2 config set repl-rdb-channel no

                set loglines [count_log_lines 0]
                set prev_forks [s 0 total_forks]
                $master set x 2

                # There will be two forks subsequently, one for rdbchannel
                # replica another for the replica without rdbchannel config.
                $replica1 replicaof $master_host $master_port
                $replica2 replicaof $master_host $master_port

                # There will be two forks subsequently, one for rdbchannel
                # replica, another for the replica without rdbchannel config.
                wait_for_log_messages 0 {"*Starting BGSAVE* replicas sockets (rdb-channel)*"} $loglines 300 100
                wait_for_log_messages 0 {"*Starting BGSAVE* replicas sockets"} $loglines 300 100

                wait_replica_online $master 0 100 100
                wait_replica_online $master 1 100 100

                # Verify two new forks.
                assert_equal [s 0 total_forks] [expr $prev_forks + 2]

                wait_for_ofs_sync $master $replica1
                wait_for_ofs_sync $master $replica2

                # Verify db's are identical
                assert_equal [$replica1 get x] 2
                assert_equal [$replica2 get x] 2
                assert_equal [$master debug digest] [$replica1 debug digest]
                assert_equal [$master debug digest] [$replica2 debug digest]
            }

            test "Test rdbchannel is not used if repl-diskless-sync config is disabled on master" {
                $replica1 replicaof no one
                $replica2 replicaof no one

                $master config set repl-diskless-sync-delay 0
                $master config set repl-diskless-sync no

                $master set x 3
                $replica1 replicaof $master_host $master_port

                # Verify log message does not mention rdbchannel
                wait_for_log_messages 0 {"*Starting BGSAVE for SYNC with target: disk*"} 0 2000 1

                wait_replica_online $master 0
                wait_for_ofs_sync $master $replica1

                # Verify db's are identical
                assert_equal [$replica1 get x] 3
                assert_equal [$master debug digest] [$replica1 debug digest]
            }
        }
    }
}

start_server {tags {"repl external:skip"}} {
    set replica [srv 0 client]
    set replica_pid [srv 0 pid]

    start_server {} {
        set master [srv 0 client]
        set master_host [srv 0 host]
        set master_port [srv 0 port]

        $master config set repl-rdb-channel yes
        $replica config set repl-rdb-channel yes

        # Reuse this test to verify large key delivery
        $master config set rdbcompression no
        $master config set rdb-key-save-delay 3000
        populate 1000 prefix1 10
        populate 5 prefix2 3000000
        populate 5 prefix3 2000000
        populate 5 prefix4 1000000

        # On master info output, we should see state transition in this order:
        # 1. wait_bgsave: Replica receives psync error (+RDBCHANNELSYNC)
        # 2. send_bulk_and_stream: Replica opens rdbchannel and delivery started
        # 3. online: Sync is completed
        test "Test replica state should start with wait_bgsave" {
            $replica config set key-load-delay 100000
            # Pause replica before opening rdb channel conn
            $replica debug repl-pause before-rdb-channel
            $replica replicaof $master_host $master_port

            wait_for_condition 50 200 {
                [s 0 connected_slaves] == 1 &&
                [string match "*wait_bgsave*" [s 0 slave0]]
            } else {
                fail "replica failed"
            }
        }

        test "Test replica state advances to send_bulk_and_stream when rdbchannel connects" {
            $master set x 1
            resume_process $replica_pid

            wait_for_condition 50 200 {
                [s 0 connected_slaves] == 1 &&
                [s 0 rdb_bgsave_in_progress] == 1 &&
                [string match "*send_bulk_and_stream*" [s 0 slave0]]
            } else {
                fail "replica failed"
            }
        }

        test "Test replica rdbchannel client has SC flag on client list output" {
            set input [$master client list type replica]

            # There will two replicas, second one should be rdbchannel
            set trimmed_input [string trimright $input]
            set lines [split $trimmed_input "\n"]
            if {[llength $lines] < 2} {
                error "There is no second line in the input: $input"
            }
            set second_line [lindex $lines 1]

            # Check if 'flags=SC' exists in the second line
            if {![regexp {flags=SC} $second_line]} {
                error "Flags are not 'SC' in the second line: $second_line"
            }
        }

        test "Test replica state advances to online when fullsync is completed" {
            # Speed up loading
            $replica config set key-load-delay 0

            wait_replica_online $master 0 100 1000
            wait_for_ofs_sync $master $replica

            wait_for_condition 50 200 {
                [s 0 rdb_bgsave_in_progress] == 0 &&
                [s 0 connected_slaves] == 1 &&
                [string match "*online*" [s 0 slave0]]
            } else {
                fail "replica failed"
            }

            wait_replica_online $master 0 100 1000
            wait_for_ofs_sync $master $replica

            # Verify db's are identical
            assert_morethan [$master dbsize] 0
            assert_equal [$master debug digest] [$replica debug digest]
        }
    }
}

start_server {tags {"repl external:skip debug_defrag:skip"}} {
    set replica [srv 0 client]

    start_server {} {
        set master [srv 0 client]
        set master_host [srv 0 host]
        set master_port [srv 0 port]

        $master config set repl-rdb-channel yes
        $replica config set repl-rdb-channel yes

        test "Test master memory does not increase during replication" {
            # Put some delay to rdb generation. If master doesn't forward
            # incoming traffic to replica, master's replication buffer will grow
            $master config set repl-diskless-sync-delay 0
            $master config set rdb-key-save-delay 500 ;# 500us delay and 10k keys means at least 5 seconds replication
            $master config set repl-backlog-size 5mb
            $replica config set replica-full-sync-buffer-limit 200mb
            populate 10000 master 10000 ;# 10k keys of 10k, means 100mb
            $replica config set loading-process-events-interval-bytes 262144 ;# process events every 256kb of rdb or command stream

            # Start write traffic
            set load_handle [start_write_load $master_host $master_port 100 "key1" 5000 4]

            set prev_used [s 0 used_memory]

            $replica replicaof $master_host $master_port
            set backlog_size [lindex [$master config get repl-backlog-size] 1]

            # Verify used_memory stays low
            set max_retry 1000
            set peak_replica_buf_size 0
            set peak_master_slave_buf_size 0
            set peak_master_used_mem 0
            set peak_master_rpl_buf 0
            while {$max_retry} {
                set replica_buf_size [s -1 replica_full_sync_buffer_size]
                set master_slave_buf_size [s mem_clients_slaves]
                set master_used_mem [s used_memory]
                set master_rpl_buf [s mem_total_replication_buffers]
                if {$replica_buf_size > $peak_replica_buf_size} {set peak_replica_buf_size $replica_buf_size}
                if {$master_slave_buf_size > $peak_master_slave_buf_size} {set peak_master_slave_buf_size $master_slave_buf_size}
                if {$master_used_mem > $peak_master_used_mem} {set peak_master_used_mem $master_used_mem}
                if {$master_rpl_buf > $peak_master_rpl_buf} {set peak_master_rpl_buf $master_rpl_buf}
                if {$::verbose} {
                    puts "[clock format [clock seconds] -format %H:%M:%S] master: $master_slave_buf_size replica: $replica_buf_size"
                }

                # Wait for the replica to finish reading the rdb (also from the master's perspective), and also consume much of the replica buffer
                if {[string match *slave0*state=online* [$master info]] &&
                    [s -1 master_link_status] == "up" &&
                    $replica_buf_size < 1000000} {
                    break
                } else {
                    incr max_retry -1
                    after 10
                }
            }
            if {$max_retry == 0} {
                error "assertion:Replica not in sync after 10 seconds"
            }

            if {$::verbose} {
                puts "peak_master_used_mem $peak_master_used_mem"
                puts "peak_master_rpl_buf $peak_master_rpl_buf"
                puts "peak_master_slave_buf_size $peak_master_slave_buf_size"
                puts "peak_replica_buf_size $peak_replica_buf_size"
            }
            # memory on the master is less than 1mb
            assert_lessthan [expr $peak_master_used_mem - $prev_used - $backlog_size] 1000000
            assert_lessthan $peak_master_rpl_buf [expr {$backlog_size + 1000000}]
            assert_lessthan $peak_master_slave_buf_size 1000000
            # buffers in the replica are more than 5mb
            assert_morethan $peak_replica_buf_size 5000000

            stop_write_load $load_handle
        }
    }
}

start_server {tags {"repl external:skip"}} {
    set replica [srv 0 client]

    start_server {} {
        set master [srv 0 client]
        set master_host [srv 0 host]
        set master_port [srv 0 port]

        $master config set repl-rdb-channel yes
        $replica config set repl-rdb-channel yes

        test "Test replication stream buffer becomes full on replica" {
            # For replication stream accumulation, replica inherits slave output
            # buffer limit as the size limit. In this test, we create traffic to
            # fill the buffer fully. Once the limit is reached, accumulation
            # will stop. This is not a failure scenario though. From that point,
            # further accumulation may occur on master side. Replication should
            # be completed successfully.

            # Create some artificial delay for rdb delivery and load. We'll
            # generate some traffic to fill the replication buffer.
            $master config set rdb-key-save-delay 1000
            $replica config set key-load-delay 1000
            $replica config set client-output-buffer-limit "replica 64kb 64kb 0"
            populate 2000 master 1

            set prev_sync_full [s 0 sync_full]
            $replica replicaof $master_host $master_port

            # Wait for replica to establish psync using main channel
            wait_for_condition 500 1000 {
                [string match "*state=send_bulk_and_stream*" [s 0 slave0]]
            } else {
                fail "replica didn't start sync"
            }

            # Create some traffic on replication stream
            populate 100 master 100000

            # Wait for replica's buffer limit reached
            wait_for_log_messages -1 {"*Replication buffer limit has been reached*"} 0 1000 10

            # Speed up loading
            $replica config set key-load-delay 0

            # Wait until sync is successful
            wait_for_condition 200 200 {
                [status $master master_repl_offset] eq [status $replica master_repl_offset] &&
                [status $master master_repl_offset] eq [status $replica slave_repl_offset]
            } else {
                fail "replica offsets didn't match in time"
            }

            # Verify sync was not interrupted.
            assert_equal [s 0 sync_full] [expr $prev_sync_full + 1]

            # Verify db's are identical
            assert_morethan [$master dbsize] 0
            assert_equal [$master debug digest] [$replica debug digest]
        }

        test "Test replication stream buffer config replica-full-sync-buffer-limit" {
            # By default, replica inherits client-output-buffer-limit of replica
            # to limit accumulated repl data during rdbchannel sync.
            # replica-full-sync-buffer-limit should override it if it is set.
            $replica replicaof no one

            # Create some artificial delay for rdb delivery and load. We'll
            # generate some traffic to fill the replication buffer.
            $master config set rdb-key-save-delay 1000
            $replica config set key-load-delay 1000
            $replica config set client-output-buffer-limit "replica 1024 1024 0"
            $replica config set replica-full-sync-buffer-limit 20mb
            populate 2000 master 1

            $replica replicaof $master_host $master_port

            # Wait until replication starts
            wait_for_condition 500 1000 {
                [string match "*state=send_bulk_and_stream*" [s 0 slave0]]
            } else {
                fail "replica didn't start sync"
            }

            # Create some traffic on replication stream
            populate 100 master 100000

            # Make sure config is used, we accumulated more than
            # client-output-buffer-limit
            assert_morethan [s -1 replica_full_sync_buffer_size] 1024
        }
    }
}

start_server {tags {"repl external:skip debug_defrag:skip"}} {
    set master [srv 0 client]
    set master_host [srv 0 host]
    set master_port [srv 0 port]
    set master_pid  [srv 0 pid]
    set loglines [count_log_lines 0]

    $master config set repl-diskless-sync yes
    $master config set repl-rdb-channel yes
    $master config set repl-backlog-size 1mb
    $master config set client-output-buffer-limit "replica 100k 0 0"
    $master config set repl-diskless-sync-delay 3

    start_server {} {
        set replica [srv 0 client]
        set replica_pid [srv 0 pid]

        $replica config set repl-rdb-channel yes
        $replica config set repl-timeout 10
        $replica config set key-load-delay 10000
        $replica config set loading-process-events-interval-bytes 1024

        test "Test master disconnects replica when output buffer limit is reached" {
            populate 20000 master 100 -1

            $replica replicaof $master_host $master_port
            wait_for_condition 100 200 {
                [s 0 loading] == 1
            } else {
                fail "Replica did not start loading"
            }

            # Generate replication traffic of ~20mb to disconnect the slave on obuf limit
            populate 20 master 1000000 -1

            wait_for_log_messages -1 {"*Client * closed * for overcoming of output buffer limits.*"} $loglines 1000 10
            $replica config set key-load-delay 0

            # Wait until replica loads RDB
            wait_for_log_messages 0 {"*Done loading RDB*"} 0 1000 10
        }

        test "Test replication recovers after output buffer failures" {
            # Verify system is operational
            $master set x 1

            # Wait until replica catches up
            wait_replica_online $master 0 1000 100
            wait_for_ofs_sync $master $replica

            # Verify db's are identical
            assert_morethan [$master dbsize] 0
            assert_equal [$replica get x] 1
            assert_equal [$master debug digest] [$replica debug digest]
        }
    }
}

start_server {tags {"repl external:skip"}} {
    set master [srv 0 client]
    set master_host [srv 0 host]
    set master_port [srv 0 port]

    $master config set repl-diskless-sync yes
    $master config set repl-rdb-channel yes
    $master config set rdb-key-save-delay 300
    $master config set client-output-buffer-limit "replica 0 0 0"
    $master config set repl-diskless-sync-delay 5

    populate 10000 master 1

    start_server {} {
        set replica1 [srv 0 client]
        $replica1 config set repl-rdb-channel yes

        start_server {} {
            set replica2 [srv 0 client]
            $replica2 config set repl-rdb-channel yes

            set load_handle [start_write_load $master_host $master_port 100 "key"]

            test "Test master continues RDB delivery if not all replicas are dropped" {
                $replica1 replicaof $master_host $master_port
                $replica2 replicaof $master_host $master_port

                wait_for_condition 50 200 {
                    [s -2 rdb_bgsave_in_progress] == 1
                } else {
                    fail "Sync did not start"
                }

                # Verify replicas are connected
                wait_for_condition 500 100 {
                    [s -2 connected_slaves] == 2
                } else {
                    fail "Replicas didn't connect: [s -2 connected_slaves]"
                }

                # kill one of the replicas
                catch {$replica1 shutdown nosave}

                # Wait until replica completes full sync
                # Verify there is no other full sync attempt
                wait_for_condition 50 1000 {
                    [s 0 master_link_status] == "up" &&
                    [s -2 sync_full] == 2 &&
                    [s -2 connected_slaves] == 1
                } else {
                    fail "Sync session did not continue
                          master_link_status: [s 0 master_link_status]
                          sync_full:[s -2 sync_full]
                          connected_slaves: [s -2 connected_slaves]"
                }

                # Wait until replica catches up
                wait_replica_online $master 0 200 100
                wait_for_condition 200 100 {
                    [s 0 mem_replica_full_sync_buffer] == 0
                } else {
                    fail "Replica did not consume buffer in time"
                }
            }

            test "Test master aborts rdb delivery if all replicas are dropped" {
                $replica2 replicaof no one

                # Start replication
                $replica2 replicaof $master_host $master_port

                wait_for_condition 50 1000 {
                    [s -2 rdb_bgsave_in_progress] == 1
                } else {
                    fail "Sync did not start"
                }
                set loglines [count_log_lines -2]

                # kill replica
                catch {$replica2 shutdown nosave}

                # Verify master aborts rdb save
                wait_for_condition 50 1000 {
                    [s -2 rdb_bgsave_in_progress] == 0 &&
                    [s -2 connected_slaves] == 0
                } else {
                    fail "Master should abort the sync
                          rdb_bgsave_in_progress:[s -2 rdb_bgsave_in_progress]
                          connected_slaves: [s -2 connected_slaves]"
                }
                wait_for_log_messages -2 {"*Background transfer error*"} $loglines 1000 50
            }

            stop_write_load $load_handle
        }
    }
}

start_server {tags {"repl external:skip"}} {
    set master [srv 0 client]
    set master_host [srv 0 host]
    set master_port [srv 0 port]

    $master config set repl-diskless-sync yes
    $master config set repl-rdb-channel yes
    $master config set rdb-key-save-delay 1000

    populate 3000 prefix1 1
    populate 100 prefix2 100000

    start_server {} {
        set replica [srv 0 client]
        set replica_pid [srv 0 pid]

        $replica config set repl-rdb-channel yes
        $replica config set repl-timeout 10

        set load_handle [start_write_load $master_host $master_port 100 "key"]

        test "Test replica recovers when rdb channel connection is killed" {
            $replica replicaof $master_host $master_port

            # Wait for sync session to start
            wait_for_condition 500 200 {
                [string match "*state=send_bulk_and_stream*" [s -1 slave0]] &&
                [s -1 rdb_bgsave_in_progress] eq 1
            } else {
                fail "replica didn't start sync session in time"
            }

            set loglines [count_log_lines -1]

            # Kill rdb channel client
            set id [get_replica_client_id $master yes]
            $master client kill id $id

            wait_for_log_messages -1 {"*Background transfer error*"} $loglines 1000 10

            # Verify master rejects main-ch-client-id after connection is killed
            assert_error {*Unrecognized*} {$master replconf main-ch-client-id $id}

            # Replica should retry
            wait_for_condition 500 200 {
                [string match "*state=send_bulk_and_stream*" [s -1 slave0]] &&
                [s -1 rdb_bgsave_in_progress] eq 1
            } else {
                fail "replica didn't retry after connection close"
            }
        }

        test "Test replica recovers when main channel connection is killed" {
            set loglines [count_log_lines -1]

            # Kill main channel client
            set id [get_replica_client_id $master yes]
            $master client kill id $id

            wait_for_log_messages -1 {"*Background transfer error*"} $loglines 1000 20

            # Replica should retry
            wait_for_condition 500 2000 {
                [string match "*state=send_bulk_and_stream*" [s -1 slave0]] &&
                [s -1 rdb_bgsave_in_progress] eq 1
            } else {
                fail "replica didn't retry after connection close"
            }
        }

        stop_write_load $load_handle

        test "Test replica recovers connection failures" {
            # Wait until replica catches up
            wait_replica_online $master 0 1000 100
            wait_for_ofs_sync $master $replica

            # Verify db's are identical
            assert_morethan [$master dbsize] 0
            assert_equal [$master debug digest] [$replica debug digest]
        }
    }
}

start_server {tags {"repl external:skip tsan:skip"}} {
    set replica [srv 0 client]
    set replica_pid  [srv 0 pid]

    start_server {} {
        set master [srv 0 client]
        set master_host [srv 0 host]
        set master_port [srv 0 port]

        test "Test master connection drops while streaming repl buffer into the db" {
            # Just after replica loads RDB, it will stream repl buffer into the
            # db. During streaming, we kill the master connection. Replica
            # will abort streaming and then try another psync with master.
            $master config set rdb-key-save-delay 1000
            $master config set repl-rdb-channel yes
            $master config set repl-diskless-sync yes
            $replica config set repl-rdb-channel yes
            $replica config set loading-process-events-interval-bytes 1024

            # Populate db and start write traffic
            populate 2000 master 1000
            set load_handle [start_write_load $master_host $master_port 100 "key1"]

            # Replica will pause in the loop of repl buffer streaming
            $replica debug repl-pause on-streaming-repl-buf
            $replica replicaof $master_host $master_port

            # Check if repl stream accumulation is started.
            wait_for_condition 50 1000 {
                [s -1 replica_full_sync_buffer_size] > 0
            } else {
                fail "repl stream accumulation not started"
            }

            # Wait until replica starts streaming repl buffer
            wait_for_log_messages -1 {"*Starting to stream replication buffer*"} 0 2000 10
            stop_write_load $load_handle
            $master config set rdb-key-save-delay 0

            # Kill master connection and resume the process
            $replica deferred 1
            $replica client kill type master
            $replica debug repl-pause clear
            resume_process $replica_pid
            $replica read
            $replica read
            $replica deferred 0

            wait_for_log_messages -1 {"*Master client was freed while streaming*"} 0 500 10

            # Quick check for stats test coverage
            assert_morethan_equal [s -1 replica_full_sync_buffer_peak] [s -1 replica_full_sync_buffer_size]

            # Wait until replica recovers and verify db's are identical
            wait_replica_online $master 0 1000 10
            wait_for_ofs_sync $master $replica

            assert_morethan [$master dbsize] 0
            assert_equal [$master debug digest] [$replica debug digest]
        }
    }
}

start_server {tags {"repl external:skip"}} {
    set replica [srv 0 client]
    set replica_pid  [srv 0 pid]

    start_server {} {
        set master [srv 0 client]
        set master_host [srv 0 host]
        set master_port [srv 0 port]

        test "Test main channel connection drops while loading rdb (disk based)" {
            # While loading rdb, we kill main channel connection.
            # We expect replica to complete loading RDB and then try psync
            # with the master.
            $master config set repl-rdb-channel yes
            $replica config set repl-rdb-channel yes
            $replica config set repl-diskless-load disabled
            $replica config set key-load-delay 10000
            $replica config set loading-process-events-interval-bytes 1024

            # Populate db and start write traffic
            populate 10000 master 100
            $replica replicaof $master_host $master_port

            # Wait until replica starts loading
            wait_for_condition 50 200 {
                [s -1 loading] == 1
            } else {
                fail "replica did not start loading"
            }

            # Kill replica connections
            $master client kill type replica
            $master set x 1

            # At this point, we expect replica to complete loading RDB. Then,
            # it will try psync with master.
            wait_for_log_messages -1 {"*Aborting rdb channel sync while loading the RDB*"} 0 2000 10
            wait_for_log_messages -1 {"*After loading RDB, replica will try psync with master*"} 0 2000 10

            # Speed up loading
            $replica config set key-load-delay 0

            # Wait until replica becomes online
            wait_replica_online $master 0 100 100

            # Verify there is another successful psync and no other full sync
            wait_for_condition 50 200 {
                [s 0 sync_full] == 1 &&
                [s 0 sync_partial_ok] == 1
            } else {
                fail "psync was not successful [s 0 sync_full] [s 0 sync_partial_ok]"
            }

            # Verify db's are identical after recovery
            wait_for_ofs_sync $master $replica
            assert_morethan [$master dbsize] 0
            assert_equal [$master debug digest] [$replica debug digest]
        }
    }
}

start_server {tags {"repl external:skip"}} {
    set replica [srv 0 client]
    set replica_pid  [srv 0 pid]

    start_server {} {
        set master [srv 0 client]
        set master_host [srv 0 host]
        set master_port [srv 0 port]

        test "Test main channel connection drops while loading rdb (diskless)" {
            # While loading rdb, kill both main and rdbchannel connections.
            # We expect replica to abort sync and later retry again.
            $master config set repl-rdb-channel yes
            $replica config set repl-rdb-channel yes
            $replica config set repl-diskless-load swapdb
            $replica config set key-load-delay 10000
            $replica config set loading-process-events-interval-bytes 1024

            # Populate db and start write traffic
            populate 10000 master 100

            $replica replicaof $master_host $master_port

            # Wait until replica starts loading
            wait_for_condition 50 200 {
                [s -1 loading] == 1
            } else {
                fail "replica did not start loading"
            }

            # Kill replica connections
            $master client kill type replica
            $master set x 1

            # At this point, we expect replica to abort loading RDB.
            wait_for_log_messages -1 {"*Aborting rdb channel sync while loading the RDB*"} 0 2000 10
            wait_for_log_messages -1 {"*Failed trying to load the MASTER synchronization DB from socket*"} 0 2000 10

            # Speed up loading
            $replica config set key-load-delay 0

            # Wait until replica recovers and becomes online
            wait_replica_online $master 0 100 100

            # Verify replica attempts another full sync
            wait_for_condition 50 200 {
                [s 0 sync_full] == 2 &&
                [s 0 sync_partial_ok] == 0
            } else {
                fail "sync was not successful [s 0 sync_full] [s 0 sync_partial_ok]"
            }

            # Verify db's are identical after recovery
            wait_for_ofs_sync $master $replica
            assert_morethan [$master dbsize] 0
            assert_equal [$master debug digest] [$replica debug digest]
        }
    }
}

start_server {tags {"repl external:skip tsan:skip"}} {
    set master2 [srv 0 client]
    set master2_host [srv 0 host]
    set master2_port [srv 0 port]
    start_server {tags {"repl external:skip"}} {
        set replica [srv 0 client]
        set replica_pid  [srv 0 pid]

        start_server {} {
            set master [srv 0 client]
            set master_host [srv 0 host]
            set master_port [srv 0 port]

            test "Test replicaof command while streaming repl buffer into the db" {
                # After replica loads the RDB, it will stream repl buffer into
                # the db. During streaming, replica receives command
                # "replicaof newmaster". Replica will abort streaming and then
                # should be able to connect to the new master.
                $master config set rdb-key-save-delay 1000
                $master config set repl-rdb-channel yes
                $master config set repl-diskless-sync yes
                $replica config set repl-rdb-channel yes
                $replica config set loading-process-events-interval-bytes 1024

                # Populate db and start write traffic
                populate 2000 master 1000
                set load_handle [start_write_load $master_host $master_port 100 "key1"]

                # Replica will pause in the loop of repl buffer streaming
                $replica debug repl-pause on-streaming-repl-buf
                $replica replicaof $master_host $master_port

                # Check if repl stream accumulation is started.
                wait_for_condition 50 1000 {
                    [s -1 replica_full_sync_buffer_size] > 0
                } else {
                    fail "repl stream accumulation not started"
                }

                # Wait until replica starts streaming repl buffer
                wait_for_log_messages -1 {"*Starting to stream replication buffer*"} 0 2000 10
                stop_write_load $load_handle
                $master config set rdb-key-save-delay 0

                # Populate the other master
                populate 100 master2 100 -2

                # Send "replicaof newmaster" command and resume the process
                $replica deferred 1
                $replica replicaof $master2_host $master2_port
                $replica debug repl-pause clear
                resume_process $replica_pid
                $replica read
                $replica read
                $replica deferred 0

                wait_for_log_messages -1 {"*Master client was freed while streaming*"} 0 500 10

                # Wait until replica recovers and verify db's are identical
                wait_replica_online $master2 0 1000 10
                wait_for_ofs_sync $master2 $replica
                assert_morethan [$master2 dbsize] 0
                assert_equal [$master2 debug digest] [$replica debug digest]

                # Try replication once more to be sure everything is okay.
                $replica replicaof no one
                $master2 set x 100

                $replica replicaof $master2_host $master2_port
                wait_replica_online $master2 0 1000 10
                wait_for_ofs_sync $master2 $replica
                assert_morethan [$master2 dbsize] 0
                assert_equal [$master2 debug digest] [$replica debug digest]
            }
        }
    }
}
