Skip to content

leader election for spring boot based on solace PubSub+ exclusive queue

License

Notifications You must be signed in to change notification settings

solacecommunity/spring-solace-leader-election

Repository files navigation

Contributor Covenant

Leader election for spring integration with solace

This starter provides leader election based on solace exclusive queues.

Spring Cloud Version Compatibility

Consult the table below to determine which version you need to use:

Spring Cloud solace-spring-integration-leader Spring Boot sol-jcsmp
2023.0.3 1.3.6 3.3.3 10.24.1
2023.0.3 1.3.5 3.3.3 10.24.1
2023.0.3 1.3.4 3.3.3 10.24.1
2023.0.2 1.3.3 3.3.0 10.24.0
2023.0.2 1.3.2 3.3.0 10.23.0
2023.0.1 1.3.1 3.2.5 10.23.0

Usage

Dependencies

In order to be able to use the leader election functionality, add the following section to your Maven pom:

<dependency>
    <groupId>community.solace.spring.integration</groupId>
    <artifactId>solace-spring-integration-leader</artifactId>
    <version>1.3.6</version>
</dependency>

Runtime Dependencies

Starting with version 1.3.4 the JCSMP Session will be shared when provided by:

<dependency>
    <groupId>ch.sbb</groupId>
    <artifactId>spring-cloud-stream-binder-solace</artifactId>
    <version>[5.0.0,)</version>
</dependency>

Use cases

A consumer group wants to schedule tasks

Only one member of your consumer group should run the scheduled task.

You have a service/component with the spring standard @Scheduled annotation, as well as the @LeaderAware annotation, that suppresses the method execution if you are not leader of the example group "demo".

@Service
public class MyScheduledService {

    @Scheduled(fixedRateString = "PT3S", initialDelayString = "PT1S")
    @LeaderAware("demo")
    void scheduler() {
        log.info("I am the leader and log it schedules all 3s");
    }
}

The @LeaderAware annotation can be used on any component method. Those methods will return null if the code was not executed. You need to specify in your application.yaml to auto join the group whenever your application is ready.

spring:
  leader:
    join-groups:
      - groupName: demo
        joinType: ON_READINESS

Having leader group name in configuration

if your not want to have the leader group name in your configuration. You could do:

@Service
public class MyScheduledService {

    @Scheduled(fixedRateString = "PT3S", initialDelayString = "PT1S")
    @LeaderAware(configValue = "application.name")
    void scheduler() {
        log.info("I am the leader and log it schedules all 3s");
    }
}
application:
  name: DemoApp

spring:
  leader:
    join-groups:
      - groupName: DemoApp
        joinType: ON_READINESS

Execute business logic on leader events

When listening to leader based events, you will receive OnGrantedEvent and OnRevokedEvent events.
You can query the leader role via event.getRole() or use the condition = "#leaderEvent.role == 'demo'" option of the @EventListener to receive events only for a single leader group.

This requires the join-groups configuration set to "ON_READINESS".

    @EventListener(classes = {AbstractLeaderEvent.class}, condition = "#leaderEvent.role == 'demo'")
    void leaderEventListener(AbstractLeaderEvent leaderEvent)  {
        log.warn("LeaderController: " + leaderEvent);
    }

Test for leadership within business logic

You can test whether you are the leader in your business logic and decide what you want to do with this information.

@Autowired
private SolaceLeaderInitiator leaderInitiator;

private void yourLeaderMethod() {
    boolean isLeader = leaderInitiator.getContext("theNameOfTheRoleC").isLeader();
    if (isLeader) {
    	// ...
    }
    return;
}

Yield the leadership - "local process (debugging)"

In case you have a cluster of leader services you may want to hand over the leadership to a local process. The leadership can not be taken but must be handed over to a new process, which joined the cluster. The order of leadership is based on the time a process joined the group. When a process X currently owns the leadership and wants to yield it, the next leader process will be the process, which joined the group right after process X.

@GetMapping(value = "/leader/yield/{role}")
public String yieldLeaderShip(@PathVariable("role") final String role) {
    Context context = leaderInitiator.getContext(role);
    if (context.isLeader()) {
        context.yield();
        return context.isLeader() ? "I am leader AGAIN! Am I the only group member?" : "I am not longer the leader";
    }
    return "I was not the leader";
}

Yield the leadership - at shutdown

By default, the leadership will be yielded on shutdown. To improve the failover speed. Because on a grace full application shut down. Tear down broker connection, database connection can take up to 1sec.

But if there is a reason that stops you from using this feature you can disable this by:

spring:
  leader:
    join-groups:
      - groupName: DemoApp
        joinType: ON_READINESS
        yieldOnShutdown: false

or if you join groups programmatically:

@Autowired
private SolaceLeaderInitiator leaderInitiator;

private void yourMethode() {
    leaderInitiator.joinGroup("theNameOfTheRoleA", false);
}

Requirement

Just add this to your pom.xml file:

        <dependency>
            <groupId>community.solace.spring.integration</groupId>
            <artifactId>solace-spring-integration-leader</artifactId>
            <version>1.2.0</version>
        </dependency>

Config

join-groups

There are three different options to join a group:

  • programmatically: explicitly invoke a method to join a group
  • during the first request of the leader context
  • as soon as your application is ready

Join methods are appended to the leader group name below the "join-groups" configuration key:

spring:
  leader:
    join-groups:
      - groupName: theNameOfTheRoleA
        joinType: PROGRAMMATIC
      - groupName: theNameOfTheRoleB
        joinType: PROGRAMMATIC
      - groupName: theNameOfTheRoleC
        joinType: FIRST_USE
      - groupName: demo
        joinType: ON_READINESS

Join group PROGRAMMATIC

@Autowired
private SolaceLeaderInitiator leaderInitiator;

private void yourMethode() {
    leaderInitiator.joinGroup("theNameOfTheRoleA");
}

By default, also those groups has to be defined in your application configuration.
You can allow anonymous PROGRAMMATIC stated groups by setting:

spring:
  leader:
    permit-anonymous-groups: true

⚠️ Be aware of the side effect, to be able to join groups via JMX you may not want to be joined from this application.

Join group FIRST_USE

The leader group will be joined when you request the leader context for the first time.

@Autowired
private SolaceLeaderInitiator leaderInitiator;

@GetMapping(value = "/leader/status/{role}")
public String isLeader(@PathVariable("role") final String role) {
    return leaderInitiator.getContext(role).isLeader() ? "leader" : "passive";
}

Join group ON_READINESS

The leader group will be joined as soon as your application is ready. This allows to run bootstrap code like cache loading via ApplicationRunner This is useful in combination with the @LeaderAware annotation.

Solace specifics

Queues

A durable Solace queue is provisioned for each leader group. The queue name is "leader." + role. A queue is exclusively used for the flow activation trigger. No other messages are supposed to pass this queue. Any other message apart from the flow activation trigger will lead to an exception.

⚠️ The Solace ClientProfile must have "Allow client to create endpoints" set to true

Timeout

The fail over timeout is managed by the solace ClientProfile. (Broker version >= 9.7)

broker# show client smf1 detail 
Client Keepalive:
  Enabled:                    Yes
  Effective Timeout (sec):    10
broker> enable
broker# configure 
broker(configure)# client-profile default message-vpn default 
broker(configure/client-profile)# service 
broker(configure/client-profile/service)# min-keepalive-timeout 10 
broker(configure/client-profile/service)# smf 
broker(configure/client-profile/service/smf)# min-keepalive-enabled 
broker> show client-profile default message-vpn default detail 
  Client Keepalive 
    Enabled for SMF clients             : Yes 
    Minimum Timeout                     : 10    seconds 

JMX integration

This starter provides some JMX operations to remote manage the application leadership.

JMX sample actions

JMS sample operations

Micrometer integration

For monitoring purpose we provide a micrometer metrics.

Here an example if your application uses the micrometer-prometheus-registry.

# HELP leader_status Indicates if this project is [-1=not joined, 0=joined but not leader, 1=is leader] for a group.
# TYPE leader_status gauge
leader_status{group="noJoined",} -1.0
leader_status{group="theOther",} 0.0
leader_status{group="demo",} 1.0 

Resources

For more information try these resources:

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Authors

See the list of contributors who participated in this project.

License

See the LICENSE file for details.

About

leader election for spring boot based on solace PubSub+ exclusive queue

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages