/* * Copyright 2015 Liu Huanting. * * 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 fm.liu.timo.mysql.handler; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import fm.liu.messenger.Mail; import fm.liu.timo.TimoServer; import fm.liu.timo.config.ErrorCode; import fm.liu.timo.config.model.Datasource; import fm.liu.timo.merger.ColumnInfo; import fm.liu.timo.merger.Merger; import fm.liu.timo.mysql.connection.MySQLConnection; import fm.liu.timo.mysql.packet.ErrorPacket; import fm.liu.timo.mysql.packet.FieldPacket; import fm.liu.timo.mysql.packet.OkPacket; import fm.liu.timo.mysql.packet.RowDataPacket; import fm.liu.timo.net.connection.BackendConnection; import fm.liu.timo.server.ServerConnection; import fm.liu.timo.server.session.Session; import fm.liu.timo.server.session.TransactionSession; import fm.liu.timo.server.session.handler.SessionResultHandler; import fm.liu.timo.statistic.SQLRecord; import fm.liu.timo.util.TimeUtil; /** * @author Liu Huanting 2015年5月9日 */ public class MultiNodeHandler extends SessionResultHandler { protected long affectedRows = 0; protected long insertId = 0; protected boolean returned = false; protected Merger merger; protected String sql; public MultiNodeHandler(Session session, Merger merger, int size) { super.session = session; super.count = new AtomicInteger(size); this.merger = merger; } @Override public void ok(byte[] data, BackendConnection con) { record(con); session.release(con); if (failed()) { if (decrement()) { onError(); } return; } OkPacket ok = new OkPacket(); ok.read(data); lock.lock(); try { affectedRows += ok.affectedRows; if (ok.insertId > 0) { insertId = (insertId == 0) ? ok.insertId : Math.min(insertId, ok.insertId); } } finally { lock.unlock(); } if (decrement()) { ok.packetId = ++packetId; ok.affectedRows = affectedRows; if (insertId > 0) { ok.insertId = insertId; } if (session instanceof TransactionSession) { ((TransactionSession) session).savepoint(ok); } else { ok.write(session.getFront()); } } } @Override public void error(byte[] data, BackendConnection con) { session.release(con); ErrorPacket err = new ErrorPacket(); err.read(data); String errmsg = new String(err.message); setFail(err.errno, errmsg); if (decrement()) { onError(); } } @Override public void field(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection con) { if (failed()) { return; } lock.lock(); allocBuffer(); try { if (returned) { return; } returned = true; Map<String, ColumnInfo> columnInfos = new HashMap<String, ColumnInfo>(); header[3] = ++packetId; ServerConnection front = session.getFront(); buffer = front.writeToBuffer(header, allocBuffer()); int fieldCount = fields.size(); for (int i = 0, len = fieldCount; i < len; ++i) { byte[] field = fields.get(i); FieldPacket packet = new FieldPacket(); packet.read(field); String column = new String(packet.name).toUpperCase(); columnInfos.put(column, new ColumnInfo(i, packet.type)); field[3] = ++packetId; buffer = front.writeToBuffer(field, buffer); } merger.init(columnInfos, fieldCount); eof[3] = ++packetId; buffer = front.writeToBuffer(eof, buffer); } catch (Exception e) { errMsg = e.getMessage(); onError(); setFail(ErrorCode.ER_YES, errMsg); session.release(con); } finally { lock.unlock(); } } @Override public void row(byte[] row, BackendConnection con) { if (failed()) { return; } lock.lock(); try { merger.offer(row); } finally { lock.unlock(); } } @Override public void eof(byte[] eof, BackendConnection con) { record(con); session.release(con); if (decrement()) { if (failed()) { onError(); return; } ServerConnection front = session.getFront(); Iterator<RowDataPacket> itor = merger.getResult().iterator(); int start = merger.getOutlets().getLimitOffset(); int end = start + merger.getOutlets().getLimitSize(); if (end == -1) { while (itor.hasNext()) { RowDataPacket row = itor.next(); itor.remove(); row.packetId = ++packetId; buffer = row.write(buffer, front); } } else { int i = 0; while (itor.hasNext()) { RowDataPacket row = itor.next(); itor.remove(); if (i < start) { i++; continue; } else if (i == end) { break; } row.packetId = ++packetId; buffer = row.write(buffer, front); i++; } } eof[3] = ++packetId; buffer = front.writeToBuffer(eof, allocBuffer()); front.write(buffer); } } private void record(BackendConnection con) { long lastActiveTime = con.getVariables().getLastActiveTime(); Datasource source = ((MySQLConnection) con).getDatasource().getConfig(); TimoServer.getSender() .send(new Mail<SQLRecord>(TimoServer.getRecorder(), new SQLRecord(source.getHost(), source.getDB(), sql, lastActiveTime, TimeUtil.currentTimeMillis() - lastActiveTime, source.getDatanodeID()))); } @Override public void close(String reason) { if (decrement()) { super.errMsg = reason; onError(); } } @Override public void setSQL(String sql) { this.sql = sql; } }