You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by mi...@apache.org on 2023/04/23 07:34:07 UTC

[shardingsphere-on-cloud] branch main updated: perf(pitr): check status of agent servers before do backup (#318)

This is an automated email from the ASF dual-hosted git repository.

miaoliyao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/shardingsphere-on-cloud.git


The following commit(s) were added to refs/heads/main by this push:
     new 3e8aef9  perf(pitr): check status of agent servers before do backup (#318)
3e8aef9 is described below

commit 3e8aef993fb8f91b84f8ffe2eac5f606a7dcf11d
Author: Xu-Wentao <cu...@yahoo.com>
AuthorDate: Sun Apr 23 15:34:02 2023 +0800

    perf(pitr): check status of agent servers before do backup (#318)
---
 pitr/cli/go.mod                               |  5 ++-
 pitr/cli/go.sum                               | 14 ++++---
 pitr/cli/internal/cmd/backup.go               | 44 ++++++++++++++++++++++
 pitr/cli/internal/cmd/backup_test.go          | 54 +++++++++++++++++++++++++++
 pitr/cli/internal/pkg/agent-server.go         | 18 +++++++++
 pitr/cli/internal/pkg/mocks/agent-server.go   | 44 ++++++++++++++--------
 pitr/cli/internal/pkg/model/as_backup.go      |  5 +++
 pitr/cli/internal/pkg/shardingsphere-proxy.go |  5 ++-
 8 files changed, 166 insertions(+), 23 deletions(-)

diff --git a/pitr/cli/go.mod b/pitr/cli/go.mod
index 14b51d0..8d5a5f2 100644
--- a/pitr/cli/go.mod
+++ b/pitr/cli/go.mod
@@ -5,10 +5,9 @@ go 1.20
 require (
 	bou.ke/monkey v1.0.2
 	gitee.com/opengauss/openGauss-connector-go-pq v1.0.4
-	github.com/DATA-DOG/go-sqlmock v1.5.0
 	github.com/golang/mock v1.6.0
 	github.com/google/uuid v1.3.0
-	github.com/jarcoal/httpmock v1.3.0
+	github.com/jedib0t/go-pretty/v6 v6.4.6
 	github.com/onsi/ginkgo/v2 v2.9.1
 	github.com/onsi/gomega v1.27.3
 	github.com/spf13/cobra v1.6.1
@@ -24,7 +23,9 @@ require (
 	github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
 	github.com/inconshreveable/mousetrap v1.0.1 // indirect
 	github.com/kr/pretty v0.3.0 // indirect
+	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
+	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/rogpeppe/go-internal v1.8.0 // indirect
 	github.com/stretchr/testify v1.8.1 // indirect
 	github.com/tjfoc/gmsm v1.4.1 // indirect
diff --git a/pitr/cli/go.sum b/pitr/cli/go.sum
index 3855af1..c85685e 100644
--- a/pitr/cli/go.sum
+++ b/pitr/cli/go.sum
@@ -4,8 +4,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
 gitee.com/opengauss/openGauss-connector-go-pq v1.0.4 h1:npfLM9/QpkmdK+XY9X2pcC2EX5gosyn/6dRDRd2sEJs=
 gitee.com/opengauss/openGauss-connector-go-pq v1.0.4/go.mod h1:2UEp+ug6ls6C0pLfZgBn7VBzBntFUzxJuy+6FlQ7qyI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
-github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
 github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -54,8 +52,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
 github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
-github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
+github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw=
+github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -64,7 +62,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk=
 github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
 github.com/onsi/gomega v1.27.3 h1:5VwIwnBY3vbBDOJrNtA4rVdiTZCsq9B5F12pvy1Drmk=
@@ -72,9 +71,12 @@ github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557c
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
@@ -90,6 +92,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
@@ -143,6 +146,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
diff --git a/pitr/cli/internal/cmd/backup.go b/pitr/cli/internal/cmd/backup.go
index 5681fd8..25956fb 100644
--- a/pitr/cli/internal/cmd/backup.go
+++ b/pitr/cli/internal/cmd/backup.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
 	"fmt"
+	"os"
 	"sync"
 	"time"
 
@@ -27,6 +28,7 @@ import (
 	"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/xerr"
 	"github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/logging"
 	"github.com/google/uuid"
+	"github.com/jedib0t/go-pretty/v6/table"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 	"golang.org/x/sync/errgroup"
@@ -221,6 +223,12 @@ func execBackup(lsBackup *model.LsBackup) error {
 	dnCh := make(chan *model.DataNode, len(sNodes))
 	g := new(errgroup.Group)
 
+	logging.Info("Checking agent server status...")
+	available := checkAgentServerStatus(lsBackup)
+	if !available {
+		return xerr.NewCliErr("one or more agent server are not available.")
+	}
+
 	logging.Info("Starting send backup command to agent server...")
 
 	for _, node := range sNodes {
@@ -253,6 +261,42 @@ func execBackup(lsBackup *model.LsBackup) error {
 	return nil
 }
 
+func checkAgentServerStatus(lsBackup *model.LsBackup) bool {
+
+	statusList := make([]*model.AgentServerStatus, 0)
+
+	// all agent server are available
+	available := true
+
+	for _, node := range lsBackup.SsBackup.StorageNodes {
+		n := node
+		agentHost := n.IP
+		if agentHost == "127.0.0.1" {
+			agentHost = Host
+		}
+		as := pkg.NewAgentServer(fmt.Sprintf("%s:%d", agentHost, AgentPort))
+		if err := as.CheckStatus(); err != nil {
+			statusList = append(statusList, &model.AgentServerStatus{IP: n.IP, Status: "Unavailable"})
+			available = false
+		} else {
+			statusList = append(statusList, &model.AgentServerStatus{IP: n.IP, Status: "Available"})
+		}
+	}
+	t := table.NewWriter()
+	t.SetOutputMirror(os.Stdout)
+	t.SetTitle("Agent Server Status")
+	t.AppendHeader(table.Row{"#", "Agent Server IP", "Status"})
+
+	for i, s := range statusList {
+		t.AppendRow([]interface{}{i + 1, s.IP, s.Status})
+		t.AppendSeparator()
+	}
+
+	t.Render()
+
+	return available
+}
+
 func _execBackup(as pkg.IAgentServer, node *model.StorageNode, dnCh chan *model.DataNode) error {
 	in := &model.BackupIn{
 		DBPort:       node.Port,
diff --git a/pitr/cli/internal/cmd/backup_test.go b/pitr/cli/internal/cmd/backup_test.go
index 8b77830..1f56485 100644
--- a/pitr/cli/internal/cmd/backup_test.go
+++ b/pitr/cli/internal/cmd/backup_test.go
@@ -18,6 +18,7 @@
 package cmd
 
 import (
+	"context"
 	"errors"
 
 	"bou.ke/monkey"
@@ -25,6 +26,8 @@ import (
 	mock_pkg "github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/mocks"
 	"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/model"
 	"github.com/apache/shardingsphere-on-cloud/pitr/cli/internal/pkg/xerr"
+	"github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/httputils"
+	mock_httputils "github.com/apache/shardingsphere-on-cloud/pitr/cli/pkg/httputils/mocks"
 	"github.com/golang/mock/gomock"
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
@@ -276,4 +279,55 @@ var _ = Describe("test backup mock", func() {
 			Expect(backup()).To(BeNil())
 		})
 	})
+
+	Context("test check agent server status", func() {
+		var mockCtrl *gomock.Controller
+		var mockIreq *mock_httputils.MockIreq
+
+		ls := &model.LsBackup{
+			Info:   nil,
+			DnList: nil,
+			SsBackup: &model.SsBackup{
+				StorageNodes: []*model.StorageNode{
+					{
+						IP:   "127.0.0.1",
+						Port: 3306,
+					},
+					{
+						IP:   "127.0.0.2",
+						Port: 3307,
+					},
+				},
+			},
+		}
+		BeforeEach(func() {
+			mockCtrl = gomock.NewController(GinkgoT())
+			mockIreq = mock_httputils.NewMockIreq(mockCtrl)
+
+			monkey.Patch(httputils.NewRequest, func(ctx context.Context, method, url string) httputils.Ireq {
+				return mockIreq
+			})
+		})
+
+		AfterEach(func() {
+			monkey.UnpatchAll()
+			mockCtrl.Finish()
+		})
+
+		It("agent server is not running", func() {
+			mockIreq.EXPECT().Send(gomock.Any()).Return(-1, errors.New("error")).AnyTimes()
+			Expect(checkAgentServerStatus(ls)).To(BeFalse())
+		})
+
+		It("agent server are running", func() {
+			mockIreq.EXPECT().Send(gomock.Any()).Return(200, nil).AnyTimes()
+			Expect(checkAgentServerStatus(ls)).To(BeTrue())
+		})
+
+		It("one agent server is not running", func() {
+			mockIreq.EXPECT().Send(gomock.Any()).Return(500, nil)
+			mockIreq.EXPECT().Send(gomock.Any()).Return(200, nil).AnyTimes()
+			Expect(checkAgentServerStatus(ls)).To(BeFalse())
+		})
+	})
 })
diff --git a/pitr/cli/internal/pkg/agent-server.go b/pitr/cli/internal/pkg/agent-server.go
index 393d14f..a823a93 100644
--- a/pitr/cli/internal/pkg/agent-server.go
+++ b/pitr/cli/internal/pkg/agent-server.go
@@ -38,12 +38,15 @@ type agentServer struct {
 }
 
 type IAgentServer interface {
+	CheckStatus() error
 	Backup(in *model.BackupIn) (string, error)
 	Restore(in *model.RestoreIn) error
 	ShowDetail(in *model.ShowDetailIn) (*model.BackupInfo, error)
 	ShowList(in *model.ShowListIn) ([]model.BackupInfo, error)
 }
 
+var _ IAgentServer = (*agentServer)(nil)
+
 func NewAgentServer(addr string) IAgentServer {
 	return &agentServer{
 		addr: addr,
@@ -55,6 +58,21 @@ func NewAgentServer(addr string) IAgentServer {
 	}
 }
 
+// CheckStatus check agent server is alive
+func (as *agentServer) CheckStatus() error {
+	url := fmt.Sprintf("%s/%s", as.addr, "ping")
+	r := httputils.NewRequest(context.Background(), http.MethodGet, url)
+	httpCode, err := r.Send(nil)
+	if err != nil {
+		efmt := "httputils.NewRequest[url=%s] return err=%s,wrap=%w"
+		return fmt.Errorf(efmt, url, err, xerr.NewCliErr(xerr.Unknown))
+	}
+	if httpCode != http.StatusOK {
+		return fmt.Errorf("httpCode=%d", httpCode)
+	}
+	return nil
+}
+
 func (as *agentServer) Backup(in *model.BackupIn) (string, error) {
 	url := fmt.Sprintf("%s%s", as.addr, as._apiBackup)
 
diff --git a/pitr/cli/internal/pkg/mocks/agent-server.go b/pitr/cli/internal/pkg/mocks/agent-server.go
index e5cbb95..e2fc4c1 100644
--- a/pitr/cli/internal/pkg/mocks/agent-server.go
+++ b/pitr/cli/internal/pkg/mocks/agent-server.go
@@ -1,22 +1,22 @@
 /*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-* contributor license agreements.  See the NOTICE file distributed with
-* this work for additional information regarding copyright ownership.
-* The ASF licenses this file to You 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.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
  */
 
 // Code generated by MockGen. DO NOT EDIT.
-// Source: internal/pkg/agent-server.go
+// Source: agent-server.go
 
 // Package mock_pkg is a generated GoMock package.
 package mock_pkg
@@ -66,6 +66,20 @@ func (mr *MockIAgentServerMockRecorder) Backup(in interface{}) *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Backup", reflect.TypeOf((*MockIAgentServer)(nil).Backup), in)
 }
 
+// CheckStatus mocks base method.
+func (m *MockIAgentServer) CheckStatus() error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CheckStatus")
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// CheckStatus indicates an expected call of CheckStatus.
+func (mr *MockIAgentServerMockRecorder) CheckStatus() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckStatus", reflect.TypeOf((*MockIAgentServer)(nil).CheckStatus))
+}
+
 // Restore mocks base method.
 func (m *MockIAgentServer) Restore(in *model.RestoreIn) error {
 	m.ctrl.T.Helper()
diff --git a/pitr/cli/internal/pkg/model/as_backup.go b/pitr/cli/internal/pkg/model/as_backup.go
index 63c45ef..bd18148 100644
--- a/pitr/cli/internal/pkg/model/as_backup.go
+++ b/pitr/cli/internal/pkg/model/as_backup.go
@@ -40,3 +40,8 @@ type (
 		Data BackupOut `json:"data"`
 	}
 )
+
+type AgentServerStatus struct {
+	IP     string `json:"ip"`
+	Status string `json:"status"`
+}
diff --git a/pitr/cli/internal/pkg/shardingsphere-proxy.go b/pitr/cli/internal/pkg/shardingsphere-proxy.go
index 5fa9f2a..d66a4de 100644
--- a/pitr/cli/internal/pkg/shardingsphere-proxy.go
+++ b/pitr/cli/internal/pkg/shardingsphere-proxy.go
@@ -126,7 +126,10 @@ func (ss *shardingSphereProxy) ExportMetaData() (*model.ClusterInfo, error) {
 		return nil, fmt.Errorf("json unmarshal return err=%s", err)
 	}
 
-	out.SnapshotInfo = nil
+	if out.SnapshotInfo != nil && out.SnapshotInfo.Csn == "" {
+		out.SnapshotInfo = nil
+	}
+
 	return &out, nil
 }