com.github.brandtg.switchboard.MysqlReplicationApplier.java Source code

Java tutorial

Introduction

Here is the source code for com.github.brandtg.switchboard.MysqlReplicationApplier.java

Source

/**
 * Copyright (C) 2015 Greg Brandt (brandt.greg@gmail.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.github.brandtg.switchboard;

import com.google.code.or.binlog.BinlogEventParser;
import com.google.code.or.binlog.BinlogEventV4;
import com.google.code.or.binlog.impl.event.FormatDescriptionEvent;
import com.google.code.or.binlog.impl.event.GtidEvent;
import com.google.code.or.binlog.impl.event.QueryEvent;
import com.google.code.or.binlog.impl.event.RawEvent;
import com.google.code.or.binlog.impl.event.XidEvent;
import com.google.code.or.binlog.impl.parser.FormatDescriptionEventParser;
import com.google.code.or.binlog.impl.parser.GtidEventParser;
import com.google.code.or.binlog.impl.parser.QueryEventParser;
import com.google.code.or.binlog.impl.parser.RawEventParser;
import com.google.code.or.binlog.impl.parser.XidEventParser;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sql.DataSource;

public class MysqlReplicationApplier implements Runnable {
    private static final Logger LOG = LoggerFactory.getLogger(MysqlReplicationApplier.class);
    private static final Joiner SPACE = Joiner.on(" ");

    private final InputStream inputStream;
    private final DataSource dataSource;
    private final AtomicBoolean isShutdown;

    /**
     * Applies a stream of MySQL binary log events to a MySQL instance.
     *
     * @param inputStream
     *  An input stream containing raw MySQL binary log.
     * @param dataSource
     *  A data source for the database to which replicated events are to be applied.
     */
    public MysqlReplicationApplier(InputStream inputStream, DataSource dataSource) {
        this.inputStream = inputStream;
        this.dataSource = dataSource;
        this.isShutdown = new AtomicBoolean(false);
    }

    /** Shuts down this applier. */
    public void shutdown() {
        if (!isShutdown.getAndSet(true)) {
            LOG.info("Shut down MySQL replication applier");
        }
    }

    @Override
    public void run() {
        while (!isShutdown.get()) {
            List<String> sqls = new ArrayList<>();
            List<String> rawSqls = new ArrayList<>();
            try {
                Collection<BinlogEventParser> eventParsers = ImmutableSet.of(
                        (BinlogEventParser) new QueryEventParser(), (BinlogEventParser) new GtidEventParser(),
                        (BinlogEventParser) new XidEventParser(),
                        (BinlogEventParser) new FormatDescriptionEventParser());
                MysqlLogParser logParser = new MysqlLogParser(inputStream, eventParsers, new RawEventParser());
                Iterator<BinlogEventV4> itr = new MysqlLogIterator(logParser);
                while (itr.hasNext()) {
                    BinlogEventV4 event = itr.next();

                    if (event instanceof FormatDescriptionEvent) {
                        FormatDescriptionEvent formatDescriptionEvent = (FormatDescriptionEvent) event;
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        baos.write(formatDescriptionEvent.getHeader().getBytes());
                        baos.write(formatDescriptionEvent.getBytes());
                        byte[] bytes = baos.toByteArray();
                        String binlogSql = String.format("BINLOG '%s'", Base64.encodeBase64String(bytes));
                        try (Connection conn = dataSource.getConnection();
                                Statement stmt = conn.createStatement()) {
                            stmt.execute("ROLLBACK");
                            stmt.execute(binlogSql);
                            LOG.info("Executed FormatDescriptionEvent SQL: {}", binlogSql);
                        }
                    } else if (event instanceof GtidEvent) {
                        GtidEvent gtidEvent = (GtidEvent) event;
                        sqls.clear();
                        rawSqls.clear();
                        sqls.add(String.format("SET @@SESSION.GTID_NEXT = '%s:%d'", gtidEvent.getSourceIdAsUuid(),
                                gtidEvent.getTransactionId()));
                        sqls.add(String.format("SET TIMESTAMP=%d", gtidEvent.getHeader().getTimestamp() / 1000));
                    } else if (event instanceof QueryEvent) {
                        QueryEvent queryEvent = (QueryEvent) event;
                        sqls.add(queryEvent.getSql().toString());
                    } else if (event instanceof RawEvent) {
                        RawEvent rawEvent = (RawEvent) event;
                        rawSqls.add(Base64.encodeBase64String(rawEvent.getBytesWithHeader()));
                    } else if (event instanceof XidEvent) {
                        try (Connection conn = dataSource.getConnection();
                                Statement stmt = conn.createStatement()) {
                            for (String sql : sqls) {
                                stmt.execute(sql);
                            }
                            String joinedRaw = SPACE.join(rawSqls);
                            String binlogSql = String.format("BINLOG '\n%s\n'", joinedRaw);
                            stmt.execute(binlogSql);
                            stmt.execute("COMMIT");
                        }
                        LOG.info("Last GTID statement: {}", sqls.get(0));
                    }
                }
            } catch (Exception e) {
                LOG.error("Error in replication applier", e);
                throw new RuntimeException(e);
            }
        }
    }
}