Could not make an Avro Schema object from date - apache-kafka

I have an Avro schema with this property:
{"name": "whenDate", "type": ["date", "null"]}
I am using a Python client and the producer
confluent_kafka.avro.AvroProducer
When I load the Avro schema with aforementioned property,
I trigger this error:
not a valid Avro schema: Type property "[u'date', u'null']" not a valid
Avro schema: Union item must be a valid Avro schema: Could not make an Avro Schema object from date.
I presume this error speaks to limitations in the Confluent parser for Avro schema. That is, date is a logical type specified here, but it not as yet supported by the Confluent parser.
Can anyone confirm? More generally, should I assume that the Confluent parser does not handle the logical types for Avro schema? Thanks.

It is a logical type, I think you just need to declare it as a one.
{"name": "date", "type": "int", "logicalType": "date"}

Related

How to transform all timestamp fields according to avro scheme when using Kafka Connect?

In our database we have over 20 fields which we need to transform from long to timestamp. Why there is no generic solution to transfer all this value ?
I know I can define:
"transforms":"tsFormat",
"transforms.tsFormat.type": "org.apache.kafka.connect.transforms.TimestampConverter$Value",
"transforms.tsFormat.target.type": "string",
"transforms.tsFormat.field": "ts_col1",
"transforms.tsFormat.field": "ts_col2",
but this is not solution for us. When we add new timestamp to db we need to update connector too
is there some generic solution to transform all fields according to avro schema ?
We are using debezium which for all timestamp fields create something like this:
{
"name": "PLATNOST_DO",
"type": {
"type": "long",
"connect.version": 1,
"connect.name": "io.debezium.time.Timestamp"
}
},
so how to find all type with connect.name = 'io.debezium.time.Timestamp' and transform it to timestamp
You'd need to write your own transform to be able to dynamically iterate of the record schema, check the types, and do the conversion.
Thus why they are called simple message transforms.
Alternatively, maybe take a closer look at the Debezium properties to see if there is a missing setting that alters how timestamps get produced.

Using Kafka TimestampConverter with microseconds?

I'm new to Kafka/Avro. My downstream database (Postgres) has a timestamptz column. My upstream database (Materialize) produces the following Avro schema for the column:
"type": [
"null",
{
"logicalType": "timestamp-micros",
"type": "long"
}
]
This seems consistent with timestamptz, which also stores microseconds. However, I'm getting the following:
org.postgresql.util.PSQLException: ERROR: column "time" is of type timestamp with time zone but expression is of type bigint
Hint: You will need to rewrite or cast the expression.
Position: 207
at io.confluent.connect.jdbc.sink.JdbcSinkTask.put(JdbcSinkTask.java:122)
at org.apache.kafka.connect.runtime.WorkerSinkTask.deliverMessages(WorkerSinkTask.java:581)
... 10 more
It seems like the Kafka messages contain bigint/long and JdbcSinkConnector doesn't know how to convert it to a timestamp. I tried using Kafka's TimestampConverter, but it assumes the input timestamp is in milliseconds.
It doesn't look like TimestampConverter supports microseconds directly (https://issues.apache.org/jira/browse/KAFKA-10561)
Is there a way to convert microseconds to milliseconds in Kafka connectors? This can just be a hack, so I don't need to change the Avro schema. If there's a transform to divide by 1000 or remove the last 3 digits, that should work. Is there a way to do this without a custom transform or ksqlDB?

Debezium MongoDb - adding non metadata headers with ExtractNewDocumentState SMT not working

I'm having trouble adding an header from a document field (not metadata), which I'm able to do using the ExtractNewRecordState SMT on Postgres, but not using ExtractNewDocumentState on MongoDb.
This is the configuration which allows me to copy a non metadata field to a header using ExtractNewRecordState (by reaching into the "after" object):
"transforms": "unwrap",
"transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState",
"transforms.unwrap.add.headers": "after.Id:Id",
"transforms.unwrap.add.headers.prefix": "",
If I try the same configuration using the ExtractNewDocumentState SMT for MongoDb, it doesn't work:
"transforms": "unwrap",
"transforms.unwrap.type":"io.debezium.connector.mongodb.transforms.ExtractNewDocumentState",
"transforms.unwrap.add.headers": "after.Id:Id",
"transforms.unwrap.add.headers.prefix": "",
I get the following error:
java.lang.IllegalArgumentException: Unexpected field name: after.Id
I suspect it has to with the type of the "after" object being a string, instead of an object (as in Postgres), so at this point the SMT is not able to reach into the object to get the fields.
From what I've seen there is no SMT available for copying a field into a header, so how can I overcome this issue?

Binary avro type mapping to Postgres?

I have the following avro definition for my Nifi flow, where i'm reading a from a BLOB database column. I'm mapping the 'xxPZPVSTREAM' column as a 'bytes' type in my avro definition
{
"namespace":"a.b.c",
"name":"pc_history",
"type":"record",
"fields": [
{"name":"COMMITDATETIME","type":["null",{"type":"long","logicalType":"timestamp-millis"}]},
....
{"name":"xxPZPVSTREAM","type":["bytes","null"]},
{"name":"xxx","type":["string","null"]}
]
}
When i attempt to write the mapped data to a Postgres database i get this error
org.postgresql.util.PSQLException: Can’t infer the SQL type to use for an instance of [Ljava.lang.Byte;. Use setObject() woth an explicit Types values to specify the type to use.
Can i add extra meta information to the avro definition to allow the Nifi processor to correctly map this binary column?
You didn't say which processor you're using, but this should be supported by PutDatabaseRecord. That processor is what you'd want to use for this as it should map byte array fields to a blob. If it doesn't, then please join the nifi-dev mailing list and let us know.

using different avro schema for new columns

I am using flume + kafka to sink the log data to hdfs. My sink data type is Avro. In avro schema (.avsc), there is 80 fields as columns.
So I created an external table like that
CREATE external TABLE pgar.tiz_biaws_fraud
PARTITIONED BY(partition_date INT)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
LOCATION '/data/datapool/flume/biaws/fraud'
TBLPROPERTIES ('avro.schema.url'='hdfs://xxxx-ns/data/datapool/flume/biaws/fraud.avsc')
Now, I need to add 25 more columns to avro schema. In that case,
if I create a new table with new schema which has 105 columns, I will have two table for one project. And if I add or remove some columns in coming days, I have to create a new table for that. I am afraid of having a lot of table which use different schema for same project.
If I swap the old schema with new schema in current table, I will have only one table for one project but I can't read and get old data anymore because of schema conflict.
What is the best way to use avro schema in case like that?
This is indeed challenging. The best way is to make sure all schema changes you make are compatible with the old data - so only remove columns with defaults, and make sure you give defaults in the columns you are adding. This way you can safely swap out the schemas without a conflict and keep reading old data. Avro is pretty clever about that, it's called "schema evolution" (in case you want to google a bit more) and allows reader and writer schemas to be a bit different.
As an aside, I want to mention that Kafka has a native HDFS connector (i.e. without Flume) that uses Confluent's schema registry to handle these kinds of schema changes automatically - you can use the registry to check if the schemas are compatible, and if they are - simply write data using the new schema and the Hive table will automatically evolve to match.
I added new columns to avro schema like that
{"name":"newColumn1", "type": "string", "default": ""},
{"name":"newColumn2", "type": "string", "default": ""},
{"name":"newColumn3", "type": "string", "default": ""},
When I use default property, if that columns doesn't exist in current data it returns default value, if that columns does exist in current data it returns the data value as expected.
For setting null value as default, you need that
{ "name": "newColumn4", "type": [ "string", "null" ], "default": "null" },
or
{ "name": "newColumn5", "type": [ "null", "string" ]},
The position of null in type property, can be first place or can be second place with default property.