Проблемы кодера с Apache Beam и CombineFn

Мы строим конвейер, используя Apache Beam и DirectRunner в качестве бегуна. В настоящее время мы пытаемся создать простой конвейер, с помощью которого мы:

  1. Получение данных из Google Cloud Pub / Sub (в настоящее время для локального запуска используется эмулятор)
  2. Десериализовать в объект Java
  3. Оконные события с использованием фиксированных окон продолжительностью 1 минуту
  4. Объедините эти окна, используя пользовательский CombineFn, который преобразует их из событий в список событий.

Код конвейера:

pipeline
.apply(PubsubIO.<String>read().topic(options.getTopic()).withCoder(StringUtf8Coder.of()))

.apply("ParseEvent", ParDo.of(new ParseEventFn()))

.apply("WindowOneMinute",Window.<Event>into(FixedWindows.of(Duration.standardMinutes(1))))              

.apply("CombineEvents", Combine.globally(new CombineEventsFn()));

Функция ParseEvent:

    static class ParseEventFn extends DoFn<String, Event> {
        @ProcessElement
        public void processElement(ProcessContext c) {
            String json = c.element();
            c.output(gson.fromJson(json, Event.class));
        }
    }

Функция CombineEvents:

public static class CombineEventsFn extends CombineFn<Event, CombineEventsFn.Accum, EventListWrapper> {
        public static class Accum {
            EventListWrapper eventListWrapper = new EventListWrapper();
        }

        @Override
        public Accum createAccumulator() {
            return new Accum();
        }

        @Override
        public Accum addInput(Accum accumulator, Event event) {
            accumulator.eventListWrapper.events.add(event);
            return accumulator;
        }

        @Override
        public Accum mergeAccumulators(Iterable<Accum> accumulators) {
            Accum merged = createAccumulator();
            for (Accum accum : accumulators) {
                merged.eventListWrapper.events.addAll(accum.eventListWrapper.events);
            }
            return merged;
        }

        @Override
        public EventListWrapper extractOutput(Accum accumulator) {
            return accumulator.eventListWrapper;
        }

    }

При попытке запустить это локально с помощью Maven и DirectRunner мы получаем следующую ошибку:

java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Unable to return a default Coder for CombineEvents/Combine.perKey(CombineEvents)/Combine.GroupedValues/ParDo(Anonymous).out [PCollection]. Correct one of the following root causes:
  No Coder has been manually specified;  you may do so using .setCoder().
  Inferring a Coder from the CoderRegistry failed: Unable to provide a default Coder for org.apache.beam.sdk.values.KV<K, OutputT>. Correct one of the following root causes:
  Building a Coder using a registered CoderFactory failed: Cannot provide coder for parameterized type org.apache.beam.sdk.values.KV<K, OutputT>: Unable to provide a default Coder for java.lang.Object. Correct one of the following root causes:
  Building a Coder using a registered CoderFactory failed: Cannot provide coder based on value with class java.lang.Object: No CoderFactory has been registered for the class.
  Building a Coder from the @DefaultCoder annotation failed: Class java.lang.Object does not have a @DefaultCoder annotation.
  Building a Coder from the fallback CoderProvider failed: Cannot provide coder for type java.lang.Object: org.apache.beam.sdk.coders.protobuf.ProtoCoder$2@6e610150 could not provide a Coder for type java.lang.Object: Cannot provide ProtoCoder because java.lang.Object is not a subclass of com.google.protobuf.Message; org.apache.beam.sdk.coders.SerializableCoder$1@7adc59c8 could not provide a Coder for type java.lang.Object: Cannot provide SerializableCoder because java.lang.Object does not implement Serializable.
  Building a Coder from the @DefaultCoder annotation failed: Class org.apache.beam.sdk.values.KV does not have a @DefaultCoder annotation.
  Using the default output Coder from the producing PTransform failed: Unable to provide a default Coder for org.apache.beam.sdk.values.KV<K, OutputT>. Correct one of the following root causes:
  Building a Coder using a registered CoderFactory failed: Cannot provide coder for parameterized type org.apache.beam.sdk.values.KV<K, OutputT>: Unable to provide a default Coder for java.lang.Object. Correct one of the following root causes:
  Building a Coder using a registered CoderFactory failed: Cannot provide coder based on value with class java.lang.Object: No CoderFactory has been registered for the class.
  Building a Coder from the @DefaultCoder annotation failed: Class java.lang.Object does not have a @DefaultCoder annotation.
  Building a Coder from the fallback CoderProvider failed: Cannot provide coder for type java.lang.Object: org.apache.beam.sdk.coders.protobuf.ProtoCoder$2@6e610150 could not provide a Coder for type java.lang.Object: Cannot provide ProtoCoder because java.lang.Object is not a subclass of com.google.protobuf.Message; org.apache.beam.sdk.coders.SerializableCoder$1@7adc59c8 could not provide a Coder for type java.lang.Object: Cannot provide SerializableCoder because java.lang.Object does not implement Serializable.
  Building a Coder from the @DefaultCoder annotation failed: Class org.apache.beam.sdk.values.KV does not have a @DefaultCoder annotation.
    at org.apache.beam.sdk.repackaged.com.google.common.base.Preconditions.checkState(Preconditions.java:444)
    at org.apache.beam.sdk.values.TypedPValue.getCoder(TypedPValue.java:51)
    at org.apache.beam.sdk.values.PCollection.getCoder(PCollection.java:130)
    at org.apache.beam.sdk.values.TypedPValue.finishSpecifying(TypedPValue.java:90)
    at org.apache.beam.sdk.runners.TransformHierarchy.finishSpecifyingInput(TransformHierarchy.java:143)
    at org.apache.beam.sdk.Pipeline.applyInternal(Pipeline.java:418)
    at org.apache.beam.sdk.Pipeline.applyTransform(Pipeline.java:334)
    at org.apache.beam.sdk.values.PCollection.apply(PCollection.java:154)
    at org.apache.beam.sdk.transforms.Combine$Globally.expand(Combine.java:1459)
    at org.apache.beam.sdk.transforms.Combine$Globally.expand(Combine.java:1336)
    at org.apache.beam.sdk.Pipeline.applyInternal(Pipeline.java:420)
    at org.apache.beam.sdk.Pipeline.applyTransform(Pipeline.java:350)
    at org.apache.beam.sdk.values.PCollection.apply(PCollection.java:167)
at ***************************.main(***************.java:231)
... 6 more

Приносим извинения за огромный дамп кода - хотел предоставить весь контекст.

Мне любопытно, почему он жалуется на отсутствие кодировщика по умолчанию для java.lang.Object и org.apache.beam.sdk.values.KV<K, OutputT> - насколько я могу судить, наш конвейер меняет типы между String, Event и EventListWrapper - у последних двух классов кодеры по умолчанию установлены в классе сам (AvroCoder в обоих случаях).

Ошибка возникает в строке, где мы применяем CombineFn - можно подтвердить, что без этого преобразования конвейер работает.

Я подозреваю, что мы как-то неправильно настроили комбинирующее преобразование, но пока еще не нашли в документации Beam ничего, что указывало бы нам в правильном направлении.

Любое понимание будет оценено - заранее спасибо!


person Chris Staikos    schedule 16.05.2017    source источник
comment
Какую версию Beam вы используете?   -  person Ben Chambers    schedule 16.05.2017
comment
Ой, наверное, следовало включить это: 0.6.0   -  person Chris Staikos    schedule 16.05.2017
comment
зарегистрирован ли кодировщик Event.class?   -  person CasualT    schedule 16.05.2017
comment
Я так считаю - у меня @DefaultCoder(AvroCoder.class) в определении этого класса   -  person Chris Staikos    schedule 16.05.2017
comment
Зарегистрирован ли в классе CombineEventsFn.Accum кодировщик?   -  person Ben Chambers    schedule 16.05.2017
comment
Это не! Я как раз собирался опубликовать, что это проблема :) Добавил @DefaultCoder(AvroCoder.class) в Accum, и это исправило. Не стесняйтесь отвечать, и я приму - спасибо!   -  person Chris Staikos    schedule 16.05.2017


Ответы (2)


Вероятная причина, по которой вы видите java.lang.Object, заключается в том, что Beam пытается вывести кодировщик для переменной неразрешенного типа, которая будет разрешена в Object. Это может быть ошибкой в ​​том, как выполняется вывод кодера в Combine.

Отдельно я ожидал бы, что класс Accum также вызовет сбой вывода кодера. Вы можете переопределить getAccumulatorCoder в своем CombineFn, чтобы указать его напрямую.

person Kenn Knowles    schedule 16.05.2017
comment
Спасибо! Я добавил @DefaultCoder(AvroCoder.class) в определение класса для CombineEventsFn.AvroCoder, что, как мне кажется, достигло именно этого :) - person Chris Staikos; 16.05.2017

Вы проверяли, работает ли добавление Serializable в ваш аккумулятор напрямую?

Так что добавьте "реализует сериализуемые" в класс Accum ...

public static class Accum implements Serializable {
            EventListWrapper eventListWrapper = new EventListWrapper();
        }
person Nishant    schedule 06.03.2019
comment
Не могли бы вы объяснить, как это разрешает данное исключение? Уже существует существующий ответ, за который проголосовали по-другому, поэтому вам обязательно нужно объяснить свой код. - person Nico Haase; 06.03.2019
comment
Вместо того, чтобы явно указывать Coder для Accumulator, можно указать класс Accum как Serializable (при условии, что он действительно Serializable). Вышеупомянутая ошибка возникает из-за того, что кодер для аккумулятора не указан. Приведенный выше подход в основном просит кодировщика перезаписать спецификацию кодировщика по умолчанию, и это нормально, если это необходимо. Однако, если предположить, что класс EventListWrapper может быть сериализован, указание реализаций Serializable упрощает - person Nishant; 08.03.2019
comment
Пожалуйста, добавляйте всю подобную информацию в сам ответ, а не в раздел комментариев. - person Nico Haase; 08.03.2019