Skip to content

Commit

Permalink
处理DDL命令在节点之间循环转发可以产生的死锁问题
Browse files Browse the repository at this point in the history
  • Loading branch information
codefollower committed Jul 15, 2015
1 parent afaf914 commit f99af75
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 32 deletions.
Expand Up @@ -23,6 +23,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -102,8 +103,18 @@ public int executeDefineCommand(DefineCommand defineCommand) {
}
}

// 从seed节点上转发命令到其他节点时会携带一个"TOKEN"参数,
// 如果在其他节点上又转发另一条命令过来,那么会构成一个循环,
// 此时就不能用this当同步对象,会产生死锁。
Object lock = this;
Properties properties = defineCommand.getSession().getOriginalProperties();
if (properties != null && properties.getProperty("TOKEN") != null)
lock = properties;

// 在seed节点上串行执行所有的defineCommand
synchronized (this) {
synchronized (lock) {
properties.setProperty("TOKEN", "1");

Set<InetAddress> liveMembers = Gossiper.instance.getLiveMembers();
List<Callable<Integer>> commands = New.arrayList(liveMembers.size());

Expand All @@ -116,6 +127,8 @@ public int executeDefineCommand(DefineCommand defineCommand) {
return CommandParallel.executeUpdateCallable(commands);
} catch (Exception e) {
throw DbException.convert(e);
} finally {
properties.remove("TOKEN");
}
}
}
Expand Down
62 changes: 31 additions & 31 deletions lealone-common/src/main/java/org/lealone/engine/ConnectionInfo.java
Expand Up @@ -32,7 +32,7 @@ public class ConnectionInfo implements Cloneable {

String[] connectionSettings = { "CIPHER", "CREATE", "CACHE_TYPE", "IGNORE_UNKNOWN_SETTINGS", "IFEXISTS",
"INIT", "PASSWORD", "RECOVER", "RECOVER_TEST", "USER", "OPEN_NEW", "PAGE_SIZE", "PASSWORD_HASH",
"IS_LOCAL" };
"IS_LOCAL", "TOKEN" };

for (String key : connectionSettings) {
if (SysProperties.CHECK && KNOWN_SETTINGS.contains(key)) {
Expand All @@ -47,7 +47,7 @@ private static boolean isKnownSetting(String s) {
}

private final Properties prop = new Properties();
private String url; //不包含后面的参数
private String url; // 不包含后面的参数
private String user;
private byte[] filePasswordHash;
private byte[] fileEncryptionKey;
Expand All @@ -73,7 +73,7 @@ private static boolean isKnownSetting(String s) {
* @param url
* @param dbName
*/
public ConnectionInfo(String url, String dbName) { //用于server端, 不需要再解析URL了
public ConnectionInfo(String url, String dbName) { // 用于server端, 不需要再解析URL了
this.url = url;
this.dbName = dbName;

Expand All @@ -87,7 +87,7 @@ public ConnectionInfo(String url, String dbName) { //用于server端, 不需要
persistent = false;
url = url.substring(Constants.URL_MEM.length());
}
//server端接收到的URL不可能是嵌入式的
// server端接收到的URL不可能是嵌入式的
if (url.startsWith(Constants.URL_EMBED)) {
throw DbException.throwInternalError("Server backend URL: " + this.url);
}
Expand All @@ -103,7 +103,7 @@ public ConnectionInfo(String url) {
* @param url the database URL
* @param info the connection properties
*/
public ConnectionInfo(String url, Properties info) { //用于client端,需要解析URL
public ConnectionInfo(String url, Properties info) { // 用于client端,需要解析URL
this.url = remapURL(url);

checkURL();
Expand All @@ -121,33 +121,33 @@ private void checkURL() {
}
}

//如果URL中有参数先读出来,然后从URL中移除
// 如果URL中有参数先读出来,然后从URL中移除
private void readAndRemoveSettingsFromURL() {
//支持两种风格的JDBC URL参数语法
//1. MySQL的JDBC URL参数语法:
//.../database[?propertyName1=propertyValue1][&propertyName2=propertyValue2]
//数据库名与参数之间用'?'号分隔,不同参数之间用'&'分隔

//2.Lealone的JDBC URL参数语法:
//.../database[;propertyName1=propertyValue1][;propertyName2=propertyValue2]
//数据库名与参数之间用';'号分隔,不同参数之间也用';'号分隔
// 支持两种风格的JDBC URL参数语法
// 1. MySQL的JDBC URL参数语法:
// .../database[?propertyName1=propertyValue1][&propertyName2=propertyValue2]
// 数据库名与参数之间用'?'号分隔,不同参数之间用'&'分隔

// 2.Lealone的JDBC URL参数语法:
// .../database[;propertyName1=propertyValue1][;propertyName2=propertyValue2]
// 数据库名与参数之间用';'号分隔,不同参数之间也用';'号分隔
int idx = url.indexOf('?');
char splitChar;
if (idx >= 0) {
splitChar = '&';
if (url.indexOf(';') >= 0)
throw getFormatException(); //不能同时出现'&'和';'
throw getFormatException(); // 不能同时出现'&'和';'
} else {
idx = url.indexOf(';');
splitChar = ';';
if (url.indexOf('&') >= 0)
throw getFormatException(); //不能出现'&'
throw getFormatException(); // 不能出现'&'
}

if (idx >= 0) {
DbSettings dbSettings = DbSettings.getDefaultSettings();
String settings = url.substring(idx + 1);
url = url.substring(0, idx); //去掉后面的参数
url = url.substring(0, idx); // 去掉后面的参数
String[] list = StringUtils.arraySplit(settings, splitChar, false);
for (String setting : list) {
if (setting.length() == 0) {
Expand Down Expand Up @@ -198,7 +198,7 @@ private void parseURL() {
}

if (remote) {
if (dbName.startsWith("//")) //在URL中"//"是可选的
if (dbName.startsWith("//")) // 在URL中"//"是可选的
dbName = dbName.substring("//".length());

int idx = dbName.indexOf('/');
Expand Down Expand Up @@ -404,19 +404,19 @@ public String getDatabaseName() {
if (persistent) {
String name = dbName;
if (nameNormalized == null) {
// if (!SysProperties.IMPLICIT_RELATIVE_PATH) {
// if (!FileUtils.isAbsolute(name)) {
// if (name.indexOf("./") < 0 && name.indexOf(".\\") < 0 && name.indexOf(":/") < 0
// && name.indexOf(":\\") < 0) {
// // the name could start with "./", or
// // it could start with a prefix such as "nio:./"
// // for Windows, the path "\test" is not considered
// // absolute as the drive letter is missing,
// // but we consider it absolute
// throw DbException.get(ErrorCode.URL_RELATIVE_TO_CWD, url);
// }
// }
// }
// if (!SysProperties.IMPLICIT_RELATIVE_PATH) {
// if (!FileUtils.isAbsolute(name)) {
// if (name.indexOf("./") < 0 && name.indexOf(".\\") < 0 && name.indexOf(":/") < 0
// && name.indexOf(":\\") < 0) {
// // the name could start with "./", or
// // it could start with a prefix such as "nio:./"
// // for Windows, the path "\test" is not considered
// // absolute as the drive letter is missing,
// // but we consider it absolute
// throw DbException.get(ErrorCode.URL_RELATIVE_TO_CWD, url);
// }
// }
// }
String suffix = Constants.SUFFIX_MV_FILE;
String n = FileUtils.toRealPath(name + suffix);
String fileName = FileUtils.getName(n);
Expand Down

0 comments on commit f99af75

Please sign in to comment.