From c25391e9769270459c8256e91f43bbe1f6bba3f5 Mon Sep 17 00:00:00 2001 From: Erik van Velzen Date: Wed, 26 Oct 2022 09:02:13 +0200 Subject: [PATCH] satellite/metabase/rangedloop: uuid generation Create helper function to generate ranges of UUIDs, for parallelizing the segment loop. Change-Id: I17dbc1d5effe27fc1a3491aa9ca56c692bd95df0 --- satellite/metabase/rangedloop/uuid.go | 44 ++++++++++ satellite/metabase/rangedloop/uuid_test.go | 94 ++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 satellite/metabase/rangedloop/uuid.go create mode 100644 satellite/metabase/rangedloop/uuid_test.go diff --git a/satellite/metabase/rangedloop/uuid.go b/satellite/metabase/rangedloop/uuid.go new file mode 100644 index 000000000..7b553a1b4 --- /dev/null +++ b/satellite/metabase/rangedloop/uuid.go @@ -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) +} diff --git a/satellite/metabase/rangedloop/uuid_test.go b/satellite/metabase/rangedloop/uuid_test.go new file mode 100644 index 000000000..fee6ca544 --- /dev/null +++ b/satellite/metabase/rangedloop/uuid_test.go @@ -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]) +}