/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package org.opencloudb.backend;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.log4j.Logger;
import org.opencloudb.MycatSystem;
import org.opencloudb.config.Alarms;
import org.opencloudb.config.model.DBHostConfig;
import org.opencloudb.config.model.DataHostConfig;
import org.opencloudb.mysql.nio.handler.ConnectionHeartBeatHandler;
import org.opencloudb.mysql.nio.handler.DelegateResponseHandler;
import org.opencloudb.mysql.nio.handler.NewConnectionRespHandler;
import org.opencloudb.mysql.nio.handler.ResponseHandler;
import org.opencloudb.util.TimeUtil;
public abstract class PhysicalDatasource {
private static final Logger LOGGER = Logger
.getLogger(PhysicalDatasource.class);
private final String name;
private final int size;
private final DBHostConfig config;
private final ConMap conMap = new ConMap();
private final boolean readNode;
private volatile long heartbeatRecoveryTime;
private final DataHostConfig hostConfig;
private final ConnectionHeartBeatHandler conHeartBeatHanler = new ConnectionHeartBeatHandler();
private PhysicalDBPool dbPool;
public PhysicalDatasource(DBHostConfig config, DataHostConfig hostConfig,
boolean isReadNode) {
this.size = config.getMaxCon();
this.config = config;
this.name = config.getHostName();
this.hostConfig = hostConfig;
this.readNode = isReadNode;
}
public boolean isMyConnection(BackendConnection con) {
for(ConQueue queue:conMap.getAllConQueue())
{
if(queue.isSameCon(con))
{
return true;
}
}
return false;
}
public DataHostConfig getHostConfig() {
return hostConfig;
}
public boolean isReadNode() {
return readNode;
}
public int getSize() {
return size;
}
public void setDbPool(PhysicalDBPool dbPool) {
this.dbPool = dbPool;
}
public PhysicalDBPool getDbPool() {
return dbPool;
}
public String getName() {
return name;
}
public long getExecuteCount() {
long executeCount = 0;
for (ConQueue queue : conMap.getAllConQueue()) {
executeCount += queue.getExecuteCount();
}
return executeCount;
}
public long getExecuteCountForSchema(String schema) {
return conMap.getSchemaConQueue(schema).getExecuteCount();
}
public int getActiveCountForSchema(String schema) {
return conMap.getActiveCountForSchema(schema, this);
}
public int getIdleCountForSchema(String schema) {
ConQueue queue = conMap.getSchemaConQueue(schema);
int total = 0;
total += queue.getAutoCommitCons().size()
+ queue.getManCommitCons().size();
return total;
}
public int getIdleCount() {
int total = 0;
for (ConQueue queue : conMap.getAllConQueue()) {
total += queue.getAutoCommitCons().size()
+ queue.getManCommitCons().size();
}
return total;
}
private boolean validSchema(String schema) {
String theSchema = schema;
return theSchema != null & !theSchema.equals("")
&& !theSchema.equals("snyn...");
}
private void checkIfNeedHeartBeat(
LinkedList<BackendConnection> heartBeatCons, ConQueue queue,
ConcurrentLinkedQueue<BackendConnection> checkLis,
long hearBeatTime, long hearBeatTime2) {
int MAX_CONS_IN_ONE_CHECK = 10;
Iterator<BackendConnection> checkListItor = checkLis.iterator();
while (checkListItor.hasNext()) {
BackendConnection con = checkListItor.next();
if (con.isClosed()) {
checkListItor.remove();
continue;
}
if (validSchema(con.getSchema())) {
if (con.getLastTime() < hearBeatTime) {
if (heartBeatCons.size() < MAX_CONS_IN_ONE_CHECK) {
checkListItor.remove();
// Heart beat check
con.setBorrowed(true);
heartBeatCons.add(con);
}
}
} else if (con.getLastTime() < hearBeatTime2) {
{// not valid schema conntion should close for idle
// exceed 2*conHeartBeatPeriod
checkListItor.remove();
con.close(" heart beate idle ");
}
}
}
}
public void heatBeatCheck(long timeout, long conHeartBeatPeriod) {
int ildeCloseCount = hostConfig.getMinCon() * 3;
int MAX_CONS_IN_ONE_CHECK = 5;
LinkedList<BackendConnection> heartBeatCons = new LinkedList<BackendConnection>();
long hearBeatTime = TimeUtil.currentTimeMillis() - conHeartBeatPeriod;
long hearBeatTime2 = TimeUtil.currentTimeMillis() - 2
* conHeartBeatPeriod;
for (ConQueue queue : conMap.getAllConQueue()) {
checkIfNeedHeartBeat(heartBeatCons, queue,
queue.getAutoCommitCons(), hearBeatTime, hearBeatTime2);
if (heartBeatCons.size() < MAX_CONS_IN_ONE_CHECK) {
checkIfNeedHeartBeat(heartBeatCons, queue,
queue.getManCommitCons(), hearBeatTime, hearBeatTime2);
}
if (heartBeatCons.size() >= MAX_CONS_IN_ONE_CHECK) {
break;
}
}
if (!heartBeatCons.isEmpty()) {
for (BackendConnection con : heartBeatCons) {
conHeartBeatHanler
.doHeartBeat(con, hostConfig.getHearbeatSQL());
}
}
// check if there has timeouted heatbeat cons
conHeartBeatHanler.abandTimeOuttedConns();
int idleCons = getIdleCount();
int activeCons = this.getActiveCount();
int createCount = (hostConfig.getMinCon() - idleCons) / 3;
// create if idle too little
if ((createCount > 0) && (idleCons + activeCons < size)
&& (idleCons < hostConfig.getMinCon())) {
LOGGER.info("create connections ,because idle connection not enough ,cur is "
+ idleCons
+ ", minCon is "
+ hostConfig.getMinCon()
+ " for " + name);
NewConnectionRespHandler simpleHandler = new NewConnectionRespHandler();
final String[] schemas = dbPool.getSchemas();
for (int i = 0; i < createCount; i++) {
if (this.getActiveCount() + this.getIdleCount() >= size) {
break;
}
try {
// creat new connection
this.createNewConnection(simpleHandler, null, schemas[i
% schemas.length]);
} catch (IOException e) {
LOGGER.warn("create connection err " + e);
}
}
} else if (getIdleCount() > hostConfig.getMinCon() + ildeCloseCount) {
LOGGER.info("too many ilde cons ,close some for datasouce " + name);
ArrayList<BackendConnection> readyCloseCons = new ArrayList<BackendConnection>(
ildeCloseCount);
for (ConQueue queue : conMap.getAllConQueue()) {
readyCloseCons.addAll(queue.getIdleConsToClose(ildeCloseCount));
if (readyCloseCons.size() >= ildeCloseCount) {
break;
}
}
for (BackendConnection idleCon : readyCloseCons) {
if (idleCon.isBorrowed()) {
LOGGER.warn("find idle con is using " + idleCon);
}
idleCon.close("too many idle con");
}
} else {
int activeCount = this.getActiveCount();
if (activeCount > size) {
StringBuilder s = new StringBuilder();
s.append(Alarms.DEFAULT).append("DATASOURCE EXCEED [name=")
.append(name).append(",active=");
s.append(activeCount).append(",size=").append(size).append(']');
LOGGER.warn(s.toString());
}
}
}
public int getActiveCount() {
return this.conMap.getActiveCountForDs(this);
}
public void clearCons(String reason) {
this.conMap.clearConnections(reason, this);
}
private BackendConnection takeCon(BackendConnection conn,
final ResponseHandler handler, final Object attachment,
String schema) {
conn.setBorrowed(true);
if (!conn.getSchema().equals(schema)) {
// need do schema syn in before sql send
conn.setSchema(schema);
}
ConQueue queue = conMap.getSchemaConQueue(schema);
queue.incExecuteCount();
// queue.incExecuteCount();
conn.setAttachment(attachment);
handler.connectionAcquired(conn);
return conn;
}
private void createNewConnection(final ResponseHandler handler,
final Object attachment, final String schema) throws IOException {
// aysn create connection
MycatSystem.getInstance().getBusinessExecutor().execute(new Runnable() {
public void run() {
try {
createNewConnection(new DelegateResponseHandler(handler) {
@Override
public void connectionError(Throwable e,
BackendConnection conn) {
handler.connectionError(e, conn);
}
@Override
public void connectionAcquired(BackendConnection conn) {
takeCon(conn, handler, attachment, schema);
}
}, schema);
} catch (IOException e) {
handler.connectionError(e, null);
}
}
});
}
public void getConnection(final ConnectionMeta conMeta,
final ResponseHandler handler, final Object attachment)
throws Exception {
BackendConnection con = this.conMap.tryTakeCon(conMeta);
if (con != null) {
takeCon(con, handler, attachment, conMeta.getSchema());
return;
} else {
LOGGER.info("not ilde connection in pool,create new connection for "
+ this.name + conMeta.toString());
// create connection
createNewConnection(handler, attachment, conMeta.getSchema());
}
}
private void returnCon(BackendConnection c) {
c.setAttachment(null);
c.setBorrowed(false);
c.setLastTime(TimeUtil.currentTimeMillis());
ConQueue queue = this.conMap.getSchemaConQueue(c.getSchema());
boolean ok = false;
if (c.isAutocommit()) {
ok = queue.getAutoCommitCons().offer(c);
} else {
ok = queue.getManCommitCons().offer(c);
}
if (!ok) {
LOGGER.warn("can't return to pool ,so close con " + c);
c.close("can't return to pool ");
}
}
public void releaseChannel(BackendConnection c) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("release channel " + c);
}
// release connection
returnCon(c);
}
public void connectionClosed(BackendConnection conn) {
ConQueue queue = this.conMap.getSchemaConQueue(conn.getSchema());
if (queue != null) {
queue.removeCon(conn);
}
}
public abstract void createNewConnection(ResponseHandler handler,
String schema) throws IOException;
public long getHeartbeatRecoveryTime() {
return heartbeatRecoveryTime;
}
public void setHeartbeatRecoveryTime(long heartbeatRecoveryTime) {
this.heartbeatRecoveryTime = heartbeatRecoveryTime;
}
public DBHostConfig getConfig() {
return config;
}
}