提交 9055002f authored 作者: Thomas Mueller's avatar Thomas Mueller

More bugs in the server-less multi-connection mode have been fixed.

上级 a2ab67b9
...@@ -18,7 +18,9 @@ Change Log ...@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>When loading triggers or other client classes <ul><li>More bugs in the server-less multi-connection mode have been fixed:
On Windows, two processes could write to the same database at the same time.
</li><li>When loading triggers or other client classes
(static functions, database event listener, user aggregate functions, other JDBC drivers), (static functions, database event listener, user aggregate functions, other JDBC drivers),
the database now uses the context class loader if the class could not be found using Class.forName(). the database now uses the context class loader if the class could not be found using Class.forName().
</li><li>Updating many rows with the same CLOB or BLOB values could result in FileNotFoundException. </li><li>Updating many rows with the same CLOB or BLOB values could result in FileNotFoundException.
......
...@@ -187,9 +187,6 @@ public class Database implements DataHandler { ...@@ -187,9 +187,6 @@ public class Database implements DataHandler {
accessModeLog = "r"; accessModeLog = "r";
} }
this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName); this.fileLockMethod = FileLock.getFileLockMethod(lockMethodName);
if (fileLockMethod == FileLock.LOCK_SERIALIZED) {
writeDelay = SysProperties.MIN_WRITE_DELAY;
}
this.databaseURL = ci.getURL(); this.databaseURL = ci.getURL();
this.eventListener = ci.getDatabaseEventListenerObject(); this.eventListener = ci.getDatabaseEventListenerObject();
ci.removeDatabaseEventListenerObject(); ci.removeDatabaseEventListenerObject();
...@@ -357,12 +354,15 @@ public class Database implements DataHandler { ...@@ -357,12 +354,15 @@ public class Database implements DataHandler {
} }
String pos = log == null ? null : log.getWritePos(); String pos = log == null ? null : log.getWritePos();
lock.setProperty("logPos", pos); lock.setProperty("logPos", pos);
lock.setProperty("changePending", pending ? "true" : null); if (pending) {
lock.setProperty("changePending", "true-" + Math.random());
} else {
lock.setProperty("changePending", null);
}
// ensure that the writer thread will // ensure that the writer thread will
// not reset the flag before we are done // not reset the flag before we are done
reconnectCheckNext = System.currentTimeMillis() + 2 * SysProperties.RECONNECT_CHECK_DELAY; reconnectCheckNext = System.currentTimeMillis() + 2 * SysProperties.RECONNECT_CHECK_DELAY;
old = lock.save(); old = lock.save();
if (pending) { if (pending) {
getTrace().debug("wait before writing again"); getTrace().debug("wait before writing again");
Thread.sleep((int) (SysProperties.RECONNECT_CHECK_DELAY * 1.1)); Thread.sleep((int) (SysProperties.RECONNECT_CHECK_DELAY * 1.1));
...@@ -1845,18 +1845,15 @@ public class Database implements DataHandler { ...@@ -1845,18 +1845,15 @@ public class Database implements DataHandler {
public void deleteLogFileLater(String fileName) throws SQLException { public void deleteLogFileLater(String fileName) throws SQLException {
if (fileLockMethod == FileLock.LOCK_SERIALIZED) { if (fileLockMethod == FileLock.LOCK_SERIALIZED) {
// need to truncate the file, because another process could keep it open // need to truncate the file, because another process could keep it open
try { FileUtils.setLength(fileName, 0);
FileSystem.getInstance(fileName).openFileObject(fileName, "rw").setFileLength(0); } else {
} catch (IOException e) {
throw Message.convertIOException(e, "could not truncate " + fileName);
}
}
if (writer != null) { if (writer != null) {
writer.deleteLogFileLater(fileName); writer.deleteLogFileLater(fileName);
} else { } else {
FileUtils.delete(fileName); FileUtils.delete(fileName);
} }
} }
}
public void setEventListener(DatabaseEventListener eventListener) { public void setEventListener(DatabaseEventListener eventListener) {
this.eventListener = eventListener; this.eventListener = eventListener;
...@@ -2341,9 +2338,9 @@ public class Database implements DataHandler { ...@@ -2341,9 +2338,9 @@ public class Database implements DataHandler {
if (fileLockMethod != FileLock.LOCK_SERIALIZED || readOnly || !reconnectChangePending || closing) { if (fileLockMethod != FileLock.LOCK_SERIALIZED || readOnly || !reconnectChangePending || closing) {
return; return;
} }
synchronized (this) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now > reconnectCheckNext + SysProperties.RECONNECT_CHECK_DELAY) { if (now > reconnectCheckNext + SysProperties.RECONNECT_CHECK_DELAY) {
synchronized (this) {
getTrace().debug("checkpoint"); getTrace().debug("checkpoint");
flushIndexes(0); flushIndexes(0);
checkpoint(); checkpoint();
......
...@@ -301,8 +301,11 @@ public class LogSystem { ...@@ -301,8 +301,11 @@ public class LogSystem {
// this can happen if the system crashes just // this can happen if the system crashes just
// after creating a new file (before writing the header) // after creating a new file (before writing the header)
// rename it, so that it doesn't get in the way the next time // rename it, so that it doesn't get in the way the next time
FileUtils.delete(s + ".corrupt"); if (FileUtils.tryDelete(s + ".corrupt")) {
FileUtils.rename(s, s + ".corrupt"); FileUtils.rename(s, s + ".corrupt");
} else {
FileUtils.setLength(s, 0);
}
} }
if (l != null) { if (l != null) {
if (l.getPos() == LOG_WRITTEN) { if (l.getPos() == LOG_WRITTEN) {
......
...@@ -124,8 +124,10 @@ public class WriterThread implements Runnable { ...@@ -124,8 +124,10 @@ public class WriterThread implements Runnable {
flushIndexesIfRequired(database); flushIndexesIfRequired(database);
} }
int wait = writeDelay;
try { try {
if (database.isFileLockSerialized()) { if (database.isFileLockSerialized()) {
wait = SysProperties.MIN_WRITE_DELAY;
database.checkpointIfRequired(); database.checkpointIfRequired();
} else { } else {
LogSystem log = database.getLog(); LogSystem log = database.getLog();
...@@ -143,7 +145,6 @@ public class WriterThread implements Runnable { ...@@ -143,7 +145,6 @@ public class WriterThread implements Runnable {
// TODO log writer: could also flush the dirty cache when there is // TODO log writer: could also flush the dirty cache when there is
// low activity // low activity
int wait = writeDelay;
if (wait < SysProperties.MIN_WRITE_DELAY) { if (wait < SysProperties.MIN_WRITE_DELAY) {
// wait 0 mean wait forever, which is not what we want // wait 0 mean wait forever, which is not what we want
wait = SysProperties.MIN_WRITE_DELAY; wait = SysProperties.MIN_WRITE_DELAY;
......
...@@ -14,6 +14,8 @@ import java.io.RandomAccessFile; ...@@ -14,6 +14,8 @@ import java.io.RandomAccessFile;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.message.Message;
import org.h2.store.fs.FileObject;
import org.h2.store.fs.FileSystem; import org.h2.store.fs.FileSystem;
/** /**
...@@ -107,9 +109,10 @@ public class FileUtils { ...@@ -107,9 +109,10 @@ public class FileUtils {
* Try to delete a file. * Try to delete a file.
* *
* @param fileName the file name * @param fileName the file name
* @return true if it worked
*/ */
public static void tryDelete(String fileName) { public static boolean tryDelete(String fileName) {
FileSystem.getInstance(fileName).tryDelete(fileName); return FileSystem.getInstance(fileName).tryDelete(fileName);
} }
/** /**
...@@ -271,7 +274,7 @@ public class FileUtils { ...@@ -271,7 +274,7 @@ public class FileUtils {
} }
/** /**
* Get the last modified date of a file * Get the last modified date of a file.
* *
* @param fileName the file name * @param fileName the file name
* @return the last modified date * @return the last modified date
...@@ -280,4 +283,20 @@ public class FileUtils { ...@@ -280,4 +283,20 @@ public class FileUtils {
return FileSystem.getInstance(fileName).getLastModified(fileName); return FileSystem.getInstance(fileName).getLastModified(fileName);
} }
/**
* Set the file length.
*
* @param fileName the file name
* @param length the new length
*/
public static void setLength(String fileName, long length) throws SQLException {
try {
FileObject f = FileSystem.getInstance(fileName).openFileObject(fileName, "rw");
f.setFileLength(length);
f.close();
} catch (IOException e) {
throw Message.convertIOException(e, fileName + " length: " + length);
}
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论