Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
16677754
Unverified
提交
16677754
authored
6月 01, 2018
作者:
Andrei Tokar
提交者:
GitHub
6月 01, 2018
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #1167 from h2database/txcommit-atomic
MVStore: Undo log synchronization removal
上级
4aeb5900
d43e9df2
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
30 个修改的文件
包含
922 行增加
和
470 行删除
+922
-470
Command.java
h2/src/main/org/h2/command/Command.java
+29
-18
Select.java
h2/src/main/org/h2/command/dml/Select.java
+4
-3
Update.java
h2/src/main/org/h2/command/dml/Update.java
+1
-0
Database.java
h2/src/main/org/h2/engine/Database.java
+6
-1
Session.java
h2/src/main/org/h2/engine/Session.java
+26
-9
JdbcConnection.java
h2/src/main/org/h2/jdbc/JdbcConnection.java
+3
-3
Cursor.java
h2/src/main/org/h2/mvstore/Cursor.java
+1
-1
DataUtils.java
h2/src/main/org/h2/mvstore/DataUtils.java
+5
-0
MVMap.java
h2/src/main/org/h2/mvstore/MVMap.java
+9
-2
MVPrimaryIndex.java
h2/src/main/org/h2/mvstore/db/MVPrimaryIndex.java
+20
-3
MVTable.java
h2/src/main/org/h2/mvstore/db/MVTable.java
+21
-20
MVTableEngine.java
h2/src/main/org/h2/mvstore/db/MVTableEngine.java
+1
-1
MVRTreeMap.java
h2/src/main/org/h2/mvstore/rtree/MVRTreeMap.java
+1
-0
CommitDecisionMaker.java
h2/src/main/org/h2/mvstore/tx/CommitDecisionMaker.java
+64
-0
RollbackDecisionMaker.java
h2/src/main/org/h2/mvstore/tx/RollbackDecisionMaker.java
+61
-0
Transaction.java
h2/src/main/org/h2/mvstore/tx/Transaction.java
+155
-11
TransactionMap.java
h2/src/main/org/h2/mvstore/tx/TransactionMap.java
+150
-136
TransactionStore.java
h2/src/main/org/h2/mvstore/tx/TransactionStore.java
+114
-181
TxDecisionMaker.java
h2/src/main/org/h2/mvstore/tx/TxDecisionMaker.java
+177
-0
VersionedValue.java
h2/src/main/org/h2/mvstore/tx/VersionedValue.java
+2
-0
MetaTable.java
h2/src/main/org/h2/table/MetaTable.java
+11
-2
Table.java
h2/src/main/org/h2/table/Table.java
+17
-1
TableFilter.java
h2/src/main/org/h2/table/TableFilter.java
+2
-9
TestBase.java
h2/src/test/org/h2/test/TestBase.java
+1
-1
TestIndex.java
h2/src/test/org/h2/test/db/TestIndex.java
+7
-1
TestOptimizations.java
h2/src/test/org/h2/test/db/TestOptimizations.java
+1
-1
TestTriggersConstraints.java
h2/src/test/org/h2/test/db/TestTriggersConstraints.java
+5
-1
TestMvcc4.java
h2/src/test/org/h2/test/mvcc/TestMvcc4.java
+11
-37
TestMvccMultiThreaded.java
h2/src/test/org/h2/test/mvcc/TestMvccMultiThreaded.java
+15
-26
TestAutoReconnect.java
h2/src/test/org/h2/test/unit/TestAutoReconnect.java
+2
-2
没有找到文件。
h2/src/main/org/h2/command/Command.java
浏览文件 @
16677754
...
...
@@ -7,6 +7,8 @@ package org.h2.command;
import
java.sql.SQLException
;
import
java.util.ArrayList
;
import
java.util.concurrent.TimeUnit
;
import
org.h2.api.ErrorCode
;
import
org.h2.engine.Constants
;
import
org.h2.engine.Database
;
...
...
@@ -184,7 +186,7 @@ public abstract class Command implements CommandInterface {
startTimeNanos
=
0
;
long
start
=
0
;
Database
database
=
session
.
getDatabase
();
Object
sync
=
database
.
isMultiThreaded
()
?
(
Object
)
session
:
(
Object
)
database
;
Object
sync
=
database
.
isMultiThreaded
()
||
database
.
getMvStore
()
!=
null
?
session
:
database
;
session
.
waitIfExclusiveModeEnabled
();
boolean
callStop
=
true
;
boolean
writing
=
!
isReadOnly
();
...
...
@@ -193,7 +195,9 @@ public abstract class Command implements CommandInterface {
// wait
}
}
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized
(
sync
)
{
session
.
startStatementWithinTransaction
();
session
.
setCurrentCommand
(
this
,
false
);
try
{
while
(
true
)
{
...
...
@@ -242,7 +246,7 @@ public abstract class Command implements CommandInterface {
public
ResultWithGeneratedKeys
executeUpdate
(
Object
generatedKeysRequest
)
{
long
start
=
0
;
Database
database
=
session
.
getDatabase
();
Object
sync
=
database
.
isMultiThreaded
()
?
(
Object
)
session
:
(
Object
)
database
;
Object
sync
=
database
.
isMultiThreaded
()
||
database
.
getMvStore
()
!=
null
?
session
:
database
;
session
.
waitIfExclusiveModeEnabled
();
boolean
callStop
=
true
;
boolean
writing
=
!
isReadOnly
();
...
...
@@ -251,8 +255,10 @@ public abstract class Command implements CommandInterface {
// wait
}
}
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized
(
sync
)
{
Session
.
Savepoint
rollback
=
session
.
setSavepoint
();
session
.
startStatementWithinTransaction
();
session
.
setCurrentCommand
(
this
,
generatedKeysRequest
);
try
{
while
(
true
)
{
...
...
@@ -311,25 +317,30 @@ public abstract class Command implements CommandInterface {
errorCode
!=
ErrorCode
.
ROW_NOT_FOUND_WHEN_DELETING_1
)
{
throw
e
;
}
long
now
=
System
.
nanoTime
()
/
1_000_000
;
if
(
start
!=
0
&&
now
-
start
>
session
.
getLockTimeout
())
{
throw
DbException
.
get
(
ErrorCode
.
LOCK_TIMEOUT_1
,
e
.
getCause
(),
""
);
long
now
=
System
.
nanoTime
();
if
(
start
!=
0
&&
TimeUnit
.
NANOSECONDS
.
toMillis
(
now
-
start
)
>
session
.
getLockTimeout
())
{
throw
DbException
.
get
(
ErrorCode
.
LOCK_TIMEOUT_1
,
e
);
}
// Only in PageStore mode we need to sleep here to avoid buzy wait loop
Database
database
=
session
.
getDatabase
();
int
sleep
=
1
+
MathUtils
.
randomInt
(
10
);
while
(
true
)
{
try
{
if
(
database
.
isMultiThreaded
())
{
Thread
.
sleep
(
sleep
);
}
else
{
database
.
wait
(
sleep
);
if
(
database
.
getMvStore
()
==
null
)
{
int
sleep
=
1
+
MathUtils
.
randomInt
(
10
);
while
(
true
)
{
try
{
if
(
database
.
isMultiThreaded
())
{
Thread
.
sleep
(
sleep
);
}
else
{
// although nobody going to notify us
// it is vital to give up lock on a database
database
.
wait
(
sleep
);
}
}
catch
(
InterruptedException
e1
)
{
// ignore
}
long
slept
=
TimeUnit
.
NANOSECONDS
.
toMillis
(
System
.
nanoTime
()
-
now
);
if
(
slept
>=
sleep
)
{
break
;
}
}
catch
(
InterruptedException
e1
)
{
// ignore
}
long
slept
=
System
.
nanoTime
()
/
1_000_000
-
now
;
if
(
slept
>=
sleep
)
{
break
;
}
}
return
start
==
0
?
now
:
start
;
...
...
h2/src/main/org/h2/command/dml/Select.java
浏览文件 @
16677754
...
...
@@ -594,7 +594,8 @@ public class Select extends Query {
}
}
ArrayList
<
Row
>
forUpdateRows
=
null
;
if
(
isForUpdateMvcc
)
{
boolean
lockRows
=
this
.
isForUpdateMvcc
;
if
(
lockRows
)
{
forUpdateRows
=
Utils
.
newSmallArrayList
();
}
int
sampleSize
=
getSampleSizeValue
(
session
);
...
...
@@ -604,7 +605,7 @@ public class Select extends Query {
return
lazyResult
;
}
while
(
lazyResult
.
next
())
{
if
(
isForUpdateMvcc
)
{
if
(
lockRows
)
{
topTableFilter
.
lockRowAdd
(
forUpdateRows
);
}
result
.
addRow
(
lazyResult
.
currentRow
());
...
...
@@ -613,7 +614,7 @@ public class Select extends Query {
break
;
}
}
if
(
isForUpdateMvcc
)
{
if
(
lockRows
)
{
topTableFilter
.
lockRows
(
forUpdateRows
);
}
return
null
;
...
...
h2/src/main/org/h2/command/dml/Update.java
浏览文件 @
16677754
...
...
@@ -135,6 +135,7 @@ public class Update extends Prepared {
}
newRow
.
setValue
(
i
,
newValue
);
}
newRow
.
setKey
(
oldRow
.
getKey
());
if
(
setOnUpdate
||
updateToCurrentValuesReturnsZero
)
{
setOnUpdate
=
false
;
for
(
int
i
=
0
;
i
<
columnCount
;
i
++)
{
...
...
h2/src/main/org/h2/engine/Database.java
浏览文件 @
16677754
...
...
@@ -45,7 +45,6 @@ import org.h2.schema.SchemaObject;
import
org.h2.schema.Sequence
;
import
org.h2.schema.TriggerObject
;
import
org.h2.security.auth.Authenticator
;
import
org.h2.security.auth.AuthenticatorFactory
;
import
org.h2.store.DataHandler
;
import
org.h2.store.FileLock
;
import
org.h2.store.FileLockMethod
;
...
...
@@ -336,6 +335,12 @@ public class Database implements DataHandler {
}
}
public
int
getLockTimeout
()
{
Setting
setting
=
findSetting
(
SetTypes
.
getTypeName
(
SetTypes
.
DEFAULT_LOCK_TIMEOUT
));
return
setting
==
null
?
Constants
.
INITIAL_LOCK_TIMEOUT
:
setting
.
getIntValue
();
}
/**
* Create a new row for a table.
*
...
...
h2/src/main/org/h2/engine/Session.java
浏览文件 @
16677754
...
...
@@ -23,7 +23,6 @@ import org.h2.command.Parser;
import
org.h2.command.Prepared
;
import
org.h2.command.ddl.Analyze
;
import
org.h2.command.dml.Query
;
import
org.h2.command.dml.SetTypes
;
import
org.h2.constraint.Constraint
;
import
org.h2.index.Index
;
import
org.h2.index.ViewIndex
;
...
...
@@ -64,6 +63,8 @@ import org.h2.value.ValueString;
*/
public
class
Session
extends
SessionWithState
implements
TransactionStore
.
RollbackListener
{
public
enum
State
{
INIT
,
RUNNING
,
BLOCKED
,
SLEEP
,
CLOSED
}
/**
* This special log position means that the log entry has been written.
*/
...
...
@@ -158,6 +159,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
private
ArrayList
<
Value
>
temporaryLobs
;
private
Transaction
transaction
;
private
State
state
=
State
.
INIT
;
private
long
startStatement
=
-
1
;
public
Session
(
Database
database
,
User
user
,
int
id
)
{
...
...
@@ -167,10 +169,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
this
.
undoLog
=
new
UndoLog
(
this
);
this
.
user
=
user
;
this
.
id
=
id
;
Setting
setting
=
database
.
findSetting
(
SetTypes
.
getTypeName
(
SetTypes
.
DEFAULT_LOCK_TIMEOUT
));
this
.
lockTimeout
=
setting
==
null
?
Constants
.
INITIAL_LOCK_TIMEOUT
:
setting
.
getIntValue
();
this
.
lockTimeout
=
database
.
getLockTimeout
();
this
.
currentSchemaName
=
Constants
.
SCHEMA_MAIN
;
this
.
columnNamerConfiguration
=
ColumnNamerConfiguration
.
getDefault
();
}
...
...
@@ -861,6 +860,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
@Override
public
void
close
()
{
if
(!
closed
)
{
state
=
State
.
CLOSED
;
try
{
database
.
checkPowerOff
();
...
...
@@ -874,9 +874,9 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
// and we need to unlock before we call removeSession(), which might
// want to take the meta lock using the system session.
database
.
unlockMeta
(
this
);
database
.
removeSession
(
this
);
}
finally
{
closed
=
true
;
database
.
removeSession
(
this
);
}
}
}
...
...
@@ -1212,11 +1212,15 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
if
(
lastThrottle
+
TimeUnit
.
MILLISECONDS
.
toNanos
(
Constants
.
THROTTLE_DELAY
)
>
time
)
{
return
;
}
State
prevState
=
this
.
state
;
lastThrottle
=
time
+
throttleNs
;
try
{
this
.
state
=
State
.
SLEEP
;
Thread
.
sleep
(
TimeUnit
.
NANOSECONDS
.
toMillis
(
throttleNs
));
}
catch
(
Exception
e
)
{
// ignore InterruptedException
}
finally
{
this
.
state
=
prevState
;
}
}
...
...
@@ -1244,6 +1248,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
long
now
=
System
.
nanoTime
();
cancelAtNs
=
now
+
TimeUnit
.
MILLISECONDS
.
toNanos
(
queryTimeout
);
}
state
=
command
==
null
?
State
.
SLEEP
:
State
.
RUNNING
;
}
/**
...
...
@@ -1633,7 +1638,7 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
public
Value
getTransactionId
()
{
if
(
database
.
getMvStore
()
!=
null
)
{
if
(
transaction
==
null
)
{
if
(
transaction
==
null
||
!
transaction
.
hasChanges
()
)
{
return
ValueNull
.
INSTANCE
;
}
return
ValueString
.
get
(
Long
.
toString
(
getTransaction
().
getSequenceNum
()));
...
...
@@ -1674,14 +1679,14 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
database
.
shutdownImmediately
();
throw
DbException
.
get
(
ErrorCode
.
DATABASE_IS_CLOSED
);
}
transaction
=
store
.
getTransactionStore
().
begin
(
this
);
transaction
=
store
.
getTransactionStore
().
begin
(
this
,
this
.
lockTimeout
,
id
);
}
startStatement
=
-
1
;
}
return
transaction
;
}
p
ublic
long
getStatementSavepoint
()
{
p
rivate
long
getStatementSavepoint
()
{
if
(
startStatement
==
-
1
)
{
startStatement
=
getTransaction
().
setSavepoint
();
}
...
...
@@ -1754,6 +1759,18 @@ public class Session extends SessionWithState implements TransactionStore.Rollba
tablesToAnalyze
.
add
(
table
);
}
public
State
getState
()
{
return
getBlockingSessionId
()
!=
0
?
State
.
BLOCKED
:
state
;
}
public
void
setState
(
State
state
)
{
this
.
state
=
state
;
}
public
int
getBlockingSessionId
()
{
return
transaction
==
null
?
0
:
transaction
.
getBlockerId
();
}
@Override
public
void
onRollback
(
MVMap
<
Object
,
VersionedValue
>
map
,
Object
key
,
VersionedValue
existingValue
,
...
...
h2/src/main/org/h2/jdbc/JdbcConnection.java
浏览文件 @
16677754
...
...
@@ -453,10 +453,10 @@ public class JdbcConnection extends TraceObject
debugCode
(
"setAutoCommit("
+
autoCommit
+
");"
);
}
checkClosed
();
if
(
autoCommit
&&
!
session
.
getAutoCommit
())
{
commit
();
}
synchronized
(
session
)
{
if
(
autoCommit
&&
!
session
.
getAutoCommit
())
{
commit
();
}
session
.
setAutoCommit
(
autoCommit
);
}
}
catch
(
Exception
e
)
{
...
...
h2/src/main/org/h2/mvstore/Cursor.java
浏览文件 @
16677754
...
...
@@ -150,7 +150,7 @@ public class Cursor<K, V> implements Iterator<K> {
* @param p the page to start from
* @param key the key to search, null means search for the first key
*/
p
ublic
static
CursorPos
traverseDown
(
Page
p
,
Object
key
)
{
p
rivate
static
CursorPos
traverseDown
(
Page
p
,
Object
key
)
{
CursorPos
cursorPos
=
null
;
while
(!
p
.
isLeaf
())
{
assert
p
.
getKeyCount
()
>
0
;
...
...
h2/src/main/org/h2/mvstore/DataUtils.java
浏览文件 @
16677754
...
...
@@ -101,6 +101,11 @@ public final class DataUtils {
*/
public
static
final
int
ERROR_TRANSACTION_TOO_BIG
=
104
;
/**
* Deadlock discovered and one of transactions involved chosen as victim and rolled back.
*/
public
static
final
int
ERROR_TRANSACTIONS_DEADLOCK
=
105
;
/**
* The type for leaf page.
*/
...
...
h2/src/main/org/h2/mvstore/MVMap.java
浏览文件 @
16677754
...
...
@@ -131,6 +131,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
*/
@Override
public
V
put
(
K
key
,
V
value
)
{
DataUtils
.
checkArgument
(
value
!=
null
,
"The value may not be null"
);
return
put
(
key
,
value
,
DecisionMaker
.
PUT
);
}
...
...
@@ -142,7 +143,6 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* @return the old value if the key existed, or null otherwise
*/
public
final
V
put
(
K
key
,
V
value
,
DecisionMaker
<?
super
V
>
decisionMaker
)
{
DataUtils
.
checkArgument
(
value
!=
null
,
"The value may not be null"
);
return
operate
(
key
,
value
,
decisionMaker
);
}
...
...
@@ -1382,7 +1382,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
}
}
public
enum
Decision
{
ABORT
,
REMOVE
,
PUT
}
public
enum
Decision
{
ABORT
,
REMOVE
,
PUT
,
REPEAT
}
/**
* Class DecisionMaker provides callback interface (and should become a such in Java 8)
...
...
@@ -1520,6 +1520,9 @@ public class MVMap<K, V> extends AbstractMap<K, V>
boolean
needUnlock
=
false
;
try
{
switch
(
decision
)
{
case
REPEAT:
decisionMaker
.
reset
();
continue
;
case
ABORT:
if
(
rootReference
!=
getRoot
())
{
decisionMaker
.
reset
();
...
...
@@ -1528,6 +1531,10 @@ public class MVMap<K, V> extends AbstractMap<K, V>
return
result
;
case
REMOVE:
{
if
(
index
<
0
)
{
if
(
rootReference
!=
getRoot
())
{
decisionMaker
.
reset
();
continue
;
}
return
null
;
}
if
(
attempt
>
2
&&
!(
needUnlock
=
lockRoot
(
decisionMaker
,
rootReference
,
...
...
h2/src/main/org/h2/mvstore/db/MVPrimaryIndex.java
浏览文件 @
16677754
...
...
@@ -115,12 +115,18 @@ public class MVPrimaryIndex extends BaseIndex {
TransactionMap
<
Value
,
Value
>
map
=
getMap
(
session
);
Value
key
=
ValueLong
.
get
(
row
.
getKey
());
try
{
if
(
map
.
put
(
key
,
ValueArray
.
get
(
row
.
getValueList
()))
!=
null
)
{
Value
oldValue
=
map
.
putIfAbsent
(
key
,
ValueArray
.
get
(
row
.
getValueList
()));
if
(
oldValue
!=
null
)
{
String
sql
=
"PRIMARY KEY ON "
+
table
.
getSQL
();
if
(
mainIndexColumn
>=
0
&&
mainIndexColumn
<
indexColumns
.
length
)
{
sql
+=
"("
+
indexColumns
[
mainIndexColumn
].
getSQL
()
+
")"
;
}
DbException
e
=
DbException
.
get
(
ErrorCode
.
DUPLICATE_KEY_1
,
sql
);
int
errorCode
=
ErrorCode
.
CONCURRENT_UPDATE_1
;
if
(
map
.
get
(
key
)
!=
null
)
{
// committed
errorCode
=
ErrorCode
.
DUPLICATE_KEY_1
;
}
DbException
e
=
DbException
.
get
(
errorCode
,
sql
+
" "
+
oldValue
);
e
.
setSource
(
this
);
throw
e
;
}
...
...
@@ -156,6 +162,18 @@ public class MVPrimaryIndex extends BaseIndex {
}
}
public
void
lockRows
(
Session
session
,
Iterable
<
Row
>
rowsForUpdate
)
{
TransactionMap
<
Value
,
Value
>
map
=
getMap
(
session
);
for
(
Row
row
:
rowsForUpdate
)
{
long
key
=
row
.
getKey
();
try
{
map
.
lock
(
ValueLong
.
get
(
key
));
}
catch
(
IllegalStateException
ex
)
{
throw
mvTable
.
convertException
(
ex
);
}
}
}
@Override
public
Cursor
find
(
Session
session
,
SearchRow
first
,
SearchRow
last
)
{
ValueLong
min
,
max
;
...
...
@@ -410,5 +428,4 @@ public class MVPrimaryIndex extends BaseIndex {
}
}
}
h2/src/main/org/h2/mvstore/db/MVTable.java
浏览文件 @
16677754
...
...
@@ -26,7 +26,6 @@ import org.h2.engine.SysProperties;
import
org.h2.index.Cursor
;
import
org.h2.index.Index
;
import
org.h2.index.IndexType
;
import
org.h2.index.MultiVersionIndex
;
import
org.h2.message.DbException
;
import
org.h2.message.Trace
;
import
org.h2.mvstore.DataUtils
;
...
...
@@ -708,7 +707,11 @@ public class MVTable extends TableBase {
index
.
remove
(
session
,
row
);
}
}
catch
(
Throwable
e
)
{
t
.
rollbackToSavepoint
(
savepoint
);
try
{
t
.
rollbackToSavepoint
(
savepoint
);
}
catch
(
Throwable
nested
)
{
e
.
addSuppressed
(
nested
);
}
throw
DbException
.
convert
(
e
);
}
analyzeIfRequired
(
session
);
...
...
@@ -734,26 +737,21 @@ public class MVTable extends TableBase {
index
.
add
(
session
,
row
);
}
}
catch
(
Throwable
e
)
{
t
.
rollbackToSavepoint
(
savepoint
);
DbException
de
=
DbException
.
convert
(
e
);
if
(
de
.
getErrorCode
()
==
ErrorCode
.
DUPLICATE_KEY_1
)
{
for
(
Index
index
:
indexes
)
{
if
(
index
.
getIndexType
().
isUnique
()
&&
index
instanceof
MultiVersionIndex
)
{
MultiVersionIndex
mv
=
(
MultiVersionIndex
)
index
;
if
(
mv
.
isUncommittedFromOtherSession
(
session
,
row
))
{
throw
DbException
.
get
(
ErrorCode
.
CONCURRENT_UPDATE_1
,
index
.
getName
());
}
}
}
try
{
t
.
rollbackToSavepoint
(
savepoint
);
}
catch
(
Throwable
nested
)
{
e
.
addSuppressed
(
nested
);
}
throw
de
;
throw
DbException
.
convert
(
e
)
;
}
analyzeIfRequired
(
session
);
}
@Override
public
void
lockRows
(
Session
session
,
Iterable
<
Row
>
rowsForUpdate
)
{
primaryIndex
.
lockRows
(
session
,
rowsForUpdate
);
}
private
void
analyzeIfRequired
(
Session
session
)
{
synchronized
(
this
)
{
if
(
nextAnalyze
==
0
||
nextAnalyze
>
changesSinceAnalyze
++)
{
...
...
@@ -919,12 +917,15 @@ public class MVTable extends TableBase {
* @return the database exception
*/
DbException
convertException
(
IllegalStateException
e
)
{
i
f
(
DataUtils
.
getErrorCode
(
e
.
getMessage
())
==
DataUtils
.
ERROR_TRANSACTION_LOCKED
)
{
i
nt
errorCode
=
DataUtils
.
getErrorCode
(
e
.
getMessage
());
if
(
errorCode
==
DataUtils
.
ERROR_TRANSACTION_LOCKED
)
{
throw
DbException
.
get
(
ErrorCode
.
CONCURRENT_UPDATE_1
,
e
,
getName
());
}
if
(
errorCode
==
DataUtils
.
ERROR_TRANSACTIONS_DEADLOCK
)
{
throw
DbException
.
get
(
ErrorCode
.
DEADLOCK_1
,
e
,
getName
());
}
return
store
.
convertIllegalStateException
(
e
);
}
}
h2/src/main/org/h2/mvstore/db/MVTableEngine.java
浏览文件 @
16677754
...
...
@@ -165,7 +165,7 @@ public class MVTableEngine implements TableEngine {
}
this
.
transactionStore
=
new
TransactionStore
(
store
,
new
ValueDataType
(
db
.
getCompareMode
(),
db
,
null
));
new
ValueDataType
(
db
.
getCompareMode
(),
db
,
null
)
,
db
.
getLockTimeout
()
);
// transactionStore.init();
}
catch
(
IllegalStateException
e
)
{
throw
convertIllegalStateException
(
e
);
...
...
h2/src/main/org/h2/mvstore/rtree/MVRTreeMap.java
浏览文件 @
16677754
...
...
@@ -180,6 +180,7 @@ public final class MVRTreeMap<V> extends MVMap<SpatialKey, V> {
result
=
index
<
0
?
null
:
(
V
)
p
.
getValue
(
index
);
Decision
decision
=
decisionMaker
.
decide
(
result
,
value
);
switch
(
decision
)
{
case
REPEAT:
break
;
case
ABORT:
break
;
case
REMOVE:
if
(
index
>=
0
)
{
...
...
h2/src/main/org/h2/mvstore/tx/CommitDecisionMaker.java
0 → 100644
浏览文件 @
16677754
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
mvstore
.
tx
;
import
org.h2.mvstore.MVMap
;
/**
* Class CommitDecisionMaker makes a decision during post-commit processing
* about how to transform uncommitted map entry into committed one,
* based on undo log information.
*
* @author <a href='mailto:andrei.tokar@gmail.com'>Andrei Tokar</a>
*/
final
class
CommitDecisionMaker
extends
MVMap
.
DecisionMaker
<
VersionedValue
>
{
private
long
undoKey
;
private
MVMap
.
Decision
decision
;
void
setUndoKey
(
long
undoKey
)
{
this
.
undoKey
=
undoKey
;
reset
();
}
@Override
public
MVMap
.
Decision
decide
(
VersionedValue
existingValue
,
VersionedValue
providedValue
)
{
assert
decision
==
null
;
if
(
existingValue
==
null
||
// map entry was treated as already committed, and then
// it has been removed by another transaction (commited and closed by now )
existingValue
.
getOperationId
()
!=
undoKey
)
{
// this is not a final undo log entry for this key,
// or map entry was treated as already committed and then
// overwritten by another transaction
// see TxDecisionMaker.decide()
decision
=
MVMap
.
Decision
.
ABORT
;
}
else
/* this is final undo log entry for this key */
if
(
existingValue
.
value
==
null
)
{
decision
=
MVMap
.
Decision
.
REMOVE
;
}
else
{
decision
=
MVMap
.
Decision
.
PUT
;
}
return
decision
;
}
@SuppressWarnings
(
"unchecked"
)
@Override
public
VersionedValue
selectValue
(
VersionedValue
existingValue
,
VersionedValue
providedValue
)
{
assert
decision
==
MVMap
.
Decision
.
PUT
;
assert
existingValue
!=
null
;
return
new
VersionedValue
(
0L
,
existingValue
.
value
);
}
@Override
public
void
reset
()
{
decision
=
null
;
}
@Override
public
String
toString
()
{
return
"commit "
+
TransactionStore
.
getTransactionId
(
undoKey
);
}
}
h2/src/main/org/h2/mvstore/tx/RollbackDecisionMaker.java
0 → 100644
浏览文件 @
16677754
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
mvstore
.
tx
;
import
org.h2.mvstore.MVMap
;
/**
* Class RollbackDecisionMaker process undo log record during transaction rollback.
*
* @author <a href='mailto:andrei.tokar@gmail.com'>Andrei Tokar</a>
*/
final
class
RollbackDecisionMaker
extends
MVMap
.
DecisionMaker
<
Object
[]>
{
private
final
TransactionStore
store
;
private
final
long
transactionId
;
private
final
long
toLogId
;
private
final
TransactionStore
.
RollbackListener
listener
;
private
MVMap
.
Decision
decision
;
RollbackDecisionMaker
(
TransactionStore
store
,
long
transactionId
,
long
toLogId
,
TransactionStore
.
RollbackListener
listener
)
{
this
.
store
=
store
;
this
.
transactionId
=
transactionId
;
this
.
toLogId
=
toLogId
;
this
.
listener
=
listener
;
}
@Override
public
MVMap
.
Decision
decide
(
Object
[]
existingValue
,
Object
[]
providedValue
)
{
assert
decision
==
null
;
assert
existingValue
!=
null
;
VersionedValue
valueToRestore
=
(
VersionedValue
)
existingValue
[
2
];
long
operationId
;
if
(
valueToRestore
==
null
||
(
operationId
=
valueToRestore
.
getOperationId
())
==
0
||
TransactionStore
.
getTransactionId
(
operationId
)
==
transactionId
&&
TransactionStore
.
getLogId
(
operationId
)
<
toLogId
)
{
int
mapId
=
(
Integer
)
existingValue
[
0
];
MVMap
<
Object
,
VersionedValue
>
map
=
store
.
openMap
(
mapId
);
if
(
map
!=
null
&&
!
map
.
isClosed
())
{
Object
key
=
existingValue
[
1
];
VersionedValue
previousValue
=
map
.
operate
(
key
,
valueToRestore
,
MVMap
.
DecisionMaker
.
DEFAULT
);
listener
.
onRollback
(
map
,
key
,
previousValue
,
valueToRestore
);
}
}
decision
=
MVMap
.
Decision
.
REMOVE
;
return
decision
;
}
@Override
public
void
reset
()
{
decision
=
null
;
}
@Override
public
String
toString
()
{
return
"rollback-"
+
transactionId
;
}
}
h2/src/main/org/h2/mvstore/tx/Transaction.java
浏览文件 @
16677754
...
...
@@ -106,19 +106,60 @@ public class Transaction {
*/
private
final
AtomicLong
statusAndLogId
;
/**
* Reference to a counter for an earliest store version used by this transaction.
* Referenced version and all newer ones can not be discarded
* at least until this transaction ends.
*/
private
MVStore
.
TxCounter
txCounter
;
/**
* Transaction name.
*/
private
String
name
;
/**
* Indicates whether this transaction was stored in preparedTransactions map
*/
boolean
wasStored
;
/**
* How long to wait for blocking transaction to commit or rollback.
*/
final
int
timeoutMillis
;
/**
* Identification of the owner of this transaction,
* usually the owner is a database session.
*/
private
final
int
ownerId
;
/**
* Blocking transaction, if any
*/
private
volatile
Transaction
blockingTransaction
;
/**
* Map on which this transaction is blocked.
*/
MVMap
<?,
VersionedValue
>
blockingMap
;
/**
* Key in blockingMap on which this transaction is blocked.
*/
Object
blockingKey
;
Transaction
(
TransactionStore
store
,
int
transactionId
,
long
sequenceNum
,
int
status
,
String
name
,
long
logId
,
TransactionStore
.
RollbackListener
listener
)
{
String
name
,
long
logId
,
int
timeoutMillis
,
int
ownerId
,
TransactionStore
.
RollbackListener
listener
)
{
this
.
store
=
store
;
this
.
transactionId
=
transactionId
;
this
.
sequenceNum
=
sequenceNum
;
this
.
statusAndLogId
=
new
AtomicLong
(
composeState
(
status
,
logId
,
false
));
this
.
name
=
name
;
this
.
timeoutMillis
=
timeoutMillis
;
this
.
ownerId
=
ownerId
;
this
.
listener
=
listener
;
}
...
...
@@ -201,6 +242,10 @@ public class Transaction {
return
name
;
}
public
int
getBlockerId
()
{
return
blockingTransaction
==
null
?
0
:
blockingTransaction
.
ownerId
;
}
/**
* Create a new savepoint.
*
...
...
@@ -217,8 +262,8 @@ public class Transaction {
public
void
markStatementEnd
()
{
MVStore
.
TxCounter
counter
=
txCounter
;
txCounter
=
null
;
if
(
counter
!=
null
)
{
txCounter
=
null
;
store
.
store
.
deregisterVersionUsage
(
counter
);
}
}
...
...
@@ -230,7 +275,7 @@ public class Transaction {
* @param key the key
* @param oldValue the old value
*/
void
log
(
int
mapId
,
Object
key
,
VersionedValue
oldValue
)
{
long
log
(
int
mapId
,
Object
key
,
VersionedValue
oldValue
)
{
long
currentState
=
statusAndLogId
.
getAndIncrement
();
long
logId
=
getLogId
(
currentState
);
if
(
logId
>=
LOG_ID_LIMIT
)
{
...
...
@@ -239,7 +284,10 @@ public class Transaction {
"Transaction {0} has too many changes"
,
transactionId
);
}
store
.
log
(
this
,
logId
,
mapId
,
key
,
oldValue
);
int
currentStatus
=
getStatus
(
currentState
);
checkOpen
(
currentStatus
);
long
undoKey
=
store
.
addUndoLogRecord
(
transactionId
,
logId
,
new
Object
[]{
mapId
,
key
,
oldValue
});
return
undoKey
;
}
/**
...
...
@@ -254,7 +302,9 @@ public class Transaction {
"Transaction {0} has internal error"
,
transactionId
);
}
store
.
logUndo
(
this
,
logId
);
int
currentStatus
=
getStatus
(
currentState
);
checkOpen
(
currentStatus
);
store
.
removeUndoLogRecord
(
transactionId
,
logId
);
}
/**
...
...
@@ -313,11 +363,29 @@ public class Transaction {
*/
public
void
commit
()
{
assert
store
.
openTransactions
.
get
().
get
(
transactionId
);
long
state
=
setStatus
(
STATUS_COMMITTING
);
long
logId
=
Transaction
.
getLogId
(
state
);
boolean
hasChanges
=
hasChanges
(
state
);
store
.
commit
(
this
,
logId
,
hasChanges
);
Throwable
ex
=
null
;
boolean
hasChanges
=
false
;
try
{
long
state
=
setStatus
(
STATUS_COMMITTING
);
hasChanges
=
hasChanges
(
state
);
if
(
hasChanges
)
{
long
logId
=
getLogId
(
state
);
store
.
commit
(
this
,
logId
);
}
}
catch
(
Throwable
e
)
{
ex
=
e
;
throw
e
;
}
finally
{
try
{
store
.
endTransaction
(
this
,
hasChanges
);
}
catch
(
Throwable
e
)
{
if
(
ex
==
null
)
{
throw
e
;
}
else
{
ex
.
addSuppressed
(
e
);
}
}
}
}
/**
...
...
@@ -341,6 +409,7 @@ public class Transaction {
"while rollback to savepoint was in progress"
,
transactionId
);
}
notifyAllWaitingTransactions
();
}
}
...
...
@@ -376,14 +445,89 @@ public class Transaction {
return
getLogId
(
statusAndLogId
.
get
());
}
/**
* Check whether this transaction is open.
*/
private
void
checkOpen
(
int
status
)
{
if
(
status
!=
STATUS_OPEN
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_TRANSACTION_ILLEGAL_STATE
,
"Transaction {0} has status {1}, not open"
,
transactionId
,
status
);
}
}
/**
* Check whether this transaction is open or prepared.
*/
void
checkNotClosed
()
{
if
(
getStatus
()
==
STATUS_CLOSED
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_CLOSED
,
"Transaction is closed"
);
DataUtils
.
ERROR_CLOSED
,
"Transaction {0} is closed"
,
transactionId
);
}
}
void
closeIt
()
{
long
lastState
=
setStatus
(
STATUS_CLOSED
);
store
.
store
.
deregisterVersionUsage
(
txCounter
);
if
(
hasChanges
(
lastState
)
||
hasRollback
(
lastState
))
{
notifyAllWaitingTransactions
();
}
}
private
synchronized
void
notifyAllWaitingTransactions
()
{
notifyAll
();
}
public
boolean
waitFor
(
Transaction
toWaitFor
)
{
if
(
isDeadlocked
(
toWaitFor
))
{
StringBuilder
details
=
new
StringBuilder
(
String
.
format
(
"Transaction %d has been chosen as a deadlock victim. Details:%n"
,
transactionId
));
for
(
Transaction
tx
=
toWaitFor
,
nextTx
;
(
nextTx
=
tx
.
blockingTransaction
)
!=
null
;
tx
=
nextTx
)
{
details
.
append
(
String
.
format
(
"Transaction %d attempts to update map <%s> entry with key <%s> modified by transaction %s%n"
,
tx
.
transactionId
,
tx
.
blockingMap
.
getName
(),
tx
.
blockingKey
,
tx
.
blockingTransaction
));
if
(
nextTx
==
this
)
{
details
.
append
(
String
.
format
(
"Transaction %d attempts to update map <%s> entry with key <%s> modified by transaction %s%n"
,
transactionId
,
blockingMap
.
getName
(),
blockingKey
,
toWaitFor
));
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_TRANSACTIONS_DEADLOCK
,
details
.
toString
());
}
}
}
blockingTransaction
=
toWaitFor
;
try
{
return
toWaitFor
.
waitForThisToEnd
(
timeoutMillis
);
}
finally
{
blockingMap
=
null
;
blockingKey
=
null
;
blockingTransaction
=
null
;
}
}
private
boolean
isDeadlocked
(
Transaction
toWaitFor
)
{
for
(
Transaction
tx
=
toWaitFor
,
nextTx
;
(
nextTx
=
tx
.
blockingTransaction
)
!=
null
&&
tx
.
getStatus
()
==
Transaction
.
STATUS_OPEN
;
tx
=
nextTx
)
{
if
(
nextTx
==
this
)
{
return
true
;
}
}
return
false
;
}
private
synchronized
boolean
waitForThisToEnd
(
int
millis
)
{
long
until
=
System
.
currentTimeMillis
()
+
millis
;
int
status
;
while
((
status
=
getStatus
())
!=
STATUS_CLOSED
&&
status
!=
STATUS_ROLLING_BACK
)
{
long
dur
=
until
-
System
.
currentTimeMillis
();
if
(
dur
<=
0
)
{
return
false
;
}
try
{
wait
(
dur
);
}
catch
(
InterruptedException
ex
)
{
return
false
;
}
}
return
true
;
}
/**
...
...
h2/src/main/org/h2/mvstore/tx/TransactionMap.java
浏览文件 @
16677754
差异被折叠。
点击展开。
h2/src/main/org/h2/mvstore/tx/TransactionStore.java
浏览文件 @
16677754
差异被折叠。
点击展开。
h2/src/main/org/h2/mvstore/tx/TxDecisionMaker.java
0 → 100644
浏览文件 @
16677754
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
mvstore
.
tx
;
import
org.h2.mvstore.MVMap
;
/**
* Class TxDecisionMaker.
*
* @author <a href='mailto:andrei.tokar@gmail.com'>Andrei Tokar</a>
*/
public
abstract
class
TxDecisionMaker
extends
MVMap
.
DecisionMaker
<
VersionedValue
>
{
private
final
int
mapId
;
private
final
Object
key
;
final
Object
value
;
private
final
Transaction
transaction
;
long
undoKey
;
private
Transaction
blockingTransaction
;
private
MVMap
.
Decision
decision
;
TxDecisionMaker
(
int
mapId
,
Object
key
,
Object
value
,
Transaction
transaction
)
{
this
.
mapId
=
mapId
;
this
.
key
=
key
;
this
.
value
=
value
;
this
.
transaction
=
transaction
;
}
@Override
public
MVMap
.
Decision
decide
(
VersionedValue
existingValue
,
VersionedValue
providedValue
)
{
assert
decision
==
null
;
long
id
;
int
blockingId
;
// if map does not have that entry yet
if
(
existingValue
==
null
||
// or entry is a committed one
(
id
=
existingValue
.
getOperationId
())
==
0
||
// or it came from the same transaction
isThisTransaction
(
blockingId
=
TransactionStore
.
getTransactionId
(
id
)))
{
logIt
(
existingValue
);
decision
=
MVMap
.
Decision
.
PUT
;
}
else
if
(
isCommitted
(
blockingId
))
{
// Condition above means that entry belongs to a committing transaction.
// We assume that we are looking at the final value for this transaction,
// and if it's not the case, then it will fail later,
// because a tree root has definitely been changed.
logIt
(
existingValue
.
value
==
null
?
null
:
new
VersionedValue
(
0L
,
existingValue
.
value
));
decision
=
MVMap
.
Decision
.
PUT
;
}
else
if
(
fetchTransaction
(
blockingId
)
==
null
)
{
// condition above means transaction has been committed/rplled back and closed by now
decision
=
MVMap
.
Decision
.
REPEAT
;
}
else
{
// this entry comes from a different transaction, and this transaction is not committed yet
// should wait on blockingTransaction that was determined earlier
decision
=
MVMap
.
Decision
.
ABORT
;
}
return
decision
;
}
@Override
public
final
void
reset
()
{
if
(
decision
!=
null
&&
decision
!=
MVMap
.
Decision
.
ABORT
&&
decision
!=
MVMap
.
Decision
.
REPEAT
)
{
// positive decision has been made already and undo record created,
// but map was updated afterwards and undo record deletion required
transaction
.
logUndo
();
}
blockingTransaction
=
null
;
decision
=
null
;
}
public
final
MVMap
.
Decision
getDecision
()
{
return
decision
;
}
final
Transaction
getBlockingTransaction
()
{
return
blockingTransaction
;
}
final
void
logIt
(
VersionedValue
value
)
{
undoKey
=
transaction
.
log
(
mapId
,
key
,
value
);
}
final
boolean
isThisTransaction
(
int
transactionId
)
{
return
transactionId
==
transaction
.
transactionId
;
}
final
boolean
isCommitted
(
int
transactionId
)
{
return
transaction
.
store
.
committingTransactions
.
get
().
get
(
transactionId
);
}
final
Transaction
fetchTransaction
(
int
transactionId
)
{
return
(
blockingTransaction
=
transaction
.
store
.
getTransaction
(
transactionId
));
}
final
MVMap
.
Decision
setDecision
(
MVMap
.
Decision
d
)
{
return
decision
=
d
;
}
@Override
public
final
String
toString
()
{
return
"txdm "
+
transaction
.
transactionId
;
}
public
static
class
PutDecisionMaker
extends
TxDecisionMaker
{
PutDecisionMaker
(
int
mapId
,
Object
key
,
Object
value
,
Transaction
transaction
)
{
super
(
mapId
,
key
,
value
,
transaction
);
}
@SuppressWarnings
(
"unchecked"
)
@Override
public
final
VersionedValue
selectValue
(
VersionedValue
existingValue
,
VersionedValue
providedValue
)
{
return
new
VersionedValue
(
undoKey
,
value
);
}
}
public
static
final
class
PutIfAbsentDecisionMaker
extends
PutDecisionMaker
{
PutIfAbsentDecisionMaker
(
int
mapId
,
Object
key
,
Object
value
,
Transaction
transaction
)
{
super
(
mapId
,
key
,
value
,
transaction
);
}
@Override
public
MVMap
.
Decision
decide
(
VersionedValue
existingValue
,
VersionedValue
providedValue
)
{
assert
getDecision
()
==
null
;
int
blockingId
;
// if map does not have that entry yet
if
(
existingValue
==
null
)
{
logIt
(
null
);
return
setDecision
(
MVMap
.
Decision
.
PUT
);
}
else
{
long
id
=
existingValue
.
getOperationId
();
if
(
id
==
0
// entry is a committed one
// or it came from the same transaction
||
isThisTransaction
(
blockingId
=
TransactionStore
.
getTransactionId
(
id
)))
{
if
(
existingValue
.
value
!=
null
)
{
return
setDecision
(
MVMap
.
Decision
.
ABORT
);
}
logIt
(
existingValue
);
return
setDecision
(
MVMap
.
Decision
.
PUT
);
}
else
if
(
isCommitted
(
blockingId
)
&&
existingValue
.
value
==
null
)
{
// entry belongs to a committing transaction
// and therefore will be committed soon
logIt
(
null
);
return
setDecision
(
MVMap
.
Decision
.
PUT
);
}
else
if
(
fetchTransaction
(
blockingId
)
==
null
)
{
// map already has specified key from uncommitted
// at the time transaction, which is closed by now
// we can retry right away
return
setDecision
(
MVMap
.
Decision
.
REPEAT
);
}
else
{
// map already has specified key from uncommitted transaction
// we need to wait for it to close and then try again
return
setDecision
(
MVMap
.
Decision
.
ABORT
);
}
}
}
}
public
static
final
class
LockDecisionMaker
extends
TxDecisionMaker
{
LockDecisionMaker
(
int
mapId
,
Object
key
,
Transaction
transaction
)
{
super
(
mapId
,
key
,
null
,
transaction
);
}
@SuppressWarnings
(
"unchecked"
)
@Override
public
VersionedValue
selectValue
(
VersionedValue
existingValue
,
VersionedValue
providedValue
)
{
return
new
VersionedValue
(
undoKey
,
existingValue
==
null
?
null
:
existingValue
.
value
);
}
}
}
h2/src/main/org/h2/mvstore/tx/VersionedValue.java
浏览文件 @
16677754
...
...
@@ -16,6 +16,8 @@ import java.nio.ByteBuffer;
*/
public
class
VersionedValue
{
public
static
final
VersionedValue
DUMMY
=
new
VersionedValue
(
0L
,
new
Object
());
/**
* The operation id.
*/
...
...
h2/src/main/org/h2/table/MetaTable.java
浏览文件 @
16677754
...
...
@@ -506,7 +506,9 @@ public class MetaTable extends Table {
"SESSION_START"
,
"STATEMENT"
,
"STATEMENT_START"
,
"CONTAINS_UNCOMMITTED"
"CONTAINS_UNCOMMITTED"
,
"STATE"
,
"BLOCKER_ID INT"
);
break
;
}
...
...
@@ -1078,6 +1080,8 @@ public class MetaTable extends Table {
Long
.
toString
(
fs
.
getWriteCount
()));
add
(
rows
,
"info.FILE_READ"
,
Long
.
toString
(
fs
.
getReadCount
()));
int
updateFailureRatio
=
(
int
)(
10000
*
mvStore
.
getStore
().
getUpdateFailureRatio
());
add
(
rows
,
"info.UPDATE_FAILURE_PERCENT"
,
""
+
updateFailureRatio
/
100
+
"."
+
updateFailureRatio
%
100
+
"%"
);
long
size
;
try
{
size
=
fs
.
getFile
().
size
();
...
...
@@ -1822,6 +1826,7 @@ public class MetaTable extends Table {
if
(
start
==
0
)
{
start
=
now
;
}
int
blockingSessionId
=
s
.
getBlockingSessionId
();
add
(
rows
,
// ID
Integer
.
toString
(
s
.
getId
()),
...
...
@@ -1834,7 +1839,11 @@ public class MetaTable extends Table {
// STATEMENT_START
new
Timestamp
(
start
).
toString
(),
// CONTAINS_UNCOMMITTED
Boolean
.
toString
(
s
.
containsUncommitted
())
Boolean
.
toString
(
s
.
containsUncommitted
()),
// STATE
String
.
valueOf
(
s
.
getState
()),
// BLOCKER_ID INT
blockingSessionId
==
0
?
null
:
String
.
valueOf
(
blockingSessionId
)
);
}
}
...
...
h2/src/main/org/h2/table/Table.java
浏览文件 @
16677754
...
...
@@ -177,6 +177,22 @@ public abstract class Table extends SchemaObjectBase {
*/
public
abstract
void
removeRow
(
Session
session
,
Row
row
);
/**
* Locks rows, preventing any updated to them, except from the session specified.
*
* @param session the session
* @param rowsForUpdate rows to lock
*/
public
void
lockRows
(
Session
session
,
Iterable
<
Row
>
rowsForUpdate
)
{
for
(
Row
row
:
rowsForUpdate
)
{
Row
newRow
=
row
.
getCopy
();
removeRow
(
session
,
row
);
session
.
log
(
this
,
UndoLogRecord
.
DELETE
,
row
);
addRow
(
session
,
newRow
);
session
.
log
(
this
,
UndoLogRecord
.
INSERT
,
newRow
);
}
}
/**
* Remove all rows from the table and indexes.
*
...
...
@@ -493,7 +509,7 @@ public abstract class Table extends SchemaObjectBase {
try
{
removeRow
(
session
,
o
);
}
catch
(
DbException
e
)
{
if
(
e
.
getErrorCode
()
==
ErrorCode
.
CONCURRENT_UPDATE_1
)
{
if
(
e
.
getErrorCode
()
==
ErrorCode
.
CONCURRENT_UPDATE_1
||
e
.
getErrorCode
()
==
ErrorCode
.
ROW_NOT_FOUND_WHEN_DELETING_1
)
{
session
.
rollbackTo
(
rollback
,
false
);
session
.
startStatementWithinTransaction
();
rollback
=
session
.
setSavepoint
();
...
...
h2/src/main/org/h2/table/TableFilter.java
浏览文件 @
16677754
...
...
@@ -15,7 +15,6 @@ import org.h2.command.dml.Select;
import
org.h2.engine.Right
;
import
org.h2.engine.Session
;
import
org.h2.engine.SysProperties
;
import
org.h2.engine.UndoLogRecord
;
import
org.h2.expression.Comparison
;
import
org.h2.expression.ConditionAndOr
;
import
org.h2.expression.Expression
;
...
...
@@ -1158,14 +1157,8 @@ public class TableFilter implements ColumnResolver {
*
* @param forUpdateRows the rows to lock
*/
public
void
lockRows
(
ArrayList
<
Row
>
forUpdateRows
)
{
for
(
Row
row
:
forUpdateRows
)
{
Row
newRow
=
row
.
getCopy
();
table
.
removeRow
(
session
,
row
);
session
.
log
(
table
,
UndoLogRecord
.
DELETE
,
row
);
table
.
addRow
(
session
,
newRow
);
session
.
log
(
table
,
UndoLogRecord
.
INSERT
,
newRow
);
}
public
void
lockRows
(
Iterable
<
Row
>
forUpdateRows
)
{
table
.
lockRows
(
session
,
forUpdateRows
);
}
public
TableFilter
getNestedJoin
()
{
...
...
h2/src/test/org/h2/test/TestBase.java
浏览文件 @
16677754
...
...
@@ -151,7 +151,7 @@ public abstract class TestBase {
}
}
catch
(
Throwable
e
)
{
println
(
"FAIL "
+
e
.
toString
());
logError
(
"FAIL "
+
e
.
toString
(),
e
);
logError
(
"FAIL
("
+
conf
+
")
"
+
e
.
toString
(),
e
);
if
(
config
.
stopOnError
)
{
throw
new
AssertionError
(
"ERROR"
);
}
...
...
h2/src/test/org/h2/test/db/TestIndex.java
浏览文件 @
16677754
...
...
@@ -51,7 +51,13 @@ public class TestIndex extends TestBase {
testHashIndexOnMemoryTable
();
testErrorMessage
();
testDuplicateKeyException
();
testConcurrentUpdate
();
int
to
=
config
.
lockTimeout
;
config
.
lockTimeout
=
50000
;
try
{
testConcurrentUpdate
();
}
finally
{
config
.
lockTimeout
=
to
;
}
testNonUniqueHashIndex
();
testRenamePrimaryKey
();
testRandomized
();
...
...
h2/src/test/org/h2/test/db/TestOptimizations.java
浏览文件 @
16677754
...
...
@@ -296,7 +296,7 @@ public class TestOptimizations extends TestBase {
assertEquals
(
11
,
rs
.
getInt
(
1
));
assertEquals
(
"World"
,
rs
.
getString
(
2
));
rs
.
next
();
assertEquals
(
2
1
,
rs
.
getInt
(
1
));
assertEquals
(
2
0
,
rs
.
getInt
(
1
));
assertEquals
(
"Hallo"
,
rs
.
getString
(
2
));
assertFalse
(
rs
.
next
());
stat
.
execute
(
"drop table test"
);
...
...
h2/src/test/org/h2/test/db/TestTriggersConstraints.java
浏览文件 @
16677754
...
...
@@ -96,7 +96,11 @@ public class TestTriggersConstraints extends TestBase implements Trigger {
stat
.
execute
(
"update test2 set id = 3"
);
task
.
get
();
}
catch
(
SQLException
e
)
{
assertEquals
(
ErrorCode
.
LOCK_TIMEOUT_1
,
e
.
getErrorCode
());
int
errorCode
=
e
.
getErrorCode
();
assertTrue
(
String
.
valueOf
(
errorCode
),
ErrorCode
.
LOCK_TIMEOUT_1
==
errorCode
||
ErrorCode
.
DEADLOCK_1
==
errorCode
||
ErrorCode
.
COMMIT_ROLLBACK_NOT_ALLOWED
==
errorCode
);
}
conn2
.
rollback
();
conn
.
rollback
();
...
...
h2/src/test/org/h2/test/mvcc/TestMvcc4.java
浏览文件 @
16677754
...
...
@@ -11,7 +11,6 @@ import java.sql.ResultSet;
import
java.sql.SQLException
;
import
java.sql.Statement
;
import
java.sql.Timestamp
;
import
java.util.Map
;
import
java.util.concurrent.CountDownLatch
;
import
org.h2.test.TestBase
;
...
...
@@ -42,7 +41,8 @@ public class TestMvcc4 extends TestBase {
}
private
void
testSelectForUpdateAndUpdateConcurrency
()
throws
SQLException
{
Connection
setup
=
getConnection
(
"mvcc4"
);
deleteDb
(
"mvcc4"
);
Connection
setup
=
getConnection
(
"mvcc4;MULTI_THREADED=TRUE"
);
setup
.
setAutoCommit
(
false
);
{
...
...
@@ -82,7 +82,13 @@ public class TestMvcc4 extends TestBase {
ps
.
executeQuery
().
next
();
executedUpdate
.
countDown
();
waitForThreadToBlockOnDB
(
mainThread
);
// interrogate new "blocker_id" metatable field instead of
// relying on stacktraces!? to determine when session is blocking
PreparedStatement
stmt
=
c2
.
prepareStatement
(
"SELECT * FROM INFORMATION_SCHEMA.SESSIONS WHERE BLOCKER_ID = SESSION_ID()"
);
ResultSet
resultSet
;
do
{
resultSet
=
stmt
.
executeQuery
();
}
while
(!
resultSet
.
next
());
c2
.
commit
();
c2
.
close
();
...
...
@@ -103,7 +109,7 @@ public class TestMvcc4 extends TestBase {
// for lock case.
PreparedStatement
ps
=
c1
.
prepareStatement
(
"UPDATE test SET lastUpdated = ?"
);
ps
.
setTimestamp
(
1
,
new
Timestamp
(
System
.
currentTimeMillis
()));
ps
.
executeUpdate
(
);
assertEquals
(
2
,
ps
.
executeUpdate
()
);
c1
.
commit
();
c1
.
close
();
...
...
@@ -114,44 +120,12 @@ public class TestMvcc4 extends TestBase {
ps
=
verify
.
prepareStatement
(
"SELECT COUNT(*) FROM test"
);
ResultSet
rs
=
ps
.
executeQuery
();
assertTrue
(
rs
.
next
());
assert
True
(
rs
.
getInt
(
1
)
==
2
);
assert
Equals
(
2
,
rs
.
getInt
(
1
)
);
verify
.
commit
();
verify
.
close
();
setup
.
close
();
}
/**
* Wait for the given thread to block on synchronizing on the database
* object.
*
* @param t the thread
*/
void
waitForThreadToBlockOnDB
(
Thread
t
)
{
while
(
true
)
{
// sleep the first time through the loop so we give the main thread
// a chance
try
{
Thread
.
sleep
(
20
);
}
catch
(
InterruptedException
e1
)
{
// ignore
}
// TODO must not use getAllStackTraces, as the method names are
// implementation details
Map
<
Thread
,
StackTraceElement
[]>
threadMap
=
Thread
.
getAllStackTraces
();
StackTraceElement
[]
elements
=
threadMap
.
get
(
t
);
if
(
elements
!=
null
&&
elements
.
length
>
1
&&
(
config
.
multiThreaded
?
"sleep"
.
equals
(
elements
[
0
]
.
getMethodName
())
:
"wait"
.
equals
(
elements
[
0
]
.
getMethodName
()))
&&
"filterConcurrentUpdate"
.
equals
(
elements
[
1
].
getMethodName
()))
{
return
;
}
}
}
}
...
...
h2/src/test/org/h2/test/mvcc/TestMvccMultiThreaded.java
浏览文件 @
16677754
...
...
@@ -7,10 +7,9 @@ package org.h2.test.mvcc;
import
java.sql.Connection
;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.sql.Statement
;
import
java.util.ArrayList
;
import
java.util.concurrent.C
ountDownLatch
;
import
java.util.concurrent.C
yclicBarrier
;
import
org.h2.api.ErrorCode
;
import
org.h2.test.TestBase
;
import
org.h2.util.Task
;
...
...
@@ -36,11 +35,8 @@ public class TestMvccMultiThreaded extends TestBase {
}
testConcurrentSelectForUpdate
();
testMergeWithUniqueKeyViolation
();
// not supported currently
if
(!
config
.
multiThreaded
)
{
testConcurrentMerge
();
testConcurrentUpdate
();
}
testConcurrentMerge
();
testConcurrentUpdate
();
}
private
void
testConcurrentSelectForUpdate
()
throws
Exception
{
...
...
@@ -55,21 +51,11 @@ public class TestMvccMultiThreaded extends TestBase {
Task
task
=
new
Task
()
{
@Override
public
void
call
()
throws
Exception
{
Connection
conn
=
getConnection
(
getTestName
());
Statement
stat
=
conn
.
createStatement
();
try
{
try
(
Connection
conn
=
getConnection
(
getTestName
()))
{
Statement
stat
=
conn
.
createStatement
();
while
(!
stop
)
{
try
{
stat
.
execute
(
"select * from test where id=1 for update"
);
}
catch
(
SQLException
e
)
{
int
errorCode
=
e
.
getErrorCode
();
assertTrue
(
e
.
getMessage
(),
errorCode
==
ErrorCode
.
DEADLOCK_1
||
errorCode
==
ErrorCode
.
LOCK_TIMEOUT_1
);
}
stat
.
execute
(
"select * from test where id=1 for update"
);
}
}
finally
{
conn
.
close
();
}
}
}.
execute
();
...
...
@@ -113,7 +99,6 @@ public class TestMvccMultiThreaded extends TestBase {
conn
.
createStatement
().
execute
(
"create table test(id int primary key, name varchar)"
);
Task
[]
tasks
=
new
Task
[
len
];
final
boolean
[]
stop
=
{
false
};
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
final
Connection
c
=
connList
[
i
];
c
.
setAutoCommit
(
false
);
...
...
@@ -124,14 +109,12 @@ public class TestMvccMultiThreaded extends TestBase {
c
.
createStatement
().
execute
(
"merge into test values(1, 'x')"
);
c
.
commit
();
Thread
.
sleep
(
1
);
}
}
};
tasks
[
i
].
execute
();
}
Thread
.
sleep
(
1000
);
stop
[
0
]
=
true
;
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
tasks
[
i
].
get
();
}
...
...
@@ -157,18 +140,24 @@ public class TestMvccMultiThreaded extends TestBase {
final
int
count
=
1000
;
Task
[]
tasks
=
new
Task
[
len
];
final
C
ountDownLatch
latch
=
new
CountDownLatch
(
len
);
final
C
yclicBarrier
barrier
=
new
CyclicBarrier
(
len
);
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
final
int
x
=
i
;
// Recent changes exposed a race condition in this test itself.
// Without preliminary record locking, counter will be off.
connList
[
x
].
setAutoCommit
(
false
);
tasks
[
i
]
=
new
Task
()
{
@Override
public
void
call
()
throws
Exception
{
for
(
int
a
=
0
;
a
<
count
;
a
++)
{
ResultSet
rs
=
connList
[
x
].
createStatement
().
executeQuery
(
"select value from test for update"
);
assertTrue
(
rs
.
next
());
connList
[
x
].
createStatement
().
execute
(
"update test set value=value+1"
);
latch
.
countDown
();
latch
.
await
();
connList
[
x
].
commit
();
barrier
.
await
();
}
}
};
...
...
h2/src/test/org/h2/test/unit/TestAutoReconnect.java
浏览文件 @
16677754
...
...
@@ -74,8 +74,8 @@ public class TestAutoReconnect extends TestBase {
"/"
+
getTestName
()
+
";OPEN_NEW=TRUE"
);
conn
.
close
();
conn
=
getConnection
(
"jdbc:h2:tcp://localhost
/"
+
getBaseDir
()
+
"/"
+
getTestName
());
conn
=
getConnection
(
"jdbc:h2:tcp://localhost
:"
+
tcp
.
getPort
()
+
"/"
+
getBaseDir
()
+
"/"
+
getTestName
());
assertThrows
(
ErrorCode
.
DATABASE_ALREADY_OPEN_1
,
this
).
getConnection
(
"jdbc:h2:"
+
getBaseDir
()
+
"/"
+
getTestName
()
+
";AUTO_SERVER=TRUE;OPEN_NEW=TRUE"
);
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论