libcouchbase 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +11 -8
  3. data/ext/libcouchbase/CMakeLists.txt +1 -1
  4. data/ext/libcouchbase/README.markdown +38 -6
  5. data/ext/libcouchbase/RELEASE_NOTES.markdown +151 -0
  6. data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -2
  7. data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
  8. data/ext/libcouchbase/cmake/source_files.cmake +1 -0
  9. data/ext/libcouchbase/contrib/cJSON/cJSON.c +686 -288
  10. data/ext/libcouchbase/contrib/cJSON/cJSON.h +0 -0
  11. data/ext/libcouchbase/contrib/cbsasl/src/hash.c +17 -17
  12. data/ext/libcouchbase/contrib/cliopts/cliopts.c +76 -0
  13. data/ext/libcouchbase/contrib/cliopts/cliopts.h +66 -15
  14. data/ext/libcouchbase/contrib/genhash/genhash.c +1 -2
  15. data/ext/libcouchbase/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp +4 -3
  16. data/ext/libcouchbase/example/instancepool/main.cc +12 -2
  17. data/ext/libcouchbase/example/libeventdirect/main.c +99 -25
  18. data/ext/libcouchbase/example/minimal/minimal.c +7 -5
  19. data/ext/libcouchbase/example/observe/durability.c +102 -0
  20. data/ext/libcouchbase/example/observe/observe.c +19 -6
  21. data/ext/libcouchbase/example/subdoc/subdoc-xattrs.c +1 -2
  22. data/ext/libcouchbase/include/libcouchbase/cntl-private.h +6 -8
  23. data/ext/libcouchbase/include/libcouchbase/cntl.h +84 -64
  24. data/ext/libcouchbase/include/libcouchbase/couchbase.h +295 -78
  25. data/ext/libcouchbase/include/libcouchbase/deprecated.h +2 -2
  26. data/ext/libcouchbase/include/libcouchbase/error.h +1 -1
  27. data/ext/libcouchbase/include/libcouchbase/iops.h +9 -9
  28. data/ext/libcouchbase/include/libcouchbase/ixmgmt.h +2 -2
  29. data/ext/libcouchbase/include/libcouchbase/n1ql.h +69 -7
  30. data/ext/libcouchbase/include/libcouchbase/vbucket.h +17 -0
  31. data/ext/libcouchbase/include/libcouchbase/views.h +3 -3
  32. data/ext/libcouchbase/include/memcached/protocol_binary.h +62 -1
  33. data/ext/libcouchbase/packaging/deb/control +1 -1
  34. data/ext/libcouchbase/packaging/rpm/libcouchbase.spec.in +37 -36
  35. data/ext/libcouchbase/src/bootstrap.cc +22 -8
  36. data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +1 -1
  37. data/ext/libcouchbase/src/bucketconfig/bc_http.cc +0 -1
  38. data/ext/libcouchbase/src/bucketconfig/confmon.cc +13 -8
  39. data/ext/libcouchbase/src/callbacks.c +2 -0
  40. data/ext/libcouchbase/src/cntl.cc +28 -17
  41. data/ext/libcouchbase/src/dns-srv.cc +1 -2
  42. data/ext/libcouchbase/src/dump.cc +4 -0
  43. data/ext/libcouchbase/src/errmap.h +89 -16
  44. data/ext/libcouchbase/src/handler.cc +28 -11
  45. data/ext/libcouchbase/src/http/http-priv.h +4 -1
  46. data/ext/libcouchbase/src/http/http.cc +3 -0
  47. data/ext/libcouchbase/src/instance.cc +1 -1
  48. data/ext/libcouchbase/src/internal.h +1 -0
  49. data/ext/libcouchbase/src/lcbio/connect.cc +2 -3
  50. data/ext/libcouchbase/src/lcbio/manager.cc +2 -2
  51. data/ext/libcouchbase/src/lcbio/ssl.h +10 -0
  52. data/ext/libcouchbase/src/mc/mcreq.c +8 -0
  53. data/ext/libcouchbase/src/mcserver/mcserver.cc +14 -1
  54. data/ext/libcouchbase/src/n1ql/ixmgmt.cc +0 -3
  55. data/ext/libcouchbase/src/n1ql/n1ql.cc +22 -29
  56. data/ext/libcouchbase/src/n1ql/params.cc +46 -1
  57. data/ext/libcouchbase/src/newconfig.cc +4 -4
  58. data/ext/libcouchbase/src/operations/durability-seqno.cc +4 -0
  59. data/ext/libcouchbase/src/operations/durability.cc +3 -0
  60. data/ext/libcouchbase/src/operations/ping.cc +315 -0
  61. data/ext/libcouchbase/src/operations/stats.cc +10 -0
  62. data/ext/libcouchbase/src/operations/subdoc.cc +13 -1
  63. data/ext/libcouchbase/src/retrychk.cc +1 -0
  64. data/ext/libcouchbase/src/settings.c +2 -0
  65. data/ext/libcouchbase/src/settings.h +13 -7
  66. data/ext/libcouchbase/src/ssl/ssl_c.c +28 -2
  67. data/ext/libcouchbase/src/ssl/ssl_common.c +3 -0
  68. data/ext/libcouchbase/src/ssl/ssl_e.c +15 -1
  69. data/ext/libcouchbase/src/ssl/ssl_iot_common.h +3 -1
  70. data/ext/libcouchbase/src/timings.c +0 -1
  71. data/ext/libcouchbase/src/vbucket/vbucket.c +49 -1
  72. data/ext/libcouchbase/tests/iotests/mock-environment.cc +58 -40
  73. data/ext/libcouchbase/tests/iotests/mock-environment.h +23 -4
  74. data/ext/libcouchbase/tests/iotests/mock-unit-test.h +8 -8
  75. data/ext/libcouchbase/tests/iotests/t_behavior.cc +5 -5
  76. data/ext/libcouchbase/tests/iotests/t_durability.cc +50 -0
  77. data/ext/libcouchbase/tests/iotests/t_eerrs.cc +4 -2
  78. data/ext/libcouchbase/tests/iotests/t_errmap.cc +6 -3
  79. data/ext/libcouchbase/tests/iotests/t_lock.cc +5 -6
  80. data/ext/libcouchbase/tests/iotests/t_misc.cc +44 -0
  81. data/ext/libcouchbase/tests/iotests/t_serverops.cc +1 -0
  82. data/ext/libcouchbase/tests/iotests/t_subdoc.cc +28 -0
  83. data/ext/libcouchbase/tests/iotests/t_views.cc +22 -10
  84. data/ext/libcouchbase/tools/CMakeLists.txt +21 -1
  85. data/ext/libcouchbase/tools/cbc-handlers.h +23 -3
  86. data/ext/libcouchbase/tools/cbc-n1qlback.cc +1 -1
  87. data/ext/libcouchbase/tools/cbc-pillowfight.cc +126 -26
  88. data/ext/libcouchbase/tools/cbc-proxy.cc +403 -0
  89. data/ext/libcouchbase/tools/cbc-subdoc.cc +826 -0
  90. data/ext/libcouchbase/tools/cbc.cc +149 -37
  91. data/ext/libcouchbase/tools/common/options.h +5 -2
  92. data/ext/libcouchbase/tools/linenoise/linenoise.c +15 -15
  93. data/lib/libcouchbase.rb +4 -0
  94. data/lib/libcouchbase/bucket.rb +51 -0
  95. data/lib/libcouchbase/connection.rb +100 -13
  96. data/lib/libcouchbase/ext/libcouchbase.rb +40 -0
  97. data/lib/libcouchbase/ext/libcouchbase/cmdsubdoc.rb +13 -1
  98. data/lib/libcouchbase/ext/libcouchbase/enums.rb +2 -1
  99. data/lib/libcouchbase/ext/libcouchbase/sdspec.rb +5 -0
  100. data/lib/libcouchbase/subdoc_request.rb +129 -0
  101. data/lib/libcouchbase/version.rb +1 -1
  102. data/spec/bucket_spec.rb +15 -1
  103. data/spec/connection_spec.rb +1 -1
  104. data/spec/subdoc_spec.rb +192 -0
  105. metadata +13 -4
  106. data/ext/libcouchbase/.travis.yml +0 -19
@@ -203,6 +203,23 @@ protected:
203
203
  void run();
204
204
  };
205
205
 
206
+ class PingHandler : public Handler {
207
+ public:
208
+ HANDLER_DESCRIPTION("Reach all services on every node and measure response time")
209
+ HANDLER_USAGE("[OPTIONS ...]")
210
+ PingHandler() : Handler("ping"), o_details("details") {
211
+ o_details.description("Render extra details about status of the services");
212
+ }
213
+ protected:
214
+ void run();
215
+ void addOptions() {
216
+ Handler::addOptions();
217
+ parser.addOption(o_details);
218
+ }
219
+ private:
220
+ cliopts::BoolOption o_details;
221
+ };
222
+
206
223
  class McFlushHandler : public Handler {
207
224
  public:
208
225
  HANDLER_DESCRIPTION("Flush a memcached bucket")
@@ -280,9 +297,9 @@ private:
280
297
 
281
298
  class N1qlHandler : public Handler {
282
299
  public:
283
- N1qlHandler() : Handler("view"), o_args("qarg"), o_opts("qopt"),
284
- o_prepare("prepare") {}
285
- HANDLER_DESCRIPTION("Execute a N1QL Query")
300
+ N1qlHandler() : Handler("query"), o_args("qarg"), o_opts("qopt"),
301
+ o_prepare("prepare"), o_analytics("analytics") {}
302
+ HANDLER_DESCRIPTION("Execute a N1QL/Analytics Query")
286
303
  HANDLER_USAGE("QUERY [--qarg PARAM1=VALUE1 --qopt PARAM2=VALUE2]")
287
304
 
288
305
  protected:
@@ -299,15 +316,18 @@ protected:
299
316
  o_opts.abbrev('Q');
300
317
 
301
318
  o_prepare.description("Prepare query before issuing");
319
+ o_analytics.description("Perform query to analytics service");
302
320
 
303
321
  parser.addOption(o_args);
304
322
  parser.addOption(o_opts);
305
323
  parser.addOption(o_prepare);
324
+ parser.addOption(o_analytics);
306
325
  }
307
326
  private:
308
327
  cliopts::ListOption o_args;
309
328
  cliopts::ListOption o_opts;
310
329
  cliopts::BoolOption o_prepare;
330
+ cliopts::BoolOption o_analytics;
311
331
  };
312
332
 
313
333
  class HttpReceiver {
@@ -122,7 +122,7 @@ private:
122
122
 
123
123
  last_update = now;
124
124
 
125
- const char *prefix = use_ansi_codes ? "\x1B[K" : "";
125
+ const char *prefix;
126
126
  const char *final_suffix;
127
127
 
128
128
  // Only use "ticker" style updates if we're a TTY and we have no
@@ -118,6 +118,7 @@ public:
118
118
  o_writeJson("json"),
119
119
  o_templatePairs("template"),
120
120
  o_subdoc("subdoc"),
121
+ o_noop("noop"),
121
122
  o_sdPathCount("pathcount"),
122
123
  o_populateOnly("populate-only"),
123
124
  o_exptime("expiry")
@@ -141,6 +142,7 @@ public:
141
142
  o_templatePairs.description("Values for templates to be inserted into user documents");
142
143
  o_templatePairs.argdesc("FIELD,MIN,MAX[,SEQUENTIAL]").hide();
143
144
  o_subdoc.description("Use subdoc instead of fulldoc operations");
145
+ o_noop.description("Use NOOP instead of document operations").setDefault(0);
144
146
  o_sdPathCount.description("Number of subdoc paths per command").setDefault(1);
145
147
  o_populateOnly.description("Exit after documents have been populated");
146
148
  o_exptime.description("Set TTL for items").abbrev('e');
@@ -256,6 +258,7 @@ public:
256
258
  parser.addOption(o_writeJson);
257
259
  parser.addOption(o_templatePairs);
258
260
  parser.addOption(o_subdoc);
261
+ parser.addOption(o_noop);
259
262
  parser.addOption(o_sdPathCount);
260
263
  parser.addOption(o_populateOnly);
261
264
  parser.addOption(o_exptime);
@@ -278,6 +281,7 @@ public:
278
281
  bool shouldPauseAtEnd() { return o_pauseAtEnd; }
279
282
  bool sequentialAccess() { return o_sequential; }
280
283
  bool isSubdoc() { return o_subdoc; }
284
+ bool isNoop() { return o_noop.result(); }
281
285
  unsigned firstKeyOffset() { return o_startAt; }
282
286
  uint32_t getNumItems() { return o_numItems; }
283
287
  uint32_t getRateLimit() { return o_rateLimit; }
@@ -319,6 +323,7 @@ private:
319
323
  // List of template ranges for value generation
320
324
  ListOption o_templatePairs;
321
325
  BoolOption o_subdoc;
326
+ BoolOption o_noop;
322
327
  UIntOption o_sdPathCount;
323
328
 
324
329
  // Compound option
@@ -394,17 +399,51 @@ struct NextOp {
394
399
  vector<lcb_IOV> m_valuefrags;
395
400
  vector<lcb_SDSPEC> m_specs;
396
401
  // The mode here is for future use with subdoc
397
- enum Mode { STORE, GET, SDSTORE, SDGET };
402
+ enum Mode { STORE, GET, SDSTORE, SDGET, NOOP };
398
403
  Mode m_mode;
399
404
  };
400
405
 
406
+ class OpGenerator {
407
+ public:
408
+ OpGenerator(int id): m_id(id) {}
409
+
410
+ virtual ~OpGenerator() {};
411
+ virtual void setNextOp(NextOp& op) = 0;
412
+ virtual void setValue(NextOp& op) = 0;
413
+ virtual bool inPopulation() const = 0;
414
+ virtual const char *getStageString() const = 0;
415
+
416
+ protected:
417
+ int m_id;
418
+ };
419
+
420
+ class NoopGenerator : public OpGenerator {
421
+ public:
422
+ NoopGenerator(int ix) : OpGenerator(ix) {}
423
+
424
+
425
+ void setNextOp(NextOp& op) {
426
+ op.m_mode = NextOp::NOOP;
427
+ }
428
+
429
+ void setValue(NextOp&) {}
430
+
431
+ bool inPopulation() const {
432
+ return false;
433
+ }
434
+
435
+ const char *getStageString() const {
436
+ return "Run";
437
+ }
438
+ };
439
+
401
440
  /** Stateful, per-thread generator */
402
- class KeyGenerator {
441
+ class KeyGenerator : public OpGenerator {
403
442
  public:
404
443
  KeyGenerator(int ix)
405
- : m_gencount(0), m_force_sequential(false),
444
+ : OpGenerator(ix), m_gencount(0), m_force_sequential(false),
406
445
  m_in_population(config.shouldPopulate)
407
- {
446
+ {
408
447
  srand(config.getRandomSeed());
409
448
 
410
449
  m_genrandom = new SeqGenerator(
@@ -423,7 +462,6 @@ public:
423
462
  m_force_sequential = config.sequentialAccess();
424
463
  }
425
464
 
426
- m_id = ix;
427
465
  m_local_genstate = config.docgen->createState(config.getNumThreads(), ix);
428
466
  if (config.isSubdoc()) {
429
467
  m_mode_read = NextOp::SDGET;
@@ -439,6 +477,10 @@ public:
439
477
  }
440
478
  }
441
479
 
480
+ void setValue(NextOp& op) {
481
+ m_local_genstate->populateIov(op.m_seqno, op.m_valuefrags);
482
+ }
483
+
442
484
  void setNextOp(NextOp& op) {
443
485
  bool store_override = false;
444
486
 
@@ -461,12 +503,12 @@ public:
461
503
  if (store_override) {
462
504
  // Populate
463
505
  op.m_mode = NextOp::STORE;
464
- m_local_genstate->populateIov(op.m_seqno, op.m_valuefrags);
506
+ setValue(op);
465
507
 
466
508
  } else if (shouldStore(op.m_seqno)) {
467
509
  op.m_mode = m_mode_write;
468
510
  if (op.m_mode == NextOp::STORE) {
469
- m_local_genstate->populateIov(op.m_seqno, op.m_valuefrags);
511
+ setValue(op);
470
512
  } else if (op.m_mode == NextOp::SDSTORE) {
471
513
  op.m_specs.resize(config.sdOpsPerCmd);
472
514
  m_sdgenstate->populateMutate(op.m_seqno, op.m_specs);
@@ -485,6 +527,19 @@ public:
485
527
  generateKey(op);
486
528
  }
487
529
 
530
+ bool inPopulation() const {
531
+ return m_in_population;
532
+ }
533
+
534
+ const char *getStageString() const {
535
+ if (m_in_population) {
536
+ return "Populate";
537
+ } else {
538
+ return "Run";
539
+ }
540
+ }
541
+
542
+ private:
488
543
  bool shouldStore(uint32_t seqno) {
489
544
  if (config.setprc == 0) {
490
545
  return false;
@@ -502,15 +557,7 @@ public:
502
557
  op.m_key.assign(config.getKeyPrefix() + buffer);
503
558
  }
504
559
 
505
- const char *getStageString() const {
506
- if (m_in_population) {
507
- return "Populate";
508
- } else {
509
- return "Run";
510
- }
511
- }
512
560
 
513
- private:
514
561
  SeqGenerator *m_genrandom;
515
562
  SeqGenerator *m_gensequence;
516
563
  size_t m_gencount;
@@ -527,18 +574,31 @@ private:
527
574
  class ThreadContext
528
575
  {
529
576
  public:
530
- ThreadContext(lcb_t handle, int ix) : kgen(ix), niter(0), instance(handle) {
577
+ ThreadContext(lcb_t handle, int ix) : niter(0), instance(handle) {
578
+ if (config.isNoop()) {
579
+ gen = new NoopGenerator(ix);
580
+ } else {
581
+ gen = new KeyGenerator(ix);
582
+ }
583
+ }
531
584
 
585
+ ~ThreadContext() {
586
+ delete gen;
587
+ gen = NULL;
588
+ }
589
+
590
+ bool inPopulation() {
591
+ return gen && (gen->inPopulation() || !retryq.empty());
532
592
  }
533
593
 
534
594
  void singleLoop() {
535
595
  bool hasItems = false;
536
- lcb_sched_enter(instance);
537
596
  NextOp opinfo;
538
597
  unsigned exptime = config.getExptime();
539
598
 
599
+ lcb_sched_enter(instance);
540
600
  for (size_t ii = 0; ii < config.opsPerCycle; ++ii) {
541
- kgen.setNextOp(opinfo);
601
+ gen->setNextOp(opinfo);
542
602
 
543
603
  switch (opinfo.m_mode) {
544
604
  case NextOp::STORE: {
@@ -569,6 +629,11 @@ public:
569
629
  error = lcb_subdoc3(instance, this, &sdcmd);
570
630
  break;
571
631
  }
632
+ case NextOp::NOOP: {
633
+ lcb_CMDNOOP ncmd = { 0 };
634
+ error = lcb_noop3(instance, this, &ncmd);
635
+ break;
636
+ }
572
637
  }
573
638
 
574
639
  if (error != LCB_SUCCESS) {
@@ -587,6 +652,25 @@ public:
587
652
  } else {
588
653
  lcb_sched_fail(instance);
589
654
  }
655
+
656
+ while (!retryq.empty()) {
657
+ lcb_sched_enter(instance);
658
+ while (!retryq.empty()) {
659
+ opinfo = retryq.front();
660
+ retryq.pop();
661
+ lcb_CMDSTORE scmd = { 0 };
662
+ scmd.operation = LCB_SET;
663
+ scmd.exptime = exptime;
664
+ LCB_CMD_SET_KEY(&scmd, opinfo.m_key.c_str(), opinfo.m_key.size());
665
+ LCB_CMD_SET_VALUEIOV(&scmd, &opinfo.m_valuefrags[0], opinfo.m_valuefrags.size());
666
+ error = lcb_store3(instance, this, &scmd);
667
+ }
668
+ lcb_sched_leave(instance);
669
+ lcb_wait(instance);
670
+ if (error != LCB_SUCCESS) {
671
+ log("Operation(s) failed: [0x%x] %s", error, lcb_strerror(instance, error));
672
+ }
673
+ }
590
674
  }
591
675
 
592
676
  bool run() {
@@ -594,7 +678,7 @@ public:
594
678
  singleLoop();
595
679
 
596
680
  if (config.isTimings()) {
597
- InstanceCookie::dumpTimings(instance, kgen.getStageString());
681
+ InstanceCookie::dumpTimings(instance, gen->getStageString());
598
682
  }
599
683
  if (config.params.shouldDump()) {
600
684
  lcb_dump(instance, stderr, LCB_DUMP_ALL);
@@ -606,11 +690,18 @@ public:
606
690
  } while (!config.isLoopDone(++niter));
607
691
 
608
692
  if (config.isTimings()) {
609
- InstanceCookie::dumpTimings(instance, kgen.getStageString(), true);
693
+ InstanceCookie::dumpTimings(instance, gen->getStageString(), true);
610
694
  }
611
695
  return true;
612
696
  }
613
697
 
698
+ void retry(NextOp &op) {
699
+ if (op.m_mode == NextOp::STORE) {
700
+ gen->setValue(op);
701
+ }
702
+ retryq.push(op);
703
+ }
704
+
614
705
  #ifndef WIN32
615
706
  pthread_t thr;
616
707
  #endif
@@ -645,31 +736,39 @@ private:
645
736
  previous_time = now;
646
737
  }
647
738
 
648
- KeyGenerator kgen;
739
+ OpGenerator *gen;
649
740
  size_t niter;
650
741
  lcb_error_t error;
651
742
  lcb_t instance;
743
+ std::queue<NextOp> retryq;
652
744
  };
653
745
 
654
- static void operationCallback(lcb_t, int, const lcb_RESPBASE *resp)
746
+ static void operationCallback(lcb_t, int cbtype, const lcb_RESPBASE *resp)
655
747
  {
656
748
  ThreadContext *tc;
657
749
 
658
750
  tc = const_cast<ThreadContext *>(reinterpret_cast<const ThreadContext *>(resp->cookie));
659
- tc->setError(resp->rc);
751
+ if (cbtype == LCB_CALLBACK_STORE && resp->rc != LCB_SUCCESS && tc->inPopulation()) {
752
+ NextOp op;
753
+ op.m_mode = NextOp::STORE;
754
+ op.m_key.assign((char *)resp->key, resp->nkey);
755
+ op.m_seqno = atoi(op.m_key.c_str());
756
+ tc->retry(op);
757
+ } else {
758
+ tc->setError(resp->rc);
759
+ }
660
760
 
661
761
  #ifndef WIN32
662
762
  static volatile unsigned long nops = 1;
663
763
  static time_t start_time = time(NULL);
664
- static int is_tty = isatty(STDOUT_FILENO);
764
+ static int is_tty = isatty(STDERR_FILENO);
665
765
  if (is_tty) {
666
766
  if (++nops % 1000 == 0) {
667
767
  time_t now = time(NULL);
668
768
  time_t nsecs = now - start_time;
669
769
  if (!nsecs) { nsecs = 1; }
670
770
  unsigned long ops_sec = nops / nsecs;
671
- printf("OPS/SEC: %10lu\r", ops_sec);
672
- fflush(stdout);
771
+ fprintf(stderr, "OPS/SEC: %10lu\r", ops_sec);
673
772
  }
674
773
  }
675
774
  #endif
@@ -795,6 +894,7 @@ int main(int argc, char **argv)
795
894
  lcb_install_callback3(instance, LCB_CALLBACK_GET, operationCallback);
796
895
  lcb_install_callback3(instance, LCB_CALLBACK_SDMUTATE, operationCallback);
797
896
  lcb_install_callback3(instance, LCB_CALLBACK_SDLOOKUP, operationCallback);
897
+ lcb_install_callback3(instance, LCB_CALLBACK_NOOP, operationCallback);
798
898
  cp.doCtls(instance);
799
899
 
800
900
  new InstanceCookie(instance);
@@ -0,0 +1,403 @@
1
+ /*
2
+ * Copyright 2017 Couchbase, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #define LCB_NO_DEPR_CXX_CTORS
18
+
19
+ #include "common/my_inttypes.h"
20
+ #include "config.h"
21
+ #include <sys/types.h>
22
+ #include <libcouchbase/couchbase.h>
23
+ #include <libcouchbase/vbucket.h>
24
+ #include <libcouchbase/api3.h>
25
+ #include <libcouchbase/pktfwd.h>
26
+ #include <memcached/protocol_binary.h>
27
+ #include <iostream>
28
+ #include <iomanip>
29
+ #include <cstdio>
30
+ #include <cerrno>
31
+ #include <sstream>
32
+ #include <signal.h>
33
+ #include "common/options.h"
34
+ #include "common/histogram.h"
35
+
36
+ #include "internal.h"
37
+
38
+ #include <event2/event.h>
39
+ #include <event2/listener.h>
40
+ #include <event2/bufferevent.h>
41
+ #include <event2/buffer.h>
42
+
43
+ using namespace cbc;
44
+ using namespace cliopts;
45
+
46
+ static void die(const char *msg)
47
+ {
48
+ fprintf(stderr, "%s\n", msg);
49
+ exit(EXIT_FAILURE);
50
+ }
51
+
52
+ static void good_or_die(lcb_error_t rc, const char *msg = "")
53
+ {
54
+ if (rc != LCB_SUCCESS) {
55
+ fprintf(stderr, "%s\n0x%02x: %s\n", msg, rc, lcb_strerror(NULL, rc));
56
+ exit(EXIT_FAILURE);
57
+ }
58
+ }
59
+
60
+ static lcb_t instance = NULL;
61
+ static struct event_base *evbase = NULL;
62
+ static Histogram hg;
63
+
64
+ #define LOGARGS(lvl) (instance)->settings, "proxy", LCB_LOG_##lvl, __FILE__, __LINE__
65
+ #define CL_LOGFMT "<%s:%s> (cl=%p,fd=%d) "
66
+ #define CL_LOGID(cl) cl->host, cl->port, (void *)cl, cl->fd
67
+
68
+ class Configuration
69
+ {
70
+ public:
71
+ Configuration() : o_trace("trace"), o_port("port")
72
+ {
73
+ o_trace.abbrev('t').description("Show packet trace on INFO log level");
74
+ o_port.abbrev('p').description("Port for proxy").setDefault(11211);
75
+ }
76
+
77
+ ~Configuration()
78
+ {
79
+ }
80
+
81
+ void addToParser(Parser &parser)
82
+ {
83
+ m_params.addToParser(parser);
84
+ parser.addOption(o_trace);
85
+ parser.addOption(o_port);
86
+ }
87
+
88
+ void processOptions()
89
+ {
90
+ }
91
+
92
+ void fillCropts(lcb_create_st &opts)
93
+ {
94
+ m_params.fillCropts(opts);
95
+ }
96
+ lcb_error_t doCtls()
97
+ {
98
+ return m_params.doCtls(instance);
99
+ }
100
+ bool useTimings()
101
+ {
102
+ return m_params.useTimings();
103
+ }
104
+ bool shouldDump()
105
+ {
106
+ return m_params.shouldDump();
107
+ }
108
+
109
+ bool isTrace()
110
+ {
111
+ return o_trace.result();
112
+ }
113
+
114
+ unsigned port()
115
+ {
116
+ return o_port.result();
117
+ }
118
+
119
+ private:
120
+ ConnParams m_params;
121
+ BoolOption o_trace;
122
+ UIntOption o_port;
123
+ };
124
+
125
+ static Configuration config;
126
+
127
+ static struct evconnlistener *listener = NULL;
128
+
129
+ static void cleanup()
130
+ {
131
+ if (instance) {
132
+ if (config.shouldDump()) {
133
+ lcb_dump(instance, stderr, LCB_DUMP_ALL);
134
+ }
135
+ if (config.useTimings()) {
136
+ hg.write();
137
+ }
138
+ if (instance) {
139
+ lcb_destroy(instance);
140
+ }
141
+ }
142
+ if (listener) {
143
+ evconnlistener_free(listener);
144
+ }
145
+ if (evbase) {
146
+ event_base_free(evbase);
147
+ }
148
+ }
149
+
150
+ struct client {
151
+ int fd;
152
+ struct bufferevent *bev;
153
+ char host[NI_MAXHOST + 1];
154
+ char port[NI_MAXSERV + 1];
155
+ };
156
+
157
+ static void dump_bytes(const struct client *cl, const char *msg, void *ptr, size_t len)
158
+ {
159
+ if (!config.isTrace()) {
160
+ return;
161
+ }
162
+
163
+ int width = 16;
164
+ const unsigned char *buf = (const unsigned char *)ptr;
165
+ size_t full_rows = len / width;
166
+ size_t remainder = len % width;
167
+ std::stringstream ss;
168
+
169
+ ss << msg << ", " << len << " bytes\n"
170
+ " +-------------------------------------------------+\n"
171
+ " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |\n"
172
+ " +--------+-------------------------------------------------+----------------+";
173
+
174
+ unsigned int row = 0;
175
+ while (row < full_rows) {
176
+ int row_start_index = row * width;
177
+ // prefix
178
+ ss << "\n |" << std::setw(8) << std::setfill('0') << std::hex << row_start_index << "|";
179
+ int row_end_index = row_start_index + width;
180
+ // hex
181
+ int i = row_start_index;
182
+ while (i < row_end_index) {
183
+ ss << " " << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)buf[i++];
184
+ }
185
+ ss << " |";
186
+ // ascii
187
+ i = row_start_index;
188
+ while (i < row_end_index) {
189
+ char b = buf[i++];
190
+ if ((b <= 0x1f) || (b >= 0x7f)) {
191
+ ss << '.';
192
+ } else {
193
+ ss << b;
194
+ }
195
+ }
196
+ ss << "|";
197
+ row++;
198
+ }
199
+ if (remainder != 0) {
200
+ int row_start_index = full_rows * width;
201
+ // prefix
202
+ ss << "\n |" << std::setw(8) << std::setfill('0') << std::hex << row_start_index << "|";
203
+ int row_end_index = row_start_index + remainder;
204
+ // hex
205
+ int i = row_start_index;
206
+ while (i < row_end_index) {
207
+ ss << " " << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)buf[i++];
208
+ }
209
+ i = width - remainder;
210
+ while (i > 0) {
211
+ ss << " ";
212
+ i--;
213
+ }
214
+ ss << " |";
215
+ // ascii
216
+ i = row_start_index;
217
+ while (i < row_end_index) {
218
+ char b = buf[i++];
219
+ if ((b <= 0x1f) || (b >= 0x7f)) {
220
+ ss << '.';
221
+ } else {
222
+ ss << b;
223
+ }
224
+ }
225
+ i = width - remainder;
226
+ while (i > 0) {
227
+ ss << " ";
228
+ i--;
229
+ }
230
+ ss << "|";
231
+ }
232
+ ss << "\n +--------+-------------------------------------------------+----------------+";
233
+ lcb_log(LOGARGS(INFO), CL_LOGFMT "%s", CL_LOGID(cl), ss.str().c_str());
234
+ }
235
+
236
+ static void pktfwd_callback(lcb_t, const void *cookie, lcb_error_t err, lcb_PKTFWDRESP *resp)
237
+ {
238
+ good_or_die(err, "Failed to forward a packet");
239
+
240
+ struct client *cl = (struct client *)cookie;
241
+ struct evbuffer *output = bufferevent_get_output(cl->bev);
242
+ for (unsigned ii = 0; ii < resp->nitems; ii++) {
243
+ dump_bytes(cl, "response", resp->iovs[ii].iov_base, resp->iovs[ii].iov_len);
244
+ evbuffer_expand(output, resp->iovs[ii].iov_len);
245
+ evbuffer_add(output, resp->iovs[ii].iov_base, resp->iovs[ii].iov_len);
246
+ }
247
+ }
248
+
249
+ static void conn_readcb(struct bufferevent *bev, void *cookie)
250
+ {
251
+ struct client *cl = (struct client *)cookie;
252
+ struct evbuffer *input;
253
+ size_t len;
254
+
255
+ input = bufferevent_get_input(bev);
256
+ len = evbuffer_get_length(input);
257
+ if (len < 24) {
258
+ lcb_log(LOGARGS(DEBUG), CL_LOGFMT "not enough data for header", CL_LOGID(cl));
259
+ return;
260
+ }
261
+
262
+ protocol_binary_request_header header;
263
+ evbuffer_copyout(input, &header, sizeof(header));
264
+ lcb_U32 bodylen = ntohl(header.request.bodylen);
265
+
266
+ size_t pktlen = sizeof(header) + bodylen;
267
+ len = evbuffer_get_length(input);
268
+ if (len < pktlen) {
269
+ lcb_log(LOGARGS(DEBUG), CL_LOGFMT "not enough data for packet", CL_LOGID(cl));
270
+ return;
271
+ }
272
+ void *pkt = malloc(pktlen);
273
+ evbuffer_remove(input, pkt, pktlen);
274
+
275
+ lcb_sched_enter(instance);
276
+ lcb_CMDPKTFWD cmd = {0};
277
+ cmd.vb.vtype = LCB_KV_COPY;
278
+ cmd.vb.u_buf.contig.bytes = pkt;
279
+ cmd.vb.u_buf.contig.nbytes = pktlen;
280
+ dump_bytes(cl, "request", pkt, pktlen);
281
+ good_or_die(lcb_pktfwd3(instance, cl, &cmd), "Failed to forward packet");
282
+ lcb_sched_leave(instance);
283
+ }
284
+
285
+ static void conn_eventcb(struct bufferevent *bev, short events, void *cookie)
286
+ {
287
+ struct client *cl = (struct client *)cookie;
288
+
289
+ if (events & BEV_EVENT_EOF) {
290
+ lcb_log(LOGARGS(INFO), CL_LOGFMT "connection closed", CL_LOGID(cl));
291
+ bufferevent_free(bev);
292
+ delete cl;
293
+ } else if (events & BEV_EVENT_ERROR) {
294
+ lcb_log(LOGARGS(ERROR), CL_LOGFMT "got an error on the connection: %s\n", CL_LOGID(cl), strerror(errno));
295
+ bufferevent_free(bev);
296
+ delete cl;
297
+ } else {
298
+ lcb_log(LOGARGS(DEBUG), CL_LOGFMT "ignore event 0x%02x", CL_LOGID(cl), events);
299
+ }
300
+ }
301
+
302
+ static void listener_cb(struct evconnlistener *, evutil_socket_t fd, struct sockaddr *addr, int naddr, void *)
303
+ {
304
+ struct bufferevent *bev;
305
+ bev = bufferevent_socket_new(evbase, fd, BEV_OPT_CLOSE_ON_FREE);
306
+
307
+ if (!bev) {
308
+ die("Error constructing bufferevent");
309
+ }
310
+
311
+ struct client *cl = new client();
312
+ cl->fd = fd;
313
+ cl->bev = bev;
314
+ getnameinfo(addr, naddr, cl->host, sizeof(cl->host), cl->port, sizeof(cl->port), NI_NUMERICHOST | NI_NUMERICSERV);
315
+ bufferevent_setcb(bev, conn_readcb, NULL, conn_eventcb, cl);
316
+ bufferevent_enable(bev, EV_READ | EV_WRITE);
317
+ lcb_log(LOGARGS(INFO), CL_LOGFMT "new client connection", CL_LOGID(cl));
318
+ }
319
+
320
+ static void setup_listener()
321
+ {
322
+ struct sockaddr_in sin;
323
+
324
+ memset(&sin, 0, sizeof(sin));
325
+ sin.sin_family = AF_INET;
326
+ sin.sin_port = htons(config.port());
327
+
328
+ listener = evconnlistener_new_bind(evbase, listener_cb, NULL, LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -1,
329
+ (struct sockaddr *)&sin, sizeof(sin));
330
+ if (!listener) {
331
+ die("Failed to create proxy listener");
332
+ }
333
+ lcb_log(LOGARGS(INFO), "Listening incoming proxy connections on port %d", config.port());
334
+ }
335
+
336
+ static void bootstrap_callback(lcb_t, lcb_error_t err)
337
+ {
338
+ good_or_die(err, "Failed to bootstrap");
339
+ lcb_log(LOGARGS(INFO), "connected to Couchbase Server");
340
+ setup_listener();
341
+ }
342
+
343
+ static int terminating = 0;
344
+ static void sigint_handler(int)
345
+ {
346
+ lcb_log(LOGARGS(INFO), "terminating the server");
347
+ if (!terminating) {
348
+ event_base_loopbreak(evbase);
349
+ terminating = 1;
350
+ }
351
+ }
352
+
353
+ static void real_main(int argc, char **argv)
354
+ {
355
+ Parser parser;
356
+
357
+ config.addToParser(parser);
358
+ parser.parse(argc, argv);
359
+ config.processOptions();
360
+
361
+ lcb_create_st cropts;
362
+ memset(&cropts, 0, sizeof cropts);
363
+ config.fillCropts(cropts);
364
+
365
+ /* bind to external libevent loop */
366
+ evbase = event_base_new();
367
+ struct lcb_create_io_ops_st ciops;
368
+ memset(&ciops, 0, sizeof(ciops));
369
+ ciops.v.v0.type = LCB_IO_OPS_LIBEVENT;
370
+ ciops.v.v0.cookie = evbase;
371
+ good_or_die(lcb_create_io_ops(&cropts.v.v3.io, &ciops), "Failed to create and IO ops strucutre for libevent");
372
+
373
+ good_or_die(lcb_create(&instance, &cropts), "Failed to create connection");
374
+ config.doCtls();
375
+ lcb_set_bootstrap_callback(instance, bootstrap_callback);
376
+ lcb_set_pktfwd_callback(instance, pktfwd_callback);
377
+
378
+ good_or_die(lcb_connect(instance), "Failed to connect to cluster");
379
+ if (config.useTimings()) {
380
+ hg.install(instance, stdout);
381
+ }
382
+ std::atexit(cleanup);
383
+
384
+ /* setup CTRL-C handler */
385
+ struct sigaction action;
386
+ sigemptyset(&action.sa_mask);
387
+ action.sa_handler = sigint_handler;
388
+ action.sa_flags = 0;
389
+ sigaction(SIGINT, &action, NULL);
390
+
391
+ event_base_dispatch(evbase);
392
+ }
393
+
394
+ int main(int argc, char **argv)
395
+ {
396
+ try {
397
+ real_main(argc, argv);
398
+ return 0;
399
+ } catch (std::exception &exc) {
400
+ std::cerr << exc.what() << std::endl;
401
+ exit(EXIT_FAILURE);
402
+ }
403
+ }