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

com.datastax.oss.simulacron.server.Server Maven / Gradle / Ivy

The newest version!
/*
 * Copyright DataStax, Inc.
 *
 * 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 com.datastax.oss.simulacron.server;

import static com.datastax.oss.simulacron.server.CompletableFutures.getUninterruptibly;

import com.datastax.oss.simulacron.common.cluster.ClusterSpec;
import com.datastax.oss.simulacron.common.cluster.DataCenterSpec;
import com.datastax.oss.simulacron.common.cluster.NodeSpec;
import com.datastax.oss.simulacron.common.stubbing.EmptyReturnMetadataHandler;
import com.datastax.oss.simulacron.common.stubbing.PeerMetadataHandler;
import com.datastax.oss.simulacron.common.stubbing.StubMapping;
import com.datastax.oss.simulacron.server.token.RandomTokenAssigner;
import com.datastax.oss.simulacron.server.token.SplitTokenAssigner;
import com.datastax.oss.simulacron.server.token.TokenAssigner;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.flush.FlushConsolidationHandler;
import io.netty.util.AttributeKey;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

/**
 * The main point of entry for registering and binding Clusters to the network. Provides methods for
 * registering and unregistering Clusters. When a Cluster is registered, all applicable nodes are
 * bound to their respective network interfaces and are ready to handle native protocol traffic.
 */
public final class Server implements AutoCloseable {

  private static final Logger logger = LoggerFactory.getLogger(Server.class);

  static final AttributeKey HANDLER = AttributeKey.valueOf("NODE");

  /** The bootstrap responsible for binding new listening server channels. */
  final ServerBootstrap serverBootstrap;

  /**
   * A resolver for deriving the next {@link SocketAddress} to assign a {@link NodeSpec} if it does
   * not provide one.
   */
  private final AddressResolver addressResolver;

  /**
   * The amount of time in nanoseconds to allow for waiting for network interfaces to bind for
   * nodes.
   */
  private final long bindTimeoutInNanos;

  /** The store that configures how to handle requests sent from a client. */
  final StubStore stubStore;

  /** The timer to use for scheduling actions. */
  final Timer timer;

  /** Whether or not a custom timer was used. We don't want to close ones users pass in. */
  private final boolean customTimer;

  /** Counter used to assign incrementing ids to clusters. */
  private final AtomicLong clusterCounter = new AtomicLong();

  /** Mapping of registered {@link BoundCluster} instances to their identifier. */
  private final Map clusters = new ConcurrentHashMap<>();

  /** Whether or not activity logging is enabled. */
  private final boolean activityLogging;

  final EventLoopGroup eventLoopGroup;

  /** Whether or not a custom event loop was used. We don't want to close ones users pass in. */
  private final boolean customEventLoop;

  private final AtomicReference> closeFuture = new AtomicReference<>();

  Server(
      AddressResolver addressResolver,
      EventLoopGroup eventLoopGroup,
      boolean customEventLoop,
      Timer timer,
      boolean customTimer,
      long bindTimeoutInNanos,
      StubStore stubStore,
      boolean activityLogging,
      ServerBootstrap serverBootstrap) {
    // custom constructor onyl made to help facilitate testing with a custom bootstrap.
    this.addressResolver = addressResolver;
    this.timer = timer;
    this.customTimer = customTimer;
    this.eventLoopGroup = eventLoopGroup;
    this.customEventLoop = customEventLoop;
    this.serverBootstrap = serverBootstrap;
    this.bindTimeoutInNanos = bindTimeoutInNanos;
    this.stubStore = stubStore;
    this.activityLogging = activityLogging;
  }

  private Server(
      AddressResolver addressResolver,
      EventLoopGroup eventLoopGroup,
      Class channelClass,
      boolean customEventLoop,
      Timer timer,
      boolean customTimer,
      long bindTimeoutInNanos,
      StubStore stubStore,
      boolean activityLogging) {
    this(
        addressResolver,
        eventLoopGroup,
        customEventLoop,
        timer,
        customTimer,
        bindTimeoutInNanos,
        stubStore,
        activityLogging,
        new ServerBootstrap()
            .group(eventLoopGroup)
            .channel(channelClass)
            .childHandler(new Initializer()));
  }

  /** @return Whether or not this has been closed. */
  public boolean isClosed() {
    return closeFuture.get() != null;
  }

   CompletionStage failByClose() {
    CompletableFuture future = new CompletableFuture<>();
    future.completeExceptionally(new IllegalStateException("Server is closed"));
    return future;
  }

  /**
   * Wraps a Cluster in a {@link BoundCluster} which is a {@link java.io.Closeable} version of
   * Cluster
   *
   * @param cluster Cluster to wrap in a bound cluster.
   */
  @SuppressWarnings("unchecked")
  private BoundCluster boundCluster(ClusterSpec cluster) {
    long clusterId = cluster.getId() == null ? clusterCounter.getAndIncrement() : cluster.getId();
    return new BoundCluster(cluster, clusterId, this);
  }

  /** synchronous version of {@link #registerAsync(ClusterSpec.Builder)} */
  public BoundCluster register(ClusterSpec.Builder builder) {
    return getUninterruptibly(registerAsync(builder));
  }

  /** see {@link #registerAsync(ClusterSpec, ServerOptions)} */
  public CompletionStage registerAsync(ClusterSpec.Builder builder) {
    return registerAsync(builder.build(), ServerOptions.DEFAULT);
  }

  /** synchronous version of {@link #registerAsync(ClusterSpec)} */
  public BoundCluster register(ClusterSpec cluster) {
    return getUninterruptibly(registerAsync(cluster));
  }

  /** see {@link #registerAsync(ClusterSpec, ServerOptions)} */
  public CompletionStage registerAsync(ClusterSpec cluster) {
    return registerAsync(cluster, ServerOptions.DEFAULT);
  }

  /** synchronous version of {@link #registerAsync(ClusterSpec, ServerOptions)} */
  public BoundCluster register(ClusterSpec cluster, ServerOptions serverOptions) {
    return getUninterruptibly(registerAsync(cluster, serverOptions));
  }

  /**
   * Registers a {@link BoundCluster} and binds its {@link BoundNode} instances to their respective
   * interfaces. If any members of the cluster lack an id, this will assign a random one for them.
   * If any nodes lack an address, this will assign one based on the configured {@link
   * AddressResolver}.
   *
   * 

The given future will fail if not completed after the configured {@link * Server.Builder#withBindTimeout(long, TimeUnit)}. * * @param cluster cluster to register * @param serverOptions custom server options to use when registering this cluster. * @return A future that when it completes provides a {@link BoundCluster} with ids and addresses * assigned. Note that the return value is not the same object as the input. */ @SuppressWarnings("unchecked") public CompletionStage registerAsync( ClusterSpec cluster, ServerOptions serverOptions) { if (isClosed()) { return failByClose(); } BoundCluster c = boundCluster(cluster); List> bindFutures = new ArrayList<>(); boolean activityLogging = serverOptions.isActivityLoggingEnabled() != null ? serverOptions.isActivityLoggingEnabled() : this.activityLogging; TokenAssigner tokenAssignment = cluster.getNumberOfTokens() == 1 ? new SplitTokenAssigner(cluster) : new RandomTokenAssigner(cluster.getNumberOfTokens()); for (DataCenterSpec dataCenter : cluster.getDataCenters()) { BoundDataCenter dc = new BoundDataCenter(dataCenter, c); for (NodeSpec node : dataCenter.getNodes()) { String tokenStr = tokenAssignment.getTokens(node); // Use node's address if set, otherwise generate a new one. SocketAddress address = node.getAddress() != null ? node.getAddress() : addressResolver.get(); bindFutures.add( bindInternal(node, c, dc, tokenStr, address, activityLogging).toCompletableFuture()); } } clusters.put(c.getId(), c); List nodes = new ArrayList<>(bindFutures.size()); Throwable exception = null; boolean timedOut = false; // Evaluate each future, if any fail capture the exception and record all successfully // bound nodes. long start = System.nanoTime(); for (CompletableFuture f : bindFutures) { try { if (timedOut) { BoundNode node = f.getNow(null); if (node != null) { nodes.add(node); } } else { nodes.add(f.get(bindTimeoutInNanos, TimeUnit.NANOSECONDS)); } if (System.nanoTime() - start > bindTimeoutInNanos) { timedOut = true; } } catch (TimeoutException te) { timedOut = true; exception = te; } catch (Exception e) { exception = e.getCause(); } } // If there was a failure close all bound nodes. if (exception != null) { final Throwable e = exception; // Close each node List> futures = nodes.stream().map(this::close).collect(Collectors.toList()); CompletableFuture future = new CompletableFuture<>(); // On completion of unbinding all nodes, fail the returned future. CompletableFuture.allOf(futures.toArray(new CompletableFuture[] {})) .handle( (v, ex) -> { // remove cluster from registry since it failed to completely register. clusters.remove(c.getId()); future.completeExceptionally(e); return v; }); return future; } else { return CompletableFuture.allOf(bindFutures.toArray(new CompletableFuture[] {})) .thenApply(__ -> c); } } /** synchronous version of {@link #unregisterAsync(BoundNode)} */ public BoundCluster unregister(BoundNode node) { return getUninterruptibly(unregisterAsync(node)); } /** * Convenience method for unregistering NodeSpec's cluster by object instead of by id as in {@link * #unregisterAsync(Long)}. * * @param node NodeSpec to unregister * @return A future that when completed provides the unregistered cluster as it existed in the * registry, may not be the same object as the input. */ public CompletionStage unregisterAsync(BoundNode node) { if (isClosed()) { return failByClose(); } if (node.getCluster() == null) { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally(new IllegalArgumentException("Node has no parent Cluster")); return future; } return unregisterAsync(node.getCluster().getId()); } /** synchronous version of {@link #unregisterAsync(BoundCluster)} */ public BoundCluster unregister(BoundCluster cluster) { return getUninterruptibly(unregisterAsync(cluster)); } /** * Convenience method for unregistering Cluster by object instead of by id as in {@link * #unregisterAsync(Long)}. * * @param cluster Cluster to unregister * @return A future that when completed provides the unregistered cluster as it existed in the * registry, may not be the same object as the input. */ public CompletionStage unregisterAsync(BoundCluster cluster) { return unregisterAsync(cluster.getId()); } /** synchronous version of {@link #unregisterAsync(Long)} */ public BoundCluster unregister(Long clusterId) { return getUninterruptibly(unregisterAsync(clusterId)); } /** * Unregisters a cluster and closes all listening network interfaces associated with it. * *

If the cluster is not currently registered the returned future will fail with an {@link * IllegalArgumentException}. * * @param clusterId id of the cluster. * @return A future that when completed provides the unregistered cluster as it existed in the * registry, may not be the same object as the input. */ public CompletionStage unregisterAsync(Long clusterId) { if (isClosed()) { return failByClose(); } CompletableFuture future = new CompletableFuture<>(); if (clusterId == null) { future.completeExceptionally(new IllegalArgumentException("Null id provided")); } else { BoundCluster foundCluster = clusters.remove(clusterId); List> closeFutures = new ArrayList<>(); if (foundCluster != null) { // Close socket on each node. for (BoundDataCenter dataCenter : foundCluster.getDataCenters()) { for (BoundNode node : dataCenter.getNodes()) { closeFutures.add(close(node)); } } CompletableFuture.allOf(closeFutures.toArray(new CompletableFuture[] {})) .whenComplete( (__, ex) -> { if (ex != null) { future.completeExceptionally(ex); } else { future.complete(foundCluster); } }); } else { future.completeExceptionally(new IllegalArgumentException("ClusterSpec not found.")); } } return future; } /** synchronous version of {@link #unregisterAllAsync()} */ public Integer unregisterAll() { return getUninterruptibly(unregisterAllAsync()); } /** * Unregister all currently registered clusters. * * @return future that is completed when all clusters are unregistered. */ public CompletionStage unregisterAllAsync() { if (isClosed()) { return failByClose(); } List> futures = clusters.keySet().stream() .map(this::unregisterAsync) .map(CompletionStage::toCompletableFuture) .collect(Collectors.toList()); return CompletableFuture.allOf(futures.toArray(new CompletableFuture[] {})) .thenApply(__ -> futures.size()); } /** synchronous version of {@link #registerAsync(NodeSpec.Builder)} */ public BoundNode register(NodeSpec.Builder builder) { return getUninterruptibly(registerAsync(builder)); } /** see {@link #registerAsync(NodeSpec, ServerOptions)} */ public CompletionStage registerAsync(NodeSpec.Builder builder) { return registerAsync(builder.build()); } /** see {@link #registerAsync(NodeSpec, ServerOptions)} */ public BoundNode register(NodeSpec node) { return getUninterruptibly(registerAsync(node)); } /** see {@link #registerAsync(NodeSpec, ServerOptions)} */ public CompletionStage registerAsync(NodeSpec node) { return registerAsync(node, ServerOptions.DEFAULT); } /** synchronous version of {@link #registerAsync(NodeSpec, ServerOptions)} */ public BoundNode register(NodeSpec node, ServerOptions serverOptions) { return getUninterruptibly(registerAsync(node, serverOptions)); } /** * Convenience method that registers a {@link NodeSpec} by wrapping it in a 'dummy' {@link * ClusterSpec} and registering that. * *

Note that if the given {@link NodeSpec} belongs to a {@link ClusterSpec}, the returned * future will fail with an {@link IllegalArgumentException}. * * @param node node to register. * @param serverOptions custom server options to use when registering this node. * @return A future that when it completes provides an updated {@link ClusterSpec} with ids and * addresses assigned. Note that the return value is not the same object as the input. */ public CompletionStage registerAsync(NodeSpec node, ServerOptions serverOptions) { if (isClosed()) { return failByClose(); } if (node.getDataCenter() != null) { CompletableFuture future = new CompletableFuture<>(); future.completeExceptionally( new IllegalArgumentException("Node belongs to a Cluster, should be standalone.")); return future; } // Wrap node in dummy cluster Long clusterId = clusterCounter.getAndIncrement(); BoundCluster dummyCluster = boundCluster(ClusterSpec.builder().withId(clusterId).withName("dummy").build()); BoundDataCenter dummyDataCenter = new BoundDataCenter(dummyCluster); clusters.put(clusterId, dummyCluster); boolean activityLogging = serverOptions.isActivityLoggingEnabled() != null ? serverOptions.isActivityLoggingEnabled() : this.activityLogging; // Use node's address if set, otherwise generate a new one. SocketAddress address = node.getAddress() != null ? node.getAddress() : addressResolver.get(); return bindInternal( node, dummyCluster, dummyDataCenter, node.resolvePeerInfo("tokens", String.class).orElse("0"), address, activityLogging); } /** * @param id identifier of the cluster * @return cluster matching given id or null. */ public BoundCluster getCluster(long id) { return this.clusters.get(id); } /** @return All clusters currently registered to this server. */ public Collection getClusters() { return this.clusters.values(); } private CompletionStage bindInternal( NodeSpec refNode, BoundCluster cluster, BoundDataCenter parent, String token, SocketAddress address, boolean activityLogging) { // derive a token for this node. This is done here as the ordering of nodes under a // data center is changed when it is bound. Map newPeerInfo = new HashMap<>(refNode.getPeerInfo()); newPeerInfo.put("tokens", token); CompletableFuture f = new CompletableFuture<>(); ChannelFuture bindFuture = this.serverBootstrap.bind(address); bindFuture.addListener( (ChannelFutureListener) channelFuture -> { if (channelFuture.isSuccess()) { BoundNode node = new BoundNode( address, refNode, newPeerInfo, cluster, parent, this, timer, channelFuture.channel(), activityLogging); logger.info("Bound Node {} to {}", node.resolveId(), channelFuture.channel()); channelFuture.channel().attr(HANDLER).set(node); f.complete(node); } else { // If failed, propagate it. f.completeExceptionally( new BindNodeException(refNode, address, channelFuture.cause())); } }); return f; } private CompletableFuture close(BoundNode node) { logger.debug("Closing Node {} on {}.", node.resolveId(), node.channel); return node.stopAsync() .thenApply( n -> { logger.debug( "Releasing {} back to address resolver so it may be reused.", node.getAddress()); addressResolver.release(node.getAddress()); return node; }) .toCompletableFuture(); } /** * Wrapper method for using netty-transport-native-epoll if available, addresses classes directly * without importing so if library isn't in environment this doesn't create any problems. * * @return Epoll-based event loop group is epoll is available, otherwise empty. */ private static Optional epollEventLoopGroup(ThreadFactory threadFactory) { if (io.netty.channel.epoll.Epoll.isAvailable()) { return Optional.of(new io.netty.channel.epoll.EpollEventLoopGroup(0, threadFactory)); } return Optional.empty(); } private static Class epollClass() { return io.netty.channel.epoll.EpollServerSocketChannel.class; } /** @return a {@link Builder} for configuring and creating {@link Server} instances. */ public static Builder builder() { return new Builder(); } /** * Closes Server and all of its resources. First unregisters all Clusters then closes event loop * and timer unless they were provided as input into {@link Server.Builder}. * * @return future that completes when Server is freed. */ public CompletionStage closeAsync() { if (isClosed()) { return closeFuture.get(); } else { return closeFuture.updateAndGet( current -> { if (current != null) { return current; } else { return this.unregisterAllAsync() .thenCompose( i -> { // If timer was created for Server, stop it. if (!customTimer) { timer.stop(); } // If event loop was created for Server, shut it down. if (!customEventLoop) { // Immediate shutdown should be appropriate since we unregister first so // nothing should be happening on event loop. Future future = eventLoopGroup.shutdownGracefully(0, 1, TimeUnit.SECONDS); // adapt future to completable future by calling get on common pool. CompletableFuture f = CompletableFuture.supplyAsync( () -> { try { future.get(); return null; } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }); return f; } else { CompletableFuture future = new CompletableFuture<>(); future.complete(null); return future; } }); } }); } } /** * @inheritDoc *

Also see {@link #closeAsync()} */ @Override public void close() { getUninterruptibly(closeAsync()); } public static class Builder { private AddressResolver addressResolver = AddressResolver.defaultResolver; private static long DEFAULT_BIND_TIMEOUT_IN_NANOS = TimeUnit.NANOSECONDS.convert(10, TimeUnit.SECONDS); private long bindTimeoutInNanos = DEFAULT_BIND_TIMEOUT_IN_NANOS; private Timer timer; private StubStore stubStore; private boolean activityLogging = true; private boolean multipleNodesPerIp = false; private EventLoopGroup eventLoopGroup; private Class channelClass; private List stubMappings = new ArrayList<>(); Builder() {} /** * Sets the bind timeout which is the amount of time allowed while binding listening interfaces * for nodes when establishing a cluster. * * @param time The amount of time to wait. * @param timeUnit The unit of time to wait in. * @return This builder. */ public Builder withBindTimeout(long time, TimeUnit timeUnit) { this.bindTimeoutInNanos = TimeUnit.NANOSECONDS.convert(time, timeUnit); return this; } /** * Sets the address resolver to use when assigning {@link SocketAddress} to {@link NodeSpec}'s * that don't have previously provided addresses. * * @param addressResolver resolver to use. * @return This builder. */ public Builder withAddressResolver(AddressResolver addressResolver) { this.addressResolver = addressResolver; return this; } /** * Sets a pre-created event loop group to use for the created servers {@link ServerBootstrap}. * This will not be closed when the server is closed. * *

If not specified, an Epoll or NIO event loop will be created with Server that is closed * when Server closes. Threads created by this event loop will be named 'simulacron-worker-X-Y'. * * @param eventLoopGroup event loop to use for Server * @param clazz The expected channel class to be created with this event loop. * @return This builder. */ public Builder withEventLoopGroup( EventLoopGroup eventLoopGroup, Class clazz) { this.eventLoopGroup = eventLoopGroup; this.channelClass = clazz; return this; } /** * Sets the timer to use for scheduling actions. If not set, a {@link HashedWheelTimer} is * created with a naming format of 'simulacron-timer-X-Y'. * * @param timer timer to use. * @return This builder. */ public Builder withTimer(Timer timer) { this.timer = timer; return this; } /** * Sets the {@link StubStore} to be used by this server. By default creates a new one with * built-in stubs for handling metadata requests for system.local and peers ({@link * PeerMetadataHandler}) * * @param stubStore stub store to use. * @return This builder. */ public Builder withStubStore(StubStore stubStore) { this.stubStore = stubStore; return this; } /** * Whether or not to enable activity logging. By default it is enabled. * * @param enabled enablement flag. * @return This builder. */ public Builder withActivityLoggingEnabled(boolean enabled) { this.activityLogging = enabled; return this; } /** * Whether to support multiple nodes per IP (as per CASSANDRA-7544). Using this with true * overrides {@link #withAddressResolver(AddressResolver)}, using {@link * AddressResolver#nodePerPortResolver}. * * @param enabled Whether or not a node can be assigned to each port. * @return This builder. */ public Builder withMultipleNodesPerIp(boolean enabled) { this.multipleNodesPerIp = enabled; if (enabled) { this.addressResolver = AddressResolver.nodePerPortResolver; } return this; } /** * Sets additional {@link StubMapping} to be used by this server on top of the built-in stubs * * @return This builder. */ public Builder withStubMapping(StubMapping stubMapping) { this.stubMappings.add(stubMapping); return this; } /** @return a {@link Server} instance based on this builder's configuration. */ public Server build() { if (stubStore == null) { stubStore = new StubStore(); stubStore.register(new PeerMetadataHandler(multipleNodesPerIp)); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.keyspaces")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.views")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.tables")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.columns")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.indexes")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.triggers")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.types")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.functions")); stubStore.register( new EmptyReturnMetadataHandler("SELECT * FROM system_schema.aggregates")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system_schema.views")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system.schema_keyspaces")); stubStore.register( new EmptyReturnMetadataHandler("SELECT * FROM system.schema_columnfamilies")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system.schema_columns")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system.schema_triggers")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system.schema_usertypes")); stubStore.register(new EmptyReturnMetadataHandler("SELECT * FROM system.schema_functions")); stubStore.register( new EmptyReturnMetadataHandler("SELECT * FROM system.schema_aggregates")); stubStore.register( new EmptyReturnMetadataHandler("SELECT * FROM system_virtual_schema.keyspaces")); stubStore.register( new EmptyReturnMetadataHandler("SELECT * FROM system_virtual_schema.columns")); stubStore.register( new EmptyReturnMetadataHandler("SELECT * FROM system_virtual_schema.tables")); stubMappings.forEach(stubStore::register); } Timer timer = this.timer; if (timer == null) { ThreadFactory f = new DefaultThreadFactory("simulacron-timer"); timer = new HashedWheelTimer(f); } EventLoopGroup eventLoopGroup = this.eventLoopGroup; Class channelClass = this.channelClass; if (eventLoopGroup == null) { ThreadFactory f = new DefaultThreadFactory("simulacron-io-worker"); try { // try to resolve Epoll class, if throws Exception, fall back on nio Class.forName("io.netty.channel.epoll.Epoll"); // if epoll event loop could be established, use it, otherwise use nio. Optional epollEventLoop = epollEventLoopGroup(f); if (epollEventLoop.isPresent()) { logger.debug("Detected epoll support, using EpollEventLoopGroup"); eventLoopGroup = epollEventLoop.get(); channelClass = epollClass(); } else { logger.debug("Could not load native transport (epoll), using NioEventLoopGroup"); eventLoopGroup = new NioEventLoopGroup(0, f); channelClass = NioServerSocketChannel.class; } } catch (ClassNotFoundException ce) { logger.debug("netty-transport-native-epoll not on classpath, using NioEventLoopGroup"); eventLoopGroup = new NioEventLoopGroup(0, f); channelClass = NioServerSocketChannel.class; } } return new Server( addressResolver, eventLoopGroup, channelClass, this.eventLoopGroup != null, timer, this.timer != null, bindTimeoutInNanos, stubStore, activityLogging); } } static class Initializer extends ChannelInitializer { @Override protected void initChannel(Channel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); BoundNode node = channel.parent().attr(HANDLER).get(); node.clientChannelGroup.add(channel); MDC.put("node", node.getId().toString()); try { logger.debug("Got new connection {}", channel); pipeline .addLast(new FlushConsolidationHandler()) .addLast("decoder", new FrameDecoder(node.getFrameCodec())) .addLast("encoder", new FrameEncoder(node.getFrameCodec())) .addLast("requestHandler", new RequestHandler(node)); } finally { MDC.remove("node"); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy