001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (license2)
004: * Initial Developer: H2 Group
005: */
006: package org.h2.test.synth;
007:
008: import java.io.InputStream;
009: import java.sql.Connection;
010: import java.sql.DriverManager;
011: import java.sql.ResultSet;
012: import java.sql.SQLException;
013: import java.sql.Statement;
014: import java.util.ArrayList;
015: import java.util.Random;
016:
017: import org.h2.constant.ErrorCode;
018: import org.h2.test.TestBase;
019: import org.h2.test.unit.SelfDestructor;
020: import org.h2.tools.Backup;
021: import org.h2.util.FileUtils;
022:
023: /**
024: * Standalone recovery test. A new process is started and then killed while it
025: * executes random statements using multiple connection.
026: */
027: public class TestKillRestartMulti extends TestBase {
028:
029: private String driver = "org.h2.Driver";
030: private String url = null, user = "sa", password = "sa";
031: private ArrayList connections = new ArrayList();
032: private ArrayList tables = new ArrayList();
033: private int openCount;
034:
035: public void test() throws Exception {
036: if (config.networked) {
037: return;
038: }
039: deleteDb("killRestartMulti");
040: url = getURL("killRestartMulti", true);
041: user = getUser();
042: password = getPassword();
043: String selfDestruct = SelfDestructor.getPropertyString(60);
044: String[] procDef = new String[] { "java", selfDestruct, "-cp",
045: "bin", getClass().getName(), "-url", url, "-user",
046: user, "-password", password };
047: deleteDb("killRestartMulti");
048: int len = getSize(3, 10);
049: Random random = new Random();
050: for (int i = 0; i < len; i++) {
051: Process p = Runtime.getRuntime().exec(procDef);
052: InputStream in = p.getInputStream();
053: OutputCatcher catcher = new OutputCatcher(in);
054: catcher.start();
055: while (true) {
056: String s = catcher.readLine(5 * 60 * 1000);
057: // System.out.println("> " + s);
058: if (s == null) {
059: error("No reply from process");
060: } else if (!s.startsWith("#")) {
061: // System.out.println(s);
062: error("Expected: #..., got: " + s);
063: } else if (s.startsWith("#Running")) {
064: int sleep = 10 + random.nextInt(100);
065: Thread.sleep(sleep);
066: printTime("killing: " + i);
067: p.destroy();
068: break;
069: } else if (s.startsWith("#Info")) {
070: // System.out.println("info: " + s);
071: } else if (s.startsWith("#Fail")) {
072: System.err.println(s);
073: while (true) {
074: String a = catcher.readLine(5 * 60 * 1000);
075: if (a == null || "#End".endsWith(a)) {
076: break;
077: }
078: System.err.println(" " + a);
079: }
080: error("Failed: " + s);
081: }
082: }
083: String backup = baseDir + "/killRestartMulti-"
084: + System.currentTimeMillis() + ".zip";
085: try {
086: Backup.execute(backup, baseDir, "killRestartMulti",
087: true);
088: Connection conn = null;
089: for (int j = 0;; j++) {
090: try {
091: conn = openConnection();
092: break;
093: } catch (SQLException e2) {
094: if (e2.getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1
095: && j < 3) {
096: Thread.sleep(100);
097: } else {
098: throw e2;
099: }
100: }
101: }
102: testConsistent(conn);
103: Statement stat = conn.createStatement();
104: stat.execute("DROP ALL OBJECTS");
105: conn.close();
106: conn = openConnection();
107: conn.close();
108: FileUtils.delete(backup);
109: } catch (SQLException e) {
110: FileUtils.rename(backup, backup + ".error");
111: throw e;
112: }
113: }
114: }
115:
116: public static void main(String[] args) throws Exception {
117: SelfDestructor.startCountdown(60);
118: new TestKillRestartMulti().test(args);
119: }
120:
121: void test(String[] args) throws Exception {
122: for (int i = 0; i < args.length; i++) {
123: if ("-url".equals(args[i])) {
124: url = args[++i];
125: } else if ("-driver".equals(args[i])) {
126: driver = args[++i];
127: } else if ("-user".equals(args[i])) {
128: user = args[++i];
129: } else if ("-password".equals(args[i])) {
130: password = args[++i];
131: }
132: }
133: System.out.println("#Started; driver: " + driver + " url: "
134: + url + " user: " + user + " password: " + password);
135: try {
136: System.out.println("#Starting...");
137: Random random = new Random();
138: boolean wasRunning = false;
139: for (int i = 0; i < 3000; i++) {
140: if (i > 1000 && connections.size() > 1
141: && tables.size() > 1) {
142: System.out.println("#Running connections: "
143: + connections.size() + " tables: "
144: + tables.size());
145: wasRunning = true;
146: }
147: if (connections.size() < 1) {
148: openConnection();
149: }
150: if (tables.size() < 1) {
151: createTable(random);
152: }
153: int p = random.nextInt(100);
154: if ((p -= 2) <= 0) {
155: // 2%: open new connection
156: if (connections.size() < 5) {
157: openConnection();
158: }
159: } else if ((p -= 1) <= 0) {
160: // 1%: close connection
161: if (connections.size() > 1) {
162: Connection conn = (Connection) connections
163: .remove(random.nextInt(connections
164: .size()));
165: if (random.nextBoolean()) {
166: conn.close();
167: }
168: }
169: } else if ((p -= 10) <= 0) {
170: // 10% create table
171: createTable(random);
172: } else if ((p -= 20) <= 0) {
173: // 20% large insert, delete, or update
174: if (tables.size() > 0) {
175: Connection conn = (Connection) connections
176: .get(random.nextInt(connections.size()));
177: Statement stat = conn.createStatement();
178: String table = (String) tables.get(random
179: .nextInt(tables.size()));
180: if (random.nextBoolean()) {
181: // 10% insert
182: stat
183: .execute("INSERT INTO "
184: + table
185: + "(NAME) SELECT 'Hello ' || X FROM SYSTEM_RANGE(0, 20)");
186: } else if (random.nextBoolean()) {
187: // 5% update
188: stat.execute("UPDATE " + table
189: + " SET NAME='Hallo Welt'");
190: } else {
191: // 5% delete
192: stat.execute("DELETE FROM " + table);
193: }
194: }
195: } else if ((p -= 5) < 0) {
196: // 5% truncate or drop table
197: if (tables.size() > 0) {
198: Connection conn = (Connection) connections
199: .get(random.nextInt(connections.size()));
200: Statement stat = conn.createStatement();
201: String table = (String) tables.get(random
202: .nextInt(tables.size()));
203: if (random.nextBoolean()) {
204: stat.execute("TRUNCATE TABLE " + table);
205: } else {
206: stat.execute("DROP TABLE " + table);
207: System.out.println("#Info table dropped: "
208: + table);
209: tables.remove(table);
210: }
211: }
212: } else if ((p -= 30) <= 0) {
213: // 30% insert
214: if (tables.size() > 0) {
215: Connection conn = (Connection) connections
216: .get(random.nextInt(connections.size()));
217: Statement stat = conn.createStatement();
218: String table = (String) tables.get(random
219: .nextInt(tables.size()));
220: stat.execute("INSERT INTO " + table
221: + "(NAME) VALUES('Hello World')");
222: }
223: } else {
224: // 32% delete
225: if (tables.size() > 0) {
226: Connection conn = (Connection) connections
227: .get(random.nextInt(connections.size()));
228: Statement stat = conn.createStatement();
229: String table = (String) tables.get(random
230: .nextInt(tables.size()));
231: stat.execute("DELETE FROM " + table
232: + " WHERE ID = SELECT MIN(ID) FROM "
233: + table);
234: }
235: }
236: }
237: System.out.println("#Fail: end " + wasRunning);
238: System.out.println("#End");
239: } catch (Throwable e) {
240: System.out.println("#Fail: openCount=" + openCount
241: + " url=" + url + " " + e.toString());
242: e.printStackTrace(System.out);
243: System.out.println("#End");
244: }
245: }
246:
247: Connection openConnection() throws Exception {
248: Class.forName(driver);
249: openCount++;
250: Connection conn = DriverManager.getConnection(url, user,
251: password);
252: connections.add(conn);
253: return conn;
254: }
255:
256: void createTable(Random random) throws Exception {
257: Connection conn = (Connection) connections.get(random
258: .nextInt(connections.size()));
259: Statement stat = conn.createStatement();
260: String table = "TEST" + random.nextInt(10);
261: try {
262: stat.execute("CREATE TABLE " + table
263: + "(ID IDENTITY, NAME VARCHAR)");
264: System.out.println("#Info table created: " + table);
265: tables.add(table);
266: } catch (SQLException e) {
267: if (e.getErrorCode() == ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1) {
268: System.out.println("#Info table already exists: "
269: + table);
270: if (!tables.contains(table)) {
271: tables.add(table);
272: }
273: // ok
274: } else {
275: throw e;
276: }
277: }
278: }
279:
280: void testConsistent(Connection conn) throws Exception {
281: for (int i = 0; i < 20; i++) {
282: Statement stat = conn.createStatement();
283: try {
284: ResultSet rs = stat.executeQuery("SELECT * FROM TEST"
285: + i);
286: while (rs.next()) {
287: rs.getLong("ID");
288: rs.getString("NAME");
289: }
290: rs = stat.executeQuery("SELECT * FROM TEST" + i
291: + " ORDER BY ID");
292: while (rs.next()) {
293: rs.getLong("ID");
294: rs.getString("NAME");
295: }
296: } catch (SQLException e) {
297: if (e.getErrorCode() == ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1) {
298: // ok
299: } else {
300: throw e;
301: }
302: }
303: }
304: }
305:
306: }
|