All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.iep.leader.LeaderService Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2024 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.netflix.iep.leader;

import com.netflix.iep.leader.api.LeaderElector;
import com.netflix.iep.leader.api.ResourceId;
import com.netflix.iep.service.AbstractService;
import com.netflix.spectator.api.Functions;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Registry;
import com.netflix.spectator.api.Timer;
import com.netflix.spectator.api.patterns.PolledMeter;
import com.netflix.spectator.impl.Scheduler;
import com.typesafe.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;

/**
 * {@code LeaderService} manages periodic leader election using the provided {@link LeaderElector}.
 */
public class LeaderService extends AbstractService {

  private static final Logger logger = LoggerFactory.getLogger(LeaderService.class);

  private final LeaderElector leaderElector;
  private final Scheduler.Options leaderElectorSchedulerOptions;
  private final Scheduler leaderElectorScheduler;
  // visible for testing
  final Runnable leaderServiceTask = createLeaderServiceTask();

  private final Registry registry;
  private final Id leaderElectionsCounterId;
  private final Timer electorInitializeTimer;
  private final AtomicLong timeSinceLastElection;

  private volatile boolean running = false;

  public LeaderService(LeaderElector leaderElector, Config config, Registry registry) {
    this(
        leaderElector,
        registry,
        new Scheduler(registry, "iep-leader-elector", 1),
        configureSchedulerOptions(config),
        registry.createId("leader.elections"),
        registry.timer("leader.electorInitializeDuration"),
        new AtomicLong()
    );

  }

  public LeaderService(
      LeaderElector leaderElector,
      Registry registry,
      Scheduler leaderElectorScheduler,
      Scheduler.Options leaderElectorSchedulerOptions,
      Id leaderElectionsCounterId,
      Timer electorInitializeTimer,
      AtomicLong timeSinceLastElection) {
    Objects.requireNonNull(leaderElector, "leaderElector");
    Objects.requireNonNull(registry, "registry");
    Objects.requireNonNull(leaderElectorScheduler, "leaderElectorScheduler");
    Objects.requireNonNull(leaderElectorSchedulerOptions, "leaderElectorSchedulerOptions");
    Objects.requireNonNull(leaderElectionsCounterId, "leaderElectionsCounterId");
    Objects.requireNonNull(electorInitializeTimer, "electorInitializeTimer");
    Objects.requireNonNull(timeSinceLastElection, "timeSinceLastElection");

    this.leaderElector = leaderElector;
    this.registry = registry;
    this.leaderElectorScheduler = leaderElectorScheduler;
    this.leaderElectorSchedulerOptions = leaderElectorSchedulerOptions;
    this.leaderElectionsCounterId = leaderElectionsCounterId;
    this.electorInitializeTimer = electorInitializeTimer;
    this.timeSinceLastElection = timeSinceLastElection;
  }

  static Scheduler.Options configureSchedulerOptions(Config config) {
    return new Scheduler.Options().withFrequency(
        Scheduler.Policy.FIXED_DELAY,
        config.getDuration("iep.leader.leaderElectorFrequency")
    );
  }

  @Override
  protected void startImpl() {
    logger.info("Starting leader service");

    electorInitializeTimer.record(
        leaderElector::initialize
    );

    running = true;
    startMonitoringTimeSinceLastElection();
    leaderElectorScheduler.schedule(leaderElectorSchedulerOptions, leaderServiceTask);

    logger.info("Leader service started");
  }

  private void startMonitoringTimeSinceLastElection() {
    timeSinceLastElection.set(registry.clock().wallTime());
    PolledMeter.using(registry)
        .withName("leader.timeSinceLastElection")
        .monitorValue(timeSinceLastElection, Functions.AGE);
  }

  private Runnable createLeaderServiceTask() {
    return () -> {
      try {
        if (running) {
          logger.info("Running an election.");
          leaderElector.runElection();
          timeSinceLastElection.set(registry.clock().wallTime());
          registry.counter(leaderElectionsCounterId.withTag("result", "success")).increment();
        } else {
          logger.info("Skipping election run.");
        }
      } catch (Throwable t) {
        final String throwableName = t.getCause() != null ?
            t.getCause().getClass().getSimpleName() : t.getClass().getSimpleName();

        final Id counterIdWithTags =
            leaderElectionsCounterId.withTag("result", "failure").withTag("error", throwableName);
        registry.counter(counterIdWithTags).increment();
        logger.warn("Leader election attempt failed", t);
      }
    };
  }

  @Override
  protected void stopImpl() {
    running = false;

    logger.info("Stopping leader service");

    leaderElectorScheduler.shutdown();
    removeLeadership();

    logger.info("Leader service stopped");
  }

  private void removeLeadership() {
    for (ResourceId resourceId : leaderElector.getResources()) {
      if (!leaderElector.removeLeaderFor(resourceId)) {
        logger.warn(
            "While removing leadership for all resources, could not remove leadership for: {}",
            resourceId
        );
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy