b92e7fcda3273a1287a997466ca622bb41de6f65
[metze/samba/wip.git] / source4 / cluster / ctdb / tests / ctdb_fetch1.c
1 /* 
2    simple ctdb fetch test
3
4    Copyright (C) Andrew Tridgell  2006
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2 of the License, or (at your option) any later version.
10
11    This library 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 GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with this library; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "includes.h"
22 #include "lib/events/events.h"
23 #include "system/filesys.h"
24 #include "popt.h"
25 #include "ctdb.h"
26 #include "ctdb_private.h"
27 #include "cmdline.h"
28 #include <sys/time.h>
29
30 #define PARENT_SRVID    0
31 #define CHILD1_SRVID    1
32 #define CHILD2_SRVID    2
33
34 int num_msg=0;
35
36 static struct timeval tp1,tp2;
37
38 static void start_timer(void)
39 {
40         gettimeofday(&tp1,NULL);
41 }
42
43 static double end_timer(void)
44 {
45         gettimeofday(&tp2,NULL);
46         return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - 
47                 (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
48 }
49
50 static void message_handler(struct ctdb_context *ctdb, uint32_t srvid, 
51                             TDB_DATA data, void *private_data)
52 {
53         num_msg++;
54 }
55 static void child_handler(struct ctdb_context *ctdb, uint32_t srvid, 
56                             TDB_DATA data, void *private_data)
57 {
58         num_msg++;
59 }
60
61 void test1(struct ctdb_db_context *ctdb_db)
62 {
63         TDB_DATA key, data, data2, store_data;
64         int ret;
65         struct ctdb_record_handle *h;
66  
67         /* 
68            test 1 : write data and read it back.   should all be the same
69          */
70         printf("Test1: write and verify we can read it back: ");
71         key.dptr  = discard_const("Record");
72         key.dsize = strlen((const char *)key.dptr)+1;
73         h = ctdb_fetch_lock(ctdb_db, ctdb_db, key, &data);
74         if (h == NULL) {
75                 printf("test1: ctdb_fetch_lock() failed\n");
76                 exit(1);
77         }
78
79         store_data.dptr  = discard_const("data to store");
80         store_data.dsize = strlen((const char *)store_data.dptr)+1;
81         ret = ctdb_record_store(h, store_data);
82         talloc_free(h);
83         if (ret!=0) {
84                 printf("test1: ctdb_record_store() failed\n");
85                 exit(1);
86         }
87
88         h = ctdb_fetch_lock(ctdb_db, ctdb_db, key, &data2);
89         if (h == NULL) {
90                 printf("test1: ctdb_fetch_lock() failed\n");
91                 exit(1);
92         }
93
94         /* hopefully   data2 will now contain the record written above */
95         if (!strcmp("data to store", (const char *)data2.dptr)) {
96                 printf("SUCCESS\n");
97         } else {
98                 printf("FAILURE\n");
99                 exit(10);
100         }
101         
102         /* just write it back to unlock it */
103         ret = ctdb_record_store(h, store_data);
104         talloc_free(h);
105         if (ret!=0) {
106                 printf("test1: ctdb_record_store() failed\n");
107                 exit(1);
108         }
109 }
110
111 void child(int srvid, struct event_context *ev, struct ctdb_context *ctdb, struct ctdb_db_context *ctdb_db)
112 {
113         TDB_DATA data;
114         TDB_DATA key, data2;
115         struct ctdb_record_handle *h;
116
117         data.dptr=discard_const("dummy message");
118         data.dsize=strlen((const char *)data.dptr)+1;
119
120         ctdb_set_message_handler(ctdb, srvid, child_handler, NULL);
121
122         ctdb_send_message(ctdb, ctdb_get_vnn(ctdb), PARENT_SRVID, data);
123         while (num_msg==0) {
124                 event_loop_once(ev);
125         }
126
127
128         /* fetch and lock the record */
129         key.dptr  = discard_const("Record");
130         key.dsize = strlen((const char *)key.dptr)+1;
131         printf("client:%d fetching the record\n",srvid);
132         h = ctdb_fetch_lock(ctdb_db, ctdb_db, key, &data2);
133         printf("client:%d the record is fetched and locked\n",srvid);
134         if (h == NULL) {
135                 printf("client: ctdb_fetch_lock() failed\n");
136                 exit(1);
137         }
138         ctdb_send_message(ctdb, ctdb_get_vnn(ctdb), PARENT_SRVID, data);
139
140         /* wait until parent tells us to release the lock */
141         while (num_msg==1) {
142                 event_loop_once(ev);
143         }
144
145         printf("child %d terminating\n",srvid);
146         exit(10);
147            
148 }
149
150 /*
151   main program
152 */
153 int main(int argc, const char *argv[])
154 {
155         struct ctdb_context *ctdb;
156         struct ctdb_db_context *ctdb_db;
157         TDB_DATA data;
158
159         struct poptOption popt_options[] = {
160                 POPT_AUTOHELP
161                 POPT_CTDB_CMDLINE
162                 POPT_TABLEEND
163         };
164         int opt;
165         const char **extra_argv;
166         int extra_argc = 0;
167         int ret;
168         poptContext pc;
169         struct event_context *ev;
170
171         pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
172
173         while ((opt = poptGetNextOpt(pc)) != -1) {
174                 switch (opt) {
175                 default:
176                         fprintf(stderr, "Invalid option %s: %s\n", 
177                                 poptBadOption(pc, 0), poptStrerror(opt));
178                         exit(1);
179                 }
180         }
181
182         /* setup the remaining options for the main program to use */
183         extra_argv = poptGetArgs(pc);
184         if (extra_argv) {
185                 extra_argv++;
186                 while (extra_argv[extra_argc]) extra_argc++;
187         }
188
189         ev = event_context_init(NULL);
190
191         /* initialise ctdb */
192         ctdb = ctdb_cmdline_init(ev);
193         if (ctdb == NULL) {
194                 printf("Failed to init ctdb\n");
195                 exit(1);
196         }
197
198         /* attach to a specific database */
199         ctdb_db = ctdb_attach(ctdb, "test.tdb", TDB_DEFAULT, O_RDWR|O_CREAT|O_TRUNC, 0666);
200         if (!ctdb_db) {
201                 printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
202                 exit(1);
203         }
204
205         /* start the protocol running */
206         ret = ctdb_start(ctdb);
207
208 #if 0
209         /* wait until all nodes are connected (should not be needed
210            outside of test code) */
211         ctdb_connect_wait(ctdb);
212 #endif
213
214         /*
215            start two child processes
216          */
217         if(fork()){
218                 /* 
219                    set up a message handler so our child processes can talk to us
220                  */
221                 ctdb_set_message_handler(ctdb, PARENT_SRVID, message_handler, NULL);
222         } else {
223                 sleep(3);
224                 if(!fork()){
225                         child(CHILD1_SRVID, ev, ctdb, ctdb_db);
226                 } else {
227                         child(CHILD2_SRVID, ev, ctdb, ctdb_db);
228                 }
229         }
230
231         /* 
232            test 1 : write data and read it back.
233          */
234         test1(ctdb_db); 
235
236         /* 
237            wait until both children have sent us a message they have started
238          */
239         printf("Wait for both child processes to start: ");
240         while (num_msg!=2) {
241                 event_loop_once(ev);
242         }
243         printf("STARTED\n");
244
245
246         /*
247            send message to child 1 to make it to fetch and lock the record 
248          */
249         data.dptr=discard_const("dummy message");
250         data.dsize=strlen((const char *)data.dptr)+1;
251         ctdb_send_message(ctdb, ctdb_get_vnn(ctdb), CHILD1_SRVID, data);
252
253         /* wait for child 1 to complete fetching and locking the record */
254         while (num_msg!=3) {
255                 event_loop_once(ev);
256         }
257
258         /* now tell child 2 to fetch and lock the same record */
259         ctdb_send_message(ctdb, ctdb_get_vnn(ctdb), CHILD2_SRVID, data);
260
261         /* wait a while for child 2 to complete fetching and locking the 
262            record, this should fail since the record is already locked
263            by the first child */
264         start_timer();
265         while ( (end_timer() < 1.0) && (num_msg!=4) ) {
266                 event_loop_once(ev);
267         }
268         if (num_msg!=4) {
269                 printf("Child 2 did not get the lock since it is held by client 1:SUCCESS\n");
270         } else {
271                 printf("Child 2 did get the lock:FAILURE\n");
272                 exit(10);
273         }
274
275         /* send message to child 1 to terminate, which should let child 2
276            get the lock.
277          */
278         ctdb_send_message(ctdb, ctdb_get_vnn(ctdb), CHILD1_SRVID, data);
279
280
281         /* wait for a final message from child 2 it has received the lock
282            which indicates success */
283         while (num_msg!=4) {
284                 event_loop_once(ev);
285         }
286         printf("child 2 aquired the lock after child 1 terminated:SUCCESS\n");
287
288         /* send a message to child 2 to tell it to terminate too */
289         ctdb_send_message(ctdb, ctdb_get_vnn(ctdb), CHILD2_SRVID, data);
290
291
292         printf("Test was SUCCESSFUL\n");
293
294         /* shut it down */
295         talloc_free(ctdb);
296         return 0;
297 }