1 /*
2    simple ctdb benchmark
3 
4    Copyright (C) Amitay Isaacs  2015
5 
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.
10 
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.
15 
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/>.
18 */
19 
20 #include "replace.h"
21 #include "system/network.h"
22 
23 #include "lib/util/debug.h"
24 #include "lib/util/tevent_unix.h"
25 
26 #include "client/client.h"
27 #include "tests/src/test_options.h"
28 #include "tests/src/cluster_wait.h"
29 
30 struct fetch_loop_state {
31 	struct tevent_context *ev;
32 	struct ctdb_client_context *client;
33 	struct ctdb_db_context *ctdb_db;
34 	int timelimit;
35 	TDB_DATA key;
36 	int locks_count;
37 };
38 
39 static void fetch_loop_next(struct tevent_req *subreq);
40 
fetch_loop_send(TALLOC_CTX * mem_ctx,struct tevent_context * ev,struct ctdb_client_context * client,struct ctdb_db_context * ctdb_db,const char * keystr,int timelimit)41 static struct tevent_req *fetch_loop_send(TALLOC_CTX *mem_ctx,
42 					  struct tevent_context *ev,
43 					  struct ctdb_client_context *client,
44 					  struct ctdb_db_context *ctdb_db,
45 					  const char *keystr,
46 					  int timelimit)
47 {
48 	struct tevent_req *req, *subreq;
49 	struct fetch_loop_state *state;
50 
51 	req = tevent_req_create(mem_ctx, &state, struct fetch_loop_state);
52 	if (req == NULL) {
53 		return NULL;
54 	}
55 
56 	state->ev = ev;
57 	state->client = client;
58 	state->ctdb_db = ctdb_db;
59 	state->timelimit = timelimit;
60 	state->key.dptr = discard_const(keystr);
61 	state->key.dsize = strlen(keystr);
62 
63 	subreq = ctdb_fetch_lock_send(state, ev, client, ctdb_db,
64 				      state->key, false);
65 	if (tevent_req_nomem(subreq, req)) {
66 		return tevent_req_post(req, ev);
67 	}
68 	tevent_req_set_callback(subreq, fetch_loop_next, req);
69 
70 	return req;
71 }
72 
fetch_loop_next(struct tevent_req * subreq)73 static void fetch_loop_next(struct tevent_req *subreq)
74 {
75 	struct tevent_req *req = tevent_req_callback_data(
76 		subreq, struct tevent_req);
77 	struct fetch_loop_state *state = tevent_req_data(
78 		req, struct fetch_loop_state);
79 	struct ctdb_record_handle *h;
80 	TDB_DATA data;
81 	int ret;
82 
83 	h = ctdb_fetch_lock_recv(subreq, NULL, state, &data, &ret);
84 	TALLOC_FREE(subreq);
85 	if (h == NULL) {
86 		tevent_req_error(req, ret);
87 		return;
88 	}
89 
90 	if (data.dsize == sizeof(uint32_t)) {
91 		state->locks_count = *(uint32_t *)data.dptr;
92 	}
93 	TALLOC_FREE(data.dptr);
94 
95 	state->locks_count += 1;
96 	data.dsize = sizeof(uint32_t);
97 	data.dptr = (uint8_t *)&state->locks_count;
98 
99 	ret = ctdb_store_record(h, data);
100 	if (ret != 0) {
101 		talloc_free(h);
102 		tevent_req_error(req, ret);
103 		return;
104 	}
105 
106 	talloc_free(h);
107 
108 	subreq = ctdb_fetch_lock_send(state, state->ev, state->client,
109 				      state->ctdb_db, state->key, false);
110 	if (tevent_req_nomem(subreq, req)) {
111 		return;
112 	}
113 	tevent_req_set_callback(subreq, fetch_loop_next, req);
114 }
115 
fetch_loop_recv(struct tevent_req * req,int * perr)116 static bool fetch_loop_recv(struct tevent_req *req, int *perr)
117 {
118 	int err;
119 
120 	if (tevent_req_is_unix_error(req, &err)) {
121 		if (perr != NULL) {
122 			*perr = err;
123 		}
124 		return false;
125 	}
126 	return true;
127 }
128 
129 static struct tevent_req *global_req;
130 
alarm_handler(int sig)131 static void alarm_handler(int sig)
132 {
133 	struct fetch_loop_state *state = tevent_req_data(
134 		global_req, struct fetch_loop_state);
135 	static int time_passed = 0;
136 
137 	time_passed += 1;
138 
139 	printf("Locks:%d\n", state->locks_count);
140 	fflush(stdout);
141 
142 	if (time_passed >= state->timelimit) {
143 		tevent_req_done(global_req);
144 	}
145 
146 	alarm(1);
147 }
148 
main(int argc,const char * argv[])149 int main(int argc, const char *argv[])
150 {
151 	const struct test_options *opts;
152 	TALLOC_CTX *mem_ctx;
153 	struct tevent_context *ev;
154 	struct ctdb_client_context *client;
155 	struct ctdb_db_context *ctdb_db;
156 	int ret;
157 	bool status;
158 
159 	setup_logging("fetch_loop_key", DEBUG_STDERR);
160 
161 	status = process_options_database(argc, argv, &opts);
162 	if (! status) {
163 		exit(1);
164 	}
165 
166 	mem_ctx = talloc_new(NULL);
167 	if (mem_ctx == NULL) {
168 		fprintf(stderr, "Memory allocation error\n");
169 		exit(1);
170 	}
171 
172 	ev = tevent_context_init(mem_ctx);
173 	if (ev == NULL) {
174 		fprintf(stderr, "Memory allocation error\n");
175 		exit(1);
176 	}
177 
178 	ret = ctdb_client_init(mem_ctx, ev, opts->socket, &client);
179 	if (ret != 0) {
180 		fprintf(stderr, "Failed to initialize client, %s\n",
181 			strerror(ret));
182 		exit(1);
183 	}
184 
185 	if (! ctdb_recovery_wait(ev, client)) {
186 		fprintf(stderr, "Memory allocation error\n");
187 		exit(1);
188 	}
189 
190 	ret = ctdb_attach(ev, client, tevent_timeval_zero(), opts->dbname, 0,
191 			  &ctdb_db);
192 	if (ret != 0) {
193 		fprintf(stderr, "Failed to attach to DB %s\n", opts->dbname);
194 		exit(1);
195 	}
196 
197 	global_req = fetch_loop_send(mem_ctx, ev, client, ctdb_db,
198 				     opts->keystr, opts->timelimit);
199 	if (global_req == NULL) {
200 		fprintf(stderr, "Memory allocation error\n");
201 		exit(1);
202 	}
203 
204 	signal(SIGALRM, alarm_handler);
205 	alarm(1);
206 
207 	tevent_req_poll(global_req, ev);
208 
209 	status = fetch_loop_recv(global_req, &ret);
210 	if (! status) {
211 		fprintf(stderr, "fetch loop test failed\n");
212 		exit(1);
213 	}
214 
215 	talloc_free(mem_ctx);
216 	return 0;
217 }
218