Skip to content

Commit 496c92f

Browse files
whites11claude
andcommitted
feat(clickhouse): add unified PEERDB_CLICKHOUSE_STAGING_BUCKET_NAME env var
Add provider-agnostic PEERDB_CLICKHOUSE_STAGING_BUCKET_NAME that works with PEERDB_CLICKHOUSE_STAGING_PROVIDER to configure staging storage. GCS uses the unified var exclusively. S3 falls back to the legacy PEERDB_CLICKHOUSE_AWS_S3_BUCKET_NAME when the unified var is not set. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d888da8 commit 496c92f

4 files changed

Lines changed: 39 additions & 33 deletions

File tree

flow/connectors/clickhouse/avro_sync.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ func NewClickHouseAvroSyncMethod(
3737
}
3838
}
3939

40-
func (s *ClickHouseAvroSyncMethod) stagingTableFunctionBuilder(ctx context.Context, avroFilePath string) (string, error) {
40+
func (s *ClickHouseAvroSyncMethod) s3TableFunctionBuilder(ctx context.Context, avroFilePath string) (string, error) {
4141
return s.staging.TableFunctionExpr(ctx, avroFilePath, "Avro")
4242
}
4343

4444
func (s *ClickHouseAvroSyncMethod) CopyStageToDestination(ctx context.Context, avroFile utils.AvroFile) error {
45-
s3TableFunction, err := s.stagingTableFunctionBuilder(ctx, avroFile.FilePath)
45+
s3TableFunction, err := s.s3TableFunctionBuilder(ctx, avroFile.FilePath)
4646
if err != nil {
4747
s.logger.Error("failed to build S3 table function",
4848
slog.String("avroFilePath", avroFile.FilePath),
@@ -276,7 +276,7 @@ func (s *ClickHouseAvroSyncMethod) pushS3DataToClickHouseForSnapshot(
276276

277277
for i := range numParts {
278278
// Get fresh credentials for each part
279-
s3TableFunction, err := s.stagingTableFunctionBuilder(ctx, avroFile.FilePath)
279+
s3TableFunction, err := s.s3TableFunctionBuilder(ctx, avroFile.FilePath)
280280
if err != nil {
281281
s.logger.Error("failed to build S3 table function",
282282
slog.String("avroFilePath", avroFile.FilePath),
@@ -365,24 +365,24 @@ func (s *ClickHouseAvroSyncMethod) writeToAvroFile(
365365
ocfWriter := utils.NewPeerDBOCFWriter(stream, avroSchema, ocf.ZStandard, protos.DBType_CLICKHOUSE, sizeTracker)
366366
prefix := s.staging.KeyPrefix()
367367

368-
uuidPrefix, err := internal.PeerDBS3UuidPrefix(ctx, s.config.Env)
368+
s3UuidPrefix, err := internal.PeerDBS3UuidPrefix(ctx, s.config.Env)
369369
if err != nil {
370370
return utils.AvroFile{}, err
371371
}
372372

373-
var avroFileKey string
374-
if uuidPrefix {
375-
avroFileKey = fmt.Sprintf("%s/%s/%s/%s.avro", prefix, uuid.NewString(), flowJobName, identifierForFile)
373+
var s3AvroFileKey string
374+
if s3UuidPrefix {
375+
s3AvroFileKey = fmt.Sprintf("%s/%s/%s/%s.avro", prefix, uuid.NewString(), flowJobName, identifierForFile)
376376
} else {
377-
avroFileKey = fmt.Sprintf("%s/%s/%s.avro", prefix, flowJobName, identifierForFile)
377+
s3AvroFileKey = fmt.Sprintf("%s/%s/%s.avro", prefix, flowJobName, identifierForFile)
378378
}
379-
avroFileKey = strings.TrimLeft(avroFileKey, "/")
379+
s3AvroFileKey = strings.TrimLeft(s3AvroFileKey, "/")
380380

381-
avroFile, err := ocfWriter.WriteRecordsToStaging(
382-
ctx, env, s.staging, avroFileKey, typeConversions, numericTruncator,
381+
avroFile, err := ocfWriter.WriteRecordsToS3(
382+
ctx, env, s.staging, s3AvroFileKey, typeConversions, numericTruncator,
383383
)
384384
if err != nil {
385-
return utils.AvroFile{}, fmt.Errorf("failed to write records to staging: %w", err)
385+
return utils.AvroFile{}, fmt.Errorf("failed to write records to S3: %w", err)
386386
}
387387

388388
return avroFile, nil

flow/connectors/clickhouse/clickhouse.go

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func createStagingStore(
108108
) (utils.StagingStore, *utils.ClickHouseS3Credentials, error) {
109109
// If user provided explicit S3 config, always use S3 staging.
110110
if config.S3 != nil || config.S3Path != "" || config.AccessKeyId != "" {
111-
return createS3StagingStore(ctx, env, config)
111+
return createS3StagingStore(ctx, env, config, "")
112112
}
113113

114114
// Check environment-based staging provider config.
@@ -117,19 +117,26 @@ func createStagingStore(
117117
return nil, nil, fmt.Errorf("failed to get staging provider: %w", err)
118118
}
119119

120+
// Prefer unified PEERDB_CLICKHOUSE_STAGING_BUCKET_NAME, fall back to provider-specific env vars.
121+
bucketName, err := internal.PeerDBClickHouseStagingBucketName(ctx, env)
122+
if err != nil {
123+
return nil, nil, fmt.Errorf("failed to get staging bucket name: %w", err)
124+
}
125+
120126
switch strings.ToLower(provider) {
121127
case "gcs":
122-
store, err := createGCSStagingStore(ctx, env)
128+
store, err := createGCSStagingStore(ctx, bucketName)
123129
return store, nil, err
124130
default:
125-
return createS3StagingStore(ctx, env, config)
131+
return createS3StagingStore(ctx, env, config, bucketName)
126132
}
127133
}
128134

129135
func createS3StagingStore(
130136
ctx context.Context,
131137
env map[string]string,
132138
config *protos.ClickhouseConfig,
139+
unifiedBucketName string,
133140
) (utils.StagingStore, *utils.ClickHouseS3Credentials, error) {
134141
var awsConfig utils.PeerAWSCredentials
135142
var awsBucketPath string
@@ -157,9 +164,13 @@ func createS3StagingStore(
157164
deploymentUID := internal.PeerDBDeploymentUID()
158165
flowName, _ := ctx.Value(shared.FlowNameKey).(string)
159166
bucketPathSuffix := fmt.Sprintf("%s/%s", url.PathEscape(deploymentUID), url.PathEscape(flowName))
160-
awsBucketName, err := internal.PeerDBClickHouseAWSS3BucketName(ctx, env)
161-
if err != nil {
162-
return nil, nil, fmt.Errorf("failed to get PeerDB ClickHouse Bucket Name: %w", err)
167+
// Prefer unified bucket name, fall back to legacy S3-specific env var.
168+
awsBucketName := unifiedBucketName
169+
if awsBucketName == "" {
170+
awsBucketName, err = internal.PeerDBClickHouseAWSS3BucketName(ctx, env)
171+
if err != nil {
172+
return nil, nil, fmt.Errorf("failed to get PeerDB ClickHouse Bucket Name: %w", err)
173+
}
163174
}
164175
if awsBucketName == "" {
165176
return nil, nil, errors.New("PeerDB ClickHouse Bucket Name not set")
@@ -182,20 +193,16 @@ func createS3StagingStore(
182193

183194
func createGCSStagingStore(
184195
ctx context.Context,
185-
env map[string]string,
196+
bucketName string,
186197
) (utils.StagingStore, error) {
187-
gcsBucketName, err := internal.PeerDBClickHouseGCSBucketName(ctx, env)
188-
if err != nil {
189-
return nil, fmt.Errorf("failed to get GCS bucket name: %w", err)
190-
}
191-
if gcsBucketName == "" {
192-
return nil, errors.New("PEERDB_CLICKHOUSE_GCS_BUCKET_NAME not set but staging provider is gcs")
198+
if bucketName == "" {
199+
return nil, errors.New("PEERDB_CLICKHOUSE_STAGING_BUCKET_NAME must be set when staging provider is gcs")
193200
}
194201

195202
deploymentUID := internal.PeerDBDeploymentUID()
196203
flowName, _ := ctx.Value(shared.FlowNameKey).(string)
197204
bucketPath := fmt.Sprintf("gs://%s/%s/%s",
198-
gcsBucketName, url.PathEscape(deploymentUID), url.PathEscape(flowName))
205+
bucketName, url.PathEscape(deploymentUID), url.PathEscape(flowName))
199206

200207
return utils.NewGCSStagingStore(ctx, bucketPath)
201208
}

flow/connectors/utils/avro_writer.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,8 @@ func (p *peerDBOCFWriter) WriteRecordsToS3(
166166
}, nil
167167
}
168168

169-
// WriteRecordsToStaging serializes records as Avro OCF and uploads via the given StagingStore.
170-
// This is the cloud-agnostic equivalent of WriteRecordsToS3.
171-
func (p *peerDBOCFWriter) WriteRecordsToStaging(
169+
// WriteRecordsToS3 serializes records as Avro OCF and uploads via the given StagingStore.
170+
func (p *peerDBOCFWriter) WriteRecordsToS3(
172171
ctx context.Context,
173172
env map[string]string,
174173
store StagingStore,

flow/internal/dynamicconf.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ var DynamicSettings = [...]*protos.DynamicSetting{
183183
TargetForSetting: protos.DynconfTarget_CLICKHOUSE,
184184
},
185185
{
186-
Name: "PEERDB_CLICKHOUSE_GCS_BUCKET_NAME",
187-
Description: "GCS bucket to store Avro files for mirrors with ClickHouse target",
186+
Name: "PEERDB_CLICKHOUSE_STAGING_BUCKET_NAME",
187+
Description: "Staging bucket name for ClickHouse mirrors (provider-agnostic, preferred over legacy env vars)",
188188
DefaultValue: "",
189189
ValueType: protos.DynconfValueType_STRING,
190190
ApplyMode: protos.DynconfApplyMode_APPLY_MODE_IMMEDIATE,
@@ -730,8 +730,8 @@ func PeerDBClickHouseStagingProvider(ctx context.Context, env map[string]string)
730730
return dynLookup(ctx, env, "PEERDB_CLICKHOUSE_STAGING_PROVIDER")
731731
}
732732

733-
func PeerDBClickHouseGCSBucketName(ctx context.Context, env map[string]string) (string, error) {
734-
return dynLookup(ctx, env, "PEERDB_CLICKHOUSE_GCS_BUCKET_NAME")
733+
func PeerDBClickHouseStagingBucketName(ctx context.Context, env map[string]string) (string, error) {
734+
return dynLookup(ctx, env, "PEERDB_CLICKHOUSE_STAGING_BUCKET_NAME")
735735
}
736736

737737
func PeerDBS3UuidPrefix(ctx context.Context, env map[string]string) (bool, error) {

0 commit comments

Comments
 (0)