Multi upcasting in Axon 3.1.1 : possible regression ?

Hello,

we are using axon in production and we have a potential regression concerning upcasting when upgrading to Axon 3.1.1.

With Axon 3.1 we have an upcaster spring bean that upcasts from one event to three events and it works perfectly :

@Component
public class MyUpcaster extends EventMultiUpcaster {
    private static SimpleSerializedType targetType = new SimpleSerializedType("my.events.Event", "1");

    @Override
    protected boolean canUpcast(final IntermediateEventRepresentation ir) {
        return ir.getType().equals(targetType);
    }

    @Override
    protected Stream<IntermediateEventRepresentation> doUpcast(final IntermediateEventRepresentation ir) {
        return Stream.of(
                ir.upcastPayload(
                        new SimpleSerializedType("my.events.Event", "2"), JsonNode.class, jsonNode -> jsonNode),

                ir.upcastPayload(
                        new SimpleSerializedType("my.events.OtherEvent1", "1"), JsonNode.class, jsonNode -> jsonNode),

                ir.upcastPayload(
                        new SimpleSerializedType("my.events.OtherEvent2", "1"), JsonNode.class, jsonNode -> jsonNode)
        );
    }
}

The following handlers are called

@Aggregate(type = "myAggregate")
public class MyAggregate {
    ...

    @EventSourcingHandler
    public void on(final my.events.Event event){
        OK PASS INTO HANDLER
    }

    @EventSourcingHandler
    public void on(final my.events.OtherEvent1 event){
        OK PASS INTO HANDLER
    }

    @EventSourcingHandler
    public void on(final my.events.OtherEvent2 event){
        OK PASS INTO HANDLER
    }
}

When upgrading from axon 3.1 to 3.1.1, only the first handler is called

@Aggregate(type = "myAggregate")
public class MyAggregate {
    ...

    @EventSourcingHandler
    public void on(final my.events.Event event){
        OK PASS INTO HANDLER
    }

    @EventSourcingHandler
    public void on(final my.events.OtherEvent1 event){
        KO DO NOT PASS INTO HANDLER
    }

    @EventSourcingHandler
    public void on(final my.events.OtherEvent2 event){
        KO DO NOT PASS INTO HANDLER
    }
}

Thanks to unit tests and ConcatenatingDomainEventStream.java javadoc we have found a workaround.
We have to set an incremented sequence number in each IntermediateEventRepresentation

@Component
public class MyUpcaster extends EventMultiUpcaster {
    private static SimpleSerializedType targetType = new SimpleSerializedType("my.events.Event", "1");

    @Override
    protected boolean canUpcast(final IntermediateEventRepresentation ir) {
        return ir.getType().equals(targetType);
    }

    @Override
    protected Stream<IntermediateEventRepresentation> doUpcast(final IntermediateEventRepresentation firstRepresentation) {

        //first representation sequence number is equal to zero (in our case)
        GenericDomainEventMessage<my.events.OtherEvent1> secondEventMessage = new GenericDomainEventMessage<>(
                firstRepresentation.getAggregateType().get(), firstRepresentation.getAggregateIdentifier().get(), firstRepresentation.getSequenceNumber().get()+1L, new my.events.OtherEvent1());

        EventData<?> secondEventData = new DomainEventEntry(secondEventMessage, serializer);

        InitialEventRepresentation secondRepresentation =
                new InitialEventRepresentation(secondEventData, serializer);

        GenericDomainEventMessage<my.events.OtherEvent2> thirdEventMessage = new GenericDomainEventMessage<>(
                firstRepresentation.getAggregateType().get(), firstRepresentation.getAggregateIdentifier().get(), firstRepresentation.getSequenceNumber().get()+2L, new my.events.OtherEvent2());

        EventData<?> thirdEventData = new DomainEventEntry(thirdEventMessage, serializer);

        InitialEventRepresentation thirdRepresentation =
                new InitialEventRepresentation(thirdEventData, serializer);

        return Stream.of(
                firstRepresentation.upcastPayload(
                        new SimpleSerializedType("my.events.Event", "2"), JsonNode.class, jsonNode -> jsonNode),

                secondRepresentation.upcastPayload(
                        new SimpleSerializedType("my.events.OtherEvent1", "1"), JsonNode.class, jsonNode -> jsonNode),

                thirdRepresentation.upcastPayload(
                        new SimpleSerializedType("my.events.OtherEvent2", "1"), JsonNode.class, jsonNode -> jsonNode)
        );
    }

And Now all handlers are called again

@Aggregate(type = "myAggregate")
    public class MyAggregate {
    ...

        @EventSourcingHandler
        public void on(final my.events.Event event) {
            OK PASS INTO HANDLER
        }

        @EventSourcingHandler
        public void on(final my.events.OtherEvent1 event) {
            OK PASS INTO HANDLER
        }

        @EventSourcingHandler
        public void on(final my.events.OtherEvent2 event) {
            OK PASS INTO HANDLER
        }

Questions :
Is it a side effect since 3.1.1 ? or the new behaviour ?
Are we doing something wrong ?

Many thanks.

Nicolas

+1

Hi Nicolas, Richard,

Sorry for the long wait, this required some discussion over here to figure it out.

First off, you should not have to adjust the sequence id when upcasting your events, also in the scenario where you’re using an EventMultiUpcaster.

The ConcatenatingDomainEventStream should be able to coop with this scenario and leave any events in the initial event stream with reoccuring sequence ids.

Only when it starts reading form the following stream should it ignore sequence ids it has already encountered.

I’ve just created this issue as documentation for the problem and will be trying to solve it today.

It’s slated for 3.1.3, which we’re trying to release this week.

So, stay tuned!

Cheers,

Steven

Hi Steven,
thank you very much for this answer !

We will surely use the new 3.1.3 version as soon as it is released.

Cheers,

Richard (and Nicolas :-))

Hi Nicolas, Richard,

The issue is resolved and merged to 3.1.3, which is now available for download.

Cheers,

Steven

Hello,

with 3.1.3 it works again !! Good job :slight_smile:

Many thanks.

Nicolas & Richard

Hi Nicolas and Richard,

Great to hear that solved it for you guys! :slight_smile:

Cheers,

Steven