github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/sink/codec/avro_test.go (about) 1 // Copyright 2020 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package codec 15 16 import ( 17 "context" 18 "time" 19 20 "github.com/linkedin/goavro/v2" 21 "github.com/pingcap/check" 22 "github.com/pingcap/errors" 23 "github.com/pingcap/log" 24 model2 "github.com/pingcap/parser/model" 25 "github.com/pingcap/parser/mysql" 26 "github.com/pingcap/ticdc/cdc/model" 27 "github.com/pingcap/ticdc/cdc/puller" 28 "github.com/pingcap/ticdc/pkg/regionspan" 29 "github.com/pingcap/ticdc/pkg/security" 30 "github.com/pingcap/ticdc/pkg/util/testleak" 31 "github.com/pingcap/tidb/types" 32 "go.uber.org/zap" 33 ) 34 35 type avroBatchEncoderSuite struct { 36 encoder *AvroEventBatchEncoder 37 } 38 39 var _ = check.Suite(&avroBatchEncoderSuite{}) 40 41 func (s *avroBatchEncoderSuite) SetUpSuite(c *check.C) { 42 startHTTPInterceptForTestingRegistry(c) 43 44 keyManager, err := NewAvroSchemaManager(context.Background(), &security.Credential{}, "http://127.0.0.1:8081", "-key") 45 c.Assert(err, check.IsNil) 46 47 valueManager, err := NewAvroSchemaManager(context.Background(), &security.Credential{}, "http://127.0.0.1:8081", "-value") 48 c.Assert(err, check.IsNil) 49 50 s.encoder = &AvroEventBatchEncoder{ 51 valueSchemaManager: valueManager, 52 keySchemaManager: keyManager, 53 resultBuf: make([]*MQMessage, 0, 4096), 54 } 55 } 56 57 func (s *avroBatchEncoderSuite) TearDownSuite(c *check.C) { 58 stopHTTPInterceptForTestingRegistry() 59 } 60 61 func (s *avroBatchEncoderSuite) TestAvroEncodeOnly(c *check.C) { 62 defer testleak.AfterTest(c)() 63 avroCodec, err := goavro.NewCodec(` 64 { 65 "type": "record", 66 "name": "test1", 67 "fields" : [ 68 {"name": "id", "type": ["null", "int"], "default": null}, 69 {"name": "myint", "type": ["null", "int"], "default": null}, 70 {"name": "mybool", "type": ["null", "int"], "default": null}, 71 {"name": "myfloat", "type": ["null", "float"], "default": null}, 72 {"name": "mybytes", "type": ["null", "bytes"], "default": null}, 73 {"name": "ts", "type": ["null", {"type": "long", "logicalType": "timestamp-millis"}], "default": null} 74 ] 75 }`) 76 77 c.Assert(err, check.IsNil) 78 79 table := model.TableName{ 80 Schema: "testdb", 81 Table: "test1", 82 } 83 84 r, err := avroEncode(&table, s.encoder.valueSchemaManager, 1, []*model.Column{ 85 {Name: "id", Value: int64(1), Type: mysql.TypeLong}, 86 {Name: "myint", Value: int64(2), Type: mysql.TypeLong}, 87 {Name: "mybool", Value: int64(1), Type: mysql.TypeTiny}, 88 {Name: "myfloat", Value: float64(3.14), Type: mysql.TypeFloat}, 89 {Name: "mybytes", Value: []byte("Hello World"), Type: mysql.TypeBlob}, 90 {Name: "ts", Value: time.Now().Format(types.TimeFSPFormat), Type: mysql.TypeTimestamp}, 91 }, time.Local) 92 c.Assert(err, check.IsNil) 93 94 res, _, err := avroCodec.NativeFromBinary(r.data) 95 c.Check(err, check.IsNil) 96 c.Check(res, check.NotNil) 97 98 txt, err := avroCodec.TextualFromNative(nil, res) 99 c.Check(err, check.IsNil) 100 log.Info("TestAvroEncodeOnly", zap.ByteString("result", txt)) 101 } 102 103 func (s *avroBatchEncoderSuite) TestAvroTimeZone(c *check.C) { 104 defer testleak.AfterTest(c)() 105 avroCodec, err := goavro.NewCodec(` 106 { 107 "type": "record", 108 "name": "test1", 109 "fields" : [ 110 {"name": "id", "type": ["null", "int"], "default": null}, 111 {"name": "myint", "type": ["null", "int"], "default": null}, 112 {"name": "mybool", "type": ["null", "int"], "default": null}, 113 {"name": "myfloat", "type": ["null", "float"], "default": null}, 114 {"name": "mybytes", "type": ["null", "bytes"], "default": null}, 115 {"name": "ts", "type": ["null", {"type": "long", "logicalType": "timestamp-millis"}], "default": null} 116 ] 117 }`) 118 119 c.Assert(err, check.IsNil) 120 121 table := model.TableName{ 122 Schema: "testdb", 123 Table: "test1", 124 } 125 126 location, err := time.LoadLocation("UTC") 127 c.Check(err, check.IsNil) 128 129 timestamp := time.Now() 130 r, err := avroEncode(&table, s.encoder.valueSchemaManager, 1, []*model.Column{ 131 {Name: "id", Value: int64(1), Type: mysql.TypeLong}, 132 {Name: "myint", Value: int64(2), Type: mysql.TypeLong}, 133 {Name: "mybool", Value: int64(1), Type: mysql.TypeTiny}, 134 {Name: "myfloat", Value: float64(3.14), Type: mysql.TypeFloat}, 135 {Name: "mybytes", Value: []byte("Hello World"), Type: mysql.TypeBlob}, 136 {Name: "ts", Value: timestamp.In(location).Format(types.TimeFSPFormat), Type: mysql.TypeTimestamp}, 137 }, location) 138 c.Assert(err, check.IsNil) 139 140 res, _, err := avroCodec.NativeFromBinary(r.data) 141 c.Check(err, check.IsNil) 142 c.Check(res, check.NotNil) 143 actual := (res.(map[string]interface{}))["ts"].(map[string]interface{})["long.timestamp-millis"].(time.Time) 144 c.Check(actual.Local().Sub(timestamp), check.LessEqual, time.Millisecond) 145 } 146 147 func (s *avroBatchEncoderSuite) TestAvroEnvelope(c *check.C) { 148 defer testleak.AfterTest(c)() 149 avroCodec, err := goavro.NewCodec(` 150 { 151 "type": "record", 152 "name": "test2", 153 "fields" : [ 154 {"name": "id", "type": "int", "default": 0} 155 ] 156 }`) 157 158 c.Assert(err, check.IsNil) 159 160 testNativeData := make(map[string]interface{}) 161 testNativeData["id"] = 7 162 163 bin, err := avroCodec.BinaryFromNative(nil, testNativeData) 164 c.Check(err, check.IsNil) 165 166 res := avroEncodeResult{ 167 data: bin, 168 registryID: 7, 169 } 170 171 evlp, err := res.toEnvelope() 172 c.Check(err, check.IsNil) 173 174 c.Assert(evlp[0], check.Equals, magicByte) 175 c.Assert(evlp[1:5], check.BytesEquals, []byte{0, 0, 0, 7}) 176 177 parsed, _, err := avroCodec.NativeFromBinary(evlp[5:]) 178 c.Assert(err, check.IsNil) 179 c.Assert(parsed, check.NotNil) 180 181 id, exists := parsed.(map[string]interface{})["id"] 182 c.Assert(exists, check.IsTrue) 183 c.Assert(id, check.Equals, int32(7)) 184 } 185 186 func (s *avroBatchEncoderSuite) TestAvroEncode(c *check.C) { 187 defer testleak.AfterTest(c)() 188 testCaseUpdate := &model.RowChangedEvent{ 189 CommitTs: 417318403368288260, 190 Table: &model.TableName{ 191 Schema: "test", 192 Table: "person", 193 }, 194 Columns: []*model.Column{ 195 {Name: "id", Type: mysql.TypeLong, Flag: model.HandleKeyFlag, Value: int64(1)}, 196 {Name: "name", Type: mysql.TypeVarchar, Value: "Bob"}, 197 {Name: "tiny", Type: mysql.TypeTiny, Value: int64(255)}, 198 {Name: "utiny", Type: mysql.TypeTiny, Flag: model.UnsignedFlag, Value: uint64(100)}, 199 {Name: "comment", Type: mysql.TypeBlob, Value: []byte("测试")}, 200 }, 201 } 202 203 testCaseDdl := &model.DDLEvent{ 204 CommitTs: 417318403368288260, 205 TableInfo: &model.SimpleTableInfo{ 206 Schema: "test", Table: "person", 207 }, 208 Query: "create table person(id int, name varchar(32), tiny tinyint unsigned, comment text, primary key(id))", 209 Type: model2.ActionCreateTable, 210 } 211 212 ctx, cancel := context.WithCancel(context.Background()) 213 defer cancel() 214 215 pm := puller.NewMockPullerManager(c, true) 216 defer pm.TearDown() 217 pm.MustExec(testCaseDdl.Query) 218 ddlPlr := pm.CreatePuller(0, []regionspan.ComparableSpan{regionspan.ToComparableSpan(regionspan.GetDDLSpan())}) 219 go func() { 220 err := ddlPlr.Run(ctx) 221 if err != nil && errors.Cause(err) != context.Canceled { 222 c.Fail() 223 } 224 }() 225 226 info := pm.GetTableInfo("test", "person") 227 testCaseDdl.TableInfo = new(model.SimpleTableInfo) 228 testCaseDdl.TableInfo.Schema = "test" 229 testCaseDdl.TableInfo.Table = "person" 230 testCaseDdl.TableInfo.ColumnInfo = make([]*model.ColumnInfo, len(info.Columns)) 231 for i, v := range info.Columns { 232 testCaseDdl.TableInfo.ColumnInfo[i] = new(model.ColumnInfo) 233 testCaseDdl.TableInfo.ColumnInfo[i].FromTiColumnInfo(v) 234 } 235 236 _, err := s.encoder.EncodeDDLEvent(testCaseDdl) 237 c.Check(err, check.IsNil) 238 239 _, err = s.encoder.AppendRowChangedEvent(testCaseUpdate) 240 c.Check(err, check.IsNil) 241 }