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

io.cdap.cdap.internal.app.runtime.distributed.DistributedProgramRuntimeService Maven / Gradle / Ivy

There is a newer version: 6.10.1
Show newest version
/*
 * Copyright © 2014-2020 Cask Data, 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 io.cdap.cdap.internal.app.runtime.distributed;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Table;
import com.google.inject.Inject;
import io.cdap.cdap.app.runtime.AbstractProgramRuntimeService;
import io.cdap.cdap.app.runtime.ProgramController;
import io.cdap.cdap.app.runtime.ProgramRunnerFactory;
import io.cdap.cdap.app.runtime.ProgramStateWriter;
import io.cdap.cdap.app.store.Store;
import io.cdap.cdap.common.app.RunIds;
import io.cdap.cdap.common.conf.CConfiguration;
import io.cdap.cdap.common.lang.Delegator;
import io.cdap.cdap.common.twill.TwillAppNames;
import io.cdap.cdap.internal.app.deploy.ProgramRunDispatcherFactory;
import io.cdap.cdap.internal.app.runtime.AbstractListener;
import io.cdap.cdap.internal.app.runtime.service.SimpleRuntimeInfo;
import io.cdap.cdap.internal.app.store.RunRecordDetail;
import io.cdap.cdap.proto.Containers;
import io.cdap.cdap.proto.DistributedProgramLiveInfo;
import io.cdap.cdap.proto.ProgramLiveInfo;
import io.cdap.cdap.proto.ProgramRunStatus;
import io.cdap.cdap.proto.ProgramType;
import io.cdap.cdap.proto.id.ProgramId;
import io.cdap.cdap.proto.id.ProgramRunId;
import org.apache.twill.api.ResourceReport;
import org.apache.twill.api.RunId;
import org.apache.twill.api.TwillController;
import org.apache.twill.api.TwillRunResources;
import org.apache.twill.api.TwillRunner;
import org.apache.twill.common.Threads;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;

import static io.cdap.cdap.proto.Containers.ContainerInfo;

/**
 *
 */
public final class DistributedProgramRuntimeService extends AbstractProgramRuntimeService {

  private static final Logger LOG = LoggerFactory.getLogger(DistributedProgramRuntimeService.class);

  private final TwillRunner twillRunner;
  private final Store store;
  private final ProgramStateWriter programStateWriter;

  @Inject
  DistributedProgramRuntimeService(CConfiguration cConf, ProgramRunnerFactory programRunnerFactory,
                                   TwillRunner twillRunner, Store store, ProgramStateWriter programStateWriter,
                                   ProgramRunDispatcherFactory programRunDispatcherFactory) {
    super(cConf, programRunnerFactory, programStateWriter, programRunDispatcherFactory);
    this.twillRunner = twillRunner;
    this.store = store;
    this.programStateWriter = programStateWriter;
  }

  @Override
  protected boolean isDistributed() {
    return true;
  }

  @Override
  protected RuntimeInfo createRuntimeInfo(final ProgramController controller, final ProgramId programId,
                                          final Runnable cleanUpTask) {
    SimpleRuntimeInfo runtimeInfo = new SimpleRuntimeInfo(controller, programId, cleanUpTask);

    // Add a listener that publishes KILLED status notification when the YARN application is killed in case that
    // the KILLED status notification is not published from the YARN application container, so we don't need to wait
    // for the run record corrector to mark the status as KILLED.
    // Also, the local staging files can be deleted when the twill program is alive.
    controller.addListener(new AbstractListener() {

      ProgramController actualController = controller;

      @Override
      public void init(ProgramController.State currentState, @Nullable Throwable cause) {
        while (actualController instanceof Delegator) {
          //noinspection unchecked
          actualController = ((Delegator) actualController).getDelegate();
        }
        if (actualController instanceof AbstractTwillProgramController) {
          runtimeInfo.setTwillRunId(((AbstractTwillProgramController) actualController).getTwillRunId());
        }
        if (currentState == ProgramController.State.ALIVE) {
          alive();
        } else if (currentState == ProgramController.State.KILLED) {
          killed();
        }
      }

      @Override
      public void alive() {
        cleanUpTask.run();
      }

      @Override
      public void killed() {
        programStateWriter.killed(programId.run(controller.getRunId()));
      }
    }, Threads.SAME_THREAD_EXECUTOR);
    return runtimeInfo;
  }

  @Override
  public RuntimeInfo lookup(ProgramId programId, final RunId runId) {
    RuntimeInfo runtimeInfo = super.lookup(programId, runId);
    if (runtimeInfo != null) {
      return runtimeInfo;
    }

    // Lookup the Twill RunId for the given run
    ProgramRunId programRunId = programId.run(runId.getId());
    RunRecordDetail record;
    synchronized (this) {
      record = store.getRun(programRunId);
    }
    if (record == null) {
      return null;
    }
    if (record.getTwillRunId() == null) {
      LOG.warn("Twill RunId does not exist for the program {}, runId {}", programId, runId.getId());
      return null;
    }

    RunId twillRunIdFromRecord = org.apache.twill.internal.RunIds.fromString(record.getTwillRunId());
    return lookupFromTwillRunner(twillRunner, programRunId, twillRunIdFromRecord);
  }

  @Override
  public Map list(ProgramType type) {
    Map result = new HashMap<>(super.list(type));

    // Table holds the Twill RunId and TwillController associated with the program matching the input type
    Table twillProgramInfo = HashBasedTable.create();

    List runtimeInfos = getRuntimeInfos();

    // Goes through all live application and fill the twillProgramInfo table
    for (TwillRunner.LiveInfo liveInfo : twillRunner.lookupLive()) {
      String appName = liveInfo.getApplicationName();
      ProgramId programId = TwillAppNames.fromTwillAppName(appName, false);
      if (programId == null) {
        continue;
      }
      if (!type.equals(programId.getType())) {
        continue;
      }

      for (TwillController controller : liveInfo.getControllers()) {
        RunId twillRunId = controller.getRunId();

        // If it already in the runtime info, no need to lookup
        if (runtimeInfos.stream().anyMatch(info -> twillRunId.equals(info.getTwillRunId()))) {
          continue;
        }

        twillProgramInfo.put(programId, twillRunId, controller);
      }
    }

    if (twillProgramInfo.isEmpty()) {
      return ImmutableMap.copyOf(result);
    }

    final Set twillRunIds = twillProgramInfo.columnKeySet();
    Collection activeRunRecords;
    synchronized (this) {
      activeRunRecords = store.getRuns(ProgramRunStatus.RUNNING, record ->
        record.getTwillRunId() != null
          && twillRunIds.contains(org.apache.twill.internal.RunIds.fromString(record.getTwillRunId()))).values();
    }

    for (RunRecordDetail record : activeRunRecords) {
      String twillRunId = record.getTwillRunId();
      if (twillRunId == null) {
        // This is unexpected. Just log and ignore the run record
        LOG.warn("No twill runId for in run record {}.", record);
        continue;
      }

      RunId twillRunIdFromRecord = org.apache.twill.internal.RunIds.fromString(twillRunId);
      // Get the CDAP RunId from RunRecord
      RunId runId = RunIds.fromString(record.getPid());
      // Get the Program and TwillController for the current twillRunId
      Map mapForTwillId = twillProgramInfo.columnMap().get(twillRunIdFromRecord);
      Map.Entry entry = mapForTwillId.entrySet().iterator().next();

      // Create RuntimeInfo for the current Twill RunId
      if (result.computeIfAbsent(runId, rid -> createRuntimeInfo(entry.getKey(), rid, entry.getValue())) == null) {
        LOG.warn("Unable to create runtime info for program {} with run id {}", entry.getKey(), runId);
      }
    }

    return ImmutableMap.copyOf(result);
  }

  @Override
  public ProgramLiveInfo getLiveInfo(ProgramId program) {
    String twillAppName = TwillAppNames.toTwillAppName(program);
    Iterator controllers = twillRunner.lookup(twillAppName).iterator();
    // this will return an empty Json if there is no live instance
    if (controllers.hasNext()) {
      TwillController controller = controllers.next();
      if (controllers.hasNext()) {
        LOG.warn("Expected at most one live instance of Twill app {} but found at least two.", twillAppName);
      }
      ResourceReport report = controller.getResourceReport();
      if (report != null) {
        DistributedProgramLiveInfo liveInfo = new DistributedProgramLiveInfo(program, report.getApplicationId());

        Containers.ContainerType containerType = Containers.ContainerType.valueOf(program.getType().name());

        for (Map.Entry> entry : report.getResources().entrySet()) {
          for (TwillRunResources resources : entry.getValue()) {
            liveInfo.addContainer(new ContainerInfo(containerType,
                                                    entry.getKey(),
                                                    resources.getInstanceId(),
                                                    resources.getContainerId(),
                                                    resources.getHost(),
                                                    resources.getMemoryMB(),
                                                    resources.getVirtualCores(),
                                                    resources.getDebugPort()));
          }
        }

        // Add a list of announced services and their discoverables to the liveInfo.
        liveInfo.addServices(report.getServices());
        return liveInfo;
      }
    }

    return super.getLiveInfo(program);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy