001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.store.OnlineBackupTest1
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyTesting.functionTests.tests.store;
023:
024: import java.sql.Connection;
025: import java.sql.Statement;
026: import java.sql.PreparedStatement;
027: import java.sql.ResultSet;
028: import java.sql.SQLException;
029: import org.apache.derby.tools.ij;
030: import org.apache.derbyTesting.functionTests.util.TestUtil;
031:
032: /*
033: * This class tests online backup when dml/ddl actions
034: * are running in parallel to the backup thread.
035: *
036: * @author <a href="mailto:suresh.thalamati@gmail.com">Suresh Thalamati</a>
037: * @version 1.0
038: */
039:
040: public class OnlineBackupTest1 {
041:
042: private static final String TEST_DATABASE_NAME = "wombat";
043: private static final String TEST_TABLE_NAME = "emp";
044: private static final String TEST_TABLE_NAME_1 = "emp_1";
045: private static final String TEST_TABLE_NAME_2 = "emp_2";
046: private static final String BACKUP_PATH = "extinout/onlinebackuptest1";
047:
048: public static void main(String[] argv) throws Throwable {
049:
050: OnlineBackupTest1 test = new OnlineBackupTest1();
051: ij.getPropertyArg(argv);
052:
053: try {
054: test.runTest();
055: } catch (SQLException sqle) {
056: dumpSQLException(sqle);
057: }
058: }
059:
060: /*
061: * Test online backup with unlogged operations. And DML/DDL's
062: * running in paralel to the backup. After the backup is complete restore
063: * the database from the backup and performs consistency checks on the
064: * database to make sure backup was good one.
065: */
066: private void runTest() throws Exception {
067: logMessage("Begin Online Backup Test1");
068: Connection conn = ij.startJBMS();
069: conn.setAutoCommit(false);
070: DatabaseActions dbActions = new DatabaseActions(conn);
071: //create the test tables.
072: dbActions.createTable(TEST_TABLE_NAME);
073: dbActions.createTable(TEST_TABLE_NAME_1);
074: dbActions.createTable(TEST_TABLE_NAME_2);
075: conn.commit();
076:
077: // start first unlogged operation
078: dbActions.startUnloggedAction(TEST_TABLE_NAME_1);
079: logMessage("First Transaction with Unlogged Operation Started");
080:
081: // start second unlogged opearation
082: Connection conn1 = ij.startJBMS();
083: conn1.setAutoCommit(false);
084: DatabaseActions dbActions1 = new DatabaseActions(conn1);
085: dbActions1.startUnloggedAction(TEST_TABLE_NAME_2);
086: logMessage("Second Transaction with Unlogged Operation Started");
087:
088: // setup threads.
089: // start a thread to perform online backup
090: OnlineBackup backup = new OnlineBackup(TEST_DATABASE_NAME,
091: BACKUP_PATH);
092: Thread backupThread = new Thread(backup, "BACKUP");
093:
094: // run some dml actions in another thread
095: Connection dmlConn = TestUtil.getConnection(TEST_DATABASE_NAME,
096: null);
097: DatabaseActions dmlActions = new DatabaseActions(
098: DatabaseActions.DMLACTIONS, dmlConn);
099: Thread dmlThread = new Thread(dmlActions, "DML_THREAD");
100:
101: // run some DDL create/drop tables in another thread
102: Connection ddlConn = TestUtil.getConnection(TEST_DATABASE_NAME,
103: null);
104:
105: DatabaseActions ddlActions = new DatabaseActions(
106: DatabaseActions.CREATEDROPS, ddlConn);
107: Thread ddlThread = new Thread(ddlActions, "DDL_THREAD");
108:
109: try {
110: // start a thread to perform online backup
111: backupThread.start();
112: // wait for the backup to start
113: backup.waitForBackupToBegin();
114: logMessage("BACKUP STARTED");
115:
116: // run some dml actions in another thread
117: dmlThread.start();
118:
119: // run some DDL create/drop tables in another thread
120: ddlThread.start();
121:
122: // sleep for few seconds just to make sure backup thread is actually
123: // gone to a wait state for unlogged actions to commit and there is
124: // some ddl and dml activity in progress.
125: java.lang.Thread.sleep(50000);
126:
127: // backup should not even start doing real work before the
128: // unlogged transaction is commited
129: if (!backup.isRunning())
130: logMessage("Backup is not waiting for unlogged actions to commit");
131:
132: // end the unlogged work transaction.
133: dbActions.endUnloggedAction(TEST_TABLE_NAME_1);
134: // end the unlogged work transaction.
135: dbActions1.endUnloggedAction(TEST_TABLE_NAME_2);
136:
137: backup.waitForBackupToEnd();
138:
139: } finally {
140: //stop all threads activities.
141: backupThread.join();
142: dmlActions.stopActivity();
143: ddlActions.stopActivity();
144: dmlThread.join();
145: ddlThread.join();
146: }
147: // close the connections.
148: conn.close();
149: conn1.close();
150: dmlConn.close();
151: ddlConn.close();
152:
153: //shutdown the test db
154: shutdown(TEST_DATABASE_NAME);
155:
156: // restore the database from the backup and run some checks
157: backup.restoreFromBackup();
158: logMessage("Restored From the Backup");
159: runConsistencyChecker(TEST_DATABASE_NAME);
160: logMessage("Consistency Check is Done");
161: //shutdown the test db
162: shutdown(TEST_DATABASE_NAME);
163: logMessage("End Online Backup Test1");
164: }
165:
166: /**
167: * Run some consistency checks.
168: * @param dbName consistency checks are performed on this database.
169: */
170: void runConsistencyChecker(String dbName) throws SQLException {
171: Connection conn = TestUtil.getConnection(dbName, null);
172: Statement stmt = conn.createStatement();
173: stmt
174: .execute("values SYSCS_UTIL.SYSCS_CHECK_TABLE('APP', 'EMP')");
175: //check the data in the EMP table.
176: DatabaseActions dbActions = new DatabaseActions(conn);
177: dbActions.select(TEST_TABLE_NAME);
178: dbActions.select(TEST_TABLE_NAME_1);
179: dbActions.select(TEST_TABLE_NAME_2);
180: conn.close();
181:
182: }
183:
184: /**
185: * Shutdown the datbase
186: * @param dbName Name of the database to shutdown.
187: */
188: void shutdown(String dbName) {
189:
190: try {
191: //shutdown
192: TestUtil.getConnection(dbName, "shutdown=true");
193: } catch (SQLException se) {
194: if (se.getSQLState() != null
195: && se.getSQLState().equals("08006"))
196: System.out.println("database shutdown properly");
197: else
198: dumpSQLException(se);
199: }
200: }
201:
202: /**
203: * Write message to the standard output.
204: */
205: void logMessage(String str) {
206: System.out.println(str);
207: }
208:
209: /**
210: * dump the SQLException to the standard output.
211: */
212: static private void dumpSQLException(SQLException sqle) {
213:
214: org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(
215: System.out, sqle);
216: sqle.printStackTrace(System.out);
217: }
218:
219: /*
220: * This class implements some DML and DDL operations to
221: * run againest the datbase, when the backup is in progress.
222: * Some of these operations can be run in seperate threads in a
223: * loop until they are stopped by some other thread.
224: */
225:
226: class DatabaseActions implements Runnable {
227:
228: public static final int DMLACTIONS = 1;
229: public static final int CREATEDROPS = 2;
230:
231: private static final int COMMIT = 1;
232: private static final int ROLLBACK = 2;
233: private static final int OPENTX = 3;
234:
235: private int action = 0;
236: private volatile boolean stopActivity = false;
237: private Connection conn;
238:
239: DatabaseActions(Connection conn) {
240: this .conn = conn;
241: };
242:
243: DatabaseActions(int action, Connection conn) {
244: this .action = action;
245: this .conn = conn;
246: }
247:
248: /**
249: * stops any actions that are looping on a differt threads.
250: */
251: public void stopActivity() {
252: stopActivity = true;
253: }
254:
255: /**
256: * implementation of run() method in the Runnable interface, which
257: * is invoked when a thread is started using this class object.
258: * <p>
259: * Performs DML ot DDL actions.
260: */
261: public void run() {
262: try {
263: conn.setAutoCommit(false);
264: switch (action) {
265: case DMLACTIONS:
266: performDmlActions();
267: break;
268: case CREATEDROPS:
269: performCreateDropTables();
270: break;
271: }
272: } catch (SQLException sqle) {
273: org.apache.derby.tools.JDBCDisplayUtil
274: .ShowSQLException(System.out, sqle);
275: sqle.printStackTrace(System.out);
276: }
277: }
278:
279: /*
280: * Run insert, update, select on the test table in a loop.
281: */
282: void performDmlActions() throws SQLException {
283:
284: while (!stopActivity) {
285: insert(TEST_TABLE_NAME, 100, COMMIT, 10);
286: insert(TEST_TABLE_NAME, 100, ROLLBACK, 10);
287: update(TEST_TABLE_NAME, 50, ROLLBACK, 10);
288: select(TEST_TABLE_NAME);
289: }
290: }
291:
292: /**
293: * start an Unlogged operation, but don't commit the transaction.
294: * @param tableName name of the table to start the unlogged operation.
295: * @exception SQLException if any database exception occurs.
296: */
297: void startUnloggedAction(String tableName) throws SQLException {
298: // load some data
299: insert(tableName, 100, COMMIT, 10);
300: // execute a unlogged database operation
301: Statement s = conn.createStatement();
302:
303: // index creation does not log the index entries
304: s.executeUpdate("create index " + tableName
305: + "_name_idx on " + tableName + "(name) ");
306: s.close();
307: }
308:
309: /**
310: * end an Unlogged operation, commit the transaction.
311: * @param tableName name of the table to end unlogged operation.
312: * @exception SQLException if any database exception occurs.
313: */
314: void endUnloggedAction(String tableName) throws SQLException {
315: // insert some rows, insert should be successful even if
316: // backup is blocking for uncommitted unlogged operations.
317: insert(tableName, 1000, OPENTX, 10);
318: conn.commit();
319: }
320:
321: /**
322: * Create and Drop some tables.
323: * @exception SQLException if any database exception occurs.
324: */
325: void performCreateDropTables() throws SQLException {
326:
327: Statement s = conn.createStatement();
328: while (!stopActivity) {
329: for (int i = 0; i < 10; i++) {
330: String tableName = "emp" + i;
331: createTable(tableName);
332: //load some data
333: insert(tableName, 100, OPENTX, 10);
334: if ((i % 2) == 0) {
335: conn.commit();
336: } else
337: conn.rollback();
338: }
339:
340: //drop all the table that are created above.
341: for (int i = 0; i < 10; i = i + 2) {
342: String tableName = "emp" + i;
343: s.executeUpdate("drop TABLE " + "emp" + i);
344: conn.commit();
345: }
346: }
347: s.close();
348: }
349:
350: /**
351: * Insert some rows into the specified table.
352: * @param tableName name of the table that rows are inserted.
353: * @param rowCount Number of rows to Insert.
354: * @param txStaus Transacton status commit/rollback/open.
355: * @param commitCount After how many inserts commit/rollbacku should happen.
356: * @exception SQLException if any database exception occurs.
357: */
358: void insert(String tableName, int rowCount, int txStatus,
359: int commitCount) throws SQLException {
360:
361: PreparedStatement ps = conn.prepareStatement("INSERT INTO "
362: + tableName + " VALUES(?,?,?)");
363: for (int i = 0; i < rowCount; i++) {
364:
365: ps.setInt(1, i); // ID
366: ps.setString(2, "skywalker" + i);
367: ps.setFloat(3, (float) (i * 2000));
368: ps.executeUpdate();
369: if ((i % commitCount) == 0) {
370: endTransaction(txStatus);
371: }
372: }
373:
374: endTransaction(txStatus);
375: ps.close();
376: }
377:
378: /**
379: * commit/rollback the transaction.
380: * @param txStaus Transacton status commit/rollback/open.
381: * @exception SQLException if any database exception occurs.
382: */
383: void endTransaction(int txStatus) throws SQLException {
384: switch (txStatus) {
385: case COMMIT:
386: conn.commit();
387: break;
388: case ROLLBACK:
389: conn.rollback();
390: break;
391: case OPENTX:
392: //do nothing
393: break;
394: }
395: }
396:
397: /**
398: * update some rows in the table.
399: * @param tableName name of the table that rows are updates.
400: * @param rowCount Number of rows to update.
401: * @param txStaus Transacton status commit/rollback/open.
402: * @param commitCount After how many updates commit/rollback should
403: * happen.
404: * @exception SQLException if any database exception occurs.
405: */
406:
407: void update(String tableName, int rowCount, int txStatus,
408: int commitCount) throws SQLException {
409:
410: PreparedStatement ps = conn.prepareStatement("update "
411: + tableName + " SET name = ? where id=?");
412:
413: for (int i = 0; i < rowCount; i++) {
414: ps.setString(1, "moonwalker" + i);
415: ps.setInt(2, i); // ID
416: ps.executeUpdate();
417: if ((i % commitCount) == 0) {
418: endTransaction(txStatus);
419: }
420: }
421: endTransaction(txStatus);
422: ps.close();
423: }
424:
425: /*
426: * read the rows in the table.
427: * @param tableName select operation is perfomed on this table.
428: * @exception SQLException if any database exception occurs.
429: */
430: void select(String tableName) throws SQLException {
431:
432: Statement s = conn.createStatement();
433: ResultSet rs = s.executeQuery("SELECT ID, name from "
434: + tableName + " order by id");
435: int count = 0;
436: int id = 0;
437: while (rs.next()) {
438: int tid = rs.getInt(1);
439: String name = rs.getString(2);
440: if (name.equals("skywalker" + id) && tid != id) {
441: logMessage("DATA IN THE TABLE IS NOT AS EXPECTED");
442: logMessage("Got :ID=" + tid + " Name=:" + name);
443: logMessage("Expected: ID=" + id + "Name="
444: + "skywalker" + id);
445: }
446:
447: id++;
448: count++;
449: }
450:
451: rs.close();
452: s.close();
453: conn.commit();
454: }
455:
456: /*
457: * create the tables that are used by this test.
458: * @param tableName Name of the table to create.
459: * @exception SQLException if any database exception occurs.
460: */
461: void createTable(String tableName) throws SQLException {
462:
463: Statement s = conn.createStatement();
464: s.executeUpdate("CREATE TABLE " + tableName + "(id INT,"
465: + "name CHAR(200)," + "salary float)");
466: s.executeUpdate("create index " + tableName + "_id_idx on "
467: + tableName + "(id)");
468: s.close();
469: }
470:
471: }
472: }
|