INT-4220: Leaders: warn event publishing errors

JIRA: https://jira.spring.io/browse/INT-4220

Currently when an error is thrown from the event publishing the role granting is broken and we just go to the role revoking.

* Since it's just an event publishing it shouldn't effect the original leader election.
* `try...catch` event publishing in the `LeaderInitiator` and `logger.warn` an `Exception`
* Make `leader/Context` as `@FunctionalInterface` for simple Lambda use-case like `NULL_CONTEXT` - `() -> false`
* Remove all the internal `NullContext` implementations in favor of above mention Lambda
* In the `LockRegistryLeaderInitiator` use `CustomizableThreadFactory` instead of custom `ThreadFactory` for prefixing
* Add `zookeeper/leader/LeaderInitiator#getContext()` for external usage and consistency with other similar components
* Fix `CuratorContext.toString()` typo
This commit is contained in:
Artem Bilan
2017-01-31 13:50:42 -05:00
committed by Gary Russell
parent a4bfd2cc42
commit 9616cc72a1
6 changed files with 140 additions and 70 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,8 @@ import org.springframework.util.StringUtils;
* @author Patrick Peralta
* @author Janne Valkealahti
* @author Gary Russell
* @author Artem Bilan
*
* @since 4.2
*/
public class LeaderInitiator implements SmartLifecycle {
@@ -45,6 +47,10 @@ public class LeaderInitiator implements SmartLifecycle {
private static final String DEFAULT_NAMESPACE = "/spring-integration/leader/";
private static final Context NULL_CONTEXT = () -> false;
private final CuratorContext context = new CuratorContext();
/**
* Curator client.
*/
@@ -196,6 +202,18 @@ public class LeaderInitiator implements SmartLifecycle {
this.leaderEventPublisher = leaderEventPublisher;
}
/**
* The context of the initiator or null if not running.
* @return the context (or null if not running)
* @since 5.0
*/
public Context getContext() {
if (this.leaderSelector == null) {
return NULL_CONTEXT;
}
return this.context;
}
/**
* @return the ZooKeeper path used for leadership election by Curator
*/
@@ -218,13 +236,16 @@ public class LeaderInitiator implements SmartLifecycle {
@Override
public void takeLeadership(CuratorFramework framework) throws Exception {
CuratorContext context = new CuratorContext();
try {
LeaderInitiator.this.candidate.onGranted(context);
LeaderInitiator.this.candidate.onGranted(LeaderInitiator.this.context);
if (LeaderInitiator.this.leaderEventPublisher != null) {
LeaderInitiator.this.leaderEventPublisher.publishOnGranted(LeaderInitiator.this, context,
LeaderInitiator.this.candidate.getRole());
try {
LeaderInitiator.this.leaderEventPublisher.publishOnGranted(LeaderInitiator.this,
LeaderInitiator.this.context, LeaderInitiator.this.candidate.getRole());
}
catch (Exception e) {
logger.warn("Error publishing OnGranted event.", e);
}
}
// when this method exits, the leadership will be revoked;
@@ -238,10 +259,15 @@ public class LeaderInitiator implements SmartLifecycle {
// reset the interrupt flag as the interrupt is handled.
}
finally {
LeaderInitiator.this.candidate.onRevoked(context);
LeaderInitiator.this.candidate.onRevoked(LeaderInitiator.this.context);
if (LeaderInitiator.this.leaderEventPublisher != null) {
LeaderInitiator.this.leaderEventPublisher.publishOnRevoked(LeaderInitiator.this, context,
LeaderInitiator.this.candidate.getRole());
try {
LeaderInitiator.this.leaderEventPublisher.publishOnRevoked(LeaderInitiator.this,
LeaderInitiator.this.context, LeaderInitiator.this.candidate.getRole());
}
catch (Exception e) {
logger.warn("Error publishing OnRevoked event.", e);
}
}
}
}
@@ -268,7 +294,7 @@ public class LeaderInitiator implements SmartLifecycle {
@Override
public String toString() {
return "LockContext{role=" + LeaderInitiator.this.candidate.getRole() +
return "CuratorContext{role=" + LeaderInitiator.this.candidate.getRole() +
", id=" + LeaderInitiator.this.candidate.getId() +
", isLeader=" + isLeader() + "}";
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 the original author or authors.
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.leader.Context;
import org.springframework.integration.leader.DefaultCandidate;
import org.springframework.integration.leader.event.AbstractLeaderEvent;
import org.springframework.integration.leader.event.DefaultLeaderEventPublisher;
import org.springframework.integration.leader.event.OnGrantedEvent;
import org.springframework.integration.leader.event.OnRevokedEvent;
import org.springframework.integration.zookeeper.ZookeeperTestSupport;
@@ -84,6 +87,34 @@ public class LeaderInitiatorFactoryBeanTests extends ZookeeperTestSupport {
assertThat(this.config.events.get(1), instanceOf(OnRevokedEvent.class));
}
@Test
public void testExceptionFromEvent() throws Exception {
CountDownLatch onGranted = new CountDownLatch(1);
LeaderInitiator initiator = new LeaderInitiator(client, new DefaultCandidate());
initiator.setLeaderEventPublisher(new DefaultLeaderEventPublisher() {
@Override
public void publishOnGranted(Object source, Context context, String role) {
try {
throw new RuntimeException("intentional");
}
finally {
onGranted.countDown();
}
}
});
initiator.start();
assertTrue(onGranted.await(10, TimeUnit.SECONDS));
assertTrue(initiator.getContext().isLeader());
initiator.stop();
}
@Configuration
public static class Config {