Snapshot not being created, no errors being thrown

I’ve created a custom snapshot trigger definition that will either trigger a snapshot when the threshold has been hit or if the EventMessage payload type is a specific event.

        @Override
        public void eventHandled(EventMessage<?> msg) {
            if (msg instanceof DomainEventMessage) {
                if ((msg.getPayloadType().getSimpleName().toLowerCase().contains("delete") || msg.getPayloadType().getSimpleName().toLowerCase().contains("remove")) || exceedsThreshold()) {
                    prepareSnapshotScheduling((DomainEventMessage) msg);
                }
            }
        }

My aggregates are setup to use the custom trigger like this

@Aggregate(snapshotTriggerDefinition = "customSnapshotTrigger")

and the trigger definition bean is setup like this

@Bean
    public SnapshotTriggerDefinition customSnapshotTrigger(Snapshotter snapshotter) {
        return new CustomSnapshotTriggerDefinition(snapshotter, 2, 5000);
    }

I’m having issues that when the snapshot is triggered by the payload type it will not create the snapshot (can’t see it in the dashboard) but when the same snapshot trigger definition is triggered by the threshold then the snapshot is created.

I’ve tried following the creation of the snapshot through TransactionManger and AbstractSnapshotter classes but they react the same.

I’ve also tried triggering a snapshot by using the snapshotter.scheduleSnapshot within a commandHandler with the same results.

Hi,
it’s not clear to me what your parameter valued 5000 contains.

Based on what I understood, here is my suggestion: in your CustomSnapshotTriggerDefinition you can create a custom Trigger that extends AbstractSnapshotTrigger abstract class. In this one, you will need to override the eventHandled method (as you did), but also having a local checker field to save the result of your logic, and then call the method implemented in the super class to continue the normal execution implemented in the framework.

@Override
public void eventHandled(EventMessage<?> msg) {
    if (msg instanceof DomainEventMessage) {
        if ((msg.getPayloadType().getSimpleName().toLowerCase().contains("delete") || msg.getPayloadType().getSimpleName().toLowerCase().contains("remove"))) {
            this.deleteRemoveSpotted = true;
        }
    }
    super.eventHandled(msg);
}

You will then need to override the exceedsThreshold() method, adding this specific check. I understand that this name is misleading, but what it does is determine if a new snapshot should be created.

@Override
public boolean exceedsThreshold() {
    return deleteRemoveSpotted || (++counter >= threshold);
}

Don’t forget to reset the value of this new field by overriding the reset() method,

@Override
public void reset() {
    deleteRemovedSpotted = false;
    counter = 0;
}

Hope that you can achieve your intention.

Thanks for reply, I’ll try your suggestions.

I apologise if some it was unclear I was trying to describe the problem without posting a big blob of code and getting distracted

I combine both EventCountSnapshotTriggerDefinition and
AggregateLoadTimeSnapshotTriggerDefinition , that’s where the 5000 parameter is used.

So I updated my CustomSnapshotTriggerDefinition to reflect the changes you’ve suggested and I’m still running into the same issue where snapshots aren’t being created when its a remove event.
I’ve added two additional events into the eventHandled (archive & update) and a snapshot is created successfully when either of those events trigger it.

I’ve put breakpoints in SilentTask and TransactionManager and both this.snapshotterTask.run() and transaction.commit() get hit when the remove event triggers the snapshot creation, so I’m at a loss as to why its not being created.

public class CustomSnapshotTriggerDefinition implements SnapshotTriggerDefinition {
    private final Snapshotter snapshotter;
    private final int threshold;

    public CustomSnapshotTriggerDefinition(Snapshotter snapshotter, int threshold) {
        this.snapshotter = snapshotter;
        this.threshold = threshold;
    }

    public SnapshotTrigger prepareTrigger(Class<?> aggregateType) {
        return new CustomSnapshotTrigger(this.snapshotter, aggregateType, this.threshold);
    }

    public SnapshotTrigger reconfigure(Class<?> aggregateType, SnapshotTrigger trigger) {
        if (trigger instanceof CustomSnapshotTrigger) {
            ((CustomSnapshotTrigger)trigger).setSnapshotter(this.snapshotter);
            return trigger;
        } else {
            return new CustomSnapshotTrigger(this.snapshotter, aggregateType, this.threshold);
        }
    }

    private class CustomSnapshotTrigger extends AbstractSnapshotTrigger {
        private final int threshold;
        private int counter = 0;
        private boolean triggerSnapshot;

        public CustomSnapshotTrigger(Snapshotter snapshotter, Class<?> aggregateType, int threshold) {
            super(snapshotter, aggregateType);
            this.threshold = threshold;
            this.triggerSnapshot = false;
        }

        @Override
        public void eventHandled(EventMessage<?> msg) {
            if (msg instanceof DomainEventMessage) {
                if (msg.getPayloadType().getSimpleName().toLowerCase().contains("archive") || msg.getPayloadType().getSimpleName().toLowerCase().contains("remove") || msg.getPayloadType().getSimpleName().toLowerCase().contains("update")) {
                    this.triggerSnapshot = true;
                }
            }
            super.eventHandled(msg);
        }


        @Override
        public boolean exceedsThreshold() {
            return (this.triggerSnapshot || (++this.counter >= this.threshold))
        }

        @Override
        public void reset() {
            this.triggerSnapshot = false;
            this.counter = 0;
        }
    }
}

Hi
in your code I spotted some missing @Override annotation, and other minor stuff.

Here my code.

import org.axonframework.eventhandling.DomainEventMessage;
import org.axonframework.eventhandling.EventMessage;
import org.axonframework.eventsourcing.AbstractSnapshotTrigger;
import org.axonframework.eventsourcing.SnapshotTrigger;
import org.axonframework.eventsourcing.SnapshotTriggerDefinition;
import org.axonframework.eventsourcing.Snapshotter;

public class CustomSnapshotTriggerDefinition implements SnapshotTriggerDefinition {

    private final Snapshotter snapshotter;
    private final int threshold;

    public CustomSnapshotTriggerDefinition(Snapshotter snapshotter, int threshold) {
        this.snapshotter = snapshotter;
        this.threshold = threshold;
    }

    @Override
    public SnapshotTrigger prepareTrigger(Class<?> aggregateType) {
        return new CustomSnapshotTriggerDefinition.CustomSnapshotTrigger(snapshotter, aggregateType, threshold);
    }

    @Override
    public SnapshotTrigger reconfigure(Class<?> aggregateType, SnapshotTrigger trigger) {
        if (trigger instanceof CustomSnapshotTriggerDefinition.CustomSnapshotTrigger) {
            ((CustomSnapshotTriggerDefinition.CustomSnapshotTrigger) trigger).setSnapshotter(snapshotter);
            return trigger;
        }
        return new CustomSnapshotTriggerDefinition.CustomSnapshotTrigger(snapshotter, aggregateType, threshold);
    }

    private static class CustomSnapshotTrigger extends AbstractSnapshotTrigger {

        private final int threshold;
        private int counter = 0;
        private boolean triggerSnapshot = false;

        public CustomSnapshotTrigger(Snapshotter snapshotter, Class<?> aggregateType, int threshold) {
            super(snapshotter, aggregateType);
            this.threshold = threshold;
        }

        @Override
        public void eventHandled(EventMessage<?> msg) {
            if (msg instanceof DomainEventMessage) {
                if (msg.getPayloadType().getSimpleName().toLowerCase().contains("archive") || msg.getPayloadType().getSimpleName().toLowerCase().contains("remove") || msg.getPayloadType().getSimpleName().toLowerCase().contains("update")) {
                    this.triggerSnapshot = true;
                }
            }
            super.eventHandled(msg);
        }


        @Override
        public boolean exceedsThreshold() {
            return triggerSnapshot || (++counter >= threshold);
        }

        @Override
        public void reset() {
            triggerSnapshot = false;
            counter = 0;
        }

    }

}

I set the threshold value a bit biggest for debugging purposes

    @Bean
    public SnapshotTriggerDefinition chatRoomSnapshotTrigger(Snapshotter snapshotter) {
        return new CustomSnapshotTriggerDefinition(snapshotter, 5);
    }

I’m running this code, with no issue.
Can you please tell me which version of AxonFramework are you using? are you using Axon Server as well?

one side note: in your eventHandled method, while checking on events name, I see archive remove update.
At this level, since we are talking about events, I expect past tense (archived removed updated)

Sorry to be nitty picky here :wink:

Thanks I’ll have another go at it with your updates and will let you know.

You are right it should be past tense, I’ll need to change that :slight_smile:

Can you please tell me which version of AxonFramework are you using? are you using Axon Server as well?

I’m using

<dependency>
	<groupId>org.axonframework</groupId>
	<artifactId>axon-spring-boot-starter</artifactId>
	<version>4.5.14</version>
</dependency>

and

<dependency>
	<groupId>org.axonframework</groupId>
	<artifactId>axon-eventsourcing</artifactId>
	<version>4.5.14</version>
</dependency>

Ok I’ve found the issue. I had an event sourcing handler which called markDeleted on the AggregateLifecye. It was a legacy implementation before I started the snapshot integration and never really thought about it afterwards.

    @EventSourcingHandler
    public void on(AssetRemovedEvent event){
        AggregateLifecycle.markDeleted();
    }

Thanks for all your help, everything seems to be working correctly now.

I’m happy that you solved it.

:clap: