2 simple ctdb benchmark for persistent databases
4 Copyright (C) Amitay Isaacs 2016
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "system/network.h"
23 #include "lib/util/debug.h"
24 #include "lib/util/tevent_unix.h"
26 #include "client/client.h"
27 #include "tests/src/test_options.h"
28 #include "tests/src/cluster_wait.h"
30 struct transaction_loop_state
{
31 struct tevent_context
*ev
;
32 struct ctdb_client_context
*client
;
33 struct ctdb_db_context
*ctdb_db
;
39 struct ctdb_transaction_handle
*h
;
40 uint32_t *old_counter
, *counter
;
41 struct tevent_req
*subreq
;
45 static void transaction_loop_start(struct tevent_req
*subreq
);
46 static void transaction_loop_started(struct tevent_req
*subreq
);
47 static void transaction_loop_committed(struct tevent_req
*subreq
);
48 static void transaction_loop_each_second(struct tevent_req
*subreq
);
49 static bool transaction_loop_check_counters(struct tevent_req
*req
);
50 static void transaction_loop_finish(struct tevent_req
*subreq
);
52 static struct tevent_req
*transaction_loop_send(
54 struct tevent_context
*ev
,
55 struct ctdb_client_context
*client
,
56 struct ctdb_db_context
*ctdb_db
,
57 int num_nodes
, int timelimit
, int interactive
,
60 struct tevent_req
*req
, *subreq
;
61 struct transaction_loop_state
*state
;
63 req
= tevent_req_create(mem_ctx
, &state
,
64 struct transaction_loop_state
);
70 state
->client
= client
;
71 state
->ctdb_db
= ctdb_db
;
72 state
->num_nodes
= num_nodes
;
73 state
->timelimit
= timelimit
;
74 state
->interactive
= interactive
;
75 state
->key
.dptr
= discard_const(keystr
);
76 state
->key
.dsize
= strlen(keystr
);
77 state
->pnn
= ctdb_client_pnn(client
);
78 state
->old_counter
= talloc_zero_array(state
, uint32_t, num_nodes
);
79 if (tevent_req_nomem(state
->old_counter
, req
)) {
80 return tevent_req_post(req
, ev
);
82 state
->counter
= talloc_zero_array(state
, uint32_t, num_nodes
);
83 if (tevent_req_nomem(state
->counter
, req
)) {
84 return tevent_req_post(req
, ev
);
87 subreq
= cluster_wait_send(state
, state
->ev
, state
->client
,
89 if (tevent_req_nomem(subreq
, req
)) {
90 return tevent_req_post(req
, ev
);
92 tevent_req_set_callback(subreq
, transaction_loop_start
, req
);
97 static void transaction_loop_start(struct tevent_req
*subreq
)
99 struct tevent_req
*req
= tevent_req_callback_data(
100 subreq
, struct tevent_req
);
101 struct transaction_loop_state
*state
= tevent_req_data(
102 req
, struct transaction_loop_state
);
106 status
= cluster_wait_recv(subreq
, &ret
);
109 tevent_req_error(req
, ret
);
113 subreq
= ctdb_transaction_start_send(state
, state
->ev
, state
->client
,
114 tevent_timeval_current_ofs(
115 state
->timelimit
, 0),
116 state
->ctdb_db
, false);
117 if (tevent_req_nomem(subreq
, req
)) {
120 tevent_req_set_callback(subreq
, transaction_loop_started
, req
);
121 state
->subreq
= subreq
;
123 if (ctdb_client_pnn(state
->client
) == 0) {
124 subreq
= tevent_wakeup_send(state
, state
->ev
,
125 tevent_timeval_current_ofs(1, 0));
126 if (tevent_req_nomem(subreq
, req
)) {
129 tevent_req_set_callback(subreq
, transaction_loop_each_second
,
133 subreq
= tevent_wakeup_send(state
, state
->ev
,
134 tevent_timeval_current_ofs(
135 state
->timelimit
, 0));
136 if (tevent_req_nomem(subreq
, req
)) {
139 tevent_req_set_callback(subreq
, transaction_loop_finish
, req
);
142 static void transaction_loop_started(struct tevent_req
*subreq
)
144 struct tevent_req
*req
= tevent_req_callback_data(
145 subreq
, struct tevent_req
);
146 struct transaction_loop_state
*state
= tevent_req_data(
147 req
, struct transaction_loop_state
);
152 state
->h
= ctdb_transaction_start_recv(subreq
, &ret
);
154 state
->subreq
= NULL
;
155 if (state
->h
== NULL
) {
156 fprintf(stderr
, "transaction start failed\n");
157 tevent_req_error(req
, ret
);
161 ret
= ctdb_transaction_fetch_record(state
->h
, state
->key
,
164 fprintf(stderr
, "transaction fetch record failed\n");
165 tevent_req_error(req
, ret
);
169 if (data
.dsize
< state
->num_nodes
* sizeof(uint32_t)) {
170 TALLOC_FREE(data
.dptr
);
172 data
.dsize
= state
->num_nodes
* sizeof(uint32_t);
173 data
.dptr
= (uint8_t *)talloc_zero_array(state
, uint32_t,
175 if (tevent_req_nomem(data
.dptr
, req
)) {
180 counter
= (uint32_t *)data
.dptr
;
181 counter
[state
->pnn
] += 1;
182 memcpy(state
->counter
, counter
, state
->num_nodes
* sizeof(uint32_t));
184 ret
= ctdb_transaction_store_record(state
->h
, state
->key
, data
);
186 fprintf(stderr
, "transaction store failed\n");
187 tevent_req_error(req
, ret
);
191 subreq
= ctdb_transaction_commit_send(state
, state
->ev
,
192 tevent_timeval_current_ofs(
193 state
->timelimit
, 0),
195 if (tevent_req_nomem(subreq
, req
)) {
198 tevent_req_set_callback(subreq
, transaction_loop_committed
, req
);
199 state
->subreq
= subreq
;
202 static void transaction_loop_committed(struct tevent_req
*subreq
)
204 struct tevent_req
*req
= tevent_req_callback_data(
205 subreq
, struct tevent_req
);
206 struct transaction_loop_state
*state
= tevent_req_data(
207 req
, struct transaction_loop_state
);
211 status
= ctdb_transaction_commit_recv(subreq
, &ret
);
213 state
->subreq
= NULL
;
215 fprintf(stderr
, "transaction commit failed - %s\n",
217 tevent_req_error(req
, ret
);
221 if (state
->pnn
== 0) {
222 if (! transaction_loop_check_counters(req
)) {
230 printf("Transaction[%u]: ", ctdb_client_pnn(state
->client
));
231 for (i
=0; i
<state
->num_nodes
; i
++) {
232 printf("%6u ", state
->counter
[i
]);
236 tevent_req_done(req
);
241 subreq
= ctdb_transaction_start_send(state
, state
->ev
, state
->client
,
242 tevent_timeval_current_ofs(
243 state
->timelimit
, 0),
244 state
->ctdb_db
, false);
245 if (tevent_req_nomem(subreq
, req
)) {
248 tevent_req_set_callback(subreq
, transaction_loop_started
, req
);
251 static void transaction_loop_each_second(struct tevent_req
*subreq
)
253 struct tevent_req
*req
= tevent_req_callback_data(
254 subreq
, struct tevent_req
);
255 struct transaction_loop_state
*state
= tevent_req_data(
256 req
, struct transaction_loop_state
);
260 status
= tevent_wakeup_recv(subreq
);
263 fprintf(stderr
, "tevent wakeup failed\n");
264 tevent_req_error(req
, EIO
);
268 if (state
->interactive
== 1) {
269 printf("Transaction[%u]: ", ctdb_client_pnn(state
->client
));
270 for (i
=0; i
<state
->num_nodes
; i
++) {
271 printf("%6u ", state
->counter
[i
]);
277 subreq
= tevent_wakeup_send(state
, state
->ev
,
278 tevent_timeval_current_ofs(1, 0));
279 if (tevent_req_nomem(subreq
, req
)) {
282 tevent_req_set_callback(subreq
, transaction_loop_each_second
, req
);
285 static bool transaction_loop_check_counters(struct tevent_req
*req
)
287 struct transaction_loop_state
*state
= tevent_req_data(
288 req
, struct transaction_loop_state
);
290 bool monotonous
= true;
292 for (i
=0; i
<state
->num_nodes
; i
++) {
293 if (state
->counter
[i
] < state
->old_counter
[i
]) {
295 "Counter reduced for node %d: %u -> %u\n",
296 i
, state
->old_counter
[i
], state
->counter
[i
]);
303 memcpy(state
->old_counter
, state
->counter
,
304 state
->num_nodes
* sizeof(uint32_t));
310 static void transaction_loop_finish(struct tevent_req
*subreq
)
312 struct tevent_req
*req
= tevent_req_callback_data(
313 subreq
, struct tevent_req
);
314 struct transaction_loop_state
*state
= tevent_req_data(
315 req
, struct transaction_loop_state
);
318 status
= tevent_wakeup_recv(subreq
);
324 tevent_req_error(req
, EIO
);
329 static bool transaction_loop_recv(struct tevent_req
*req
, int *perr
)
333 if (tevent_req_is_unix_error(req
, &err
)) {
342 int main(int argc
, const char *argv
[])
344 const struct test_options
*opts
;
346 struct tevent_context
*ev
;
347 struct ctdb_client_context
*client
;
348 struct ctdb_db_context
*ctdb_db
;
349 struct tevent_req
*req
;
354 setup_logging("transaction_loop", DEBUG_STDERR
);
356 status
= process_options_database(argc
, argv
, &opts
);
361 mem_ctx
= talloc_new(NULL
);
362 if (mem_ctx
== NULL
) {
363 fprintf(stderr
, "Memory allocation error\n");
367 ev
= tevent_context_init(mem_ctx
);
369 fprintf(stderr
, "Memory allocation error\n");
373 ret
= ctdb_client_init(mem_ctx
, ev
, opts
->socket
, &client
);
375 fprintf(stderr
, "Failed to initialize client, ret=%d\n", ret
);
379 if (! ctdb_recovery_wait(ev
, client
)) {
380 fprintf(stderr
, "Memory allocation error\n");
384 if (strcmp(opts
->dbtype
, "persistent") == 0) {
385 db_flags
= CTDB_DB_FLAGS_PERSISTENT
;
386 } else if (strcmp(opts
->dbtype
, "replicated") == 0) {
387 db_flags
= CTDB_DB_FLAGS_REPLICATED
;
389 fprintf(stderr
, "Database must be persistent or replicated\n");
393 ret
= ctdb_attach(ev
, client
, tevent_timeval_zero(), opts
->dbname
,
396 fprintf(stderr
, "Failed to attach to persistent DB %s\n",
401 req
= transaction_loop_send(mem_ctx
, ev
, client
, ctdb_db
,
402 opts
->num_nodes
, opts
->timelimit
,
403 opts
->interactive
, opts
->keystr
);
405 fprintf(stderr
, "Memory allocation error\n");
409 tevent_req_poll(req
, ev
);
411 status
= transaction_loop_recv(req
, &ret
);
413 fprintf(stderr
, "transaction loop test failed, ret=%d\n", ret
);
417 talloc_free(mem_ctx
);