start_server {tags {"lazyfree"}} {
    test "UNLINK can reclaim memory in background" {
        set orig_mem [s used_memory]
        set args {}
        for {set i 0} {$i < 100000} {incr i} {
            lappend args $i
        }
        r sadd myset {*}$args
        assert {[r scard myset] == 100000}
        set peak_mem [s used_memory]
        assert {[r unlink myset] == 1}
        assert {$peak_mem > $orig_mem+1000000}
        wait_for_condition 50 100 {
            [s used_memory] < $peak_mem &&
            [s used_memory] < $orig_mem*2
        } else {
            fail "Memory is not reclaimed by UNLINK"
        }
    }

    test "FLUSHDB ASYNC can reclaim memory in background" {
        # make the previous test is really done before sampling used_memory
        wait_lazyfree_done r

        set orig_mem [s used_memory]
        set args {}
        for {set i 0} {$i < 100000} {incr i} {
            lappend args $i
        }
        r sadd myset {*}$args
        assert {[r scard myset] == 100000}
        set peak_mem [s used_memory]
        r flushdb async
        assert {$peak_mem > $orig_mem+1000000}
        wait_for_condition 50 100 {
            [s used_memory] < $peak_mem &&
            [s used_memory] < $orig_mem*2
        } else {
            fail "Memory is not reclaimed by FLUSHDB ASYNC"
        }
    }

    test "lazy free a stream with all types of metadata" {
        # make the previous test is really done before doing RESETSTAT
        wait_for_condition 50 100 {
            [s lazyfree_pending_objects] == 0
        } else {
            fail "lazyfree isn't done"
        }

        r config resetstat
        r config set stream-node-max-entries 5
        for {set j 0} {$j < 1000} {incr j} {
            if {rand() < 0.9} {
                r xadd stream * foo $j
            } else {
                r xadd stream * bar $j
            }
        }
        r xgroup create stream mygroup 0
        set records [r xreadgroup GROUP mygroup Alice COUNT 2 STREAMS stream >]
        r xdel stream [lindex [lindex [lindex [lindex $records 0] 1] 1] 0]
        r xack stream mygroup [lindex [lindex [lindex [lindex $records 0] 1] 0] 0]
        r unlink stream

        # make sure it was lazy freed
        wait_for_condition 50 100 {
            [s lazyfree_pending_objects] == 0
        } else {
            fail "lazyfree isn't done"
        }
        assert_equal [s lazyfreed_objects] 1
    } {} {needs:config-resetstat}

    test "lazy free a stream with deleted cgroup" {
        r config resetstat
        r xadd s * a b
        r xgroup create s bla $
        r xgroup destroy s bla
        r unlink s

        # make sure it was not lazy freed
        wait_for_condition 50 100 {
            [s lazyfree_pending_objects] == 0
        } else {
            fail "lazyfree isn't done"
        }
        assert_equal [s lazyfreed_objects] 0
    } {} {needs:config-resetstat}

    test "FLUSHALL SYNC optimized to run in bg as blocking FLUSHALL ASYNC" {
        set num_keys 1000
        r config resetstat

        # Verify at start there are no lazyfree pending objects
        assert_equal [s lazyfree_pending_objects] 0

        # Fillup DB with items
        populate $num_keys

        # Run FLUSHALL SYNC command, optimized as blocking ASYNC
        r flushall

        # Verify all keys counted as lazyfreed
        assert_equal [s lazyfreed_objects] $num_keys
    }

    test "Run consecutive blocking FLUSHALL ASYNC successfully" {
        r config resetstat
        set rd [redis_deferring_client]

        # Fillup DB with items
        r set x 1
        r set y 2

        $rd write "FLUSHALL\r\nFLUSHALL\r\nFLUSHDB\r\n"
        $rd flush
        assert_equal [$rd read] {OK}
        assert_equal [$rd read] {OK}
        assert_equal [$rd read] {OK}
        assert_equal [s lazyfreed_objects] 2
        $rd close
    }

    test "FLUSHALL SYNC in MULTI not optimized to run as blocking FLUSHALL ASYNC" {
        r config resetstat

        # Fillup DB with items
        r set x 11
        r set y 22

        # FLUSHALL SYNC in multi
        r multi
        r flushall
        r exec

        # Verify flushall not run as lazyfree
        assert_equal [s lazyfree_pending_objects] 0
        assert_equal [s lazyfreed_objects] 0
    }

    test "Client closed in the middle of blocking FLUSHALL ASYNC" {
        set num_keys 100000
        r config resetstat

        # Fillup DB with items
        populate $num_keys

        # close client in the middle of ongoing Blocking FLUSHALL ASYNC
        set rd [redis_deferring_client]
        $rd flushall
        $rd close

        # Wait to verify all keys counted as lazyfreed
        wait_for_condition 50 100 {
            [s lazyfreed_objects] == $num_keys
        } else {
            fail "Unexpected number of lazyfreed_objects: [s lazyfreed_objects]"
        }
    }

    test "Pending commands in querybuf processed once unblocking FLUSHALL ASYNC" {
        r config resetstat
        set rd [redis_deferring_client]

        # Fillup DB with items
        r set x 1
        r set y 2

        $rd write "FLUSHALL\r\nPING\r\n"
        $rd flush
        assert_equal [$rd read] {OK}
        assert_equal [$rd read] {PONG}
        assert_equal [s lazyfreed_objects] 2
        $rd close
    }

    test "Unblocks client blocked on lazyfree via REPLICAOF command" {
        r config resetstat
        set rd [redis_deferring_client]

        populate 50000 ;# Just to make flushdb async slower
        $rd flushdb

        # Verify flushdb run as lazyfree
        wait_for_condition 50 100 {
            [s lazyfree_pending_objects] > 0 ||
            [s lazyfreed_objects] > 0
        } else {
            fail "FLUSHDB didn't run as lazyfree"
        }

        # Test that slaveof command unblocks clients without assertion failure
        r slaveof 127.0.0.1 0
        assert_equal [$rd read] {OK}
        $rd close
        r ping
        r slaveof no one
    } {OK} {external:skip}
}
