Repository: DiceDB/dice
File: core/queueref_test.go (Go)

Code before:
	core.Put("key2", core.NewObj(val[1], 1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key2")
	core.Put("key3", core.NewObj(val[2], -1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key3")
	time.Sleep(2 * time.Millisecond)
	if qe, err := qr.Remove(); err != nil || qe.Obj.Value != val[2] {
		t.Error("test for removing mulitple expired key before non-expired failed , Expected : ", val[2], " Got : ", qe.Obj.Value)
	}
}

// Test for removing from queue with all expired keys
func TestRemoveAllExpired(t *testing.T) {
	qr := core.NewQueueRef()
	val := [3]int{10, 20, 30}
	core.Put("key1", core.NewObj(val[0], 1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key1")
	core.Put("key2", core.NewObj(val[1], 1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key2")
	core.Put("key3", core.NewObj(val[2], 1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key3")
	time.Sleep(2 * time.Millisecond)
	if _, err := qr.Remove(); err == nil {
		t.Error("test for removing all expired key failed, Expected : ", core.ErrQueueEmpty, " Got : ", err)
	}
	// remove from empty queue
	if _, err := qr.Remove(); err == nil {
		fmt.Println(err)
		t.Error("test for removing from empty queue failed Expected : ", core.ErrQueueEmpty, " Got : ", err)
	}
}

// Benchmark queueref by inserting expired, non-expired and expired keys in order and removing them
func BenchmarkQueueRef(b *testing.B) {
	benchmarkCount := [][]int{{10, 20, 30}, {100, 200, 300}, {100, 2000, 3000}, {10000, 20000, 30000}}

	for _, count := range benchmarkCount {
		qr := core.NewQueueRef()
		config.KeysLimit = 100000000 // override keys limit high for benchmarking

		// need to init again for each round with the overriden buffer size
		// otherwise the watchchannel buffer size will stay as it is with the global keylimits size
		core.WatchChannel = make(chan core.WatchEvent, config.KeysLimit)
		b.Run(fmt.Sprintf("Benchmark queue with expired : %d, non-expired : %d, expired : %d keys", count[0],
			count[1], count[2]), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				// total number of keys , expired + non-expired + expired
				total := count[0] + count[1] + count[2]
				dataset := []tupple{}

				for i := 0; i < total; i++ {
					dataset = append(dataset, tupple{fmt.Sprintf("k%d", i), fmt.Sprintf("v%d", i)})

Review comment (performance):
I have a few suggestions to improve the benchmark:

1. Let's split the benchmark into separate operations - insertion and removal. This will give us more granular performance insights for each operation.
2. Please use `b.Run()` calls to create sub-benchmarks for different data sizes and operations.
3. Call `b.ResetTimer()` to ensure we're only timing the operations we intend to measure, not the setup code. Once the setup is complete (initializing queue, dataset, channel, etc.) make a call to `b.ResetTimer()` to ensure these operations don't affect the benchmark results.

You can probably structure it this way:

```go
func BenchmarkQueueRef(b *testing.B) {
    benchmarkCases := []struct {
        name         string
        expiredFirst int
        nonExpired   int
        expiredLast  int
    }{
        {"Small", 10, 20, 30},
        {"Medium", 100, 200, 300},
        {"Large", 100, 2000, 3000},
        {"VeryLarge", 10000, 20000, 30000},
    }

    for _, bc := range benchmarkCases {
        // Setup code...

        b.Run(fmt.Sprintf("%s_Insert", bc.name), func(b *testing.B) {
            for i := 0; i < b.N; i++ {
                // Insertion benchmark...
            }
        })

        b.Run(fmt.Sprintf("%s_Remove", bc.name), func(b *testing.B) {
            for i := 0; i < b.N; i++ {
                // Removal benchmark...
            }
        })
    }
}

Code after:
	core.Put("key3", core.NewObj(val[2], -1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key3")
	time.Sleep(2 * time.Millisecond)
	fmt.Printf("Queue size : %d\n", qr.Length())
	qe, err := qr.Remove()
	assert.Check(t, err == nil || qe.Obj.Value == val[2], fmt.Sprintf("test for removing mulitple expired key before non-expired failed , Expected : %d, Got %d\n", val[2], qe.Obj.Value))
}

// Test for removing from queue with all expired keys
func TestRemoveAllExpired(t *testing.T) {
	qr := core.NewQueueRef()
	val := [3]int{10, 20, 30}
	core.Put("key1", core.NewObj(val[0], 1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key1")
	core.Put("key2", core.NewObj(val[1], 1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key2")
	core.Put("key3", core.NewObj(val[2], 1, core.OBJ_TYPE_STRING, core.OBJ_ENCODING_INT))
	qr.Insert("key3")
	time.Sleep(2 * time.Millisecond)
	// remove from empty queue
	_, err := qr.Remove()
	assert.Equal(t, err, core.ErrQueueEmpty, fmt.Sprintf("test for removing from empty queue failed Expected : %s, Got : %s\n", core.ErrQueueEmpty, err))
}

// Benchmark queueref by inserting expired, non-expired and expired keys in order and removing them
func BenchmarkQueueRef(b *testing.B) {
	// Setup code...
	qr := core.NewQueueRef()
	config.KeysLimit = 100000000 // override keys limit high for benchmarking

	// need to init again for each round with the overriden buffer size
	// otherwise the watchchannel buffer size will stay as it is with the global keylimits size
	core.WatchChannel = make(chan core.WatchEvent, config.KeysLimit)

	expiredCount := 0
	nonExpiredCount := 0
	b.ResetTimer()
	b.Run("Insertion of expired and non-expired keys", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			key := fmt.Sprintf("k%d", i)
			value := fmt.Sprintf("v%d", i)
			random := rand.Intn(2)
			// Insert keys without expiration
			expiration := -1
			if random == 1 {
				// Insert keys with expiration of 1ms
				expiration = 1
				expiredCount++
			} else {
				nonExpiredCount++
			}