enhance: Make GetReplicateInfo API work at the pchannel level (#44809)

issue: https://github.com/milvus-io/milvus/issues/44123

Signed-off-by: bigsheeper <yihao.dai@zilliz.com>
This commit is contained in:
yihao.dai 2025-10-14 15:12:00 +08:00 committed by GitHub
parent 2ea8d85c2f
commit cebe923d4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 54 additions and 64 deletions

View File

@ -6,15 +6,13 @@ require (
github.com/blang/semver/v4 v4.0.0
github.com/cockroachdb/errors v1.9.1
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654
github.com/milvus-io/milvus/pkg/v2 v2.6.3
github.com/quasilyte/go-ruleguard/dsl v0.3.23
github.com/samber/lo v1.27.0
github.com/stretchr/testify v1.11.1
github.com/tidwall/gjson v1.17.1
go.opentelemetry.io/otel v1.34.0
go.uber.org/atomic v1.11.0
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
google.golang.org/grpc v1.71.0
google.golang.org/protobuf v1.36.5
)
@ -93,6 +91,7 @@ require (
go.etcd.io/etcd/server/v3 v3.5.5 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
@ -103,6 +102,7 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.35.0 // indirect

View File

@ -322,8 +322,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3 h1:w7IBrU25KULWNlHKoKwx6ruTsDAmzrWknotIc6A4ys4=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654 h1:p604i9izeR8eWrQhOFmcmxhNhYlsvTkkmph4b2GbOeg=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus/pkg/v2 v2.6.3 h1:WDf4mXFWL5Sk/V87yLwRKq24MYMkjS2YA6qraXbLbJA=
github.com/milvus-io/milvus/pkg/v2 v2.6.3/go.mod h1:49umaGHK9nKHJNtgBlF/iB24s1sZ/SG5/Q7iLj/Gc14=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=

View File

@ -26,10 +26,7 @@ func (c *Client) UpdateReplicateConfiguration(ctx context.Context, config *commo
}
// GetReplicateInfo gets replicate information from the Milvus cluster
func (c *Client) GetReplicateInfo(ctx context.Context, sourceClusterID string, opts ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error) {
req := &milvuspb.GetReplicateInfoRequest{
SourceClusterId: sourceClusterID,
}
func (c *Client) GetReplicateInfo(ctx context.Context, req *milvuspb.GetReplicateInfoRequest, opts ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error) {
var resp *milvuspb.GetReplicateInfoResponse
err := c.callService(func(milvusService milvuspb.MilvusServiceClient) error {
var err error

View File

@ -21,6 +21,7 @@ import (
"context"
"log"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
@ -81,13 +82,16 @@ func ExampleClient_GetReplicateInfo() {
}
// Get replicate information for a specific source cluster
resp, err := cli.GetReplicateInfo(ctx, "source-cluster")
resp, err := cli.GetReplicateInfo(ctx, &milvuspb.GetReplicateInfoRequest{
SourceClusterId: "source-cluster",
TargetPchannel: "source-channel-dml_0",
})
if err != nil {
log.Printf("Failed to get replicate information: %v", err)
return
}
log.Printf("Replicate information retrieved successfully, checkpoint count: %d", len(resp.GetCheckpoints()))
log.Printf("Replicate information retrieved successfully, checkpoint: %v", resp.GetCheckpoint())
}
func ExampleClient_CreateReplicateStream() {

2
go.mod
View File

@ -21,7 +21,7 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/klauspost/compress v1.17.9
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654
github.com/minio/minio-go/v7 v7.0.73
github.com/panjf2000/ants/v2 v2.11.3 // indirect
github.com/pingcap/log v1.1.1-0.20221015072633-39906604fb81 // indirect

4
go.sum
View File

@ -794,8 +794,8 @@ github.com/milvus-io/cgosymbolizer v0.0.0-20250318084424-114f4050c3a6 h1:YHMFI6L
github.com/milvus-io/cgosymbolizer v0.0.0-20250318084424-114f4050c3a6/go.mod h1:DvXTE/K/RtHehxU8/GtDs4vFtfw64jJ3PaCnFri8CRg=
github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b h1:TfeY0NxYxZzUfIfYe5qYDBzt4ZYRqzUjTR6CvUzjat8=
github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b/go.mod h1:iwW+9cWfIzzDseEBCCeDSN5SD16Tidvy8cwQ7ZY8Qj4=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3 h1:w7IBrU25KULWNlHKoKwx6ruTsDAmzrWknotIc6A4ys4=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654 h1:p604i9izeR8eWrQhOFmcmxhNhYlsvTkkmph4b2GbOeg=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs=
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI=

View File

@ -26,7 +26,7 @@ import (
type MilvusClient interface {
// GetReplicateInfo gets the replicate information from the milvus cluster.
GetReplicateInfo(ctx context.Context, sourceClusterID string, opts ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error)
GetReplicateInfo(ctx context.Context, req *milvuspb.GetReplicateInfoRequest, opts ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error)
// CreateReplicateStream creates a replicate stream to the milvus cluster.
CreateReplicateStream(ctx context.Context, opts ...grpc.CallOption) (milvuspb.MilvusService_CreateReplicateStreamClient, error)
// Close closes the milvus client.

View File

@ -144,14 +144,14 @@ func (_c *MockMilvusClient_CreateReplicateStream_Call) RunAndReturn(run func(con
return _c
}
// GetReplicateInfo provides a mock function with given fields: ctx, sourceClusterID, opts
func (_m *MockMilvusClient) GetReplicateInfo(ctx context.Context, sourceClusterID string, opts ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error) {
// GetReplicateInfo provides a mock function with given fields: ctx, req, opts
func (_m *MockMilvusClient) GetReplicateInfo(ctx context.Context, req *milvuspb.GetReplicateInfoRequest, opts ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, sourceClusterID)
_ca = append(_ca, ctx, req)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
@ -161,19 +161,19 @@ func (_m *MockMilvusClient) GetReplicateInfo(ctx context.Context, sourceClusterI
var r0 *milvuspb.GetReplicateInfoResponse
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error)); ok {
return rf(ctx, sourceClusterID, opts...)
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.GetReplicateInfoRequest, ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error)); ok {
return rf(ctx, req, opts...)
}
if rf, ok := ret.Get(0).(func(context.Context, string, ...grpc.CallOption) *milvuspb.GetReplicateInfoResponse); ok {
r0 = rf(ctx, sourceClusterID, opts...)
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.GetReplicateInfoRequest, ...grpc.CallOption) *milvuspb.GetReplicateInfoResponse); ok {
r0 = rf(ctx, req, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*milvuspb.GetReplicateInfoResponse)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string, ...grpc.CallOption) error); ok {
r1 = rf(ctx, sourceClusterID, opts...)
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.GetReplicateInfoRequest, ...grpc.CallOption) error); ok {
r1 = rf(ctx, req, opts...)
} else {
r1 = ret.Error(1)
}
@ -188,14 +188,14 @@ type MockMilvusClient_GetReplicateInfo_Call struct {
// GetReplicateInfo is a helper method to define mock.On call
// - ctx context.Context
// - sourceClusterID string
// - req *milvuspb.GetReplicateInfoRequest
// - opts ...grpc.CallOption
func (_e *MockMilvusClient_Expecter) GetReplicateInfo(ctx interface{}, sourceClusterID interface{}, opts ...interface{}) *MockMilvusClient_GetReplicateInfo_Call {
func (_e *MockMilvusClient_Expecter) GetReplicateInfo(ctx interface{}, req interface{}, opts ...interface{}) *MockMilvusClient_GetReplicateInfo_Call {
return &MockMilvusClient_GetReplicateInfo_Call{Call: _e.mock.On("GetReplicateInfo",
append([]interface{}{ctx, sourceClusterID}, opts...)...)}
append([]interface{}{ctx, req}, opts...)...)}
}
func (_c *MockMilvusClient_GetReplicateInfo_Call) Run(run func(ctx context.Context, sourceClusterID string, opts ...grpc.CallOption)) *MockMilvusClient_GetReplicateInfo_Call {
func (_c *MockMilvusClient_GetReplicateInfo_Call) Run(run func(ctx context.Context, req *milvuspb.GetReplicateInfoRequest, opts ...grpc.CallOption)) *MockMilvusClient_GetReplicateInfo_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]grpc.CallOption, len(args)-2)
for i, a := range args[2:] {
@ -203,7 +203,7 @@ func (_c *MockMilvusClient_GetReplicateInfo_Call) Run(run func(ctx context.Conte
variadicArgs[i] = a.(grpc.CallOption)
}
}
run(args[0].(context.Context), args[1].(string), variadicArgs...)
run(args[0].(context.Context), args[1].(*milvuspb.GetReplicateInfoRequest), variadicArgs...)
})
return _c
}
@ -213,7 +213,7 @@ func (_c *MockMilvusClient_GetReplicateInfo_Call) Return(_a0 *milvuspb.GetReplic
return _c
}
func (_c *MockMilvusClient_GetReplicateInfo_Call) RunAndReturn(run func(context.Context, string, ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error)) *MockMilvusClient_GetReplicateInfo_Call {
func (_c *MockMilvusClient_GetReplicateInfo_Call) RunAndReturn(run func(context.Context, *milvuspb.GetReplicateInfoRequest, ...grpc.CallOption) (*milvuspb.GetReplicateInfoResponse, error)) *MockMilvusClient_GetReplicateInfo_Call {
_c.Call.Return(run)
return _c
}

View File

@ -24,7 +24,7 @@ import (
"github.com/cockroachdb/errors"
"go.uber.org/zap"
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/internal/cdc/replication/replicatestream"
"github.com/milvus-io/milvus/internal/cdc/resource"
"github.com/milvus-io/milvus/internal/distributed/streaming"
@ -157,18 +157,16 @@ func (r *channelReplicator) getReplicateCheckpoint() (*utility.ReplicateCheckpoi
defer milvusClient.Close(ctx)
sourceClusterID := paramtable.Get().CommonCfg.ClusterPrefix.GetValue()
replicateInfo, err := milvusClient.GetReplicateInfo(ctx, sourceClusterID)
req := &milvuspb.GetReplicateInfoRequest{
SourceClusterId: sourceClusterID,
TargetPchannel: r.replicateInfo.GetTargetChannelName(),
}
replicateInfo, err := milvusClient.GetReplicateInfo(ctx, req)
if err != nil {
return nil, err
}
var checkpoint *commonpb.ReplicateCheckpoint
for _, cp := range replicateInfo.GetCheckpoints() {
if cp.GetPchannel() == r.replicateInfo.GetSourceChannelName() {
checkpoint = cp
break
}
}
checkpoint := replicateInfo.GetCheckpoint()
if checkpoint == nil || checkpoint.MessageId == nil {
initializedCheckpoint := utility.NewReplicateCheckpointFromProto(r.replicateInfo.InitializedCheckpoint)
logger.Info("channel not found in replicate info, will start from the beginning",

View File

@ -49,11 +49,9 @@ func TestChannelReplicator_StartReplicateChannel(t *testing.T) {
mockMilvusClient := cluster.NewMockMilvusClient(t)
mockMilvusClient.EXPECT().GetReplicateInfo(mock.Anything, mock.Anything).
Return(&milvuspb.GetReplicateInfoResponse{
Checkpoints: []*commonpb.ReplicateCheckpoint{
{
Pchannel: "test-source-channel",
MessageId: newMockPulsarMessageID(),
},
Checkpoint: &commonpb.ReplicateCheckpoint{
Pchannel: "test-source-channel",
MessageId: newMockPulsarMessageID(),
},
}, nil)
mockMilvusClient.EXPECT().Close(mock.Anything).Return(nil)

View File

@ -6549,32 +6549,25 @@ func (node *Proxy) GetReplicateInfo(ctx context.Context, req *milvuspb.GetReplic
return nil, err
}
logger := log.Ctx(ctx).With(zap.String("sourceClusterID", req.GetSourceClusterId()))
logger := log.Ctx(ctx).With(
zap.String("sourceClusterID", req.GetSourceClusterId()),
zap.String("pchannel", req.GetTargetPchannel()),
)
logger.Info("GetReplicateInfo received")
defer func() {
if err != nil {
logger.Warn("GetReplicateInfo fail", zap.Error(err))
} else {
logger.Info("GetReplicateInfo success", zap.Any("checkpoints", resp.GetCheckpoints()))
logger.Info("GetReplicateInfo success", zap.Any("checkpoint", resp.GetCheckpoint()))
}
}()
configHelper, err := streaming.WAL().Replicate().GetReplicateConfiguration(ctx)
checkpoint, err := streaming.WAL().Replicate().GetReplicateCheckpoint(ctx, req.GetTargetPchannel())
if err != nil {
return nil, err
}
currentCluster := configHelper.GetCurrentCluster()
checkpoints := make([]*commonpb.ReplicateCheckpoint, 0, len(currentCluster.GetPchannels()))
for _, pchannel := range currentCluster.GetPchannels() {
checkpoint, err := streaming.WAL().Replicate().GetReplicateCheckpoint(ctx, pchannel)
if err != nil {
return nil, err
}
checkpoints = append(checkpoints, checkpoint.IntoProto())
}
return &milvuspb.GetReplicateInfoResponse{
Checkpoints: checkpoints,
Checkpoint: checkpoint.IntoProto(),
}, nil
}

View File

@ -21,7 +21,7 @@ require (
github.com/jolestar/go-commons-pool/v2 v2.1.2
github.com/json-iterator/go v1.1.12
github.com/klauspost/compress v1.17.9
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654
github.com/minio/minio-go/v7 v7.0.73
github.com/panjf2000/ants/v2 v2.11.3
github.com/prometheus/client_golang v1.20.5

View File

@ -605,8 +605,8 @@ github.com/milvus-io/cgosymbolizer v0.0.0-20250318084424-114f4050c3a6 h1:YHMFI6L
github.com/milvus-io/cgosymbolizer v0.0.0-20250318084424-114f4050c3a6/go.mod h1:DvXTE/K/RtHehxU8/GtDs4vFtfw64jJ3PaCnFri8CRg=
github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b h1:TfeY0NxYxZzUfIfYe5qYDBzt4ZYRqzUjTR6CvUzjat8=
github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b/go.mod h1:iwW+9cWfIzzDseEBCCeDSN5SD16Tidvy8cwQ7ZY8Qj4=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3 h1:w7IBrU25KULWNlHKoKwx6ruTsDAmzrWknotIc6A4ys4=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654 h1:p604i9izeR8eWrQhOFmcmxhNhYlsvTkkmph4b2GbOeg=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.73 h1:qr2vi96Qm7kZ4v7LLebjte+MQh621fFWnv93p12htEo=

View File

@ -54,7 +54,7 @@ require (
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20240226150601-1dcf7310316a // indirect
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3 // indirect
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect

View File

@ -330,8 +330,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3 h1:w7IBrU25KULWNlHKoKwx6ruTsDAmzrWknotIc6A4ys4=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.3/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654 h1:p604i9izeR8eWrQhOFmcmxhNhYlsvTkkmph4b2GbOeg=
github.com/milvus-io/milvus-proto/go-api/v2 v2.6.4-0.20251013093953-f3e0a710c654/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus/pkg/v2 v2.6.3 h1:WDf4mXFWL5Sk/V87yLwRKq24MYMkjS2YA6qraXbLbJA=
github.com/milvus-io/milvus/pkg/v2 v2.6.3/go.mod h1:49umaGHK9nKHJNtgBlF/iB24s1sZ/SG5/Q7iLj/Gc14=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=