libcouchbase 1.0.4 → 1.1.0

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