fixing concurrent map access issue (#415)

Signed-off-by: Aaron Schlesinger <aaron@ecomaz.net>
Co-authored-by: Aaron Wislang <asw101@users.noreply.github.com>
This commit is contained in:
Aaron Schlesinger 2022-03-22 11:58:37 -07:00 committed by GitHub
parent b15bc3de5f
commit 2d35d0debf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 118 additions and 81 deletions

View File

@ -73,7 +73,9 @@ func (e *impl) IsActive(
)
if !ok {
err := fmt.Errorf("host '%s' not found in counts", host)
allCounts := mergeCountsWithRoutingTable(e.pinger.counts(), e.routingTable)
allCounts := e.pinger.mergeCountsWithRoutingTable(
e.routingTable,
)
lggr.Error(err, "Given host was not found in queue count map", "host", host, "allCounts", allCounts)
return nil, err
}
@ -173,7 +175,7 @@ func (e *impl) GetMetrics(
hostCount = e.pinger.aggregate()
} else {
err := fmt.Errorf("host '%s' not found in counts", host)
allCounts := mergeCountsWithRoutingTable(e.pinger.counts(), e.routingTable)
allCounts := e.pinger.mergeCountsWithRoutingTable(e.routingTable)
lggr.Error(err, "allCounts", allCounts)
return nil, err
}

View File

@ -4,22 +4,6 @@ import (
"github.com/kedacore/http-add-on/pkg/routing"
)
// mergeCountsWithRoutingTable ensures that all hosts in routing table
// are present in combined counts, if count is not present value is set to 0
func mergeCountsWithRoutingTable(
counts map[string]int,
table routing.TableReader,
) map[string]int {
mergedCounts := make(map[string]int)
for _, host := range table.Hosts() {
mergedCounts[host] = 0
}
for key, value := range counts {
mergedCounts[key] = value
}
return mergedCounts
}
// getHostCount gets proper count for given host regardless whether
// host is in counts or only in routerTable
func getHostCount(

View File

@ -14,7 +14,8 @@ type testCase struct {
retCounts map[string]int
}
var cases = []testCase{
func cases() []testCase {
return []testCase{
{
name: "empty queue",
table: newRoutingTable([]hostAndTarget{
@ -68,25 +69,11 @@ var cases = []testCase{
"example2.com": 0,
},
},
}
func TestMergeCountsWithRoutingTable(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
r := require.New(t)
ret := mergeCountsWithRoutingTable(
tc.counts,
tc.table,
)
r.Equal(tc.retCounts, ret)
})
}
}
func TestGetHostCount(t *testing.T) {
for _, tc := range cases {
for _, tc := range cases() {
for host, retCount := range tc.retCounts {
t.Run(tc.name, func(t *testing.T) {
r := require.New(t)

View File

@ -11,6 +11,7 @@ import (
"github.com/go-logr/logr"
"github.com/kedacore/http-add-on/pkg/k8s"
"github.com/kedacore/http-add-on/pkg/queue"
"github.com/kedacore/http-add-on/pkg/routing"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
@ -124,6 +125,23 @@ func (q *queuePinger) counts() map[string]int {
return q.allCounts
}
// mergeCountsWithRoutingTable ensures that all hosts in routing table
// are present in combined counts, if count is not present value is set to 0
func (q *queuePinger) mergeCountsWithRoutingTable(
table routing.TableReader,
) map[string]int {
q.pingMut.RLock()
defer q.pingMut.RUnlock()
mergedCounts := make(map[string]int)
for _, host := range table.Hosts() {
mergedCounts[host] = 0
}
for key, value := range q.allCounts {
mergedCounts[key] = value
}
return mergedCounts
}
func (q *queuePinger) aggregate() int {
q.pingMut.RLock()
defer q.pingMut.RUnlock()

View File

@ -9,6 +9,7 @@ import (
"github.com/kedacore/http-add-on/pkg/k8s"
"github.com/kedacore/http-add-on/pkg/queue"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
v1 "k8s.io/api/core/v1"
)
@ -215,3 +216,48 @@ func TestFetchCounts(t *testing.T) {
}
r.Equal(expectedCounts, cts)
}
func TestMergeCountsWithRoutingTable(t *testing.T) {
for _, tc := range cases() {
t.Run(tc.name, func(t *testing.T) {
ctx := context.Background()
grp, ctx := errgroup.WithContext(ctx)
r := require.New(t)
const C = 100
tickr, q, err := newFakeQueuePinger(
ctx,
logr.Discard(),
)
r.NoError(err)
defer tickr.Stop()
q.allCounts = tc.counts
retCh := make(chan map[string]int)
for i := 0; i < C; i++ {
grp.Go(func() error {
retCh <- q.mergeCountsWithRoutingTable(tc.table)
return nil
})
}
// ensure we receive from retCh C times
allRets := map[int]map[string]int{}
for i := 0; i < C; i++ {
allRets[i] = <-retCh
}
r.NoError(grp.Wait())
// ensure that all returned maps are the
// same
prev := allRets[0]
for i := 1; i < C; i++ {
r.Equal(prev, allRets[i])
prev = allRets[i]
}
// ensure that all the returned maps are
// equal to what we expected
r.Equal(tc.retCounts, prev)
})
}
}