Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion downstreamadapter/sink/cloudstorage/encoder_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ func newTestTxnEncoderConfig(t *testing.T) *common.Config {
config.ProtocolCsv,
replicaConfig.Sink,
config.DefaultMaxMessageBytes,
config.DefaultMaxMessageBytes,
)
require.NoError(t, err)
return encoderConfig
Expand Down
8 changes: 4 additions & 4 deletions downstreamadapter/sink/cloudstorage/sink.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func Verify(ctx context.Context, changefeedID common.ChangeFeedID, sinkURI *url.
if err != nil {
return err
}
_, err = helper.GetEncoderConfig(changefeedID, sinkURI, protocol, sinkConfig, math.MaxInt, math.MaxInt)
_, err = helper.GetEncoderConfig(changefeedID, sinkURI, protocol, sinkConfig, math.MaxInt)
if err != nil {
return err
}
Expand Down Expand Up @@ -116,9 +116,9 @@ func New(
}
// get cloud storage file extension according to the specific protocol.
ext := helper.GetFileExtension(protocol)
// Message size limits are mainly for MQ batch protocols. Cloud storage uses
// max int for both the final message limit and the batch threshold.
encoderConfig, err := helper.GetEncoderConfig(changefeedID, sinkURI, protocol, sinkConfig, math.MaxInt, math.MaxInt)
// the last param maxMsgBytes is mainly to limit the size of a single message for
// batch protocols in mq scenario. In cloud storage sink, we just set it to max int.
encoderConfig, err := helper.GetEncoderConfig(changefeedID, sinkURI, protocol, sinkConfig, math.MaxInt)
if err != nil {
return nil, err
}
Expand Down
9 changes: 5 additions & 4 deletions downstreamadapter/sink/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,17 @@ func GetEncoderConfig(
sinkURI *url.URL,
protocol config.Protocol,
sinkConfig *config.SinkConfig,
maxMessageBytes int,
maxBatchedBytes int,
maxMsgBytes int,
) (*common.Config, error) {
encoderConfig := common.NewConfig(protocol)
if err := encoderConfig.Apply(sinkURI, sinkConfig); err != nil {
return nil, errors.WrapError(errors.ErrSinkInvalidConfig, err)
}
// Always set encoder's `MaxMessageBytes` equal to producer's `MaxMessageBytes`
// to prevent that the encoder generate batched message too large
// then cause producer meet `message too large`.
encoderConfig = encoderConfig.
WithMaxMessageBytes(maxMessageBytes).
WithMaxBatchedBytes(maxBatchedBytes).
WithMaxMessageBytes(maxMsgBytes).
WithChangefeedID(changefeedID)

tz, err := util.GetTimezone(config.GetGlobalServerConfig().TZ)
Expand Down
5 changes: 1 addition & 4 deletions downstreamadapter/sink/kafka/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,7 @@ func newKafkaSinkComponent(
return comp, protocol, err
}

encoderConfig, err := helper.GetEncoderConfig(
changefeedID, sinkURI, protocol, sinkConfig,
options.MaxMessageBytes, options.MaxBatchedBytes,
)
encoderConfig, err := helper.GetEncoderConfig(changefeedID, sinkURI, protocol, sinkConfig, options.MaxMessageBytes)
if err != nil {
return comp, protocol, err
}
Expand Down
5 changes: 1 addition & 4 deletions downstreamadapter/sink/kafka/sink.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,7 @@ func Verify(ctx context.Context, changefeedID common.ChangeFeedID, uri *url.URL,
}
options.Topic = topic

encoderConfig, err := helper.GetEncoderConfig(
changefeedID, uri, protocol, sinkConfig,
options.MaxMessageBytes, options.MaxBatchedBytes,
)
encoderConfig, err := helper.GetEncoderConfig(changefeedID, uri, protocol, sinkConfig, options.MaxMessageBytes)
if err != nil {
return err
}
Expand Down
18 changes: 1 addition & 17 deletions downstreamadapter/sink/kafka/sink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,6 @@ import (

const kafkaSinkTestTopic = "mock_topic"

func TestVerifyValidatesEncoderConfigBeforeKafkaConnection(t *testing.T) {
openProtocol := config.ProtocolOpen.String()
sinkConfig := &config.SinkConfig{Protocol: &openProtocol}
sinkURI, err := url.Parse("kafka://127.0.0.1:1/" + kafkaSinkTestTopic + "?max-batch-size=0")
require.NoError(t, err)

changefeedID := common.NewChangefeedID4Test("test", "verify-existing-topic")
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
err = Verify(ctx, changefeedID, sinkURI, sinkConfig)
require.ErrorContains(t, err, "invalid max-batch-size 0")
}

func TestSinkWorkersReturnContextError(t *testing.T) {
contexts := []struct {
name string
Expand Down Expand Up @@ -197,10 +184,7 @@ func newKafkaSinkForTestWithProducers(ctx context.Context,
if err != nil {
return nil, err
}
encoderConfig, err := helper.GetEncoderConfig(
changefeedID, sinkURI, protocol, sinkConfig,
options.MaxMessageBytes, options.MaxBatchedBytes,
)
encoderConfig, err := helper.GetEncoderConfig(changefeedID, sinkURI, protocol, sinkConfig, options.MaxMessageBytes)
if err != nil {
return nil, err
}
Expand Down
5 changes: 1 addition & 4 deletions downstreamadapter/sink/pulsar/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,7 @@ func newPulsarSinkComponentWithFactory(ctx context.Context,
return pulsarComponent, protocol, errors.Trace(err)
}

encoderConfig, err := helper.GetEncoderConfig(
changefeedID, sinkURI, protocol, sinkConfig,
config.DefaultMaxMessageBytes, config.DefaultMaxMessageBytes,
)
encoderConfig, err := helper.GetEncoderConfig(changefeedID, sinkURI, protocol, sinkConfig, config.DefaultMaxMessageBytes)
if err != nil {
return pulsarComponent, protocol, errors.Trace(err)
}
Expand Down
70 changes: 39 additions & 31 deletions downstreamadapter/sink/topicmanager/kafka_topic_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,42 +212,50 @@ func TestEnsureTopicExistsWaitsUntilVisible(t *testing.T) {

ctrl := gomock.NewController(t)
adminClient := kafka.NewMockClusterAdminClient(ctrl)
cfg := &kafka.AutoCreateTopicConfig{
AutoCreate: true,
PartitionNum: 2,
ReplicationFactor: 1,
}

topic := "delayed-topic"
gomock.InOrder(
adminClient.EXPECT().GetTopicsMeta([]string{topic}, true).Return(
map[string]kafka.TopicDetail{}, nil),
adminClient.EXPECT().GetTopicsMeta([]string{topic}, false).Return(
map[string]kafka.TopicDetail{}, nil),
adminClient.EXPECT().CreateTopic(gomock.Any()).DoAndReturn(
func(detail *kafka.TopicDetail) error {
require.Equal(t, &kafka.TopicDetail{
Name: topic,
NumPartitions: 2,
ReplicationFactor: 1,
}, detail)
return nil
}),
adminClient.EXPECT().GetTopicsMeta([]string{topic}, false).Return(
map[string]kafka.TopicDetail{}, nil),
adminClient.EXPECT().GetTopicsMeta([]string{topic}, false).Return(
map[string]kafka.TopicDetail{
topic: {
Name: topic,
created := false
postCreateDescribeCount := 0
adminClient.EXPECT().GetTopicsMeta([]string{"delayed-topic"}, true).Return(map[string]kafka.TopicDetail{}, nil)
adminClient.EXPECT().GetTopicsMeta([]string{"delayed-topic"}, false).DoAndReturn(
func([]string, bool) (map[string]kafka.TopicDetail, error) {
if !created {
return map[string]kafka.TopicDetail{}, nil
}
postCreateDescribeCount++
if postCreateDescribeCount == 1 {
return map[string]kafka.TopicDetail{}, nil
}
return map[string]kafka.TopicDetail{
"delayed-topic": {
Name: "delayed-topic",
NumPartitions: 2,
},
}, nil),
}, nil
}).Times(3)
adminClient.EXPECT().CreateTopic(gomock.Any()).DoAndReturn(
func(detail *kafka.TopicDetail) error {
require.Equal(t, &kafka.TopicDetail{
Name: "delayed-topic",
NumPartitions: 2,
ReplicationFactor: 1,
}, detail)
created = true
return nil
})

err := EnsureTopic(
context.Background(),
common.NewChangefeedID4Test("test", "test"),
"delayed-topic",
&kafka.AutoCreateTopicConfig{
AutoCreate: true,
PartitionNum: 2,
ReplicationFactor: 1,
},
adminClient,
)

ctx := context.Background()
changefeedID := common.NewChangefeedID4Test("test", "test")
err := EnsureTopic(ctx, changefeedID, topic, cfg, adminClient)
require.NoError(t, err)
require.Equal(t, 2, postCreateDescribeCount)
}

func TestGetTopicManagerStartsBackgroundRefreshAfterTopicReady(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions pkg/sink/codec/canal/canal_json_txn_encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func (j *JSONTxnEventEncoder) AppendTxnEvent(event *commonEvent.DMLEvent) error
return err
}
length := len(value) + common.MaxRecordOverhead
// For single message that is longer than max-message-bytes, do not send it.
if length > j.config.MaxMessageBytes {
log.Warn("Single message is too large for canal-json",
zap.Int("maxMessageBytes", j.config.MaxMessageBytes),
Expand Down
20 changes: 2 additions & 18 deletions pkg/sink/codec/common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,9 @@ type Config struct {

Protocol config.Protocol

// control batch behavior, only for `open-protocol` and `craft` at the moment.
MaxMessageBytes int

// MaxBatchedBytes controls open-protocol encoder's maximum number of bytes for a batched message.
MaxBatchedBytes int
// MaxBatchedSize controls open-protocol encoder's maximum number of events for a batched message.
MaxBatchSize int
MaxBatchSize int

// DeleteOnlyHandleKeyColumns is true, for the delete event only output the handle key columns.
DeleteOnlyHandleKeyColumns bool
Expand Down Expand Up @@ -117,7 +114,6 @@ func NewConfig(protocol config.Protocol) *Config {
Protocol: protocol,

MaxMessageBytes: config.DefaultMaxMessageBytes,
MaxBatchedBytes: config.DefaultMaxMessageBytes,
MaxBatchSize: defaultMaxBatchSize,

EnableTiDBExtension: false,
Expand Down Expand Up @@ -348,12 +344,6 @@ func (c *Config) WithMaxMessageBytes(bytes int) *Config {
return c
}

// WithMaxBatchedBytes sets the maximum batched message bytes.
func (c *Config) WithMaxBatchedBytes(bytes int) *Config {
c.MaxBatchedBytes = bytes
return c
}

// WithChangefeedID set the `changefeedID`
func (c *Config) WithChangefeedID(id common.ChangeFeedID) *Config {
c.ChangefeedID = id
Expand Down Expand Up @@ -423,12 +413,6 @@ func (c *Config) Validate() error {
if c.MaxMessageBytes <= 0 {
return errors.ErrCodecInvalidConfig.GenWithStack("invalid max-message-bytes %d", c.MaxMessageBytes)
}
if c.MaxBatchedBytes < 0 {
return errors.ErrCodecInvalidConfig.GenWithStack("invalid max-batch-message-bytes %d", c.MaxBatchedBytes)
}
if c.MaxBatchedBytes > c.MaxMessageBytes {
return errors.ErrCodecInvalidConfig.GenWithStack("max-batch-message-bytes %d cannot be greater than max-message-bytes %d", c.MaxBatchedBytes, c.MaxMessageBytes)
}

if c.MaxBatchSize <= 0 {
return errors.ErrCodecInvalidConfig.GenWithStack("invalid max-batch-size %d", c.MaxBatchSize)
Expand Down
17 changes: 1 addition & 16 deletions pkg/sink/codec/common/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestApplyReturnsSinkInvalidConfigForQueryBindingError(t *testing.T) {
require.Equal(t, errors.ErrSinkInvalidConfig.RFCCode(), errCode)
}

func TestValidateMaxBatchMessageBytes(t *testing.T) {
func TestValidateMessageLimits(t *testing.T) {
tests := []struct {
name string
adjust func(*Config)
Expand All @@ -46,21 +46,6 @@ func TestValidateMaxBatchMessageBytes(t *testing.T) {
},
expected: "invalid max-message-bytes 0",
},
{
name: "negative max batched bytes",
adjust: func(cfg *Config) {
cfg.MaxBatchedBytes = -1
},
expected: "invalid max-batch-message-bytes -1",
},
{
name: "max batched bytes exceeds max message bytes",
adjust: func(cfg *Config) {
cfg.MaxMessageBytes = 100
cfg.MaxBatchedBytes = 101
},
expected: "max-batch-message-bytes 101 cannot be greater than max-message-bytes 100",
},
{
name: "non-positive max batch size",
adjust: func(cfg *Config) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/sink/codec/open/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var (
)

// batchEncoder for open protocol will batch multiple row changed events into a single message.
// One message can contain at most MaxBatchSize events, and the total size of the message cannot exceed MaxBatchedBytes.
// One message can contain at most MaxBatchSize events, and the total size of the message cannot exceed MaxMessageBytes.
type batchEncoder struct {
messages []*common.Message
// buff the callback of the latest message
Expand Down Expand Up @@ -164,7 +164,7 @@ func (d *batchEncoder) pushMessage(key, value []byte, callback func()) {
binary.BigEndian.PutUint64(keyLenByte[:], uint64(len(key)))
binary.BigEndian.PutUint64(valueLenByte[:], uint64(len(value)))

if len(d.messages) == 0 || d.messages[len(d.messages)-1].Length()+length > d.config.MaxBatchedBytes || d.messages[len(d.messages)-1].GetRowsCount() >= d.config.MaxBatchSize {
if len(d.messages) == 0 || d.messages[len(d.messages)-1].Length()+length > d.config.MaxMessageBytes || d.messages[len(d.messages)-1].GetRowsCount() >= d.config.MaxBatchSize {
d.finalizeCallback()
// create a new message
versionHead := make([]byte, 8)
Expand Down
56 changes: 5 additions & 51 deletions pkg/sink/codec/open/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,9 +778,7 @@ func TestEncoderMultipleMessage(t *testing.T) {
`insert into test.t values (3, 333)`)

ctx := context.Background()
codecConfig := common.NewConfig(config.ProtocolOpen).
WithMaxMessageBytes(1000).
WithMaxBatchedBytes(400)
codecConfig := common.NewConfig(config.ProtocolOpen).WithMaxMessageBytes(400)
encoder, err := NewBatchEncoder(codecConfig, nil)
require.NoError(t, err)

Expand Down Expand Up @@ -810,13 +808,11 @@ func TestEncoderMultipleMessage(t *testing.T) {
require.Equal(t, 2, len(messages))
require.Equal(t, 2, messages[0].GetRowsCount())
require.Equal(t, 1, messages[1].GetRowsCount())
require.LessOrEqual(t, messages[0].Length(), codecConfig.MaxBatchedBytes)
require.LessOrEqual(t, messages[1].Length(), codecConfig.MaxBatchedBytes)
require.Equal(t, 0, count)

messages[0].Callback()
require.Equal(t, 2, count)
messages[1].Callback()
for _, message := range messages {
message.Callback()
}

require.Equal(t, 3, count)

decoder, err := NewDecoder(ctx, 0, codecConfig, nil)
Expand Down Expand Up @@ -889,48 +885,6 @@ func TestMessageTooLarge(t *testing.T) {
require.Equal(t, count, 0)
}

func TestMessageLargerThanBatchLimit(t *testing.T) {
ctx := context.Background()
codecConfig := common.NewConfig(config.ProtocolOpen).
WithMaxMessageBytes(400).
WithMaxBatchedBytes(100)
encoder, err := NewBatchEncoder(codecConfig, nil)
require.NoError(t, err)

helper := commonEvent.NewEventTestHelper(t)
defer helper.Close()
helper.Tk().MustExec("use test")

job := helper.DDL2Job(`create table test.t(a tinyint primary key, b int)`)
tableInfo := helper.GetTableInfo(job)
dmlEvent := helper.DML2Event("test", "t", `insert into test.t values (1, 123)`)
require.NotNil(t, dmlEvent)
insertRow, ok := dmlEvent.GetNextRow()
require.True(t, ok)

count := 0
insertRowEvent := &commonEvent.RowEvent{
TableInfo: tableInfo,
CommitTs: dmlEvent.GetCommitTs(),
Event: insertRow,
ColumnSelector: columnselector.NewDefaultColumnSelector(),
Callback: func() { count++ },
}

err = encoder.AppendRowChangedEvent(ctx, "", insertRowEvent)
require.NoError(t, err)

messages := encoder.Build()
require.Len(t, messages, 1)
require.Equal(t, 1, messages[0].GetRowsCount())
require.Greater(t, messages[0].Length(), codecConfig.MaxBatchedBytes)
require.LessOrEqual(t, messages[0].Length(), codecConfig.MaxMessageBytes)
require.Equal(t, 0, count)

messages[0].Callback()
require.Equal(t, 1, count)
}

func TestLargeMessageWithHandleEnableHandleKeyOnly(t *testing.T) {
helper := commonEvent.NewEventTestHelper(t)
defer helper.Close()
Expand Down
Loading
Loading