0

I am working on a project in which a database is polled for events, if a valid event is detected then service activators are called downstream to get information about the event. Some processing is performed and then the result is written to back to the database.

What I am trying to achieve is in the case in which the database is down or any of the downstream services are unavailable then my microservice would pause polling for a configurable amount of time after which it would start again.

So far I have looked into CircuitBreakerAdvice and RetryAdvice but those seem to apply to service activators and not inbound channel adapters. I am also aware that Resilience4j provides a comprehensive circuit breaker mechanism but I have found no way to implement it into my project.

The solution that I have come up with is to implement a ReceiveMessageAdvice which sets the polling active and pass it to the poller. The error channel will keep track of the number of errors that have accumulated and when a configured threshold is reached it sets the pollingActive attribute to false. As for reactivating the polling I am a bit stuck. My guess would be scheduling a task to change value back to true after some time but am unsure of where or how to do it.

Polling Channel

@Bean
public IntegrationFlow readDBMessage() {
    return IntegrationFLows.fromSupplier(
            () -> dbService.readMessage(),
                  channelAdapter ->
                         channelAdapter.poller(
                                pollerSpec ->
                                      pollerSpec.fixedDelay(
                                                 \\polling period)
                                                .advice(messagePollingControlAdvice())
            .channel("apiCallChannel")
            .get();
}

MessagePollingControlAdvice

   public static class MessagePollingControlAdvice implements ReceiveMessageAdvice {
    private volatile boolean pollingActive = false;

    @Override
    public boolean beforeReceive(Object source) {
        return pollingActive;
    }

    @Override
    public Message<?> afterReceive(Message<?> result, Object source) {
        return result;
    }

    public boolean isPollingActive() {
        return pollingActive;
    }

    //call this method from whatever place in your code to activate/deactivate poller
    public void setPollingActive(boolean pollingActive) {
        this.pollingActive = pollingActive;
    }
   } 

Taken from How to stop OR change delay of Spring Integration Poller

Any advice on how I should go on about doing this? Is there something that I am missing in the documentation?

UPDATE Thank you Artem!

I have implemented the suggestion that Artem has given. Below is the code for reference in case anyone else runs into this.

MessagePollingControlAdvice

   public static class MessagePollingControlAdvice implements ReceiveMessageAdvice {
    private volatile boolean pollingActive = false;
    private volatile Long pollingDeactivatedTime = Instant.now().getEpochSecond();

    @Override
    public boolean beforeReceive(Object source) {
        
        // Get the desired time from configuration file
        if (!pollingActive && (Instant.now().getEpochSecond() - pollingDeactivatedTime) > 30) {
            pollingActive = true;

        }
        return pollingActive;
    }

    @Override
    public Message<?> afterReceive(Message<?> result, Object source) {
        return result;
    }

    public boolean isPollingActive() {
        return pollingActive;
    }

    //call this method from whatever place in your code to activate/deactivate poller
    public void setPollingActive(boolean pollingActive) {
        this.pollingDeactivatedTime = Instant.now().getEpochSecond();
        this.pollingActive = pollingActive;
    }
   } 

I have taken a look at SimpleActiveIdleReceiveMessageAdvice and will certainly implement some of the logic in my code as well.

As a follow up question: from what I understand the code inside the advice gets executed even when an error occurs during polling, so is it possible to keep track of errors in this class and extend the logic to deactivate polling from inside it?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Ramac
  • 1
  • 2

1 Answers1

0

You can do that timeout check in your MessagePollingControlAdvice. There is no need in extra scheduled task since this poller on the channel adapter is still scheduled.

So, in that beforeReceive() you can track not only the current state, but also the time from the previous opened change.

The SimpleActiveIdleReceiveMessageAdvice should be also a good addition to your whole algorithm.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Thank you Artem, your advice has been very useful. I have updated the code according to your suggestion and it seems to work well. This approach is much better than what I had in mind since downstream I intend to use an executor channel to batch requests and was worried that the different threads might crash with eachother. – Ramac Jun 01 '22 at 17:24
  • When your `beforeReceive()` returns `false`, that your executor channel is not going to be involved. Just because there is not going to be a message emitted from this poll. – Artem Bilan Jun 01 '22 at 17:50