LCOV - code coverage report
Current view: top level - root/contrail/src/contrail-analytics/contrail-query-engine - QEOpServerProxy.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 633 763 83.0 %
Date: 2026-11-09 02:10:49 Functions: 29 30 96.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include <atomic>
       7             : #include <cstdlib>
       8             : #include <cerrno>
       9             : #include <utility>
      10             : #include <list>
      11             : #include <mutex>
      12             : 
      13             : #include <boost/bind/bind.hpp>
      14             : #include <boost/asio.hpp>
      15             : #include <boost/tuple/tuple.hpp>
      16             : #include "base/util.h"
      17             : #include "base/logging.h"
      18             : #include "base/address_util.h"
      19             : #include "hiredis/hiredis.h"
      20             : #include "hiredis/hiredis_ssl.h"
      21             : #include "hiredis/boostasio.hpp"
      22             : #include <contrail-collector/redis_connection.h>
      23             : #include "base/work_pipeline.h"
      24             : #include "QEOpServerProxy.h"
      25             : #include "rapidjson/document.h"
      26             : #include "rapidjson/stringbuffer.h"
      27             : #include "rapidjson/writer.h"
      28             : #include "query.h"
      29             : #include "stats_query.h"
      30             : #include "analytics_types.h"
      31             : #include "stats_select.h"
      32             : #include <base/connection_info.h>
      33             : 
      34             : using std::list;
      35             : using std::string;
      36             : using std::vector;
      37             : using std::map;
      38             : using boost::assign::list_of;
      39             : using boost::ptr_map;
      40             : using boost::nullable;
      41             : using boost::tuple;
      42             : using boost::shared_ptr;
      43             : using boost::scoped_ptr;
      44             : using std::pair;
      45             : using std::unique_ptr;
      46             : using std::make_pair;
      47             : using process::ConnectionState;
      48             : using process::ConnectionType;
      49             : using process::ConnectionStatus;
      50             : using process::Endpoint;
      51             : using namespace boost::placeholders;
      52             : 
      53             : extern RedisAsyncConnection * rac_alloc(EventManager *, const std::string & ,unsigned short,
      54             : RedisAsyncConnection::ClientConnectCbFn ,
      55             : RedisAsyncConnection::ClientDisconnectCbFn,
      56             : const bool, const std::string &, const std::string &, const std::string &);
      57             : 
      58             : extern RedisAsyncConnection * rac_alloc_nocheck(EventManager *, const std::string & ,unsigned short,
      59             : RedisAsyncConnection::ClientConnectCbFn ,
      60             : RedisAsyncConnection::ClientDisconnectCbFn,
      61             : const bool, const std::string &, const std::string &, const std::string &);
      62             : 
      63             : SandeshTraceBufferPtr QeTraceBuf(SandeshTraceBufferCreate(QE_TRACE_BUF, 10000));
      64             : 
      65             : struct RawResultT {
      66             :     QEOpServerProxy::QPerfInfo perf;
      67             :     shared_ptr<QEOpServerProxy::BufferT> res;
      68             :     shared_ptr<QEOpServerProxy::OutRowMultimapT>  mres;
      69             :     shared_ptr<WhereResultT> wres;
      70             : };
      71             : 
      72             : typedef pair<redisReply,vector<string> > RedisT;
      73             : 
      74        3408 : bool RedisAsyncArgCommand(RedisAsyncConnection * rac,
      75             :             void *rpi, const vector<string>& args) {
      76             : 
      77        3408 :     return rac->RedisAsyncArgCmd(rpi, args);
      78             : }
      79             : 
      80             : class QEOpServerProxy::QEOpServerImpl {
      81             : public:
      82             :     typedef std::vector<std::string> QEOutputT;
      83             : 
      84             :     struct Input {
      85             :         int redis_host_idx;
      86             :         int cnum;
      87             :         string hostname;
      88             :         QueryEngine::QueryParams qp;
      89             :         vector<uint64_t> chunk_size;
      90             :         uint32_t wterms;
      91             :         bool need_merge;
      92             :         bool map_output;
      93             :         string where;
      94             :         string select;
      95             :         string post;
      96             :         uint64_t time_period;
      97             :         string table;
      98             :         uint32_t max_rows;
      99             :         std::atomic<uint32_t> chunk_q;
     100             :         std::atomic<uint32_t> total_rows;
     101             : 
     102             :         // to handle absent operator= for std::atomic
     103        1251 :         Input& operator=(const Input& other) {
     104        1251 :             if (this != &other) {
     105        1251 :                 redis_host_idx = other.redis_host_idx;
     106        1251 :                 cnum = other.cnum;
     107        1251 :                 hostname = other.hostname;
     108        1254 :                 qp = other.qp;
     109        1227 :                 chunk_size = other.chunk_size;
     110        1229 :                 wterms = other.wterms;
     111        1229 :                 need_merge = other.need_merge;
     112        1229 :                 map_output = other.map_output;
     113        1229 :                 where = other.where;
     114        1235 :                 select = other.select;
     115        1250 :                 post = other.post;
     116        1253 :                 time_period = other.time_period;
     117        1253 :                 table = other.table;
     118        1257 :                 max_rows = other.max_rows;
     119             :                 // these two are handled only in QueryExec and are not used in next stages - don't copy they
     120        1257 :                 chunk_q = 0;
     121        1257 :                 total_rows = 0;
     122             :             }
     123        1259 :             return *this;
     124             :         }
     125             :     };
     126             : 
     127        3914 :     void JsonInsert(std::vector<query_column> &columns,
     128             :             contrail_rapidjson::Document& dd,
     129             :             std::pair<const string,string> * map_it) {
     130             : 
     131        3914 :         bool found = false;
     132       42568 :         for (size_t j = 0; j < columns.size(); j++)
     133             :         {
     134       38654 :             if ((0 == map_it->first.compare(0,5,string("COUNT")))) {
     135           0 :                 contrail_rapidjson::Value val(contrail_rapidjson::kNumberType);
     136           0 :                 unsigned long num = 0;
     137           0 :                 stringToInteger(map_it->second, num);
     138           0 :                 val.SetUint64(num);
     139           0 :                 contrail_rapidjson::Value vk;
     140           0 :                 dd.AddMember(vk.SetString(map_it->first.c_str(), dd.GetAllocator()), val, dd.GetAllocator());
     141           0 :                 found = true;
     142       38654 :             } else if (columns[j].name == map_it->first) {
     143        3914 :                 if (map_it->second.length() == 0) {
     144          12 :                     contrail_rapidjson::Value val(contrail_rapidjson::kNullType);
     145          12 :                     contrail_rapidjson::Value vk;
     146          12 :                     dd.AddMember(vk.SetString(map_it->first.c_str(), dd.GetAllocator()), val, dd.GetAllocator());
     147          12 :                     found = true;
     148          12 :                     continue;
     149          12 :                 }
     150             : 
     151             :                 // find out type and convert
     152        4838 :                 if (columns[j].datatype == "string" ||
     153         936 :                     columns[j].datatype == "uuid")
     154             :                 {
     155        2966 :                     contrail_rapidjson::Value val(contrail_rapidjson::kStringType);
     156        2966 :                     val.SetString(map_it->second.c_str(), dd.GetAllocator());
     157        2966 :                     contrail_rapidjson::Value vk;
     158        2966 :                     dd.AddMember(vk.SetString(map_it->first.c_str(), dd.GetAllocator()), val, dd.GetAllocator());
     159        3902 :                 } else if (columns[j].datatype == "ipaddr") {
     160           0 :                     contrail_rapidjson::Value val(contrail_rapidjson::kStringType);
     161           0 :                    val.SetString(map_it->second.c_str(),
     162             :                                  dd.GetAllocator());
     163           0 :                     contrail_rapidjson::Value vk;
     164           0 :                     dd.AddMember(vk.SetString(map_it->first.c_str(), dd.GetAllocator()), val, dd.GetAllocator());
     165             : 
     166         936 :                 } else if (columns[j].datatype == "double") {
     167           0 :                     contrail_rapidjson::Value val(contrail_rapidjson::kNumberType);
     168           0 :                     double dval = (double) strtod(map_it->second.c_str(), NULL);
     169           0 :                     val.SetDouble(dval);
     170           0 :                     contrail_rapidjson::Value vk;
     171           0 :                     dd.AddMember(vk.SetString(map_it->first.c_str(), dd.GetAllocator()), val, dd.GetAllocator());
     172           0 :                 } else {
     173         936 :                     contrail_rapidjson::Value val(contrail_rapidjson::kNumberType);
     174         936 :                     unsigned long num = 0;
     175         936 :                     stringToInteger(map_it->second, num);
     176         936 :                     val.SetUint64(num);
     177         936 :                     contrail_rapidjson::Value vk;
     178         936 :                     dd.AddMember(vk.SetString(map_it->first.c_str(), dd.GetAllocator()), val, dd.GetAllocator());
     179         936 :                 }
     180        3902 :                 found = true;
     181             :             }
     182             :         }
     183        3914 :         assert(found);
     184        3914 :     }
     185             : 
     186         126 :     void QueryJsonify(const string& table, bool map_output,
     187             :         const BufferT* raw_res, const OutRowMultimapT* raw_mres, QEOutputT* raw_json) {
     188             : 
     189         126 :         vector<OutRowT>::iterator res_it;
     190             : 
     191         126 :         std::vector<query_column>  columns;
     192             : 
     193         126 :         if (!table.size()) return;
     194         126 :         bool found = false;
     195         882 :         for(size_t i = 0; i < g_viz_constants._TABLES.size(); i++)
     196             :         {
     197         756 :             if (g_viz_constants._TABLES[i].name == table) {
     198          59 :                 found = true;
     199          59 :                 columns = g_viz_constants._TABLES[i].schema.columns;
     200             :             }
     201             :         }
     202         126 :         if (!found) {
     203          67 :             found = true;
     204          67 :             columns = g_viz_constants._OBJECT_TABLE_SCHEMA.columns;
     205             :         }
     206         126 :         assert(found || map_output);
     207             : 
     208         126 :         if (map_output) {
     209          70 :             OutRowMultimapT::const_iterator mres_it;
     210         264 :             for (mres_it = raw_mres->begin(); mres_it != raw_mres->end(); ++mres_it) {
     211         194 :                 string jstr;
     212         194 :                 StatsSelect::Jsonify(table, mres_it->second.first, mres_it->second.second, jstr);
     213         194 :                 raw_json->push_back(jstr);
     214         194 :             }
     215             :         } else {
     216          56 :             QEOpServerProxy::BufferT* raw_result =
     217             :                 const_cast<QEOpServerProxy::BufferT*>(raw_res);
     218          56 :             QEOpServerProxy::BufferT::iterator res_it;
     219        3152 :             for (res_it = raw_result->begin(); res_it != raw_result->end(); ++res_it) {
     220        3096 :                 std::map<std::string, std::string>::iterator map_it;
     221        3096 :                 contrail_rapidjson::Document dd;
     222        3096 :                 dd.SetObject();
     223             : 
     224        3096 :                 for (map_it = (*res_it).first.begin();
     225        7010 :                      map_it != (*res_it).first.end(); ++map_it) {
     226             :                     // search for column name in the schema
     227        3914 :                     JsonInsert(columns, dd, &(*map_it));
     228             :                 }
     229        3096 :                 contrail_rapidjson::StringBuffer sb;
     230        3096 :                 contrail_rapidjson::Writer<contrail_rapidjson::StringBuffer> writer(sb);
     231        3096 :                 dd.Accept(writer);
     232        3096 :                 raw_json->push_back(sb.GetString());
     233        3096 :             }
     234             :         }
     235         126 :     }
     236             : 
     237             : 
     238         904 :     void QECallback(void * qid, QPerfInfo qperf,
     239             :             unique_ptr<std::vector<query_result_unit_t> > res) {
     240             : 
     241         904 :         RawResultT* raw(new RawResultT);
     242         904 :         raw->perf = qperf;
     243         904 :         raw->wres = std::move(res);
     244             : 
     245         904 :         ExternalProcIf<RawResultT> * rpi = NULL;
     246         904 :         if (qid)
     247         904 :             rpi = reinterpret_cast<ExternalProcIf<RawResultT> *>(qid);
     248             : 
     249         904 :         if (rpi) {
     250         904 :             unique_ptr<RawResultT> rp(raw);
     251         904 :             rpi->Response(std::move(rp));
     252         904 :         }
     253         904 :     }
     254             : 
     255         856 :     void QECallback(void * qid, QPerfInfo qperf, unique_ptr<QEOpServerProxy::BufferT> res,
     256             :             unique_ptr<QEOpServerProxy::OutRowMultimapT> mres) {
     257             : 
     258         856 :         RawResultT* raw(new RawResultT);
     259         856 :         raw->perf = qperf;
     260         856 :         raw->res = std::move(res);
     261         856 :         raw->mres = std::move(mres);
     262             : 
     263         856 :         ExternalProcIf<RawResultT> * rpi = NULL;
     264         856 :         if (qid)
     265         856 :             rpi = reinterpret_cast<ExternalProcIf<RawResultT> *>(qid);
     266             : 
     267         856 :         if (rpi) {
     268         856 :             unique_ptr<RawResultT> rp(raw);
     269         856 :             rpi->Response(std::move(rp));
     270         856 :         }
     271         856 :     }
     272             : 
     273             :     struct Stage0Out {
     274             :         Input inp;
     275             :         bool ret_code;
     276             :         vector<QPerfInfo> ret_info;
     277             :         vector<uint32_t> chunk_merge_time;
     278             :         shared_ptr<BufferT> result;
     279             :         shared_ptr<OutRowMultimapT> mresult;
     280             :         vector<shared_ptr<WhereResultT> > welem;
     281             :         shared_ptr<WhereResultT> wresult;
     282             :         uint32_t current_chunk;
     283             :     };
     284             : 
     285        2737 :     ExternalBase::Efn QueryExec(uint32_t inst, const vector<RawResultT*> & exts,
     286             :             const Input & inp, Stage0Out & res) {
     287        2737 :         uint32_t step = exts.size();
     288             : 
     289        2749 :         if (!step) {
     290         988 :             res.inp = inp;
     291        1007 :             res.ret_code = true;
     292             : 
     293        1007 :             if (inp.map_output)
     294         560 :                 res.mresult = shared_ptr<OutRowMultimapT>(new OutRowMultimapT());
     295             :             else
     296         447 :                 res.result = shared_ptr<BufferT>(new BufferT());
     297             : 
     298         980 :             res.wresult = shared_ptr<WhereResultT>(new WhereResultT());
     299        2002 :             for (size_t or_idx=0; or_idx<inp.wterms; or_idx++) {
     300        1022 :                 shared_ptr<WhereResultT> ss;
     301        1022 :                 res.welem.push_back(ss);
     302        1026 :             }
     303             : 
     304         980 :             Input& cinp = const_cast<Input&>(inp);
     305         980 :             res.current_chunk = cinp.chunk_q.fetch_add(1);
     306         980 :             const uint32_t chunknum = res.current_chunk;
     307         980 :             if (chunknum >= inp.chunk_size.size()) {
     308         154 :                 return NULL;
     309             :             }
     310             : 
     311             :             // Update query status
     312         851 :             RedisAsyncConnection * rac = conns_[res.inp.redis_host_idx][res.inp.cnum].get();
     313         847 :             string rkey = "REPLY:" + res.inp.qp.qid;
     314             :             char stat[40];
     315         844 :             uint prg = 10 + (chunknum * 75)/inp.chunk_size.size();
     316         845 :             sprintf(stat,"{\"progress\":%d}", prg);
     317         827 :             RedisAsyncArgCommand(rac, NULL,
     318        1698 :                 list_of(string("RPUSH"))(rkey)(stat));
     319             : 
     320        1703 :             return boost::bind(&QueryEngine::QueryExecWhere, qosp_->qe_,
     321        1704 :                     _1, inp.qp, chunknum, 0);
     322         853 :         }
     323             : 
     324        1761 :         res.ret_info.push_back(exts[step-1]->perf);
     325        1760 :         if (exts[step-1]->perf.error) {
     326           0 :             res.ret_code =false;
     327             :         }
     328        1760 :         if (!res.ret_code) return NULL;
     329             : 
     330             :         // Number of substeps per chunk is the number of OR terms in WHERE
     331             :         // plus one more substep for select and post processing
     332        1760 :         uint32_t substep = step % (inp.wterms + 1);
     333             : 
     334        1760 :         if (substep == inp.wterms) {
     335             :             // Get the result of the final WHERE
     336         856 :             res.welem[substep-1] = exts[step-1]->wres;
     337             : 
     338             :             // The set "OR" API needs raw pointers
     339         855 :             vector<WhereResultT*> oterms;
     340        1759 :             for (size_t or_idx = 0; or_idx < inp.wterms; or_idx++) {
     341         903 :                 oterms.push_back(res.welem[or_idx].get());
     342             :             }
     343             : 
     344             :             // Do SET operations
     345         856 :             QE_ASSERT(res.wresult->size() == 0);
     346         856 :             SetOperationUnit::op_or(res.inp.qp.qid, *res.wresult, oterms);
     347             : 
     348        1760 :                 for (size_t or_idx=0; or_idx<inp.wterms; or_idx++) {
     349         904 :                 res.welem[or_idx].reset();
     350             :             }
     351             : 
     352             :             // Start the SELECT and POST-processing
     353        2568 :             return boost::bind(&QueryEngine::QueryExec, qosp_->qe_,
     354        1711 :                    _1, inp.qp, res.current_chunk, res.wresult.get());
     355             : 
     356        1760 :         } else if (substep == 0) {
     357             :             // A chunk is complete. Start another one
     358         856 :             res.wresult->clear();
     359             :             uint32_t added_rows;
     360             : 
     361         856 :             if (inp.need_merge) {
     362         448 :                 uint64_t then = UTCTimestampUsec();
     363         448 :                 if (inp.map_output) {
     364         416 :                     uint32_t base_rows = res.mresult->size();
     365             :                     // TODO: This interface should not be Stats-Specific
     366         416 :                     StatsSelect::Merge(std::string(""), *(exts[step-1]->mres), *(res.mresult));
     367             :                     // Some rows of this chunk will merge into existing results
     368         416 :                     added_rows = res.mresult->size() - base_rows;
     369             :                 } else {
     370          32 :                     uint32_t base_rows = res.result->size();
     371          32 :                     res.ret_code =
     372          32 :                         qosp_->qe_->QueryAccumulate(inp.qp,
     373          32 :                             *(exts[step-1]->res), *(res.result));
     374             :                     // Some rows of this chunk will merge into existing results
     375          32 :                     added_rows = res.result->size() - base_rows;
     376             :                 }
     377         896 :                 res.chunk_merge_time.push_back(
     378         448 :                     static_cast<uint32_t>((UTCTimestampUsec() - then)/1000));
     379             : 
     380             :             } else {
     381             :                 // TODO : When merge is not needed, we can just send
     382             :                 //        a result upto redis at this point.
     383             : 
     384         408 :                 if (inp.map_output) {
     385          41 :                     added_rows = exts[step-1]->mres->size();
     386          41 :                     OutRowMultimapT::iterator jt = res.mresult->begin();
     387          41 :                     for (OutRowMultimapT::const_iterator it = exts[step-1]->mres->begin();
     388          79 :                             it != exts[step-1]->mres->end(); it++ ) {
     389             : 
     390          76 :                         jt = res.mresult->insert(jt,
     391          76 :                                 std::make_pair(it->first, it->second));
     392             :                     }
     393             :                 } else {
     394         367 :                     added_rows = exts[step-1]->res->size();
     395        1101 :                     res.result->insert(res.result->begin(),
     396         367 :                         exts[step-1]->res->begin(),
     397         367 :                         exts[step-1]->res->end());
     398             :                 }
     399             :             }
     400         856 :             Input& cinp = const_cast<Input&>(inp);
     401        1712 :             if (cinp.total_rows.fetch_add(added_rows) + added_rows > cinp.max_rows) {
     402           0 :                 QE_LOG_NOQID(ERROR,  "QueryExec Max Rows Exceeded " <<
     403             :                     cinp.total_rows << " chunk " << cinp.chunk_q);
     404           0 :                 return NULL;
     405             :             }
     406             : 
     407         856 :                 res.current_chunk = cinp.chunk_q.fetch_add(1);
     408         856 :             const uint32_t chunknum = res.current_chunk;
     409         856 :             if (chunknum < inp.chunk_size.size()) {
     410           2 :                 string key = "QUERY:" + res.inp.qp.qid;
     411             : 
     412             :                 // Update query status
     413           2 :                 RedisAsyncConnection * rac = conns_[res.inp.redis_host_idx][res.inp.cnum].get();
     414           2 :                 string rkey = "REPLY:" + res.inp.qp.qid;
     415             :                 char stat[40];
     416           2 :                 uint prg = 10 + (chunknum * 75)/inp.chunk_size.size();
     417           2 :                 sprintf(stat,"{\"progress\":%d}", prg);
     418           2 :                 RedisAsyncArgCommand(rac, NULL,
     419           4 :                     list_of(string("RPUSH"))(rkey)(stat));
     420           4 :                 return boost::bind(&QueryEngine::QueryExecWhere, qosp_->qe_,
     421           4 :                         _1, inp.qp, chunknum, 0);
     422           2 :             } else {
     423         854 :                 return NULL;
     424             :             }
     425             :         } else {
     426             :             // We are in the middle of doing WHERE processing for a chunk
     427             : 
     428          48 :             res.welem[substep-1] = exts[step-1]->wres;
     429             : 
     430          96 :             return boost::bind(&QueryEngine::QueryExecWhere, qosp_->qe_,
     431          96 :                     _1, inp.qp, res.current_chunk, substep);
     432             :         }
     433             :         return NULL;
     434             :     }
     435             : 
     436             :     struct Stage0Merge {
     437             :         Input inp;
     438             :         bool ret_code;
     439             :         bool overflow;
     440             :         uint32_t fm_time;
     441             :         vector<vector<QPerfInfo> > ret_info;
     442             :         vector<vector<uint32_t> > chunk_merge_time;
     443             :         BufferT result;
     444             :         OutRowMultimapT mresult;
     445             :     };
     446         126 :     bool QueryMerge(const std::vector<boost::shared_ptr<Stage0Out> > & subs,
     447             :            const boost::shared_ptr<Input> & inp, Stage0Merge & res) {
     448             : 
     449         126 :         res.ret_code = true;
     450         126 :         res.overflow = false;
     451         126 :         res.inp = subs[0]->inp;
     452         126 :         res.fm_time = 0;
     453             : 
     454         126 :         uint32_t rows = 0;
     455         126 :         for (vector<shared_ptr<Stage0Out> >::const_iterator it = subs.begin() ;
     456        1134 :                 it!=subs.end(); it++) {
     457        1008 :             if (res.inp.map_output)
     458         560 :                 rows += (*it)->mresult->size();
     459             :             else
     460         448 :                 rows += (*it)->result->size();
     461             :         }
     462             : 
     463             :         // If max_rows have been exceeded, don't do any more processing
     464         126 :         if (rows > res.inp.max_rows) {
     465           0 :             res.overflow = true;
     466           0 :             return true;
     467             :         }
     468             : 
     469         126 :         std::vector<boost::shared_ptr<OutRowMultimapT> > mqsubs;
     470         126 :         std::vector<boost::shared_ptr<QEOpServerProxy::BufferT> > qsubs;
     471         126 :         for (vector<shared_ptr<Stage0Out> >::const_iterator it = subs.begin() ;
     472        1134 :                 it!=subs.end(); it++) {
     473             : 
     474        1008 :             res.ret_info.push_back((*it)->ret_info);
     475        1008 :             res.chunk_merge_time.push_back((*it)->chunk_merge_time);
     476             : 
     477        1008 :             if ((*it)->ret_code == false) {
     478           0 :                 res.ret_code = false;
     479             :             } else {
     480        1008 :                 if (res.inp.map_output)
     481         560 :                     mqsubs.push_back((*it)->mresult);
     482             :                 else
     483         448 :                     qsubs.push_back((*it)->result);
     484             :             }
     485             :         }
     486             : 
     487         126 :         if (!res.ret_code) return true;
     488             : 
     489         126 :         if (res.inp.need_merge) {
     490          67 :             uint64_t then = UTCTimestampUsec();
     491             : 
     492          67 :             if (res.inp.map_output) {
     493          63 :                 res.ret_code =
     494          63 :                     qosp_->qe_->QueryFinalMerge(res.inp.qp, mqsubs, res.mresult);
     495             : 
     496             :             } else {
     497           4 :                 res.ret_code =
     498           4 :                     qosp_->qe_->QueryFinalMerge(res.inp.qp, qsubs, res.result);
     499             :             }
     500             : 
     501          67 :             uint64_t now = UTCTimestampUsec();
     502          67 :             res.fm_time = static_cast<uint32_t>((now - then)/1000);
     503             :         } else {
     504             :             // TODO : If a merge was not needed, results have been sent to
     505             :             //        redis already. The only thing still needed is the status
     506          59 :             for (vector<shared_ptr<Stage0Out> >::const_iterator it = subs.begin() ;
     507         531 :                     it!=subs.end(); it++) {
     508             : 
     509         472 :                 if (res.inp.map_output) {
     510          56 :                     OutRowMultimapT::iterator jt = res.mresult.begin();
     511          56 :                     for (OutRowMultimapT::const_iterator kt = (*it)->mresult->begin();
     512          94 :                             kt != (*it)->mresult->end(); kt++) {
     513          38 :                         jt = res.mresult.insert(jt,
     514          76 :                                 std::make_pair(kt->first, kt->second));
     515             :                     }
     516             :                 } else {
     517         832 :                     res.result.insert(res.result.begin(),
     518         416 :                         (*it)->result->begin(),
     519         416 :                         (*it)->result->end());
     520             :                 }
     521             :             }
     522             :         }
     523             : 
     524         126 :         return true;
     525         126 :     }
     526             : 
     527             :     struct Output {
     528             :         Input inp;
     529             :         uint32_t redis_time;
     530             :         bool ret_code;
     531             :     };
     532         503 :     ExternalBase::Efn QueryResp(uint32_t inst, const vector<RedisT*> & exts,
     533             :             const Stage0Merge & inp, Output & ret) {
     534         503 :         uint32_t step = exts.size();
     535         503 :         switch (inst) {
     536         252 :         case 0: {
     537         252 :                 if (!step)  {
     538         126 :                     ret.inp = inp.inp;
     539         126 :                     RedisAsyncConnection * rac = conns_[ret.inp.redis_host_idx][ret.inp.cnum].get();
     540         126 :                     std::stringstream keystr;
     541         126 :                     unique_ptr<QEOutputT> jsonresult(new QEOutputT);
     542             : 
     543         126 :                     QE_LOG_NOQID(INFO,  "Will Jsonify #rows " <<
     544             :                         inp.result.size() + inp.mresult.size());
     545         126 :                     QueryJsonify(inp.inp.table, inp.inp.map_output,
     546             :                         &inp.result, &inp.mresult, jsonresult.get());
     547             : 
     548         126 :                     vector<string> const * const res = jsonresult.get();
     549         126 :                     vector<string>::size_type idx = 0;
     550         126 :                     uint32_t rownum = 0;
     551             : 
     552         126 :                     QE_LOG_NOQID(INFO,  "Did Jsonify #rows " << res->size());
     553             : 
     554         126 :                     uint64_t then = UTCTimestampUsec();
     555             :                     char stat[80];
     556         126 :                     string key = "REPLY:" + ret.inp.qp.qid;
     557             : 
     558         126 :                     if (inp.overflow) {
     559           0 :                         sprintf(stat,"{\"progress\":%d}", - ENOBUFS);
     560         126 :                     } else if (!inp.ret_code) {
     561           0 :                         sprintf(stat,"{\"progress\":%d}", - EIO);
     562             :                     } else {
     563         700 :                         while (idx < res->size()) {
     564         574 :                             uint32_t rowsize = 0;
     565         574 :                             keystr.str(string());
     566         574 :                             keystr << "RESULT:" << ret.inp.qp.qid << ":" << rownum;
     567        1148 :                             vector<string> command = list_of(string("RPUSH"))(keystr.str());
     568        3864 :                             while ((idx < res->size()) && (((int)rowsize) < kMaxRowThreshold)) {
     569        3290 :                                 command.push_back(res->at(idx));
     570        3290 :                                 rowsize += res->at(idx).size();
     571        3290 :                                 idx++;
     572             :                             }
     573         574 :                             RedisAsyncArgCommand(rac, NULL, command);
     574         574 :                             RedisAsyncArgCommand(rac, NULL,
     575        1148 :                                 list_of(string("EXPIRE"))(keystr.str())("300"));
     576         574 :                             sprintf(stat,"{\"progress\":90, \"lines\":%d}",
     577             :                                 (int)rownum);
     578         574 :                             RedisAsyncArgCommand(rac, NULL,
     579        1148 :                                 list_of(string("RPUSH"))(key)(stat));
     580         574 :                             rownum++;
     581         574 :                         }
     582         126 :                         sprintf(stat,"{\"progress\":100, \"lines\":%d, \"count\":%d}",
     583         126 :                             (int)rownum, (int)res->size());
     584             :                     }
     585         126 :                     uint64_t now = UTCTimestampUsec();
     586         126 :                     ret.redis_time = static_cast<uint32_t>((now - then)/1000);
     587         126 :                     QE_LOG_NOQID(DEBUG,  "QE Query Result is " << stat);
     588         252 :                     return boost::bind(&RedisAsyncArgCommand,
     589         126 :                             conns_[ret.inp.redis_host_idx][ret.inp.cnum].get(), _1,
     590         378 :                             list_of(string("RPUSH"))(key)(stat));
     591         126 :                 } else {
     592         126 :                     RedisAsyncConnection * rac = conns_[ret.inp.redis_host_idx][ret.inp.cnum].get();
     593         126 :                     string key = "REPLY:" + ret.inp.qp.qid;
     594         126 :                     RedisAsyncArgCommand(rac, NULL,
     595         252 :                         list_of(string("EXPIRE"))(key)("300"));
     596             : 
     597         126 :                     key = "QUERY:" + ret.inp.qp.qid;
     598         126 :                     RedisAsyncArgCommand(rac, NULL,
     599         252 :                         list_of(string("EXPIRE"))(key)("300"));
     600             : 
     601         126 :                     uint64_t now = UTCTimestampUsec();
     602         126 :                     uint32_t qtime = static_cast<uint32_t>(
     603         126 :                             (now - ret.inp.qp.query_starttm)/1000);
     604             : 
     605         126 :                     uint64_t enqtm = atol(ret.inp.qp.terms["enqueue_time"].c_str());
     606         126 :                     uint32_t enq_delay = static_cast<uint32_t>(
     607         126 :                             (ret.inp.qp.query_starttm - enqtm)/1000);
     608             : 
     609         126 :                     QueryStats qs;
     610             :                     size_t outsize;
     611             : 
     612         126 :                     if (ret.inp.map_output)
     613          70 :                         outsize = inp.mresult.size();
     614             :                     else
     615          56 :                         outsize = inp.result.size();
     616             : 
     617         126 :                     qs.set_rows(static_cast<uint32_t>(outsize));
     618         126 :                     qs.set_time(qtime);
     619         126 :                     qs.set_qid(ret.inp.qp.qid);
     620         126 :                     qs.set_chunks(inp.inp.chunk_size.size());
     621         126 :                     std::ostringstream wherestr, selstr, poststr;
     622        1134 :                     for (size_t i=0; i < inp.ret_info.size(); i++) {
     623        2768 :                         for (size_t j=0; j < inp.ret_info[i].size(); j++) {
     624        1760 :                             wherestr << inp.ret_info[i][j].chunk_where_time << ",";
     625        1760 :                             selstr << inp.ret_info[i][j].chunk_select_time << ",";
     626        1760 :                             poststr << inp.ret_info[i][j].chunk_postproc_time << ",";
     627             :                         }
     628        1008 :                         wherestr << " ";
     629        1008 :                         selstr << " ";
     630        1008 :                         poststr << " ";
     631             :                     }
     632         126 :                     if (inp.overflow) {
     633           0 :                         qs.set_error("ERROR-ENOBUFS");
     634         126 :                     } else if (!inp.ret_code) {
     635           0 :                         qs.set_error("ERROR-EIO");
     636             :                     } else {
     637         126 :                         qs.set_error("None");
     638             :                     }
     639         126 :                     qs.set_chunk_where_time(wherestr.str());
     640         126 :                     qs.set_chunk_select_time(selstr.str());
     641         126 :                     qs.set_chunk_postproc_time(poststr.str());
     642             : 
     643         126 :                     std::ostringstream mergestr;
     644        1134 :                     for (size_t i=0; i < inp.chunk_merge_time.size(); i++) {
     645        1456 :                         for (size_t j=0; j < inp.chunk_merge_time[i].size(); j++) {
     646         448 :                             mergestr << inp.chunk_merge_time[i][j] << ",";
     647             :                         }
     648        1008 :                         mergestr << " ";
     649             :                     }
     650             : 
     651         126 :                     qs.set_chunk_merge_time(mergestr.str());
     652         126 :                     qs.set_final_merge_time(inp.fm_time);
     653         126 :                     qs.set_where(inp.inp.where);
     654         126 :                     qs.set_select(inp.inp.select);
     655         126 :                     qs.set_post(inp.inp.post);
     656         126 :                     qs.set_time_span(static_cast<uint32_t>(inp.inp.time_period));
     657         126 :                     qs.set_enq_delay(enq_delay);
     658         126 :                     QUERY_PERF_INFO_SEND(Sandesh::source(), // name
     659             :                                          inp.inp.table,     // table
     660             :                                          qs);
     661             : 
     662         126 :                     QE_LOG_NOQID(INFO, "Finished: QID " << ret.inp.qp.qid <<
     663             :                         " Table " << inp.inp.table <<
     664             :                         " Time(ms) " << qtime <<
     665             :                         " RedisTime(ms) " << ret.redis_time <<
     666             :                         " MergeTime(ms) " << inp.fm_time <<
     667             :                         " Rows " << outsize <<
     668             :                         " EnQ-delay" << enq_delay);
     669             : 
     670         126 :                     ret.ret_code = true;
     671         126 :                 }
     672             :             }
     673         126 :             break;
     674         252 :         case 1: {
     675         252 :                 if (!step)  {
     676         126 :                     string key = "ENGINE:" + inp.inp.hostname;
     677         252 :                     return boost::bind(&RedisAsyncArgCommand,
     678         126 :                             conns_[inp.inp.redis_host_idx][inp.inp.cnum].get(), _1,
     679         378 :                             list_of(string("LREM"))(key)("0")(inp.inp.qp.qid));
     680         126 :                 } else {
     681         126 :                     ret.ret_code = true;
     682             :                 }
     683             :             }
     684         126 :             break;
     685             :         }
     686         251 :         return NULL;
     687             :     }
     688             : 
     689             : 
     690             :     typedef WorkPipeline<Input, Stage0Merge, Output> QEPipeT;
     691             : 
     692         126 :     void QEPipeCb(QEPipeT *wp, bool ret_code) {
     693         126 :         std::scoped_lock lock(mutex_);
     694             : 
     695         126 :         boost::shared_ptr<Output> res = wp->Result();
     696         126 :         assert(pipes_.find(res->inp.qp.qid)->second == wp);
     697         126 :         pipes_.erase(res->inp.qp.qid);
     698         126 :         m_analytics_queries.erase(res->inp.qp.qid);
     699         126 :         npipes_[res->inp.redis_host_idx][res->inp.cnum-1]--;
     700         126 :         QE_LOG_NOQID(DEBUG,  " Result " << res->ret_code << " , " << res->inp.cnum << " conn");
     701         126 :         delete wp;
     702         126 :     }
     703             : 
     704         356 :     void ConnUpPostProcess(int redis_host_idx, uint8_t cnum) {
     705         356 :         if (!connState_[redis_host_idx][cnum]) {
     706         230 :             QE_LOG_NOQID(DEBUG, "ConnUp SetCB" << (uint32_t)cnum);
     707         230 :             cb_proc_fn_[redis_host_idx][cnum] = boost::bind(&QEOpServerImpl::CallbackProcess,
     708         230 :                     this, redis_host_idx, cnum, _1, _2, _3);
     709         230 :             conns_[redis_host_idx][cnum].get()->SetClientAsyncCmdCb(cb_proc_fn_[redis_host_idx][cnum]);
     710         230 :             connState_[redis_host_idx][cnum] = true;
     711             :         }
     712             : 
     713         356 :         bool isConnected = true;
     714        2136 :         for(int i=0; i<kConnections+1; i++)
     715        1780 :             if (!connState_[redis_host_idx][i]) isConnected = false;
     716         356 :         if (isConnected) {
     717         172 :             string key = "ENGINE:" + hostname_;
     718         344 :             conns_[redis_host_idx][0].get()->RedisAsyncArgCmd(0,
     719         344 :                     list_of(string("BRPOPLPUSH"))("QUERYQ")(key)("0"));
     720         172 :         }
     721         356 :     }
     722             : 
     723         126 :     int LeastLoadedConnection(int redis_host_idx) {
     724         126 :         int minvalue = npipes_[redis_host_idx][0];
     725         126 :         int minindex = 0;
     726         504 :         for (int i = 1; i < kConnections; i++) {
     727         378 :             if (npipes_[redis_host_idx][i] < minvalue) {
     728           0 :                 minvalue = npipes_[redis_host_idx][i];
     729           0 :                 minindex = i;
     730             :             }
     731             :         }
     732         126 :         return minindex;
     733             :     }
     734             : 
     735           0 :     void QueryError(int redis_host_idx, string qid, int ret_code) {
     736           0 :         string redis_host = redis_host_port_pairs_[redis_host_idx].first;
     737           0 :         int redis_port = redis_host_port_pairs_[redis_host_idx].second;
     738           0 :         redisContext *c = redisConnect(redis_host.c_str(), redis_port);
     739           0 :         if (c->err) {
     740           0 :             QE_LOG_NOQID(ERROR, "Cannot report query error for " << qid <<
     741             :                 " . No Redis Connection");
     742           0 :             redisFree(c);
     743           0 :             return;
     744             :         }
     745             : 
     746             :         /* Secure the connection if SSL enabled */
     747           0 :         if (redis_ssl_enable_) {
     748           0 :             redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
     749           0 :             redisSSLContext *ssl_ctx = redisCreateSSLContext(
     750             :                 redis_ca_cert_.c_str(),
     751             :                 NULL,
     752             :                 redis_certfile_.c_str(),
     753             :                 redis_keyfile_.c_str(),
     754             :                 "sni",
     755             :                 &ssl_error);
     756           0 :             if (!ssl_ctx || ssl_error != REDIS_SSL_CTX_NONE) {
     757           0 :                 QE_LOG_NOQID(ERROR, "Cannot report query error for " << qid <<
     758             :                              " SSL Context Error: " << redisSSLContextGetError(ssl_error));
     759           0 :                 if (ssl_ctx) redisFreeSSLContext(ssl_ctx);
     760           0 :                 redisFree(c);
     761           0 :                 return;
     762             :             }
     763           0 :             int rc = redisInitiateSSLWithContext(c, ssl_ctx);
     764           0 :             if (rc != REDIS_OK) {
     765           0 :                 QE_LOG_NOQID(ERROR, "Cannot report query error for " << qid <<
     766             :                              " SSL Connection Error: " << c->errstr);
     767           0 :                 redisFreeSSLContext(ssl_ctx);
     768           0 :                 redisFree(c);
     769           0 :                 return;
     770             :             }
     771           0 :             redisFreeSSLContext(ssl_ctx);
     772             :         }
     773             : 
     774             :         //Authenticate the context with password
     775           0 :         if (!redis_password_.empty()) {
     776           0 :             redisReply * reply = (redisReply *) redisCommand(c, "AUTH %s",
     777             :                                                              redis_password_.c_str());
     778           0 :             if (reply->type == REDIS_REPLY_ERROR) {
     779           0 :                 QE_LOG_NOQID(ERROR, "Authentication to redis error");
     780           0 :                 freeReplyObject(reply);
     781           0 :                 redisFree(c);
     782           0 :                 return;
     783             :             }
     784           0 :             freeReplyObject(reply);
     785             :         }
     786             : 
     787             :         char stat[80];
     788           0 :         string key = "REPLY:" + qid;
     789           0 :         sprintf(stat,"{\"progress\":%d}", - ret_code);
     790             : 
     791           0 :         redisReply * reply = (redisReply *) redisCommand(c, "RPUSH %s %s",
     792             :             key.c_str(), stat);
     793             : 
     794           0 :         freeReplyObject(reply);
     795           0 :         redisFree(c);
     796           0 :     }
     797             : 
     798             : 
     799         126 :     void StartPipeline(const string qid, int redis_host_idx) {
     800         126 :         QueryStats qs;
     801         126 :         qs.set_qid(qid);
     802         126 :         qs.set_rows(0);
     803         126 :         qs.set_time(0);
     804         126 :         qs.set_final_merge_time(0);
     805         126 :         qs.set_enq_delay(0);
     806             : 
     807         126 :         uint64_t now = UTCTimestampUsec();
     808             : 
     809         126 :         std::scoped_lock lock(mutex_);
     810             : 
     811         126 :         string redis_host = redis_host_port_pairs_[redis_host_idx].first;
     812         126 :         int redis_port = redis_host_port_pairs_[redis_host_idx].second;
     813         126 :         QE_LOG_NOQID(INFO, "StartPipeline on :" << " Redis:" << redis_host << " Port:" << redis_port);
     814         126 :         redisContext *c = redisConnect(redis_host.c_str(), redis_port);
     815             : 
     816         126 :         if (c->err) {
     817           0 :             QE_LOG_NOQID(ERROR, "Cannot start Pipleline for " << qid <<
     818             :                 " . No Redis Connection");
     819           0 :             redisFree(c);
     820           0 :             qs.set_error("No Redis Connection");
     821           0 :             QUERY_PERF_INFO_SEND(Sandesh::source(), // name
     822             :                                  "__UNKNOWN__",     // table
     823             :                                  qs);
     824           0 :             return;
     825             :         }
     826             : 
     827             :         /* Secure the connection if SSL enabled */
     828         126 :         if (redis_ssl_enable_) {
     829           0 :             redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
     830           0 :             redisSSLContext *ssl_ctx = redisCreateSSLContext(
     831             :                 redis_ca_cert_.c_str(),
     832             :                 NULL,
     833             :                 redis_certfile_.c_str(),
     834             :                 redis_keyfile_.c_str(),
     835             :                 "sni",
     836             :                 &ssl_error);
     837           0 :             if (!ssl_ctx || ssl_error != REDIS_SSL_CTX_NONE) {
     838           0 :                 QE_LOG_NOQID(ERROR, "Cannot start pipeline for " << qid <<
     839             :                                 " SSL Context Error: " << redisSSLContextGetError(ssl_error));
     840           0 :                 if (ssl_ctx) redisFreeSSLContext(ssl_ctx);
     841           0 :                 redisFree(c);
     842           0 :                 qs.set_error("Redis SSL Context Error");
     843           0 :                 QUERY_PERF_INFO_SEND(Sandesh::source(), "__UNKNOWN__", qs);
     844           0 :                 return;
     845             :             }
     846           0 :             int rc = redisInitiateSSLWithContext(c, ssl_ctx);
     847           0 :             if (rc != REDIS_OK) {
     848           0 :                 QE_LOG_NOQID(ERROR, "Cannot start pipeline for " << qid <<
     849             :                                 " SSL Connection Error: " << c->errstr);
     850           0 :                 redisFreeSSLContext(ssl_ctx);
     851           0 :                 redisFree(c);
     852           0 :                 qs.set_error("Redis SSL Connection Error");
     853           0 :                 QUERY_PERF_INFO_SEND(Sandesh::source(), "__UNKNOWN__", qs);
     854           0 :                 return;
     855             :             }
     856           0 :             redisFreeSSLContext(ssl_ctx);
     857             :         }
     858             : 
     859             :         //Authenticate the context with password
     860         126 :         if ( !redis_password_.empty()) {
     861           0 :             redisReply * reply = (redisReply *) redisCommand(c, "AUTH %s",
     862             :                                                              redis_password_.c_str());
     863           0 :             if (reply->type == REDIS_REPLY_ERROR) {
     864           0 :                 QE_LOG_NOQID(ERROR, "Authentication to redis error");
     865           0 :                 freeReplyObject(reply);
     866           0 :                 redisFree(c);
     867           0 :                 qs.set_error("Redis Auth Failed");
     868           0 :                 QUERY_PERF_INFO_SEND(Sandesh::source(), // name
     869             :                                      "__UNKNOWN__",     // table
     870             :                                      qs);
     871           0 :                 return;
     872             :             }
     873           0 :             freeReplyObject(reply);
     874             :         }
     875             : 
     876         126 :         string key = "QUERY:" + qid;
     877         126 :         redisReply * reply = (redisReply *) redisCommand(c, "hgetall %s",
     878             :             key.c_str());
     879             : 
     880         126 :         map<string,string> terms;
     881         126 :         if (!(c->err) && (reply->type == REDIS_REPLY_ARRAY)) {
     882        1200 :             for (uint32_t i=0; i<reply->elements; i+=2) {
     883        1074 :                 string idx(reply->element[i]->str);
     884        1074 :                 string val(reply->element[i+1]->str);
     885        1074 :                 terms[idx] = val;
     886        1074 :             }
     887         126 :         } else {
     888           0 :             QE_LOG_NOQID(ERROR, "Cannot start Pipleline for " << qid <<
     889             :                 ". Could not read query input");
     890           0 :             freeReplyObject(reply);
     891           0 :             redisFree(c);
     892           0 :             QueryError(redis_host_idx, qid, 5);
     893           0 :             qs.set_error("Could not read query input");
     894           0 :             QUERY_PERF_INFO_SEND(Sandesh::source(), // name
     895             :                                  "__UNKNOWN__",     // table
     896             :                                  qs);
     897           0 :             return;
     898             :         }
     899             : 
     900         126 :         freeReplyObject(reply);
     901         126 :         redisFree(c);
     902             : 
     903         126 :         QueryEngine::QueryParams qp(qid, terms, max_tasks_,
     904         252 :             UTCTimestampUsec());
     905             : 
     906         126 :         vector<uint64_t> chunk_size;
     907             :         bool need_merge;
     908             :         bool map_output;
     909         126 :         string table;
     910         126 :         string where;
     911             :         uint32_t wterms;
     912         126 :         string select;
     913         126 :         string post;
     914             :         uint64_t time_period;
     915             : 
     916         126 :         int ret = qosp_->qe_->QueryPrepare(qp, chunk_size, need_merge, map_output,
     917             :             where, wterms, select, post, time_period, table);
     918             : 
     919         126 :         qs.set_where(where);
     920         126 :         qs.set_select(select);
     921         126 :         qs.set_post(post);
     922         126 :         qs.set_time_span(time_period);
     923         126 :         uint64_t enqtm = atol(terms["enqueue_time"].c_str());
     924         126 :         uint32_t enq_delay = static_cast<uint32_t>((now - enqtm)/1000);
     925         126 :         qs.set_enq_delay(enq_delay);
     926             : 
     927         126 :         if (ret!=0) {
     928           0 :             QueryError(redis_host_idx, qid, ret);
     929           0 :             QE_LOG_NOQID(ERROR, "Cannot start Pipleline for " << qid <<
     930             :                 ". Query Parsing Error " << ret);
     931           0 :             qs.set_error("Query Parsing Error");
     932           0 :             QUERY_PERF_INFO_SEND(Sandesh::source(), // name
     933             :                                  table,             // table
     934             :                                  qs);
     935           0 :             return;
     936             :         } else {
     937         126 :             QE_LOG_NOQID(INFO, "Chunks: " << chunk_size.size() <<
     938             :                 " Need Merge: " << need_merge);
     939             :         }
     940             : 
     941         126 :         if (pipes_.size() >= 32) {
     942           0 :             QueryError(redis_host_idx, qid, EMFILE);
     943           0 :             QE_LOG_NOQID(ERROR, "Cannot start Pipleline for " << qid <<
     944             :                 ". Too many queries : " << pipes_.size());
     945           0 :             qs.set_error("EMFILE");
     946           0 :             QUERY_PERF_INFO_SEND(Sandesh::source(), // name
     947             :                                  table,             // table
     948             :                                  qs);
     949           0 :             return;
     950             :         }
     951             : 
     952         126 :         shared_ptr<Input> inp(new Input());
     953         126 :         inp.get()->hostname = hostname_;
     954         126 :         inp.get()->qp = qp;
     955         126 :         inp.get()->map_output = map_output;
     956         126 :         inp.get()->need_merge = need_merge;
     957         126 :         inp.get()->chunk_size = chunk_size;
     958         126 :         inp.get()->where = where;
     959         126 :         inp.get()->select = select;
     960         126 :         inp.get()->post = post;
     961         126 :         inp.get()->time_period = time_period;
     962         126 :         inp.get()->table = table;
     963         126 :         inp.get()->chunk_q = 0;
     964         126 :         inp.get()->total_rows = 0;
     965         126 :         inp.get()->max_rows = max_rows_;
     966         126 :         inp.get()->wterms = wterms;
     967         126 :         vector<pair<int,int> > tinfo;
     968        1134 :         for (uint idx=0; idx<(uint)max_tasks_; idx++) {
     969        1008 :             tinfo.push_back(make_pair(0, -1));
     970             :         }
     971             : 
     972             :         QEPipeT *wp = new QEPipeT(
     973         252 :             new WorkStage<Input, Stage0Merge, RawResultT, Stage0Out>(
     974             :                 tinfo,
     975             :                 boost::bind(&QEOpServerImpl::QueryExec, this, _1,_2,_3,_4),
     976         126 :                 boost::bind(&QEOpServerImpl::QueryMerge, this, _1,_2,_3)),
     977         252 :             new WorkStage<Stage0Merge, Output, RedisT>(
     978         252 :                 list_of(make_pair(0,-1))(make_pair(0,-1)),
     979         126 :                 boost::bind(&QEOpServerImpl::QueryResp, this, _1,_2,_3,_4)));
     980             : 
     981         126 :         pipes_.insert(make_pair(qid, wp));
     982             : 
     983             :         // Initialize the m_analytics_queries for this qid
     984         126 :         m_analytics_queries[qid] = std::vector<boost::shared_ptr<AnalyticsQuery> > ();
     985             : 
     986         126 :         int conn = LeastLoadedConnection(redis_host_idx);
     987         126 :         QE_LOG_NOQID(DEBUG, "Getting Least Loaded Conn as :" << conn);
     988         126 :         npipes_[redis_host_idx][conn]++;
     989             : 
     990             :         // The cnum with index 0 is only used for receiving new queries
     991         126 :         inp.get()->cnum = conn+1;
     992         126 :         inp.get()->redis_host_idx = redis_host_idx;
     993             : 
     994         126 :         wp->Start(boost::bind(&QEOpServerImpl::QEPipeCb, this, wp, _1), inp);
     995         126 :         QE_LOG_NOQID(DEBUG, "Starting Pipeline for " << qid << " , " << conn+1 <<
     996             :             " conn, " << tinfo.size() << " tasks");
     997             : 
     998             :         // Update query status
     999         126 :         RedisAsyncConnection * rac = conns_[redis_host_idx][inp.get()->cnum].get();
    1000         126 :         string rkey = "REPLY:" + qid;
    1001             :         char stat[40];
    1002         126 :         sprintf(stat,"{\"progress\":15}");
    1003         126 :         RedisAsyncArgCommand(rac, NULL,
    1004         252 :             list_of(string("RPUSH"))(rkey)(stat));
    1005             : 
    1006             : 
    1007         126 :     }
    1008             : 
    1009         230 :     void ConnUpPrePostProcess(int redis_host_idx, uint8_t cnum) {
    1010             :         //Assign callback for AUTH command
    1011         230 :         cb_proc_fn_[redis_host_idx][cnum] = boost::bind(&QEOpServerImpl::ConnectCallbackProcess,
    1012         230 :                     this, redis_host_idx, cnum, _1, _2, _3);
    1013         230 :         conns_[redis_host_idx][cnum].get()->SetClientAsyncCmdCb(cb_proc_fn_[redis_host_idx][cnum]);
    1014             :         //Send AUTH command
    1015         230 :         RedisAsyncConnection * rac = conns_[redis_host_idx][cnum].get();
    1016         230 :         if (!redis_password_.empty()) {
    1017          20 :             RedisAsyncArgCommand(rac, NULL,
    1018          40 :                     list_of(string("AUTH"))(redis_password_.c_str()));
    1019             :         } else {
    1020         210 :             RedisAsyncArgCommand(rac, NULL,
    1021         420 :                     list_of(string("PING")));
    1022             :         }
    1023         230 :     }
    1024             : 
    1025         230 :     void ConnUp(int redis_host_idx, uint8_t cnum) {
    1026         230 :         string redis_host = redis_host_port_pairs_[redis_host_idx].first;
    1027         230 :         std::ostringstream ostr;
    1028         230 :         ostr << "ConnUp.. UP " << (uint32_t)cnum << " With Redis:" << redis_host;
    1029         230 :         QE_LOG_NOQID(DEBUG, ostr.str());
    1030         460 :         qosp_->evm_->io_service()->post(
    1031         230 :                     boost::bind(&QEOpServerImpl::ConnUpPrePostProcess,
    1032             :                     this, redis_host_idx, cnum));
    1033         230 :     }
    1034             : 
    1035         245 :     void UpdateRedisConnectionStatus () {
    1036         245 :         std::ostringstream ostr;
    1037         245 :         map<string, string>::iterator it;
    1038         245 :         vector<string> redis_endpoints;
    1039         245 :         int redis_hosts_cnt = redis_host_port_pairs_.size();
    1040         245 :         int redis_down_count = 0;
    1041         245 :         int redis_idx = 0;
    1042         245 :         int conn_idx = 0;
    1043         245 :         vector<Endpoint> redis_endpoint_list;
    1044         490 :         for (redis_idx = 0; redis_idx < redis_hosts_cnt; redis_idx++) {
    1045         245 :             string redis_host(redis_host_port_pairs_[redis_idx].first);
    1046         245 :             redis_endpoint_list.push_back(conns_[redis_idx][0]->Endpoint());
    1047         541 :             for (conn_idx = 0; conn_idx < kConnections + 1; conn_idx++) {
    1048         486 :                 if (true == connState_[redis_idx][conn_idx]) {
    1049         190 :                     break;
    1050             :                 }
    1051             :             }
    1052         245 :             if (conn_idx == kConnections + 1) {
    1053          55 :                 ostr << conns_[redis_idx][0]->Endpoint() << "::Down.";
    1054          55 :                 redis_down_count++;
    1055             :             }
    1056         245 :         }
    1057         245 :         if (redis_down_count < redis_hosts_cnt) {
    1058             :             /* Then QE should be UP, but display if any redis connection is down */
    1059         380 :             ConnectionState::GetInstance()->Update(ConnectionType::REDIS_QUERY,
    1060         380 :                 "Query", ConnectionStatus::UP, redis_endpoint_list, ostr.str());
    1061             :         } else {
    1062          55 :             std::ostringstream empty_ostr;
    1063         110 :             ConnectionState::GetInstance()->Update(ConnectionType::REDIS_QUERY,
    1064         110 :                 "Query", ConnectionStatus::DOWN, redis_endpoint_list, empty_ostr.str());
    1065          55 :         }
    1066         245 :     }
    1067             : 
    1068          15 :     void ConnDown(int redis_host_idx, uint8_t cnum) {
    1069          15 :         string redis_host = redis_host_port_pairs_[redis_host_idx].first;
    1070          15 :         std::ostringstream ostr;
    1071          15 :         ostr << "ConnDown.. DOWN.. Reconnect.." << (uint32_t)cnum << " With Redis:" << redis_host;
    1072          15 :         QE_LOG_NOQID(DEBUG, ostr.str());
    1073          15 :         connState_[redis_host_idx][cnum] = false;
    1074          30 :         qosp_->evm_->io_service()->post(
    1075          15 :                      boost::bind(&QEOpServerImpl::UpdateRedisConnectionStatus, this));
    1076          30 :         qosp_->evm_->io_service()->post(boost::bind(&RedisAsyncConnection::RAC_Connect,
    1077          15 :             conns_[redis_host_idx][cnum].get()));
    1078          15 :     }
    1079             : 
    1080         230 :     void ConnectCallbackProcess(int redis_host_idx, uint8_t cnum, const redisAsyncContext *c, void *r, void *privdata) {
    1081         230 :         QE_LOG_NOQID(DEBUG, "UP ConnectCallbackProcess.." << conns_[redis_host_idx][cnum]->Endpoint());
    1082         230 :         if (r == NULL) {
    1083           0 :             QE_LOG_NOQID(DEBUG, "In ConnectCallbackProcess.. NULL Reply");
    1084           0 :             return;
    1085             :         }
    1086         230 :         redisReply reply = *reinterpret_cast<redisReply*>(r);
    1087         230 :         if (reply.type != REDIS_REPLY_ERROR) {
    1088         230 :             QE_LOG_NOQID(DEBUG, "In ConnectCallbackProcess.." << conns_[redis_host_idx][cnum]->Endpoint());
    1089         460 :             qosp_->evm_->io_service()->post(
    1090         230 :                      boost::bind(&QEOpServerImpl::UpdateRedisConnectionStatus, this));
    1091         460 :             qosp_->evm_->io_service()->post(
    1092         460 :                      boost::bind(&QEOpServerImpl::ConnUpPostProcess,
    1093             :                      this, redis_host_idx, cnum));
    1094             :         } else {
    1095           0 :             QE_LOG_NOQID(ERROR,"In connectCallbackProcess.. Error");
    1096           0 :             QE_ASSERT(reply.type != REDIS_REPLY_ERROR);
    1097             :         }
    1098             :     }
    1099             : 
    1100        3337 :     void CallbackProcess(int redis_host_idx, uint8_t cnum, const redisAsyncContext *c, void *r, void *privdata) {
    1101             : 
    1102             :         //QE_TRACE_NOQID(DEBUG, "Redis CB" << cnum);
    1103        3337 :         if (0 == cnum) {
    1104         129 :             if (r == NULL) {
    1105           3 :                 QE_LOG_NOQID(DEBUG,  __func__ << ": received NULL reply from redis");
    1106           3 :                 return;
    1107             :             }
    1108             : 
    1109         126 :             redisReply reply = *reinterpret_cast<redisReply*>(r);
    1110         126 :             if (reply.type != REDIS_REPLY_STRING) {
    1111           0 :                 QE_LOG_NOQID(ERROR,  __func__ << " Bad Redis reply on control connection: " << reply.type);
    1112           0 :                 if (reply.type == REDIS_REPLY_ERROR) {
    1113           0 :                     string errstr(reply.str);
    1114           0 :                     QE_LOG_NOQID(ERROR,  __func__ << " Redis Error: " << reply.str);
    1115           0 :                     sleep(1000);
    1116           0 :                 }
    1117             :             }
    1118         126 :             QE_ASSERT(reply.type == REDIS_REPLY_STRING);
    1119         126 :             string qid(reply.str);
    1120             : 
    1121         126 :             StartPipeline(qid, redis_host_idx);
    1122             : 
    1123         252 :             qosp_->evm_->io_service()->post(
    1124         126 :                     boost::bind(&QEOpServerImpl::ConnUpPostProcess,
    1125             :                     this, redis_host_idx, cnum));
    1126         126 :             return;
    1127         126 :         }
    1128             : 
    1129        3208 :         unique_ptr<RedisT> fullReply;
    1130        3208 :         vector<string> elements;
    1131             : 
    1132        3208 :         if (r == NULL) {
    1133             :             //QE_TRACE_NOQID(DEBUG, "NULL Reply...\n");
    1134             :         } else {
    1135        3208 :             redisReply reply = *reinterpret_cast<redisReply*>(r);
    1136        3208 :             fullReply.reset(new RedisT);
    1137        3208 :             fullReply.get()->first = reply;
    1138        3208 :             if (reply.type == REDIS_REPLY_ARRAY) {
    1139           0 :                 for (uint32_t i=0; i<reply.elements; i++) {
    1140           0 :                     string element(reply.element[i]->str);
    1141           0 :                     fullReply.get()->second.push_back(element);
    1142           0 :                 }
    1143        3208 :             } else if (reply.type == REDIS_REPLY_STRING) {
    1144           0 :                 fullReply.get()->second.push_back(string(reply.str));
    1145             :             }
    1146             :         }
    1147        3208 :         if (!privdata) {
    1148             :             //QE_TRACE_NOQID(DEBUG, "Ignoring redis reply");
    1149        2956 :             return;
    1150             :         }
    1151         252 :         ExternalProcIf<RedisT> * rpi =
    1152             :                 reinterpret_cast<ExternalProcIf<RedisT> *>(privdata);
    1153         252 :         QE_TRACE_NOQID(DEBUG,  " Rx data from REDIS for " << rpi->Key());
    1154             : 
    1155         252 :         rpi->Response(std::move(fullReply));
    1156             : 
    1157        6164 :     }
    1158             : 
    1159          44 :     void BuildRedisIPPort(vector<string> redis_ip_port_list) {
    1160             :         typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
    1161          44 :         for (vector<string>::const_iterator it = redis_ip_port_list.begin();
    1162          88 :              it != redis_ip_port_list.end(); it++) {
    1163          44 :             string redis_ip_port(*it);
    1164          44 :             boost::char_separator<char> sep(":");
    1165          44 :             tokenizer tokens(redis_ip_port, sep);
    1166          44 :             tokenizer::iterator tit = tokens.begin();
    1167          44 :             string redis_ip(*tit);
    1168          44 :             ++tit;
    1169          44 :             string redis_port(*tit);
    1170          44 :             redis_host_port_pairs_.push_back(make_pair(redis_ip, atoi(redis_port.c_str())));
    1171          44 :         }
    1172          44 :     }
    1173             : 
    1174          44 :     QEOpServerImpl(vector<string> redis_ip_ports,
    1175             :                    const string & redis_password,
    1176             :                    const bool redis_ssl_enable,
    1177             :                    const string & redis_keyfile,
    1178             :                    const string & redis_certfile,
    1179             :                    const string & redis_ca_cert,
    1180             :                    QEOpServerProxy * qosp, int max_tasks,
    1181          44 :                    int max_rows, const string &host_ip) :
    1182          44 :             hostname_(ResolveCanonicalName(host_ip)),
    1183          44 :             redis_password_(redis_password),
    1184          44 :             redis_ssl_enable_(redis_ssl_enable),
    1185          44 :             redis_keyfile_(redis_keyfile),
    1186          44 :             redis_certfile_(redis_certfile),
    1187          44 :             redis_ca_cert_(redis_ca_cert),
    1188          44 :             qosp_(qosp),
    1189          44 :             max_tasks_(max_tasks),
    1190          88 :             max_rows_(max_rows) {
    1191          44 :         int redis_host_idx = 0;
    1192             :         /* Initialize */
    1193          44 :         BuildRedisIPPort(redis_ip_ports);
    1194          44 :         int redis_host_count = redis_host_port_pairs_.size();
    1195          44 :         cb_proc_fn_ = new RedisAsyncConnection::ClientAsyncCmdCbFn* [redis_host_count];
    1196          44 :         conns_ = new boost::shared_ptr<RedisAsyncConnection>* [redis_host_count];
    1197          44 :         npipes_ = new int* [redis_host_count];
    1198          44 :         connState_ = new bool* [redis_host_count];
    1199          88 :         for (int i = 0; i < redis_host_count; i++) {
    1200         264 :             cb_proc_fn_[i] = new RedisAsyncConnection::ClientAsyncCmdCbFn[kConnections + 1];
    1201         264 :             conns_[i] = new boost::shared_ptr<RedisAsyncConnection>[kConnections + 1];
    1202          44 :             npipes_[i] = new int[kConnections];
    1203          44 :             connState_[i] = new bool[kConnections + 1];
    1204             :         }
    1205          88 :         for (redis_host_idx = 0; redis_host_idx < int(redis_host_count); redis_host_idx++) {
    1206          44 :             string redis_host = redis_host_port_pairs_[redis_host_idx].first;
    1207          44 :             int redis_port = redis_host_port_pairs_[redis_host_idx].second;
    1208         264 :             for (int i = 0; i < kConnections + 1; i++) {
    1209         220 :                 cb_proc_fn_[redis_host_idx][i] = boost::bind(&QEOpServerImpl::CallbackProcess,
    1210         220 :                                              this, redis_host_idx, i, _1, _2, _3);
    1211         220 :                 connState_[redis_host_idx][i] = false;
    1212         220 :                 if (i) {
    1213         176 :                     conns_[redis_host_idx][i].reset(rac_alloc(qosp->evm_, redis_host, redis_port,
    1214             :                                   boost::bind(&QEOpServerImpl::ConnUp, this, redis_host_idx, i),
    1215             :                                   boost::bind(&QEOpServerImpl::ConnDown, this, redis_host_idx, i),
    1216             :                                   redis_ssl_enable, redis_keyfile, redis_certfile, redis_ca_cert));
    1217             :                 } else {
    1218          44 :                     conns_[redis_host_idx][i].reset(rac_alloc_nocheck(qosp->evm_, redis_host, redis_port,
    1219             :                                   boost::bind(&QEOpServerImpl::ConnUp, this, redis_host_idx, i),
    1220             :                                   boost::bind(&QEOpServerImpl::ConnDown, this, redis_host_idx, i),
    1221             :                                   redis_ssl_enable, redis_keyfile, redis_certfile, redis_ca_cert));
    1222             :                 }
    1223             :                 // The cnum with index 0 is only used for receiving new queries
    1224             :                 // It does not host any pipelines
    1225         220 :                 if (i) npipes_[redis_host_idx][i-1] = 0;
    1226             :             }
    1227          44 :         }
    1228          44 :     }
    1229             : 
    1230         264 :     ~QEOpServerImpl() {
    1231          44 :     }
    1232             : 
    1233         904 :     void AddAnalyticsQuery(const std::string &qid,
    1234             :                            boost::shared_ptr<AnalyticsQuery> q) {
    1235         904 :         std::scoped_lock lock(mutex_);
    1236         904 :         m_analytics_queries[qid].push_back(q);
    1237         904 :     }
    1238             : 
    1239             : private:
    1240             : 
    1241             :     static const int kMaxRowThreshold = 10000;
    1242             : 
    1243             :     // We always have one connection to receive new queries from OpServer
    1244             :     // This is the number of addition connections, which will be
    1245             :     // used to read query parameters and write query results
    1246             :     static const uint8_t kConnections = 4;
    1247             : 
    1248             :     const string hostname_;
    1249             :     map<string, string> redis_status_maps;
    1250             :     const string redis_password_;
    1251             :     const bool redis_ssl_enable_;
    1252             :     const string redis_keyfile_;
    1253             :     const string redis_certfile_;
    1254             :     const string redis_ca_cert_;
    1255             :     QEOpServerProxy * const qosp_;
    1256             :     boost::shared_ptr<RedisAsyncConnection> **conns_;
    1257             :     RedisAsyncConnection::ClientAsyncCmdCbFn **cb_proc_fn_;
    1258             :     bool **connState_;
    1259             : 
    1260             :     std::mutex mutex_;
    1261             :     map<string,QEPipeT*> pipes_;
    1262             :     vector<pair<string, int> > redis_host_port_pairs_;
    1263             :     int **npipes_;
    1264             :     int max_tasks_;
    1265             :     int max_rows_;
    1266             :     std::map<std::string, std::vector<boost::shared_ptr<AnalyticsQuery> > >
    1267             :         m_analytics_queries;
    1268             : 
    1269             : };
    1270             : 
    1271          44 : QEOpServerProxy::QEOpServerProxy(EventManager *evm, QueryEngine *qe,
    1272             :             vector<string> redis_ip_ports,
    1273             :             const string & redis_password,
    1274             :             const bool redis_ssl_enable,
    1275             :             const string & redis_keyfile,
    1276             :             const string & redis_certfile,
    1277             :             const string & redis_ca_cert,
    1278          44 :             const std::string &host_ip, int max_tasks, int max_rows) :
    1279          44 :         evm_(evm),
    1280          44 :         qe_(qe),
    1281          44 :         impl_(new QEOpServerImpl(redis_ip_ports, redis_password, redis_ssl_enable,
    1282             :             redis_keyfile, redis_certfile, redis_ca_cert,
    1283          88 :             this, max_tasks, max_rows, host_ip)) {}
    1284             : 
    1285          88 : QEOpServerProxy::~QEOpServerProxy() {}
    1286             : 
    1287             : void
    1288         856 : QEOpServerProxy::QueryResult(void * qid, QPerfInfo qperf,
    1289             :         std::auto_ptr<BufferT> res, std::auto_ptr<OutRowMultimapT> mres) {
    1290             : 
    1291         856 :     impl_->QECallback(qid, qperf, std::unique_ptr<BufferT>(res.release()), std::unique_ptr<OutRowMultimapT>(mres.release()));
    1292         856 : }
    1293             : 
    1294             : void
    1295         904 : QEOpServerProxy::QueryResult(void * qid, QPerfInfo qperf,
    1296             :         std::auto_ptr<std::vector<query_result_unit_t> > res) {
    1297             : 
    1298         904 :     impl_->QECallback(qid, qperf, std::unique_ptr<std::vector<query_result_unit_t>>(res.release()));
    1299         904 : }
    1300             : 
    1301         903 : void QEOpServerProxy::AddAnalyticsQuery(const std::string &qid,
    1302             :         boost::shared_ptr<AnalyticsQuery> q) {
    1303         903 :     impl_->AddAnalyticsQuery(qid, q);
    1304         904 : }

Generated by: LCOV version 1.14