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

com.caucho.v5.bartender.pod.PodHeartbeatImpl Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Baratine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Baratine; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.v5.bartender.pod;

import io.baratine.event.EventsSync;
import io.baratine.service.Result;
import io.baratine.service.Services;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import com.caucho.v5.amp.AmpSystem;
import com.caucho.v5.amp.Direct;
import com.caucho.v5.baratine.InService;
import com.caucho.v5.bartender.BartenderSystem;
import com.caucho.v5.bartender.ClusterBartender;
import com.caucho.v5.bartender.ServerBartender;
import com.caucho.v5.bartender.ServerOnUpdate;
import com.caucho.v5.bartender.heartbeat.ClusterHeartbeat;
import com.caucho.v5.bartender.heartbeat.HeartbeatService;
import com.caucho.v5.bartender.heartbeat.HeartbeatServiceLocal;
import com.caucho.v5.bartender.heartbeat.PodBuilderConfig;
import com.caucho.v5.bartender.heartbeat.ServerHeartbeat;
import com.caucho.v5.bartender.pod.PodBartender.PodType;
import com.caucho.v5.util.CurrentTime;

@InService(HeartbeatServiceLocal.class)
public class PodHeartbeatImpl
{
  private static final Logger log = Logger.getLogger(PodHeartbeatImpl.class.getName());
  
  private static final String CLUSTER_ROOT = "cluster_root";
  
  private final ConcurrentHashMap _podMap
    = new ConcurrentHashMap<>();
  
  private final ConcurrentHashMap _podProxyMap
    = new ConcurrentHashMap<>();
  
  private ArrayList _serverList = new ArrayList<>();
  
  //private HashMap _podConfigMap = new HashMap<>();
  
  private HashMap _podInitMap = new HashMap<>();
  private HashMap _podUpdateMap = new HashMap<>();
  
  // private final HeartbeatService _heartbeatService;
  
  //private final PodManagerImpl _podManager;
  
  private PodOnUpdate _podEvents;

  private ServerHeartbeat _serverSelf;

  private UpdatePodSystem _updatePodSystem;

  // private long _lastRootSequence;

  private BartenderSystem _bartender;

  private HeartbeatServiceLocal _heartbeatLocal;
//   private int _roundRobin;

  private String _clusterRootName;

  private PodBartenderImpl _localPod;

  private PodBartenderProxy _clusterRoot;

  private ArrayList _selfUpdatePods = new ArrayList<>();

  // private List _seedServers;
  
  public PodHeartbeatImpl(BartenderSystem bartender,
                          ServerHeartbeat serverSelf,
                          HeartbeatService heartbeatService,
                          HeartbeatServiceLocal heartbeatLocal)
  {
    Objects.requireNonNull(bartender);
    Objects.requireNonNull(serverSelf);
    
    _bartender = bartender;

    _serverSelf = serverSelf;
    
    // _podManager = new PodManagerImpl(_bartender, serverSelf, this);
    
    _updatePodSystem = new UpdatePodSystem(_podUpdateMap.values(), _updatePodSystem);

    // _heartbeatService = heartbeatService;
    _heartbeatLocal = heartbeatLocal;
    
    // EventService eventService = AmpSystem.getCurrent().getEventService();
    Services manager = AmpSystem.currentManager();

    _podEvents = manager.service(PodOnUpdate.ADDRESS)
                        .as(PodOnUpdate.class);
  //  initPods(podBuilderList);
    
    _clusterRoot = getPodProxy(CLUSTER_ROOT + "." + serverSelf.getClusterId());
  }
  
  public UpdatePodSystem getUpdate(long seq, long crc)
  {
    return _updatePodSystem;
  }
  
  /*
  public PodManagerImpl getManager()
  {
    return _podManager;
  }
  */

  // @OnInit
  public void start()
  {
    Services manager = AmpSystem.currentManager();
    //EventService events = AmpSystem.getCurrent().getEventService();
    
    EventsSync events = manager.service(EventsSync.class);
    
    events.subscriber(ServerOnUpdate.class, s->onServerUpdate(s));
    
    for (ServerHeartbeat server : _serverSelf.getCluster().getServers()) {
      onServerUpdate(server);
    }
  }

  //
  // service methods
  //  
  
  @Direct
  public UpdatePodSystem getUpdatePodSystem()
  {
    return _updatePodSystem;
  }
  
  
  @Direct
  public PodBartender getPod(String name)
  {
    PodBartenderProxy podProxy = getPodProxy(name);
    // _podProxyMap.get(name);
    
    if (podProxy.getDelegate() == null) {
      PodBartenderImpl pod = _podMap.get(name);
      
      if (pod != null) {
        podProxy.setDelegate(pod);
      }
    }
    
    return podProxy;
  }
  
  private PodBartenderProxy getPodProxy(String name)
  {
    PodBartenderProxy podProxy = _podProxyMap.get(name);
    
    if (podProxy == null) {
      podProxy = new PodBartenderProxy(name);
      
      _podProxyMap.putIfAbsent(name, podProxy);
      
      podProxy = _podProxyMap.get(name);
    }
    
    return podProxy;
    
  }

  public Map podMap()
  {
    HashMap podMap = new HashMap<>();
    
    podMap.putAll(_podProxyMap);

    return podMap;
  }
  
  @Direct
  public PodBartender getActivePod(String name)
  {
    PodBartenderProxy podProxy = _podProxyMap.get(name);
    
    if (podProxy != null && podProxy.getDelegate() != null) {
        // && podProxy.getType() != PodType.off) {
      return podProxy;
    }
    else {
      return null;
    }
  }
  
  @Direct
  public Iterable getPods()
  {
    ArrayList pods = new ArrayList<>();
    
    for (String podName : _podMap.keySet()) {
      pods.add(getPodProxy(podName));
    }
    
    if (_localPod != null) {
      pods.add(getPodProxy(_localPod.getId()));
    }
    
    return pods;
  }
  
  //
  // updates
  //

  public void initPod(UpdatePod updatePod)
  {
    if (updatePod == null) {
      return;
    }
    
    _podInitMap.put(updatePod.getId(), updatePod);

    if ("local".equals(updatePod.getPodName())) {
      initLocal(updatePod);
    }
    else {
      updatePod(updatePod);
    }
  }
  
  public UpdatePod getInitPod(String id)
  {
    return _podInitMap.get(id);
  }
  
  private void initLocal(UpdatePod updatePod)
  {
    ClusterHeartbeat cluster = _serverSelf.getCluster();
    
    PodBartenderImpl pod = new PodBartenderImpl(updatePod, cluster);
    
    _localPod = pod;
    
    PodBartenderProxy podProxy = getPodProxy("local.local");
    
    podProxy.setDelegate(pod);
  }

  public void updatePodSystem(UpdatePodSystem update)
  {
    if (update == null) {
      return;
    }
    
    if (_clusterRoot.getNode(0).isServerOwner(_serverSelf)) {
      return;
    }
    
    for (UpdatePod pod : update.getPods()) {
      updatePod(pod);
    }
    
    sendUpdateHeartbeats();
  }
  
  public void updatePod(UpdatePod update)
  {
    UpdatePod updateFill = updatePodImpl(update);
    
    putPod(updateFill);
  }

  private UpdatePod updatePodImpl(UpdatePod update)
  {
    if (update.getPodName().equals(CLUSTER_ROOT)) {
      throw new IllegalStateException();
    }
    
    if (update.getPodName().equals("local")) {
      throw new IllegalStateException();
    }
    
    UpdatePod oldUpdate = _podUpdateMap.get(update.getId());
    
    if (! _serverSelf.getClusterId().equals(update.getClusterId())) {
      if (oldUpdate == null) {
        return update;
      }
      else if (oldUpdate.compareTo(update) <= 0) {
        return update;
      }
      else {
        return null;
      }
    }
    
    if (oldUpdate != null && update.compareTo(oldUpdate) <= 0) {
      return oldUpdate;
    }
    else {
      return update;
    }
  }

  public void addNewPod(UpdatePod updatePod, Result result)
  {
    addPod(updatePod);
    
    String id = updatePod.getPodName() + '.' + updatePod.getClusterId();
    
    result.ok(getPod(id));
  }

  public void addPod(UpdatePod updatePod)
  {
    putPod(updatePod);
    
    sendUpdateHeartbeats();
  }

  public void updatePods(ArrayList podList)
  {
    _selfUpdatePods = podList;
    
    updateSelfPods();
  }
  
  private void updateSelfPods()
  {
    // only update if cluster_root owner
    if (! _clusterRoot.getNode(0).isServerOwner(_serverSelf)) {
      return;
    }
    
    for (UpdatePod pod : _selfUpdatePods) {
      putPod(pod);
    }
    
    sendUpdateHeartbeats();
  }
  
  public void putPod(UpdatePod update)
  {
    Objects.requireNonNull(update);
    
    UpdatePod oldUpdate = _podUpdateMap.get(update.getId());
    
    long sequence = update.getSequence();
    
    if (oldUpdate == null) {
    }
    else if (oldUpdate.getCrc() != update.getCrc()) {
      sequence = Math.max(oldUpdate.getSequence() + 1, sequence);
    }
    else {
      sequence = Math.max(oldUpdate.getSequence(), sequence);
    }
    
    if (update.getSequence() <= sequence) {
      update = new UpdatePod(update, sequence);
    }

    _podUpdateMap.put(update.getId(), update);
    
    if (oldUpdate == null || oldUpdate.getCrc() != update.getCrc()) {
      _updatePodSystem = new UpdatePodSystem(_podUpdateMap.values(), _updatePodSystem);
    }
    
    updatePodProxy(update);
  }
  
  private void updatePodProxy(UpdatePod update)
  {
    Objects.requireNonNull(update);
    
    ClusterBartender clusterBar = _bartender.findCluster(update.getClusterId());
    ClusterHeartbeat cluster = (ClusterHeartbeat) clusterBar;
    
    PodBartenderImpl pod;
    
    try {
      pod = new PodBartenderImpl(update, cluster);
    } catch (Exception e) {
      e.printStackTrace();
      throw e;
    }
    
    String name = pod.name() + '.' + pod.getClusterId();

    PodBartenderImpl oldPod = _podMap.get(name);
    
    if (oldPod != null && pod.getSequence() < oldPod.getSequence()) {
      return;
    }
    
    _podMap.put(name, pod);
    
    PodBartenderProxy podProxy = getPodProxy(name);

    podProxy.setDelegate(pod);

    if (oldPod == null || oldPod.getSequence() != pod.getSequence()) {
      _podEvents.onUpdate(podProxy);
    }
  }
  
  /**
   * Compare and merge the cluster_root with an update.
   * 
   * cluster_root is a special case because the root cluster decides on
   * the owning server for the entire cluster.
   */
  private void updateClusterRoot()
  {
    ArrayList serverList = new ArrayList<>();
    
    for (ServerHeartbeat server : _serverSelf.getCluster().getServers()) {
      serverList.add(server);
    }
    
    Collections.sort(serverList, (x,y)->compareClusterRoot(x,y));
    
    UpdatePodBuilder builder = new UpdatePodBuilder();
    builder.name("cluster_root");
    builder.cluster(_serverSelf.getCluster());
    builder.type(PodType.solo);
    builder.depth(16);
    
    for (ServerHeartbeat server : serverList) {
      builder.server(server.getAddress(), server.port());
    }
    
    long sequence = CurrentTime.currentTime();
    
    sequence = Math.max(sequence, _clusterRoot.getSequence() + 1);
    
    builder.sequence(sequence);

    UpdatePod update = builder.build();
    
    updatePodProxy(update);
  }
  
  private int compareClusterRoot(ServerHeartbeat serverA, 
                                 ServerHeartbeat serverB)
  {
    int seedIndexA = serverA.getSeedIndex();
    
    if (seedIndexA <= 0) {
      seedIndexA = Integer.MAX_VALUE / 4;
    }
    
    int seedIndexB = serverB.getSeedIndex();
    
    if (seedIndexB <= 0) {
      seedIndexB = Integer.MAX_VALUE / 4;
    }
    
    int cmp = seedIndexA - seedIndexB;
    
    if (cmp != 0) {
      return cmp;
    }
    
    return serverA.getId().compareTo(serverB.getId());
  }
  
  /**
   * Send a heartbeat with the updated pods to other servers in the cluster.
   * 
   * This call wakes the heartbeat service to send the actual heartbeat.
   */
  private void sendUpdateHeartbeats()
  {
    HeartbeatServiceLocal heartbeat = _bartender.getHeartbeatLocal();
   
    if (heartbeat != null) {
      heartbeat.updateHeartbeats();
    }
  }
  
  private void onServerUpdate(ServerBartender serverBar)
  {
    ServerHeartbeat server = (ServerHeartbeat) serverBar;

    boolean isSelfCluster = (server.getCluster() == _serverSelf.getCluster());
    
    if (isSelfCluster && ! _serverList.contains(server)) {
      _serverList.add(server);
      
      updateClusterRoot();
    }
    
    ArrayList updateList = new ArrayList<>(_podUpdateMap.values());
    
    for (UpdatePod update : updateList) {
      putPod(update);
    }
  }

  /**
   * Update the sequence for all init pods after the join has completed.
   */
  void onJoinStart()
  {
    //long now = CurrentTime.getCurrentTime();
    ArrayList updatePods = new ArrayList<>();
    
    updateClusterRoot();

    /*
    for (UpdatePod updatePod : _podUpdateMap.values()) {
      if (updatePod.getSequence() == 0) {
        updatePods.add(new UpdatePod(updatePod, now));
      }
    }
    */
    
    /*
    for (UpdatePod updatePod : updatePods) {
      updatePod(updatePod);
    }
    */
    
    updatePods.addAll(_podUpdateMap.values());
    
    for (UpdatePod updatePod : updatePods) {
      updatePod(updatePod);
    }
    
    // XXX:
  }

  /*
  public void initPodConfig(PodConfig podConfig)
  {
    _podConfigMap.put(podConfig.getName(), podConfig);
  }
  
  public Iterable getPodConfigs()
  {
    return _podConfigMap.values();
  }
  
  public PodConfig getPodConfig(String podName)
  {
    return _podConfigMap.get(podName);
  }
  */
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy