Skip to content

Make TransactionalApplicationListenerAdapter support fallbackExecution #34674

@reda-alaoui

Description

@reda-alaoui

I have the following use case.

I have a bean that should act as a multi node event router.
The bean must listen for a particular event interface and broadcast a received event to the other nodes. If a transaction is running, event should be broadcast after commit. If there is no transaction, the event should be immediatly broadcast.

The event interface looks like this:

interface MultiNodeClusterEvent extends Serializable {}

The router looks like this:

/**
 * @author Réda Housni Alaoui
 */
@Component
class MultiNodeClusterEventRouter {

  private final ApplicationEventPublisher eventPublisher;

  MultiNodeClusterEventRouter(ApplicationEventPublisher eventPublisher) {
    this.eventPublisher = requireNonNull(eventPublisher);
  }

  @TransactionalEventListener(fallbackExecution = true)
  public void onLocalEvent(MultiNodeClusterEvent event) {
    // Broadcast to other nodes
  }

  // Triggered by JGroup
  private void onRemoteEvent(MultiNodeClusterEvent event) {
    eventPublisher.publishEvent(event);
  }

}

The obvious issue with this code is that the routers (plural because 1 router/node) may enter in an infinite broadcast loop. The easiest solution that comes to mind is to make the router check the event source and ignore the event if the source is itself. But since I am listening for a raw event, I don't have access to ApplicationEvent informations.

I know that under the cover, Spring use PayloadApplicationEvent for raw events. So I try to implement the infinite loop guard like this:

/**
 * @author Réda Housni Alaoui
 */
@Component
class MultiNodeClusterEventRouter extends TransactionalApplicationListenerAdapter<PayloadApplicationEvent<MultiNodeClusterEvent>> {

  private final ApplicationEventPublisher eventPublisher;

  MultiNodeClusterEventRouter(ApplicationEventPublisher eventPublisher) {
    super(event -> {
      // Missing fallbackExecution !
      // If event.source == this, ignore.
      // Else, broadcast to other nodes
   });
    this.eventPublisher = requireNonNull(eventPublisher);
  }

  // Triggered by JGroup
  private void onRemoteEvent(MultiNodeClusterEvent event) {
    eventPublisher.publishEvent(new PayloadApplicationEvent<>(event));
  }

}

But when I do this, I loose TransactionalApplicationListenerMethodAdapter#fallbackExecution feature.

Could we add this feature to TransactionalApplicationListenerAdapter and pass its activation as an argument to TransactionalApplicationListener#forPayload ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: dataIssues in data modules (jdbc, orm, oxm, tx)status: feedback-providedFeedback has been providedstatus: invalidAn issue that we don't feel is valid

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions