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

org.bboxdb.distribution.membership.ZookeeperBBoxDBInstanceAdapter Maven / Gradle / Ivy

There is a newer version: 1.0.0-rc3
Show newest version
/*******************************************************************************
 *
 *    Copyright (C) 2015-2018 the BBoxDB project
 *  
 *    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 org.bboxdb.distribution.membership;

import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.bboxdb.commons.Retryer;
import org.bboxdb.commons.ServiceState;
import org.bboxdb.commons.SystemInfo;
import org.bboxdb.distribution.zookeeper.ZookeeperClient;
import org.bboxdb.distribution.zookeeper.ZookeeperException;
import org.bboxdb.distribution.zookeeper.ZookeeperNotFoundException;
import org.bboxdb.misc.BBoxDBConfiguration;
import org.bboxdb.misc.BBoxDBConfigurationManager;
import org.bboxdb.misc.Const;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.primitives.Longs;

public class ZookeeperBBoxDBInstanceAdapter implements Watcher {

	/**
	 * The zookeeper client
	 */
	private final ZookeeperClient zookeeperClient;
	
	/**
	 * The path helper
	 */
	private final ZookeeperInstancePathHelper pathHelper;

	/**
	 * The logger
	 */
	private final static Logger logger = LoggerFactory.getLogger(ZookeeperBBoxDBInstanceAdapter.class);

	public ZookeeperBBoxDBInstanceAdapter(final ZookeeperClient zookeeperClient) {
		this.zookeeperClient = Objects.requireNonNull(zookeeperClient);
		this.pathHelper = new ZookeeperInstancePathHelper(zookeeperClient);
	}

	/**
	 * Register a watch on membership changes. A watch is a one-time operation,
	 * the watch is reregistered on each method call.
	 */
	public boolean readMembershipAndRegisterWatch() {

		try {
			final BBoxDBInstanceManager distributedInstanceManager 
				= BBoxDBInstanceManager.getInstance();

			// Reregister watch on membership
			final String activeInstancesPath = zookeeperClient.getActiveInstancesPath();
			zookeeperClient.getChildren(activeInstancesPath, this);
			
			// Read version data
			final String detailsPath = zookeeperClient.getDetailsPath();
			final List instances = zookeeperClient.getChildren(detailsPath, this);
			
			final Set instanceSet = new HashSet<>();
			
			for (final String instanceName : instances) {
				final BBoxDBInstance distributedInstance = readInstance(instanceName);
				instanceSet.add(distributedInstance);
			}

			distributedInstanceManager.updateInstanceList(instanceSet);
		} catch (ZookeeperNotFoundException | ZookeeperException e) {
			logger.warn("Unable to read membership and create a watch", e);
			return false;
		} catch(InterruptedException e) {
			Thread.currentThread().interrupt();
			return false;
		}
 
		return true;
	}

	/**
	 * Read the given instance
	 * @param instanceName
	 * @return
	 * @throws ZookeeperNotFoundException
	 * @throws ZookeeperException 
	 */
	private BBoxDBInstance readInstance(final String instanceName) 
			throws ZookeeperException, InterruptedException {
		
		final BBoxDBInstance instance = new BBoxDBInstance(instanceName);
		
		// After the instance is registered, it can take some time until the
		// complete ressource data is available in zookeeper			
		final Retryer retryer = new Retryer<>(5, 200, TimeUnit.MILLISECONDS, () -> {
			
			// Version
			final String instanceVersion = getVersionForInstance(instance);
			instance.setVersion(instanceVersion);
			
			// State
			final BBoxDBInstanceState state = getStateForInstance(instanceName);
			instance.setState(state);
			
			// CPU cores
			final int cpuCores = getCpuCoresForInstnace(instance);
			instance.setCpuCores(cpuCores);
			
			// Memory
			final long memory = getMemoryForInstance(instance);
			instance.setMemory(memory);
			
			// Diskspace
			readDiskSpaceForInstance(instance);
			
			return true;
		});
	
		final boolean result = retryer.execute();
		
		if(! result) {
			throw new ZookeeperException("Unable to read instance " + instanceName, 
					retryer.getLastException());
		}
		
		return instance;
	}

	/**
	 * Read the state for the given instance
	 * 
	 * @param member
	 * @return
	 * @throws ZookeeperNotFoundException
	 */
	private BBoxDBInstanceState getStateForInstance(final String member) {
		final String nodesPath = zookeeperClient.getActiveInstancesPath();
		final String statePath = nodesPath + "/" + member;

		try {
			final String state = zookeeperClient.readPathAndReturnString(statePath, this);
			if (BBoxDBInstanceState.OUTDATED.getZookeeperValue().equals(state)) {
				return BBoxDBInstanceState.OUTDATED;
			} else if (BBoxDBInstanceState.READY.getZookeeperValue().equals(state)) {
				return BBoxDBInstanceState.READY;
			}
		} catch (ZookeeperException | ZookeeperNotFoundException e) {
			// Ignore exception, instance state is unknown
		}

		return BBoxDBInstanceState.FAILED;
	}

	/**
	 * Read the version for the given instance
	 * 
	 * @param instance
	 * @return
	 * @throws ZookeeperNotFoundException
	 */
	private String getVersionForInstance(final BBoxDBInstance instance) throws ZookeeperNotFoundException {
		final String versionPath = pathHelper.getInstancesVersionPath(instance);

		try {
			return zookeeperClient.readPathAndReturnString(versionPath, null);
		} catch (ZookeeperException e) {
			logger.error("Unable to read version for: {}", versionPath);
		}

		return BBoxDBInstance.UNKOWN_PROPERTY;
	}
	
	/**
	 * Get the amount of CPU cores fot the instance
	 * @param instance
	 * @return
	 * @throws ZookeeperNotFoundException
	 */
	private int getCpuCoresForInstnace(final BBoxDBInstance instance) throws ZookeeperNotFoundException {
		final String versionPath = pathHelper.getInstancesCpuCorePath(instance);

		String versionString = null;
		
		try {
			versionString = zookeeperClient.readPathAndReturnString(versionPath);
			return Integer.parseInt(versionString);
		} catch (ZookeeperException e) {
			logger.error("Unable to read cpu cores for: {}", versionPath);
		} catch(NumberFormatException e) {
			logger.error("Unable to parse {} as CPU cores", versionString);
		}

		return -1;
	}
	
	/**
	 * Get the total memory for the given instance
	 * @param instance
	 * @return
	 * @throws ZookeeperNotFoundException
	 */
	private long getMemoryForInstance(final BBoxDBInstance instance) throws ZookeeperNotFoundException {
		final String memoryPath = pathHelper.getInstancesMemoryPath(instance);

		String memoryString = null;

		try {
			memoryString = zookeeperClient.readPathAndReturnString(memoryPath);
			return Long.parseLong(memoryString);
		} catch (ZookeeperException e) {
			logger.error("Unable to read memory for: {}", memoryPath);
		} catch(NumberFormatException e) {
			logger.error("Unable to parse {} as memory", memoryString);
		}

		return -1;
	}
	
	/**
	 * Read the free and the total diskspace for the given instance
	 * @param instance
	 * @throws ZookeeperNotFoundException
	 * @throws ZookeeperException 
	 */
	private void readDiskSpaceForInstance(final BBoxDBInstance instance) 
			throws ZookeeperNotFoundException, ZookeeperException {
		
		final String diskspacePath = pathHelper.getInstancesDiskspacePath(instance);
		
		final List diskspaceChilds = zookeeperClient.getChildren(diskspacePath);
		
		for(final String path : diskspaceChilds) {
			final String unquotedPath = ZookeeperInstancePathHelper.unquotePath(path);
			final String totalDiskspacePath = pathHelper.getInstancesDiskspaceTotalPath(instance, unquotedPath);
			final String freeDiskspacePath = pathHelper.getInstancesDiskspaceFreePath(instance, unquotedPath);
			
			final String totalDiskspaceString = zookeeperClient.readPathAndReturnString(totalDiskspacePath);
			final String freeDiskspaceString = zookeeperClient.readPathAndReturnString(freeDiskspacePath);
			
			final Long totalDiskspace = Longs.tryParse(totalDiskspaceString);
			final Long freeDiskspace = Longs.tryParse(freeDiskspaceString);
							
			if(totalDiskspace == null) {
				logger.error("Unable to parse {} as total diskspace", totalDiskspaceString);
			} else {
				instance.addTotalSpace(unquotedPath, totalDiskspace);
			}
			
			if(freeDiskspace == null) {
				logger.error("Unable to parse {} as free diskspace", freeDiskspaceString);
			} else {
				instance.addFreeSpace(unquotedPath, freeDiskspace);
			}
		}
	}
	

	/**
	 * Zookeeper watched event
	 */
	@Override
	public void process(final WatchedEvent watchedEvent) {

		final boolean aquired = zookeeperClient.acquire();

		try {
			logger.debug("Got zookeeper event: {} " + watchedEvent);
			
			if(aquired) {
				processZookeeperEvent(watchedEvent);
			} else {
				logger.info("Ignoring zookeeper event, unable to aquire zookeeper");
			}
			
		} catch (Throwable e) {
			logger.error("Got uncought exception while processing event", e);
		} finally {
			if(aquired) {
				zookeeperClient.release();
			}
		}

	}

	/**
	 * Process zooekeeper events
	 * 
	 * @param watchedEvent
	 */
	private synchronized void processZookeeperEvent(final WatchedEvent watchedEvent) {
		// Ignore null parameter
		if (watchedEvent == null) {
			logger.warn("process called with an null argument");
			return;
		}

		final ServiceState serviceState = zookeeperClient.getServiceState();
		
		// Shutdown is pending, stop event processing
		if (! serviceState.isInRunningState()) {
			logger.debug("Ignoring event {}, because service state is {}", watchedEvent, serviceState);
			return;
		}

		// Ignore type=none event
		if (watchedEvent.getType() == EventType.None) {
			return;
		}

		// Process events
		if (watchedEvent.getPath() != null) {
			readMembershipAndRegisterWatch();	
		} else {
			logger.warn("Got unknown zookeeper event: {}", watchedEvent);
		}
	}

	/**
	 * Get the zookeeper client
	 * @return
	 */
	public ZookeeperClient getZookeeperClient() {
		return zookeeperClient;
	}
	
	/**
	 * Update the hardware info
	 * @param zookeeperClient
	 * @throws ZookeeperException 
	 */
	public void updateNodeInfo(final BBoxDBInstance instance) throws ZookeeperException {
		
		// Version 
		final String versionPath = pathHelper.getInstancesVersionPath(instance);
		zookeeperClient.replacePersistentNode(versionPath, Const.VERSION.getBytes());
		
		// CPUs
		final int cpuCores = SystemInfo.getCPUCores();
		final String cpuCoresPath = pathHelper.getInstancesCpuCorePath(instance);
		zookeeperClient.replacePersistentNode(cpuCoresPath, Integer.toString(cpuCores).getBytes());

		// Memory
		final long memory = SystemInfo.getAvailableMemory();
		final String memoryPath = pathHelper.getInstancesMemoryPath(instance);
		zookeeperClient.replacePersistentNode(memoryPath, Long.toString(memory).getBytes());

		// Diskspace
		final BBoxDBConfiguration bboxDBConfiguration = BBoxDBConfigurationManager.getConfiguration();
		final List directories = bboxDBConfiguration.getStorageDirectories();
		for(final String directory : directories) {
			final File path = new File(directory);
			
			// Free
			final long freeDiskspace = SystemInfo.getFreeDiskspace(path);
			final String freeDiskspacePath = pathHelper.getInstancesDiskspaceFreePath(instance, directory);
			zookeeperClient.replacePersistentNode(freeDiskspacePath, Long.toString(freeDiskspace).getBytes());

			// Total
			final long totalDiskspace = SystemInfo.getTotalDiskspace(path);
			final String totalDiskspacePath = pathHelper.getInstancesDiskspaceTotalPath(instance, directory);
			zookeeperClient.replacePersistentNode(totalDiskspacePath, Long.toString(totalDiskspace).getBytes());
		}
	}

	/**
	 * Update the instance data
	 * @param zookeeperClient
	 * @throws ZookeeperException 
	 */
	public void updateStateData(final BBoxDBInstance instance) throws ZookeeperException {
		updateStateData(instance, instance.getState());
	}
	
	/**
	 * Update the instance data
	 * @param zookeeperClient
	 * @throws ZookeeperException 
	 */
	public void updateStateData(final BBoxDBInstance instance, final BBoxDBInstanceState newState) 
			throws ZookeeperException {
		
		final String statePath = zookeeperClient.getActiveInstancesPath() + "/" + instance.getStringValue();
		
		logger.info("Update instance state on: {}", statePath);
		zookeeperClient.replaceEphemeralNode(statePath, newState.getZookeeperValue().getBytes());
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy