Skip to content

Commit

Permalink
table: fix data race in select builder usage
Browse files Browse the repository at this point in the history
When table is used with SelectBuilder it triggers data race on Where()
condition usage.

This change copies underlining slices on table creation to allow safe
concurrent usage of the table.
  • Loading branch information
ajankovic authored and mmatczuk committed Feb 25, 2020
1 parent c36e6c5 commit ab2a96d
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
3 changes: 2 additions & 1 deletion table/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func New(m Metadata) *Table { // nolint: gocritic
for _, k := range m.SortKey {
t.primaryKeyCmp = append(t.primaryKeyCmp, qb.Eq(k))
}
t.partKeyCmp = t.primaryKeyCmp[:len(t.metadata.PartKey)]
t.partKeyCmp = make([]qb.Cmp, len(m.PartKey))
copy(t.partKeyCmp, t.primaryKeyCmp[:len(t.metadata.PartKey)])

// prepare get stmt
t.get.stmt, t.get.names = qb.Select(m.Name).Where(t.primaryKeyCmp...).ToCql()
Expand Down
58 changes: 58 additions & 0 deletions table/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
package table

import (
"sync"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/scylladb/gocqlx/qb"
)

func TestTableGet(t *testing.T) {
Expand Down Expand Up @@ -219,3 +221,59 @@ func TestTableDelete(t *testing.T) {
}
}
}

func TestTableConcurrentUsage(t *testing.T) {
table := []struct {
Name string
M Metadata
C []string
N []string
S string
}{
{
Name: "Full select",
M: Metadata{
Name: "table",
Columns: []string{"a", "b", "c", "d"},
PartKey: []string{"a"},
SortKey: []string{"b"},
},
N: []string{"a", "b"},
S: "SELECT * FROM table WHERE a=? AND b=? ",
},
{
Name: "Sub select",
M: Metadata{
Name: "table",
Columns: []string{"a", "b", "c", "d"},
PartKey: []string{"a"},
SortKey: []string{"b"},
},
C: []string{"d"},
N: []string{"a", "b"},
S: "SELECT d FROM table WHERE a=? AND b=? ",
},
}

parallelCount := 3
// run SelectBuilder on the data set in parallel
for _, test := range table {
var wg sync.WaitGroup
testTable := New(test.M)
wg.Add(parallelCount)
for i := 0; i < parallelCount; i++ {
go func() {
defer wg.Done()
stmt, names := testTable.SelectBuilder(test.C...).
Where(qb.Eq("b")).ToCql()
if diff := cmp.Diff(test.S, stmt); diff != "" {
t.Error(diff)
}
if diff := cmp.Diff(test.N, names); diff != "" {
t.Error(diff, names)
}
}()
}
wg.Wait()
}
}

0 comments on commit ab2a96d

Please sign in to comment.