Drizzled Public API Documentation

zeromq_log.cc
00001 /* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2011 Marcus Eriksson
00005  *
00006  *  Authors:
00007  *
00008  *  Marcus Eriksson <krummas@gmail.com>
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU General Public License
00021  *  along with this program; if not, write to the Free Software
00022  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00023  */
00024 
00025 #include <config.h>
00026 #include "zeromq_log.h"
00027 #include <drizzled/message/transaction.pb.h>
00028 #include <google/protobuf/io/coded_stream.h>
00029 #include <stdio.h>
00030 #include <drizzled/module/registry.h>
00031 #include <drizzled/plugin.h>
00032 #include <stdint.h>
00033 #include <boost/program_options.hpp>
00034 #include <drizzled/module/option_map.h>
00035 #include <zmq.h>
00036 
00037 namespace po= boost::program_options;
00038 
00039 using namespace std;
00040 using namespace drizzled;
00041 using namespace google;
00042 
00043 namespace drizzle_plugin
00044 {
00045 
00046 ZeroMQLog::ZeroMQLog(const string &name, const string &endpoint) :
00047   plugin::TransactionApplier(name)
00048 {
00049   void *context= zmq_init(1);
00050   _socket= zmq_socket (context, ZMQ_PUB);
00051   assert (_socket);
00052   int rc= zmq_bind (_socket, endpoint.c_str());
00053   assert (rc == 0);
00054   pthread_mutex_init(&publishLock, NULL);
00055 }
00056 
00057 ZeroMQLog::~ZeroMQLog()
00058 {
00059   zmq_close(_socket);
00060   pthread_mutex_destroy(&publishLock);
00061 }
00062 
00063 plugin::ReplicationReturnCode
00064 ZeroMQLog::apply(Session &, const message::Transaction &to_apply)
00065 {
00066   size_t message_byte_length= to_apply.ByteSize();
00067   uint8_t* buffer= new uint8_t[message_byte_length];
00068   if(buffer == NULL)
00069   {
00070     errmsg_printf(error::ERROR, _("Failed to allocate enough memory to transaction message\n"));
00071     deactivate();
00072     return plugin::UNKNOWN_ERROR;
00073   }
00074 
00075   string schema= getSchemaName(to_apply);
00076   zmq_msg_t schemamsg;
00077   int rc= zmq_msg_init_size(&schemamsg, schema.length());
00078   memcpy(zmq_msg_data(&schemamsg), schema.c_str(), schema.length());
00079 
00080   to_apply.SerializeWithCachedSizesToArray(buffer);
00081   zmq_msg_t msg;
00082   rc= zmq_msg_init_size(&msg, message_byte_length);
00083   assert (rc == 0);
00084   memcpy(zmq_msg_data(&msg), buffer, message_byte_length);
00085 
00086   // need a mutex around this since several threads can call this method at the same time
00087   pthread_mutex_lock(&publishLock);
00088   rc= zmq_send(_socket, &schemamsg, ZMQ_SNDMORE);
00089   rc= zmq_send(_socket, &msg, 0);
00090   pthread_mutex_unlock(&publishLock);
00091 
00092   zmq_msg_close(&msg);
00093   zmq_msg_close(&schemamsg);
00094   delete[] buffer;
00095   return plugin::SUCCESS;
00096 }
00097 
00098 string ZeroMQLog::getSchemaName(const message::Transaction &txn) {
00099   if(txn.statement_size() == 0) return "";
00100 
00101   const message::Statement &statement= txn.statement(0);
00102 
00103   switch(statement.type())
00104   {
00105   case message::Statement::INSERT:
00106     return statement.insert_header().table_metadata().schema_name();
00107   case message::Statement::UPDATE:
00108     return statement.update_header().table_metadata().schema_name();
00109   case message::Statement::DELETE:
00110     return statement.delete_header().table_metadata().schema_name();
00111   case message::Statement::CREATE_TABLE:
00112     return statement.create_table_statement().table().schema();
00113   case message::Statement::TRUNCATE_TABLE:
00114     return statement.truncate_table_statement().table_metadata().schema_name();
00115   case message::Statement::DROP_TABLE:
00116     return statement.drop_table_statement().table_metadata().schema_name();
00117   case message::Statement::CREATE_SCHEMA:
00118     return statement.create_schema_statement().schema().name();
00119   case message::Statement::DROP_SCHEMA:
00120     return statement.drop_schema_statement().schema_name();
00121     default:
00122     return "";
00123   }
00124 }
00125 
00126 static ZeroMQLog *zeromqLogger; 
00127 
00131 static int init(drizzled::module::Context &context)
00132 {
00133   const module::option_map &vm= context.getOptions();
00134   zeromqLogger= new ZeroMQLog("zeromq_log_applier", vm["endpoint"].as<string>());
00135   context.add(zeromqLogger);
00136   ReplicationServices::attachApplier(zeromqLogger, vm["use-replicator"].as<string>());
00137   context.registerVariable(new sys_var_const_string_val("endpoint", vm["endpoint"].as<string>()));
00138   return 0;
00139 }
00140 
00141 
00142 static void init_options(drizzled::module::option_context &context)
00143 {
00144   context("endpoint", 
00145           po::value<string>()->default_value("tcp://*:9999"),
00146           _("End point to bind to"));
00147   context("use-replicator",
00148           po::value<string>()->default_value("default_replicator"),
00149           _("Name of the replicator plugin to use (default='default_replicator')"));
00150 
00151 }
00152 
00153 } /* namespace drizzle_plugin */
00154 
00155 DRIZZLE_PLUGIN(drizzle_plugin::init, NULL, drizzle_plugin::init_options);