Skip to content

Commit 90bf412

Browse files
authored
Use unique staging paths for BigQuery source (#4411)
A very good write-up of the situation is in [DBI-790](https://linear.app/clickhouse/issue/DBI-790/). To resolve the conflicts between the previous snapshot cleanup and the new snapshot sync, add a subfolder with the workflow's run id. Use the same temporal session mechanism that's used to pass PG snapshot name to the QRep flows, it's great that we have it. It won't help us on the future snapshot+CDC path but we'll get there when we get there, the whole "return string as an `any` transaction" situation is already a mess. Validated locally and as a hotfix on the customer service that hit this. Closes DBI-790
1 parent 234a1ea commit 90bf412

4 files changed

Lines changed: 24 additions & 6 deletions

File tree

flow/activities/snapshot_activity.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ type SlotSnapshotState struct {
2525
}
2626

2727
type TxSnapshotState struct {
28-
SnapshotName string
28+
SnapshotName string
29+
SnapshotStagingPath string
2930
}
3031

3132
type SnapshotActivity struct {
@@ -123,7 +124,8 @@ func (a *SnapshotActivity) MaintainTx(ctx context.Context, sessionID string, flo
123124
a.SnapshotStatesMutex.Lock()
124125
if exportSnapshotOutput != nil {
125126
a.TxSnapshotStates[sessionID] = TxSnapshotState{
126-
SnapshotName: exportSnapshotOutput.SnapshotName,
127+
SnapshotName: exportSnapshotOutput.SnapshotName,
128+
SnapshotStagingPath: exportSnapshotOutput.SnapshotStagingPath,
127129
}
128130
} else {
129131
a.TxSnapshotStates[sessionID] = TxSnapshotState{}

flow/connectors/bigquery/qrep_object_pull.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"cloud.google.com/go/bigquery"
1212
"cloud.google.com/go/storage"
1313
"github.com/google/uuid"
14+
"go.temporal.io/sdk/activity"
1415
"google.golang.org/api/googleapi"
1516
"google.golang.org/api/iterator"
1617

@@ -331,9 +332,18 @@ func (c *BigQueryConnector) ExportTxSnapshot(
331332

332333
_ = c.LogFlowInfo(ctx, flowName, "Starting snapshot BigQuery export to GCS staging bucket")
333334

335+
// Make the staging path unique per-run so it can be cleaned up asynchronously on resync.
336+
activityInfo := activity.GetInfo(ctx)
337+
basePath, err := parseGCSPath(cfg.SnapshotStagingPath)
338+
if err != nil {
339+
return nil, nil, fmt.Errorf("failed to parse snapshot staging path %q: %w", cfg.SnapshotStagingPath, err)
340+
}
341+
runSnapshotStagingPath := basePath.JoinPath(activityInfo.WorkflowExecution.RunID).String()
342+
c.logger.Info("Run snapshot staging path", slog.String("path", runSnapshotStagingPath))
343+
334344
jobs := make(map[string]*bigquery.Job)
335345
for _, tm := range cfg.TableMappings {
336-
exportSQL, err := c.bigQueryExportQueryStatement(ctx, tm.SourceTableIdentifier, cfg.SnapshotStagingPath)
346+
exportSQL, err := c.bigQueryExportQueryStatement(ctx, tm.SourceTableIdentifier, runSnapshotStagingPath)
337347
if err != nil {
338348
return nil, nil, fmt.Errorf("failed to build export SQL for table %s: %w", tm.SourceTableIdentifier, err)
339349
}
@@ -365,7 +375,7 @@ func (c *BigQueryConnector) ExportTxSnapshot(
365375
_ = c.LogFlowInfo(ctx, flowName, "Exported snapshot data to GCS for table "+sourceTableIdentifier)
366376
}
367377

368-
return nil, cfg.SnapshotStagingPath, nil
378+
return &protos.ExportTxSnapshotOutput{SnapshotStagingPath: runSnapshotStagingPath}, runSnapshotStagingPath, nil
369379
}
370380

371381
// bigQueryExportQueryStatement builds the EXPORT DATA SQL statement for exporting data from BigQuery to GCS in Parquet format.

flow/workflows/snapshot_flow.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package peerflow
22

33
import (
4+
"cmp"
45
"fmt"
56
"log/slog"
67
"time"
@@ -102,6 +103,7 @@ func (s *SnapshotFlowExecution) cloneTable(
102103
ctx workflow.Context,
103104
boundSelector *shared.BoundSelector,
104105
snapshotName string,
106+
stagingPathOverride string,
105107
mapping *protos.TableMapping,
106108
sourcePeerType protos.DBType,
107109
destinationPeerType protos.DBType,
@@ -209,7 +211,7 @@ func (s *SnapshotFlowExecution) cloneTable(
209211
NumRowsPerPartition: numRowsPerPartition,
210212
NumPartitionsOverride: numPartitionsOverride,
211213
MaxParallelWorkers: numWorkers,
212-
StagingPath: s.config.SnapshotStagingPath,
214+
StagingPath: cmp.Or(stagingPathOverride, s.config.SnapshotStagingPath),
213215
SyncedAtColName: s.config.SyncedAtColName,
214216
SoftDeleteColName: s.config.SoftDeleteColName,
215217
WriteMode: snapshotWriteMode,
@@ -231,6 +233,7 @@ func (s *SnapshotFlowExecution) cloneTables(
231233
snapshotType snapshotType,
232234
slotName string,
233235
snapshotName string,
236+
stagingPathOverride string,
234237
maxParallelClones int,
235238
) error {
236239
if snapshotType == SNAPSHOT_TYPE_SLOT {
@@ -273,7 +276,7 @@ func (s *SnapshotFlowExecution) cloneTables(
273276
if v.PartitionKey == "" {
274277
v.PartitionKey = res.TableDefaultPartitionKeyMapping[source]
275278
}
276-
if err := s.cloneTable(ctx, boundSelector, snapshotName, v, sourcePeerType, destinationPeerType); err != nil {
279+
if err := s.cloneTable(ctx, boundSelector, snapshotName, stagingPathOverride, v, sourcePeerType, destinationPeerType); err != nil {
277280
s.logger.Error("failed to start clone child workflow", slog.Any("error", err))
278281
return err
279282
}
@@ -316,6 +319,7 @@ func (s *SnapshotFlowExecution) cloneTablesWithSlot(
316319
SNAPSHOT_TYPE_SLOT,
317320
slotName,
318321
snapshotName,
322+
"",
319323
numTablesInParallel,
320324
); err != nil {
321325
s.logger.Error("failed to clone tables", slog.Any("error", err))
@@ -410,6 +414,7 @@ func SnapshotFlowWorkflow(
410414
SNAPSHOT_TYPE_TX,
411415
"",
412416
txnSnapshotState.SnapshotName,
417+
txnSnapshotState.SnapshotStagingPath,
413418
numTablesInParallel,
414419
); err != nil {
415420
return fmt.Errorf("failed to clone tables: %w", err)

protos/flow.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ message IsQRepPartitionSyncedInput {
559559
message ExportTxSnapshotOutput {
560560
reserved 2;
561561
string snapshot_name = 1;
562+
string snapshot_staging_path = 3;
562563
}
563564

564565
enum DynconfValueType {

0 commit comments

Comments
 (0)