com.yahoo.gondola.container.AdminClient.java Source code

Java tutorial

Introduction

Here is the source code for com.yahoo.gondola.container.AdminClient.java

Source

/*
 * Copyright 2015, Yahoo Inc.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */

package com.yahoo.gondola.container;

import com.google.common.collect.Range;
import com.yahoo.gondola.Config;
import com.yahoo.gondola.container.ShardManagerProtocol.ShardManagerException;
import com.yahoo.gondola.container.client.ShardManagerClient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Map;

import static com.yahoo.gondola.container.ShardManagerProtocol.ShardManagerException.CODE.SLAVE_NOT_SYNC;

/**
 * The type Admin client.
 */
public class AdminClient {

    // TODO: move to config
    public static final int RETRY_COUNT = 3;
    public static final int TIMEOUT_MS = 3000;
    private String serviceName;
    private Config config;
    private ShardManagerClient shardManagerClient;
    private ConfigWriter configWriter;
    private GondolaAdminClient gondolaAdminClient;

    private static Logger logger = LoggerFactory.getLogger(AdminClient.class);
    private boolean tracing = false;

    /**
     * Instantiates a new Admin client.
     *
     * @param serviceName        the service name
     * @param shardManagerClient the shard manager client
     * @param config             the config
     */
    public AdminClient(String serviceName, ShardManagerClient shardManagerClient, Config config,
            ConfigWriter configWriter, GondolaAdminClient gondolaAdminClient) {
        this.serviceName = serviceName;
        this.shardManagerClient = shardManagerClient;
        this.config = config;
        this.configWriter = configWriter;
        this.config.registerForUpdates(config1 -> {
            tracing = config1.getBoolean("tracing.adminCli");
        });
        this.gondolaAdminClient = gondolaAdminClient;
    }

    public static AdminClient getInstance(Config config, String clientName) {
        return new AdminClient(Utils.getRegistryConfig(config).attributes.get("serviceName"),
                new ShardManagerProvider().getShardManagerClient(config, clientName), config,
                new ConfigWriter(config.getFile()), new GondolaAdminClient(config));
    }

    /**
     * Sets service name.
     *
     * @param serviceName the service name
     * @throws AdminException the admin exception
     */
    public void setServiceName(String serviceName) throws AdminException {
        this.serviceName = serviceName;
    }

    /**
     * Gets service name.
     *
     * @return the service name
     * @throws AdminException the admin exception
     */
    public String getServiceName() throws AdminException {
        return serviceName;
    }

    /**
     * Gets config.
     *
     * @return the config
     * @throws AdminException the admin exception
     */
    public Config getConfig() throws AdminException {
        return config;
    }

    /**
     * Sets config.
     *
     * @param configFile the config file.
     * @throws AdminException the admin exception
     */
    public void setConfig(File configFile) throws AdminException {
    }

    /**
     * Split shard.
     *
     * @param fromShardId the from shard id
     * @param toShardId   the to shard id
     * @throws AdminException the admin exception
     */
    public void splitShard(String fromShardId, String toShardId) throws AdminException, InterruptedException {
        Range<Integer> range = lookupSplitRange(fromShardId, toShardId);
        assignBuckets(range.lowerEndpoint(), range.upperEndpoint(), fromShardId, toShardId);
    }

    /**
     * Assign buckets.
     */
    public void assignBuckets(int lowerBound, int upperBound, String fromShardId, String toShardId)
            throws InterruptedException, AdminException {
        Range range = Range.closed(lowerBound, upperBound);
        trace("[admin] Executing assign buckets={} from {} to {}", range, fromShardId, toShardId);
        for (int i = 1; i <= RETRY_COUNT; i++) {
            try {
                trace("[admin] Initializing slaves on {} ...", toShardId);
                shardManagerClient.startObserving(toShardId, fromShardId, TIMEOUT_MS * 3);

                trace("[admin] All nodes in {} are in slave mode, "
                        + "waiting for slave logs approaching to leader's log position.", toShardId);

                if (!shardManagerClient.waitSlavesApproaching(toShardId, -1)) {
                    throw new ShardManagerException(SLAVE_NOT_SYNC);
                }

                trace("[admin] All nodes in {} logs approached to leader's log position, assigning buckets={} ...",
                        toShardId, range);
                // migrateBuckets is a atomic operation executing on leader at fromShard,
                // after operation is success, it will stop observing mode of toShard.
                shardManagerClient.migrateBuckets(range, fromShardId, toShardId, TIMEOUT_MS);
                trace("[admin] success!");
                trace("[admin] Writing latest config to config storage!");
                saveConfig(fromShardId, toShardId);
                break;
            } catch (ShardManagerException e) {
                logger.warn("Error occurred during assign buckets.. retrying {} / {}, errorMsg={}", i, RETRY_COUNT,
                        e.getMessage());
                try {
                    shardManagerClient.stopObserving(toShardId, fromShardId, TIMEOUT_MS);
                    shardManagerClient.rollbackBuckets(range);
                } catch (ShardManagerException e1) {
                    logger.info("Rollback, Stop observing failed, ignoring the error. msg={}", e1.getMessage());
                }
                if (i == RETRY_COUNT) {
                    logger.error("Assign bucket failed, lastError={}", e.getMessage());
                    throw new AdminException(e);
                }
            }
        }
    }

    private void saveConfig(String fromShardId, String toShardId) throws AdminException {
        configWriter.setBucketMap(fromShardId, getBucketMapString(fromShardId));
        configWriter.setBucketMap(fromShardId, getBucketMapString(toShardId));
        setConfig(configWriter.save());
    }

    private String getBucketMapString(String fromShardId) {
        // TODO: implement
        return "";
    }

    private void trace(String format, Object... args) {
        if (tracing) {
            logger.info(format, args);
        }
    }

    private Range<Integer> lookupSplitRange(String fromShardId, String toShardId) {
        // TODO: implement
        return Range.closed(0, 5);
    }

    /**
     * Merge shard.
     *
     * @param fromShardId the from shard id
     * @param toShardId   the to shard id
     * @throws AdminException the admin exception
     */
    public void mergeShard(String fromShardId, String toShardId) throws AdminException, InterruptedException {
        Range<Integer> range = lookupMergeRange(fromShardId, toShardId);
        assignBuckets(range.lowerEndpoint(), range.upperEndpoint(), fromShardId, toShardId);
    }

    private Range<Integer> lookupMergeRange(String fromShardId, String toShardId) {
        // TODO: implement
        return Range.closed(1, 5);
    }

    /**
     * Enable.
     *
     * @param target   the target
     * @param targetId the target id
     * @param enable   enable or disable
     * @throws AdminException the admin exception
     */
    public void enable(Target target, String targetId, boolean enable) throws AdminException {
        switch (target) {
        case HOST:
            for (String shardId : config.getShardIds(targetId)) {
                gondolaAdminClient.enable(targetId, shardId, enable);
            }
            break;
        case SHARD:
            throw new UnsupportedOperationException("Enable/disable a shard is not allowed.");
        case SITE:
            config.getHostIds().stream().filter(hostId -> config.getSiteIdForHost(hostId).equals(targetId))
                    .forEach(hostId -> {
                        for (String shardId : config.getShardIds(hostId)) {
                            gondolaAdminClient.enable(hostId, shardId, enable);
                        }
                    });
            break;
        case STORAGE:
            break;
        case ALL:
            throw new UnsupportedOperationException("Enable/disable a shard is not allowed.");
        default:
            throw new IllegalStateException("unsupported target");
        }
    }

    /**
     * Enable trace.
     *
     * @param target   the target
     * @param targetId the target id
     * @throws AdminException the admin exception
     */
    public void enableTracing(Target target, String targetId) throws AdminException {

    }

    /**
     * Disable trace.
     *
     * @param target   the target
     * @param targetId the target id
     * @throws AdminException the admin exception
     */
    public void disableTracing(Target target, String targetId) throws AdminException {

    }

    enum Target {
        HOST, SHARD, SITE, STORAGE, ALL
    }

    class AdminException extends Exception {

        ErrorCode errorCode;

        public AdminException(ShardManagerException e) {
            super(e);
        }

        public AdminException() {

        }
    }

    enum ErrorCode {
        CONFIG_NOT_FOUND(10000);
        private int code;

        ErrorCode(int code) {
            this.code = code;
        }
    }

    public Map setLeader(String hostId, String shardId) {
        return gondolaAdminClient.setLeader(hostId, shardId);
    }

    public Map getHostStatus(String hostId) {
        return gondolaAdminClient.getHostStatus(hostId);
    }

    public Map<String, Object> getServiceStatus() {
        return gondolaAdminClient.getServiceStatus();
    }

    public Map inspectRequestUri(String hostId, String uri) {
        return gondolaAdminClient.inspectRequestUri(uri, hostId);
    }
}