1 /*-
2  * Copyright (c) 2011, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
5  */
6 
7 /*
8  * Basic smoke test for XA transactions.  The client randomly sends requests
9  * to each of the servers to insert data into table 1, and inserts the
10  * same data into table 2 using regular transactions.
11  */
12 
13 #include <sys/types.h>
14 #include <sys/time.h>
15 
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 #include <tx.h>
23 #include <atmi.h>
24 #include <fml32.h>
25 #include <fml1632.h>
26 
27 #include <db.h>
28 
29 #include "datafml.h"
30 #include "hdbrec.h"
31 #include "htimestampxa.h"
32 #include "../utilities/bdb_xa_util.h"
33 
34 #define HOME	"../data2"
35 #define	TABLE1	"../data/table1.db"
36 #define	TABLE2	"../data2/table2.db"
37 
38 char *progname;					/* Client run-time name. */
39 
40 int   usage(void);
41 
42 int
main(int argc,char * argv[])43 main(int argc, char* argv[])
44 {
45 	DB *dbp2;
46 	DBT key, data;
47 	FBFR *buf, *replyBuf;
48 	HDbRec rec;
49 	TPINIT *initBuf;
50         DB_ENV *dbenv2, *dbenv1;
51 	long len, replyLen, seqNo;
52 	int ch, cnt, cnt_abort, cnt_commit, cnt_server1, i, ret;
53 	char *target;
54 	char *home = HOME;
55 	u_int32_t flags = DB_INIT_MPOOL | DB_INIT_LOG | DB_INIT_TXN |
56 	  DB_INIT_LOCK | DB_CREATE | DB_RECOVER | DB_REGISTER;
57 	u_int32_t dbflags = DB_CREATE;
58 
59 	progname = argv[0];
60 
61 	dbenv2 = dbenv1  = NULL;
62 	dbp2 = NULL;
63 	buf = replyBuf = NULL;
64 	initBuf = NULL;
65 	cnt = 1000;
66 	cnt_abort = cnt_commit = cnt_server1 = 0;
67 
68 	while ((ch = getopt(argc, argv, "n:v")) != EOF)
69 		switch (ch) {
70 		case 'n':
71 			cnt = atoi(optarg);
72 			break;
73 		case 'v':
74 			verbose = 1;
75 			break;
76 		case '?':
77 		default:
78 			return (usage());
79 		}
80 	argc -= optind;
81 	argv += optind;
82 
83 	if (verbose)
84 		printf("%s: called\n", progname);
85 
86 	/* Seed random number generator. */
87 	srand((u_int)(time(NULL) | getpid()));
88 
89 	if (tpinit((TPINIT *)NULL) == -1)
90 		goto tuxedo_err;
91 	if (verbose)
92 		printf("%s: tpinit() OK\n", progname);
93 
94 	/* Allocate init buffer */
95 	if ((initBuf = (TPINIT *)tpalloc("TPINIT", NULL, TPINITNEED(0))) == 0)
96 		goto tuxedo_err;
97 	if (verbose)
98 		printf("%s: tpalloc(\"TPINIT\") OK\n", progname);
99 
100 	/* Create the DB environment. */
101 	if ((ret = db_env_create(&dbenv2, 0)) != 0 ||
102 	    (ret = dbenv2->open(dbenv2, home, flags, 0)) != 0) {
103 		fprintf(stderr,
104 		    "%s: %s: %s\n", progname, home, db_strerror(ret));
105 		goto err;
106 	}
107 	dbenv2->set_errfile(dbenv2, stderr);
108 	if (verbose)
109 		printf("%s: opened %s OK\n", progname, home);
110 
111 	/*
112 	 * Open table #2 -- Data is inserted into table 1 using XA
113 	 * transactions, and inserted into table 2 using regular transactions.
114 	 */
115 	if ((ret = db_create(&dbp2, dbenv2, 0)) != 0 ||
116 	    (ret = dbp2->open(dbp2,
117 	    NULL, TABLE2, NULL, DB_BTREE, dbflags, 0660)) != 0) {
118 		fprintf(stderr,
119 		    "%s: %s %s\n", progname, TABLE2, db_strerror(ret));
120 		goto err;
121 	}
122 	if (verbose)
123 		printf("%s: opened %s OK\n", progname, TABLE2);
124 
125 	/* Allocate send buffer. */
126 	len = Fneeded(1, 3 * sizeof(long));
127 	if ((buf = (FBFR*)tpalloc("FML32", NULL, len)) == 0)
128 		goto tuxedo_err;
129 	if (verbose)
130 		printf("%s: tpalloc(\"FML32\"), send buffer OK\n", progname);
131 
132 	/* Allocate reply buffer. */
133 	replyLen = 1024;
134 	if ((replyBuf = (FBFR*)tpalloc("FML32", NULL, replyLen)) == NULL)
135 		goto tuxedo_err;
136 	if (verbose)
137 		printf("%s: tpalloc(\"FML32\"), reply buffer OK\n", progname);
138 
139 	memset(&key, 0, sizeof(key));
140 	memset(&data, 0, sizeof(data));
141 	for (rec.SeqNo = 1, i = 0; i < cnt; ++i, ++rec.SeqNo) {
142 		GetTime(&rec.Ts);
143 
144 		if (Fchg(buf, SEQ_NO, 0, (char *)&rec.SeqNo, 0) == -1)
145 			goto tuxedo_fml_err;
146 		if (verbose)
147 			printf("%s: Fchg(), sequence number OK\n", progname);
148 		if (Fchg(buf, TS_SEC, 0, (char *)&rec.Ts.Sec, 0) == -1)
149 			goto tuxedo_fml_err;
150 		if (verbose)
151 			printf("%s: Fchg(), seconds OK\n", progname);
152 		if (Fchg(buf, TS_USEC, 0, (char *)&rec.Ts.Usec, 0) == -1)
153 			goto tuxedo_fml_err;
154 		if (verbose)
155 			printf("%s: Fchg(), microseconds OK\n", progname);
156 
157 		if (tpbegin(60L, 0L) == -1)
158 			goto tuxedo_err;
159 		if (verbose)
160 			printf("%s: tpbegin() OK\n", progname);
161 
162 		/* Randomly send half of our requests to each server. */
163 		if (rand() % 2 > 0) {
164 			++cnt_server1;
165 			target = "TestTxn1";
166 		} else
167 			target = "TestTxn2";
168 		if (tpcall(target, (char *)buf,
169 		    0L, (char **)&replyBuf, &replyLen, TPSIGRSTRT) == -1)
170 			goto tuxedo_err;
171 		/* Commit for a return value of 0, otherwise abort. */
172 		if (tpurcode == 0) {
173 			++cnt_commit;
174 			if (verbose)
175 				printf("%s: txn success\n", progname);
176 
177 			if (tpcommit(0L) == -1)
178 				goto abort;
179 			if (verbose)
180 				printf("%s: tpcommit() OK\n", progname);
181 
182 			/*
183 			 * Store a copy of the key/data pair into table #2
184 			 * on success, we'll compare table #1 and table #2
185 			 * after the run finishes.
186 			 */
187 			seqNo = rec.SeqNo;
188 			key.data = &seqNo;
189 			key.size = sizeof(seqNo);
190 			data.data = &seqNo;
191 			data.size = sizeof(seqNo);
192 			if ((ret =
193 			    dbp2->put(dbp2, NULL, &key, &data, 0)) != 0) {
194 				fprintf(stderr, "%s: DB->put: %s %s\n",
195 				    progname, TABLE2, db_strerror(ret));
196 				goto err;
197 			}
198 		} else {
199  abort:			++cnt_abort;
200 			if (verbose)
201 				printf("%s: txn failure\n", progname);
202 
203 			if (tpabort(0L) == -1)
204 				goto tuxedo_err;
205 			if (verbose)
206 				printf("%s: tpabort() OK\n", progname);
207 		}
208 	}
209 
210 	printf("%s: %d requests: %d committed, %d aborted\n",
211 	    progname, cnt, cnt_commit, cnt_abort);
212 	printf("%s: %d sent to server #1, %d sent to server #2\n",
213 	    progname, cnt_server1, cnt - cnt_server1);
214 
215 	/* Check that database 1 and database 2 are identical. */
216 	if (dbp2 != NULL)
217 		(void)dbp2->close(dbp2, 0);
218 	dbp2 = NULL;
219 	if ((ret = db_env_create(&dbenv1, 0)) != 0 ||
220 	    (ret = dbenv1->open(dbenv1, "../data", flags, 0)) != 0)
221 		goto err;
222 	ret = check_data(dbenv1, TABLE1, dbenv2, TABLE2, progname);
223 
224 	if (0) {
225 tuxedo_err:	fprintf(stderr, "%s: TUXEDO ERROR: %s (code %d)\n",
226 		    progname, tpstrerror(tperrno), tperrno);
227 		goto err;
228 	}
229 	if (0) {
230 tuxedo_fml_err:	fprintf(stderr, "%s: FML ERROR: %s (code %d)\n",
231 		    progname, Fstrerror(Ferror), Ferror);
232 	}
233 	if (0) {
234 err:		ret = EXIT_FAILURE;
235 	}
236 
237 	if (replyBuf != NULL)
238 		tpfree((char *)replyBuf);
239 	if (buf != NULL)
240 		tpfree((char *)buf);
241 	if (initBuf != NULL)
242 		tpfree((char *)initBuf);
243 	if (dbp2 != NULL)
244 		(void)dbp2->close(dbp2, 0);
245 	if (dbenv1 != NULL)
246 		(void)dbenv1->close(dbenv1, 0);
247 	if (dbenv2 != NULL)
248 		(void)dbenv2->close(dbenv2, 0);
249 
250 	tpterm();
251 	if (verbose)
252 		printf("%s: tpterm() OK\n", progname);
253 
254 	return (ret);
255 }
256 
257 int
usage()258 usage()
259 {
260 	fprintf(stderr, "usage: %s [-v] [-n txn]\n", progname);
261 	return (EXIT_FAILURE);
262 }
263