@@ -343,6 +343,8 @@ func (c *MySqlConnector) PullRecords(
343343 var skewLossReported bool
344344 var inTx bool
345345 var recordCount uint32
346+ // set when a tx is preventing us from respecting the timeout, immediately exit after we see inTx false
347+ var overtime bool
346348 defer func () {
347349 if recordCount == 0 {
348350 req .RecordStream .SignalAsEmpty ()
@@ -373,22 +375,48 @@ func (c *MySqlConnector) PullRecords(
373375 var mysqlParser * parser.Parser
374376 for inTx || recordCount < req .MaxBatchSize {
375377 getCtx := ctx
376- if ! inTx {
377- // don't gamble on closed timeoutCtx.Done() being prioritized over event backlog channel
378- if err := timeoutCtx .Err (); err != nil {
379- if errors .Is (err , context .DeadlineExceeded ) {
378+ if overtime && ! inTx {
379+ return nil
380+ }
381+
382+ // don't gamble on closed timeoutCtx.Done() being prioritized over event backlog channel
383+ if err := timeoutCtx .Err (); err != nil {
384+ if errors .Is (err , context .DeadlineExceeded ) {
385+ if inTx {
386+ c .logger .Info ("[mysql] timeout reached, but still in transaction, waiting for inTx false" ,
387+ slog .Int ("recordCount" , int (recordCount )))
388+ // reset timeoutCtx to a low value and wait for inTx to become false
389+ // cancelTimeout should be called by defer, spurious lint
390+ //nolint:lostcancel
391+ timeoutCtx , cancelTimeout = context .WithTimeout (ctx , 10 * time .Second )
392+ overtime = true
393+ } else {
380394 return nil
381395 }
396+ } else {
382397 return err
383398 }
399+ }
400+ if recordCount > 0 && ! inTx {
401+ // if we have records and are safe, start respecting the timeout
384402 getCtx = timeoutCtx
385403 }
404+
386405 event , err := mystream .GetEvent (getCtx )
387406 if err != nil {
388- if ! inTx && errors .Is (err , context .DeadlineExceeded ) {
389- return nil
407+ if errors .Is (err , context .DeadlineExceeded ) {
408+ if ! inTx {
409+ return nil
410+ }
411+ // if in tx, don't let syncer exit due to deadline exceeded
412+ } else {
413+ if errors .Is (err , context .Canceled ) {
414+ c .logger .Info ("[mysql] PullRecords context canceled, stopping streaming" , slog .Any ("error" , err ))
415+ } else {
416+ c .logger .Error ("[mysql] PullRecords failed to get event" , slog .Any ("error" , err ))
417+ }
418+ return err
390419 }
391- return err
392420 }
393421
394422 otelManager .Metrics .FetchedBytesCounter .Add (ctx , int64 (len (event .RawData )))
@@ -456,16 +484,18 @@ func (c *MySqlConnector) PullRecords(
456484 if ev .Table .ColumnName != nil {
457485 unsafeName := shared .UnsafeFastReadOnlyBytesToString (ev .Table .ColumnName [idx ])
458486 if _ , excluded := exclusion [unsafeName ]; ! excluded {
459- for _ , col := range schema .Columns {
460- if col .Name == unsafeName {
461- return col
487+ idx2 := slices .IndexFunc (schema .Columns , func (col * protos.FieldDescription ) bool {
488+ return col .Name == unsafeName
489+ })
490+ if idx2 == - 1 {
491+ if ! skewLossReported {
492+ skewLossReported = true
493+ c .logger .Warn ("Unknown column name received, ignoring" , slog .String ("name" , string (ev .Table .ColumnName [idx ])))
462494 }
495+ } else {
496+ return schema .Columns [idx2 ]
463497 }
464498 }
465- if ! skewLossReported {
466- skewLossReported = true
467- c .logger .Warn ("Unknown column name received, ignoring" , slog .String ("name" , string (ev .Table .ColumnName [idx ])))
468- }
469499 return nil
470500 }
471501 if idx < len (schema .Columns ) {
0 commit comments