Files
tidb/br/pkg/checkpoint/checkpoint_test.go
2023-01-18 14:35:51 +08:00

230 lines
6.2 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 checkpoint_test
import (
"context"
"encoding/json"
"os"
"strings"
"testing"
"time"
backuppb "github.com/pingcap/kvproto/pkg/brpb"
"github.com/pingcap/kvproto/pkg/encryptionpb"
"github.com/pingcap/tidb/br/pkg/checkpoint"
"github.com/pingcap/tidb/br/pkg/rtree"
"github.com/pingcap/tidb/br/pkg/storage"
"github.com/stretchr/testify/require"
"github.com/tikv/client-go/v2/oracle"
)
func TestCheckpointMeta(t *testing.T) {
ctx := context.Background()
base := t.TempDir()
s, err := storage.NewLocalStorage(base)
require.NoError(t, err)
checkpointMeta := &checkpoint.CheckpointMetadata{
ConfigHash: []byte("123456"),
BackupTS: 123456,
}
err = checkpoint.SaveCheckpointMetadata(ctx, s, checkpointMeta)
require.NoError(t, err)
checkpointMeta2, err := checkpoint.LoadCheckpointMetadata(ctx, s)
require.NoError(t, err)
require.Equal(t, checkpointMeta.ConfigHash, checkpointMeta2.ConfigHash)
require.Equal(t, checkpointMeta.BackupTS, checkpointMeta2.BackupTS)
}
type mockTimer struct {
p int64
l int64
}
func NewMockTimer(p, l int64) *mockTimer {
return &mockTimer{p: p, l: l}
}
func (t *mockTimer) GetTS(ctx context.Context) (int64, int64, error) {
return t.p, t.l, nil
}
func TestCheckpointRunner(t *testing.T) {
ctx := context.Background()
base := t.TempDir()
s, err := storage.NewLocalStorage(base)
require.NoError(t, err)
os.MkdirAll(base+checkpoint.CheckpointDataDir, 0o755)
os.MkdirAll(base+checkpoint.CheckpointChecksumDir, 0o755)
cipher := &backuppb.CipherInfo{
CipherType: encryptionpb.EncryptionMethod_AES256_CTR,
CipherKey: []byte("01234567890123456789012345678901"),
}
checkpointRunner, err := checkpoint.StartCheckpointRunnerForTest(ctx, s, cipher, 5*time.Second, NewMockTimer(10, 10))
require.NoError(t, err)
data := map[string]struct {
StartKey string
EndKey string
Name string
Name2 string
}{
"a": {
StartKey: "a",
EndKey: "b",
Name: "c",
Name2: "d",
},
"A": {
StartKey: "A",
EndKey: "B",
Name: "C",
Name2: "D",
},
"1": {
StartKey: "1",
EndKey: "2",
Name: "3",
Name2: "4",
},
}
data2 := map[string]struct {
StartKey string
EndKey string
Name string
Name2 string
}{
"+": {
StartKey: "+",
EndKey: "-",
Name: "*",
Name2: "/",
},
}
for _, d := range data {
err = checkpointRunner.Append(ctx, "a", []byte(d.StartKey), []byte(d.EndKey), []*backuppb.File{
{Name: d.Name},
{Name: d.Name2},
})
require.NoError(t, err)
}
checkpointRunner.FlushChecksum(ctx, 1, 1, 1, 1, checkpoint.MaxChecksumTotalCost-20.0)
checkpointRunner.FlushChecksum(ctx, 2, 2, 2, 2, 40.0)
// now the checksum is flushed, because the total time cost is larger than `MaxChecksumTotalCost`
checkpointRunner.FlushChecksum(ctx, 3, 3, 3, 3, checkpoint.MaxChecksumTotalCost-20.0)
time.Sleep(6 * time.Second)
// the checksum has not been flushed even though after 6 seconds,
// because the total time cost is less than `MaxChecksumTotalCost`
checkpointRunner.FlushChecksum(ctx, 4, 4, 4, 4, 40.0)
for _, d := range data2 {
err = checkpointRunner.Append(ctx, "+", []byte(d.StartKey), []byte(d.EndKey), []*backuppb.File{
{Name: d.Name},
{Name: d.Name2},
})
require.NoError(t, err)
}
checkpointRunner.WaitForFinish(ctx)
checker := func(groupKey string, resp *rtree.Range) {
require.NotNil(t, resp)
d, ok := data[string(resp.StartKey)]
if !ok {
d, ok = data2[string(resp.StartKey)]
require.True(t, ok)
}
require.Equal(t, d.StartKey, string(resp.StartKey))
require.Equal(t, d.EndKey, string(resp.EndKey))
require.Equal(t, d.Name, resp.Files[0].Name)
require.Equal(t, d.Name2, resp.Files[1].Name)
}
_, err = checkpoint.WalkCheckpointFile(ctx, s, cipher, checker)
require.NoError(t, err)
checkpointMeta := &checkpoint.CheckpointMetadata{
ConfigHash: []byte("123456"),
BackupTS: 123456,
}
err = checkpoint.SaveCheckpointMetadata(ctx, s, checkpointMeta)
require.NoError(t, err)
meta, err := checkpoint.LoadCheckpointMetadata(ctx, s)
require.NoError(t, err)
var i int64
for i = 1; i <= 4; i++ {
require.Equal(t, meta.CheckpointChecksum[i].Crc64xor, uint64(i))
}
// only 2 checksum files exists, they are t2_and__ and t4_and__
count := 0
err = s.WalkDir(ctx, &storage.WalkOption{SubDir: checkpoint.CheckpointChecksumDir}, func(s string, i int64) error {
count += 1
if !strings.Contains(s, "t2") {
require.True(t, strings.Contains(s, "t4"))
}
return nil
})
require.NoError(t, err)
require.Equal(t, count, 2)
}
func getLockData(p, l int64) ([]byte, error) {
lock := checkpoint.CheckpointLock{
LockId: oracle.ComposeTS(p, l),
ExpireAt: p + 10,
}
return json.Marshal(lock)
}
func TestCheckpointRunnerLock(t *testing.T) {
ctx := context.Background()
base := t.TempDir()
s, err := storage.NewLocalStorage(base)
require.NoError(t, err)
os.MkdirAll(base+checkpoint.CheckpointDataDir, 0o755)
os.MkdirAll(base+checkpoint.CheckpointChecksumDir, 0o755)
cipher := &backuppb.CipherInfo{
CipherType: encryptionpb.EncryptionMethod_AES256_CTR,
CipherKey: []byte("01234567890123456789012345678901"),
}
data, err := getLockData(10, 20)
require.NoError(t, err)
err = s.WriteFile(ctx, checkpoint.CheckpointLockPath, data)
require.NoError(t, err)
_, err = checkpoint.StartCheckpointRunnerForTest(ctx, s, cipher, 5*time.Second, NewMockTimer(10, 10))
require.Error(t, err)
runner, err := checkpoint.StartCheckpointRunnerForTest(ctx, s, cipher, 5*time.Second, NewMockTimer(30, 10))
require.NoError(t, err)
_, err = checkpoint.StartCheckpointRunnerForTest(ctx, s, cipher, 5*time.Second, NewMockTimer(40, 10))
require.Error(t, err)
runner.WaitForFinish(ctx)
}