table: fix data race in select builder usage
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.
This commit is contained in:
committed by
Michal Jan Matczuk
parent
c36e6c5e66
commit
ab2a96d9f3
@@ -45,7 +45,8 @@ func New(m Metadata) *Table { // nolint: gocritic
|
|||||||
for _, k := range m.SortKey {
|
for _, k := range m.SortKey {
|
||||||
t.primaryKeyCmp = append(t.primaryKeyCmp, qb.Eq(k))
|
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
|
// prepare get stmt
|
||||||
t.get.stmt, t.get.names = qb.Select(m.Name).Where(t.primaryKeyCmp...).ToCql()
|
t.get.stmt, t.get.names = qb.Select(m.Name).Where(t.primaryKeyCmp...).ToCql()
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
package table
|
package table
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/scylladb/gocqlx/qb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTableGet(t *testing.T) {
|
func TestTableGet(t *testing.T) {
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user