/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* 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.linkedin.pinot.transport.netty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.linkedin.pinot.common.response.ServerInstance;
import com.linkedin.pinot.transport.common.Callback;
import com.linkedin.pinot.transport.common.NoneType;
import com.linkedin.pinot.transport.metrics.NettyClientMetrics;
import com.linkedin.pinot.transport.pool.KeyedPool;
import com.linkedin.pinot.transport.pool.PooledResourceManager;
import io.netty.channel.EventLoopGroup;
import io.netty.util.Timer;
public class PooledNettyClientResourceManager implements PooledResourceManager<PooledNettyClientResourceManager.PooledClientConnection> {
protected static Logger LOGGER = LoggerFactory.getLogger(PooledNettyClientResourceManager.class);
private KeyedPool<PooledClientConnection> _pool;
private final EventLoopGroup _eventLoop;
private final NettyClientMetrics _metrics;
private final Timer _timer;
public PooledNettyClientResourceManager(EventLoopGroup eventLoop, Timer timer, NettyClientMetrics metrics) {
_eventLoop = eventLoop;
_metrics = metrics;
_timer = timer;
}
public void setPool(KeyedPool<PooledClientConnection> pool) {
_pool = pool;
}
@Override
public PooledClientConnection create(ServerInstance key) {
PooledClientConnection conn = new PooledClientConnection(_pool, key, _eventLoop, _timer, _metrics);
conn.connect();
// At this point, we have already waited for a connection to complete. Whether it succeeds or fails,
// we should return the object to the pool. It is possible to return null if the connection attempt
// fails (i.e. we get back a false return above), but then the pool starts to initiate new connections
// to the host to maintain the minimum pool size. We don't want to DOS the server continuously
// trying to create new connections. A connection is always validated before the pool returns it
// from the idle list to the user.
return conn;
}
// This destroy method is called ONLY when asyncpoolimpl wants to destroy the connection object.
@Override
public boolean destroy(ServerInstance key, boolean isBad, PooledClientConnection resource) {
LOGGER.info("Destroying client connection {}, isBad: {}", resource, isBad);
boolean closed = false;
try {
resource.close();
resource.setDestroyed(true);
closed = true;
} catch (InterruptedException e) {
LOGGER.error("Got interrupted exception when closing resource {}", resource, e);
}
return closed;
}
@Override
public boolean validate(ServerInstance key, PooledClientConnection resource) {
if ( null != resource)
return resource.validate();
return false;
}
/**
* Pool aware NettyTCPClientConnection
*
*/
public class PooledClientConnection extends NettyTCPClientConnection implements Callback<NoneType> {
private final KeyedPool<PooledNettyClientResourceManager.PooledClientConnection> _pool;
private boolean _destroyed = false;
public PooledClientConnection(KeyedPool<PooledClientConnection> pool, ServerInstance server,
EventLoopGroup eventGroup, Timer timer, NettyClientMetrics metric) {
super(server, eventGroup, timer, metric);
_pool = pool;
init();
}
public void setDestroyed(boolean isDestroyed) {
_destroyed = isDestroyed;
}
public void init() {
setRequestCallback(this);
}
@Override
public void onSuccess(NoneType arg0) {
/**
* We got the response successfully. Time to checkin back to the pool.
*/
_pool.checkinObject(getServer(), this);
}
@Override
public void onError(Throwable arg0) {
if (isSelfClose()) {
LOGGER.info("Got notified on self-close to {} connId {}", _server, getConnId());
}
/**
* We got error. Time to discard this connection.
*/
if (!_destroyed) {
LOGGER.info("Destroying connection (onError) {} due to error connId {}", _server, getConnId(),
arg0.getMessage());
_pool.destroyObject(getServer(), this);
_destroyed = true;
}
}
@Override
protected void releaseResources() {
// _pool is null in tests only.
if (_pool != null && !_destroyed) {
LOGGER.info("Destroying connection (releaseResources) {} due to error connId {}", _server, getConnId());
_pool.destroyObject(getServer(), this);
_destroyed = true;
}
}
}
}