libcouchbase 1.2.8 → 1.3.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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -4
  3. data/README.md +16 -8
  4. data/ext/libcouchbase/CMakeLists.txt +34 -32
  5. data/ext/libcouchbase/RELEASE_NOTES.markdown +277 -6
  6. data/ext/libcouchbase/cmake/Modules/ConfigureDtrace.cmake +14 -0
  7. data/ext/libcouchbase/cmake/Modules/FindCouchbaseLibevent.cmake +2 -0
  8. data/ext/libcouchbase/cmake/Modules/FindCouchbaseLibuv.cmake +2 -1
  9. data/ext/libcouchbase/cmake/Modules/GenerateConfigDotH.cmake +2 -0
  10. data/ext/libcouchbase/cmake/Modules/GetLibcouchbaseFlags.cmake +8 -1
  11. data/ext/libcouchbase/cmake/Modules/GetVersionInfo.cmake +3 -3
  12. data/ext/libcouchbase/cmake/config-cmake.h.in +14 -0
  13. data/ext/libcouchbase/cmake/configure +8 -26
  14. data/ext/libcouchbase/cmake/defs.mk.in +2 -2
  15. data/ext/libcouchbase/cmake/libcouchbase.stp.in +829 -0
  16. data/ext/libcouchbase/cmake/source_files.cmake +11 -2
  17. data/ext/libcouchbase/contrib/cbsasl/CMakeLists.txt +18 -2
  18. data/ext/libcouchbase/contrib/cbsasl/include/cbsasl/cbsasl.h +44 -2
  19. data/ext/libcouchbase/contrib/cbsasl/src/client.c +285 -73
  20. data/ext/libcouchbase/contrib/cbsasl/src/common.c +4 -0
  21. data/ext/libcouchbase/contrib/cbsasl/src/scram-sha/scram_utils.c +500 -0
  22. data/ext/libcouchbase/contrib/cbsasl/src/scram-sha/scram_utils.h +99 -0
  23. data/ext/libcouchbase/contrib/cliopts/CMakeLists.txt +1 -1
  24. data/ext/libcouchbase/contrib/cliopts/cliopts.h +14 -1
  25. data/ext/libcouchbase/contrib/snappy/CMakeLists.txt +2 -3
  26. data/ext/libcouchbase/contrib/snappy/snappy-sinksource.cc +4 -0
  27. data/ext/libcouchbase/contrib/snappy/snappy-stubs-public.h +7 -5
  28. data/ext/libcouchbase/contrib/snappy/snappy.cc +7 -2
  29. data/ext/libcouchbase/example/crypto/.gitignore +2 -0
  30. data/ext/libcouchbase/example/crypto/Makefile +13 -0
  31. data/ext/libcouchbase/example/crypto/common_provider.c +24 -0
  32. data/ext/libcouchbase/example/crypto/common_provider.h +31 -0
  33. data/ext/libcouchbase/example/crypto/openssl_symmetric_decrypt.c +139 -0
  34. data/ext/libcouchbase/example/crypto/openssl_symmetric_encrypt.c +147 -0
  35. data/ext/libcouchbase/example/crypto/openssl_symmetric_provider.c +281 -0
  36. data/ext/libcouchbase/example/crypto/openssl_symmetric_provider.h +29 -0
  37. data/ext/libcouchbase/example/tracing/.gitignore +2 -0
  38. data/ext/libcouchbase/example/tracing/Makefile +8 -0
  39. data/ext/libcouchbase/example/tracing/cJSON.c +1 -0
  40. data/ext/libcouchbase/example/tracing/cJSON.h +1 -0
  41. data/ext/libcouchbase/example/tracing/tracing.c +439 -0
  42. data/ext/libcouchbase/example/tracing/views.c +444 -0
  43. data/ext/libcouchbase/include/libcouchbase/auth.h +56 -4
  44. data/ext/libcouchbase/include/libcouchbase/cbft.h +8 -0
  45. data/ext/libcouchbase/include/libcouchbase/cntl-private.h +55 -1
  46. data/ext/libcouchbase/include/libcouchbase/cntl.h +101 -1
  47. data/ext/libcouchbase/include/libcouchbase/configuration.h.in +6 -0
  48. data/ext/libcouchbase/include/libcouchbase/couchbase.h +109 -6
  49. data/ext/libcouchbase/include/libcouchbase/crypto.h +140 -0
  50. data/ext/libcouchbase/include/libcouchbase/error.h +38 -2
  51. data/ext/libcouchbase/include/libcouchbase/kvbuf.h +6 -1
  52. data/ext/libcouchbase/include/libcouchbase/metrics.h +79 -0
  53. data/ext/libcouchbase/include/libcouchbase/n1ql.h +9 -0
  54. data/ext/libcouchbase/include/libcouchbase/tracing.h +319 -0
  55. data/ext/libcouchbase/include/libcouchbase/vbucket.h +1 -1
  56. data/ext/libcouchbase/include/libcouchbase/views.h +8 -0
  57. data/ext/libcouchbase/include/memcached/protocol_binary.h +40 -10
  58. data/ext/libcouchbase/packaging/rpm/libcouchbase.spec.in +6 -14
  59. data/ext/libcouchbase/plugins/io/libuv/plugin-internal.h +3 -0
  60. data/ext/libcouchbase/plugins/io/libuv/plugin-libuv.c +1 -0
  61. data/ext/libcouchbase/plugins/io/select/plugin-select.c +4 -1
  62. data/ext/libcouchbase/src/auth-priv.h +36 -4
  63. data/ext/libcouchbase/src/auth.cc +66 -27
  64. data/ext/libcouchbase/src/bootstrap.cc +1 -1
  65. data/ext/libcouchbase/src/bucketconfig/bc_cccp.cc +12 -7
  66. data/ext/libcouchbase/src/bucketconfig/bc_http.cc +26 -17
  67. data/ext/libcouchbase/src/bucketconfig/bc_http.h +1 -1
  68. data/ext/libcouchbase/src/bucketconfig/clconfig.h +4 -2
  69. data/ext/libcouchbase/src/bucketconfig/confmon.cc +6 -3
  70. data/ext/libcouchbase/src/cbft.cc +48 -0
  71. data/ext/libcouchbase/src/cntl.cc +138 -2
  72. data/ext/libcouchbase/src/config_static.h +17 -0
  73. data/ext/libcouchbase/src/connspec.cc +54 -6
  74. data/ext/libcouchbase/src/connspec.h +9 -1
  75. data/ext/libcouchbase/src/crypto.cc +386 -0
  76. data/ext/libcouchbase/src/ctx-log-inl.h +23 -6
  77. data/ext/libcouchbase/src/dump.cc +4 -0
  78. data/ext/libcouchbase/src/getconfig.cc +1 -2
  79. data/ext/libcouchbase/src/handler.cc +65 -27
  80. data/ext/libcouchbase/src/hostlist.cc +35 -7
  81. data/ext/libcouchbase/src/hostlist.h +7 -0
  82. data/ext/libcouchbase/src/http/http-priv.h +2 -0
  83. data/ext/libcouchbase/src/http/http.cc +77 -37
  84. data/ext/libcouchbase/src/http/http_io.cc +19 -2
  85. data/ext/libcouchbase/src/instance.cc +90 -17
  86. data/ext/libcouchbase/src/internal.h +5 -0
  87. data/ext/libcouchbase/src/lcbio/connect.cc +39 -4
  88. data/ext/libcouchbase/src/lcbio/connect.h +27 -0
  89. data/ext/libcouchbase/src/lcbio/ctx.c +49 -23
  90. data/ext/libcouchbase/src/lcbio/ioutils.cc +30 -3
  91. data/ext/libcouchbase/src/lcbio/ioutils.h +2 -0
  92. data/ext/libcouchbase/src/lcbio/manager.cc +44 -8
  93. data/ext/libcouchbase/src/lcbio/manager.h +2 -0
  94. data/ext/libcouchbase/src/lcbio/rw-inl.h +1 -0
  95. data/ext/libcouchbase/src/lcbio/ssl.h +3 -5
  96. data/ext/libcouchbase/src/logging.c +1 -1
  97. data/ext/libcouchbase/src/logging.h +2 -0
  98. data/ext/libcouchbase/src/mc/compress.cc +164 -0
  99. data/ext/libcouchbase/src/mc/compress.h +7 -12
  100. data/ext/libcouchbase/src/mc/mcreq-flush-inl.h +5 -1
  101. data/ext/libcouchbase/src/mc/mcreq.c +11 -1
  102. data/ext/libcouchbase/src/mc/mcreq.h +35 -4
  103. data/ext/libcouchbase/src/mcserver/mcserver.cc +30 -7
  104. data/ext/libcouchbase/src/mcserver/mcserver.h +7 -0
  105. data/ext/libcouchbase/src/mcserver/negotiate.cc +103 -57
  106. data/ext/libcouchbase/src/mcserver/negotiate.h +2 -2
  107. data/ext/libcouchbase/src/mctx-helper.h +11 -0
  108. data/ext/libcouchbase/src/metrics.cc +132 -0
  109. data/ext/libcouchbase/src/n1ql/ixmgmt.cc +2 -1
  110. data/ext/libcouchbase/src/n1ql/n1ql.cc +66 -0
  111. data/ext/libcouchbase/src/newconfig.cc +9 -2
  112. data/ext/libcouchbase/src/operations/counter.cc +2 -1
  113. data/ext/libcouchbase/src/operations/durability-cas.cc +11 -0
  114. data/ext/libcouchbase/src/operations/durability-seqno.cc +3 -0
  115. data/ext/libcouchbase/src/operations/durability.cc +24 -2
  116. data/ext/libcouchbase/src/operations/durability_internal.h +19 -0
  117. data/ext/libcouchbase/src/operations/get.cc +4 -2
  118. data/ext/libcouchbase/src/operations/observe-seqno.cc +1 -0
  119. data/ext/libcouchbase/src/operations/observe.cc +113 -62
  120. data/ext/libcouchbase/src/operations/ping.cc +246 -67
  121. data/ext/libcouchbase/src/operations/remove.cc +2 -1
  122. data/ext/libcouchbase/src/operations/store.cc +17 -14
  123. data/ext/libcouchbase/src/operations/touch.cc +3 -0
  124. data/ext/libcouchbase/src/packetutils.h +68 -4
  125. data/ext/libcouchbase/src/probes.d +132 -161
  126. data/ext/libcouchbase/src/rdb/bigalloc.c +1 -1
  127. data/ext/libcouchbase/src/retryq.cc +6 -2
  128. data/ext/libcouchbase/src/rnd.cc +68 -0
  129. data/ext/libcouchbase/src/rnd.h +39 -0
  130. data/ext/libcouchbase/src/settings.c +27 -0
  131. data/ext/libcouchbase/src/settings.h +67 -3
  132. data/ext/libcouchbase/src/ssl/CMakeLists.txt +0 -12
  133. data/ext/libcouchbase/src/ssl/ssl_common.c +23 -4
  134. data/ext/libcouchbase/src/strcodecs/base64.c +141 -16
  135. data/ext/libcouchbase/src/strcodecs/strcodecs.h +16 -1
  136. data/ext/libcouchbase/src/trace.h +68 -61
  137. data/ext/libcouchbase/src/tracing/span.cc +289 -0
  138. data/ext/libcouchbase/src/tracing/threshold_logging_tracer.cc +171 -0
  139. data/ext/libcouchbase/src/tracing/tracer.cc +53 -0
  140. data/ext/libcouchbase/src/tracing/tracing-internal.h +213 -0
  141. data/ext/libcouchbase/src/utilities.c +5 -0
  142. data/ext/libcouchbase/src/vbucket/CMakeLists.txt +2 -2
  143. data/ext/libcouchbase/src/vbucket/vbucket.c +50 -18
  144. data/ext/libcouchbase/src/views/docreq.cc +26 -1
  145. data/ext/libcouchbase/src/views/docreq.h +17 -0
  146. data/ext/libcouchbase/src/views/viewreq.cc +64 -1
  147. data/ext/libcouchbase/src/views/viewreq.h +21 -0
  148. data/ext/libcouchbase/tests/CMakeLists.txt +6 -6
  149. data/ext/libcouchbase/tests/basic/t_base64.cc +34 -6
  150. data/ext/libcouchbase/tests/basic/t_connstr.cc +14 -0
  151. data/ext/libcouchbase/tests/basic/t_creds.cc +10 -10
  152. data/ext/libcouchbase/tests/basic/t_host.cc +22 -2
  153. data/ext/libcouchbase/tests/basic/t_scram.cc +514 -0
  154. data/ext/libcouchbase/tests/check-all.cc +6 -2
  155. data/ext/libcouchbase/tests/iotests/mock-environment.cc +64 -0
  156. data/ext/libcouchbase/tests/iotests/mock-environment.h +27 -1
  157. data/ext/libcouchbase/tests/iotests/t_confmon.cc +2 -2
  158. data/ext/libcouchbase/tests/iotests/t_forward.cc +8 -0
  159. data/ext/libcouchbase/tests/iotests/t_netfail.cc +124 -0
  160. data/ext/libcouchbase/tests/iotests/t_smoke.cc +1 -1
  161. data/ext/libcouchbase/tests/iotests/t_snappy.cc +316 -0
  162. data/ext/libcouchbase/tests/socktests/socktest.cc +2 -2
  163. data/ext/libcouchbase/tests/socktests/t_basic.cc +6 -6
  164. data/ext/libcouchbase/tests/socktests/t_manager.cc +1 -1
  165. data/ext/libcouchbase/tests/socktests/t_ssl.cc +1 -1
  166. data/ext/libcouchbase/tools/CMakeLists.txt +1 -1
  167. data/ext/libcouchbase/tools/cbc-handlers.h +17 -0
  168. data/ext/libcouchbase/tools/cbc-n1qlback.cc +7 -4
  169. data/ext/libcouchbase/tools/cbc-pillowfight.cc +408 -100
  170. data/ext/libcouchbase/tools/cbc-proxy.cc +134 -3
  171. data/ext/libcouchbase/tools/cbc-subdoc.cc +1 -2
  172. data/ext/libcouchbase/tools/cbc.cc +113 -8
  173. data/ext/libcouchbase/tools/common/histogram.cc +1 -0
  174. data/ext/libcouchbase/tools/common/options.cc +28 -1
  175. data/ext/libcouchbase/tools/common/options.h +5 -0
  176. data/ext/libcouchbase/tools/docgen/docgen.h +36 -10
  177. data/ext/libcouchbase/tools/docgen/loc.h +5 -4
  178. data/ext/libcouchbase/tools/docgen/seqgen.h +28 -0
  179. data/lib/libcouchbase/ext/libcouchbase/enums.rb +10 -0
  180. data/lib/libcouchbase/n1ql.rb +6 -1
  181. data/lib/libcouchbase/version.rb +1 -1
  182. data/spec/connection_spec.rb +6 -6
  183. metadata +38 -5
  184. data/ext/libcouchbase/cmake/Modules/FindCouchbaseSnappy.cmake +0 -11
  185. data/ext/libcouchbase/src/mc/compress.c +0 -90
  186. data/ext/libcouchbase/tools/common/my_inttypes.h +0 -22
@@ -206,7 +206,7 @@ Loop::initSockCommon(ESocket *sock)
206
206
  void
207
207
  Loop::connectPooled(ESocket *sock, lcb_host_t *host, unsigned mstmo)
208
208
  {
209
- lcb_host_t tmphost;
209
+ lcb_host_t tmphost = {0};
210
210
  sock->parent = this;
211
211
  if (!host) {
212
212
  populateHost(&tmphost);
@@ -222,7 +222,7 @@ Loop::connectPooled(ESocket *sock, lcb_host_t *host, unsigned mstmo)
222
222
  void
223
223
  Loop::connect(ESocket *sock, lcb_host_t *host, unsigned mstmo)
224
224
  {
225
- lcb_host_t tmphost;
225
+ lcb_host_t tmphost = {0};
226
226
 
227
227
  if (host == NULL) {
228
228
  populateHost(&tmphost);
@@ -62,7 +62,7 @@ static bool isRefused(lcbio_OSERR err)
62
62
  TEST_F(SockConnTest, testRefused)
63
63
  {
64
64
  ESocket sock;
65
- lcb_host_t host;
65
+ lcb_host_t host = {0};
66
66
  strcpy(host.host, "localhost");
67
67
  strcpy(host.port, "1");
68
68
  loop->connect(&sock, &host, 100000);
@@ -73,7 +73,7 @@ TEST_F(SockConnTest, testRefused)
73
73
  TEST_F(SockConnTest, testBadDomain)
74
74
  {
75
75
  ESocket sock;
76
- lcb_host_t host;
76
+ lcb_host_t host = {0};
77
77
  strcpy(host.host, "domain-should-not-work.nonexist.com");
78
78
  strcpy(host.port, "123");
79
79
  loop->connect(&sock, &host, 1000);
@@ -83,7 +83,7 @@ TEST_F(SockConnTest, testBadDomain)
83
83
  TEST_F(SockConnTest, testInvalidPort)
84
84
  {
85
85
  ESocket sock;
86
- lcb_host_t host;
86
+ lcb_host_t host = {0};
87
87
  strcpy(host.host, "localhost");
88
88
  strcpy(host.port, "111111111");
89
89
  loop->connect(&sock, &host, 1000);
@@ -93,7 +93,7 @@ TEST_F(SockConnTest, testInvalidPort)
93
93
  TEST_F(SockTest, testEmptyHost)
94
94
  {
95
95
  ESocket sock;
96
- lcb_host_t host;
96
+ lcb_host_t host = {0};
97
97
  host.host[0] = '\0';
98
98
  host.port[0] = '\0';
99
99
  loop->connect(&sock, &host, 1000);
@@ -103,7 +103,7 @@ TEST_F(SockTest, testEmptyHost)
103
103
  TEST_F(SockConnTest, testCancellation)
104
104
  {
105
105
  ESocket sock;
106
- lcb_host_t host;
106
+ lcb_host_t host = {0};
107
107
  loop->populateHost(&host);
108
108
  sock.creq = lcbio_connect(
109
109
  loop->iot, loop->settings, &host, 100000, NULL, NULL);
@@ -132,7 +132,7 @@ conncb_1(lcbio_SOCKET *sock, void *arg, lcb_error_t err, lcbio_OSERR syserr)
132
132
  TEST_F(SockConnTest, testImmediateUnref)
133
133
  {
134
134
  ESocket sock;
135
- lcb_host_t host;
135
+ lcb_host_t host = {0};
136
136
  sock.parent = loop;
137
137
  loop->populateHost(&host);
138
138
  sock.creq = lcbio_connect(
@@ -30,7 +30,7 @@ TEST_F(SockMgrTest, testBasic)
30
30
 
31
31
  TEST_F(SockMgrTest, testCancellation)
32
32
  {
33
- lcb_host_t host;
33
+ lcb_host_t host = {0};
34
34
  loop->populateHost(&host);
35
35
  lcb::io::ConnectionRequest *req = loop->sockpool->get(host, LCB_MS2US(1000), NULL, NULL);
36
36
  ASSERT_FALSE(req == NULL);
@@ -16,7 +16,7 @@ protected:
16
16
 
17
17
  SockTest::SetUp();
18
18
  loop->settings->sslopts = LCB_SSL_ENABLED|LCB_SSL_NOVERIFY;
19
- loop->settings->ssl_ctx = lcbio_ssl_new(NULL, 1, &errp, loop->settings);
19
+ loop->settings->ssl_ctx = lcbio_ssl_new(NULL, NULL, NULL, 1, &errp, loop->settings);
20
20
  loop->server->factory = TestServer::sslSocketFactory;
21
21
  EXPECT_FALSE(loop->settings->ssl_ctx == NULL) << lcb_strerror(NULL, errp);
22
22
  }
@@ -62,7 +62,7 @@ IF(NOT WIN32)
62
62
  cat create observe observe-seqno incr decr mcflush hash lock
63
63
  unlock rm stats version verbosity view n1ql admin ping
64
64
  bucket-create bucket-delete bucket-flush connstr write-config strerror
65
- touch role-list user-list user-upsert user-delete)
65
+ touch role-list user-list user-upsert user-delete watch)
66
66
 
67
67
  FOREACH(subcmd IN ITEMS ${CBC_SUBCOMMANDS})
68
68
  ADD_CUSTOM_COMMAND(TARGET cbc POST_BUILD
@@ -194,6 +194,23 @@ private:
194
194
  cliopts::BoolOption o_keystats;
195
195
  };
196
196
 
197
+ class WatchHandler : public Handler {
198
+ public:
199
+ HANDLER_DESCRIPTION("Aggregate and display server statistics")
200
+ HANDLER_USAGE("[KEYS ....] [OPTIONS ...]")
201
+ WatchHandler() : Handler("watch"), o_interval("interval") {
202
+ o_interval.abbrev('n').description("Update interval in seconds").setDefault(1);
203
+ }
204
+ protected:
205
+ void run();
206
+ void addOptions() {
207
+ Handler::addOptions();
208
+ parser.addOption(o_interval);
209
+ }
210
+ private:
211
+ cliopts::UIntOption o_interval;
212
+ };
213
+
197
214
  class VerbosityHandler : public Handler {
198
215
  public:
199
216
  HANDLER_DESCRIPTION("Modify the memcached logging level")
@@ -141,9 +141,9 @@ private:
141
141
  final_suffix = "\n";
142
142
  }
143
143
 
144
- printf("%sQUERIES/SEC: %lu\n", prefix, (long int)(n_queries / duration));
145
- printf("%sROWS/SEC: %lu\n", prefix, (long int)(n_rows / duration));
146
- printf("%sERRORS: %lu%s", prefix, n_errors, final_suffix);
144
+ printf("%sQUERIES/SEC: %lu\n", prefix, (unsigned long)(n_queries / duration));
145
+ printf("%sROWS/SEC: %lu\n", prefix, (unsigned long)(n_rows / duration));
146
+ printf("%sERRORS: %lu%s", prefix, (unsigned long)n_errors, final_suffix);
147
147
 
148
148
  if (hg != NULL) {
149
149
  hg->write();
@@ -213,11 +213,14 @@ public:
213
213
  }
214
214
 
215
215
  string curline;
216
- while (std::getline(ifs, curline).good() && !ifs.eof()) {
216
+ while (ifs.good()) {
217
+ std::getline(ifs, curline);
217
218
  if (!curline.empty()) {
218
219
  m_queries.push_back(curline);
219
220
  }
220
221
  }
222
+ std::cerr << "Loaded " << m_queries.size() << " queries "
223
+ << "from \"" << o_file.const_result() << "\"" << std::endl;
221
224
  if (m_params.useTimings()) {
222
225
  GlobalMetrics.prepare_timings();
223
226
  }
@@ -32,6 +32,7 @@
32
32
  #include <signal.h>
33
33
  #ifndef WIN32
34
34
  #include <pthread.h>
35
+ #include <libcouchbase/metrics.h>
35
36
  #else
36
37
  #define usleep(n) Sleep(n/1000)
37
38
  #endif
@@ -105,6 +106,7 @@ public:
105
106
  o_keyPrefix("key-prefix"),
106
107
  o_numThreads("num-threads"),
107
108
  o_randSeed("random-seed"),
109
+ o_randomBody("random-body"),
108
110
  o_setPercent("set-pct"),
109
111
  o_minSize("min-size"),
110
112
  o_maxSize("max-size"),
@@ -121,13 +123,19 @@ public:
121
123
  o_noop("noop"),
122
124
  o_sdPathCount("pathcount"),
123
125
  o_populateOnly("populate-only"),
124
- o_exptime("expiry")
126
+ o_exptime("expiry"),
127
+ o_collection("collection"),
128
+ o_separator("separator"),
129
+ o_persist("persist-to"),
130
+ o_replicate("replicate-to"),
131
+ o_lock("lock")
125
132
  {
126
133
  o_multiSize.setDefault(100).abbrev('B').description("Number of operations to batch");
127
134
  o_numItems.setDefault(1000).abbrev('I').description("Number of items to operate on");
128
135
  o_keyPrefix.abbrev('p').description("key prefix to use");
129
136
  o_numThreads.setDefault(1).abbrev('t').description("The number of threads to use");
130
137
  o_randSeed.setDefault(0).abbrev('s').description("Specify random seed").hide();
138
+ o_randomBody.setDefault(false).abbrev('R').description("Randomize document body (otherwise use 'x' and '*' to fill)");
131
139
  o_setPercent.setDefault(33).abbrev('r').description("The percentage of operations which should be mutations");
132
140
  o_minSize.setDefault(50).abbrev('m').description("Set minimum payload size");
133
141
  o_maxSize.setDefault(5120).abbrev('M').description("Set maximum payload size");
@@ -138,7 +146,7 @@ public:
138
146
  o_startAt.setDefault(0).description("For sequential access, set the first item");
139
147
  o_rateLimit.setDefault(0).description("Set operations per second limit (per thread)");
140
148
  o_userdocs.description("User documents to load (overrides --min-size and --max-size");
141
- o_writeJson.description("Enable writing JSON values (rather than bytes)");
149
+ o_writeJson.abbrev('J').description("Enable writing JSON values (rather than bytes)");
142
150
  o_templatePairs.description("Values for templates to be inserted into user documents");
143
151
  o_templatePairs.argdesc("FIELD,MIN,MAX[,SEQUENTIAL]").hide();
144
152
  o_subdoc.description("Use subdoc instead of fulldoc operations");
@@ -146,6 +154,12 @@ public:
146
154
  o_sdPathCount.description("Number of subdoc paths per command").setDefault(1);
147
155
  o_populateOnly.description("Exit after documents have been populated");
148
156
  o_exptime.description("Set TTL for items").abbrev('e');
157
+ o_collection.description("Allowed collection name (could be specified multiple times)").hide();
158
+ o_separator.setDefault(":").description("Separator for collection prefix in keys").hide();
159
+ o_persist.description("Wait until item is persisted to this number of nodes (-1 for master+replicas)").setDefault(0);
160
+ o_replicate.description("Wait until item is replicated to this number of nodes (-1 for all replicas)").setDefault(0);
161
+ o_lock.description("Lock keys for updates for given time (will not lock when set to zero)").setDefault(0);
162
+ params.getTimings().description("Enable command timings (second time to dump timings automatically)");
149
163
  }
150
164
 
151
165
  void processOptions() {
@@ -153,7 +167,18 @@ public:
153
167
  prefix = o_keyPrefix.result();
154
168
  setprc = o_setPercent.result();
155
169
  shouldPopulate = !o_noPopulate.result();
170
+ persistTo = o_persist.result();
171
+ replicateTo = o_replicate.result();
172
+ lockTime = o_lock.result();
173
+ if (lockTime && o_numItems < opsPerCycle * o_numThreads) {
174
+ fprintf(stderr, "The --num-items=%d cannot be smaller than --batch-size=%d multiplied to --num-thread=%d when used with --lock=%d\n",
175
+ (int)o_numItems, (int)opsPerCycle, (int)o_numThreads, (int)lockTime);
176
+ exit(EXIT_FAILURE);
177
+ }
156
178
 
179
+ if (o_keyPrefix.passed() && o_collection.passed()) {
180
+ throw std::runtime_error("The --collection is not compatible with --key-prefix");
181
+ }
157
182
  if (depr.loop.passed()) {
158
183
  fprintf(stderr, "The --loop/-l option is deprecated. Use --num-cycles\n");
159
184
  maxCycles = -1;
@@ -211,17 +236,17 @@ public:
211
236
 
212
237
  if (specs.empty()) {
213
238
  if (o_writeJson.result()) {
214
- docgen = new JsonDocGenerator(o_minSize.result(), o_maxSize.result());
239
+ docgen = new JsonDocGenerator(o_minSize.result(), o_maxSize.result(), o_randomBody.numSpecified());
215
240
  } else if (!userdocs.empty()) {
216
241
  docgen = new PresetDocGenerator(userdocs);
217
242
  } else {
218
- docgen = new RawDocGenerator(o_minSize.result(), o_maxSize.result());
243
+ docgen = new RawDocGenerator(o_minSize.result(), o_maxSize.result(), o_randomBody.numSpecified());
219
244
  }
220
245
  } else {
221
246
  if (o_writeJson.result()) {
222
247
  if (userdocs.empty()) {
223
248
  docgen = new PlaceholderJsonGenerator(
224
- o_minSize.result(), o_maxSize.result(), specs);
249
+ o_minSize.result(), o_maxSize.result(), specs, o_randomBody.numSpecified());
225
250
  } else {
226
251
  docgen = new PlaceholderJsonGenerator(userdocs, specs);
227
252
  }
@@ -237,6 +262,17 @@ public:
237
262
  if (o_sdPathCount.passed()) {
238
263
  o_subdoc.setDefault(true);
239
264
  }
265
+
266
+ if (o_collection.passed()) {
267
+ string separator = o_separator.result();
268
+ if (separator.empty()) {
269
+ throw std::runtime_error("Collection name separator must not be empty");
270
+ }
271
+ vector<string> names = o_collection.result();
272
+ for (size_t ii = 0; ii < names.size(); ii++) {
273
+ collections.push_back(names[ii] + separator);
274
+ }
275
+ }
240
276
  }
241
277
 
242
278
  void addOptions(Parser& parser) {
@@ -245,6 +281,7 @@ public:
245
281
  parser.addOption(o_keyPrefix);
246
282
  parser.addOption(o_numThreads);
247
283
  parser.addOption(o_randSeed);
284
+ parser.addOption(o_randomBody);
248
285
  parser.addOption(o_setPercent);
249
286
  parser.addOption(o_noPopulate);
250
287
  parser.addOption(o_minSize);
@@ -262,11 +299,16 @@ public:
262
299
  parser.addOption(o_sdPathCount);
263
300
  parser.addOption(o_populateOnly);
264
301
  parser.addOption(o_exptime);
302
+ parser.addOption(o_collection);
303
+ parser.addOption(o_separator);
304
+ parser.addOption(o_persist);
305
+ parser.addOption(o_replicate);
306
+ parser.addOption(o_lock);
265
307
  params.addToParser(parser);
266
308
  depr.addOptions(parser);
267
309
  }
268
310
 
269
- bool isTimings(void) { return params.useTimings(); }
311
+ int numTimings(void) { return params.numTimings(); }
270
312
 
271
313
  bool isLoopDone(size_t niter) {
272
314
  if (maxCycles == -1) {
@@ -282,6 +324,8 @@ public:
282
324
  bool sequentialAccess() { return o_sequential; }
283
325
  bool isSubdoc() { return o_subdoc; }
284
326
  bool isNoop() { return o_noop.result(); }
327
+ bool useCollections() { return o_collection.passed(); }
328
+ bool writeJson() { return o_writeJson.result(); }
285
329
  unsigned firstKeyOffset() { return o_startAt; }
286
330
  uint32_t getNumItems() { return o_numItems; }
287
331
  uint32_t getRateLimit() { return o_rateLimit; }
@@ -296,6 +340,10 @@ public:
296
340
  bool hasTemplates;
297
341
  ConnParams params;
298
342
  const DocGeneratorBase *docgen;
343
+ vector<string> collections;
344
+ int replicateTo;
345
+ int persistTo;
346
+ int lockTime;
299
347
 
300
348
  private:
301
349
  UIntOption o_multiSize;
@@ -303,6 +351,7 @@ private:
303
351
  StringOption o_keyPrefix;
304
352
  UIntOption o_numThreads;
305
353
  UIntOption o_randSeed;
354
+ BoolOption o_randomBody;
306
355
  UIntOption o_setPercent;
307
356
  UIntOption o_minSize;
308
357
  UIntOption o_maxSize;
@@ -331,6 +380,12 @@ private:
331
380
 
332
381
  UIntOption o_exptime;
333
382
 
383
+ ListOption o_collection;
384
+ StringOption o_separator;
385
+ IntOption o_persist;
386
+ IntOption o_replicate;
387
+
388
+ IntOption o_lock;
334
389
  DeprecatedOptions depr;
335
390
  } config;
336
391
 
@@ -341,7 +396,7 @@ void log(const char *format, ...)
341
396
 
342
397
  va_start(args, format);
343
398
  vsprintf(buffer, format, args);
344
- if (config.isTimings()) {
399
+ if (config.numTimings() > 0) {
345
400
  std::cerr << "[" << std::fixed << lcb_nstime() / 1000000000.0 << "] ";
346
401
  }
347
402
  std::cerr << buffer << std::endl;
@@ -352,16 +407,24 @@ void log(const char *format, ...)
352
407
 
353
408
  extern "C" {
354
409
  static void operationCallback(lcb_t, int, const lcb_RESPBASE*);
410
+ static void storeCallback(lcb_t, int, const lcb_RESPBASE *);
355
411
  }
356
412
 
413
+ class ThreadContext;
414
+
357
415
  class InstanceCookie {
358
416
  public:
359
417
  InstanceCookie(lcb_t instance) {
360
418
  lcb_set_cookie(instance, this);
361
419
  lastPrint = 0;
362
- if (config.isTimings()) {
420
+ if (config.numTimings() > 0) {
363
421
  hg.install(instance, stdout);
364
422
  }
423
+ stats.total = 0;
424
+ stats.retried = 0;
425
+ stats.etmpfail = 0;
426
+ stats.eexist = 0;
427
+ stats.etimeout = 0;
365
428
  }
366
429
 
367
430
  static InstanceCookie* get(lcb_t instance) {
@@ -369,7 +432,7 @@ public:
369
432
  }
370
433
 
371
434
 
372
- static void dumpTimings(lcb_t instance, const char *header, bool force=false) {
435
+ static void dumpTimings(lcb_t instance, const char *header = NULL, bool force=false) {
373
436
  time_t now = time(NULL);
374
437
  InstanceCookie *ic = get(instance);
375
438
 
@@ -380,19 +443,37 @@ public:
380
443
  }
381
444
 
382
445
  Histogram &h = ic->hg;
383
- printf("[%f %s]\n", lcb_nstime() / 1000000000.0, header);
384
- printf(" +---------+---------+---------+---------+\n");
446
+ if (header) {
447
+ printf("[%f %s]\n", lcb_nstime() / 1000000000.0, header);
448
+ }
449
+ printf(" +---------+---------+---------+---------+\n");
385
450
  h.write();
386
- printf(" +----------------------------------------\n");
451
+ printf(" +----------------------------------------\n");
387
452
  }
388
453
 
454
+ void setContext(ThreadContext *context) {
455
+ m_context = context;
456
+ }
457
+
458
+ ThreadContext * getContext() {
459
+ return m_context;
460
+ }
461
+
462
+ struct {
463
+ size_t total;
464
+ size_t retried;
465
+ size_t etmpfail;
466
+ size_t eexist;
467
+ size_t etimeout;
468
+ } stats;
389
469
  private:
390
470
  time_t lastPrint;
391
471
  Histogram hg;
472
+ ThreadContext *m_context;
392
473
  };
393
474
 
394
475
  struct NextOp {
395
- NextOp() : m_seqno(0), m_mode(GET) {}
476
+ NextOp() : m_seqno(0), m_mode(GET), m_cas(0) {}
396
477
 
397
478
  string m_key;
398
479
  uint32_t m_seqno;
@@ -401,6 +482,7 @@ struct NextOp {
401
482
  // The mode here is for future use with subdoc
402
483
  enum Mode { STORE, GET, SDSTORE, SDGET, NOOP };
403
484
  Mode m_mode;
485
+ uint64_t m_cas;
404
486
  };
405
487
 
406
488
  class OpGenerator {
@@ -410,7 +492,9 @@ public:
410
492
  virtual ~OpGenerator() {};
411
493
  virtual void setNextOp(NextOp& op) = 0;
412
494
  virtual void setValue(NextOp& op) = 0;
495
+ virtual void populateIov(uint32_t, vector<lcb_IOV>&) = 0;
413
496
  virtual bool inPopulation() const = 0;
497
+ virtual void checkin(uint32_t) = 0;
414
498
  virtual const char *getStageString() const = 0;
415
499
 
416
500
  protected:
@@ -427,11 +511,14 @@ public:
427
511
  }
428
512
 
429
513
  void setValue(NextOp&) {}
514
+ void populateIov(uint32_t, vector<lcb_IOV>&) {}
430
515
 
431
516
  bool inPopulation() const {
432
517
  return false;
433
518
  }
434
519
 
520
+ void checkin(uint32_t) {}
521
+
435
522
  const char *getStageString() const {
436
523
  return "Run";
437
524
  }
@@ -481,6 +568,10 @@ public:
481
568
  m_local_genstate->populateIov(op.m_seqno, op.m_valuefrags);
482
569
  }
483
570
 
571
+ void populateIov(uint32_t seq, vector<lcb_IOV>& iov_out) {
572
+ m_local_genstate->populateIov(seq, iov_out);
573
+ }
574
+
484
575
  void setNextOp(NextOp& op) {
485
576
  bool store_override = false;
486
577
 
@@ -494,10 +585,10 @@ public:
494
585
  }
495
586
  }
496
587
 
497
- if (m_force_sequential) {
498
- op.m_seqno = m_gensequence->next();
588
+ if (m_in_population || !config.lockTime) {
589
+ op.m_seqno = (m_force_sequential ? m_gensequence : m_genrandom)->next();
499
590
  } else {
500
- op.m_seqno = m_genrandom->next();
591
+ op.m_seqno = (m_force_sequential ? m_gensequence : m_genrandom)->checkout();
501
592
  }
502
593
 
503
594
  if (store_override) {
@@ -531,6 +622,10 @@ public:
531
622
  return m_in_population;
532
623
  }
533
624
 
625
+ void checkin(uint32_t seqno) {
626
+ (m_force_sequential ? m_gensequence : m_genrandom)->checkin(seqno);
627
+ }
628
+
534
629
  const char *getStageString() const {
535
630
  if (m_in_population) {
536
631
  return "Populate";
@@ -554,14 +649,16 @@ private:
554
649
  uint32_t seqno = op.m_seqno;
555
650
  char buffer[21];
556
651
  snprintf(buffer, sizeof(buffer), "%020d", seqno);
557
- op.m_key.assign(config.getKeyPrefix() + buffer);
652
+ string &prefix = config.useCollections()
653
+ ? config.collections[seqno % config.collections.size()]
654
+ : config.getKeyPrefix();
655
+ op.m_key.assign(prefix + buffer);
558
656
  }
559
657
 
560
658
 
561
659
  SeqGenerator *m_genrandom;
562
660
  SeqGenerator *m_gensequence;
563
661
  size_t m_gencount;
564
- int m_id;
565
662
 
566
663
  bool m_force_sequential;
567
664
  bool m_in_population;
@@ -571,6 +668,8 @@ private:
571
668
  SubdocGeneratorState *m_sdgenstate;
572
669
  };
573
670
 
671
+ #define OPFLAGS_LOCKED 0x01
672
+
574
673
  class ThreadContext
575
674
  {
576
675
  public:
@@ -591,79 +690,52 @@ public:
591
690
  return gen && (gen->inPopulation() || !retryq.empty());
592
691
  }
593
692
 
693
+ void checkin(uint32_t seqno) {
694
+ if (gen) {
695
+ gen->checkin(seqno);
696
+ }
697
+ }
698
+
594
699
  void singleLoop() {
595
700
  bool hasItems = false;
596
- NextOp opinfo;
597
- unsigned exptime = config.getExptime();
598
701
 
599
702
  lcb_sched_enter(instance);
600
703
  for (size_t ii = 0; ii < config.opsPerCycle; ++ii) {
601
- gen->setNextOp(opinfo);
602
-
603
- switch (opinfo.m_mode) {
604
- case NextOp::STORE: {
605
- lcb_CMDSTORE scmd = { 0 };
606
- scmd.operation = LCB_SET;
607
- scmd.exptime = exptime;
608
- LCB_CMD_SET_KEY(&scmd, opinfo.m_key.c_str(), opinfo.m_key.size());
609
- LCB_CMD_SET_VALUEIOV(&scmd, &opinfo.m_valuefrags[0], opinfo.m_valuefrags.size());
610
- error = lcb_store3(instance, this, &scmd);
611
- break;
612
- }
613
- case NextOp::GET: {
614
- lcb_CMDGET gcmd = { 0 };
615
- LCB_CMD_SET_KEY(&gcmd, opinfo.m_key.c_str(), opinfo.m_key.size());
616
- gcmd.exptime = exptime;
617
- error = lcb_get3(instance, this, &gcmd);
618
- break;
619
- }
620
- case NextOp::SDSTORE:
621
- case NextOp::SDGET: {
622
- lcb_CMDSUBDOC sdcmd = { 0 };
623
- if (opinfo.m_mode == NextOp::SDSTORE) {
624
- sdcmd.exptime = exptime;
625
- }
626
- LCB_CMD_SET_KEY(&sdcmd, opinfo.m_key.c_str(), opinfo.m_key.size());
627
- sdcmd.specs = &opinfo.m_specs[0];
628
- sdcmd.nspecs = opinfo.m_specs.size();
629
- error = lcb_subdoc3(instance, this, &sdcmd);
630
- break;
631
- }
632
- case NextOp::NOOP: {
633
- lcb_CMDNOOP ncmd = { 0 };
634
- error = lcb_noop3(instance, this, &ncmd);
635
- break;
636
- }
637
- }
638
-
639
- if (error != LCB_SUCCESS) {
640
- hasItems = false;
641
- log("Failed to schedule operation: [0x%x] %s", error, lcb_strerror(instance, error));
642
- } else {
643
- hasItems = true;
644
- }
704
+ hasItems = scheduleNextOperation();
645
705
  }
646
706
  if (hasItems) {
707
+ error = LCB_SUCCESS;
647
708
  lcb_sched_leave(instance);
648
709
  lcb_wait(instance);
649
- if (error != LCB_SUCCESS) {
650
- log("Operation(s) failed: [0x%x] %s", error, lcb_strerror(instance, error));
651
- }
652
710
  } else {
653
711
  lcb_sched_fail(instance);
654
712
  }
713
+ purgeRetryQueue();
714
+ }
715
+
716
+ void purgeRetryQueue() {
717
+ NextOp opinfo;
718
+ InstanceCookie *cookie = InstanceCookie::get(instance);
655
719
 
656
720
  while (!retryq.empty()) {
721
+ unsigned exptime = config.getExptime();
657
722
  lcb_sched_enter(instance);
658
723
  while (!retryq.empty()) {
659
724
  opinfo = retryq.front();
660
725
  retryq.pop();
661
- lcb_CMDSTORE scmd = { 0 };
726
+ lcb_CMDSTOREDUR scmd = { 0 };
662
727
  scmd.operation = LCB_SET;
663
728
  scmd.exptime = exptime;
664
729
  LCB_CMD_SET_KEY(&scmd, opinfo.m_key.c_str(), opinfo.m_key.size());
665
730
  LCB_CMD_SET_VALUEIOV(&scmd, &opinfo.m_valuefrags[0], opinfo.m_valuefrags.size());
666
- error = lcb_store3(instance, this, &scmd);
731
+ if (config.persistTo > 0 || config.replicateTo > 0) {
732
+ scmd.persist_to = config.persistTo;
733
+ scmd.replicate_to = config.replicateTo;
734
+ error = lcb_storedur3(instance, NULL, &scmd);
735
+ } else {
736
+ error = lcb_store3(instance, NULL, reinterpret_cast<lcb_CMDSTORE*>(&scmd));
737
+ }
738
+ cookie->stats.retried++;
667
739
  }
668
740
  lcb_sched_leave(instance);
669
741
  lcb_wait(instance);
@@ -673,11 +745,77 @@ public:
673
745
  }
674
746
  }
675
747
 
748
+ bool scheduleNextOperation()
749
+ {
750
+ NextOp opinfo;
751
+ unsigned exptime = config.getExptime();
752
+ gen->setNextOp(opinfo);
753
+
754
+ switch (opinfo.m_mode) {
755
+ case NextOp::STORE: {
756
+ if (!gen->inPopulation() && config.lockTime > 0) {
757
+ lcb_CMDGET gcmd = { 0 };
758
+ LCB_CMD_SET_KEY(&gcmd, opinfo.m_key.c_str(), opinfo.m_key.size());
759
+ gcmd.lock = config.lockTime;
760
+ error = lcb_get3(instance, (void *)OPFLAGS_LOCKED, &gcmd);
761
+ } else {
762
+ lcb_CMDSTOREDUR scmd = { 0 };
763
+ scmd.operation = LCB_SET;
764
+ scmd.exptime = exptime;
765
+ if (config.writeJson()) {
766
+ scmd.datatype = LCB_VALUE_F_JSON;
767
+ }
768
+ LCB_CMD_SET_KEY(&scmd, opinfo.m_key.c_str(), opinfo.m_key.size());
769
+ LCB_CMD_SET_VALUEIOV(&scmd, &opinfo.m_valuefrags[0], opinfo.m_valuefrags.size());
770
+ if (config.persistTo > 0 || config.replicateTo > 0) {
771
+ scmd.persist_to = config.persistTo;
772
+ scmd.replicate_to = config.replicateTo;
773
+ error = lcb_storedur3(instance, NULL, &scmd);
774
+ } else {
775
+ error = lcb_store3(instance, NULL, reinterpret_cast<lcb_CMDSTORE*>(&scmd));
776
+ }
777
+ }
778
+ break;
779
+ }
780
+ case NextOp::GET: {
781
+ lcb_CMDGET gcmd = { 0 };
782
+ LCB_CMD_SET_KEY(&gcmd, opinfo.m_key.c_str(), opinfo.m_key.size());
783
+ gcmd.exptime = exptime;
784
+ error = lcb_get3(instance, this, &gcmd);
785
+ break;
786
+ }
787
+ case NextOp::SDSTORE:
788
+ case NextOp::SDGET: {
789
+ lcb_CMDSUBDOC sdcmd = { 0 };
790
+ if (opinfo.m_mode == NextOp::SDSTORE) {
791
+ sdcmd.exptime = exptime;
792
+ }
793
+ LCB_CMD_SET_KEY(&sdcmd, opinfo.m_key.c_str(), opinfo.m_key.size());
794
+ sdcmd.specs = &opinfo.m_specs[0];
795
+ sdcmd.nspecs = opinfo.m_specs.size();
796
+ error = lcb_subdoc3(instance, NULL, &sdcmd);
797
+ break;
798
+ }
799
+ case NextOp::NOOP: {
800
+ lcb_CMDNOOP ncmd = { 0 };
801
+ error = lcb_noop3(instance, NULL, &ncmd);
802
+ break;
803
+ }
804
+ }
805
+
806
+ if (error != LCB_SUCCESS) {
807
+ log("Failed to schedule operation: [0x%x] %s", error, lcb_strerror(instance, error));
808
+ return false;
809
+ } else {
810
+ return true;
811
+ }
812
+ }
813
+
676
814
  bool run() {
677
815
  do {
678
816
  singleLoop();
679
817
 
680
- if (config.isTimings()) {
818
+ if (config.numTimings() > 1) {
681
819
  InstanceCookie::dumpTimings(instance, gen->getStageString());
682
820
  }
683
821
  if (config.params.shouldDump()) {
@@ -689,7 +827,7 @@ public:
689
827
 
690
828
  } while (!config.isLoopDone(++niter));
691
829
 
692
- if (config.isTimings()) {
830
+ if (config.numTimings() > 1) {
693
831
  InstanceCookie::dumpTimings(instance, gen->getStageString(), true);
694
832
  }
695
833
  return true;
@@ -702,13 +840,25 @@ public:
702
840
  retryq.push(op);
703
841
  }
704
842
 
843
+ void populateIov(uint32_t seq, vector<lcb_IOV>& iov_out)
844
+ {
845
+ gen->populateIov(seq, iov_out);
846
+ }
847
+
848
+
705
849
  #ifndef WIN32
706
850
  pthread_t thr;
707
851
  #endif
708
852
 
853
+ lcb_t getInstance() {
854
+ return instance;
855
+ }
856
+
709
857
  protected:
710
858
  // the callback methods needs to be able to set the error handler..
711
859
  friend void operationCallback(lcb_t, int, const lcb_RESPBASE*);
860
+ friend void storeCallback(lcb_t, int, const lcb_RESPBASE *);
861
+
712
862
  Histogram histogram;
713
863
 
714
864
  void setError(lcb_error_t e) { error = e; }
@@ -743,52 +893,195 @@ private:
743
893
  std::queue<NextOp> retryq;
744
894
  };
745
895
 
746
- static void operationCallback(lcb_t, int cbtype, const lcb_RESPBASE *resp)
896
+ static void updateOpsPerSecDisplay()
747
897
  {
748
- ThreadContext *tc;
749
898
 
750
- tc = const_cast<ThreadContext *>(reinterpret_cast<const ThreadContext *>(resp->cookie));
751
- if (cbtype == LCB_CALLBACK_STORE && resp->rc != LCB_SUCCESS && tc->inPopulation()) {
899
+ static time_t start_time = time(NULL);
900
+ static int is_tty =
901
+ #ifdef WIN32
902
+ 0;
903
+ #else
904
+ isatty(STDERR_FILENO);
905
+ #endif
906
+ static volatile unsigned long nops = 0;
907
+ time_t now = time(NULL);
908
+ time_t nsecs = now - start_time;
909
+ if (!nsecs) { nsecs = 1; }
910
+ unsigned long ops_sec = nops / nsecs;
911
+ if (++nops % 10000 == 0) {
912
+ fprintf(stderr, "OPS/SEC: %10lu%c", ops_sec, is_tty ? '\r' : '\n');
913
+ }
914
+ }
915
+
916
+ static void updateStats(InstanceCookie *cookie, lcb_error_t rc)
917
+ {
918
+ cookie->stats.total++;
919
+ switch (rc) {
920
+ case LCB_ETMPFAIL:
921
+ cookie->stats.etmpfail++;
922
+ break;
923
+ case LCB_KEY_EEXISTS:
924
+ cookie->stats.eexist++;
925
+ break;
926
+ case LCB_ETIMEDOUT:
927
+ cookie->stats.etimeout++;
928
+ break;
929
+ default:
930
+ break;
931
+ }
932
+ }
933
+
934
+ static void operationCallback(lcb_t instance, int cbtype, const lcb_RESPBASE *resp)
935
+ {
936
+ InstanceCookie *cookie = InstanceCookie::get(instance);
937
+ ThreadContext *tc = cookie->getContext();
938
+ tc->setError(resp->rc);
939
+ updateStats(cookie, resp->rc);
940
+
941
+ uintptr_t flags = 0;
942
+ if (resp->cookie) {
943
+ flags = (uintptr_t)resp->cookie;
944
+ }
945
+ bool done = true;
946
+ string key((const char*)resp->key, resp->nkey);
947
+ uint32_t seqno = atoi(key.c_str());
948
+ if (cbtype == LCB_CALLBACK_GET && (flags & OPFLAGS_LOCKED)) {
949
+ if (resp->rc == LCB_SUCCESS) {
950
+ lcb_CMDSTOREDUR scmd = { 0 };
951
+ vector<lcb_IOV> valuefrags;
952
+ scmd.operation = LCB_SET;
953
+ scmd.exptime = config.getExptime();
954
+ scmd.cas = resp->cas;
955
+ tc->populateIov(seqno, valuefrags);
956
+ LCB_CMD_SET_KEY(&scmd, resp->key, resp->nkey);
957
+ LCB_CMD_SET_VALUEIOV(&scmd, &valuefrags[0], valuefrags.size());
958
+ if (config.persistTo > 0 || config.replicateTo > 0) {
959
+ scmd.persist_to = config.persistTo;
960
+ scmd.replicate_to = config.replicateTo;
961
+ lcb_storedur3(instance, NULL, &scmd);
962
+ } else {
963
+ lcb_store3(instance, NULL, reinterpret_cast<lcb_CMDSTORE*>(&scmd));
964
+ }
965
+ done = false;
966
+ } else if (resp->rc == LCB_ETMPFAIL) {
967
+ NextOp op;
968
+ op.m_mode = NextOp::STORE;
969
+ op.m_key = key;
970
+ op.m_seqno = seqno;
971
+ tc->retry(op);
972
+ done = false;
973
+ }
974
+ }
975
+
976
+ if (done) {
977
+ tc->checkin(seqno);
978
+ }
979
+ updateOpsPerSecDisplay();
980
+ }
981
+
982
+ static void storeCallback(lcb_t instance, int, const lcb_RESPBASE *resp)
983
+ {
984
+ InstanceCookie *cookie = InstanceCookie::get(instance);
985
+ ThreadContext *tc = cookie->getContext();
986
+ tc->setError(resp->rc);
987
+ updateStats(cookie, resp->rc);
988
+
989
+ string key((const char*)resp->key, resp->nkey);
990
+ uint32_t seqno = atoi(key.c_str());
991
+ if (resp->rc != LCB_SUCCESS && tc->inPopulation()) {
752
992
  NextOp op;
753
993
  op.m_mode = NextOp::STORE;
754
- op.m_key.assign((char *)resp->key, resp->nkey);
755
- op.m_seqno = atoi(op.m_key.c_str());
994
+ op.m_key = key;
995
+ op.m_seqno = seqno;
756
996
  tc->retry(op);
757
997
  } else {
758
- tc->setError(resp->rc);
998
+ tc->checkin(seqno);
759
999
  }
760
1000
 
761
- #ifndef WIN32
762
- static volatile unsigned long nops = 1;
763
- static time_t start_time = time(NULL);
764
- static int is_tty = isatty(STDERR_FILENO);
765
- if (is_tty) {
766
- if (++nops % 1000 == 0) {
767
- time_t now = time(NULL);
768
- time_t nsecs = now - start_time;
769
- if (!nsecs) { nsecs = 1; }
770
- unsigned long ops_sec = nops / nsecs;
771
- fprintf(stderr, "OPS/SEC: %10lu\r", ops_sec);
772
- }
773
- }
774
- #endif
1001
+ updateOpsPerSecDisplay();
775
1002
  }
776
1003
 
777
-
778
1004
  std::list<ThreadContext *> contexts;
779
1005
 
780
1006
  extern "C" {
781
- typedef void (*handler_t)(int);
1007
+ typedef void (*handler_t)(int);
1008
+
1009
+ static void dump_metrics(void)
1010
+ {
1011
+ std::list<ThreadContext *>::iterator it;
1012
+ for (it = contexts.begin(); it != contexts.end(); ++it) {
1013
+ lcb_t instance = (*it)->getInstance();
1014
+ lcb_CMDDIAG req = {};
1015
+ req.options = LCB_PINGOPT_F_JSONPRETTY;
1016
+ lcb_diag(instance, NULL, &req);
1017
+ if (config.numTimings() > 0) {
1018
+ InstanceCookie::dumpTimings(instance);
1019
+ }
1020
+ }
782
1021
  }
783
1022
 
784
1023
  #ifndef WIN32
1024
+ static void diag_callback(lcb_t instance, int, const lcb_RESPBASE *rb)
1025
+ {
1026
+ const lcb_RESPDIAG *resp = (const lcb_RESPDIAG *)rb;
1027
+ if (resp->rc != LCB_SUCCESS) {
1028
+ fprintf(stderr, "%p, diag failed: %s\n", (void *)instance, lcb_strerror(NULL, resp->rc));
1029
+ } else {
1030
+ if (resp->njson) {
1031
+ fprintf(stderr, "\n%.*s", (int)resp->njson, resp->json);
1032
+ }
1033
+
1034
+ {
1035
+ InstanceCookie *cookie = InstanceCookie::get(instance);
1036
+ lcb_METRICS* metrics;
1037
+ size_t ii;
1038
+ lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_METRICS, &metrics);
1039
+
1040
+ fprintf(stderr, "%p: total: %lu, etmpfail: %lu, eexist: %lu, etimeout: %lu, retried: %lu, rq: %lu\n",
1041
+ (void *)instance,
1042
+ (unsigned long)cookie->stats.total,
1043
+ (unsigned long)cookie->stats.etmpfail,
1044
+ (unsigned long)cookie->stats.eexist,
1045
+ (unsigned long)cookie->stats.etimeout,
1046
+ (unsigned long)cookie->stats.retried,
1047
+ (unsigned long)metrics->packets_retried);
1048
+ for (ii = 0; ii < metrics->nservers; ii++) {
1049
+ fprintf(stderr, " [srv-%d] snt: %lu, rcv: %lu, q: %lu, err: %lu, tmo: %lu, nmv: %lu, orph: %lu\n",
1050
+ (int)ii,
1051
+ (unsigned long)metrics->servers[ii]->packets_sent,
1052
+ (unsigned long)metrics->servers[ii]->packets_read,
1053
+ (unsigned long)metrics->servers[ii]->packets_queued,
1054
+ (unsigned long)metrics->servers[ii]->packets_errored,
1055
+ (unsigned long)metrics->servers[ii]->packets_timeout,
1056
+ (unsigned long)metrics->servers[ii]->packets_nmv,
1057
+ (unsigned long)metrics->servers[ii]->packets_ownerless);
1058
+ }
1059
+ }
1060
+ }
1061
+ }
1062
+
1063
+ static void sigquit_handler(int)
1064
+ {
1065
+ dump_metrics();
1066
+ signal(SIGQUIT, sigquit_handler); // Reinstall
1067
+ }
1068
+
1069
+ static void setup_sigquit_handler()
1070
+ {
1071
+ struct sigaction action;
1072
+ sigemptyset(&action.sa_mask);
1073
+ action.sa_handler = sigquit_handler;
1074
+ action.sa_flags = 0;
1075
+ sigaction(SIGQUIT, &action, NULL);
1076
+ }
1077
+
785
1078
  static void sigint_handler(int)
786
1079
  {
787
1080
  static int ncalled = 0;
788
1081
  ncalled++;
789
1082
 
790
1083
  if (ncalled < 2) {
791
- log("Termination requested. Waiting threads to finish. Ctrl-C to force termination.");
1084
+ log("\nTermination requested. Waiting threads to finish. Ctrl-C to force termination.");
792
1085
  signal(SIGINT, sigint_handler); // Reinstall
793
1086
  config.maxCycles = 0;
794
1087
  return;
@@ -811,9 +1104,7 @@ static void setup_sigint_handler()
811
1104
  sigaction(SIGINT, &action, NULL);
812
1105
  }
813
1106
 
814
- extern "C" {
815
1107
  static void* thread_worker(void*);
816
- }
817
1108
 
818
1109
  static void start_worker(ThreadContext *ctx)
819
1110
  {
@@ -837,12 +1128,12 @@ static void join_worker(ThreadContext *ctx)
837
1128
  }
838
1129
 
839
1130
  #else
1131
+ static void setup_sigquit_handler() {}
840
1132
  static void setup_sigint_handler() {}
841
1133
  static void start_worker(ThreadContext *ctx) { ctx->run(); }
842
1134
  static void join_worker(ThreadContext *ctx) { (void)ctx; }
843
1135
  #endif
844
1136
 
845
- extern "C" {
846
1137
  static void *thread_worker(void *arg)
847
1138
  {
848
1139
  ThreadContext *ctx = static_cast<ThreadContext *>(arg);
@@ -855,6 +1146,7 @@ int main(int argc, char **argv)
855
1146
  {
856
1147
  int exit_code = EXIT_SUCCESS;
857
1148
  setup_sigint_handler();
1149
+ setup_sigquit_handler();
858
1150
 
859
1151
  Parser parser("cbc-pillowfight");
860
1152
  try {
@@ -890,14 +1182,26 @@ int main(int argc, char **argv)
890
1182
  log("Failed to create instance: %s", lcb_strerror(NULL, error));
891
1183
  exit(EXIT_FAILURE);
892
1184
  }
893
- lcb_install_callback3(instance, LCB_CALLBACK_STORE, operationCallback);
1185
+ lcb_install_callback3(instance, LCB_CALLBACK_STOREDUR, storeCallback);
1186
+ lcb_install_callback3(instance, LCB_CALLBACK_STORE, storeCallback);
894
1187
  lcb_install_callback3(instance, LCB_CALLBACK_GET, operationCallback);
895
1188
  lcb_install_callback3(instance, LCB_CALLBACK_SDMUTATE, operationCallback);
896
1189
  lcb_install_callback3(instance, LCB_CALLBACK_SDLOOKUP, operationCallback);
897
1190
  lcb_install_callback3(instance, LCB_CALLBACK_NOOP, operationCallback);
1191
+ #ifndef WIN32
1192
+ lcb_install_callback3(instance, LCB_CALLBACK_DIAG, diag_callback);
1193
+ {
1194
+ int activate = 1;
1195
+ lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_METRICS, &activate);
1196
+ }
1197
+ #endif
898
1198
  cp.doCtls(instance);
1199
+ if (config.useCollections()) {
1200
+ int use = 1;
1201
+ lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_USE_COLLECTIONS, &use);
1202
+ }
899
1203
 
900
- new InstanceCookie(instance);
1204
+ InstanceCookie *cookie = new InstanceCookie(instance);
901
1205
 
902
1206
  lcb_connect(instance);
903
1207
  lcb_wait(instance);
@@ -910,6 +1214,7 @@ int main(int argc, char **argv)
910
1214
  }
911
1215
 
912
1216
  ThreadContext *ctx = new ThreadContext(instance, ii);
1217
+ cookie->setContext(ctx);
913
1218
  contexts.push_back(ctx);
914
1219
  start_worker(ctx);
915
1220
  }
@@ -918,5 +1223,8 @@ int main(int argc, char **argv)
918
1223
  it != contexts.end(); ++it) {
919
1224
  join_worker(*it);
920
1225
  }
1226
+ if (config.numTimings() > 0) {
1227
+ dump_metrics();
1228
+ }
921
1229
  return exit_code;
922
1230
  }