001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.store.LogChecksumSetup
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.*;
025: import java.io.ByteArrayOutputStream;
026: import java.io.DataOutputStream;
027: import java.io.IOException;
028: import java.util.zip.CRC32;
029: import org.apache.derbyTesting.functionTests.util.corruptio.CorruptibleIo;
030: import org.apache.derby.tools.ij;
031:
032: /*
033: * Purpose of this class is to simulate out of order incomplete
034: * log write corruption (see derby-96 for details) using the proxy storage
035: * factory (org.apache.derbyTesting.functionTests.util.corruptio.
036: * CorruptDiskStorageFactory) instead of the default storage factory.
037: * By defailt all io is delegated to the default database storage factory,
038: * except when corruption is enabled through CorruptibleIo class.
039: * Proxy storage factory is loaded using the following properties in
040: * the test properties file:
041: * derby.subSubProtocol.csf=org.apache.derbyTesting.functionTests.
042: * util.corruptio.CorruptDiskStorageFactory
043: * database=jdbc:derby:csf:wombat
044: *
045: * @author <a href="mailto:suresh.thalamati@gmail.com">Suresh Thalamati</a>
046: * @version 1.0
047: * @see CorruptibleIo
048: */
049:
050: public class LogChecksumSetup {
051:
052: private CorruptibleIo cbio;
053:
054: LogChecksumSetup() {
055: cbio = CorruptibleIo.getInstance();
056: }
057:
058: /**
059: * Insert some rows into the table and corrupt the log for the last row,
060: * so when we recover , there should be one row less even though we committed.
061: */
062: void insertAndCorrupt(Connection conn, int rowCount)
063: throws SQLException {
064:
065: PreparedStatement ps = conn.prepareStatement("INSERT INTO "
066: + "T1" + " VALUES(?,?,?)");
067:
068: java.util.Random r = new java.util.Random();
069: CRC32 checksum = new CRC32(); // holder for the checksum
070: boolean corrupt = false;
071: for (int i = 0; i < rowCount; i++) {
072:
073: //setup last row for log corruption
074: if (i == (rowCount - 1)) {
075: // Note: offset/len for corruption here refers to
076: // the actual log write request
077: // that is being done for this insert.
078: setupLogCorruption(50, 10);
079: corrupt = true;
080: }
081: ps.setInt(1, i); // ID
082: byte[] dataBytes = generateBinaryData(r, 90000, 1000 * i);
083: ps.setBytes(2, dataBytes);
084: //calculate checksum for blob data
085: checksum.update(dataBytes, 0, dataBytes.length);
086: checksum.reset();
087: checksum.update(dataBytes, 0, dataBytes.length);
088: ps.setLong(3, checksum.getValue());
089: ps.executeUpdate();
090: conn.commit();
091: }
092: }
093:
094: /**
095: * update some rows in the table and corrupt the log for the last row,
096: * so when we recover , All checsum should be correct because corrupted
097: * log transaction should been rolled back.
098: */
099:
100: void updateAndCorrupt(Connection conn, int rowCount)
101: throws SQLException {
102:
103: PreparedStatement ps = conn.prepareStatement("update " + "T1"
104: + " SET " + "DATA=?, DATACHECKSUM=? where ID=?");
105:
106: java.util.Random r = new java.util.Random();
107: CRC32 checksum = new CRC32(); // holder for the checksum
108: int updateCount = 0;
109: boolean corrupt = false;
110: for (int i = 0; i < rowCount; i++) {
111:
112: //setup last row for log corruption
113: if (i == (rowCount - 1)) {
114: // Note: offset/len for corruption here refers to
115: // the actual log write request
116: // that is being done for this insert.
117: setupLogCorruption(50, 10);
118: corrupt = true;
119: }
120: byte[] dataBytes = generateBinaryData(r, 1234, 5000 * i);
121: ps.setBytes(1, dataBytes);
122:
123: // calculate checksum for blob data
124: checksum.update(dataBytes, 0, dataBytes.length);
125: checksum.reset();
126: checksum.update(dataBytes, 0, dataBytes.length);
127:
128: ps.setLong(2, checksum.getValue());
129: ps.setInt(3, i); // ID
130: updateCount += ps.executeUpdate();
131: conn.commit();
132: }
133: }
134:
135: /*
136: * read the data from the table and verify the blob data using the
137: * checksum and make sure that expected number rows exist in the table.
138: *
139: */
140: void verifyData(Connection conn, int expectedRowCount)
141: throws SQLException {
142:
143: Statement s = conn.createStatement();
144: CRC32 checksum = new CRC32(); // holder for the checksum
145:
146: ResultSet rs = s
147: .executeQuery("SELECT DATA , DATACHECKSUM, ID FROM "
148: + "T1");
149: int count = 0;
150: while (rs.next()) {
151: byte[] dataBytes = rs.getBytes(1);
152: long ckmRead = rs.getLong(2);
153: int id = rs.getInt(3);
154:
155: checksum.reset();
156: checksum.update(dataBytes, 0, dataBytes.length);
157:
158: if (checksum.getValue() != ckmRead) {
159: logMessage("CHECKSUMs ARE NOT MATCHING");
160: logMessage("ID=" + id + " Checksum From DB:" + ckmRead);
161: logMessage("Recalcaulted sum :" + checksum.getValue());
162: logMessage("Length of Data:" + dataBytes.length);
163: }
164:
165: count++;
166: }
167: conn.commit();
168:
169: if (count != expectedRowCount) {
170: logMessage("Expected Number Of Rows (" + expectedRowCount
171: + ")" + "!=" + "No Of rows in the Table(" + count
172: + ")");
173: }
174: }
175:
176: /*
177: * create the tables that are used by this test.
178: */
179: private void createTable(Connection conn) throws SQLException {
180:
181: Statement s = conn.createStatement();
182: s.executeUpdate("CREATE TABLE " + "T1" + "(ID INT,"
183: + "DATA BLOB(300000)," + "DATACHECKSUM BIGINT)");
184: conn.commit();
185: s.close();
186: }
187:
188: /*
189: * Log is corrupted using the corrupt storage factory.
190: * setup offset/length where we want the transaction
191: * log to be corrupted. Transaction tat the corruption
192: * is simulated on should be rolled back because the log check
193: * should identify that the writes were incomplete.
194: */
195: private void setupLogCorruption(int off, int len) {
196: cbio.setLogCorruption(true);
197: cbio.setOffset(off);
198: cbio.setLength(len);
199: }
200:
201: /*
202: * utility routine to generate random byte array of data.
203: */
204: private byte[] generateBinaryData(java.util.Random r, int factor,
205: int size) {
206:
207: ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
208: try {
209: DataOutputStream daos = new DataOutputStream(baos);
210: for (int i = 0; i < size; i++) {
211: int p = r.nextInt() % factor;
212: if (p < 0)
213: p = p * -1;
214: daos.writeInt(p);
215: }
216:
217: } catch (IOException ie) {
218: logMessage(ie.getMessage());
219: }
220: return baos.toByteArray();
221: }
222:
223: private void runTest(Connection conn) throws SQLException {
224: logMessage("Begin LogCheckum Setup Test");
225: createTable(conn);
226: insertAndCorrupt(conn, 11);
227: logMessage("End LogChecksum Setup Test");
228: }
229:
230: void logMessage(String str) {
231: System.out.println(str);
232: }
233:
234: public static void main(String[] argv) throws Throwable {
235: LogChecksumSetup lctest = new LogChecksumSetup();
236: ij.getPropertyArg(argv);
237: Connection conn = ij.startJBMS();
238: conn.setAutoCommit(false);
239:
240: try {
241: lctest.runTest(conn);
242: } catch (SQLException sqle) {
243: org.apache.derby.tools.JDBCDisplayUtil.ShowSQLException(
244: System.out, sqle);
245: sqle.printStackTrace(System.out);
246: }
247: }
248: }
|