Merge branch 'master' into clustertest-retry-coverage-v2

This commit is contained in:
rachithrr 2022-03-10 17:14:39 -06:00 committed by GitHub
commit d29f2d219a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 132 additions and 76 deletions

View file

@ -287,6 +287,46 @@ func (h *Holder) IndexesPath() string {
return filepath.Join(h.path, IndexesDir)
}
func (h *Holder) deletePerShard(index *Index, shard uint64) error {
inprocessRecords := NewRow()
frag := h.fragment(index.name, existenceFieldName, viewStandard, shard)
if frag == nil {
return nil
}
tx := index.Txf().NewTx(Txo{Write: !writable, Index: index, Shard: shard})
defer tx.Rollback()
// filter rows based on having _exists>=1, which is used to flag delete in-flight
rows, err := frag.rows(context.Background(), tx, 1)
if err != nil {
return err
}
// check if any rows are found
if len(rows) == 0 {
return nil
}
for _, record := range rows {
row, err2 := frag.row(tx, record)
if err2 != nil {
return fmt.Errorf("getting row IDs: %v", err2)
}
inprocessRecords = inprocessRecords.Union(row)
}
h.Logger.Printf("retrying delete: index=%v shard=%v record count=%v", index.name, shard, inprocessRecords.Count())
tx.Rollback() // release the read tx in case a checksum is needed in DeleteRows
_, err = DeleteRows(context.Background(), inprocessRecords, index, shard)
if err != nil {
return fmt.Errorf("deleting rows: %v", err)
}
return nil
}
// processDeleteInflight checks if deletion was in progress when server shutdown
// the _exists field is set to row+1 when delete is started. Upon completion, the row is deleted.
// if _exists>=1, we finish deleting the rows
@ -294,37 +334,26 @@ func (h *Holder) processDeleteInflight() error {
for _, index := range h.Indexes() {
if index.trackExistence {
shards := index.AvailableShards(includeRemote).Slice()
index := index
ch := make(chan uint64, len(shards))
for _, shard := range shards {
inprocessRowIDs := NewRow()
ch <- shard
}
close(ch)
frag := h.fragment(index.name, existenceFieldName, viewStandard, shard)
if frag == nil {
continue
}
tx := index.Txf().NewTx(Txo{Write: !writable, Index: index, Shard: shard})
defer tx.Rollback()
// filter rows based on having _exists>=1, which is used to flag delete in-flight
rows, err := frag.rows(context.Background(), tx, 1)
if err != nil {
return err
}
// check if any rows are found
if len(rows) == 0 {
return nil
}
for _, rowID := range rows {
row, err2 := frag.row(tx, rowID)
if err2 != nil {
return err2
g := new(errgroup.Group)
for i := 0; i < runtime.NumCPU(); i++ {
g.Go(func() error {
for shard := range ch {
if err := h.deletePerShard(index, shard); err != nil {
return fmt.Errorf("delete shard %d: %w", shard, err)
}
}
inprocessRowIDs = inprocessRowIDs.Union(row)
}
DeleteRows(context.Background(), inprocessRowIDs, index, shard)
return nil
})
}
if err := g.Wait(); err != nil {
return err
}
}
}

View file

@ -18,6 +18,48 @@ func mustHolderConfig() *HolderConfig {
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
shard := uint64(0)
tx := idx.Txf().NewTx(Txo{Write: true, Index: idx, Shard: shard})
defer tx.Rollback()
for _, r := range rowCol {
_, err = f.SetBit(tx, r.row, r.col, nil)
if err != nil {
t.Fatalf("failed to set bit in index %v: %v", indexName, err)
}
_, err = existencefield.SetBit(tx, r.row, r.col, nil)
if err != nil {
t.Fatalf("failed to set bit in index %v: %v", indexName, err)
}
}
if err = tx.Commit(); 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())
@ -28,63 +70,48 @@ func TestHolder_ProcessDeleteInflight(t *testing.T) {
t.Fatalf("failed to open holder: %v", err)
}
idx, err := h.CreateIndexIfNotExists("i", IndexOptions{TrackExistence: true})
if err != nil {
t.Fatalf("failed to create index: %v", err)
}
f, err := idx.CreateFieldIfNotExists("f", OptFieldTypeDefault())
if err != nil {
t.Fatalf("failed to create field: %v", err)
}
existencefield := idx.existenceFld
shard := uint64(0)
tx := idx.Txf().NewTx(Txo{Write: true, Index: idx, Shard: shard})
defer tx.Rollback()
rowCol := []struct {
row uint64
col uint64
}{
rowCol := []rowCols{
{1, 1},
{1, 2},
{30, 33},
{22, 2},
}
for _, r := range rowCol {
_, err = f.SetBit(tx, r.row, r.col, nil)
if err != nil {
t.Fatalf("failed to set bit: %v", err)
}
_, err = existencefield.SetBit(tx, r.row, r.col, nil)
if err != nil {
t.Fatalf("failed to set bit: %v", err)
}
{10, ShardWidth + 1},
{1, ShardWidth * 2},
}
if err = tx.Commit(); err != nil {
t.Fatalf("failed to commit tx: %v", err)
}
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)
}
tx = idx.Txf().NewTx(Txo{Write: false, Index: idx, Shard: shard})
defer tx.Rollback()
for _, r := range rowCol {
row, err := f.Row(tx, r.row)
if err != nil {
t.Fatalf("failed to get row: %v", err)
}
existenceRow, err := existencefield.Row(tx, 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")
}
tests := []struct {
idx *Index
f *Field
}{
{idx1, f1},
{idx2, f2},
}
for _, test := range tests {
func() {
idx, f := test.idx, test.f
tx := idx.Txf().NewTx(Txo{Write: false, Index: idx1, Shard: uint64(0)})
defer tx.Rollback()
for _, r := range rowCol {
row, err := f.Row(tx, r.row)
if err != nil {
t.Fatalf("failed to get row: %v", err)
}
existenceRow, err := idx.existenceFld.Row(tx, 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")
}
}
}()
}
}