Skip to content

Commit 35f565f

Browse files
committed
API e2e test for peer re-utilizaton validation
1 parent 0950728 commit 35f565f

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

flow/e2e/api_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,81 @@ func (s APITestSuite) TestClickHouseMirrorValidation_Pass() {
325325
require.NotNil(s.t, response)
326326
}
327327

328+
// TestValidateCDCMirror_ServerIDPeerReuse verifies that validating a CDC mirror whose MySQL source
329+
// peer pins a fixed server_id is rejected when that peer already backs another streaming CDC mirror.
330+
func (s APITestSuite) TestValidateCDCMirror_ServerIDPeerReuse() {
331+
mySource, ok := s.source.(*MySqlSource)
332+
if !ok {
333+
s.t.Skip("server_id peer-reuse validation is MySQL-specific")
334+
}
335+
ctx := s.t.Context()
336+
337+
// Source table backing the existing mirror.
338+
require.NoError(s.t, s.source.Exec(ctx,
339+
fmt.Sprintf("CREATE TABLE %s(id int primary key, val text)", AttachSchema(s, "serverid_reuse"))))
340+
341+
// A MySQL source peer that pins a fixed server_id. It clones the suite's MySQL config, so it
342+
// points at the same (reachable) instance.
343+
serverID := uint32(4224)
344+
pinnedConfig := proto.CloneOf(mySource.Config)
345+
pinnedConfig.ServerId = &serverID
346+
peerName := "serverid_reuse_" + s.suffix
347+
_, err := s.CreatePeer(ctx, &protos.CreatePeerRequest{
348+
Peer: &protos.Peer{
349+
Name: peerName,
350+
Type: protos.DBType_MYSQL,
351+
Config: &protos.Peer_MysqlConfig{MysqlConfig: pinnedConfig},
352+
},
353+
})
354+
require.NoError(s.t, err)
355+
356+
// An existing streaming CDC mirror that already uses the pinned-server_id peer as its source.
357+
existingGen := FlowConnectionGenerationConfig{
358+
FlowJobName: "serverid_reuse_existing_" + s.suffix,
359+
TableNameMapping: map[string]string{AttachSchema(s, "serverid_reuse"): "serverid_reuse"},
360+
Destination: s.ch.Peer().Name,
361+
}
362+
existingCfg := existingGen.GenerateFlowConnectionConfigs(s)
363+
existingCfg.SourceName = peerName
364+
existingCfg.DoInitialSnapshot = true
365+
createResp, err := s.CreateCDCFlow(ctx, &protos.CreateCDCFlowRequest{ConnectionConfigs: existingCfg})
366+
require.NoError(s.t, err)
367+
require.NotNil(s.t, createResp)
368+
369+
tc := NewTemporalClient(s.t)
370+
env, err := GetPeerflow(ctx, s.catalog, tc, existingCfg.FlowJobName)
371+
require.NoError(s.t, err)
372+
373+
// Validating a second CDC mirror on the same pinned-server_id peer must be rejected.
374+
newGen := FlowConnectionGenerationConfig{
375+
FlowJobName: "serverid_reuse_new_" + s.suffix,
376+
TableNameMapping: map[string]string{AttachSchema(s, "serverid_reuse"): "serverid_reuse"},
377+
Destination: s.ch.Peer().Name,
378+
}
379+
newCfg := newGen.GenerateFlowConnectionConfigs(s)
380+
newCfg.SourceName = peerName
381+
newCfg.DoInitialSnapshot = true
382+
383+
res, err := s.ValidateCDCMirror(ctx, &protos.CreateCDCFlowRequest{ConnectionConfigs: newCfg})
384+
require.Nil(s.t, res)
385+
require.Error(s.t, err)
386+
st, ok := status.FromError(err)
387+
require.True(s.t, ok)
388+
require.Equal(s.t, codes.FailedPrecondition, st.Code())
389+
require.Contains(s.t, st.Message(), "cannot be shared across mirrors")
390+
391+
// Tear down via gRPC: drop the mirror, then the now-unreferenced peer.
392+
_, err = s.FlowStateChange(ctx, &protos.FlowStateChangeRequest{
393+
FlowJobName: existingCfg.FlowJobName,
394+
RequestedFlowState: protos.FlowStatus_STATUS_TERMINATING,
395+
})
396+
require.NoError(s.t, err)
397+
s.waitForFlowDropped(env, existingCfg.FlowJobName)
398+
399+
_, err = s.DropPeer(ctx, &protos.DropPeerRequest{PeerName: peerName})
400+
require.NoError(s.t, err)
401+
}
402+
328403
func (s APITestSuite) TestClickHouseMirrorValidation_NoPrimaryKey() {
329404
switch s.source.(type) {
330405
case *PostgresSource, *MySqlSource:

0 commit comments

Comments
 (0)