|
|
@ -19,7 +19,9 @@ import (
|
|
|
|
"math/rand"
|
|
|
|
"math/rand"
|
|
|
|
"os"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
|
|
|
@ -356,6 +358,56 @@ func TestChunkDiskMapper_Truncate_PreservesFileSequence(t *testing.T) {
|
|
|
|
verifyFiles([]int{5, 6, 7})
|
|
|
|
verifyFiles([]int{5, 6, 7})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func TestChunkDiskMapper_Truncate_WriteQueueRaceCondition(t *testing.T) {
|
|
|
|
|
|
|
|
hrw := createChunkDiskMapper(t, "")
|
|
|
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
|
|
|
require.NoError(t, hrw.Close())
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This test should only run when the queue is enabled.
|
|
|
|
|
|
|
|
if hrw.writeQueue == nil {
|
|
|
|
|
|
|
|
t.Skip("This test should only run when the queue is enabled")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add an artificial delay in the writeChunk function to easily trigger the race condition.
|
|
|
|
|
|
|
|
origWriteChunk := hrw.writeQueue.writeChunk
|
|
|
|
|
|
|
|
hrw.writeQueue.writeChunk = func(seriesRef HeadSeriesRef, mint, maxt int64, chk chunkenc.Chunk, ref ChunkDiskMapperRef, isOOO, cutFile bool) error {
|
|
|
|
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
|
|
|
|
return origWriteChunk(seriesRef, mint, maxt, chk, ref, isOOO, cutFile)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
wg := sync.WaitGroup{}
|
|
|
|
|
|
|
|
wg.Add(2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Write a chunk. Since the queue is enabled, the chunk will be written asynchronously (with the artificial delay).
|
|
|
|
|
|
|
|
ref := hrw.WriteChunk(1, 0, 10, randomChunk(t), false, func(err error) {
|
|
|
|
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seq, _ := ref.Unpack()
|
|
|
|
|
|
|
|
require.Equal(t, 1, seq)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Truncate, simulating that all chunks from segment files before 1 can be dropped.
|
|
|
|
|
|
|
|
require.NoError(t, hrw.Truncate(1))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Request to cut a new file when writing the next chunk. If there's a race condition, cutting a new file will
|
|
|
|
|
|
|
|
// allow us to detect there's actually an issue with the sequence number (because it's checked when a new segment
|
|
|
|
|
|
|
|
// file is created).
|
|
|
|
|
|
|
|
hrw.CutNewFile()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Write another chunk. This will cut a new file.
|
|
|
|
|
|
|
|
ref = hrw.WriteChunk(1, 0, 10, randomChunk(t), false, func(err error) {
|
|
|
|
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seq, _ = ref.Unpack()
|
|
|
|
|
|
|
|
require.Equal(t, 2, seq)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TestHeadReadWriter_TruncateAfterIterateChunksError tests for
|
|
|
|
// TestHeadReadWriter_TruncateAfterIterateChunksError tests for
|
|
|
|
// https://github.com/prometheus/prometheus/issues/7753
|
|
|
|
// https://github.com/prometheus/prometheus/issues/7753
|
|
|
|
func TestHeadReadWriter_TruncateAfterFailedIterateChunks(t *testing.T) {
|
|
|
|
func TestHeadReadWriter_TruncateAfterFailedIterateChunks(t *testing.T) {
|
|
|
|