satellite/metabase/rangedloop: uuid generation

Create helper function to generate ranges of UUIDs, for parallelizing
the segment loop.

Change-Id: I17dbc1d5effe27fc1a3491aa9ca56c692bd95df0
This commit is contained in:
Erik van Velzen 2022-10-26 09:02:13 +02:00
parent 1da7520a88
commit c25391e976
2 changed files with 138 additions and 0 deletions

View File

@ -0,0 +1,44 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package rangedloop
import (
"encoding/binary"
"storj.io/common/uuid"
)
// CreateUUIDBoundaries splits up the entire 128-bit UUID range into equal parts.
func CreateUUIDBoundaries(nRanges uint32) ([]uuid.UUID, error) {
if nRanges == 0 {
// every time this line is executed a mathematician feels a disturbance in the force
nRanges = 1
}
increment := uint32(1 << 32 / uint64(nRanges))
result := []uuid.UUID{}
for i := uint32(1); i < nRanges; i++ {
topBits := i * increment
newUuid, err := MakeUUIDWithTopBits(topBits)
if err != nil {
return nil, err
}
result = append(result, newUuid)
}
return result, nil
}
// MakeUUIDWithTopBits creates a zeroed UUID with the top 32 bits set from the input.
// Technically the result is not a UUID since it doesn't have the version and variant bits set.
func MakeUUIDWithTopBits(topBits uint32) (uuid.UUID, error) {
bytes := make([]byte, 16)
binary.BigEndian.PutUint32(bytes, topBits)
return uuid.FromBytes(bytes)
}

View File

@ -0,0 +1,94 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
package rangedloop_test
import (
"testing"
"github.com/stretchr/testify/require"
"storj.io/common/uuid"
"storj.io/storj/satellite/metabase/rangedloop"
)
func TestMakeUuids(t *testing.T) {
inouts := []struct {
inTopBits uint32
outUuid string
}{
{
0,
"00000000-0000-0000-0000-000000000000",
}, {
0xffffffff,
"ffffffff-0000-0000-0000-000000000000",
}, {
0x12345678,
"12345678-0000-0000-0000-000000000000",
},
}
for _, inout := range inouts {
createdUuid, err := rangedloop.MakeUUIDWithTopBits(inout.inTopBits)
require.NoError(t, err)
require.Equal(t, inout.outUuid, createdUuid.String())
}
}
func TestCreateUuidBoundaries(t *testing.T) {
inouts := []struct {
inNumRanges uint32
outUuids []string
}{
{
0,
[]string{},
}, {
1,
[]string{},
}, {
2,
[]string{
"80000000-0000-0000-0000-000000000000",
},
}, {
4,
[]string{
"40000000-0000-0000-0000-000000000000",
"80000000-0000-0000-0000-000000000000",
"c0000000-0000-0000-0000-000000000000",
},
},
}
for _, inout := range inouts {
expectedUuids := []uuid.UUID{}
for _, outUuidString := range inout.outUuids {
outUuid, err := uuid.FromString(outUuidString)
require.NoError(t, err)
expectedUuids = append(expectedUuids, outUuid)
}
createdRange, err := rangedloop.CreateUUIDBoundaries(inout.inNumRanges)
require.NoError(t, err)
require.Equal(t, expectedUuids, createdRange)
}
}
func TestCreateUUIDBoundariesFor8191Ranges(t *testing.T) {
// 0x1fff = Mersenne prime 8191
boundaries, err := rangedloop.CreateUUIDBoundaries(0x1fff)
require.NoError(t, err)
require.Len(t, boundaries, 0x1ffe)
// floor(2 ^ 32 / 0x1fff) = 0x80040
secondUuid, err := uuid.FromString("00080040-0000-0000-0000-000000000000")
require.NoError(t, err)
require.Equal(t, secondUuid, boundaries[0])
// floor(2 ^ 32 / 0x1fff) * 0x1ffe = 0xfff7ff80
lastUuid, err := uuid.FromString("fff7ff80-0000-0000-0000-000000000000")
require.NoError(t, err)
require.Equal(t, lastUuid, boundaries[0x1ffd])
}