package fm.liu.timo.mysql.handler.xa; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import org.pmw.tinylog.Logger; import fm.liu.messenger.Mail; import fm.liu.timo.TimoServer; import fm.liu.timo.backend.Node; import fm.liu.timo.mysql.connection.MySQLConnection; import fm.liu.timo.mysql.packet.CommandPacket; import fm.liu.timo.mysql.packet.ErrorPacket; import fm.liu.timo.mysql.packet.RowDataPacket; import fm.liu.timo.net.connection.BackendConnection; import fm.liu.timo.server.session.handler.SessionResultHandler; /** * @author liuhuanting */ public class XARecoverHandler extends SessionResultHandler { private Map<Integer, Node> nodes; private HashMap<String, ConcurrentHashMap<Integer, Boolean>> logResult; private volatile HashMap<String, HashSet<Integer>> dbResult; private volatile AtomicInteger recoverCount; public XARecoverHandler(HashMap<String, ConcurrentHashMap<Integer, Boolean>> recoveryLog, Map<Integer, Node> nodes) { super.count = new AtomicInteger(nodes.size()); this.nodes = nodes; this.logResult = recoveryLog; this.dbResult = new HashMap<>(); } public XARecoverHandler(int size) { super.count = new AtomicInteger(size); } @Override public void ok(byte[] ok, BackendConnection con) { if (decrement()) { if (this.failed()) { error(); } TimoServer.getSender() .send(new Mail<String>(TimoServer.getInstance().getStarter(), "start")); } } @Override public void error(byte[] error, BackendConnection con) { ErrorPacket err = new ErrorPacket(); err.read(error); String message = new String(err.message); Logger.warn("error :{} received from :{} when RECOVER", con, message); this.setFail(err.errno, message); if (decrement()) { error(); } } private void error() { System.exit(-1); } @Override public void field(byte[] header, List<byte[]> fields, byte[] eof, BackendConnection con) {} @Override public void row(byte[] row, BackendConnection con) { RowDataPacket packet = new RowDataPacket(4); packet.read(row); String data = new String(packet.fieldValues.get(3)); this.recoverCount.incrementAndGet(); if (dbResult.containsKey(data)) { dbResult.get(data).add(con.getDatanodeID()); } else { HashSet<Integer> set = new HashSet<>(); set.add(con.getDatanodeID()); dbResult.put(data, set); } } @Override public void eof(byte[] eof, BackendConnection con) { con.release(); if (decrement()) { if (this.failed()) { error(); } else { if (this.recoverCount == null) { TimoServer.getSender() .send(new Mail<String>(TimoServer.getInstance().getStarter(), "start")); } else { XARecoverHandler handler = new XARecoverHandler(this.recoverCount.get()); for (String xid : dbResult.keySet()) { HashSet<Integer> recover = dbResult.get(xid); if (logResult.containsKey(xid)) { recover.parallelStream().forEach(i -> { MySQLConnection conn = (MySQLConnection) nodes.get(i).getSource().notNullGet(); conn.setResultHandler(handler); CommandPacket packet = new CommandPacket(CommandPacket.COM_QUERY); packet.arg = ("XA COMMIT '" + xid + "'").getBytes(); packet.write(conn); }); } else { recover.parallelStream().forEach(i -> { MySQLConnection conn = (MySQLConnection) nodes.get(i).getSource().notNullGet(); conn.setResultHandler(handler); CommandPacket packet = new CommandPacket(CommandPacket.COM_QUERY); packet.arg = ("XA ROLLBACK '" + xid + "'").getBytes(); packet.write(conn); }); } } } } } } }