@@ -12,7 +12,9 @@ import (
1212 "github.com/jackc/pgerrcode"
1313 "github.com/jackc/pgx/v5"
1414 "go.opentelemetry.io/otel/attribute"
15+ "go.opentelemetry.io/otel/codes"
1516 "go.opentelemetry.io/otel/metric"
17+ "go.opentelemetry.io/otel/trace"
1618 "go.temporal.io/sdk/activity"
1719 "go.temporal.io/sdk/log"
1820 "golang.org/x/sync/errgroup"
@@ -136,6 +138,11 @@ func syncCore[TPull connectors.CDCPullConnectorCore, TSync connectors.CDCSyncCon
136138 ctx = context .WithValue (ctx , shared .FlowNameKey , flowName )
137139 logger := internal .LoggerFromCtx (ctx )
138140
141+ ctx , batchSpan := a .OtelManager .Tracer .Start (ctx , "cdc.batch" , trace .WithAttributes (
142+ attribute .String (otel_metrics .FlowNameKey , flowName ),
143+ ))
144+ defer batchSpan .End ()
145+
139146 tblNameMapping := make (map [string ]model.NameAndExclude , len (options .TableMappings ))
140147 for _ , v := range options .TableMappings {
141148 tblNameMapping [v .SourceTableIdentifier ] = model .NewNameAndExclude (v .DestinationTableIdentifier , v .Exclude )
@@ -193,11 +200,42 @@ func syncCore[TPull connectors.CDCPullConnectorCore, TSync connectors.CDCSyncCon
193200 return nil , err
194201 }
195202
203+ syncBatchID , err := func () (int64 , error ) {
204+ // special case pg-pg replication, where batch ID is stored on destination instead of catalog
205+ if _ , isSourcePg := any (srcConn ).(* connpostgres.PostgresConnector ); isSourcePg {
206+ dstPgConn , dstPgClose , err := connectors .GetPostgresConnectorByName (ctx , config .Env , a .CatalogPool , config .DestinationName )
207+ if err != nil {
208+ if ! errors .Is (err , errors .ErrUnsupported ) {
209+ return 0 , fmt .Errorf ("failed to get destination connector to get last sync batch ID: %w" , err )
210+ }
211+ // else fallthrough to loading from catalog
212+ } else {
213+ defer dstPgClose (ctx )
214+ return dstPgConn .GetLastSyncBatchID (ctx , flowName )
215+ }
216+ }
217+ pgMetadata := connmetadata .NewPostgresMetadataFromCatalog (logger , a .CatalogPool )
218+ return pgMetadata .GetLastSyncBatchID (ctx , flowName )
219+ }()
220+ if err != nil {
221+ batchSpan .RecordError (err )
222+ batchSpan .SetStatus (codes .Error , err .Error ())
223+ return nil , a .Alerter .LogFlowError (ctx , flowName , err )
224+ }
225+ syncBatchID += 1
226+ batchSpan .SetAttributes (attribute .Int64 (otel_metrics .BatchIdKey , syncBatchID ))
227+
196228 startTime := time .Now ()
197229 syncState .Store (new ("syncing "))
198230 errGroup , errCtx := errgroup .WithContext (ctx )
199231 errGroup .Go (func () error {
200- return pull (srcConn , errCtx , a .CatalogPool , a .OtelManager , & model.PullRecordsRequest [Items ]{
232+ pullCtx , pullSpan := a .OtelManager .Tracer .Start (errCtx , "cdc.pull" , trace .WithAttributes (
233+ attribute .String (otel_metrics .FlowNameKey , flowName ),
234+ attribute .Int (otel_metrics .TableCountKey , len (options .TableMappings )),
235+ attribute .Int64 (otel_metrics .BatchIdKey , syncBatchID ),
236+ ))
237+ defer pullSpan .End ()
238+ err := pull (srcConn , pullCtx , a .CatalogPool , a .OtelManager , & model.PullRecordsRequest [Items ]{
201239 FlowJobName : flowName ,
202240 SrcTableIDNameMapping : options .SrcTableIdNameMapping ,
203241 TableNameMapping : tblNameMapping ,
@@ -212,6 +250,11 @@ func syncCore[TPull connectors.CDCPullConnectorCore, TSync connectors.CDCSyncCon
212250 Env : config .Env ,
213251 InternalVersion : config .Version ,
214252 })
253+ if err != nil {
254+ pullSpan .RecordError (err )
255+ pullSpan .SetStatus (codes .Error , err .Error ())
256+ }
257+ return err
215258 })
216259
217260 hasRecords := ! recordBatchSync .WaitAndCheckEmpty ()
@@ -247,46 +290,35 @@ func syncCore[TPull connectors.CDCPullConnectorCore, TSync connectors.CDCSyncCon
247290
248291 var res * model.SyncResponse
249292 errGroup .Go (func () error {
250- syncBatchID , err := func () (int64 , error ) {
251- // special case pg-pg replication, where batch ID is stored on destination instead of catalog
252- if _ , isSourcePg := any (srcConn ).(* connpostgres.PostgresConnector ); isSourcePg {
253- dstPgConn , dstPgClose , err := connectors .GetPostgresConnectorByName (ctx , config .Env , a .CatalogPool , config .DestinationName )
254- if err != nil {
255- if ! errors .Is (err , errors .ErrUnsupported ) {
256- return 0 , fmt .Errorf ("failed to get destination connector to get last sync batch ID: %w" , err )
257- }
258- // else fallthrough to loading from catalog
259- } else {
260- defer dstPgClose (ctx )
261- return dstPgConn .GetLastSyncBatchID (errCtx , flowName )
262- }
263- }
264- pgMetadata := connmetadata .NewPostgresMetadataFromCatalog (logger , a .CatalogPool )
265- return pgMetadata .GetLastSyncBatchID (errCtx , flowName )
266- }()
267- if err != nil {
268- return err
269- }
270- syncBatchID += 1
293+ syncCtx , syncSpan := a .OtelManager .Tracer .Start (errCtx , "cdc.sync" , trace .WithAttributes (
294+ attribute .String (otel_metrics .FlowNameKey , flowName ),
295+ attribute .Int (otel_metrics .TableCountKey , len (options .TableMappings )),
296+ attribute .Int64 (otel_metrics .BatchIdKey , syncBatchID ),
297+ ))
298+ defer syncSpan .End ()
271299 syncingBatchID .Store (syncBatchID )
272300 logger .Info ("begin pulling records for batch" , slog .Int64 ("syncBatchID" , syncBatchID ))
273301
274- if err := monitoring .AddCDCBatchForFlow (errCtx , a .CatalogPool , flowName , monitoring.CDCBatchInfo {
302+ if err := monitoring .AddCDCBatchForFlow (syncCtx , a .CatalogPool , flowName , monitoring.CDCBatchInfo {
275303 BatchID : syncBatchID ,
276304 RowsInBatch : 0 ,
277305 BatchEndlSN : 0 ,
278306 StartTime : startTime ,
279307 }); err != nil {
308+ syncSpan .RecordError (err )
309+ syncSpan .SetStatus (codes .Error , err .Error ())
280310 return a .Alerter .LogFlowError (ctx , flowName , err )
281311 }
282312
283- dstConn , dstClose , err := connectors .GetByNameAs [TSync ](ctx , config .Env , a .CatalogPool , config .DestinationName )
313+ dstConn , dstClose , err := connectors .GetByNameAs [TSync ](syncCtx , config .Env , a .CatalogPool , config .DestinationName )
284314 if err != nil {
315+ syncSpan .RecordError (err )
316+ syncSpan .SetStatus (codes .Error , err .Error ())
285317 return fmt .Errorf ("failed to get destination connector: %w" , err )
286318 }
287319 defer dstClose (ctx )
288320
289- res , err = sync (dstConn , errCtx , & model.SyncRecordsRequest [Items ]{
321+ res , err = sync (dstConn , syncCtx , & model.SyncRecordsRequest [Items ]{
290322 SyncBatchID : syncBatchID ,
291323 Records : recordBatchSync ,
292324 ConsumedOffset : & consumedOffset ,
@@ -300,8 +332,11 @@ func syncCore[TPull connectors.CDCPullConnectorCore, TSync connectors.CDCSyncCon
300332 Flags : config .Flags ,
301333 })
302334 if err != nil {
335+ syncSpan .RecordError (err )
336+ syncSpan .SetStatus (codes .Error , err .Error ())
303337 return a .Alerter .LogFlowError (ctx , flowName , fmt .Errorf ("failed to push records: %w" , err ))
304338 }
339+ syncSpan .SetAttributes (attribute .Int64 (otel_metrics .RowsInBatchKey , res .NumRecordsSynced ))
305340 for _ , warning := range res .Warnings {
306341 a .Alerter .LogFlowWarning (ctx , flowName , warning )
307342 }
@@ -317,13 +352,21 @@ func syncCore[TPull connectors.CDCPullConnectorCore, TSync connectors.CDCSyncCon
317352 if ! (isDesync || shared .IsSQLStateError (err , pgerrcode .ObjectInUse )) {
318353 _ = a .Alerter .LogFlowError (ctx , flowName , err )
319354 }
355+ batchSpan .RecordError (err )
356+ batchSpan .SetStatus (codes .Error , err .Error ())
320357 return nil , fmt .Errorf ("[cdc] failed to pull records: %w" , err )
321358 }
322359 syncState .Store (new ("bookkeeping "))
323360
324361 syncDuration := time .Since (syncStartTime )
325362 lastCheckpoint := recordBatchSync .GetLastCheckpoint ()
326363 logger .Info ("batch synced" , slog .Any ("checkpoint" , lastCheckpoint ))
364+ batchSpan .SetAttributes (
365+ attribute .Int64 (otel_metrics .LastCheckpointIDKey , lastCheckpoint .ID ),
366+ attribute .String (otel_metrics .LastCheckpointTextKey , lastCheckpoint .Text ),
367+ attribute .Int64 (otel_metrics .RowsInBatchKey , res .NumRecordsSynced ),
368+ )
369+
327370 if err := srcConn .UpdateReplStateLastOffset (ctx , lastCheckpoint ); err != nil {
328371 return nil , a .Alerter .LogFlowError (ctx , flowName , err )
329372 }
@@ -684,17 +727,34 @@ func (a *FlowableActivity) startNormalize(
684727
685728 for {
686729 logger .Info ("normalizing batches" , slog .Int64 ("syncBatchID" , batchID ))
687- res , err := dstConn .NormalizeRecords (ctx , & model.NormalizeRecordsRequest {
688- FlowJobName : config .FlowJobName ,
689- Env : config .Env ,
690- TableNameSchemaMapping : tableNameSchemaMapping ,
691- TableMappings : config .TableMappings ,
692- SoftDeleteColName : config .SoftDeleteColName ,
693- SyncedAtColName : config .SyncedAtColName ,
694- SyncBatchID : batchID ,
695- Version : config .Version ,
696- Flags : config .Flags ,
697- })
730+ res , err := func () (model.NormalizeResponse , error ) {
731+ normCtx , normSpan := a .OtelManager .Tracer .Start (ctx , "cdc.normalize" , trace .WithAttributes (
732+ attribute .String (otel_metrics .FlowNameKey , config .FlowJobName ),
733+ attribute .Int64 (otel_metrics .BatchIdKey , batchID ),
734+ ))
735+ defer normSpan .End ()
736+ res , err := dstConn .NormalizeRecords (normCtx , & model.NormalizeRecordsRequest {
737+ FlowJobName : config .FlowJobName ,
738+ Env : config .Env ,
739+ TableNameSchemaMapping : tableNameSchemaMapping ,
740+ TableMappings : config .TableMappings ,
741+ SoftDeleteColName : config .SoftDeleteColName ,
742+ SyncedAtColName : config .SyncedAtColName ,
743+ SyncBatchID : batchID ,
744+ Version : config .Version ,
745+ Flags : config .Flags ,
746+ })
747+ if err != nil {
748+ normSpan .RecordError (err )
749+ normSpan .SetStatus (codes .Error , err .Error ())
750+ return res , err
751+ }
752+ normSpan .SetAttributes (
753+ attribute .Int64 (otel_metrics .StartBatchIDKey , res .StartBatchID ),
754+ attribute .Int64 (otel_metrics .EndBatchIDKey , res .EndBatchID ),
755+ )
756+ return res , nil
757+ }()
698758 if err != nil {
699759 return a .Alerter .LogFlowError (ctx , config .FlowJobName ,
700760 exceptions .NewNormalizationError (fmt .Errorf ("failed to normalize records: %w" , err )))
0 commit comments