Files
tidb/br/pkg/metautil/debug.go

135 lines
3.9 KiB
Go

// Copyright 2024 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 metautil
import (
"bytes"
"context"
"crypto/sha256"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/pingcap/errors"
backuppb "github.com/pingcap/kvproto/pkg/brpb"
berrors "github.com/pingcap/tidb/br/pkg/errors"
"github.com/pingcap/tidb/br/pkg/utils"
"github.com/pingcap/tidb/pkg/objstore/storeapi"
tidbutil "github.com/pingcap/tidb/pkg/util"
"golang.org/x/sync/errgroup"
)
const (
// JSONFileFormat represents json file name format
JSONFileFormat = "jsons/%s.json"
)
// DecodeStatsFile decodes the stats file to json format, it is called by br debug
func DecodeStatsFile(
ctx context.Context,
s storeapi.Storage,
cipher *backuppb.CipherInfo,
schemas []*backuppb.Schema,
) error {
for _, schema := range schemas {
for _, statsIndex := range schema.StatsIndex {
if len(statsIndex.Name) == 0 {
continue
}
content, err := s.ReadFile(ctx, statsIndex.Name)
if err != nil {
return errors.Trace(err)
}
decryptContent, err := utils.Decrypt(content, cipher, statsIndex.CipherIv)
if err != nil {
return errors.Trace(err)
}
checksum := sha256.Sum256(decryptContent)
if !bytes.Equal(statsIndex.Sha256, checksum[:]) {
return berrors.ErrInvalidMetaFile.GenWithStackByArgs(fmt.Sprintf(
"checksum mismatch expect %x, got %x", statsIndex.Sha256, checksum[:]))
}
statsFileBlocks := &backuppb.StatsFile{}
if err := proto.Unmarshal(decryptContent, statsFileBlocks); err != nil {
return errors.Trace(err)
}
jsonContent, err := utils.MarshalStatsFile(statsFileBlocks)
if err != nil {
return errors.Trace(err)
}
if err := s.WriteFile(ctx, fmt.Sprintf(JSONFileFormat, statsIndex.Name), jsonContent); err != nil {
return errors.Trace(err)
}
}
}
return nil
}
// DecodeMetaFile decodes the meta file to json format, it is called by br debug
func DecodeMetaFile(
ctx context.Context,
s storeapi.Storage,
cipher *backuppb.CipherInfo,
metaIndex *backuppb.MetaFile,
) error {
if metaIndex == nil {
return nil
}
eg, ectx := errgroup.WithContext(ctx)
workers := tidbutil.NewWorkerPool(8, "download files workers")
for _, node := range metaIndex.MetaFiles {
workers.ApplyOnErrorGroup(eg, func() error {
content, err := s.ReadFile(ectx, node.Name)
if err != nil {
return errors.Trace(err)
}
decryptContent, err := utils.Decrypt(content, cipher, node.CipherIv)
if err != nil {
return errors.Trace(err)
}
checksum := sha256.Sum256(decryptContent)
if !bytes.Equal(node.Sha256, checksum[:]) {
return berrors.ErrInvalidMetaFile.GenWithStackByArgs(fmt.Sprintf(
"checksum mismatch expect %x, got %x", node.Sha256, checksum[:]))
}
child := &backuppb.MetaFile{}
if err = proto.Unmarshal(decryptContent, child); err != nil {
return errors.Trace(err)
}
// the max depth of the root metafile is only 1.
// ASSERT: len(child.MetaFiles) == 0
if len(child.MetaFiles) > 0 {
return errors.Errorf("the metafile has unexpected level: %v", child)
}
jsonContent, err := utils.MarshalMetaFile(child)
if err != nil {
return errors.Trace(err)
}
if err := s.WriteFile(ctx, fmt.Sprintf(JSONFileFormat, node.Name), jsonContent); err != nil {
return errors.Trace(err)
}
err = DecodeStatsFile(ctx, s, cipher, child.Schemas)
return errors.Trace(err)
})
}
return eg.Wait()
}