libcouchbase 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|