mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-09-09 22:51:02 +00:00
The "field/view will just synthesize a tx" behavior is awful and also hides a number of fundamental flaws. We distinguish between "we really do mean to work on a single shard here" and "we intend to work on the whole field or view", and the latter now take Qcx instead of Tx. This eliminates a lot of very weird cases where we checked for nil Tx and synthesized them, and also gets us away from field and view taking Tx parameters when no possible Tx can be constructed which is valid, because Tx are inherently shard-specific at this time.
118 lines
2.8 KiB
Go
118 lines
2.8 KiB
Go
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package pilosa
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/featurebasedb/featurebase/v3/disco"
|
|
"github.com/featurebasedb/featurebase/v3/testhook"
|
|
)
|
|
|
|
// mustHolderConfig sets up a default holder config for tests.
|
|
func mustHolderConfig() *HolderConfig {
|
|
cfg := DefaultHolderConfig()
|
|
cfg.StorageConfig.FsyncEnabled = false
|
|
cfg.RBFConfig.FsyncEnabled = false
|
|
cfg.Schemator = disco.NewInMemSchemator()
|
|
cfg.Sharder = disco.InMemSharder
|
|
return cfg
|
|
}
|
|
|
|
func setupTest(t *testing.T, h *Holder, rowCol []rowCols, indexName string) (*Index, *Field) {
|
|
idx, err := h.CreateIndexIfNotExists(indexName, IndexOptions{TrackExistence: true})
|
|
if err != nil {
|
|
t.Fatalf("failed to create index %v: %v", indexName, err)
|
|
}
|
|
f, err := idx.CreateFieldIfNotExists("f", OptFieldTypeDefault())
|
|
if err != nil {
|
|
t.Fatalf("failed to create field in index %v: %v", indexName, err)
|
|
}
|
|
existencefield := idx.existenceFld
|
|
|
|
qcx := idx.Txf().NewWritableQcx()
|
|
defer qcx.Abort()
|
|
|
|
for _, r := range rowCol {
|
|
_, err = f.SetBit(qcx, r.row, r.col, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to set bit in index %v: %v", indexName, err)
|
|
}
|
|
|
|
_, err = existencefield.SetBit(qcx, r.row, r.col, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to set bit in index %v: %v", indexName, err)
|
|
}
|
|
}
|
|
|
|
if err = qcx.Finish(); err != nil {
|
|
t.Fatalf("failed to commit tx for index %v: %v", indexName, err)
|
|
}
|
|
|
|
shardsFound := idx.AvailableShards(includeRemote).Slice()
|
|
if len(shardsFound) != 3 {
|
|
t.Fatalf("expected 3 shards for index %v, got %v", indexName, len(shardsFound))
|
|
}
|
|
return idx, f
|
|
}
|
|
|
|
type rowCols struct {
|
|
row uint64
|
|
col uint64
|
|
}
|
|
|
|
func TestHolder_ProcessDeleteInflight(t *testing.T) {
|
|
path, _ := testhook.TempDir(t, "delete-inflight")
|
|
h := NewHolder(path, mustHolderConfig())
|
|
defer h.Close()
|
|
|
|
err := h.Open()
|
|
if err != nil {
|
|
t.Fatalf("failed to open holder: %v", err)
|
|
}
|
|
|
|
rowCol := []rowCols{
|
|
{1, 1},
|
|
{1, 2},
|
|
{10, ShardWidth + 1},
|
|
{1, ShardWidth * 2},
|
|
}
|
|
|
|
idx1, f1 := setupTest(t, h, rowCol, "idxdelete1")
|
|
idx2, f2 := setupTest(t, h, rowCol, "idxdelete2")
|
|
|
|
err = h.processDeleteInflight()
|
|
if err != nil {
|
|
t.Fatalf("failed to delete: %v", err)
|
|
}
|
|
|
|
tests := []struct {
|
|
idx *Index
|
|
f *Field
|
|
}{
|
|
{idx1, f1},
|
|
{idx2, f2},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
func() {
|
|
idx, f := test.idx, test.f
|
|
qcx := idx.Txf().NewQcx()
|
|
defer qcx.Abort()
|
|
for _, r := range rowCol {
|
|
row, err := f.Row(qcx, r.row)
|
|
if err != nil {
|
|
t.Fatalf("failed to get row: %v", err)
|
|
}
|
|
existenceRow, err := idx.existenceFld.Row(qcx, r.row)
|
|
if err != nil {
|
|
t.Fatalf("failed to get row: %v", err)
|
|
}
|
|
if len(row.Columns()) != 0 || len(existenceRow.Columns()) != 0 {
|
|
t.Fatalf("expected columns for fields to be empty after delete")
|
|
}
|
|
}
|
|
}()
|
|
|
|
}
|
|
}
|