提交 fdb4eb02 authored 作者: noelgrandin's avatar noelgrandin

fix deadlock in concurrent LOB update

上级 3c754151
...@@ -18,7 +18,7 @@ Change Log ...@@ -18,7 +18,7 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>- <ul><li>Fix deadlock when updating LOB's concurrently. See TestLob#testDeadlock2()
</li></ul> </li></ul>
<h2>Version 1.3.172 (2013-05-25)</h2> <h2>Version 1.3.172 (2013-05-25)</h2>
......
...@@ -2356,7 +2356,7 @@ public class Database implements DataHandler { ...@@ -2356,7 +2356,7 @@ public class Database implements DataHandler {
} }
@Override @Override
public Connection getLobConnection() { public JdbcConnection getLobConnection() {
String url = Constants.CONN_URL_INTERNAL; String url = Constants.CONN_URL_INTERNAL;
JdbcConnection conn = new JdbcConnection(systemSession, systemUser.getName(), url); JdbcConnection conn = new JdbcConnection(systemSession, systemUser.getName(), url);
conn.setTraceLevel(TraceSystem.OFF); conn.setTraceLevel(TraceSystem.OFF);
......
...@@ -9,7 +9,6 @@ package org.h2.store; ...@@ -9,7 +9,6 @@ package org.h2.store;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -17,11 +16,11 @@ import java.sql.Statement; ...@@ -17,11 +16,11 @@ import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.tools.CompressTool; import org.h2.tools.CompressTool;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
...@@ -60,7 +59,7 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -60,7 +59,7 @@ public class LobStorageBackend implements LobStorageInterface {
*/ */
private static final int HASH_CACHE_SIZE = 4 * 1024; private static final int HASH_CACHE_SIZE = 4 * 1024;
private Connection conn; private JdbcConnection conn;
private final HashMap<String, PreparedStatement> prepared = New.hashMap(); private final HashMap<String, PreparedStatement> prepared = New.hashMap();
private long nextBlock; private long nextBlock;
private final CompressTool compress = CompressTool.getInstance(); private final CompressTool compress = CompressTool.getInstance();
...@@ -156,7 +155,7 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -156,7 +155,7 @@ public class LobStorageBackend implements LobStorageInterface {
/** /**
* Remove all LOBs for this table. * Remove all LOBs for this table.
* *
* @param tableId the table id * @param tableId the table id
*/ */
public void removeAllForTable(int tableId) { public void removeAllForTable(int tableId) {
...@@ -185,30 +184,33 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -185,30 +184,33 @@ public class LobStorageBackend implements LobStorageInterface {
/** /**
* Read a block of data from the given LOB. * Read a block of data from the given LOB.
* *
* @param lob the lob id * @param lob the lob id
* @param seq the block sequence number * @param seq the block sequence number
* @return the block (expanded if stored compressed) * @return the block (expanded if stored compressed)
*/ */
byte[] readBlock(long lob, int seq) throws SQLException { byte[] readBlock(long lob, int seq) throws SQLException {
synchronized (database) { // we have to take the lock on the session before the lock on the database to prevent ABBA deadlocks
String sql = "SELECT COMPRESSED, DATA FROM " + LOB_MAP + " M " + synchronized (conn.getSession()) {
"INNER JOIN " + LOB_DATA + " D ON M.BLOCK = D.BLOCK " + synchronized (database) {
"WHERE M.LOB = ? AND M.SEQ = ?"; String sql = "SELECT COMPRESSED, DATA FROM " + LOB_MAP + " M " +
PreparedStatement prep = prepare(sql); "INNER JOIN " + LOB_DATA + " D ON M.BLOCK = D.BLOCK " +
prep.setLong(1, lob); "WHERE M.LOB = ? AND M.SEQ = ?";
prep.setInt(2, seq); PreparedStatement prep = prepare(sql);
ResultSet rs = prep.executeQuery(); prep.setLong(1, lob);
if (!rs.next()) { prep.setInt(2, seq);
ResultSet rs = prep.executeQuery();
if (!rs.next()) {
throw DbException.get(ErrorCode.IO_EXCEPTION_1, "Missing lob entry: "+ lob + "/" + seq).getSQLException(); throw DbException.get(ErrorCode.IO_EXCEPTION_1, "Missing lob entry: "+ lob + "/" + seq).getSQLException();
}
int compressed = rs.getInt(1);
byte[] buffer = rs.getBytes(2);
if (compressed != 0) {
buffer = compress.expand(buffer);
}
reuse(sql, prep);
return buffer;
} }
int compressed = rs.getInt(1);
byte[] buffer = rs.getBytes(2);
if (compressed != 0) {
buffer = compress.expand(buffer);
}
reuse(sql, prep);
return buffer;
} }
} }
...@@ -216,28 +218,30 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -216,28 +218,30 @@ public class LobStorageBackend implements LobStorageInterface {
* Retrieve the sequence id and position that is smaller than the requested * Retrieve the sequence id and position that is smaller than the requested
* position. Those values can be used to quickly skip to a given position * position. Those values can be used to quickly skip to a given position
* without having to read all data. * without having to read all data.
* *
* @param lob the lob * @param lob the lob
* @param pos the required position * @param pos the required position
* @return null if the data is not available, or an array of two elements: * @return null if the data is not available, or an array of two elements:
* the sequence, and the offset * the sequence, and the offset
*/ */
long[] skipBuffer(long lob, long pos) throws SQLException { long[] skipBuffer(long lob, long pos) throws SQLException {
synchronized (database) { synchronized (conn.getSession()) {
String sql = "SELECT MAX(SEQ), MAX(POS) FROM " + LOB_MAP + synchronized (database) {
" WHERE LOB = ? AND POS < ?"; String sql = "SELECT MAX(SEQ), MAX(POS) FROM " + LOB_MAP +
PreparedStatement prep = prepare(sql); " WHERE LOB = ? AND POS < ?";
prep.setLong(1, lob); PreparedStatement prep = prepare(sql);
prep.setLong(2, pos); prep.setLong(1, lob);
ResultSet rs = prep.executeQuery(); prep.setLong(2, pos);
rs.next(); ResultSet rs = prep.executeQuery();
int seq = rs.getInt(1); rs.next();
pos = rs.getLong(2); int seq = rs.getInt(1);
boolean wasNull = rs.wasNull(); pos = rs.getLong(2);
rs.close(); boolean wasNull = rs.wasNull();
reuse(sql, prep); rs.close();
// upgraded: offset not set reuse(sql, prep);
return wasNull ? null : new long[]{seq, pos}; // upgraded: offset not set
return wasNull ? null : new long[] { seq, pos };
}
} }
} }
...@@ -266,41 +270,43 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -266,41 +270,43 @@ public class LobStorageBackend implements LobStorageInterface {
@Override @Override
public void removeLob(long lob) { public void removeLob(long lob) {
try { try {
synchronized (database) { synchronized (conn.getSession()) {
String sql = "SELECT BLOCK, HASH FROM " + LOB_MAP + " D WHERE D.LOB = ? " + synchronized (database) {
"AND NOT EXISTS(SELECT 1 FROM " + LOB_MAP + " O " + String sql = "SELECT BLOCK, HASH FROM " + LOB_MAP + " D WHERE D.LOB = ? " +
"WHERE O.BLOCK = D.BLOCK AND O.LOB <> ?)"; "AND NOT EXISTS(SELECT 1 FROM " + LOB_MAP + " O " +
PreparedStatement prep = prepare(sql); "WHERE O.BLOCK = D.BLOCK AND O.LOB <> ?)";
prep.setLong(1, lob); PreparedStatement prep = prepare(sql);
prep.setLong(2, lob); prep.setLong(1, lob);
ResultSet rs = prep.executeQuery(); prep.setLong(2, lob);
ArrayList<Long> blocks = New.arrayList(); ResultSet rs = prep.executeQuery();
while (rs.next()) { ArrayList<Long> blocks = New.arrayList();
blocks.add(rs.getLong(1)); while (rs.next()) {
int hash = rs.getInt(2); blocks.add(rs.getLong(1));
setHashCacheBlock(hash, -1); int hash = rs.getInt(2);
} setHashCacheBlock(hash, -1);
reuse(sql, prep); }
reuse(sql, prep);
sql = "DELETE FROM " + LOB_MAP + " WHERE LOB = ?"; sql = "DELETE FROM " + LOB_MAP + " WHERE LOB = ?";
prep = prepare(sql); prep = prepare(sql);
prep.setLong(1, lob); prep.setLong(1, lob);
prep.execute(); prep.execute();
reuse(sql, prep); reuse(sql, prep);
sql = "DELETE FROM " + LOB_DATA + " WHERE BLOCK = ?"; sql = "DELETE FROM " + LOB_DATA + " WHERE BLOCK = ?";
prep = prepare(sql); prep = prepare(sql);
for (long block : blocks) { for (long block : blocks) {
prep.setLong(1, block); prep.setLong(1, block);
prep.execute();
}
reuse(sql, prep);
sql = "DELETE FROM " + LOBS + " WHERE ID = ?";
prep = prepare(sql);
prep.setLong(1, lob);
prep.execute(); prep.execute();
reuse(sql, prep);
} }
reuse(sql, prep);
sql = "DELETE FROM " + LOBS + " WHERE ID = ?";
prep = prepare(sql);
prep.setLong(1, lob);
prep.execute();
reuse(sql, prep);
} }
} catch (SQLException e) { } catch (SQLException e) {
throw DbException.convert(e); throw DbException.convert(e);
...@@ -311,19 +317,21 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -311,19 +317,21 @@ public class LobStorageBackend implements LobStorageInterface {
public InputStream getInputStream(long lobId, byte[] hmac, long byteCount) throws IOException { public InputStream getInputStream(long lobId, byte[] hmac, long byteCount) throws IOException {
init(); init();
if (byteCount == -1) { if (byteCount == -1) {
synchronized (database) { synchronized (conn.getSession()) {
try { synchronized (database) {
String sql = "SELECT BYTE_COUNT FROM " + LOBS + " WHERE ID = ?"; try {
PreparedStatement prep = prepare(sql); String sql = "SELECT BYTE_COUNT FROM " + LOBS + " WHERE ID = ?";
prep.setLong(1, lobId); PreparedStatement prep = prepare(sql);
ResultSet rs = prep.executeQuery(); prep.setLong(1, lobId);
if (!rs.next()) { ResultSet rs = prep.executeQuery();
throw DbException.get(ErrorCode.IO_EXCEPTION_1, "Missing lob: "+ lobId).getSQLException(); if (!rs.next()) {
throw DbException.get(ErrorCode.IO_EXCEPTION_1, "Missing lob: " + lobId).getSQLException();
}
byteCount = rs.getLong(1);
reuse(sql, prep);
} catch (SQLException e) {
throw DbException.convertToIOException(e);
} }
byteCount = rs.getLong(1);
reuse(sql, prep);
} catch (SQLException e) {
throw DbException.convertToIOException(e);
} }
} }
} }
...@@ -360,11 +368,13 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -360,11 +368,13 @@ public class LobStorageBackend implements LobStorageInterface {
small = b; small = b;
break; break;
} }
synchronized (database) { synchronized (conn.getSession()) {
if (seq == 0) { synchronized (database) {
lobId = getNextLobId(); if (seq == 0) {
lobId = getNextLobId();
}
storeBlock(lobId, seq, length, b, compressAlgorithm);
} }
storeBlock(lobId, seq, length, b, compressAlgorithm);
} }
length += len; length += len;
} }
...@@ -390,50 +400,54 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -390,50 +400,54 @@ public class LobStorageBackend implements LobStorageInterface {
} }
private ValueLobDb registerLob(int type, long lobId, int tableId, long byteCount) { private ValueLobDb registerLob(int type, long lobId, int tableId, long byteCount) {
synchronized (database) { synchronized (conn.getSession()) {
try { synchronized (database) {
String sql = "INSERT INTO " + LOBS + "(ID, BYTE_COUNT, TABLE) VALUES(?, ?, ?)"; try {
PreparedStatement prep = prepare(sql); String sql = "INSERT INTO " + LOBS + "(ID, BYTE_COUNT, TABLE) VALUES(?, ?, ?)";
prep.setLong(1, lobId); PreparedStatement prep = prepare(sql);
prep.setLong(2, byteCount); prep.setLong(1, lobId);
prep.setInt(3, tableId); prep.setLong(2, byteCount);
prep.execute(); prep.setInt(3, tableId);
reuse(sql, prep); prep.execute();
ValueLobDb v = ValueLobDb.create(type, this, tableId, lobId, null, byteCount); reuse(sql, prep);
return v; ValueLobDb v = ValueLobDb.create(type, this, tableId, lobId, null, byteCount);
} catch (SQLException e) { return v;
throw DbException.convert(e); } catch (SQLException e) {
throw DbException.convert(e);
}
} }
} }
} }
@Override @Override
public ValueLobDb copyLob(int type, long oldLobId, int tableId, long length) { public ValueLobDb copyLob(int type, long oldLobId, int tableId, long length) {
synchronized (database) { synchronized (conn.getSession()) {
try { synchronized (database) {
init(); try {
long lobId = getNextLobId(); init();
String sql = "INSERT INTO " + LOB_MAP + "(LOB, SEQ, POS, HASH, BLOCK) " + long lobId = getNextLobId();
"SELECT ?, SEQ, POS, HASH, BLOCK FROM " + LOB_MAP + " WHERE LOB = ?"; String sql = "INSERT INTO " + LOB_MAP + "(LOB, SEQ, POS, HASH, BLOCK) " +
PreparedStatement prep = prepare(sql); "SELECT ?, SEQ, POS, HASH, BLOCK FROM " + LOB_MAP + " WHERE LOB = ?";
prep.setLong(1, lobId); PreparedStatement prep = prepare(sql);
prep.setLong(2, oldLobId); prep.setLong(1, lobId);
prep.executeUpdate(); prep.setLong(2, oldLobId);
reuse(sql, prep); prep.executeUpdate();
reuse(sql, prep);
sql = "INSERT INTO " + LOBS + "(ID, BYTE_COUNT, TABLE) " + sql = "INSERT INTO " + LOBS + "(ID, BYTE_COUNT, TABLE) " +
"SELECT ?, BYTE_COUNT, ? FROM " + LOBS + " WHERE ID = ?"; "SELECT ?, BYTE_COUNT, ? FROM " + LOBS + " WHERE ID = ?";
prep = prepare(sql); prep = prepare(sql);
prep.setLong(1, lobId); prep.setLong(1, lobId);
prep.setLong(2, tableId); prep.setLong(2, tableId);
prep.setLong(3, oldLobId); prep.setLong(3, oldLobId);
prep.executeUpdate(); prep.executeUpdate();
reuse(sql, prep); reuse(sql, prep);
ValueLobDb v = ValueLobDb.create(type, this, tableId, lobId, null, length); ValueLobDb v = ValueLobDb.create(type, this, tableId, lobId, null, length);
return v; return v;
} catch (SQLException e) { } catch (SQLException e) {
throw DbException.convert(e); throw DbException.convert(e);
}
} }
} }
} }
...@@ -467,7 +481,7 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -467,7 +481,7 @@ public class LobStorageBackend implements LobStorageInterface {
/** /**
* Store a block in the LOB storage. * Store a block in the LOB storage.
* *
* @param lobId the lob id * @param lobId the lob id
* @param seq the sequence number * @param seq the sequence number
* @param pos the position within the lob * @param pos the position within the lob
...@@ -481,43 +495,45 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -481,43 +495,45 @@ public class LobStorageBackend implements LobStorageInterface {
b = compress.compress(b, compressAlgorithm); b = compress.compress(b, compressAlgorithm);
} }
int hash = Arrays.hashCode(b); int hash = Arrays.hashCode(b);
synchronized (database) { synchronized (conn.getSession()) {
block = getHashCacheBlock(hash); synchronized (database) {
if (block != -1) { block = getHashCacheBlock(hash);
String sql = "SELECT COMPRESSED, DATA FROM " + LOB_DATA + if (block != -1) {
" WHERE BLOCK = ?"; String sql = "SELECT COMPRESSED, DATA FROM " + LOB_DATA +
PreparedStatement prep = prepare(sql); " WHERE BLOCK = ?";
prep.setLong(1, block); PreparedStatement prep = prepare(sql);
ResultSet rs = prep.executeQuery(); prep.setLong(1, block);
if (rs.next()) { ResultSet rs = prep.executeQuery();
boolean compressed = rs.getInt(1) != 0; if (rs.next()) {
byte[] compare = rs.getBytes(2); boolean compressed = rs.getInt(1) != 0;
if (compressed == (compressAlgorithm != null) && Arrays.equals(b, compare)) { byte[] compare = rs.getBytes(2);
blockExists = true; if (compressed == (compressAlgorithm != null) && Arrays.equals(b, compare)) {
blockExists = true;
}
} }
reuse(sql, prep);
} }
reuse(sql, prep); if (!blockExists) {
} block = nextBlock++;
if (!blockExists) { setHashCacheBlock(hash, block);
block = nextBlock++; String sql = "INSERT INTO " + LOB_DATA + "(BLOCK, COMPRESSED, DATA) VALUES(?, ?, ?)";
setHashCacheBlock(hash, block); PreparedStatement prep = prepare(sql);
String sql = "INSERT INTO " + LOB_DATA + "(BLOCK, COMPRESSED, DATA) VALUES(?, ?, ?)"; prep.setLong(1, block);
prep.setInt(2, compressAlgorithm == null ? 0 : 1);
prep.setBytes(3, b);
prep.execute();
reuse(sql, prep);
}
String sql = "INSERT INTO " + LOB_MAP + "(LOB, SEQ, POS, HASH, BLOCK) VALUES(?, ?, ?, ?, ?)";
PreparedStatement prep = prepare(sql); PreparedStatement prep = prepare(sql);
prep.setLong(1, block); prep.setLong(1, lobId);
prep.setInt(2, compressAlgorithm == null ? 0 : 1); prep.setInt(2, seq);
prep.setBytes(3, b); prep.setLong(3, pos);
prep.setLong(4, hash);
prep.setLong(5, block);
prep.execute(); prep.execute();
reuse(sql, prep); reuse(sql, prep);
} }
String sql = "INSERT INTO " + LOB_MAP + "(LOB, SEQ, POS, HASH, BLOCK) VALUES(?, ?, ?, ?, ?)";
PreparedStatement prep = prepare(sql);
prep.setLong(1, lobId);
prep.setInt(2, seq);
prep.setLong(3, pos);
prep.setLong(4, hash);
prep.setLong(5, block);
prep.execute();
reuse(sql, prep);
} }
} }
...@@ -545,17 +561,19 @@ public class LobStorageBackend implements LobStorageInterface { ...@@ -545,17 +561,19 @@ public class LobStorageBackend implements LobStorageInterface {
@Override @Override
public void setTable(long lobId, int table) { public void setTable(long lobId, int table) {
synchronized (database) { synchronized (conn.getSession()) {
try { synchronized (database) {
init(); try {
String sql = "UPDATE " + LOBS + " SET TABLE = ? WHERE ID = ?"; init();
PreparedStatement prep = prepare(sql); String sql = "UPDATE " + LOBS + " SET TABLE = ? WHERE ID = ?";
prep.setInt(1, table); PreparedStatement prep = prepare(sql);
prep.setLong(2, lobId); prep.setInt(1, table);
prep.executeUpdate(); prep.setLong(2, lobId);
reuse(sql, prep); prep.executeUpdate();
} catch (SQLException e) { reuse(sql, prep);
throw DbException.convert(e); } catch (SQLException e) {
throw DbException.convert(e);
}
} }
} }
} }
......
...@@ -9,6 +9,7 @@ package org.h2.test.db; ...@@ -9,6 +9,7 @@ package org.h2.test.db;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.CharArrayReader; import java.io.CharArrayReader;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Reader; import java.io.Reader;
...@@ -64,6 +65,7 @@ public class TestLob extends TestBase { ...@@ -64,6 +65,7 @@ public class TestLob extends TestBase {
testBlobInputStreamSeek(true); testBlobInputStreamSeek(true);
testBlobInputStreamSeek(false); testBlobInputStreamSeek(false);
testDeadlock(); testDeadlock();
testDeadlock2();
testCopyManyLobs(); testCopyManyLobs();
testCopyLob(); testCopyLob();
testConcurrentCreate(); testConcurrentCreate();
...@@ -275,6 +277,103 @@ public class TestLob extends TestBase { ...@@ -275,6 +277,103 @@ public class TestLob extends TestBase {
conn2.close(); conn2.close();
} }
private final class Deadlock2Task1 extends Task {
public final Connection conn;
private Deadlock2Task1() throws SQLException {
this.conn = getDeadlock2Connection();
}
@Override
public void call() throws Exception {
Random random = new Random();
Statement stat = conn.createStatement();
char[] tmp = new char[1024];
while (!stop) {
try {
ResultSet rs = stat.executeQuery("select name from test where id = " + random.nextInt(999));
if (rs.next()) {
Reader r = rs.getClob("name").getCharacterStream();
while ( r.read(tmp) > 0) {}
r.close();
}
rs.close();
} catch (SQLException ex) {
// ignore "LOB gone away", this can happen in the presence of concurrent updates
if (ex.getErrorCode() != ErrorCode.IO_EXCEPTION_2) {
ex.printStackTrace();
}
} catch (IOException ex) {
// ignore "LOB gone away", this can happen in the presence of concurrent updates
if (!(ex.getCause() instanceof SQLException)) {
ex.printStackTrace();
}
SQLException ex2 = (SQLException) ex.getCause();
if (ex2.getErrorCode() != 90028) {
ex.printStackTrace();
}
}
}
}
}
private final class Deadlock2Task2 extends Task {
public final Connection conn;
private Deadlock2Task2() throws SQLException {
this.conn = getDeadlock2Connection();
}
@Override
public void call() throws Exception {
Random random = new Random();
Statement stat = conn.createStatement();
while (!stop) {
stat.execute("update test set counter = " + random.nextInt(10) + " where id = " + random.nextInt(1000));
}
}
}
private void testDeadlock2() throws Exception {
deleteDb("lob");
Connection conn = getDeadlock2Connection();
Statement stat = conn.createStatement();
stat.execute("create cached table test(id int not null identity, name clob, counter int)");
stat.execute("insert into test(id, name) select x, space(100000) from system_range(1, 1000)");
Deadlock2Task1 task1 = new Deadlock2Task1();
Deadlock2Task1 task2 = new Deadlock2Task1();
Deadlock2Task1 task3 = new Deadlock2Task1();
Deadlock2Task1 task4 = new Deadlock2Task1();
Deadlock2Task2 task5 = new Deadlock2Task2();
Deadlock2Task2 task6 = new Deadlock2Task2();
task1.execute();
task2.execute();
task3.execute();
task4.execute();
task5.execute();
task6.execute();
for (int i = 0; i < 1000; i++) {
stat.execute("insert into test values(null, space(10000 + " + i + "), 1)");
}
task1.get();
task1.conn.close();
task2.get();
task2.conn.close();
task3.get();
task3.conn.close();
task4.get();
task4.conn.close();
task5.get();
task5.conn.close();
task6.get();
task6.conn.close();
conn.close();
}
private Connection getDeadlock2Connection() throws SQLException {
return getConnection("lob;MULTI_THREADED=TRUE;LOCK_TIMEOUT=60000");
}
private void testCopyManyLobs() throws Exception { private void testCopyManyLobs() throws Exception {
deleteDb("lob"); deleteDb("lob");
Connection conn = getConnection("lob"); Connection conn = getConnection("lob");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论