You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avro.apache.org by "Dan Lipofsky (Jira)" <ji...@apache.org> on 2020/07/23 21:49:00 UTC

[jira] [Created] (AVRO-2904) timestamp-millis doesn't truncate when in a union with null

Dan Lipofsky created AVRO-2904:
----------------------------------

             Summary: timestamp-millis doesn't truncate when in a union with null
                 Key: AVRO-2904
                 URL: https://issues.apache.org/jira/browse/AVRO-2904
             Project: Apache Avro
          Issue Type: Bug
          Components: java, logical types
    Affects Versions: 1.10.0
            Reporter: Dan Lipofsky


In Avro 1.10.0 the setter for logical-type timestamp-millis will truncate the timestamp to millis in the simplest condition but not when it is in a union with null.

It looks like something similar was addressed in AVRO-2360 but that was supposedly fixed in 1.9.0 and this is still broken in 1.10.0.

Here is my IDL
{noformat}
  record TimestampTest1 {
    union { null, timestamp_ms } ts;
  }
  record TimestampTest2 {
    timestamp_ms ts;
  }
{noformat}
Here is my test, in which {{TimestampTest2}} passes but {{TimestampTest1}} fails.
{noformat}
public class SerDeTest {
    @Test
    public void TimestampTest1() throws IOException {
        final TimestampTest1 ts = TimestampTest1.newBuilder().setTs(Instant.now()).build();
        byte[] bytes = serialize(ts);
        TimestampTest1 other = deserialize(ts.getSchema(), bytes);
        Assert.assertEquals(ts, other);
    }
    @Test
    public void TimestampTest2() throws IOException {
        final TimestampTest2 ts = TimestampTest2.newBuilder().setTs(Instant.now()).build();
        byte[] bytes = serialize(ts);
        TimestampTest2 other = deserialize(ts.getSchema(), bytes);
        Assert.assertEquals(ts, other);
    }

    private <T extends SpecificRecord> T deserialize(Schema schema, byte[] bytes)
            throws IOException {
        Decoder decoder = DecoderFactory.get().binaryDecoder(bytes, null);
        SpecificDatumReader<T> reader = new SpecificDatumReader<>(schema);
        return reader.read(null, decoder);
    }

    private byte[] serialize(SpecificRecord record) throws IOException {
        try (final ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            SpecificDatumWriter<SpecificRecord> writer = new SpecificDatumWriter<>(
                record.getSchema());
            Encoder encoder = EncoderFactory.get().binaryEncoder(out, null);
            writer.write(record, encoder);
            encoder.flush();
            return out.toByteArray();
        }
    }
}
{noformat}
{{TimestampTest1}} fails with
{noformat}
java.lang.AssertionError: 
Expected :{"ts": 2020-07-23T21:35:12.348379Z}
Actual   :{"ts": 2020-07-23T21:35:12.348Z}
{noformat}
If I change my tests to use {{Instant.now().truncatedTo(ChronoUnit.MILLIS)}} they'll both pass, but it seems like that should not be necessary, particularly given the code below.

Looking at the generated Java DTO, I see a few differences, but the most important would seem to be that the working DTO has
{noformat}
  public void setTs(java.time.Instant value) {
    this.ts = value.truncatedTo(java.time.temporal.ChronoUnit.MILLIS);
  }
{noformat}
while the broken one has
{noformat}
  public void setTs(java.time.Instant value) {
    this.ts = value;
  }
{noformat}
The working one also has this code, which is missing from the broken one:
{noformat}
  private static final org.apache.avro.Conversion<?>[] conversions =
      new org.apache.avro.Conversion<?>[] {
      new org.apache.avro.data.TimeConversions.TimestampMillisConversion(),
      null
  };

  @Override
  public org.apache.avro.Conversion<?> getConversion(int field) {
    return conversions[field];
  }
{noformat}



--
This message was sent by Atlassian Jira
(v8.3.4#803005)