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