Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
e8f0a660
提交
e8f0a660
authored
1月 28, 2013
作者:
Thomas Mueller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
MVStore: various changes (store temporary, store committed and rollback on close)
上级
cfb047bd
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
6 个修改的文件
包含
527 行增加
和
108 行删除
+527
-108
Chunk.java
h2/src/main/org/h2/mvstore/Chunk.java
+1
-1
MVMap.java
h2/src/main/org/h2/mvstore/MVMap.java
+14
-17
MVMapConcurrent.java
h2/src/main/org/h2/mvstore/MVMapConcurrent.java
+1
-1
MVStore.java
h2/src/main/org/h2/mvstore/MVStore.java
+243
-83
TestMVStore.java
h2/src/test/org/h2/test/store/TestMVStore.java
+113
-6
TestSpinLock.java
h2/src/test/org/h2/test/store/TestSpinLock.java
+155
-0
没有找到文件。
h2/src/main/org/h2/mvstore/Chunk.java
浏览文件 @
e8f0a660
...
...
@@ -72,7 +72,7 @@ public class Chunk {
long
version
;
/**
* When this chunk was created, in seconds after the store was created.
* When this chunk was created, in
milli
seconds after the store was created.
*/
long
time
;
...
...
h2/src/main/org/h2/mvstore/MVMap.java
浏览文件 @
e8f0a660
...
...
@@ -48,8 +48,10 @@ public class MVMap<K, V> extends AbstractMap<K, V>
private
boolean
closed
;
private
boolean
readOnly
;
/**
* This flag is set during a write operation to the tree.
*/
private
volatile
boolean
writing
;
private
volatile
int
writeCount
;
protected
MVMap
(
DataType
keyType
,
DataType
valueType
)
{
this
.
keyType
=
keyType
;
...
...
@@ -918,26 +920,21 @@ public class MVMap<K, V> extends AbstractMap<K, V>
}
/**
* This method is called after writing to the map.
* This method is called after writing to the map (whether or not the write
* operation was successful).
*/
protected
void
afterWrite
()
{
writeCount
++;
writing
=
false
;
}
void
waitUntilWritten
(
long
version
)
{
if
(
root
.
getVersion
()
<
version
)
{
// a write will create a new version
return
;
}
// wait until writing is done,
// but only for the current write operation
// a bit like a spin lock
int
w
=
writeCount
;
while
(
writing
)
{
if
(
writeCount
>
w
)
{
return
;
}
/**
* If there is a concurrent update to the given version, wait until it is
* finished.
*
* @param root the root page
*/
protected
void
waitUntilWritten
(
Page
root
)
{
while
(
writing
&&
root
==
this
.
root
)
{
Thread
.
yield
();
}
}
...
...
@@ -967,7 +964,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
/**
* Remove the given page (make the space available).
*
* @param p
the pag
e
* @param p
os the position of the page to remov
e
*/
protected
void
removePage
(
long
pos
)
{
store
.
removePage
(
this
,
pos
);
...
...
h2/src/main/org/h2/mvstore/MVMapConcurrent.java
浏览文件 @
e8f0a660
...
...
@@ -52,7 +52,7 @@ public class MVMapConcurrent<K, V> extends MVMap<K, V> {
}
}
void
waitUntilWritten
(
long
version
)
{
protected
void
waitUntilWritten
(
Page
root
)
{
// no need to wait
}
...
...
h2/src/main/org/h2/mvstore/MVStore.java
浏览文件 @
e8f0a660
差异被折叠。
点击展开。
h2/src/test/org/h2/test/store/TestMVStore.java
浏览文件 @
e8f0a660
...
...
@@ -42,6 +42,8 @@ public class TestMVStore extends TestBase {
FileUtils
.
deleteRecursive
(
getBaseDir
(),
true
);
FileUtils
.
createDirectories
(
getBaseDir
());
testWriteBuffer
();
testWriteDelay
();
testEncryptedFile
();
testFileFormatChange
();
testRecreateMap
();
...
...
@@ -77,6 +79,107 @@ public class TestMVStore extends TestBase {
testSimple
();
}
private
void
testWriteBuffer
()
throws
IOException
{
String
fileName
=
getBaseDir
()
+
"/testAutoStoreBuffer.h3"
;
FileUtils
.
delete
(
fileName
);
MVStore
s
;
MVMap
<
Integer
,
byte
[]>
m
;
byte
[]
data
=
new
byte
[
1000
];
long
lastSize
=
0
;
int
len
=
1000
;
for
(
int
bs
=
0
;
bs
<=
1
;
bs
++)
{
s
=
new
MVStore
.
Builder
().
fileName
(
fileName
).
writeBufferSize
(
bs
).
open
();
m
=
s
.
openMap
(
"data"
);
for
(
int
i
=
0
;
i
<
len
;
i
++)
{
m
.
put
(
i
,
data
);
}
long
size
=
s
.
getFile
().
size
();
assertTrue
(
"last:"
+
lastSize
+
" now: "
+
size
,
size
>
lastSize
);
lastSize
=
size
;
s
.
close
();
}
s
=
new
MVStore
.
Builder
().
fileName
(
fileName
).
open
();
m
=
s
.
openMap
(
"data"
);
assertFalse
(
m
.
containsKey
(
1
));
m
.
put
(
1
,
data
);
s
.
commit
();
m
.
put
(
2
,
data
);
s
.
close
();
s
=
new
MVStore
.
Builder
().
fileName
(
fileName
).
open
();
m
=
s
.
openMap
(
"data"
);
assertTrue
(
m
.
containsKey
(
1
));
assertFalse
(
m
.
containsKey
(
2
));
s
.
close
();
FileUtils
.
delete
(
fileName
);
}
private
void
testWriteDelay
()
throws
InterruptedException
{
String
fileName
=
getBaseDir
()
+
"/testUndoTempStore.h3"
;
FileUtils
.
delete
(
fileName
);
MVStore
s
;
MVMap
<
Integer
,
String
>
m
;
s
=
new
MVStore
.
Builder
().
writeDelay
(
1
).
fileName
(
fileName
).
open
();
m
=
s
.
openMap
(
"data"
);
m
.
put
(
1
,
"Hello"
);
s
.
store
();
long
v
=
s
.
getCurrentVersion
();
m
.
put
(
2
,
"World"
);
Thread
.
sleep
(
5
);
// must not store, as nothing has been committed yet
assertEquals
(
v
,
s
.
getCurrentVersion
());
s
.
commit
();
m
.
put
(
3
,
"!"
);
for
(
int
i
=
100
;
i
>
0
;
i
--)
{
if
(
s
.
getCurrentVersion
()
>
v
)
{
break
;
}
if
(
i
<
10
)
{
fail
();
}
Thread
.
sleep
(
1
);
}
s
.
close
();
s
=
new
MVStore
.
Builder
().
fileName
(
fileName
).
open
();
m
=
s
.
openMap
(
"data"
);
assertEquals
(
"Hello"
,
m
.
get
(
1
));
assertEquals
(
"World"
,
m
.
get
(
2
));
assertFalse
(
m
.
containsKey
(
3
));
String
data
=
new
String
(
new
char
[
1000
]).
replace
((
char
)
0
,
'x'
);
for
(
int
i
=
0
;
i
<
1000
;
i
++)
{
m
.
put
(
i
,
data
);
}
s
.
close
();
s
=
new
MVStore
.
Builder
().
fileName
(
fileName
).
open
();
m
=
s
.
openMap
(
"data"
);
assertEquals
(
"Hello"
,
m
.
get
(
1
));
assertEquals
(
"World"
,
m
.
get
(
2
));
assertFalse
(
m
.
containsKey
(
3
));
s
.
close
();
FileUtils
.
delete
(
fileName
);
}
private
void
testEncryptedFile
()
{
String
fileName
=
getBaseDir
()
+
"/testEncryptedFile.h3"
;
FileUtils
.
delete
(
fileName
);
...
...
@@ -175,6 +278,8 @@ public class TestMVStore extends TestBase {
assertEquals
(
"world"
,
map
.
getName
());
s
.
rollbackTo
(
old
);
assertEquals
(
"hello"
,
map
.
getName
());
s
.
rollbackTo
(
0
);
assertTrue
(
map
.
isClosed
());
s
.
close
();
}
...
...
@@ -211,7 +316,7 @@ public class TestMVStore extends TestBase {
for
(
int
cacheSize
=
0
;
cacheSize
<=
6
;
cacheSize
+=
4
)
{
s
=
new
MVStore
.
Builder
().
fileName
(
fileName
).
cacheSize
MB
(
1
+
3
*
cacheSize
).
open
();
cacheSize
(
1
+
3
*
cacheSize
).
open
();
map
=
s
.
openMap
(
"test"
);
for
(
int
i
=
0
;
i
<
1024
;
i
+=
128
)
{
for
(
int
j
=
0
;
j
<
i
;
j
++)
{
...
...
@@ -253,11 +358,11 @@ public class TestMVStore extends TestBase {
private
void
testFileHeader
()
{
String
fileName
=
getBaseDir
()
+
"/testFileHeader.h3"
;
MVStore
s
=
openStore
(
fileName
);
long
time
=
System
.
currentTimeMillis
()
/
1000
;
long
time
=
System
.
currentTimeMillis
();
assertEquals
(
"3"
,
s
.
getFileHeader
().
get
(
"H"
));
long
creationTime
=
Long
.
parseLong
(
s
.
getFileHeader
()
.
get
(
"creationTime"
));
assertTrue
(
Math
.
abs
(
time
-
creationTime
)
<
5
);
assertTrue
(
Math
.
abs
(
time
-
creationTime
)
<
100
);
s
.
getFileHeader
().
put
(
"test"
,
"123"
);
MVMap
<
Integer
,
Integer
>
map
=
s
.
openMap
(
"test"
);
map
.
put
(
10
,
100
);
...
...
@@ -274,6 +379,7 @@ public class TestMVStore extends TestBase {
MVMap
<
Integer
,
Integer
>
map
=
s
.
openMap
(
"test"
);
map
.
put
(
10
,
100
);
FilePath
f
=
FilePath
.
get
(
s
.
getFileName
());
s
.
store
();
s
.
close
();
int
blockSize
=
4
*
1024
;
// test corrupt file headers
...
...
@@ -299,6 +405,7 @@ public class TestMVStore extends TestBase {
// header should be used
s
=
openStore
(
fileName
);
map
=
s
.
openMap
(
"test"
);
assertEquals
(
100
,
map
.
get
(
10
).
intValue
());
s
.
close
();
}
else
{
// both headers are corrupt
...
...
@@ -710,11 +817,11 @@ public class TestMVStore extends TestBase {
FileUtils
.
delete
(
fileName
);
MVMap
<
String
,
String
>
meta
;
MVStore
s
=
openStore
(
fileName
);
assertEquals
(
45
,
s
.
getRetentionTime
());
assertEquals
(
45
000
,
s
.
getRetentionTime
());
s
.
setRetentionTime
(
0
);
assertEquals
(
0
,
s
.
getRetentionTime
());
s
.
setRetentionTime
(
45
);
assertEquals
(
45
,
s
.
getRetentionTime
());
s
.
setRetentionTime
(
45
000
);
assertEquals
(
45
000
,
s
.
getRetentionTime
());
assertEquals
(
0
,
s
.
getCurrentVersion
());
assertFalse
(
s
.
hasUnsavedChanges
());
MVMap
<
String
,
String
>
m
=
s
.
openMap
(
"data"
);
...
...
h2/src/test/org/h2/test/store/TestSpinLock.java
0 → 100644
浏览文件 @
e8f0a660
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
test
.
store
;
import
org.h2.test.TestBase
;
/**
* Test using volatile fields to ensure we don't read from a version that is
* concurrently written to.
*/
public
class
TestSpinLock
extends
TestBase
{
/**
* The version to use for writing.
*/
volatile
int
writeVersion
;
/**
* The current data object.
*/
volatile
Data
data
=
new
Data
(
0
,
null
);
/**
* Run just this test.
*
* @param a ignored
*/
public
static
void
main
(
String
...
a
)
throws
Exception
{
TestBase
.
createCaller
().
init
().
test
();
}
@Override
public
void
test
()
throws
Exception
{
final
TestSpinLock
obj
=
new
TestSpinLock
();
Thread
t
=
new
Thread
()
{
public
void
run
()
{
while
(!
isInterrupted
())
{
for
(
int
i
=
0
;
i
<
10000
;
i
++)
{
Data
d
=
obj
.
copyOnWrite
();
obj
.
data
=
d
;
d
.
write
(
i
);
d
.
writing
=
false
;
}
}
}
};
t
.
start
();
try
{
for
(
int
i
=
0
;
i
<
100000
;
i
++)
{
Data
d
=
obj
.
getImmutable
();
int
z
=
d
.
x
+
d
.
y
;
if
(
z
!=
0
)
{
String
error
=
i
+
" result: "
+
z
+
" now: "
+
d
.
x
+
" "
+
d
.
y
;
System
.
out
.
println
(
error
);
throw
new
Exception
(
error
);
}
}
}
finally
{
t
.
interrupt
();
t
.
join
();
}
}
/**
* Clone the data object if necessary (if the write version is newer than
* the current version).
*
* @return the data object
*/
Data
copyOnWrite
()
{
Data
d
=
data
;
d
.
writing
=
true
;
int
w
=
writeVersion
;
if
(
w
<=
data
.
version
)
{
return
d
;
}
Data
d2
=
new
Data
(
w
,
data
);
d2
.
writing
=
true
;
d
.
writing
=
false
;
return
d2
;
}
/**
* Get an immutable copy of the data object.
*
* @return the immutable object
*/
private
Data
getImmutable
()
{
Data
d
=
data
;
++
writeVersion
;
// wait until writing is done,
// but only for the current write operation:
// a bit like a spin lock
while
(
d
.
writing
)
{
// Thread.yield() is not required, specially
// if there are multiple cores
// but getImmutable() doesn't
// need to be that fast actually
Thread
.
yield
();
}
return
d
;
}
/**
* The data class - represents the root page.
*/
static
class
Data
{
/**
* The version.
*/
final
int
version
;
/**
* The values.
*/
int
x
,
y
;
/**
* Whether a write operation is in progress.
*/
volatile
boolean
writing
;
/**
* Create a copy of the data.
*
* @param version the new version
* @param old the old data or null
*/
Data
(
int
version
,
Data
old
)
{
this
.
version
=
version
;
if
(
old
!=
null
)
{
this
.
x
=
old
.
x
;
this
.
y
=
old
.
y
;
}
}
/**
* Write to the fields in an unsynchronized way.
*
* @param value the new value
*/
void
write
(
int
value
)
{
this
.
x
=
value
;
this
.
y
=
-
value
;
}
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论