Repository: influxdata/influxdb
File: tsdb/engine/tsm1/array_cursor_optimization_bug_test.go (Go)

Code before:
	for _, cv := range cacheValues {
		if tsmTimestamps[cv.UnixNano()] {
			t.Fatalf("Test precondition failed: TSM and cache data share timestamp %d. This will interfere with test results.", cv.UnixNano())
		}
	}
	// Verify the cache fills most but not all of the buffer
	if len(cacheValues) >= bufferSize {
		t.Fatalf("Test precondition failed: Cache must not fill entire buffer (size %d), has %d values", bufferSize, len(cacheValues))
	}

	// Verify cache has sufficient data to trigger the bug
	minCacheSize := bufferSize - 10 // Leave room for at least some TSM data
	if len(cacheValues) < minCacheSize {
		t.Fatalf("Test precondition failed: Cache must have at least %d values to trigger bug, has %d", minCacheSize, len(cacheValues))
	}

	// Verify TSM has data both before and after cache range
	var tsmBeforeCache, tsmAfterCache int
	cacheMin, cacheMax := cacheValues[0].UnixNano(), cacheValues[len(cacheValues)-1].UnixNano()
	for _, v := range tsmData[0].values {
		if v.UnixNano() < cacheMin {
			tsmBeforeCache++
		} else if v.UnixNano() > cacheMax {
			tsmAfterCache++
		}
	}
	if tsmAfterCache < 2 {
		t.Fatalf("Test precondition failed: Need at least 2 TSM points after cache range to trigger bug, has %d", tsmAfterCache)
	}

	kc := fs.KeyCursor(context.Background(), []byte("measurement,field=value#!~#value"), 0, true)
	defer kc.Close()

	cursor := newFloatArrayAscendingCursor()
	defer cursor.Close()

	// Find min and max timestamps across both TSM and cache data
	minTime := int64(tsmMinTime) // Start with first TSM timestamp
	maxTime := int64(tsmMaxTime) // Start with last TSM timestamp

	// Check if cache has earlier timestamps
	if len(cacheValues) > 0 && cacheValues[0].UnixNano() < minTime {
		minTime = cacheValues[0].UnixNano()
	}

	// Check if cache has later timestamps
	if len(cacheValues) > 0 && cacheValues[len(cacheValues)-1].UnixNano() > maxTime {
		maxTime = cacheValues[len(cacheValues)-1].UnixNano()
	}

	// Add buffer to ensure we capture all data

Review comment (suggestion):
the test only looks at float array cursors but all types are impacted. They are made from a template so i made the assumption testing one type also covers the other datatypes. 

Code after:
	for _, cv := range cacheValues {
		require.Falsef(tsmTimestamps[cv.UnixNano()], "Test precondition failed: TSM and cache data share timestamp %d. This will interfere with test results.", cv.UnixNano())
	}
	// Verify the cache fills most but not all of the buffer
	require.Lessf(len(cacheValues), bufferSize, "Test precondition failed: Cache must not fill entire buffer (size %d), has %d values", bufferSize, len(cacheValues))

	// Verify cache has sufficient data to trigger the bug
	minCacheSize := bufferSize - 10 // Leave room for at least some TSM data
	require.GreaterOrEqualf(len(cacheValues), minCacheSize, "Test precondition failed: Cache must have at least %d values to trigger bug, has %d", minCacheSize, len(cacheValues))

	// Verify TSM has data both before and after cache range
	var tsmBeforeCache, tsmAfterCache int
	cacheMin, cacheMax := cacheValues[0].UnixNano(), cacheValues[len(cacheValues)-1].UnixNano()
	for _, v := range tsmData[0].values {
		if v.UnixNano() < cacheMin {
			tsmBeforeCache++
		} else if v.UnixNano() > cacheMax {
			tsmAfterCache++
		}
	}
	require.GreaterOrEqualf(tsmAfterCache, 2, "Test precondition failed: Need at least 2 TSM points after cache range to trigger bug, has %d", tsmAfterCache)

	kc := fs.KeyCursor(context.Background(), []byte("measurement,field=value#!~#value"), 0, true)
	defer kc.Close()

	cursor := newFloatArrayAscendingCursor()
	defer cursor.Close()

	// Find min and max timestamps across both TSM and cache data
	minTime := int64(tsmMinTime) // Start with first TSM timestamp
	maxTime := int64(tsmMaxTime) // Start with last TSM timestamp

	// Check if cache has earlier timestamps
	if len(cacheValues) > 0 && cacheValues[0].UnixNano() < minTime {
		minTime = cacheValues[0].UnixNano()
	}

	// Check if cache has later timestamps
	if len(cacheValues) > 0 && cacheValues[len(cacheValues)-1].UnixNano() > maxTime {
		maxTime = cacheValues[len(cacheValues)-1].UnixNano()
	}

	// Add buffer to ensure we capture all data
	maxTime += 1000

	// search over the whole time range, ascending
	err = cursor.reset(minTime, maxTime, cacheValues, kc)
	require.NoError(err, "error resetting cursor")

	// Collect all timestamps and values from all Next() calls
	var allTimestamps []int64