Skip to content

Commit e50311a

Browse files
committed
CCBC-1350: an option for cbc-n1ql to control prepared/adhoc switch
Change-Id: Iff3319173ad364b26c2fe7d0a00c462bbdc72f0a Reviewed-on: http://review.couchbase.org/c/libcouchbase/+/142170 Tested-by: Build Bot <build@couchbase.com> Reviewed-by: Sergey Avseyev <sergey.avseyev@gmail.com>
1 parent 1ca98f9 commit e50311a

7 files changed

Lines changed: 120 additions & 38 deletions

File tree

doc/cbc-n1qlback.markdown

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ For simple queries, only the `statement` field needs to be set:
2121
For more complex queries (for example, placeholders, custom options), you may
2222
refer to the N1QL REST API reference.
2323

24+
For example, the following line shows how to use named parameter
25+
26+
{"statement":"SELECT RAW meta().id FROM `travel-sample` WHERE type=$type LIMIT 1", "$type":"airline"}
27+
28+
There is also special query option, `"n1qlback"`, which is stripped before sending the payload.
29+
Currently it allows to tell if the particular query must be prepared before execution:
30+
31+
{"statement":"SELECT * FROM `travel-sample` WHERE type=$type LIMIT 10", "$type":"airline", "n1qlback": {"prepare": true}}
32+
2433
`n1qlback` requires that any resources (data items, indexes) are already
2534
defined.
2635

@@ -64,16 +73,19 @@ command-line
6473
The following will create a file with 3 queries and 5 threads alternating
6574
between them. It also creates indexes on the `travel-sample` bucket
6675

67-
cbc n1ql -U couchbase://192.168.72.101/a_bucket 'CREATE INDEX ix_name ON `travel-sample`(name)'
68-
cbc n1ql -U couchbase://192.168.72.101/a_bucket 'CREATE INDEX ix_country ON `travel-sample`(country)'
76+
cbc n1ql 'CREATE INDEX ix_name ON `travel-sample`(name)'
77+
cbc n1ql 'CREATE INDEX ix_country ON `travel-sample`(country)'
78+
79+
Crete text file `queries.txt` with the following content (note that fourth query has parameter)
6980

70-
cat queries.txt <<EOF
7181
{"statement":"SELECT country FROM `travel-sample` WHERE `travel-sample`.country = \"United States\""}
7282
{"statement":"SELECT name FROM `travel-sample` LIMIT 10"}
7383
{"statement":"SELECT country, COUNT(country) FROM `travel-sample` GROUP BY country"}
74-
EOF
84+
{"statement":"SELECT RAW meta().id FROM `travel-sample` WHERE type=$type LIMIT 1", "$type":"airline", "n1qlback": {"prepare": true}}
85+
86+
Run the test load
7587

76-
cbc-n1qlback -U couchbase://192.168.72.101/a_bucket -t 5 -f queries.txt
88+
cbc-n1qlback --num-threads 5 --queryfile queries.txt
7789

7890
## BUGS
7991

doc/cbc-pillowfight.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ The following options control workload generation:
125125

126126
* `d`, `--durability`=_LEVEL_:
127127
Specify durability level for mutation operations. Known values are: "none",
128-
"majority", "majority\_and\_persist\_on\_master", "persist\_to\_majority".
128+
"majority", "majority\_and\_persist\_to\_active", "persist\_to\_majority".
129129

130130
* `p`, `--persist-to`=_NUMNODES_:
131131
Wait until the item has been persisted to at least `NUMNODES` nodes' disk. If

doc/cbc.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ In addition to the options in the [OPTIONS](#OPTIONS) section, the following opt
124124

125125
* `d`, `--durability`=_LEVEL_:
126126
Specify durability level for mutation operations. Known values are: "none",
127-
"majority", "majority\_and\_persist\_on\_master", "persist\_to\_majority".
127+
"majority", "majority\_and\_persist\_to\_active", "persist\_to\_majority".
128128

129129
* `p`, `--persist-to`=_NUMNODES_:
130130
Wait until the item has been persisted to at least `NUMNODES` nodes' disk. If

doc/man/cbc-n1qlback.1

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.\" generated with Ronn/v0.7.3
22
.\" http://github.com/rtomayko/ronn/tree/0.7.3
33
.
4-
.TH "CBC\-N1QLBACK" "1" "April 2019" "" ""
4+
.TH "CBC\-N1QLBACK" "1" "December 2020" "" ""
55
.
66
.SH "NAME"
77
\fBcbc\-n1qlback\fR \- Stress Test for Couchbase Query (N1QL)
@@ -30,6 +30,32 @@
3030
For more complex queries (for example, placeholders, custom options), you may refer to the N1QL REST API reference\.
3131
.
3232
.P
33+
For example, the following line shows how to use named parameter
34+
.
35+
.IP "" 4
36+
.
37+
.nf
38+
39+
{"statement":"SELECT RAW meta()\.id FROM `travel\-sample` WHERE type=$type LIMIT 1", "$type":"airline"}
40+
.
41+
.fi
42+
.
43+
.IP "" 0
44+
.
45+
.P
46+
There is also special query option, \fB"n1qlback"\fR, which is stripped before sending the payload\. Currently it allows to tell if the particular query must be prepared before execution:
47+
.
48+
.IP "" 4
49+
.
50+
.nf
51+
52+
{"statement":"SELECT * FROM `travel\-sample` WHERE type=$type LIMIT 10", "$type":"airline", "n1qlback": {"prepare": true}}
53+
.
54+
.fi
55+
.
56+
.IP "" 0
57+
.
58+
.P
3359
\fBn1qlback\fR requires that any resources (data items, indexes) are already defined\.
3460
.
3561
.SH "OPTIONS"
@@ -176,16 +202,37 @@ The following will create a file with 3 queries and 5 threads alternating betwee
176202
.
177203
.nf
178204

179-
cbc n1ql \-U couchbase://192\.168\.72\.101/a_bucket \'CREATE INDEX ix_name ON `travel\-sample`(name)\'
180-
cbc n1ql \-U couchbase://192\.168\.72\.101/a_bucket \'CREATE INDEX ix_country ON `travel\-sample`(country)\'
205+
cbc n1ql \'CREATE INDEX ix_name ON `travel\-sample`(name)\'
206+
cbc n1ql \'CREATE INDEX ix_country ON `travel\-sample`(country)\'
207+
.
208+
.fi
209+
.
210+
.IP "" 0
211+
.
212+
.P
213+
Crete text file \fBqueries\.txt\fR with the following content (note that fourth query has parameter)
214+
.
215+
.IP "" 4
216+
.
217+
.nf
181218

182-
cat queries\.txt <<EOF
183219
{"statement":"SELECT country FROM `travel\-sample` WHERE `travel\-sample`\.country = \e"United States\e""}
184220
{"statement":"SELECT name FROM `travel\-sample` LIMIT 10"}
185221
{"statement":"SELECT country, COUNT(country) FROM `travel\-sample` GROUP BY country"}
186-
EOF
222+
{"statement":"SELECT RAW meta()\.id FROM `travel\-sample` WHERE type=$type LIMIT 1", "$type":"airline", "n1qlback": {"prepare": true}}
223+
.
224+
.fi
225+
.
226+
.IP "" 0
227+
.
228+
.P
229+
Run the test load
230+
.
231+
.IP "" 4
232+
.
233+
.nf
187234

188-
cbc\-n1qlback \-U couchbase://192\.168\.72\.101/a_bucket \-t 5 \-f queries\.txt
235+
cbc\-n1qlback \-\-num\-threads 5 \-\-queryfile queries\.txt
189236
.
190237
.fi
191238
.

tools/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ ADD_EXECUTABLE(cbc-pillowfight cbc-pillowfight.cc
1313
TARGET_LINK_LIBRARIES(cbc-pillowfight couchbase)
1414

1515
ADD_EXECUTABLE(cbc-n1qlback cbc-n1qlback.cc
16-
$<TARGET_OBJECTS:lcbtools> $<TARGET_OBJECTS:cliopts>)
16+
$<TARGET_OBJECTS:lcbtools> $<TARGET_OBJECTS:cliopts> $<TARGET_OBJECTS:lcb_jsoncpp>)
1717
TARGET_LINK_LIBRARIES(cbc-n1qlback couchbase)
1818

1919
INSTALL(TARGETS cbc cbc-pillowfight cbc-n1qlback

tools/cbc-n1qlback.cc

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#endif
3636
#include "common/options.h"
3737
#include "common/histogram.h"
38+
#include "contrib/lcb-jsoncpp/lcb-jsoncpp.h"
3839

3940
using namespace cbc;
4041
using namespace cliopts;
@@ -56,6 +57,11 @@ static void do_or_die(lcb_STATUS rc)
5657
}
5758
}
5859

60+
struct Query {
61+
std::string payload{};
62+
bool prepare{false};
63+
};
64+
5965
class Metrics
6066
{
6167
public:
@@ -237,7 +243,22 @@ class Configuration
237243
while (ifs.good()) {
238244
std::getline(ifs, curline);
239245
if (!curline.empty()) {
240-
m_queries.push_back(curline);
246+
Json::Value json;
247+
if (!Json::Reader().parse(curline, json)) {
248+
std::cerr << "Failed to parse query \"" << curline << "\" as JSON, skipping" << std::endl;
249+
continue;
250+
}
251+
Query query{};
252+
Json::Value options = json.get("n1qlback", Json::nullValue);
253+
if (options.isObject()) {
254+
Json::Value should_prepare = options.get("prepare", Json::nullValue);
255+
if (should_prepare.isBool()) {
256+
query.prepare = should_prepare.asBool();
257+
}
258+
}
259+
json.removeMember("n1qlback");
260+
query.payload = Json::FastWriter().write(json);
261+
m_queries.emplace_back(query);
241262
}
242263
}
243264
std::cerr << "Loaded " << m_queries.size() << " queries "
@@ -255,14 +276,15 @@ class Configuration
255276
errstr += strerror(ec_save);
256277
throw std::runtime_error(errstr);
257278
}
279+
std::cerr << "Errors will be logged in \"" << o_errlog.const_result() << "\"" << std::endl;
258280
}
259281
}
260282

261-
void set_cropts(lcb_CREATEOPTS *opts)
283+
void set_cropts(lcb_CREATEOPTS *&opts)
262284
{
263285
m_params.fillCropts(opts);
264286
}
265-
const vector<string> &queries() const
287+
const vector<Query> &queries() const
266288
{
267289
return m_queries;
268290
}
@@ -281,7 +303,7 @@ class Configuration
281303
}
282304

283305
private:
284-
vector<string> m_queries;
306+
vector<Query> m_queries;
285307
StringOption o_file;
286308
UIntOption o_threads;
287309
ConnParams m_params;
@@ -312,9 +334,8 @@ class ThreadContext
312334
void run()
313335
{
314336
while (!m_cancelled) {
315-
vector<string>::const_iterator ii = m_queries.begin();
316-
for (; ii != m_queries.end(); ++ii) {
317-
run_one_query(*ii);
337+
for (const auto &query : m_queries) {
338+
run_one_query(query);
318339
}
319340
}
320341
}
@@ -388,8 +409,7 @@ class ThreadContext
388409
}
389410
}
390411

391-
ThreadContext(Metrics &metrics, lcb_INSTANCE *instance, const vector<string> &initial_queries,
392-
std::ofstream *errlog)
412+
ThreadContext(Metrics &metrics, lcb_INSTANCE *instance, const vector<Query> &initial_queries, std::ofstream *errlog)
393413
: m_instance(instance), last_nrow(0), m_cmd(nullptr), m_metrics(metrics), m_cancelled(false), m_thr(nullptr),
394414
m_errlog(errlog)
395415
{
@@ -424,19 +444,20 @@ class ThreadContext
424444
}
425445
}
426446

427-
void run_one_query(const string &txt)
447+
void run_one_query(const Query &query)
428448
{
429449
// Reset counters
430450
last_nrow = 0;
431451

432-
lcb_cmdquery_payload(m_cmd, txt.c_str(), txt.size());
452+
lcb_cmdquery_payload(m_cmd, query.payload.c_str(), query.payload.size());
453+
lcb_cmdquery_adhoc(m_cmd, !query.prepare);
433454

434455
// Set up our context
435456
QueryContext qctx(this);
436457

437458
lcb_STATUS rc = lcb_query(m_instance, &qctx, m_cmd);
438459
if (rc != LCB_SUCCESS) {
439-
log_error(rc, txt.c_str(), txt.size());
460+
log_error(rc, query.payload.c_str(), query.payload.size());
440461
} else {
441462
lcb_wait(m_instance, LCB_WAIT_DEFAULT);
442463
m_metrics.lock();
@@ -447,7 +468,7 @@ class ThreadContext
447468
}
448469

449470
lcb_INSTANCE *m_instance;
450-
vector<string> m_queries;
471+
vector<Query> m_queries;
451472
size_t last_nrow;
452473
lcb_CMDQUERY *m_cmd;
453474
Metrics &m_metrics;

tools/common/options.cc

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,9 @@ void ConnParams::fillCropts(lcb_CREATEOPTS *&cropts)
303303
string host = o_host.result();
304304
string bucket = o_bucket.result();
305305

306-
for (size_t ii = 0; ii < host.size(); ++ii) {
307-
if (host[ii] == ';') {
308-
host[ii] = ',';
306+
for (char &ii : host) {
307+
if (ii == ';') {
308+
ii = ',';
309309
}
310310
}
311311

@@ -374,9 +374,9 @@ void ConnParams::fillCropts(lcb_CREATEOPTS *&cropts)
374374
connstr += '&';
375375
}
376376

377-
const std::vector< std::string > &extras = o_cparams.const_result();
378-
for (size_t ii = 0; ii < extras.size(); ii++) {
379-
connstr += extras[ii];
377+
const std::vector<std::string> &extras = o_cparams.const_result();
378+
for (const auto &extra : extras) {
379+
connstr += extra;
380380
connstr += '&';
381381
}
382382

@@ -396,7 +396,8 @@ void ConnParams::fillCropts(lcb_CREATEOPTS *&cropts)
396396
lcb_createopts_credentials(cropts, NULL, 0, passwd.c_str(), passwd.size());
397397
}
398398

399-
template < typename T > void doPctl(lcb_INSTANCE *instance, int cmd, T arg)
399+
template <typename T>
400+
void doPctl(lcb_INSTANCE *instance, int cmd, T arg)
400401
{
401402
lcb_STATUS err;
402403
err = lcb_cntl(instance, LCB_CNTL_SET, cmd, (void *)arg);
@@ -405,9 +406,10 @@ template < typename T > void doPctl(lcb_INSTANCE *instance, int cmd, T arg)
405406
}
406407
}
407408

408-
template < typename T > void doSctl(lcb_INSTANCE *instance, int cmd, T arg)
409+
template <typename T>
410+
void doSctl(lcb_INSTANCE *instance, int cmd, T arg)
409411
{
410-
doPctl< T * >(instance, cmd, &arg);
412+
doPctl<T *>(instance, cmd, &arg);
411413
}
412414

413415
void doStringCtl(lcb_INSTANCE *instance, const char *s, const char *val)
@@ -423,11 +425,11 @@ lcb_STATUS ConnParams::doCtls(lcb_INSTANCE *instance)
423425
{
424426
try {
425427
if (o_saslmech.passed()) {
426-
doPctl< const char * >(instance, LCB_CNTL_FORCE_SASL_MECH, o_saslmech.result().c_str());
428+
doPctl<const char *>(instance, LCB_CNTL_FORCE_SASL_MECH, o_saslmech.result().c_str());
427429
}
428430

429431
// Set the detailed error codes option
430-
doSctl< int >(instance, LCB_CNTL_DETAILED_ERRCODES, 1);
432+
doSctl<int>(instance, LCB_CNTL_DETAILED_ERRCODES, 1);
431433

432434
if (!o_connstr.passed() || o_connstr.result().find("compression=") == std::string::npos) {
433435
int opts = LCB_COMPRESS_IN;

0 commit comments

Comments
 (0)