mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
If non-primary host fails to process a request, retry on primary node. conditions when we should not do this: - no error - we've aleady tried the primary - we're making a status request to get the primary node...this could lead to lock contention if we allow it to happen as we are making an http request within an on going http request to discover the primary node. This also deletes the RemoveHost method and the associated test b/c it is not used anywhere anymore and updates the returned error type.
305 lines
8.9 KiB
Go
305 lines
8.9 KiB
Go
package client
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/molecula/featurebase/v3/logger"
|
|
"github.com/molecula/featurebase/v3/test"
|
|
)
|
|
|
|
func TestIngestAPIBatchAdd(t *testing.T) {
|
|
t.Run("unkeyed", func(t *testing.T) {
|
|
batch := NewIngestAPIBatch(nil, 10, logger.NopLogger, []*Field{
|
|
{
|
|
name: "a",
|
|
index: &Index{name: "idxname", options: &IndexOptions{}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeSet,
|
|
},
|
|
},
|
|
{
|
|
name: "b",
|
|
index: &Index{name: "idxname", options: &IndexOptions{}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeSet,
|
|
keys: true,
|
|
},
|
|
},
|
|
{
|
|
name: "c",
|
|
index: &Index{name: "idxname", options: &IndexOptions{}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeTime,
|
|
keys: true,
|
|
},
|
|
},
|
|
})
|
|
qt := QuantizedTime{}
|
|
qt.Set(time.Date(2007, time.January, 1, 15, 0, 0, 0, time.UTC))
|
|
err := batch.Add(Row{
|
|
ID: uint64(1),
|
|
Values: []interface{}{uint64(2), "bkey", "ckey"},
|
|
Time: qt,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("adding row to batch: %v", err)
|
|
}
|
|
|
|
if batch.records[1]["a"] != uint64(2) {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.records)
|
|
}
|
|
if batch.records[1]["b"] != "bkey" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.records)
|
|
}
|
|
if batch.records[1]["c"].(map[string]interface{})["time"] != "2007-01-01T15:00:00Z" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.records)
|
|
}
|
|
if batch.records[1]["c"].(map[string]interface{})["values"] != "ckey" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.records)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("keyed", func(t *testing.T) {
|
|
batch := NewIngestAPIBatch(nil, 10, logger.NopLogger, []*Field{
|
|
{
|
|
name: "a",
|
|
index: &Index{name: "idxname", options: &IndexOptions{keys: true}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeSet,
|
|
},
|
|
},
|
|
{
|
|
name: "b",
|
|
index: &Index{name: "idxname", options: &IndexOptions{keys: true}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeSet,
|
|
keys: true,
|
|
},
|
|
},
|
|
{
|
|
name: "c",
|
|
index: &Index{name: "idxname", options: &IndexOptions{keys: true}},
|
|
options: &FieldOptions{
|
|
fieldType: FieldTypeTime,
|
|
keys: true,
|
|
},
|
|
},
|
|
})
|
|
qt := QuantizedTime{}
|
|
qt.Set(time.Date(2007, time.January, 1, 15, 0, 0, 0, time.UTC))
|
|
err := batch.Add(Row{
|
|
ID: "1",
|
|
Values: []interface{}{uint64(2), "bkey", "ckey"},
|
|
Time: qt,
|
|
})
|
|
|
|
checkResult := func(batch *ingestAPIBatch, id string, err error) {
|
|
if err != nil {
|
|
t.Fatalf("adding row to batch: %v", err)
|
|
}
|
|
|
|
if batch.recordsK[id]["a"] != uint64(2) {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.recordsK)
|
|
}
|
|
if batch.recordsK[id]["b"] != "bkey" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.recordsK)
|
|
}
|
|
if batch.recordsK[id]["c"].(map[string]interface{})["time"] != "2007-01-01T15:00:00Z" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.recordsK)
|
|
}
|
|
if batch.recordsK[id]["c"].(map[string]interface{})["values"] != "ckey" {
|
|
t.Fatalf("unexpected batch.records: %+v", batch.recordsK)
|
|
}
|
|
}
|
|
checkResult(batch, "1", err)
|
|
|
|
// test wrong type row ID
|
|
if err := batch.Add(Row{ID: 64.5}); !strings.Contains(err.Error(), "unsupported rowID") {
|
|
t.Fatalf("unexpected error w/ floating point rowID: %v", err)
|
|
}
|
|
|
|
// test that byte slice ID works same as string
|
|
err = batch.Add(Row{
|
|
ID: []byte("2"),
|
|
Values: []interface{}{uint64(2), "bkey", "ckey"},
|
|
Time: qt,
|
|
})
|
|
checkResult(batch, "2", err)
|
|
|
|
})
|
|
}
|
|
|
|
func TestIngestAPIBatch(t *testing.T) {
|
|
c := test.MustRunCluster(t, 3)
|
|
defer c.Close()
|
|
|
|
urls := make([]string, len(c.Nodes))
|
|
for i, n := range c.Nodes {
|
|
urls[i] = n.URL()
|
|
}
|
|
|
|
// Create a new client for the cluster
|
|
cli, err := newClientFromAddresses(urls, &ClientOptions{})
|
|
if err != nil {
|
|
t.Fatalf("getting new client: %v", err)
|
|
}
|
|
defer cli.Close()
|
|
|
|
cli.IngestSchema(map[string]interface{}{
|
|
"index-name": "test-1",
|
|
"index-action": "create",
|
|
"primary-key-type": "uint",
|
|
"field-action": "create",
|
|
"fields": []map[string]interface{}{
|
|
{
|
|
"field-name": "astr",
|
|
"field-type": "string",
|
|
"field-options": map[string]interface{}{},
|
|
},
|
|
{
|
|
"field-name": "bint",
|
|
"field-type": "int",
|
|
"field-options": map[string]interface{}{},
|
|
},
|
|
{
|
|
"field-name": "cid",
|
|
"field-type": "id",
|
|
"field-options": map[string]interface{}{},
|
|
},
|
|
{
|
|
"field-name": "dtimestamp",
|
|
"field-type": "timestamp",
|
|
"field-options": map[string]interface{}{
|
|
"unit": "s",
|
|
},
|
|
},
|
|
{
|
|
"field-name": "etime",
|
|
"field-type": "string",
|
|
"field-options": map[string]interface{}{
|
|
"time-quantum": "YMD",
|
|
},
|
|
},
|
|
{
|
|
"field-name": "fdecimal",
|
|
"field-type": "decimal",
|
|
"field-options": map[string]interface{}{
|
|
"scale": 3,
|
|
},
|
|
},
|
|
{
|
|
"field-name": "gbool",
|
|
"field-type": "bool",
|
|
"field-options": map[string]interface{}{},
|
|
},
|
|
},
|
|
})
|
|
|
|
schema, err := cli.Schema()
|
|
if err != nil {
|
|
t.Fatalf("getting schema: %v", err)
|
|
}
|
|
index := schema.Index("test-1")
|
|
defer cli.DeleteIndex(index)
|
|
|
|
batch := NewIngestAPIBatch(cli, 10, logger.NopLogger, []*Field{
|
|
{
|
|
name: "astr",
|
|
index: &Index{name: "test-1", options: &IndexOptions{}},
|
|
options: &FieldOptions{fieldType: FieldTypeSet, keys: true},
|
|
},
|
|
{
|
|
name: "bint",
|
|
options: &FieldOptions{fieldType: FieldTypeInt},
|
|
},
|
|
{
|
|
name: "cid",
|
|
options: &FieldOptions{fieldType: FieldTypeSet, keys: false},
|
|
},
|
|
{
|
|
name: "dtimestamp",
|
|
options: &FieldOptions{fieldType: FieldTypeTimestamp},
|
|
},
|
|
{
|
|
name: "etime",
|
|
options: &FieldOptions{fieldType: FieldTypeTime, keys: true, timeQuantum: TimeQuantumYearMonthDay},
|
|
},
|
|
{
|
|
name: "fdecimal",
|
|
options: &FieldOptions{fieldType: FieldTypeDecimal, scale: 3},
|
|
},
|
|
{
|
|
name: "gbool",
|
|
options: &FieldOptions{fieldType: FieldTypeBool},
|
|
},
|
|
})
|
|
|
|
qt0 := &QuantizedTime{}
|
|
qt0.Set(time.Date(2010, time.January, 1, 0, 0, 0, 0, time.UTC))
|
|
if err := batch.Add(Row{
|
|
ID: uint64(7),
|
|
Values: []interface{}{"a", -2, 9, 1287367623, "e", 1.2345, true},
|
|
Time: *qt0,
|
|
}); err != nil {
|
|
t.Fatalf("adding row: %v", err)
|
|
}
|
|
|
|
// test nil value case
|
|
if err := batch.Add(Row{
|
|
ID: uint64(8),
|
|
Values: []interface{}{nil, nil, nil, nil, nil, nil, nil},
|
|
Time: QuantizedTime{},
|
|
}); err != nil {
|
|
t.Fatalf("error adding all nil batch which should affect nothing: %v", err)
|
|
}
|
|
|
|
if err := batch.Import(); err != nil {
|
|
t.Fatalf("importing row: %v", err)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(astr=a)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(asr=a) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(bint==-2)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(bint==-2) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(cid=9)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(cid=9) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(dtimestamp=='2010-10-18T02:07:03Z')", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(dtimestamp=='2010-10-18T02:07:03Z') result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(etime=e, from='2010-01-01', to='2010-01-02')", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(etime=e, from='2010-01-01', to='2010-01-02') result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(fdecimal==1.234)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(fdecimal==1.234) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
if resp, err := cli.Query(NewPQLBaseQuery("Row(gbool=true)", &Index{name: "test-1", options: &IndexOptions{}}, nil)); err != nil {
|
|
t.Fatalf("querying: %v", err)
|
|
} else if len(resp.Result().Row().Columns) != 1 || resp.Result().Row().Columns[0] != uint64(7) {
|
|
t.Fatalf("unexpected Row(gbool=true) result: %+v", resp.Result().Row().Columns)
|
|
}
|
|
|
|
}
|