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.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +11 -8
  3. data/ext/libcouchbase/CMakeLists.txt +1 -1
  4. data/ext/libcouchbase/README.markdown +38 -6
  5. data/ext/libcouchbase/RELEASE_NOTES.markdown +151 -0
  6. data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -2
  7. data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
  8. data/ext/libcouchbase/cmake/source_files.cmake +1 -0
  9. data/ext/libcouchbase/contrib/cJSON/cJSON.c +686 -288
  10. data/ext/libcouchbase/contrib/cJSON/cJSON.h +0 -0
  11. data/ext/libcouchbase/contrib/cbsasl/src/hash.c +17 -17
  12. data/ext/libcouchbase/contrib/cliopts/cliopts.c +76 -0
  13. data/ext/libcouchbase/contrib/cliopts/cliopts.h +66 -15
  14. data/ext/libcouchbase/contrib/genhash/genhash.c +1 -2
  15. data/ext/libcouchbase/contrib/lcb-jsoncpp/lcb-jsoncpp.cpp +4 -3
  16. data/ext/libcouchbase/example/instancepool/main.cc +12 -2
  17. data/ext/libcouchbase/example/libeventdirect/main.c +99 -25
  18. data/ext/libcouchbase/example/minimal/minimal.c +7 -5
  19. data/ext/libcouchbase/example/observe/durability.c +102 -0
  20. data/ext/libcouchbase/example/observe/observe.c +19 -6
  21. data/ext/libcouchbase/example/subdoc/subdoc-xattrs.c +1 -2
  22. data/ext/libcouchbase/include/libcouchbase/cntl-private.h +6 -8
  23. data/ext/libcouchbase/include/libcouchbase/cntl.h +84 -64
  24. data/ext/libcouchbase/include/libcouchbase/couchbase.h +295 -78
  25. data/ext/libcouchbase/include/libcouchbase/deprecated.h +2 -2
  26. data/ext/libcouchbase/include/libcouchbase/error.h +1 -1
  27. data/ext/libcouchbase/include/libcouchbase/iops.h +9 -9
  28. data/ext/libcouchbase/include/libcouchbase/ixmgmt.h +2 -2
  29. data/ext/libcouchbase/include/libcouchbase/n1ql.h +69 -7
  30. data/ext/libcouchbase/include/libcouchbase/vbucket.h +17 -0
  31. data/ext/libcouchbase/include/libcouchbase/views.h +3 -3
  32. data/ext/libcouchbase/include/memcached/protocol_binary.h +62 -1
  33. data/ext/libcouchbase/packaging/deb/control +1 -1
  34. data/ext/libcouchbase/packaging/rpm/libcouchbase.spec.in +37 -36
  35. data/ext/libcouchbase/src/bootstrap.cc +22 -8
  36. data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +1 -1
  37. data/ext/libcouchbase/src/bucketconfig/bc_http.cc +0 -1
  38. data/ext/libcouchbase/src/bucketconfig/confmon.cc +13 -8
  39. data/ext/libcouchbase/src/callbacks.c +2 -0
  40. data/ext/libcouchbase/src/cntl.cc +28 -17
  41. data/ext/libcouchbase/src/dns-srv.cc +1 -2
  42. data/ext/libcouchbase/src/dump.cc +4 -0
  43. data/ext/libcouchbase/src/errmap.h +89 -16
  44. data/ext/libcouchbase/src/handler.cc +28 -11
  45. data/ext/libcouchbase/src/http/http-priv.h +4 -1
  46. data/ext/libcouchbase/src/http/http.cc +3 -0
  47. data/ext/libcouchbase/src/instance.cc +1 -1
  48. data/ext/libcouchbase/src/internal.h +1 -0
  49. data/ext/libcouchbase/src/lcbio/connect.cc +2 -3
  50. data/ext/libcouchbase/src/lcbio/manager.cc +2 -2
  51. data/ext/libcouchbase/src/lcbio/ssl.h +10 -0
  52. data/ext/libcouchbase/src/mc/mcreq.c +8 -0
  53. data/ext/libcouchbase/src/mcserver/mcserver.cc +14 -1
  54. data/ext/libcouchbase/src/n1ql/ixmgmt.cc +0 -3
  55. data/ext/libcouchbase/src/n1ql/n1ql.cc +22 -29
  56. data/ext/libcouchbase/src/n1ql/params.cc +46 -1
  57. data/ext/libcouchbase/src/newconfig.cc +4 -4
  58. data/ext/libcouchbase/src/operations/durability-seqno.cc +4 -0
  59. data/ext/libcouchbase/src/operations/durability.cc +3 -0
  60. data/ext/libcouchbase/src/operations/ping.cc +315 -0
  61. data/ext/libcouchbase/src/operations/stats.cc +10 -0
  62. data/ext/libcouchbase/src/operations/subdoc.cc +13 -1
  63. data/ext/libcouchbase/src/retrychk.cc +1 -0
  64. data/ext/libcouchbase/src/settings.c +2 -0
  65. data/ext/libcouchbase/src/settings.h +13 -7
  66. data/ext/libcouchbase/src/ssl/ssl_c.c +28 -2
  67. data/ext/libcouchbase/src/ssl/ssl_common.c +3 -0
  68. data/ext/libcouchbase/src/ssl/ssl_e.c +15 -1
  69. data/ext/libcouchbase/src/ssl/ssl_iot_common.h +3 -1
  70. data/ext/libcouchbase/src/timings.c +0 -1
  71. data/ext/libcouchbase/src/vbucket/vbucket.c +49 -1
  72. data/ext/libcouchbase/tests/iotests/mock-environment.cc +58 -40
  73. data/ext/libcouchbase/tests/iotests/mock-environment.h +23 -4
  74. data/ext/libcouchbase/tests/iotests/mock-unit-test.h +8 -8
  75. data/ext/libcouchbase/tests/iotests/t_behavior.cc +5 -5
  76. data/ext/libcouchbase/tests/iotests/t_durability.cc +50 -0
  77. data/ext/libcouchbase/tests/iotests/t_eerrs.cc +4 -2
  78. data/ext/libcouchbase/tests/iotests/t_errmap.cc +6 -3
  79. data/ext/libcouchbase/tests/iotests/t_lock.cc +5 -6
  80. data/ext/libcouchbase/tests/iotests/t_misc.cc +44 -0
  81. data/ext/libcouchbase/tests/iotests/t_serverops.cc +1 -0
  82. data/ext/libcouchbase/tests/iotests/t_subdoc.cc +28 -0
  83. data/ext/libcouchbase/tests/iotests/t_views.cc +22 -10
  84. data/ext/libcouchbase/tools/CMakeLists.txt +21 -1
  85. data/ext/libcouchbase/tools/cbc-handlers.h +23 -3
  86. data/ext/libcouchbase/tools/cbc-n1qlback.cc +1 -1
  87. data/ext/libcouchbase/tools/cbc-pillowfight.cc +126 -26
  88. data/ext/libcouchbase/tools/cbc-proxy.cc +403 -0
  89. data/ext/libcouchbase/tools/cbc-subdoc.cc +826 -0
  90. data/ext/libcouchbase/tools/cbc.cc +149 -37
  91. data/ext/libcouchbase/tools/common/options.h +5 -2
  92. data/ext/libcouchbase/tools/linenoise/linenoise.c +15 -15
  93. data/lib/libcouchbase.rb +4 -0
  94. data/lib/libcouchbase/bucket.rb +51 -0
  95. data/lib/libcouchbase/connection.rb +100 -13
  96. data/lib/libcouchbase/ext/libcouchbase.rb +40 -0
  97. data/lib/libcouchbase/ext/libcouchbase/cmdsubdoc.rb +13 -1
  98. data/lib/libcouchbase/ext/libcouchbase/enums.rb +2 -1
  99. data/lib/libcouchbase/ext/libcouchbase/sdspec.rb +5 -0
  100. data/lib/libcouchbase/subdoc_request.rb +129 -0
  101. data/lib/libcouchbase/version.rb +1 -1
  102. data/spec/bucket_spec.rb +15 -1
  103. data/spec/connection_spec.rb +1 -1
  104. data/spec/subdoc_spec.rb +192 -0
  105. metadata +13 -4
  106. data/ext/libcouchbase/.travis.yml +0 -19
@@ -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(), host.isSSL(), rc);
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 handler, void *arg)
438
- : user_handler(handler), user_arg(arg), sock(NULL), syserr(0),
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, *next;
293
- LCB_LIST_SAFE_FOR(cur, next, (lcb_list_t *)&parent->requests) {
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
- static bool
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
- unsigned code = jcode.asUInt();
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 = LCB_HTTP_TYPE_RAW;
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
- if (!cmd->host) {
625
- lasterr = LCB_EINVAL;
626
- return;
627
- }
628
- cbashost.assign(cmd->host);
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 = reinterpret_cast<lcb_GUESSVB*>(calloc(
110
- LCBT_VBCONFIG(instance)->nvb, sizeof *guesses));
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
+ }