diff --git a/checkstyle/suppressions.xml b/checkstyle/suppressions.xml
index 10af4e66c28dd..93aaadec0eebb 100644
--- a/checkstyle/suppressions.xml
+++ b/checkstyle/suppressions.xml
@@ -130,7 +130,7 @@
+ files="(DistributedHerder|KafkaConfigBackingStore|Values|IncrementalCooperativeAssignor).java"/>
connectorTargetStateChanges = new HashSet<>();
private boolean needsReconfigRebalance;
+ private volatile boolean fencedFromConfigTopic;
private volatile int generation;
private volatile long scheduledRebalance;
private volatile SecretKey sessionKey;
@@ -288,6 +291,7 @@ public DistributedHerder(DistributedConfig config,
configState = ClusterConfigState.EMPTY;
rebalanceResolved = true; // If we still need to follow up after a rebalance occurred, starting up tasks
needsReconfigRebalance = false;
+ fencedFromConfigTopic = false;
canReadConfigs = true; // We didn't try yet, but Configs are readable until proven otherwise
scheduledRebalance = Long.MAX_VALUE;
keyExpiration = Long.MAX_VALUE;
@@ -372,18 +376,36 @@ public void tick() {
return;
}
+ if (fencedFromConfigTopic) {
+ if (isLeader()) {
+ // We were accidentally fenced out, possibly by a zombie leader
+ try {
+ log.debug("Reclaiming write privileges for config topic after being fenced out");
+ configBackingStore.claimWritePrivileges();
+ fencedFromConfigTopic = false;
+ log.debug("Successfully reclaimed write privileges for config topic after being fenced out");
+ } catch (Exception e) {
+ log.warn("Unable to claim write privileges for config topic. Will backoff and possibly retry if still the leader", e);
+ backoff(CONFIG_TOPIC_WRITE_PRIVILEGES_BACKOFF_MS);
+ return;
+ }
+ } else {
+ log.trace("Relinquished write privileges for config topic after being fenced out, since worker is no longer the leader of the cluster");
+ // We were meant to be fenced out because we fell out of the group and a new leader was elected
+ fencedFromConfigTopic = false;
+ }
+ }
+
long now = time.milliseconds();
if (checkForKeyRotation(now)) {
log.debug("Distributing new session key");
keyExpiration = Long.MAX_VALUE;
try {
- configBackingStore.putSessionKey(new SessionKey(
- keyGenerator.generateKey(),
- now
- ));
+ SessionKey newSessionKey = new SessionKey(keyGenerator.generateKey(), now);
+ writeToConfigTopicAsLeader(() -> configBackingStore.putSessionKey(newSessionKey));
} catch (Exception e) {
- log.info("Failed to write new session key to config topic; forcing a read to the end of the config topic before possibly retrying");
+ log.info("Failed to write new session key to config topic; forcing a read to the end of the config topic before possibly retrying", e);
canReadConfigs = false;
return;
}
@@ -492,6 +514,12 @@ private boolean checkForKeyRotation(long now) {
SecretKey key;
long expiration;
synchronized (this) {
+ // This happens on startup; the snapshot contains the session key,
+ // but no callback in the config update listener has been fired for it yet.
+ if (sessionKey == null && configState.sessionKey() != null) {
+ sessionKey = configState.sessionKey().key();
+ keyExpiration = configState.sessionKey().creationTimestamp() + keyRotationIntervalMs;
+ }
key = sessionKey;
expiration = keyExpiration;
}
@@ -511,10 +539,6 @@ private boolean checkForKeyRotation(long now) {
+ "than required by current worker configuration. Distributing new key now.");
return true;
}
- } else if (key == null && configState.sessionKey() != null) {
- // This happens on startup for follower workers; the snapshot contains the session key,
- // but no callback in the config update listener has been fired for it yet.
- sessionKey = configState.sessionKey().key();
}
}
return false;
@@ -836,7 +860,7 @@ public void deleteConnectorConfig(final String connName, final Callback configBackingStore.removeConnectorConfig(connName));
callback.onCompletion(null, new Created<>(false, null));
}
return null;
@@ -1008,7 +1032,7 @@ public void putConnectorConfig(final String connName, final Map
}
log.trace("Submitting connector config {} {} {}", connName, allowReplace, configState.connectors());
- configBackingStore.putConnectorConfig(connName, config);
+ writeToConfigTopicAsLeader(() -> configBackingStore.putConnectorConfig(connName, config));
// Note that we use the updated connector config despite the fact that we don't have an updated
// snapshot yet. The existing task info should still be accurate.
@@ -1107,7 +1131,7 @@ public void putTaskConfigs(final String connName, final List