-
Notifications
You must be signed in to change notification settings - Fork 103
[SNOW-851840] Enable tombstone record ingestion in Snowpipe Streaming #688
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 37 commits
f3916c5
eec908e
62c1f24
b39a691
290aad2
240d968
a2c53e6
cd09713
928b943
00bee04
1dde035
62e4757
94ac2ca
e73a063
553999b
754824e
6c20656
b85f733
4ab7ffa
cafdd42
37dd5a7
65aab68
cfd6f54
32d7a60
014b8eb
434ebc2
6583657
abc32d3
4ba153e
8673464
ffa955b
a6f76d7
07e0db4
e644e90
27b6641
631f75a
c815b48
eef2642
06c69ba
abeaf39
3fc817f
3fd589b
21cc65a
f516110
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,6 +71,8 @@ public class RecordService { | |
static final String HEADERS = "headers"; | ||
|
||
private boolean enableSchematization = false; | ||
private SnowflakeSinkConnectorConfig.BehaviorOnNullValues behaviorOnNullValues = | ||
SnowflakeSinkConnectorConfig.BehaviorOnNullValues.DEFAULT; | ||
|
||
// For each task, we require a separate instance of SimpleDataFormat, since they are not | ||
// inherently thread safe | ||
|
@@ -89,7 +91,6 @@ public class RecordService { | |
|
||
// This class is designed to work with empty metadata config map | ||
private SnowflakeMetadataConfig metadataConfig = new SnowflakeMetadataConfig(); | ||
|
||
/** Send Telemetry Data to Snowflake */ | ||
private final SnowflakeTelemetryService telemetryService; | ||
|
||
|
@@ -108,13 +109,12 @@ public RecordService(SnowflakeTelemetryService telemetryService) { | |
/** Record service with null telemetry Service, only use it for testing. */ | ||
@VisibleForTesting | ||
public RecordService() { | ||
this.telemetryService = null; | ||
this(null); | ||
sfc-gh-rcheng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
public void setMetadataConfig(SnowflakeMetadataConfig metadataConfigIn) { | ||
metadataConfig = metadataConfigIn; | ||
} | ||
|
||
sfc-gh-rcheng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/** | ||
* extract enableSchematization from the connector config and set the value for the recordService | ||
* | ||
|
@@ -145,26 +145,45 @@ public void setEnableSchematization(final boolean enableSchematization) { | |
this.enableSchematization = enableSchematization; | ||
} | ||
|
||
/** | ||
* Directly set the behaviorOnNullValues through param | ||
* | ||
* <p>This method is only for testing | ||
* | ||
* @param behaviorOnNullValues how to handle null values | ||
*/ | ||
@VisibleForTesting | ||
public void setBehaviorOnNullValues( | ||
final SnowflakeSinkConnectorConfig.BehaviorOnNullValues behaviorOnNullValues) { | ||
this.behaviorOnNullValues = behaviorOnNullValues; | ||
} | ||
|
||
/** | ||
* process given SinkRecord, only support snowflake converters | ||
* | ||
* @param record SinkRecord | ||
* @return a Row wrapper which contains both actual content(payload) and metadata | ||
*/ | ||
private SnowflakeTableRow processRecord(SinkRecord record) { | ||
SnowflakeRecordContent valueContent; | ||
|
||
if (record.value() == null || record.valueSchema() == null) { | ||
throw SnowflakeErrors.ERROR_5016.getException(); | ||
} | ||
if (!record.valueSchema().name().equals(SnowflakeJsonSchema.NAME)) { | ||
throw SnowflakeErrors.ERROR_0009.getException(); | ||
} | ||
if (!(record.value() instanceof SnowflakeRecordContent)) { | ||
throw SnowflakeErrors.ERROR_0010.getException( | ||
"Input record should be SnowflakeRecordContent object"); | ||
if (this.behaviorOnNullValues == SnowflakeSinkConnectorConfig.BehaviorOnNullValues.DEFAULT) { | ||
valueContent = new SnowflakeRecordContent(); | ||
} else { | ||
throw SnowflakeErrors.ERROR_5016.getException(); | ||
} | ||
} else { | ||
if (!record.valueSchema().name().equals(SnowflakeJsonSchema.NAME)) { | ||
throw SnowflakeErrors.ERROR_0009.getException(); | ||
} | ||
if (!(record.value() instanceof SnowflakeRecordContent)) { | ||
throw SnowflakeErrors.ERROR_0010.getException( | ||
"Input record should be SnowflakeRecordContent object"); | ||
} | ||
valueContent = (SnowflakeRecordContent) record.value(); | ||
} | ||
|
||
SnowflakeRecordContent valueContent = (SnowflakeRecordContent) record.value(); | ||
|
||
ObjectNode meta = MAPPER.createObjectNode(); | ||
if (metadataConfig.topicFlag) { | ||
meta.put(TOPIC, record.topic()); | ||
|
@@ -297,7 +316,7 @@ public SnowflakeTableRow(SnowflakeRecordContent content, JsonNode metadata) { | |
} | ||
|
||
void putKey(SinkRecord record, ObjectNode meta) { | ||
if (record.key() == null) { | ||
if (record.key() == null || record.keySchema() == null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why we can't put the key when the keySchema is null but not the key? This change might be risky and introduce a behavior change for both Snowpipe and Snowpipe Streaming There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the current code will NPE on the next if statement if keySchema is null. handleNativeRecord() prevents null keySchemas from being passed into putKey, however we should throw a better error if we ever get a null keySchema, so I added a null check. This shouldn't be risky because we are changing the error thrown from NPE to a Snowflake error, what do you think? |
||
return; | ||
} | ||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.