Skip to content

Commit

Permalink
Fix MMap Filler Hash Range
Browse files Browse the repository at this point in the history
  • Loading branch information
tung.tq committed Oct 4, 2023
1 parent 410c08e commit 8d2f54f
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 14 deletions.
86 changes: 72 additions & 14 deletions mmap/filler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mmap

import (
"context"
"sort"
)

// FillKey ...
Expand All @@ -10,17 +11,12 @@ type FillKey[R comparable] struct {
Range HashRange
}

type multiGetState[T any, R comparable] struct {
keys []FillKey[R]
result map[R][]T
err error
}

// NewMultiGetFiller converts from function often using SELECT WHERE IN
// into a Filler[T, R] that allow to be passed to New
func NewMultiGetFiller[T any, R comparable](
func NewMultiGetFiller[T any, R comparable, K Key](
multiGetFunc func(ctx context.Context, keys []FillKey[R]) ([]T, error),
getRootKey func(v T) R,
getKey func(v T) K,
) Filler[T, R] {
var state *multiGetState[T, R]

Expand All @@ -44,20 +40,82 @@ func NewMultiGetFiller[T any, R comparable](
if err != nil {
s.err = err
} else {
for _, v := range values {
rootKey := getRootKey(v)
prev := s.result[rootKey]
s.result[rootKey] = append(prev, v)
}
collectStateValues(s, values, getRootKey, getKey)
}
}

if s.err != nil {
return nil, s.err
}

result := s.result[rootKey]
return result, nil
valuesByRootKey := s.result[rootKey]
lowerBound := findLowerBound(valuesByRootKey, getKey, hashRange.Begin)

return computeValuesInHashRange(valuesByRootKey, lowerBound, hashRange, getKey), nil
}
}
}

type multiGetState[T any, R comparable] struct {
keys []FillKey[R]
result map[R][]T
err error
}

func findLowerBound[T any, K Key](
values []T,
getKey func(v T) K,
lowerBound uint64,
) int {
// similar to std::lower_bound of C++
first := 0
last := len(values)
for first != last {
mid := (first + last) / 2

val := values[mid]
if getKey(val).Hash() >= lowerBound {
last = mid
} else {
first = mid + 1
}
}
return first
}

func computeValuesInHashRange[T any, K Key](
values []T,
lowerBound int,
hashRange HashRange,
getKey func(T) K,
) []T {
var result []T
for i := lowerBound; i < len(values); i++ {
v := values[i]
if getKey(v).Hash() > hashRange.End {
break
}
result = append(result, v)
}
return result
}

func collectStateValues[T any, R comparable, K Key](
s *multiGetState[T, R],
values []T,
getRootKey func(T) R,
getKey func(T) K,
) {
for _, v := range values {
rootKey := getRootKey(v)
prev := s.result[rootKey]
s.result[rootKey] = append(prev, v)
}

// sort by hash
for _, v := range s.result {
sort.Slice(v, func(i, j int) bool {
return getKey(v[i]).Hash() < getKey(v[j]).Hash()
})
}
}
44 changes: 44 additions & 0 deletions mmap/filler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func newMultiGetFillerTest() *multiGetFillerTest {
return f.fillFunc(ctx, keys)
},
stockLocation.getRootKey,
stockLocation.getKey,
)

return f
Expand Down Expand Up @@ -180,4 +181,47 @@ func TestNewMultiGetFiller(t *testing.T) {
},
}, f.fillKeys)
})

t.Run("multiple same root key", func(t *testing.T) {
f := newMultiGetFillerTest()

stock1 := stockLocation{
Sku: sku1,
Location: loc1,
Hash: hash1.Begin + 100,
Quantity: 41,
}
stock2 := stockLocation{
Sku: sku1,
Location: loc2,
Hash: hash2.Begin + 100,
Quantity: 42,
}

f.fillFunc = func(ctx context.Context, keys []FillKey[stockLocationRootKey]) ([]stockLocation, error) {
return []stockLocation{stock2, stock1}, nil
}

fn1 := f.filler(context.Background(), stock1.getRootKey(), hash1)
fn2 := f.filler(context.Background(), stock2.getRootKey(), hash2)

resp, err := fn1()
assert.Equal(t, nil, err)
assert.Equal(t, []stockLocation{
stock1,
}, resp)

resp, err = fn2()
assert.Equal(t, nil, err)
assert.Equal(t, []stockLocation{
stock2,
}, resp)

assert.Equal(t, [][]FillKey[stockLocationRootKey]{
{
{RootKey: stock1.getRootKey(), Range: hash1},
{RootKey: stock2.getRootKey(), Range: hash2},
},
}, f.fillKeys)
})
}

0 comments on commit 8d2f54f

Please sign in to comment.