Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
16677754
Unverified
提交
16677754
authored
7 年前
作者:
Andrei Tokar
提交者:
GitHub
7 年前
浏览文件
操作
浏览文件
下载
差异文件
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
;
...
...
This diff is collapsed.
Click to expand it.
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
;
...
...
This diff is collapsed.
Click to expand it.
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
++)
{
...
...
This diff is collapsed.
Click to expand it.
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.
*
...
...
This diff is collapsed.
Click to expand it.
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
,
...
...
This diff is collapsed.
Click to expand it.
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
)
{
...
...
This diff is collapsed.
Click to expand it.
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
;
...
...
This diff is collapsed.
Click to expand it.
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.
*/
...
...
This diff is collapsed.
Click to expand it.
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
,
...
...
This diff is collapsed.
Click to expand it.
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 {
}
}
}
This diff is collapsed.
Click to expand it.
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
);
}
}
This diff is collapsed.
Click to expand it.
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
);
...
...
This diff is collapsed.
Click to expand it.
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
)
{
...
...
This diff is collapsed.
Click to expand it.
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
);
}
}
This diff is collapsed.
Click to expand it.
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
;
}
}
This diff is collapsed.
Click to expand it.
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
;
}
/**
...
...
This diff is collapsed.
Click to expand it.
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
);
}
}
}
This diff is collapsed.
Click to expand it.
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.
*/
...
...
This diff is collapsed.
Click to expand it.
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
)
);
}
}
...
...
This diff is collapsed.
Click to expand it.
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
();
...
...
This diff is collapsed.
Click to expand it.
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
()
{
...
...
This diff is collapsed.
Click to expand it.
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"
);
}
...
...
This diff is collapsed.
Click to expand it.
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
();
...
...
This diff is collapsed.
Click to expand it.
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"
);
...
...
This diff is collapsed.
Click to expand it.
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
();
...
...
This diff is collapsed.
Click to expand it.
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
;
}
}
}
}
...
...
This diff is collapsed.
Click to expand it.
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
();
}
}
};
...
...
This diff is collapsed.
Click to expand it.
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"
);
...
...
This diff is collapsed.
Click to expand it.
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论