|
5 | 5 | package sstable
|
6 | 6 |
|
7 | 7 | import (
|
| 8 | + "iter" |
8 | 9 | "maps"
|
| 10 | + "math/rand/v2" |
9 | 11 | "testing"
|
| 12 | + "time" |
10 | 13 |
|
| 14 | + "github.com/cockroachdb/pebble/internal/testutils" |
11 | 15 | "github.com/cockroachdb/pebble/sstable/blob"
|
12 | 16 | "github.com/stretchr/testify/require"
|
13 | 17 | )
|
@@ -77,3 +81,68 @@ func TestBlobRefValueLivenessWriter(t *testing.T) {
|
77 | 81 | require.Equal(t, uint8(0x7), blocks[0].Bitmap[0])
|
78 | 82 | })
|
79 | 83 | }
|
| 84 | + |
| 85 | +func TestBlobRefLivenessEncoding_Randomized(t *testing.T) { |
| 86 | + const valueSize = uint64(10) |
| 87 | + prng := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 0)) |
| 88 | + w := blobRefValueLivenessWriter{} |
| 89 | + |
| 90 | + collectSlice := func(i iter.Seq2[blob.ReferenceID, []byte]) [][]byte { |
| 91 | + var s [][]byte |
| 92 | + for _, enc := range i { |
| 93 | + s = append(s, enc) |
| 94 | + } |
| 95 | + return s |
| 96 | + } |
| 97 | + |
| 98 | + for range 20 { |
| 99 | + w.init() |
| 100 | + numRefs := testutils.RandIntInRange(prng, 1, 10) |
| 101 | + for refID := range numRefs { |
| 102 | + numBlocks := testutils.RandIntInRange(prng, 1, 6) |
| 103 | + currentBlockID := blob.BlockID(testutils.RandIntInRange(prng, 0, 4)) |
| 104 | + |
| 105 | + for range numBlocks { |
| 106 | + // Generate blockIDs that are increasing -- with occasional |
| 107 | + // duplicates. Allow a 70% chance to repeat the previous block |
| 108 | + // ID. |
| 109 | + if prng.Float64() < 0.3 { |
| 110 | + currentBlockID += blob.BlockID(testutils.RandIntInRange(prng, 1, 4)) |
| 111 | + } |
| 112 | + numValues := testutils.RandIntInRange(prng, 10, 101) |
| 113 | + currentValueID := blob.BlockValueID(testutils.RandIntInRange(prng, 0, 4)) |
| 114 | + for range numValues { |
| 115 | + require.NoError(t, w.addLiveValue( |
| 116 | + blob.ReferenceID(refID), |
| 117 | + currentBlockID, |
| 118 | + currentValueID, |
| 119 | + valueSize, |
| 120 | + )) |
| 121 | + currentValueID += blob.BlockValueID(testutils.RandIntInRange(prng, 1, 4)) |
| 122 | + } |
| 123 | + } |
| 124 | + } |
| 125 | + encoded := collectSlice(w.finish()) |
| 126 | + |
| 127 | + // Test the encoding/decoding roundtrip to ensure idempotence. |
| 128 | + // Reinitialize the writer before reconstructing values. |
| 129 | + w.init() |
| 130 | + for refID, blockEnc := range encoded { |
| 131 | + for _, block := range DecodeBlobRefLivenessEncoding(blockEnc) { |
| 132 | + // Reconstruct the live values from the bitmap and add them to |
| 133 | + // the writer. |
| 134 | + for valueID := range IterSetBitsInRunLengthBitmap(block.Bitmap) { |
| 135 | + require.NoError(t, w.addLiveValue( |
| 136 | + blob.ReferenceID(refID), |
| 137 | + block.BlockID, |
| 138 | + blob.BlockValueID(valueID), |
| 139 | + valueSize, |
| 140 | + )) |
| 141 | + } |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + reencoded := collectSlice(w.finish()) |
| 146 | + require.Equal(t, encoded, reencoded) |
| 147 | + } |
| 148 | +} |
0 commit comments