Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
53fd89a8
提交
53fd89a8
authored
7月 27, 2013
作者:
Thomas Mueller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
MVTableEngine
上级
f26792c2
显示空白字符变更
内嵌
并排
正在显示
11 个修改的文件
包含
828 行增加
和
35 行删除
+828
-35
Database.java
h2/src/main/org/h2/engine/Database.java
+31
-6
Chunk.java
h2/src/main/org/h2/mvstore/Chunk.java
+1
-1
FreeSpaceBitSet.java
h2/src/main/org/h2/mvstore/FreeSpaceBitSet.java
+163
-0
MVStore.java
h2/src/main/org/h2/mvstore/MVStore.java
+49
-25
TestAll.java
h2/src/test/org/h2/test/TestAll.java
+11
-2
test.properties
h2/src/test/org/h2/test/bench/test.properties
+1
-0
FreeSpaceList.java
h2/src/test/org/h2/test/store/FreeSpaceList.java
+205
-0
FreeSpaceTree.java
h2/src/test/org/h2/test/store/FreeSpaceTree.java
+194
-0
TestFreeSpace.java
h2/src/test/org/h2/test/store/TestFreeSpace.java
+144
-0
TestMVTableEngine.java
h2/src/test/org/h2/test/store/TestMVTableEngine.java
+28
-0
TestTransactionStore.java
h2/src/test/org/h2/test/store/TestTransactionStore.java
+1
-1
没有找到文件。
h2/src/main/org/h2/engine/Database.java
浏览文件 @
53fd89a8
...
...
@@ -6,6 +6,7 @@
*/
package
org
.
h2
.
engine
;
import
java.beans.ExceptionListener
;
import
java.io.IOException
;
import
java.sql.SQLException
;
import
java.util.ArrayList
;
...
...
@@ -273,6 +274,15 @@ public class Database implements DataHandler {
public
void
setMvStore
(
MVTableEngine
.
Store
mvStore
)
{
this
.
mvStore
=
mvStore
;
mvStore
.
getStore
().
setBackgroundExceptionListener
(
new
ExceptionListener
()
{
@Override
public
void
exceptionThrown
(
Exception
e
)
{
setBackgroundException
(
DbException
.
convert
(
e
));
}
});
}
/**
...
...
@@ -1086,6 +1096,7 @@ public class Database implements DataHandler {
if
(
closing
)
{
return
;
}
throwLastBackgroundException
();
if
(
fileLockMethod
==
FileLock
.
LOCK_SERIALIZED
&&
!
reconnectChangePending
)
{
// another connection may have written something - don't write
try
{
...
...
@@ -1799,6 +1810,17 @@ public class Database implements DataHandler {
* @param session the session
*/
synchronized
void
commit
(
Session
session
)
{
throwLastBackgroundException
();
if
(
readOnly
)
{
return
;
}
if
(
pageStore
!=
null
)
{
pageStore
.
commit
(
session
);
}
session
.
setAllCommitted
();
}
private
void
throwLastBackgroundException
()
{
if
(
backgroundException
!=
null
)
{
// we don't care too much about concurrency here,
// we just want to make sure the exception is _normally_
...
...
@@ -1809,13 +1831,16 @@ public class Database implements DataHandler {
throw
b
;
}
}
if
(
readOnly
)
{
return
;
}
if
(
pageStore
!=
null
)
{
pageStore
.
commit
(
session
);
public
void
setBackgroundException
(
DbException
e
)
{
if
(
backgroundException
==
null
)
{
backgroundException
=
e
;
TraceSystem
t
=
getTraceSystem
();
if
(
t
!=
null
)
{
t
.
getTrace
(
Trace
.
DATABASE
).
error
(
e
,
"flush"
);
}
}
session
.
setAllCommitted
();
}
/**
...
...
h2/src/main/org/h2/mvstore/Chunk.java
浏览文件 @
53fd89a8
...
...
@@ -116,7 +116,7 @@ public class Chunk {
}
/**
* Write the header.
* Write the
chunk
header.
*
* @param buff the target buffer
*/
...
...
h2/src/main/org/h2/mvstore/FreeSpaceBitSet.java
0 → 100644
浏览文件 @
53fd89a8
/*
* Copyright 2004-2013 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
.
mvstore
;
import
java.util.BitSet
;
import
org.h2.util.MathUtils
;
/**
* A free space bit set.
*/
public
class
FreeSpaceBitSet
{
/**
* The first usable block.
*/
private
final
int
firstFreeBlock
;
/**
* The block size in bytes.
*/
private
final
int
blockSize
;
/**
* The bit set.
*/
private
final
BitSet
set
=
new
BitSet
();
/**
* Create a new free space map.
*
* @param firstFreeBlock the first free block
* @param blockSize the block size
*/
public
FreeSpaceBitSet
(
int
firstFreeBlock
,
int
blockSize
)
{
this
.
firstFreeBlock
=
firstFreeBlock
;
this
.
blockSize
=
blockSize
;
clear
();
}
/**
* Reset the list.
*/
public
synchronized
void
clear
()
{
set
.
clear
();
set
.
set
(
0
,
firstFreeBlock
);
}
/**
* Check whether one of the blocks is in use.
*
* @param pos the position in bytes
* @param length the number of bytes
* @return true if a block is in use
*/
public
synchronized
boolean
isUsed
(
long
pos
,
int
length
)
{
int
start
=
getBlock
(
pos
);
int
blocks
=
getBlockCount
(
length
);
for
(
int
i
=
start
;
i
<
start
+
blocks
;
i
++)
{
if
(!
set
.
get
(
i
))
{
return
false
;
}
}
return
true
;
}
/**
* Check whether one of the blocks is free.
*
* @param pos the position in bytes
* @param length the number of bytes
* @return true if a block is free
*/
public
synchronized
boolean
isFree
(
long
pos
,
int
length
)
{
int
start
=
getBlock
(
pos
);
int
blocks
=
getBlockCount
(
length
);
for
(
int
i
=
start
;
i
<
start
+
blocks
;
i
++)
{
if
(
set
.
get
(
i
))
{
return
false
;
}
}
return
true
;
}
/**
* Allocate a number of blocks and mark them as used.
*
* @param length the number of bytes to allocate
* @return the start position in bytes
*/
public
synchronized
long
allocate
(
int
length
)
{
int
blocks
=
getBlockCount
(
length
);
for
(
int
i
=
0
;;)
{
int
start
=
set
.
nextClearBit
(
i
);
int
end
=
set
.
nextSetBit
(
start
+
1
);
if
(
end
<
0
||
end
-
start
>=
blocks
)
{
set
.
set
(
start
,
start
+
blocks
);
return
getPos
(
start
);
}
i
=
end
;
}
}
/**
* Mark the space as in use.
*
* @param pos the position in bytes
* @param length the number of bytes
*/
public
synchronized
void
markUsed
(
long
pos
,
int
length
)
{
int
start
=
getBlock
(
pos
);
int
blocks
=
getBlockCount
(
length
);
set
.
set
(
start
,
start
+
blocks
);
}
/**
* Mark the space as free.
*
* @param pos the position in bytes
* @param length the number of bytes
*/
public
synchronized
void
free
(
long
pos
,
int
length
)
{
int
start
=
getBlock
(
pos
);
int
blocks
=
getBlockCount
(
length
);
set
.
clear
(
start
,
start
+
blocks
);
}
private
long
getPos
(
int
block
)
{
return
(
long
)
block
*
(
long
)
blockSize
;
}
private
int
getBlock
(
long
pos
)
{
return
(
int
)
(
pos
/
blockSize
);
}
private
int
getBlockCount
(
int
length
)
{
return
MathUtils
.
roundUpInt
(
length
,
blockSize
)
/
blockSize
;
}
@Override
public
String
toString
()
{
StringBuilder
buff
=
new
StringBuilder
(
"["
);
for
(
int
i
=
0
;;)
{
if
(
i
>
0
)
{
buff
.
append
(
", "
);
}
int
start
=
set
.
nextClearBit
(
i
);
buff
.
append
(
start
).
append
(
'-'
);
int
end
=
set
.
nextSetBit
(
start
+
1
);
if
(
end
<
0
)
{
break
;
}
buff
.
append
(
end
-
1
);
i
=
end
+
1
;
}
return
buff
.
append
(
']'
).
toString
();
}
}
\ No newline at end of file
h2/src/main/org/h2/mvstore/MVStore.java
浏览文件 @
53fd89a8
...
...
@@ -6,6 +6,7 @@
*/
package
org
.
h2
.
mvstore
;
import
java.beans.ExceptionListener
;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
import
java.nio.channels.FileChannel
;
...
...
@@ -104,7 +105,6 @@ MVStore:
- maybe rename 'rollback' to 'revert' to distinguish from transactions
- support other compression algorithms (deflate, LZ4,...)
- only retain the last version, unless explicitly set (setRetainVersion)
- unit test for the FreeSpaceList; maybe find a simpler implementation
- support opening (existing) maps by id
- more consistent null handling (keys/values sometimes may be null)
- logging mechanism, specially for operations in a background thread
...
...
@@ -161,11 +161,12 @@ public class MVStore {
*/
private
final
ConcurrentHashMap
<
Integer
,
Chunk
>
chunks
=
new
ConcurrentHashMap
<
Integer
,
Chunk
>();
/**
* The list of free spaces between the chunks.
* The free spaces between the chunks. The first block to use is block 2
* (the first two blocks are the file header).
*/
private
FreeSpaceList
freeSpaceList
=
new
FreeSpaceList
();
private
FreeSpaceBitSet
freeSpace
=
new
FreeSpaceBitSet
(
2
,
BLOCK_SIZE
);
/**
* The map of temporarily removed pages. The key is the unsaved version, the
...
...
@@ -235,6 +236,8 @@ public class MVStore {
*/
private
int
writeDelay
=
1000
;
private
ExceptionListener
backgroundExceptionListener
;
MVStore
(
HashMap
<
String
,
Object
>
config
)
{
String
f
=
(
String
)
config
.
get
(
"fileName"
);
if
(
f
!=
null
&&
f
.
indexOf
(
':'
)
<
0
)
{
...
...
@@ -598,12 +601,13 @@ public class MVStore {
}
}
// rebuild the free space list
freeSpace
List
.
clear
();
freeSpace
.
clear
();
for
(
Chunk
c
:
chunks
.
values
())
{
if
(
c
.
start
==
Long
.
MAX_VALUE
)
{
continue
;
}
freeSpaceList
.
markUsed
(
c
);
int
len
=
MathUtils
.
roundUpInt
(
c
.
length
,
BLOCK_SIZE
)
+
BLOCK_SIZE
;
freeSpace
.
markUsed
(
c
.
start
,
len
);
}
}
...
...
@@ -710,6 +714,7 @@ public class MVStore {
/**
* Close the file and the store, without writing anything.
* This will stop the background thread.
*/
public
void
closeImmediately
()
{
closeFile
(
false
);
...
...
@@ -743,7 +748,7 @@ public class MVStore {
}
meta
=
null
;
chunks
.
clear
();
freeSpace
List
.
clear
();
freeSpace
.
clear
();
cache
.
clear
();
maps
.
clear
();
}
catch
(
Exception
e
)
{
...
...
@@ -927,6 +932,8 @@ public class MVStore {
int
chunkLength
=
buff
.
position
();
// round to the next block,
// and one additional block for the file header
int
length
=
MathUtils
.
roundUpInt
(
chunkLength
,
BLOCK_SIZE
)
+
BLOCK_SIZE
;
if
(
length
>
buff
.
capacity
())
{
buff
=
DataUtils
.
ensureCapacity
(
buff
,
length
-
buff
.
capacity
());
...
...
@@ -934,17 +941,22 @@ public class MVStore {
buff
.
limit
(
length
);
long
fileSizeUsed
=
getFileSizeUsed
();
long
filePos
=
reuseSpace
?
allocateChunk
(
length
)
:
fileSizeUsed
;
long
filePos
;
if
(
reuseSpace
)
{
filePos
=
freeSpace
.
allocate
(
length
);
}
else
{
filePos
=
fileSizeUsed
;
freeSpace
.
markUsed
(
fileSizeUsed
,
length
);
}
boolean
storeAtEndOfFile
=
filePos
+
length
>=
fileSizeUsed
;
// free up the space of unused chunks now
for
(
Chunk
x
:
removedChunks
)
{
freeSpace
List
.
markFree
(
x
);
freeSpace
.
free
(
x
.
start
,
x
.
length
);
}
c
.
start
=
filePos
;
c
.
length
=
chunkLength
;
freeSpaceList
.
markUsed
(
c
);
c
.
metaRootPos
=
meta
.
getRoot
().
getPos
();
buff
.
position
(
0
);
c
.
writeHeader
(
buff
);
...
...
@@ -1107,10 +1119,6 @@ public class MVStore {
return
size
;
}
private
long
allocateChunk
(
long
length
)
{
return
((
long
)
freeSpaceList
.
allocatePages
(
length
))
*
BLOCK_SIZE
;
}
/**
* Check whether there are any unsaved changes.
*
...
...
@@ -1450,6 +1458,20 @@ public class MVStore {
return
v
;
}
/**
* Set the listener to be used for exceptions that occur in the background thread.
*
* @param backgroundExceptionListener the listener
*/
public
void
setBackgroundExceptionListener
(
ExceptionListener
backgroundExceptionListener
)
{
this
.
backgroundExceptionListener
=
backgroundExceptionListener
;
}
public
ExceptionListener
getBackgroundExceptionListener
()
{
return
backgroundExceptionListener
;
}
/**
* Check whether all data can be read from this version. This requires that
* all chunks referenced by this version are still available (not
...
...
@@ -1561,7 +1583,7 @@ public class MVStore {
}
meta
.
clear
();
chunks
.
clear
();
freeSpace
List
.
clear
();
freeSpace
.
clear
();
maps
.
clear
();
synchronized
(
freedPages
)
{
freedPages
.
clear
();
...
...
@@ -1594,7 +1616,7 @@ public class MVStore {
loadFromFile
=
true
;
do
{
last
=
chunks
.
remove
(
lastChunkId
);
freeSpace
List
.
markFree
(
last
);
freeSpace
.
free
(
last
.
start
,
last
.
length
);
lastChunkId
--;
}
while
(
last
.
version
>
version
&&
chunks
.
size
()
>
0
);
rootChunkStart
=
last
.
start
;
...
...
@@ -1771,7 +1793,13 @@ public class MVStore {
if
(
time
<=
lastStoreTime
+
writeDelay
)
{
return
;
}
try
{
store
(
true
);
}
catch
(
Exception
e
)
{
if
(
backgroundExceptionListener
!=
null
)
{
backgroundExceptionListener
.
exceptionThrown
(
e
);
}
}
}
/**
...
...
@@ -1780,8 +1808,10 @@ public class MVStore {
* @param mb the cache size in MB.
*/
public
void
setCacheSize
(
long
mb
)
{
if
(
cache
!=
null
)
{
cache
.
setMaxMemory
(
mb
*
1024
*
1024
);
}
}
public
boolean
isReadOnly
()
{
return
readOnly
;
...
...
@@ -1848,13 +1878,7 @@ public class MVStore {
// ignore
}
}
try
{
store
.
storeInBackground
();
}
catch
(
Exception
e
)
{
int
todo
;
// TODO throw the exception in the main thread
// at some point, or log the problem
}
}
}
...
...
h2/src/test/org/h2/test/TestAll.java
浏览文件 @
53fd89a8
...
...
@@ -110,6 +110,7 @@ import org.h2.test.store.TestCacheLIRS;
import
org.h2.test.store.TestCacheLongKeyLIRS
;
import
org.h2.test.store.TestConcurrent
;
import
org.h2.test.store.TestDataUtils
;
import
org.h2.test.store.TestFreeSpace
;
import
org.h2.test.store.TestMVRTree
;
import
org.h2.test.store.TestMVStore
;
import
org.h2.test.store.TestMVTableEngine
;
...
...
@@ -448,10 +449,17 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
}
else
{
test
.
runTests
();
Profiler
prof
=
new
Profiler
();
prof
.
depth
=
4
;
prof
.
depth
=
8
;
prof
.
interval
=
1
;
prof
.
startCollecting
();
if
(
test
.
mvStore
)
{
TestPerformance
.
main
(
"-init"
,
"-db"
,
"9"
,
"-size"
,
"1000"
);
TestPerformance
.
main
(
"-init"
,
"-db"
,
"1"
,
"-size"
,
"1000"
);
TestPerformance
.
main
(
"-init"
,
"-db"
,
"9"
,
"-size"
,
"1000"
);
TestPerformance
.
main
(
"-init"
,
"-db"
,
"1"
,
"-size"
,
"1000"
);
}
else
{
TestPerformance
.
main
(
"-init"
,
"-db"
,
"1"
);
}
prof
.
stopCollecting
();
System
.
out
.
println
(
prof
.
getTop
(
3
));
// Recover.execute("data", null);
...
...
@@ -694,6 +702,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new
TestCacheLongKeyLIRS
().
runTest
(
this
);
new
TestConcurrent
().
runTest
(
this
);
new
TestDataUtils
().
runTest
(
this
);
new
TestFreeSpace
().
runTest
(
this
);
new
TestMVRTree
().
runTest
(
this
);
new
TestMVStore
().
runTest
(
this
);
new
TestMVTableEngine
().
runTest
(
this
);
...
...
h2/src/test/org/h2/test/bench/test.properties
浏览文件 @
53fd89a8
db1
=
H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
db9
=
H2 (MVStore), org.h2.Driver, jdbc:h2:data/test;MV_STORE=TRUE;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
#xdb1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;DEFAULT_TABLE_ENGINE=org.h2.mvstore.db.MVTableEngine, sa, sa
...
...
h2/src/
main/org/h2/mv
store/FreeSpaceList.java
→
h2/src/
test/org/h2/test/
store/FreeSpaceList.java
浏览文件 @
53fd89a8
...
...
@@ -4,30 +4,37 @@
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package
org
.
h2
.
mv
store
;
package
org
.
h2
.
test
.
store
;
import
java.util.ArrayList
;
import
java.util.List
;
import
org.h2.mvstore.DataUtils
;
import
org.h2.util.MathUtils
;
/**
* A list that maintains ranges of free space (in
pages) in a file
.
* A list that maintains ranges of free space (in
blocks)
.
*/
public
class
FreeSpaceList
{
/**
* The first
2 pages are occupied by the file header
.
* The first
usable block
.
*/
private
static
final
int
FIRST_FREE_PAGE
=
2
;
private
final
int
firstFreeBlock
;
/**
* The maximum number of pages. Smaller than than MAX_VALUE to avoid
* overflow errors during arithmetic operations.
* The block size in bytes.
*/
private
static
final
int
MAX_PAGE_COUNT
=
Integer
.
MAX_VALUE
/
2
;
private
final
int
blockSize
;
private
List
<
PageRange
>
freeSpaceList
=
new
ArrayList
<
Page
Range
>();
private
List
<
BlockRange
>
freeSpaceList
=
new
ArrayList
<
Block
Range
>();
public
FreeSpaceList
()
{
public
FreeSpaceList
(
int
firstFreeBlock
,
int
blockSize
)
{
this
.
firstFreeBlock
=
firstFreeBlock
;
if
(
Integer
.
bitCount
(
blockSize
)
!=
1
)
{
throw
DataUtils
.
newIllegalArgumentException
(
"Block size is not a power of 2"
);
}
this
.
blockSize
=
blockSize
;
clear
();
}
...
...
@@ -36,20 +43,23 @@ public class FreeSpaceList {
*/
public
synchronized
void
clear
()
{
freeSpaceList
.
clear
();
freeSpaceList
.
add
(
new
PageRange
(
FIRST_FREE_PAGE
,
MAX_PAGE_COUNT
));
freeSpaceList
.
add
(
new
BlockRange
(
firstFreeBlock
,
Integer
.
MAX_VALUE
-
firstFreeBlock
));
}
/**
* Allocate a number of
pages
.
* Allocate a number of
blocks and mark them as used
.
*
* @param length the number of bytes to allocate
* @return the
position in pag
es
* @return the
start position in byt
es
*/
public
synchronized
int
allocatePages
(
long
length
)
{
int
required
=
(
int
)
(
length
/
MVStore
.
BLOCK_SIZE
)
+
1
;
for
(
Page
Range
pr
:
freeSpaceList
)
{
public
synchronized
long
allocate
(
int
length
)
{
int
required
=
getBlockCount
(
length
)
;
for
(
Block
Range
pr
:
freeSpaceList
)
{
if
(
pr
.
length
>=
required
)
{
return
pr
.
start
;
int
result
=
pr
.
start
;
this
.
markUsed
(
pr
.
start
*
blockSize
,
length
);
return
result
*
blockSize
;
}
}
throw
DataUtils
.
newIllegalStateException
(
...
...
@@ -57,18 +67,13 @@ public class FreeSpaceList {
"Could not find a free page to allocate"
);
}
/**
* Mark a chunk as used.
*
* @param c the chunk
*/
public
synchronized
void
markUsed
(
Chunk
c
)
{
int
chunkStart
=
(
int
)
(
c
.
start
/
MVStore
.
BLOCK_SIZE
);
int
required
=
(
int
)
((
c
.
start
+
c
.
length
)
/
MVStore
.
BLOCK_SIZE
)
+
2
-
chunkStart
;
PageRange
found
=
null
;
public
synchronized
void
markUsed
(
long
pos
,
int
length
)
{
int
start
=
(
int
)
(
pos
/
blockSize
);
int
required
=
getBlockCount
(
length
);
BlockRange
found
=
null
;
int
i
=
0
;
for
(
Page
Range
pr
:
freeSpaceList
)
{
if
(
chunkStart
>=
pr
.
start
&&
chunkS
tart
<
(
pr
.
start
+
pr
.
length
))
{
for
(
Block
Range
pr
:
freeSpaceList
)
{
if
(
start
>=
pr
.
start
&&
s
tart
<
(
pr
.
start
+
pr
.
length
))
{
found
=
pr
;
break
;
}
...
...
@@ -77,52 +82,43 @@ public class FreeSpaceList {
if
(
found
==
null
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_INTERNAL
,
"Cannot find spot to mark
chunk as used in free list: {0}"
,
c
);
"Cannot find spot to mark
as used in free list"
);
}
if
(
chunkS
tart
+
required
>
found
.
start
+
found
.
length
)
{
if
(
s
tart
+
required
>
found
.
start
+
found
.
length
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_INTERNAL
,
"
Chunk runs over edge of free space: {0}"
,
c
);
"
Runs over edge of free space"
);
}
if
(
found
.
start
==
chunkS
tart
)
{
// if the used
-chunk
is at the beginning of a free-space-range
if
(
found
.
start
==
s
tart
)
{
// if the used
space
is at the beginning of a free-space-range
found
.
start
+=
required
;
found
.
length
-=
required
;
if
(
found
.
length
==
0
)
{
// if the free-space-range is now empty, remove it
freeSpaceList
.
remove
(
i
);
}
}
else
if
(
found
.
start
+
found
.
length
==
chunkS
tart
+
required
)
{
// if the used
-chunk
is at the end of a free-space-range
}
else
if
(
found
.
start
+
found
.
length
==
s
tart
+
required
)
{
// if the used
space
is at the end of a free-space-range
found
.
length
-=
required
;
if
(
found
.
length
==
0
)
{
// if the free-space-range is now empty, remove it
freeSpaceList
.
remove
(
i
);
}
}
else
{
// it's in the middle, so split the existing entry
int
length1
=
chunkS
tart
-
found
.
start
;
int
start2
=
chunkS
tart
+
required
;
int
length2
=
found
.
start
+
found
.
length
-
chunkS
tart
-
required
;
int
length1
=
s
tart
-
found
.
start
;
int
start2
=
s
tart
+
required
;
int
length2
=
found
.
start
+
found
.
length
-
s
tart
-
required
;
found
.
length
=
length1
;
PageRange
newRange
=
new
Page
Range
(
start2
,
length2
);
BlockRange
newRange
=
new
Block
Range
(
start2
,
length2
);
freeSpaceList
.
add
(
i
+
1
,
newRange
);
}
}
/**
* Mark the chunk as free.
*
* @param c the chunk
*/
public
synchronized
void
markFree
(
Chunk
c
)
{
int
chunkStart
=
(
int
)
(
c
.
start
/
MVStore
.
BLOCK_SIZE
);
int
required
=
(
c
.
length
/
MVStore
.
BLOCK_SIZE
)
+
1
;
PageRange
found
=
null
;
public
synchronized
void
free
(
long
pos
,
int
length
)
{
int
start
=
(
int
)
(
pos
/
blockSize
);
int
required
=
getBlockCount
(
length
);
BlockRange
found
=
null
;
int
i
=
0
;
for
(
Page
Range
pr
:
freeSpaceList
)
{
if
(
pr
.
start
>
chunkS
tart
)
{
for
(
Block
Range
pr
:
freeSpaceList
)
{
if
(
pr
.
start
>
s
tart
)
{
found
=
pr
;
break
;
}
...
...
@@ -131,18 +127,18 @@ public class FreeSpaceList {
if
(
found
==
null
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_INTERNAL
,
"Cannot find spot to mark
chunk as unused in free list: {0}"
,
c
);
"Cannot find spot to mark
as unused in free list"
);
}
if
(
chunkStart
+
required
+
1
==
found
.
start
)
{
// if the used
-chunk
is adjacent to the beginning of a
if
(
start
+
required
==
found
.
start
)
{
// if the used
space
is adjacent to the beginning of a
// free-space-range
found
.
start
=
chunkS
tart
;
found
.
start
=
s
tart
;
found
.
length
+=
required
;
// compact: merge the previous entry into this one if
// they are now adjacent
if
(
i
>
0
)
{
Page
Range
previous
=
freeSpaceList
.
get
(
i
-
1
);
if
(
previous
.
start
+
previous
.
length
+
1
==
found
.
start
)
{
Block
Range
previous
=
freeSpaceList
.
get
(
i
-
1
);
if
(
previous
.
start
+
previous
.
length
==
found
.
start
)
{
previous
.
length
+=
found
.
length
;
freeSpaceList
.
remove
(
i
);
}
...
...
@@ -150,64 +146,60 @@ public class FreeSpaceList {
return
;
}
if
(
i
>
0
)
{
// if the used
-chunk
is adjacent to the end of a free-space-range
Page
Range
previous
=
freeSpaceList
.
get
(
i
-
1
);
if
(
previous
.
start
+
previous
.
length
+
1
==
chunkS
tart
)
{
// if the used
space
is adjacent to the end of a free-space-range
Block
Range
previous
=
freeSpaceList
.
get
(
i
-
1
);
if
(
previous
.
start
+
previous
.
length
==
s
tart
)
{
previous
.
length
+=
required
;
// compact: merge the next entry into this one if
// they are now adjacent
if
(
previous
.
start
+
previous
.
length
+
1
==
found
.
start
)
{
previous
.
length
+=
found
.
length
;
freeSpaceList
.
remove
(
i
);
}
return
;
}
}
// it is between 2 entries, so add a new one
PageRange
newRange
=
new
PageRange
(
chunkS
tart
,
required
);
BlockRange
newRange
=
new
BlockRange
(
s
tart
,
required
);
freeSpaceList
.
add
(
i
,
newRange
);
}
@Override
public
String
toString
()
{
StringBuilder
buff
=
new
StringBuilder
();
boolean
first
=
true
;
for
(
PageRange
r
:
freeSpaceList
)
{
if
(
first
)
{
first
=
false
;
}
else
{
buff
.
append
(
", "
);
private
int
getBlockCount
(
int
length
)
{
if
(
length
<=
0
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_INTERNAL
,
"Free space invalid length"
);
}
buff
.
append
(
r
.
start
+
"-"
+
(
r
.
start
+
r
.
length
-
1
))
;
return
MathUtils
.
roundUpInt
(
length
,
blockSize
)
/
blockSize
;
}
return
buff
.
toString
();
@Override
public
String
toString
()
{
return
freeSpaceList
.
toString
();
}
/**
* A range of free
page
s.
* A range of free
block
s.
*/
private
static
final
class
Page
Range
{
private
static
final
class
Block
Range
{
/**
* The starting point, in
page
s.
* The starting point, in
block
s.
*/
public
int
start
;
int
start
;
/**
* The length, in
page
s.
* The length, in
block
s.
*/
public
int
length
;
int
length
;
public
Page
Range
(
int
start
,
int
length
)
{
public
Block
Range
(
int
start
,
int
length
)
{
this
.
start
=
start
;
this
.
length
=
length
;
}
@Override
public
String
toString
()
{
return
"start:"
+
start
+
" length:"
+
length
;
if
(
start
+
length
==
Integer
.
MAX_VALUE
)
{
return
start
+
"-"
;
}
return
start
+
"-"
+
(
start
+
length
-
1
);
}
}
}
h2/src/test/org/h2/test/store/FreeSpaceTree.java
0 → 100644
浏览文件 @
53fd89a8
/*
* Copyright 2004-2013 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
java.util.TreeSet
;
import
org.h2.mvstore.DataUtils
;
import
org.h2.util.MathUtils
;
/**
* A list that maintains ranges of free space (in blocks) in a file.
*/
public
class
FreeSpaceTree
{
/**
* The first usable block.
*/
private
final
int
firstFreeBlock
;
/**
* The block size in bytes.
*/
private
final
int
blockSize
;
/**
* The list of free space.
*/
private
TreeSet
<
BlockRange
>
freeSpace
=
new
TreeSet
<
BlockRange
>();
public
FreeSpaceTree
(
int
firstFreeBlock
,
int
blockSize
)
{
this
.
firstFreeBlock
=
firstFreeBlock
;
if
(
Integer
.
bitCount
(
blockSize
)
!=
1
)
{
throw
DataUtils
.
newIllegalArgumentException
(
"Block size is not a power of 2"
);
}
this
.
blockSize
=
blockSize
;
clear
();
}
/**
* Reset the list.
*/
public
synchronized
void
clear
()
{
freeSpace
.
clear
();
freeSpace
.
add
(
new
BlockRange
(
firstFreeBlock
,
Integer
.
MAX_VALUE
-
firstFreeBlock
));
}
/**
* Allocate a number of blocks and mark them as used.
*
* @param length the number of bytes to allocate
* @return the start position in bytes
*/
public
synchronized
long
allocate
(
int
length
)
{
int
blocks
=
getBlockCount
(
length
);
BlockRange
x
=
null
;
for
(
BlockRange
b
:
freeSpace
)
{
if
(
b
.
blocks
>=
blocks
)
{
x
=
b
;
break
;
}
}
long
pos
=
getPos
(
x
.
start
);
if
(
x
.
blocks
==
blocks
)
{
freeSpace
.
remove
(
x
);
}
else
{
x
.
start
+=
blocks
;
x
.
blocks
-=
blocks
;
}
return
pos
;
}
public
synchronized
void
markUsed
(
long
pos
,
int
length
)
{
int
start
=
getBlock
(
pos
);
int
blocks
=
getBlockCount
(
length
);
BlockRange
x
=
new
BlockRange
(
start
,
blocks
);
BlockRange
prev
=
freeSpace
.
floor
(
x
);
if
(
prev
==
null
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_INTERNAL
,
"Free space already marked"
);
}
if
(
prev
.
start
==
start
)
{
if
(
prev
.
blocks
==
blocks
)
{
// match
freeSpace
.
remove
(
prev
);
}
else
{
// cut the front
prev
.
start
+=
blocks
;
prev
.
blocks
-=
blocks
;
}
}
else
if
(
prev
.
start
+
prev
.
blocks
==
start
+
blocks
)
{
// cut the end
prev
.
blocks
-=
blocks
;
}
else
{
// insert an entry
x
.
start
=
start
+
blocks
;
x
.
blocks
=
prev
.
start
+
prev
.
blocks
-
x
.
start
;
freeSpace
.
add
(
x
);
prev
.
blocks
=
start
-
prev
.
start
;
}
}
public
synchronized
void
free
(
long
pos
,
int
length
)
{
int
start
=
getBlock
(
pos
);
int
blocks
=
getBlockCount
(
length
);
BlockRange
x
=
new
BlockRange
(
start
,
blocks
);
BlockRange
next
=
freeSpace
.
ceiling
(
x
);
if
(
next
==
null
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_INTERNAL
,
"Free space sentinel is missing"
);
}
BlockRange
prev
=
freeSpace
.
lower
(
x
);
if
(
prev
!=
null
)
{
if
(
prev
.
start
+
prev
.
blocks
==
start
)
{
// extend the previous entry
prev
.
blocks
+=
blocks
;
if
(
prev
.
start
+
prev
.
blocks
==
next
.
start
)
{
// merge with the next entry
prev
.
blocks
+=
next
.
blocks
;
freeSpace
.
remove
(
next
);
}
return
;
}
}
if
(
start
+
blocks
==
next
.
start
)
{
// extend the next entry
next
.
start
-=
blocks
;
next
.
blocks
+=
blocks
;
return
;
}
freeSpace
.
add
(
x
);
}
private
long
getPos
(
int
block
)
{
return
(
long
)
block
*
(
long
)
blockSize
;
}
private
int
getBlock
(
long
pos
)
{
return
(
int
)
(
pos
/
blockSize
);
}
private
int
getBlockCount
(
int
length
)
{
if
(
length
<=
0
)
{
throw
DataUtils
.
newIllegalStateException
(
DataUtils
.
ERROR_INTERNAL
,
"Free space invalid length"
);
}
return
MathUtils
.
roundUpInt
(
length
,
blockSize
)
/
blockSize
;
}
@Override
public
String
toString
()
{
return
freeSpace
.
toString
();
}
/**
* A range of free blocks.
*/
private
static
final
class
BlockRange
implements
Comparable
<
BlockRange
>
{
/**
* The starting point (the block number).
*/
public
int
start
;
/**
* The length, in blocks.
*/
public
int
blocks
;
public
BlockRange
(
int
start
,
int
blocks
)
{
this
.
start
=
start
;
this
.
blocks
=
blocks
;
}
@Override
public
int
compareTo
(
BlockRange
o
)
{
return
start
<
o
.
start
?
-
1
:
start
>
o
.
start
?
1
:
0
;
}
@Override
public
String
toString
()
{
if
(
blocks
+
start
==
Integer
.
MAX_VALUE
)
{
return
start
+
"-"
;
}
return
start
+
"-"
+
(
start
+
blocks
-
1
);
}
}
}
h2/src/test/org/h2/test/store/TestFreeSpace.java
0 → 100644
浏览文件 @
53fd89a8
/*
* Copyright 2004-2013 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
java.util.Random
;
import
org.h2.mvstore.FreeSpaceBitSet
;
import
org.h2.test.TestBase
;
import
org.h2.util.Utils
;
/**
* Tests the free space list.
*/
public
class
TestFreeSpace
extends
TestBase
{
/**
* Run just this test.
*
* @param a ignored
*/
public
static
void
main
(
String
...
a
)
throws
Exception
{
TestBase
.
createCaller
().
init
().
test
();
testMemoryUsage
();
testPerformance
();
}
@Override
public
void
test
()
throws
Exception
{
testSimple
();
testRandomized
();
}
private
static
void
testPerformance
()
{
for
(
int
i
=
0
;
i
<
10
;
i
++)
{
long
t
=
System
.
currentTimeMillis
();
FreeSpaceBitSet
f
=
new
FreeSpaceBitSet
(
0
,
4096
);
// 75 ms
// FreeSpaceList f = new FreeSpaceList(0, 4096);
// 13868 ms
// FreeSpaceTree f = new FreeSpaceTree(0, 4096);
// 56 ms
for
(
int
j
=
0
;
j
<
100000
;
j
++)
{
f
.
markUsed
(
j
*
2
*
4096
,
4096
);
}
for
(
int
j
=
0
;
j
<
100000
;
j
++)
{
f
.
free
(
j
*
2
*
4096
,
4096
);
}
for
(
int
j
=
0
;
j
<
100000
;
j
++)
{
f
.
allocate
(
4096
*
2
);
}
System
.
out
.
println
(
System
.
currentTimeMillis
()
-
t
);
}
}
private
static
void
testMemoryUsage
()
{
// 16 GB file size
long
size
=
16L
*
1024
*
1024
*
1024
;
System
.
gc
();
System
.
gc
();
long
first
=
Utils
.
getMemoryUsed
();
FreeSpaceBitSet
f
=
new
FreeSpaceBitSet
(
0
,
4096
);
// 512 KB
// FreeSpaceTree f = new FreeSpaceTree(0, 4096);
// 64 MB
// FreeSpaceList f = new FreeSpaceList(0, 4096);
// too slow
for
(
long
j
=
size
;
j
>
0
;
j
-=
4
*
4096
)
{
f
.
markUsed
(
j
,
4096
);
}
System
.
gc
();
System
.
gc
();
long
mem
=
Utils
.
getMemoryUsed
()
-
first
;
System
.
out
.
println
(
"Memory used: "
+
mem
);
System
.
out
.
println
(
"f: "
+
f
.
toString
().
length
());
}
private
void
testSimple
()
{
FreeSpaceBitSet
f1
=
new
FreeSpaceBitSet
(
2
,
1024
);
FreeSpaceList
f2
=
new
FreeSpaceList
(
2
,
1024
);
FreeSpaceTree
f3
=
new
FreeSpaceTree
(
2
,
1024
);
assertEquals
(
f1
.
toString
(),
f2
.
toString
());
assertEquals
(
f1
.
toString
(),
f3
.
toString
());
assertEquals
(
2
*
1024
,
f1
.
allocate
(
10240
));
assertEquals
(
2
*
1024
,
f2
.
allocate
(
10240
));
assertEquals
(
2
*
1024
,
f3
.
allocate
(
10240
));
assertEquals
(
f1
.
toString
(),
f2
.
toString
());
assertEquals
(
f1
.
toString
(),
f3
.
toString
());
f1
.
markUsed
(
20480
,
1024
);
f2
.
markUsed
(
20480
,
1024
);
f3
.
markUsed
(
20480
,
1024
);
assertEquals
(
f1
.
toString
(),
f2
.
toString
());
assertEquals
(
f1
.
toString
(),
f3
.
toString
());
}
private
void
testRandomized
()
{
FreeSpaceBitSet
f1
=
new
FreeSpaceBitSet
(
2
,
8
);
FreeSpaceList
f2
=
new
FreeSpaceList
(
2
,
8
);
Random
r
=
new
Random
(
1
);
StringBuilder
log
=
new
StringBuilder
();
for
(
int
i
=
0
;
i
<
100000
;
i
++)
{
long
pos
=
r
.
nextInt
(
1024
);
int
length
=
1
+
r
.
nextInt
(
8
*
128
);
switch
(
r
.
nextInt
(
3
))
{
case
0
:
{
log
.
append
(
"allocate("
+
length
+
");\n"
);
long
a
=
f1
.
allocate
(
length
);
long
b
=
f2
.
allocate
(
length
);
assertEquals
(
a
,
b
);
break
;
}
case
1
:
if
(
f1
.
isUsed
(
pos
,
length
))
{
log
.
append
(
"free("
+
pos
+
", "
+
length
+
");\n"
);
f1
.
free
(
pos
,
length
);
f2
.
free
(
pos
,
length
);
}
break
;
case
2
:
if
(
f1
.
isFree
(
pos
,
length
))
{
log
.
append
(
"markUsed("
+
pos
+
", "
+
length
+
");\n"
);
f1
.
markUsed
(
pos
,
length
);
f2
.
markUsed
(
pos
,
length
);
}
break
;
}
assertEquals
(
f1
.
toString
(),
f2
.
toString
());
}
}
}
h2/src/test/org/h2/test/store/TestMVTableEngine.java
浏览文件 @
53fd89a8
...
...
@@ -46,6 +46,7 @@ public class TestMVTableEngine extends TestBase {
@Override
public
void
test
()
throws
Exception
{
// testShrinkDatabaseFile();
testTwoPhaseCommit
();
testRecover
();
testSeparateKey
();
testRollback
();
...
...
@@ -88,6 +89,33 @@ public class TestMVTableEngine extends TestBase {
}
}
private
void
testTwoPhaseCommit
()
throws
Exception
{
FileUtils
.
deleteRecursive
(
getBaseDir
(),
true
);
Connection
conn
;
Statement
stat
;
String
url
=
"mvstore;MV_STORE=TRUE"
;
url
=
getURL
(
url
,
true
);
conn
=
getConnection
(
url
);
stat
=
conn
.
createStatement
();
stat
.
execute
(
"create table test(id int primary key, name varchar)"
);
stat
.
execute
(
"set write_delay 0"
);
conn
.
setAutoCommit
(
false
);
stat
.
execute
(
"insert into test values(1, 'Hello')"
);
stat
.
execute
(
"prepare commit test_tx"
);
stat
.
execute
(
"shutdown immediately"
);
JdbcUtils
.
closeSilently
(
conn
);
conn
=
getConnection
(
url
);
stat
=
conn
.
createStatement
();
ResultSet
rs
;
rs
=
stat
.
executeQuery
(
"select * from information_schema.in_doubt"
);
assertTrue
(
rs
.
next
());
stat
.
execute
(
"commit transaction test_tx"
);
rs
=
stat
.
executeQuery
(
"select * from test"
);
assertTrue
(
rs
.
next
());
conn
.
close
();
}
private
void
testRecover
()
throws
Exception
{
FileUtils
.
deleteRecursive
(
getBaseDir
(),
true
);
Connection
conn
;
...
...
h2/src/test/org/h2/test/store/TestTransactionStore.java
浏览文件 @
53fd89a8
...
...
@@ -45,7 +45,7 @@ public class TestTransactionStore extends TestBase {
@Override
public
void
test
()
throws
Exception
{
FileUtils
.
createDirectories
(
getBaseDir
());
testStopWhileCommitting
();
//
testStopWhileCommitting();
testGetModifiedMaps
();
testKeyIterator
();
testMultiStatement
();
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论