902 lines
34 KiB
Go
902 lines
34 KiB
Go
// Copyright 2022 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package executor_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/kvproto/pkg/kvrpcpb"
|
|
"github.com/pingcap/kvproto/pkg/metapb"
|
|
"github.com/pingcap/tidb/pkg/store/mockstore"
|
|
"github.com/pingcap/tidb/pkg/store/mockstore/unistore"
|
|
"github.com/pingcap/tidb/pkg/testkit"
|
|
"github.com/pingcap/tidb/pkg/util/syncutil"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/tikv/client-go/v2/testutils"
|
|
"github.com/tikv/client-go/v2/tikv"
|
|
"github.com/tikv/client-go/v2/tikvrpc"
|
|
"go.uber.org/atomic"
|
|
)
|
|
|
|
// withMockTiFlash sets the mockStore to have N TiFlash stores (naming as tiflash0, tiflash1, ...).
|
|
func withMockTiFlash(nodes int) mockstore.MockTiKVStoreOption {
|
|
return mockstore.WithMultipleOptions(
|
|
mockstore.WithClusterInspector(func(c testutils.Cluster) {
|
|
mockCluster := c.(*unistore.Cluster)
|
|
_, _, region1 := mockstore.BootstrapWithSingleStore(c)
|
|
tiflashIdx := 0
|
|
for tiflashIdx < nodes {
|
|
store2 := c.AllocID()
|
|
peer2 := c.AllocID()
|
|
addr2 := fmt.Sprintf("tiflash%d", tiflashIdx)
|
|
mockCluster.AddStore(store2, addr2, &metapb.StoreLabel{Key: "engine", Value: "tiflash"})
|
|
mockCluster.AddPeer(region1, store2, peer2)
|
|
tiflashIdx++
|
|
}
|
|
}),
|
|
mockstore.WithStoreType(mockstore.EmbedUnistore),
|
|
)
|
|
}
|
|
|
|
func TestCompactTableTooBusy(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
return &kvrpcpb.CompactResponse{
|
|
Error: &kvrpcpb.CompactError{Error: &kvrpcpb.CompactError_ErrTooManyPendingTasks{}},
|
|
}, nil
|
|
})
|
|
defer mocker.RequireAllHandlersHit()
|
|
store := testkit.CreateMockStore(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows(
|
|
`Warning 1105 compact on store tiflash0 failed: store is too busy`,
|
|
))
|
|
}
|
|
|
|
func TestCompactTableInProgress(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
return &kvrpcpb.CompactResponse{
|
|
Error: &kvrpcpb.CompactError{Error: &kvrpcpb.CompactError_ErrCompactInProgress{}},
|
|
}, nil
|
|
})
|
|
defer mocker.RequireAllHandlersHit()
|
|
store := testkit.CreateMockStore(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows(
|
|
`Warning 1105 compact on store tiflash0 failed: table is compacting in progress`,
|
|
))
|
|
}
|
|
|
|
func TestCompactTableInternalError(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
return &kvrpcpb.CompactResponse{
|
|
Error: &kvrpcpb.CompactError{Error: &kvrpcpb.CompactError_ErrInvalidStartKey{}},
|
|
}, nil
|
|
})
|
|
defer mocker.RequireAllHandlersHit()
|
|
store := testkit.CreateMockStore(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows(
|
|
`Warning 1105 compact on store tiflash0 failed: internal error (check logs for details)`,
|
|
))
|
|
}
|
|
|
|
// TestCompactTableNoRemaining: Returns NoRemaining for request #1.
|
|
func TestCompactTableNoRemaining(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
store, do := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "t")
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, tableID)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
mocker.RequireAllHandlersHit()
|
|
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "t")
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, tableID)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
tk = testkit.NewTestKit(t, store)
|
|
tk.MustExec(`alter table test.t compact;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
mocker.RequireAllHandlersHit()
|
|
}
|
|
|
|
// TestCompactTableHasRemaining: Returns HasRemaining=true for request #1 and #2, returns HasRemaining=false for request #3.
|
|
func TestCompactTableHasRemaining(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, do := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "t")
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, tableID)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "t")
|
|
require.Equal(t, []byte{0xFF}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, tableID)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0x20},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#3`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "t")
|
|
require.Equal(t, []byte{0xFF, 0x20}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, tableID)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0xA0},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
}
|
|
|
|
// TestCompactTableErrorInHalfway: Returns error for request #2.
|
|
func TestCompactTableErrorInHalfway(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, _ := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
Error: &kvrpcpb.CompactError{Error: &kvrpcpb.CompactError_ErrTooManyPendingTasks{}},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows(
|
|
`Warning 1105 compact on store tiflash0 failed: store is too busy`,
|
|
))
|
|
}
|
|
|
|
// TestCompactTableNoRemainingMultipleTiFlash: 2 TiFlash stores, both returns NoRemaining for request #1.
|
|
func TestCompactTableNoRemainingMultipleTiFlash(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, do := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(2), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "t")
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, tableID)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash1/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "t")
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, tableID)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
}
|
|
|
|
// TestCompactTableMultipleTiFlash: 2 TiFlash stores.
|
|
// Store0 - #1 (remaining=true), #2 (remaining=true), #3 (remaining=false)
|
|
// Store1 - #1 (remaining=true), #2 (remaining=false)
|
|
func TestCompactTableMultipleTiFlash(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, _ := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(2), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xFF}, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0x20},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#3`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xFF, 0x20}, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0xA0},
|
|
}, nil
|
|
})
|
|
|
|
mocker.MockFrom(`tiflash1/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xC0, 0xCC}, // Use a different end key as tiflash0
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash1/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xC0, 0xCC}, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xDD},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
}
|
|
|
|
// TestCompactTableMultipleTiFlashWithError: 3 TiFlash stores.
|
|
// Store0 - #1 (remaining=true), #2 (remaining=true), #3 (remaining=false)
|
|
// Store1 - #1 (remaining=true), #2 (error)
|
|
// Store2 - #1 (error)
|
|
func TestCompactTableMultipleTiFlashWithError(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, _ := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(3), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xFF}, req.StartKey)
|
|
time.Sleep(time.Second * 1)
|
|
// This request must be returned after store1 and store2 return errors. We should still receive req #3.
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0x20},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#3`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xFF, 0x20}, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0xA0},
|
|
}, nil
|
|
})
|
|
|
|
mocker.MockFrom(`tiflash1/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xC0, 0xCC},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash1/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xC0, 0xCC}, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
Error: &kvrpcpb.CompactError{Error: &kvrpcpb.CompactError_ErrTooManyPendingTasks{}},
|
|
}, nil
|
|
})
|
|
|
|
mocker.MockFrom(`tiflash2/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
Error: &kvrpcpb.CompactError{Error: &kvrpcpb.CompactError_ErrTooManyPendingTasks{}},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Sort().Check(testkit.Rows(
|
|
"Warning 1105 compact on store tiflash1 failed: store is too busy",
|
|
"Warning 1105 compact on store tiflash2 failed: store is too busy",
|
|
))
|
|
}
|
|
|
|
// TestCompactTableWithRangePartition: 1 TiFlash, table has 4 partitions.
|
|
// Partition 0: 1 Partials
|
|
// Partition 1: 3 Partials
|
|
// Partition 2: 1 Partials
|
|
// Partition 3: 2 Partials
|
|
// There will be 7 requests sent in series.
|
|
func TestCompactTableWithRangePartition(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, do := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 0)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xCC},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#3`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Equal(t, []byte{0xCC}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#4`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Equal(t, []byte{0xFF}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0xAA},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#5`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 2)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xAB},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#6`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 3)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xC0},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#7`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 3)
|
|
require.Equal(t, []byte{0xC0}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xC1, 0xFF, 0x00},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec(`
|
|
CREATE TABLE employees (
|
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
fname VARCHAR(25) NOT NULL,
|
|
lname VARCHAR(25) NOT NULL,
|
|
store_id INT NOT NULL,
|
|
department_id INT NOT NULL
|
|
)
|
|
PARTITION BY RANGE(id) (
|
|
PARTITION p0 VALUES LESS THAN (5),
|
|
PARTITION p1 VALUES LESS THAN (10),
|
|
PARTITION p2 VALUES LESS THAN (15),
|
|
PARTITION p3 VALUES LESS THAN MAXVALUE
|
|
);
|
|
`)
|
|
tk.MustExec(`alter table employees set tiflash replica 1;`)
|
|
tk.MustExec(`alter table employees compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
}
|
|
|
|
// TestCompactTableWithHashPartition: 1 TiFlash, table has 3 partitions (hash partition).
|
|
// During compacting the partition, one partition will return failure PhysicalTableNotExist. The remaining partitions should be still compacted.
|
|
func TestCompactTableWithHashPartitionAndOnePartitionFailed(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, do := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 0)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xA0},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#3`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Equal(t, []byte{0xA0}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
Error: &kvrpcpb.CompactError{Error: &kvrpcpb.CompactError_ErrPhysicalTableNotExist{}}, // For example, may be this partition got dropped
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#4`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 2)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xCD},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec(`
|
|
CREATE TABLE employees (
|
|
id INT NOT NULL,
|
|
fname VARCHAR(30),
|
|
lname VARCHAR(30),
|
|
hired DATE NOT NULL DEFAULT '1970-01-01',
|
|
separated DATE DEFAULT '9999-12-31',
|
|
job_code INT,
|
|
store_id INT
|
|
)
|
|
PARTITION BY HASH(store_id)
|
|
PARTITIONS 3;
|
|
`)
|
|
tk.MustExec(`alter table employees set tiflash replica 1;`)
|
|
tk.MustExec(`alter table employees compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
}
|
|
|
|
// TestCompactTableWithTiFlashDown: 2 TiFlash stores.
|
|
// Store0 - #1 (remaining=true, takes 3s), #2 (remaining=false)
|
|
// Store1 - #1 (remaining=true), #2+ (down)
|
|
func TestCompactTableWithTiFlashDown(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, _ := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(2), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
time.Sleep(time.Second * 3)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xFF}, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0x00, 0xAA},
|
|
}, nil
|
|
})
|
|
|
|
mocker.MockFrom(`^tiflash1/#1$`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xA0},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash1/#`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
// For #2~#N requests, always return network errors
|
|
return nil, errors.New("Bad network")
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Sort().Check(testkit.Rows(
|
|
"Warning 1105 compact on store tiflash1 failed: Bad network",
|
|
))
|
|
}
|
|
|
|
// TestCompactTableWithSpecifiedRangePartition: 1 TiFlash, table has 4 partitions.
|
|
// only compact Partition 1: 3 Partials
|
|
// There will be 3 requests sent in series.
|
|
func TestCompactTableWithSpecifiedRangePartition(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, do := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xCC},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Equal(t, []byte{0xCC}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#3`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Equal(t, []byte{0xFF}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0xAA},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec(`
|
|
CREATE TABLE employees (
|
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
fname VARCHAR(25) NOT NULL,
|
|
lname VARCHAR(25) NOT NULL,
|
|
store_id INT NOT NULL,
|
|
department_id INT NOT NULL
|
|
)
|
|
PARTITION BY RANGE(id) (
|
|
PARTITION p0 VALUES LESS THAN (5),
|
|
PARTITION p1 VALUES LESS THAN (10),
|
|
PARTITION p2 VALUES LESS THAN (15),
|
|
PARTITION p3 VALUES LESS THAN MAXVALUE
|
|
);
|
|
`)
|
|
tk.MustExec(`alter table employees set tiflash replica 1;`)
|
|
tk.MustExec(`alter table employees compact partition p1 tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
}
|
|
|
|
// TestCompactTableWithSpecifiedHashPartition: 1 TiFlash, table has 3 partitions (hash partition).
|
|
// only compact p1, p2
|
|
// During compacting the partition, one partition will return failure PhysicalTableNotExist. The remaining partitions should be still compacted.
|
|
func TestCompactTableWithSpecifiedHashPartitionAndOnePartitionFailed(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, do := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(1), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xA0},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 1)
|
|
require.Equal(t, []byte{0xA0}, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
Error: &kvrpcpb.CompactError{Error: &kvrpcpb.CompactError_ErrPhysicalTableNotExist{}}, // For example, may be this partition got dropped
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#3`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
tableID := do.MustGetTableID(t, "test", "employees")
|
|
pid := do.MustGetPartitionAt(t, "test", "employees", 2)
|
|
require.Empty(t, req.StartKey)
|
|
require.EqualValues(t, req.PhysicalTableId, pid)
|
|
require.EqualValues(t, req.LogicalTableId, tableID)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xCD},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec(`
|
|
CREATE TABLE employees (
|
|
id INT NOT NULL,
|
|
fname VARCHAR(30),
|
|
lname VARCHAR(30),
|
|
hired DATE NOT NULL DEFAULT '1970-01-01',
|
|
separated DATE DEFAULT '9999-12-31',
|
|
job_code INT,
|
|
store_id INT
|
|
)
|
|
PARTITION BY HASH(store_id)
|
|
PARTITIONS 3;
|
|
`)
|
|
tk.MustExec(`alter table employees set tiflash replica 1;`)
|
|
tk.MustExec(`alter table employees compact PARTITION p1,p2 tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
}
|
|
|
|
// TestCompactTableWithTiFlashDownAndRestore: 2 TiFlash stores.
|
|
// Store0 - #1 (remaining=true, takes 3s), #2 (remaining=false)
|
|
// Store1 - #1 (remaining=true), #2 (down), #3 (restored, remaining=false)
|
|
func TestCompactTableWithTiFlashDownAndRestore(t *testing.T) {
|
|
mocker := newCompactRequestMocker(t)
|
|
defer mocker.RequireAllHandlersHit()
|
|
store, _ := testkit.CreateMockStoreAndDomain(t, withMockTiFlash(2), mocker.AsOpt())
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
mocker.MockFrom(`tiflash0/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
time.Sleep(time.Second * 3)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash0/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xFF}, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF, 0x00, 0xAA},
|
|
}, nil
|
|
})
|
|
|
|
mocker.MockFrom(`tiflash1/#1`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Empty(t, req.StartKey)
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: true,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xA0},
|
|
}, nil
|
|
})
|
|
mocker.MockFrom(`tiflash1/#2`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xA0}, req.StartKey)
|
|
time.Sleep(time.Second * 1)
|
|
return nil, errors.New("Bad Network")
|
|
})
|
|
mocker.MockFrom(`tiflash1/#3`, func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) {
|
|
require.Equal(t, []byte{0xA0}, req.StartKey) // The same request should be sent again.
|
|
return &kvrpcpb.CompactResponse{
|
|
HasRemaining: false,
|
|
CompactedStartKey: []byte{},
|
|
CompactedEndKey: []byte{0xFF},
|
|
}, nil
|
|
})
|
|
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(a int)")
|
|
tk.MustExec(`alter table t set tiflash replica 1;`)
|
|
tk.MustExec(`alter table t compact tiflash replica;`)
|
|
tk.MustQuery(`show warnings;`).Check(testkit.Rows())
|
|
}
|
|
|
|
// Code below are helper utilities for the test cases.
|
|
|
|
type compactClientHandler struct {
|
|
matcher *regexp.Regexp
|
|
matcherString string
|
|
fn func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error)
|
|
hit atomic.Bool
|
|
}
|
|
|
|
type compactRequestMocker struct {
|
|
tikv.Client
|
|
t *testing.T
|
|
handlers []*compactClientHandler
|
|
|
|
mu syncutil.Mutex
|
|
receivedRequestsOfAddr map[string]int
|
|
}
|
|
|
|
func (client *compactRequestMocker) SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) {
|
|
if req.Type == tikvrpc.CmdCompact {
|
|
client.mu.Lock()
|
|
client.receivedRequestsOfAddr[addr]++
|
|
handlerKey := fmt.Sprintf("%s/#%d", addr, client.receivedRequestsOfAddr[addr])
|
|
client.mu.Unlock()
|
|
for _, handler := range client.handlers {
|
|
if handler.matcher.MatchString(handlerKey) {
|
|
handler.hit.Store(true)
|
|
resp, err := handler.fn(req.Compact())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &tikvrpc.Response{Resp: resp}, nil
|
|
}
|
|
}
|
|
// If we enter here, it means no handler is matching. We should fail!
|
|
require.Fail(client.t, fmt.Sprintf("Received request %s but no matching handler, maybe caused by unexpected number of requests?", handlerKey))
|
|
}
|
|
return client.Client.SendRequest(ctx, addr, req, timeout)
|
|
}
|
|
|
|
func newCompactRequestMocker(t *testing.T) *compactRequestMocker {
|
|
return &compactRequestMocker{
|
|
handlers: make([]*compactClientHandler, 0),
|
|
receivedRequestsOfAddr: make(map[string]int),
|
|
t: t,
|
|
}
|
|
}
|
|
|
|
func (client *compactRequestMocker) MockFrom(matchPattern string, fn func(req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error)) *compactRequestMocker {
|
|
m := regexp.MustCompile(matchPattern)
|
|
client.handlers = append(client.handlers, &compactClientHandler{
|
|
matcher: m,
|
|
matcherString: matchPattern,
|
|
fn: fn,
|
|
hit: atomic.Bool{},
|
|
})
|
|
return client
|
|
}
|
|
|
|
func (client *compactRequestMocker) RequireAllHandlersHit() {
|
|
for _, handler := range client.handlers {
|
|
if !handler.hit.Load() {
|
|
require.Fail(client.t, fmt.Sprintf("Request handler %s did not hit, maybe caused by request was missing?", handler.matcherString))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (client *compactRequestMocker) AsOpt() mockstore.MockTiKVStoreOption {
|
|
return mockstore.WithClientHijacker(func(kvClient tikv.Client) tikv.Client {
|
|
client.Client = kvClient
|
|
return client
|
|
})
|
|
}
|