package com.zillabyte.motherbrain.flow; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import org.eclipse.jdt.annotation.NonNullByDefault; import com.google.common.collect.Lists; import com.google.monitoring.runtime.instrumentation.common.com.google.common.collect.Maps; import com.zillabyte.motherbrain.flow.heartbeats.Heartbeat; import com.zillabyte.motherbrain.flow.operations.Operation; import com.zillabyte.motherbrain.universe.Config; import com.zillabyte.motherbrain.utils.Utils; @NonNullByDefault @SuppressWarnings("all") public class FlowInstanceSetBuilder { private static Logger _log = Utils.getLogger(FlowInstanceSetBuilder.class); /***** * * */ public static final class Mock extends FlowInstanceSetBuilder { public Mock() { super(); } /** * @param instanceId */ public Mock addInstance(String type, String operationId, String instanceId, String state, Long lastHeartbeat) { // final HashMap<String, String> stats = new HashMap<>(); // final Map<String, Object> info = new ConcurrentHashMap<>(); final Map<String, Object> inst = new ConcurrentHashMap<>(); inst.put("name", operationId); inst.put("type", type); inst.put("instance_name", instanceId); // final OperationInstanceState<FlowInstance,ConcurrentHashMap<String,Object>> instState = // OperationInstanceState.makeOperationInstanceState(state, stats, info); // instState.updated(lastHeartbeat); // _instances.add(instState); inst.put("state", state); inst.put("last_heartbeat", lastHeartbeat); inst.put("last_update_time", System.currentTimeMillis()); _instances.add(inst); return this; } /*** * */ public Mock addInstance(String type, String operationId, String instanceId, String state) { return addInstance(type, operationId, instanceId, state, System.currentTimeMillis()); } } public static final FlowInstanceSetBuilder EMPTY_SET = new FlowInstanceSetBuilder(Collections.EMPTY_LIST); private Long KILL_INTERVAL_MS = Config.getOrDefault("heartbeat.kill.interval", Heartbeat.DEFAULT_KILL_INTERVAL_MS)+1000; private Long ACTIVITY_KILL_INTERVAL_MS = Config.getOrDefault("operation.activity.timeout", Operation.ACTIVE_OPERATION_TIMEOUT_DEFAULT)+1000; final protected Collection<Map<String, Object>> _instances; public FlowInstanceSetBuilder(final Collection<Map<String,Object>> instances) { _instances = instances; } public FlowInstanceSetBuilder() { _instances = new ArrayList<>(); } /*** * * @param instances */ public static FlowInstanceSetBuilder buildFromOperationInstanceStates (FlowOperationInstanceCollection instances) { final Collection<Map<String, Object>> hashInstances = new ArrayList<>(); for(FlowOperationInstance h : instances) { final Map<String, Object> info = h.getInfo(); final Map<String,String> stats = h.getStats(); final Map<String, Object> inst = Maps.newHashMap(); for(String key : info.keySet()) { inst.put(key, info.get(key)); } inst.put("state", h.getState()); inst.put("last_update_time", h.lastUpdateTime()); if(stats.containsKey("last_heartbeat")) inst.put("last_heartbeat", stats.get("last_heartbeat")); hashInstances.add(inst); } return new FlowInstanceSetBuilder(hashInstances); } public Collection<Map<String, Object>> instances() { return _instances; } /*** * * @param states */ public FlowInstanceSetBuilder inState(String... states) { // Init final Collection<Map<String, Object>> newList = new LinkedList<>(); for(Map<String, Object> h : _instances) { // Init String opState = (String) h.get("state"); boolean found = false; for(String target : states) { if (opState.equals(target)) { found = true; break; } } if (found) { // This op is the target state, so add it to the list newList.add(h); } } return new FlowInstanceSetBuilder(newList); } /*** * * @param states */ public FlowInstanceSetBuilder notInState(String... states) { // Init final Collection<Map<String, Object>> newList = new LinkedList<>(); for(Map<String, Object> h : _instances) { // Init String opState = (String) h.get("state"); boolean found = false; for(String target : states) { if (opState.equals(target)) { found = true; break; } } if (found == false) { // This op is not the target state, so add it to the list newList.add(h); } } return new FlowInstanceSetBuilder(newList); } /**** * * @param targetTypes */ public FlowInstanceSetBuilder ofType(String... targetTypes) { final Collection<Map<String, Object>> newList = new LinkedList<>(); for(Map<String, Object> h : _instances) { synchronized (h) { final Object opType = h.get("type"); if (opType == null) { continue; } boolean found = false; for(final String target : targetTypes) { if (opType.equals(target)) { found = true; break; } } if (found) { /* * This op type was not found, so add it to the list. */ newList.add(h); } } } return new FlowInstanceSetBuilder(newList); } /*** * * @param targetTypes */ public FlowInstanceSetBuilder ofOperation(Operation... targetTypes) { return new FlowInstanceSetBuilder((getOperationInstances(targetTypes))); } public FlowInstanceSetBuilder ofOperation(String... targetOperations) { return new FlowInstanceSetBuilder((getOperationInstances(targetOperations))); } private List<Map<String, Object>> getOperationInstances(String... operations) { final LinkedList<Map<String, Object>> ret = new LinkedList<>(); for(Map<String, Object> h : _instances) { synchronized (h) { final Object name = h.get("name"); if (name == null) { continue; } for(String o : operations) { if (o.equals(name)) { ret.add(h); } } } } return ret; } /*** * * @param operations */ private List<Map<String, Object>> getOperationInstances(Operation... operations) { final LinkedList<Map<String, Object>> ret = new LinkedList<>(); for(Map<String, Object> h : _instances) { synchronized (h) { final Object name = h.get("name"); if (name == null) { continue; } for(Operation o : operations) { if (o.namespaceName().equals(name)) { ret.add(h); } } } } return ret; } /*** * * @param targetTypes */ public FlowInstanceSetBuilder notOfType(String... targetTypes) { // Init final Collection<Map<String, Object>> newList = new LinkedList<>(); for(Map<String, Object> h : _instances) { synchronized (h) { final Object opType = h.get("type"); if (opType == null) { continue; } boolean found = false; for(final String target : targetTypes) { if (opType.equals(target)) { found = true; break; } } if (found == false) { /* * This op type was not found, so add it to the list. */ newList.add(h); } } } return new FlowInstanceSetBuilder(newList); } public boolean isEmpty() { return this._instances.isEmpty(); } public boolean isNotEmpty() { return !isEmpty(); } /*** * * @param states */ public boolean anyInState(String... states) { FlowInstanceSetBuilder r = this.inState(states); return r._instances.size() > 0; } /**** * * @param states */ public boolean allInState(String... states) { int preSize = this._instances.size(); FlowInstanceSetBuilder r = this.inState(states); return r._instances.size() == preSize && preSize > 0; } public FlowInstanceSetBuilder sources() { return this.ofType("source"); } public FlowInstanceSetBuilder nonSources() { return this.notOfType("source"); } /*** * * @param ops */ public FlowInstanceSetBuilder ofOperation(final List<Operation> ops) { final Operation[] arrayOps = ops.toArray(new Operation[] {}); /* * True by contract. */ assert (arrayOps != null); return this.ofOperation(arrayOps); } /**** * */ public void debugStates() { System.err.println("----"); for(Map<String, Object> h : _instances) { final String opState = (String) h.get("state"); synchronized (h) { final Object name = h.get("name"); System.err.println(name == null? "null" : name + ": " + opState); } } } public Set<String> operationIds() { final Set<String> ids = new HashSet<>(); for(Map<String, Object> h : _instances) { synchronized (h) { final Object name = h.get("name"); // _log.info(_uuid + " in operationIds, the set info is: "+info); if (name != null) { ids.add(name.toString()); } } } return ids; } public int size() { return this._instances.size(); } /**** * * @return */ public FlowInstanceSetBuilder withAliveHeartbeats() { final Collection<Map<String, Object>> newList = Lists.newLinkedList(); for(Map<String, Object> instanceState : _instances) { final String state = (String) instanceState.get("state"); synchronized (instanceState) { final Object opId = instanceState.get("instance_name"); // _log.info(_uuid+" Set info: "+info); final Long lastHeartbeat = (Long) instanceState.get("last_heartbeat"); if (opId == null) continue; if(lastHeartbeat == null) { // If we don't have heartbeat info yet, sanity check last state update time if(System.currentTimeMillis() - ((Long) instanceState.get("last_update_time")) < ACTIVITY_KILL_INTERVAL_MS) { newList.add(instanceState); } else { _log.debug(instanceState.get("name") + " [" + opId + "] has not had activity for more than "+ACTIVITY_KILL_INTERVAL_MS); } } else { // Use heartbeat info to determine if instance is dead if ( System.currentTimeMillis() - lastHeartbeat < KILL_INTERVAL_MS) { newList.add(instanceState); } else { _log.debug(instanceState.get("name") + " [" + opId + "]'s last heartbeat was at " + lastHeartbeat+". That was more than " + KILL_INTERVAL_MS +" ago."); } } } } return new FlowInstanceSetBuilder(newList); } /*** * * @return */ public FlowInstanceSetBuilder withDeadHeartbeats() { final Collection<Map<String, Object>> newList = Lists.newLinkedList(); for(Map<String, Object> instanceState : _instances) { final String state = (String) instanceState.get("state"); synchronized (instanceState) { final Object opId = instanceState.get("instance_name"); final String lastHeartbeat = (String) instanceState.get("last_heartbeat"); if (opId == null) continue; if(lastHeartbeat == null) { // if we don't have heartbeat info yet, sanity check last state update time if (System.currentTimeMillis() - Long.valueOf((String) instanceState.get("last_update_time")) >= ACTIVITY_KILL_INTERVAL_MS) newList.add(instanceState); } else { // Use heartbeat info to determine if instance is dead final Long longHeartbeat = Long.valueOf(lastHeartbeat); if (System.currentTimeMillis() - longHeartbeat >= KILL_INTERVAL_MS) newList.add(instanceState); } } } return new FlowInstanceSetBuilder(newList); } /*** * If all the instances of a given operation are dead, then return empty set. * @return */ public FlowInstanceSetBuilder assertAtLeastOneInstanceAliveFromEachOperation() { // _log.info(_uuid+" All operations: "+this.operationIds()); // _log.info(_uuid+" Live ops: "+this.withAliveHeartbeats().operationIds()); if (this.withAliveHeartbeats().operationIds().equals( this.operationIds() )) { return this; } else { return EMPTY_SET; } } public boolean allOperationsAlive() { if(this.assertAtLeastOneInstanceAliveFromEachOperation() == this) { return true; } return false; } public Set<String> getNotAliveOperationNames() { Set<String> allNotAliveOps = this.operationIds(); for(String op : this.withAliveHeartbeats().operationIds()) { allNotAliveOps.remove(op); } return allNotAliveOps; //any remaining ops are not online yet.. } }