herddb.client.ZookeeperClientSideMetadataProvider.java Source code

Java tutorial

Introduction

Here is the source code for herddb.client.ZookeeperClientSideMetadataProvider.java

Source

/*
 Licensed to Diennea S.r.l. under one
 or more contributor license agreements. See the NOTICE file
 distributed with this work for additional information
 regarding copyright ownership. Diennea S.r.l. licenses this file
 to you 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 herddb.client;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import herddb.model.NodeMetadata;
import herddb.model.TableSpace;
import herddb.network.ServerHostData;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

/**
 * Clients side lookup of metadata using ZooKeeper
 *
 * @author enrico.olivelli
 */
public class ZookeeperClientSideMetadataProvider implements ClientSideMetadataProvider {

    private static final Logger LOG = Logger.getLogger(ZookeeperClientSideMetadataProvider.class.getName());

    private final String basePath;
    private final Supplier<ZooKeeper> zookeeperSupplier;
    private boolean ownZooKeeper;
    private static final int MAX_TRIALS = 20;

    public ZookeeperClientSideMetadataProvider(String basePath, Supplier<ZooKeeper> zookeeper) {
        this.basePath = basePath;
        this.zookeeperSupplier = zookeeper;
        this.ownZooKeeper = false;
    }

    public ZookeeperClientSideMetadataProvider(String zkAddress, int zkSessionTimeout, String basePath) {
        this(basePath, () -> {
            try {
                CountDownLatch waitForConnection = new CountDownLatch(1);
                ZooKeeper zk = new ZooKeeper(zkAddress, zkSessionTimeout, new Watcher() {
                    @Override
                    @SuppressFBWarnings(value = "SF_SWITCH_NO_DEFAULT")
                    public void process(WatchedEvent event) {
                        LOG.log(Level.SEVERE, "zk client event " + event);
                        switch (event.getState()) {
                        case SyncConnected:
                        case ConnectedReadOnly:
                            waitForConnection.countDown();
                            break;
                        }
                    }
                });
                boolean waitResult = waitForConnection.await(zkSessionTimeout * 2L, TimeUnit.SECONDS);
                return zk;
            } catch (Exception err) {
                LOG.log(Level.SEVERE, "zk client error " + err, err);
                return null;
            }
        });
        this.ownZooKeeper = true;
    }

    private final Map<String, String> tableSpaceLeaders = new ConcurrentHashMap<>();
    private final Map<String, ServerHostData> servers = new ConcurrentHashMap<>();

    @Override
    public void requestMetadataRefresh() {
        tableSpaceLeaders.clear();
        servers.clear();
    }

    @Override
    public String getTableSpaceLeader(String tableSpace) throws ClientSideMetadataProviderException {
        tableSpace = tableSpace.toLowerCase();
        String cached = tableSpaceLeaders.get(tableSpace);
        if (cached != null) {
            return cached;
        }
        ZooKeeper zooKeeper = getZooKeeper();
        try {
            for (int i = 0; i < MAX_TRIALS; i++) {
                try {
                    try {
                        return readAsTableSpace(zooKeeper, tableSpace);
                    } catch (KeeperException.NoNodeException ex) {
                        try {
                            // use the nodeid as tablespace
                            return readAsNode(zooKeeper, tableSpace);
                        } catch (KeeperException.NoNodeException ex2) {
                            return null;
                        }
                    }
                } catch (KeeperException.ConnectionLossException ex) {
                    LOG.log(Level.SEVERE, "tmp error getTableSpaceLeader for " + tableSpace + ": " + ex);
                    try {
                        Thread.sleep(i * 500 + 1000);
                    } catch (InterruptedException exit) {
                        throw new ClientSideMetadataProviderException(exit);
                    }
                } catch (KeeperException | InterruptedException | IOException ex) {
                    throw new ClientSideMetadataProviderException(ex);
                }
            }
        } finally {
            if (ownZooKeeper) {
                try {
                    zooKeeper.close();
                } catch (InterruptedException ex) {
                    throw new ClientSideMetadataProviderException(ex);
                }
            }
        }

        throw new ClientSideMetadataProviderException(
                "Could not find a leader for tablespace " + tableSpace + " in time");
    }

    private String readAsTableSpace(ZooKeeper zooKeeper, String tableSpace)
            throws IOException, InterruptedException, KeeperException {
        tableSpace = tableSpace.toLowerCase();
        Stat stat = new Stat();
        byte[] result = zooKeeper.getData(basePath + "/tableSpaces/" + tableSpace, false, stat);
        String leader = TableSpace.deserialize(result, stat.getVersion()).leaderId;
        tableSpaceLeaders.put(tableSpace, leader);
        return leader;
    }

    private String readAsNode(ZooKeeper zooKeeper, String tableSpace)
            throws IOException, InterruptedException, KeeperException {
        tableSpace = tableSpace.toLowerCase();
        Stat stat = new Stat();
        byte[] result = zooKeeper.getData(basePath + "/nodes/" + tableSpace, false, stat);
        NodeMetadata md = NodeMetadata.deserialize(result, stat.getVersion());
        String leader = md.nodeId;
        tableSpaceLeaders.put(tableSpace, leader);
        return leader;
    }

    @Override
    public ServerHostData getServerHostData(String nodeId) throws ClientSideMetadataProviderException {
        ServerHostData cached = servers.get(nodeId);
        if (cached != null) {
            return cached;
        }
        ZooKeeper zooKeeper = getZooKeeper();
        try {
            for (int i = 0; i < MAX_TRIALS; i++) {
                try {
                    Stat stat = new Stat();
                    byte[] node = zooKeeper.getData(basePath + "/nodes/" + nodeId, null, stat);
                    NodeMetadata nodeMetadata = NodeMetadata.deserialize(node, stat.getVersion());
                    ServerHostData result = new ServerHostData(nodeMetadata.host, nodeMetadata.port, "?",
                            nodeMetadata.ssl, new HashMap<>());
                    servers.put(nodeId, result);
                    return result;
                } catch (KeeperException.NoNodeException ex) {
                    return null;
                } catch (KeeperException.ConnectionLossException ex) {
                    LOG.log(Level.SEVERE, "tmp error getServerHostData for " + nodeId + ": " + ex);
                    try {
                        Thread.sleep(i * 500 + 1000);
                    } catch (InterruptedException exit) {
                        throw new ClientSideMetadataProviderException(exit);
                    }
                } catch (KeeperException | InterruptedException | IOException ex) {
                    throw new ClientSideMetadataProviderException(ex);
                } finally {
                    if (ownZooKeeper) {
                        try {
                            zooKeeper.close();
                        } catch (InterruptedException ex) {
                            throw new ClientSideMetadataProviderException(ex);
                        }
                    }
                }
            }
        } finally {
            if (ownZooKeeper) {
                try {
                    zooKeeper.close();
                } catch (InterruptedException ex) {
                    throw new ClientSideMetadataProviderException(ex);
                }
            }
        }
        throw new ClientSideMetadataProviderException(
                "Could not find a server info for node " + nodeId + " in time");
    }

    private ZooKeeper getZooKeeper() throws ClientSideMetadataProviderException {
        ZooKeeper zooKeeper = zookeeperSupplier.get();
        if (zooKeeper == null) {
            throw new ClientSideMetadataProviderException(new Exception("ZooKeeper client is not available"));
        }
        return zooKeeper;
    }

}