libcouchbase 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +11 -8
- data/ext/libcouchbase/CMakeLists.txt +1 -1
- data/ext/libcouchbase/README.markdown +38 -6
- data/ext/libcouchbase/RELEASE_NOTES.markdown +151 -0
- data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -2
- data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
- data/ext/libcouchbase/cmake/source_files.cmake +1 -0
- data/ext/libcouchbase/contrib/cJSON/cJSON.c +686 -288
- data/ext/libcouchbase/contrib/cJSON/cJSON.h +0 -0
- data/ext/libcouchbase/contrib/cbsasl/src/hash.c +17 -17
- data/ext/libcouchbase/contrib/cliopts/cliopts.c +76 -0
- data/ext/libcouchbase/contrib/cliopts/cliopts.h +66 -15
- data/ext/libcouchbase/contrib/genhash/genhash.c +1 -2
- data/ext/libcouchbase/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp +4 -3
- data/ext/libcouchbase/example/instancepool/main.cc +12 -2
- data/ext/libcouchbase/example/libeventdirect/main.c +99 -25
- data/ext/libcouchbase/example/minimal/minimal.c +7 -5
- data/ext/libcouchbase/example/observe/durability.c +102 -0
- data/ext/libcouchbase/example/observe/observe.c +19 -6
- data/ext/libcouchbase/example/subdoc/subdoc-xattrs.c +1 -2
- data/ext/libcouchbase/include/libcouchbase/cntl-private.h +6 -8
- data/ext/libcouchbase/include/libcouchbase/cntl.h +84 -64
- data/ext/libcouchbase/include/libcouchbase/couchbase.h +295 -78
- data/ext/libcouchbase/include/libcouchbase/deprecated.h +2 -2
- data/ext/libcouchbase/include/libcouchbase/error.h +1 -1
- data/ext/libcouchbase/include/libcouchbase/iops.h +9 -9
- data/ext/libcouchbase/include/libcouchbase/ixmgmt.h +2 -2
- data/ext/libcouchbase/include/libcouchbase/n1ql.h +69 -7
- data/ext/libcouchbase/include/libcouchbase/vbucket.h +17 -0
- data/ext/libcouchbase/include/libcouchbase/views.h +3 -3
- data/ext/libcouchbase/include/memcached/protocol_binary.h +62 -1
- data/ext/libcouchbase/packaging/deb/control +1 -1
- data/ext/libcouchbase/packaging/rpm/libcouchbase.spec.in +37 -36
- data/ext/libcouchbase/src/bootstrap.cc +22 -8
- data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +1 -1
- data/ext/libcouchbase/src/bucketconfig/bc_http.cc +0 -1
- data/ext/libcouchbase/src/bucketconfig/confmon.cc +13 -8
- data/ext/libcouchbase/src/callbacks.c +2 -0
- data/ext/libcouchbase/src/cntl.cc +28 -17
- data/ext/libcouchbase/src/dns-srv.cc +1 -2
- data/ext/libcouchbase/src/dump.cc +4 -0
- data/ext/libcouchbase/src/errmap.h +89 -16
- data/ext/libcouchbase/src/handler.cc +28 -11
- data/ext/libcouchbase/src/http/http-priv.h +4 -1
- data/ext/libcouchbase/src/http/http.cc +3 -0
- data/ext/libcouchbase/src/instance.cc +1 -1
- data/ext/libcouchbase/src/internal.h +1 -0
- data/ext/libcouchbase/src/lcbio/connect.cc +2 -3
- data/ext/libcouchbase/src/lcbio/manager.cc +2 -2
- data/ext/libcouchbase/src/lcbio/ssl.h +10 -0
- data/ext/libcouchbase/src/mc/mcreq.c +8 -0
- data/ext/libcouchbase/src/mcserver/mcserver.cc +14 -1
- data/ext/libcouchbase/src/n1ql/ixmgmt.cc +0 -3
- data/ext/libcouchbase/src/n1ql/n1ql.cc +22 -29
- data/ext/libcouchbase/src/n1ql/params.cc +46 -1
- data/ext/libcouchbase/src/newconfig.cc +4 -4
- data/ext/libcouchbase/src/operations/durability-seqno.cc +4 -0
- data/ext/libcouchbase/src/operations/durability.cc +3 -0
- data/ext/libcouchbase/src/operations/ping.cc +315 -0
- data/ext/libcouchbase/src/operations/stats.cc +10 -0
- data/ext/libcouchbase/src/operations/subdoc.cc +13 -1
- data/ext/libcouchbase/src/retrychk.cc +1 -0
- data/ext/libcouchbase/src/settings.c +2 -0
- data/ext/libcouchbase/src/settings.h +13 -7
- data/ext/libcouchbase/src/ssl/ssl_c.c +28 -2
- data/ext/libcouchbase/src/ssl/ssl_common.c +3 -0
- data/ext/libcouchbase/src/ssl/ssl_e.c +15 -1
- data/ext/libcouchbase/src/ssl/ssl_iot_common.h +3 -1
- data/ext/libcouchbase/src/timings.c +0 -1
- data/ext/libcouchbase/src/vbucket/vbucket.c +49 -1
- data/ext/libcouchbase/tests/iotests/mock-environment.cc +58 -40
- data/ext/libcouchbase/tests/iotests/mock-environment.h +23 -4
- data/ext/libcouchbase/tests/iotests/mock-unit-test.h +8 -8
- data/ext/libcouchbase/tests/iotests/t_behavior.cc +5 -5
- data/ext/libcouchbase/tests/iotests/t_durability.cc +50 -0
- data/ext/libcouchbase/tests/iotests/t_eerrs.cc +4 -2
- data/ext/libcouchbase/tests/iotests/t_errmap.cc +6 -3
- data/ext/libcouchbase/tests/iotests/t_lock.cc +5 -6
- data/ext/libcouchbase/tests/iotests/t_misc.cc +44 -0
- data/ext/libcouchbase/tests/iotests/t_serverops.cc +1 -0
- data/ext/libcouchbase/tests/iotests/t_subdoc.cc +28 -0
- data/ext/libcouchbase/tests/iotests/t_views.cc +22 -10
- data/ext/libcouchbase/tools/CMakeLists.txt +21 -1
- data/ext/libcouchbase/tools/cbc-handlers.h +23 -3
- data/ext/libcouchbase/tools/cbc-n1qlback.cc +1 -1
- data/ext/libcouchbase/tools/cbc-pillowfight.cc +126 -26
- data/ext/libcouchbase/tools/cbc-proxy.cc +403 -0
- data/ext/libcouchbase/tools/cbc-subdoc.cc +826 -0
- data/ext/libcouchbase/tools/cbc.cc +149 -37
- data/ext/libcouchbase/tools/common/options.h +5 -2
- data/ext/libcouchbase/tools/linenoise/linenoise.c +15 -15
- data/lib/libcouchbase.rb +4 -0
- data/lib/libcouchbase/bucket.rb +51 -0
- data/lib/libcouchbase/connection.rb +100 -13
- data/lib/libcouchbase/ext/libcouchbase.rb +40 -0
- data/lib/libcouchbase/ext/libcouchbase/cmdsubdoc.rb +13 -1
- data/lib/libcouchbase/ext/libcouchbase/enums.rb +2 -1
- data/lib/libcouchbase/ext/libcouchbase/sdspec.rb +5 -0
- data/lib/libcouchbase/subdoc_request.rb +129 -0
- data/lib/libcouchbase/version.rb +1 -1
- data/spec/bucket_spec.rb +15 -1
- data/spec/connection_spec.rb +1 -1
- data/spec/subdoc_spec.rb +192 -0
- metadata +13 -4
- 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("
|
284
|
-
|
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 {
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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) :
|
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
|
-
|
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,
|
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,
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
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
|
+
}
|