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
@@ -78,7 +78,8 @@ struct Request {
|
|
78
78
|
bool is_data_request() const {
|
79
79
|
return reqtype == LCB_HTTP_TYPE_N1QL ||
|
80
80
|
reqtype == LCB_HTTP_TYPE_VIEW ||
|
81
|
-
reqtype == LCB_HTTP_TYPE_FTS
|
81
|
+
reqtype == LCB_HTTP_TYPE_FTS ||
|
82
|
+
reqtype == LCB_HTTP_TYPE_CBAS;
|
82
83
|
}
|
83
84
|
|
84
85
|
/**
|
@@ -310,6 +311,8 @@ struct Request {
|
|
310
311
|
|
311
312
|
/** overrides default timeout if nonzero */
|
312
313
|
const uint32_t user_timeout;
|
314
|
+
|
315
|
+
hrtime_t start; /**< Start time */
|
313
316
|
};
|
314
317
|
|
315
318
|
} // namespace: http
|
@@ -368,6 +368,8 @@ httype2svctype(unsigned httype)
|
|
368
368
|
return LCBVB_SVCTYPE_N1QL;
|
369
369
|
case LCB_HTTP_TYPE_FTS:
|
370
370
|
return LCBVB_SVCTYPE_FTS;
|
371
|
+
case LCB_HTTP_TYPE_CBAS:
|
372
|
+
return LCBVB_SVCTYPE_CBAS;
|
371
373
|
default:
|
372
374
|
return LCBVB_SVCTYPE__MAX;
|
373
375
|
}
|
@@ -548,6 +550,7 @@ Request::create(lcb_t instance,
|
|
548
550
|
*rc = LCB_CLIENT_ENOMEM;
|
549
551
|
return NULL;
|
550
552
|
}
|
553
|
+
req->start = gethrtime();
|
551
554
|
|
552
555
|
*rc = req->setup_inputs(cmd);
|
553
556
|
if (*rc != LCB_SUCCESS) {
|
@@ -129,7 +129,7 @@ lcb_st::process_dns_srv(Connspec& spec)
|
|
129
129
|
|
130
130
|
const Spechost& host = spec.hosts().front();
|
131
131
|
lcb_error_t rc = LCB_ERROR;
|
132
|
-
Hostlist* hl = dnssrv_getbslist(host.hostname.c_str(),
|
132
|
+
Hostlist* hl = dnssrv_getbslist(host.hostname.c_str(), spec.sslopts() & LCB_SSL_ENABLED, rc);
|
133
133
|
|
134
134
|
if (hl == NULL) {
|
135
135
|
lcb_log(LOGARGS(this, INFO), "DNS SRV lookup failed: %s. Ignore this if not relying on DNS SRV records", lcb_strerror(this, rc));
|
@@ -132,6 +132,7 @@ struct lcb_st {
|
|
132
132
|
lcb_MUTATION_TOKEN *dcpinfo; /**< Mapping of known vbucket to {uuid,seqno} info */
|
133
133
|
lcbio_pTIMER dtor_timer; /**< Asynchronous destruction timer */
|
134
134
|
int type; /**< Type of connection */
|
135
|
+
lcb_BTYPE btype; /**< Type of the bucket */
|
135
136
|
|
136
137
|
#ifdef __cplusplus
|
137
138
|
lcb_settings* getSettings() { return settings; }
|
@@ -302,7 +302,6 @@ E_conncb(lcb_socket_t, short events, void *arg)
|
|
302
302
|
goto GT_NEXTSOCK;
|
303
303
|
|
304
304
|
} else {
|
305
|
-
rv = 0;
|
306
305
|
ai = cs->ai;
|
307
306
|
|
308
307
|
GT_CONNECT:
|
@@ -434,8 +433,8 @@ lcbio_connect(lcbio_TABLE *iot, lcb_settings *settings, const lcb_host_t *dest,
|
|
434
433
|
|
435
434
|
Connstart::Connstart(lcbio_TABLE* iot_, lcb_settings* settings_,
|
436
435
|
const lcb_host_t *dest, uint32_t timeout,
|
437
|
-
lcbio_CONNDONE_cb
|
438
|
-
: user_handler(
|
436
|
+
lcbio_CONNDONE_cb handler_, void *arg)
|
437
|
+
: user_handler(handler_), user_arg(arg), sock(NULL), syserr(0),
|
439
438
|
event(NULL), ev_active(false), in_uhandler(false),
|
440
439
|
ai_root(NULL), ai(NULL), state(CS_PENDING), last_error(LCB_SUCCESS),
|
441
440
|
timer(iot_, this) {
|
@@ -289,8 +289,8 @@ void PoolConnInfo::on_connected(lcbio_SOCKET *sock_, lcb_error_t err) {
|
|
289
289
|
|
290
290
|
if (err != LCB_SUCCESS) {
|
291
291
|
/** If the connection failed, fail out all remaining requests */
|
292
|
-
lcb_list_t *cur, *
|
293
|
-
LCB_LIST_SAFE_FOR(cur,
|
292
|
+
lcb_list_t *cur, *nxt;
|
293
|
+
LCB_LIST_SAFE_FOR(cur, nxt, (lcb_list_t *)&parent->requests) {
|
294
294
|
PoolRequest *req = PoolRequest::from_llnode(cur);
|
295
295
|
lcb_clist_delete(&parent->requests, req);
|
296
296
|
req->sock = NULL;
|
@@ -26,6 +26,16 @@ extern "C" {
|
|
26
26
|
* @file
|
27
27
|
* @brief SSL Socket Routines
|
28
28
|
*/
|
29
|
+
#ifndef LCB_NO_SSL
|
30
|
+
#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L
|
31
|
+
// OpenSSL 1.1 has changed behavior of BIO_get_mem_ptr behavior, so we cannot
|
32
|
+
// apply reduce-memory-copy optimization, and fallback to BIO_write
|
33
|
+
// Reference: https://github.com/openssl/openssl/commit/9fe9d0461ea
|
34
|
+
#define LCB_CAN_OPTIMIZE_SSL_BIO 1
|
35
|
+
#else
|
36
|
+
#define LCB_CAN_OPTIMIZE_SSL_BIO 0
|
37
|
+
#endif
|
38
|
+
#endif
|
29
39
|
|
30
40
|
/**
|
31
41
|
* @ingroup lcbio
|
@@ -362,6 +362,8 @@ mcreq_renew_packet(const mc_PACKET *src)
|
|
362
362
|
assert(vdata == inflated);
|
363
363
|
|
364
364
|
if (rv != 0) {
|
365
|
+
/* TODO: log error details when snappy will be enabled */
|
366
|
+
free(edst);
|
365
367
|
return NULL;
|
366
368
|
}
|
367
369
|
nvdata = n_inflated;
|
@@ -461,6 +463,9 @@ mcreq_basic_packet(
|
|
461
463
|
if (!queue->config) {
|
462
464
|
return LCB_CLIENT_ETMPFAIL;
|
463
465
|
}
|
466
|
+
if (!cmd) {
|
467
|
+
return LCB_EINVAL;
|
468
|
+
}
|
464
469
|
|
465
470
|
mcreq_map_key(queue, &cmd->key, &cmd->_hashkey,
|
466
471
|
sizeof(*req) + extlen, &vb, &srvix);
|
@@ -476,6 +481,9 @@ mcreq_basic_packet(
|
|
476
481
|
}
|
477
482
|
|
478
483
|
*packet = mcreq_allocate_packet(*pipeline);
|
484
|
+
if (*packet == NULL) {
|
485
|
+
return LCB_CLIENT_ENOMEM;
|
486
|
+
}
|
479
487
|
|
480
488
|
mcreq_reserve_key(*pipeline, *packet, sizeof(*req) + extlen, &cmd->key);
|
481
489
|
|
@@ -261,6 +261,19 @@ int Server::handle_unknown_error(const mc_PACKET *request,
|
|
261
261
|
newerr = LCB_GENERIC_SUBDOCERR;
|
262
262
|
}
|
263
263
|
|
264
|
+
/* TODO: remove masking LOCKED in 3.0 release */
|
265
|
+
if (err.hasAttribute(errmap::ITEM_LOCKED)) {
|
266
|
+
switch (mcresp.opcode()) {
|
267
|
+
case PROTOCOL_BINARY_CMD_SET:
|
268
|
+
case PROTOCOL_BINARY_CMD_REPLACE:
|
269
|
+
case PROTOCOL_BINARY_CMD_DELETE:
|
270
|
+
newerr = LCB_KEY_EEXISTS;
|
271
|
+
break;
|
272
|
+
default:
|
273
|
+
newerr = LCB_ETMPFAIL;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
264
277
|
int rv = 0;
|
265
278
|
|
266
279
|
if (err.hasAttribute(errmap::AUTO_RETRY)) {
|
@@ -385,7 +398,7 @@ Server::try_read(lcbio_CTX *ctx, rdb_IOROPE *ior)
|
|
385
398
|
/* figure out how many buffers we want to use as an upper limit for the
|
386
399
|
* IOV arrays. Currently we'll keep it simple and ensure the entire
|
387
400
|
* response is contiguous. */
|
388
|
-
lcb_PKTFWDRESP resp = { 0 };
|
401
|
+
lcb_PKTFWDRESP resp = { 0 }; /* TODO: next ABI version should include is_last flag */
|
389
402
|
rdb_ROPESEG *segs;
|
390
403
|
nb_IOV iov;
|
391
404
|
|
@@ -407,9 +407,6 @@ do_index_list(lcb_t instance, const void *cookie, const lcb_CMDN1XMGMT *cmd,
|
|
407
407
|
if (spec.ixtype) {
|
408
408
|
const char *s_ixtype = ixtype_2_str(spec.ixtype);
|
409
409
|
if (s_ixtype == NULL) {
|
410
|
-
if (ctx != NULL) {
|
411
|
-
delete ctx;
|
412
|
-
}
|
413
410
|
return LCB_EINVAL;
|
414
411
|
}
|
415
412
|
ss.append(" using=\"").append(s_ixtype).append("\" AND");
|
@@ -155,9 +155,6 @@ typedef struct lcb_N1QLREQ : lcb::jsparse::Parser::Actions {
|
|
155
155
|
// How many rows were received. Used to avoid parsing the meta
|
156
156
|
size_t nrows;
|
157
157
|
|
158
|
-
// Host for CBAS/Analytics query
|
159
|
-
std::string cbashost;
|
160
|
-
|
161
158
|
/** The PREPARE query itself */
|
162
159
|
struct lcb_N1QLREQ *prepare_req;
|
163
160
|
|
@@ -171,6 +168,9 @@ typedef struct lcb_N1QLREQ : lcb::jsparse::Parser::Actions {
|
|
171
168
|
/** Whether we're retrying this */
|
172
169
|
bool was_retried;
|
173
170
|
|
171
|
+
/** Is this query to Analytics (CBAS) service */
|
172
|
+
bool is_cbas;
|
173
|
+
|
174
174
|
lcb_N1QLCACHE& cache() { return *instance->n1ql_cache; }
|
175
175
|
|
176
176
|
/**
|
@@ -208,6 +208,11 @@ typedef struct lcb_N1QLREQ : lcb::jsparse::Parser::Actions {
|
|
208
208
|
*/
|
209
209
|
inline bool maybe_retry();
|
210
210
|
|
211
|
+
/**
|
212
|
+
* Returns true if payload matches retry conditions.
|
213
|
+
*/
|
214
|
+
inline bool has_retriable_error(const Json::Value& root);
|
215
|
+
|
211
216
|
/**
|
212
217
|
* Did the application request this query to use prepared statements
|
213
218
|
* @return true if using prepared statements
|
@@ -230,10 +235,6 @@ typedef struct lcb_N1QLREQ : lcb::jsparse::Parser::Actions {
|
|
230
235
|
*/
|
231
236
|
inline void fail_prepared(const lcb_RESPN1QL *orig, lcb_error_t err);
|
232
237
|
|
233
|
-
bool is_cbas() const {
|
234
|
-
return !cbashost.empty();
|
235
|
-
}
|
236
|
-
|
237
238
|
inline lcb_N1QLREQ(lcb_t obj, const void *user_cookie, const lcb_CMDN1QL *cmd);
|
238
239
|
inline ~lcb_N1QLREQ();
|
239
240
|
|
@@ -298,8 +299,8 @@ static const char *wtf_magic_strings[] = {
|
|
298
299
|
NULL
|
299
300
|
};
|
300
301
|
|
301
|
-
|
302
|
-
has_retriable_error(const Json::Value& root)
|
302
|
+
bool
|
303
|
+
N1QLREQ::has_retriable_error(const Json::Value& root)
|
303
304
|
{
|
304
305
|
if (!root.isObject()) {
|
305
306
|
return false;
|
@@ -316,9 +317,11 @@ has_retriable_error(const Json::Value& root)
|
|
316
317
|
}
|
317
318
|
const Json::Value& jmsg = cur["msg"];
|
318
319
|
const Json::Value& jcode = cur["code"];
|
320
|
+
unsigned code = 0;
|
319
321
|
if (jcode.isNumeric()) {
|
320
|
-
|
322
|
+
code = jcode.asUInt();
|
321
323
|
if (code == 4050 || code == 4070) {
|
324
|
+
lcb_log(LOGARGS(this, TRACE), LOGFMT "Will retry request. code: %d", LOGID(this), code);
|
322
325
|
return true;
|
323
326
|
}
|
324
327
|
}
|
@@ -326,6 +329,7 @@ has_retriable_error(const Json::Value& root)
|
|
326
329
|
const char *jmstr = jmsg.asCString();
|
327
330
|
for (const char **curs = wtf_magic_strings; *curs; curs++) {
|
328
331
|
if (!strstr(jmstr, *curs)) {
|
332
|
+
lcb_log(LOGARGS(this, TRACE), LOGFMT "Will retry request. code: %d, msg: %s", LOGID(this), code, jmstr);
|
329
333
|
return true;
|
330
334
|
}
|
331
335
|
}
|
@@ -458,8 +462,6 @@ chunk_callback(lcb_t instance, int ign, const lcb_RESPBASE *rb)
|
|
458
462
|
req->parser->feed(static_cast<const char*>(rh->body), rh->nbody);
|
459
463
|
}
|
460
464
|
|
461
|
-
#define QUERY_PATH "/query/service"
|
462
|
-
|
463
465
|
void
|
464
466
|
N1QLREQ::fail_prepared(const lcb_RESPN1QL *orig, lcb_error_t err)
|
465
467
|
{
|
@@ -523,9 +525,8 @@ N1QLREQ::issue_htreq(const std::string& body)
|
|
523
525
|
htcmd.content_type = "application/json";
|
524
526
|
htcmd.method = LCB_HTTP_METHOD_POST;
|
525
527
|
|
526
|
-
if (is_cbas
|
527
|
-
htcmd.type =
|
528
|
-
htcmd.host = cbashost.c_str();
|
528
|
+
if (is_cbas) {
|
529
|
+
htcmd.type = LCB_HTTP_TYPE_CBAS;
|
529
530
|
} else {
|
530
531
|
htcmd.type = LCB_HTTP_TYPE_N1QL;
|
531
532
|
}
|
@@ -607,7 +608,7 @@ lcb_N1QLREQ::lcb_N1QLREQ(lcb_t obj,
|
|
607
608
|
parser(new lcb::jsparse::Parser(lcb::jsparse::Parser::MODE_N1QL, this)),
|
608
609
|
cookie(user_cookie), callback(cmd->callback), instance(obj),
|
609
610
|
lasterr(LCB_SUCCESS), flags(cmd->cmdflags), timeout(0),
|
610
|
-
nrows(0), prepare_req(NULL), was_retried(false)
|
611
|
+
nrows(0), prepare_req(NULL), was_retried(false), is_cbas(false)
|
611
612
|
{
|
612
613
|
if (cmd->handle) {
|
613
614
|
*cmd->handle = this;
|
@@ -621,19 +622,11 @@ lcb_N1QLREQ::lcb_N1QLREQ(lcb_t obj,
|
|
621
622
|
}
|
622
623
|
|
623
624
|
if (flags & LCB_CMDN1QL_F_CBASQUERY) {
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
if (cbashost.empty()) {
|
630
|
-
lasterr = LCB_EINVAL;
|
631
|
-
return;
|
632
|
-
}
|
633
|
-
if (flags & LCB_CMDN1QL_F_PREPCACHE) {
|
634
|
-
lasterr = LCB_OPTIONS_CONFLICT;
|
635
|
-
return;
|
636
|
-
}
|
625
|
+
is_cbas = true;
|
626
|
+
}
|
627
|
+
if (is_cbas && (flags & LCB_CMDN1QL_F_PREPCACHE)) {
|
628
|
+
lasterr = LCB_OPTIONS_CONFLICT;
|
629
|
+
return;
|
637
630
|
}
|
638
631
|
|
639
632
|
const Json::Value& j_statement = json_const()["statement"];
|
@@ -1,3 +1,20 @@
|
|
1
|
+
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2015-2017 Couchbase, Inc.
|
4
|
+
*
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
*
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
*
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
* See the License for the specific language governing permissions and
|
15
|
+
* limitations under the License.
|
16
|
+
*/
|
17
|
+
|
1
18
|
#include <libcouchbase/couchbase.h>
|
2
19
|
#include <libcouchbase/n1ql.h>
|
3
20
|
#include <libcouchbase/vbucket.h>
|
@@ -84,6 +101,34 @@ lcb_n1p_posparam(lcb_N1QLPARAMS *params, const char *value, size_t nvalue)
|
|
84
101
|
return LCB_SUCCESS;
|
85
102
|
}
|
86
103
|
|
104
|
+
lcb_error_t
|
105
|
+
lcb_n1p_readonly(lcb_N1QLPARAMS *params, int readonly)
|
106
|
+
{
|
107
|
+
params->root["readonly"] = readonly ? true : false;
|
108
|
+
return LCB_SUCCESS;
|
109
|
+
}
|
110
|
+
|
111
|
+
lcb_error_t
|
112
|
+
lcb_n1p_scancap(lcb_N1QLPARAMS *params, int scancap)
|
113
|
+
{
|
114
|
+
params->root["scan_cap"] = Json::valueToString(scancap);
|
115
|
+
return LCB_SUCCESS;
|
116
|
+
}
|
117
|
+
|
118
|
+
lcb_error_t
|
119
|
+
lcb_n1p_pipelinecap(lcb_N1QLPARAMS *params, int pipelinecap)
|
120
|
+
{
|
121
|
+
params->root["pipeline_cap"] = Json::valueToString(pipelinecap);
|
122
|
+
return LCB_SUCCESS;
|
123
|
+
}
|
124
|
+
|
125
|
+
lcb_error_t
|
126
|
+
lcb_n1p_pipelinebatch(lcb_N1QLPARAMS *params, int pipelinebatch)
|
127
|
+
{
|
128
|
+
params->root["pipeline_batch"] = Json::valueToString(pipelinebatch);
|
129
|
+
return LCB_SUCCESS;
|
130
|
+
}
|
131
|
+
|
87
132
|
static void
|
88
133
|
encode_mutation_token(Json::Value& sparse, const lcb_MUTATION_TOKEN *sv)
|
89
134
|
{
|
@@ -92,7 +137,7 @@ encode_mutation_token(Json::Value& sparse, const lcb_MUTATION_TOKEN *sv)
|
|
92
137
|
Json::Value& cur_sv = sparse[buf];
|
93
138
|
|
94
139
|
cur_sv[0] = static_cast<Json::UInt64>(sv->seqno_);
|
95
|
-
sprintf(buf, "%llu", sv->uuid_);
|
140
|
+
sprintf(buf, "%llu", (unsigned long long)sv->uuid_);
|
96
141
|
cur_sv[1] = buf;
|
97
142
|
}
|
98
143
|
|
@@ -103,12 +103,12 @@ lcb_vbguess_remap(lcb_t instance, int vbid, int bad)
|
|
103
103
|
|
104
104
|
} else {
|
105
105
|
lcb_GUESSVB *guesses = instance->vbguess;
|
106
|
-
lcb_GUESSVB *guess = guesses + vbid;
|
107
|
-
int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 1);
|
108
106
|
if (!guesses) {
|
109
|
-
guesses = instance->vbguess =
|
110
|
-
LCBT_VBCONFIG(instance)->nvb, sizeof
|
107
|
+
guesses = instance->vbguess =
|
108
|
+
reinterpret_cast< lcb_GUESSVB * >(calloc(LCBT_VBCONFIG(instance)->nvb, sizeof(lcb_GUESSVB)));
|
111
109
|
}
|
110
|
+
lcb_GUESSVB *guess = guesses + vbid;
|
111
|
+
int newix = lcbvb_nmv_remap_ex(LCBT_VBCONFIG(instance), vbid, bad, 1);
|
112
112
|
if (newix > -1 && newix != bad) {
|
113
113
|
guess->newix = newix;
|
114
114
|
guess->oldix = bad;
|
@@ -113,6 +113,10 @@ SeqnoDurset::poll_impl()
|
|
113
113
|
ent.callback = seqno_callback;
|
114
114
|
|
115
115
|
size_t nservers = ent.prepare(servers);
|
116
|
+
if (nservers == 0) {
|
117
|
+
ret_err = LCB_DURABILITY_ETOOMANY;
|
118
|
+
continue;
|
119
|
+
}
|
116
120
|
for (size_t jj = 0; jj < nservers; jj++) {
|
117
121
|
lcb_error_t err;
|
118
122
|
cmd.server_index = servers[jj];
|
@@ -284,6 +284,9 @@ lcb_error_t
|
|
284
284
|
lcb_durability_validate(lcb_t instance,
|
285
285
|
lcb_U16 *persist_to, lcb_U16 *replicate_to, int options)
|
286
286
|
{
|
287
|
+
if (!LCBT_VBCONFIG(instance)) {
|
288
|
+
return LCB_CLIENT_ENOCONF;
|
289
|
+
}
|
287
290
|
int replica_max = std::min(
|
288
291
|
LCBT_NREPLICAS(instance),
|
289
292
|
LCBT_NDATASERVERS(instance)-1);
|
@@ -0,0 +1,315 @@
|
|
1
|
+
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2017 Couchbase, Inc.
|
4
|
+
*
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
*
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
*
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
* See the License for the specific language governing permissions and
|
15
|
+
* limitations under the License.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#include "internal.h"
|
19
|
+
#include <libcouchbase/n1ql.h>
|
20
|
+
#include "http/http.h"
|
21
|
+
#include "auth-priv.h"
|
22
|
+
|
23
|
+
static void refcnt_dtor_ping(mc_PACKET *);
|
24
|
+
static void handle_ping(mc_PIPELINE *, mc_PACKET *, lcb_error_t, const void *);
|
25
|
+
|
26
|
+
static mc_REQDATAPROCS ping_procs = {
|
27
|
+
handle_ping,
|
28
|
+
refcnt_dtor_ping
|
29
|
+
};
|
30
|
+
|
31
|
+
struct PingCookie : mc_REQDATAEX {
|
32
|
+
int remaining;
|
33
|
+
int options;
|
34
|
+
std::list<lcb_PINGSVC> responses;
|
35
|
+
|
36
|
+
PingCookie(const void *cookie_, int _options)
|
37
|
+
: mc_REQDATAEX(cookie_, ping_procs, gethrtime()),
|
38
|
+
remaining(0), options(_options) {
|
39
|
+
}
|
40
|
+
|
41
|
+
~PingCookie() {
|
42
|
+
for (std::list<lcb_PINGSVC>::iterator it = responses.begin(); it != responses.end(); it++) {
|
43
|
+
if (it->server) {
|
44
|
+
free(it->server);
|
45
|
+
it->server = NULL;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
bool needMetrics() {
|
51
|
+
return (options & LCB_PINGOPT_F_NOMETRICS) == 0;
|
52
|
+
}
|
53
|
+
|
54
|
+
bool needJSON() {
|
55
|
+
return options & LCB_PINGOPT_F_JSON;
|
56
|
+
}
|
57
|
+
|
58
|
+
bool needDetails() {
|
59
|
+
return options & LCB_PINGOPT_F_JSONDETAILS;
|
60
|
+
}
|
61
|
+
|
62
|
+
bool needPretty() {
|
63
|
+
return options & LCB_PINGOPT_F_JSONPRETTY;
|
64
|
+
}
|
65
|
+
};
|
66
|
+
|
67
|
+
static void
|
68
|
+
refcnt_dtor_ping(mc_PACKET *pkt)
|
69
|
+
{
|
70
|
+
PingCookie *ck = static_cast<PingCookie *>(pkt->u_rdata.exdata);
|
71
|
+
if (!--ck->remaining) {
|
72
|
+
delete ck;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
static std::string latency_to_string(lcb_U64 latency)
|
77
|
+
{
|
78
|
+
std::stringstream ss;
|
79
|
+
ss.precision(3);
|
80
|
+
ss.setf(std::ios::fixed);
|
81
|
+
if (latency < 1000) {
|
82
|
+
ss << latency << "ns";
|
83
|
+
} else if (latency < LCB_US2NS(1000)) {
|
84
|
+
ss << (latency / (double)LCB_US2NS(1)) << "us";
|
85
|
+
} else if (latency < LCB_MS2NS(1000)) {
|
86
|
+
ss << (latency / (double)LCB_MS2NS(1)) << "ms";
|
87
|
+
} else {
|
88
|
+
ss << (latency / (double)LCB_S2NS(1)) << "s";
|
89
|
+
}
|
90
|
+
return ss.str();
|
91
|
+
}
|
92
|
+
|
93
|
+
static const char* svc_to_string(lcb_PINGSVCTYPE type)
|
94
|
+
{
|
95
|
+
switch (type) {
|
96
|
+
case LCB_PINGSVC_KV:
|
97
|
+
return "kv";
|
98
|
+
case LCB_PINGSVC_VIEWS:
|
99
|
+
return "views";
|
100
|
+
case LCB_PINGSVC_N1QL:
|
101
|
+
return "n1ql";
|
102
|
+
case LCB_PINGSVC_FTS:
|
103
|
+
return "fts";
|
104
|
+
default:
|
105
|
+
return "unknown";
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
static void
|
110
|
+
build_ping_json(lcb_RESPPING &ping, Json::Value &root, bool details)
|
111
|
+
{
|
112
|
+
Json::Value services;
|
113
|
+
for (size_t ii = 0; ii < ping.nservices; ii++) {
|
114
|
+
lcb_PINGSVC &svc = ping.services[ii];
|
115
|
+
Json::Value service;
|
116
|
+
service["server"] = svc.server;
|
117
|
+
service["latency"] = latency_to_string(svc.latency);
|
118
|
+
service["status"] = svc.status;
|
119
|
+
if (details) {
|
120
|
+
service["details"] = lcb_strerror(NULL, svc.status);
|
121
|
+
}
|
122
|
+
services[svc_to_string(svc.type)].append(service);
|
123
|
+
}
|
124
|
+
root["services"] = services;
|
125
|
+
}
|
126
|
+
|
127
|
+
static void
|
128
|
+
invoke_ping_callback(lcb_t instance, PingCookie *ck)
|
129
|
+
{
|
130
|
+
lcb_RESPPING ping;
|
131
|
+
std::string json;
|
132
|
+
size_t idx = 0;
|
133
|
+
memset(&ping, 0, sizeof(ping));
|
134
|
+
if (ck->needMetrics()) {
|
135
|
+
ping.nservices = ck->responses.size();
|
136
|
+
ping.services = new lcb_PINGSVC[ping.nservices];
|
137
|
+
for(std::list<lcb_PINGSVC>::const_iterator it = ck->responses.begin(); it != ck->responses.end(); ++it){
|
138
|
+
ping.services[idx++] = *it;
|
139
|
+
}
|
140
|
+
if (ck->needJSON()) {
|
141
|
+
Json::Value root;
|
142
|
+
build_ping_json(ping, root, ck->needDetails());
|
143
|
+
Json::Writer *w;
|
144
|
+
if (ck->needPretty()) {
|
145
|
+
w = new Json::StyledWriter();
|
146
|
+
} else {
|
147
|
+
w = new Json::FastWriter();
|
148
|
+
}
|
149
|
+
json = w->write(root);
|
150
|
+
delete w;
|
151
|
+
ping.njson = json.size();
|
152
|
+
ping.json = json.c_str();
|
153
|
+
}
|
154
|
+
}
|
155
|
+
lcb_RESPCALLBACK callback;
|
156
|
+
callback = lcb_find_callback(instance, LCB_CALLBACK_PING);
|
157
|
+
ping.cookie = const_cast<void*>(ck->cookie);
|
158
|
+
callback(instance, LCB_CALLBACK_PING, (lcb_RESPBASE *)&ping);
|
159
|
+
if (ping.services != NULL) {
|
160
|
+
delete []ping.services;
|
161
|
+
}
|
162
|
+
delete ck;
|
163
|
+
}
|
164
|
+
|
165
|
+
static void
|
166
|
+
handle_ping(mc_PIPELINE *pipeline, mc_PACKET *req, lcb_error_t err, const void *)
|
167
|
+
{
|
168
|
+
lcb::Server *server = static_cast<lcb::Server*>(pipeline);
|
169
|
+
PingCookie *ck = (PingCookie *)req->u_rdata.exdata;
|
170
|
+
|
171
|
+
if (ck->needMetrics()) {
|
172
|
+
std::string hh(server->get_host().host);
|
173
|
+
hh.append(":");
|
174
|
+
hh.append(server->get_host().port);
|
175
|
+
|
176
|
+
lcb_PINGSVC svc;
|
177
|
+
svc.type = LCB_PINGSVC_KV;
|
178
|
+
svc.server = strdup(hh.c_str());
|
179
|
+
svc.latency = gethrtime() - MCREQ_PKT_RDATA(req)->start;
|
180
|
+
svc.status = err;
|
181
|
+
ck->responses.push_back(svc);
|
182
|
+
}
|
183
|
+
|
184
|
+
if (--ck->remaining) {
|
185
|
+
return;
|
186
|
+
}
|
187
|
+
invoke_ping_callback(server->get_instance(), ck);
|
188
|
+
}
|
189
|
+
|
190
|
+
static void handle_http(lcb_t instance, lcb_PINGSVCTYPE type, const lcb_RESPHTTP *resp)
|
191
|
+
{
|
192
|
+
if ((resp->rflags & LCB_RESP_F_FINAL) == 0) {
|
193
|
+
return;
|
194
|
+
}
|
195
|
+
PingCookie *ck = (PingCookie *)resp->cookie;
|
196
|
+
lcb::http::Request *htreq = reinterpret_cast<lcb::http::Request*>(resp->_htreq);
|
197
|
+
|
198
|
+
if (ck->needMetrics()) {
|
199
|
+
lcb_PINGSVC svc;
|
200
|
+
svc.type = type;
|
201
|
+
svc.server = strdup((htreq->host + ":" + htreq->port).c_str());
|
202
|
+
svc.latency = gethrtime() - htreq->start;
|
203
|
+
svc.status = resp->rc;
|
204
|
+
ck->responses.push_back(svc);
|
205
|
+
}
|
206
|
+
if (--ck->remaining) {
|
207
|
+
return;
|
208
|
+
}
|
209
|
+
invoke_ping_callback(instance, ck);
|
210
|
+
}
|
211
|
+
|
212
|
+
static void handle_n1ql(lcb_t instance, int, const lcb_RESPBASE *resp)
|
213
|
+
{
|
214
|
+
handle_http(instance, LCB_PINGSVC_N1QL, (const lcb_RESPHTTP *)resp);
|
215
|
+
}
|
216
|
+
|
217
|
+
static void handle_views(lcb_t instance, int, const lcb_RESPBASE *resp)
|
218
|
+
{
|
219
|
+
handle_http(instance, LCB_PINGSVC_VIEWS, (const lcb_RESPHTTP *)resp);
|
220
|
+
}
|
221
|
+
|
222
|
+
static void handle_fts(lcb_t instance, int, const lcb_RESPBASE *resp)
|
223
|
+
{
|
224
|
+
handle_http(instance, LCB_PINGSVC_FTS, (const lcb_RESPHTTP *)resp);
|
225
|
+
}
|
226
|
+
|
227
|
+
LIBCOUCHBASE_API
|
228
|
+
lcb_error_t
|
229
|
+
lcb_ping3(lcb_t instance, const void *cookie, const lcb_CMDPING *cmd)
|
230
|
+
{
|
231
|
+
mc_CMDQUEUE *cq = &instance->cmdq;
|
232
|
+
unsigned ii;
|
233
|
+
|
234
|
+
if (!cq->config) {
|
235
|
+
return LCB_CLIENT_ETMPFAIL;
|
236
|
+
}
|
237
|
+
|
238
|
+
PingCookie *ckwrap = new PingCookie(cookie, cmd->options);
|
239
|
+
|
240
|
+
if (cmd->services & LCB_PINGSVC_F_KV) {
|
241
|
+
for (ii = 0; ii < cq->npipelines; ii++) {
|
242
|
+
mc_PIPELINE *pl = cq->pipelines[ii];
|
243
|
+
mc_PACKET *pkt = mcreq_allocate_packet(pl);
|
244
|
+
protocol_binary_request_header hdr;
|
245
|
+
memset(&hdr, 0, sizeof(hdr));
|
246
|
+
|
247
|
+
if (!pkt) {
|
248
|
+
return LCB_CLIENT_ENOMEM;
|
249
|
+
}
|
250
|
+
|
251
|
+
pkt->u_rdata.exdata = ckwrap;
|
252
|
+
pkt->flags |= MCREQ_F_REQEXT;
|
253
|
+
|
254
|
+
hdr.request.magic = PROTOCOL_BINARY_REQ;
|
255
|
+
hdr.request.opaque = pkt->opaque;
|
256
|
+
hdr.request.opcode = PROTOCOL_BINARY_CMD_NOOP;
|
257
|
+
|
258
|
+
mcreq_reserve_header(pl, pkt, MCREQ_PKT_BASESIZE);
|
259
|
+
memcpy(SPAN_BUFFER(&pkt->kh_span), hdr.bytes, sizeof(hdr.bytes));
|
260
|
+
mcreq_sched_add(pl, pkt);
|
261
|
+
ckwrap->remaining++;
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
lcbvb_CONFIG *cfg = LCBT_VBCONFIG(instance);
|
266
|
+
const lcbvb_SVCMODE mode = LCBT_SETTING(instance, sslopts) ?
|
267
|
+
LCBVB_SVCMODE_SSL : LCBVB_SVCMODE_PLAIN;
|
268
|
+
for (int idx = 0; idx < (int)LCBVB_NSERVERS(cfg); idx++) {
|
269
|
+
#define PING_HTTP(SVC, QUERY, TMO, CB) \
|
270
|
+
do { \
|
271
|
+
lcb_error_t rc; \
|
272
|
+
struct lcb_http_request_st *htreq; \
|
273
|
+
lcb_CMDHTTP htcmd = {0}; \
|
274
|
+
htcmd.host = lcbvb_get_resturl(cfg, idx, SVC, mode); \
|
275
|
+
if (htcmd.host == NULL) { \
|
276
|
+
continue; \
|
277
|
+
} \
|
278
|
+
htcmd.body = QUERY; \
|
279
|
+
htcmd.nbody = strlen(htcmd.body); \
|
280
|
+
htcmd.content_type = "application/json"; \
|
281
|
+
htcmd.method = LCB_HTTP_METHOD_POST; \
|
282
|
+
htcmd.type = LCB_HTTP_TYPE_RAW; \
|
283
|
+
htcmd.reqhandle = &htreq; \
|
284
|
+
const lcb::Authenticator& auth = *instance->settings->auth; \
|
285
|
+
htcmd.username = auth.username_for(LCBT_SETTING(instance, bucket)).c_str(); \
|
286
|
+
htcmd.password = auth.password_for(LCBT_SETTING(instance, bucket)).c_str(); \
|
287
|
+
htcmd.cmdflags = LCB_CMDHTTP_F_CASTMO; \
|
288
|
+
htcmd.cas = LCBT_SETTING(instance, TMO); \
|
289
|
+
rc = lcb_http3(instance, ckwrap, &htcmd); \
|
290
|
+
if (rc == LCB_SUCCESS) { \
|
291
|
+
htreq->set_callback(CB); \
|
292
|
+
} \
|
293
|
+
ckwrap->remaining++; \
|
294
|
+
} while (0);
|
295
|
+
|
296
|
+
if (cmd->services & LCB_PINGSVC_F_N1QL) {
|
297
|
+
PING_HTTP(LCBVB_SVCTYPE_N1QL, "{\"statement\":\"select 1\"}",
|
298
|
+
n1ql_timeout, handle_n1ql);
|
299
|
+
}
|
300
|
+
if (cmd->services & LCB_PINGSVC_F_VIEWS) {
|
301
|
+
PING_HTTP(LCBVB_SVCTYPE_VIEWS, "", views_timeout, handle_views);
|
302
|
+
}
|
303
|
+
if (cmd->services & LCB_PINGSVC_F_FTS) {
|
304
|
+
PING_HTTP(LCBVB_SVCTYPE_FTS, "", n1ql_timeout, handle_fts);
|
305
|
+
}
|
306
|
+
#undef PING_HTTP
|
307
|
+
}
|
308
|
+
|
309
|
+
if (ckwrap->remaining == 0) {
|
310
|
+
delete ckwrap;
|
311
|
+
return LCB_NO_MATCHING_SERVER;
|
312
|
+
}
|
313
|
+
MAYBE_SCHEDLEAVE(instance);
|
314
|
+
return LCB_SUCCESS;
|
315
|
+
}
|