mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efb42cfa47 | ||
|
|
29b8a70a2e | ||
|
|
d47cdbc188 | ||
|
|
56cb92bec6 | ||
|
|
fbf178ec68 | ||
|
|
648b0141fa | ||
|
|
d3f28558e7 | ||
|
|
17a3c25564 | ||
|
|
4ff493bb38 | ||
|
|
0e7d927217 | ||
|
|
bb2ab26b9f | ||
|
|
4ce0542eeb | ||
|
|
0d3679d0af | ||
|
|
317a78197b | ||
|
|
1a523dc802 | ||
|
|
5369e08d83 | ||
|
|
92f04d6c5f | ||
|
|
8e7a29ca1f | ||
|
|
eff6e2a549 | ||
|
|
b6c329e406 | ||
|
|
3e1a6dd665 | ||
|
|
1dcbe51bdf | ||
|
|
6d10405754 | ||
|
|
b4f8173984 | ||
|
|
d9aecd909f | ||
|
|
5dc9fbd117 | ||
|
|
4bd0cf541d | ||
|
|
f287f8dc93 | ||
|
|
ddd088c8b9 | ||
|
|
bcfc53b436 | ||
|
|
2d1dd5bce7 | ||
|
|
7d8d56413d | ||
|
|
671d15cbdd | ||
|
|
ee294c24ed | ||
|
|
d43b4cd570 | ||
|
|
d1ae67bdb9 | ||
|
|
7a8d7358bb | ||
|
|
f1219644ec | ||
|
|
b47cfec5d0 | ||
|
|
58c78358ef | ||
|
|
89695bfbaa | ||
|
|
bb4178066d | ||
|
|
a3b8c76d8e | ||
|
|
2c9df9f9e5 | ||
|
|
ec8111c5a4 | ||
|
|
5c76ba9dc4 | ||
|
|
60f3ec2ca5 | ||
|
|
f9842869cf | ||
|
|
f43edff9ac | ||
|
|
4a285d0dd3 | ||
|
|
773f914a40 | ||
|
|
ca1aee2a19 | ||
|
|
ba64a4cf52 | ||
|
|
7177dc46a1 | ||
|
|
9bbccda01e | ||
|
|
58c4aee42e | ||
|
|
a94aef6596 | ||
|
|
c8c1a549f0 | ||
|
|
b2584577fa | ||
|
|
641cc8d541 | ||
|
|
5efa0d6b0d | ||
|
|
63ace8ef0e | ||
|
|
6ce5580882 | ||
|
|
c292dc8540 | ||
|
|
a16d0cc494 | ||
|
|
b703a56e5c | ||
|
|
0f48e56f96 | ||
|
|
ad52626cf4 | ||
|
|
fd94353ea3 | ||
|
|
d68f9f4580 | ||
|
|
bee2f48a66 | ||
|
|
aa71718ac3 | ||
|
|
340ed4fac7 | ||
|
|
cb1bf09bfc | ||
|
|
267b9b1091 | ||
|
|
0153e14569 | ||
|
|
227b5d52df | ||
|
|
ebe89ef9d8 | ||
|
|
c8267120d8 | ||
|
|
5d6e8df1cf | ||
|
|
ae05a83e19 | ||
|
|
5851cb7fdb | ||
|
|
349db90497 | ||
|
|
000b9ec217 | ||
|
|
79edf0a7a8 | ||
|
|
d7d8605f56 | ||
|
|
cae22c53fb | ||
|
|
49c5a12dbe | ||
|
|
a3609b7dde | ||
|
|
cb094e13bb | ||
|
|
0834bbd0b1 | ||
|
|
83f7a8eed0 | ||
|
|
ab8f6c01a6 | ||
|
|
ff9d286877 | ||
|
|
b9f5900658 | ||
|
|
8843129ece | ||
|
|
f1302ebab8 | ||
|
|
1b59a490ef | ||
|
|
50e470791d | ||
|
|
e62986260f | ||
|
|
1e35584251 | ||
|
|
bd44ec2006 | ||
|
|
e9e0101ca4 | ||
|
|
a984040f0b | ||
|
|
87fe4bcab8 | ||
|
|
545d206040 | ||
|
|
eab4f49a2c | ||
|
|
f5576c3317 | ||
|
|
4d0c3b1f96 | ||
|
|
b5e4ee2e76 | ||
|
|
e264eebcd7 | ||
|
|
bd9f1d2d0f | ||
|
|
b09c018b4d | ||
|
|
a466120cd5 | ||
|
|
029b98984e | ||
|
|
eb7647ee6c | ||
|
|
6267ed251a | ||
|
|
f66026c8c7 | ||
|
|
f69e63dc5f | ||
|
|
596cc1547a | ||
|
|
767d1f3ff1 | ||
|
|
98094f6cac | ||
|
|
88ff80780b | ||
|
|
94c78b11e4 | ||
|
|
5d359fc337 | ||
|
|
74a334a2eb | ||
|
|
6a54278b4a | ||
|
|
09b534cc2f | ||
|
|
38a06aa225 | ||
|
|
02830a20f8 | ||
|
|
dc30a9b69b | ||
|
|
40aa520a51 | ||
|
|
2095ae8858 | ||
|
|
3d9d77d900 | ||
|
|
518cd1a62a | ||
|
|
dda474399a | ||
|
|
b2274da0e5 | ||
|
|
cd43446d01 | ||
|
|
931c9df886 | ||
|
|
5c0e304170 | ||
|
|
fb4c6a31ce | ||
|
|
f3a7847598 | ||
|
|
a13026fb2f | ||
|
|
c71f5bb9aa | ||
|
|
f82e693a31 | ||
|
|
29a198564d | ||
|
|
1583cb402e | ||
|
|
8149bb9d6a | ||
|
|
468ca61105 | ||
|
|
f0e8216197 | ||
|
|
ad27045d14 | ||
|
|
da7c89d79d | ||
|
|
a42757607d | ||
|
|
189e711b37 | ||
|
|
3c3252a06a | ||
|
|
6492430eaf | ||
|
|
f383641a1d | ||
|
|
92c60547fe | ||
|
|
e626016545 | ||
|
|
6a6aef25c1 | ||
|
|
f0bf3bf34b | ||
|
|
6451673f37 | ||
|
|
9fad3e21eb | ||
|
|
181bb43f08 | ||
|
|
25d81e4577 | ||
|
|
bdba6c56dd | ||
|
|
eea1cad850 | ||
|
|
930bfa5750 | ||
|
|
6a40ee5eb1 | ||
|
|
5d27d65bef | ||
|
|
3e994809a1 | ||
|
|
5882e847aa | ||
|
|
434ccf2df4 | ||
|
|
d51eb0b583 | ||
|
|
4479dff208 | ||
|
|
4e0f2aadb0 | ||
|
|
521d234bda | ||
|
|
36ea379cdc | ||
|
|
03c7ae24c2 | ||
|
|
a5debd7ed8 | ||
|
|
b02d0c238d | ||
|
|
25810ca9c7 | ||
|
|
210b49cac9 | ||
|
|
18864567c8 | ||
|
|
35e6ba734e | ||
|
|
3548e4220d | ||
|
|
8c7eb59838 | ||
|
|
506e8448be | ||
|
|
6eb317bb4c | ||
|
|
084a3c697c | ||
|
|
da96f2fe00 | ||
|
|
66d8360beb | ||
|
|
cd77d3c558 | ||
|
|
aa56b7e74a | ||
|
|
1c8cbf315f | ||
|
|
a7d103198e | ||
|
|
990b2f4cb3 | ||
|
|
72c8533f2c | ||
|
|
78a6e07c83 | ||
|
|
47fd574829 | ||
|
|
8272a6de57 | ||
|
|
df38c1ce66 | ||
|
|
de76909464 | ||
|
|
dc64c2f5cc | ||
|
|
c77aab584e | ||
|
|
5265c5709e | ||
|
|
7a73adda9c | ||
|
|
efa948741c | ||
|
|
3bdce20e2b | ||
|
|
d6862b3f51 | ||
|
|
01cfea6637 | ||
|
|
992dfc7811 | ||
|
|
6c2bf9e916 | ||
|
|
392f6d49e5 | ||
|
|
5b7e09bd6f | ||
|
|
248b6b551e | ||
|
|
baa5302447 | ||
|
|
d9280ff2d2 | ||
|
|
beb44ec3eb | ||
|
|
f1239d5f10 | ||
|
|
71a82a0abe | ||
|
|
3e63e553b4 | ||
|
|
b70967aac7 | ||
|
|
b5cad5c986 | ||
|
|
8295d364e2 | ||
|
|
29111e92b4 | ||
|
|
cca141ecf8 | ||
|
|
d655a3aab7 | ||
|
|
fe642cbee6 | ||
|
|
1966a7c055 | ||
|
|
85b2bde3dd | ||
|
|
3189398c00 | ||
|
|
f49e12b892 | ||
|
|
fefd4b98c5 | ||
|
|
ed9f5e21aa | ||
|
|
d3d20dcdca | ||
|
|
ef0fc1b461 | ||
|
|
313e877285 | ||
|
|
0bb555630f | ||
|
|
de00f28132 | ||
|
|
39c6c5fc1b | ||
|
|
479c566771 | ||
|
|
8d1e52165d | ||
|
|
59ef2ba559 | ||
|
|
306b9adfd8 | ||
|
|
c06c4c01c5 | ||
|
|
f0edf9d575 | ||
|
|
75e8b6dfcf | ||
|
|
7441a0ecba | ||
|
|
ebb9b2ed16 | ||
|
|
e8480d4d9d | ||
|
|
5234fc1b70 | ||
|
|
26015f3b48 | ||
|
|
14b3f683a1 | ||
|
|
d9d3cacaf5 | ||
|
|
ebcc8ae1d3 | ||
|
|
fa1b558299 | ||
|
|
120bc4715b | ||
|
|
127a850beb | ||
|
|
a703f16da9 | ||
|
|
f29dc1b906 | ||
|
|
09957ded1d | ||
|
|
d77476ef55 | ||
|
|
dae3b5545c | ||
|
|
a98317f555 | ||
|
|
106a94af15 | ||
|
|
d1edf4eced | ||
|
|
89ab1381f8 | ||
|
|
1f7f5a5a79 | ||
|
|
5f6adf3f6a | ||
|
|
8c072044d2 | ||
|
|
d69241586e | ||
|
|
2b4d5ceb47 | ||
|
|
9358178edc | ||
|
|
2a5463ae84 | ||
|
|
01e6badbb6 | ||
|
|
4774a9409b | ||
|
|
fe2bd190a5 | ||
|
|
75133b1b6b | ||
|
|
7ca9eb7542 | ||
|
|
3c5eed5ebe | ||
|
|
48e7effa79 | ||
|
|
52e6404e0f | ||
|
|
a4396f9218 | ||
|
|
88d85b6820 | ||
|
|
c84fad7a19 | ||
|
|
34f8ac0c99 | ||
|
|
1721635f76 | ||
|
|
959f86e5f8 | ||
|
|
a291f3a1e5 | ||
|
|
e50326f327 | ||
|
|
36c2a7cfb1 | ||
|
|
f2f6acbef3 | ||
|
|
b45b1cde3e | ||
|
|
20d49da90c | ||
|
|
b4fa51dfaf | ||
|
|
2263f86901 | ||
|
|
dcde292c3b | ||
|
|
fdc72a5c5d | ||
|
|
5a5c0492db | ||
|
|
8a1ecd1982 | ||
|
|
c4e00cf10a | ||
|
|
05ebfebc17 | ||
|
|
62eb2c23aa | ||
|
|
04d3ac4e63 | ||
|
|
79b6ab43ce | ||
|
|
04a2631dbe | ||
|
|
703f1375c2 | ||
|
|
186b3afaa8 | ||
|
|
a283800616 | ||
|
|
9a31b8dd2c | ||
|
|
0920bf4e68 | ||
|
|
18d2e4beb7 | ||
|
|
aef763b6d6 | ||
|
|
916e524134 | ||
|
|
0b80f6b380 | ||
|
|
77ebc82b92 | ||
|
|
68a2f94d5d | ||
|
|
06ea73d60b | ||
|
|
f83eecff99 | ||
|
|
b157d3d128 | ||
|
|
5e298f865b | ||
|
|
a661c0d208 | ||
|
|
bb474e9a02 | ||
|
|
06f378d75e | ||
|
|
c4f5d0aa15 | ||
|
|
48c8d8445e | ||
|
|
cf98df34c1 | ||
|
|
0f4244fe00 | ||
|
|
bcdc8c8b19 | ||
|
|
93f7e7a450 | ||
|
|
7d23210226 | ||
|
|
3c5d1b16b1 | ||
|
|
ed995c6a69 | ||
|
|
c2affccc18 | ||
|
|
26a33ee5bd | ||
|
|
4db46aa2e6 | ||
|
|
3231a2e581 | ||
|
|
ca89ac2ba9 | ||
|
|
c8c8104d7e | ||
|
|
4fc442029a | ||
|
|
34b55dd8c0 | ||
|
|
51a90415e2 | ||
|
|
5af9446ee6 | ||
|
|
a69d4deefb | ||
|
|
33b33235ff | ||
|
|
49ddd7ec4f | ||
|
|
eafb09a380 | ||
|
|
c5a64cffd2 | ||
|
|
62f70f6d14 | ||
|
|
79496d71ed | ||
|
|
d6b9b3b884 | ||
|
|
576d2d6c60 | ||
|
|
1218704b61 | ||
|
|
9144d1f186 | ||
|
|
2d049ab7e8 | ||
|
|
5d9a623e2c | ||
|
|
08809f5e6c | ||
|
|
10d14693ac | ||
|
|
39fb88f14e | ||
|
|
1a761e6a6c | ||
|
|
f48758e939 | ||
|
|
cf3a671733 | ||
|
|
199fabc197 | ||
|
|
d9c9f3c809 | ||
|
|
71cae59a99 | ||
|
|
00ed41daee | ||
|
|
c9a52cbdc8 | ||
|
|
8fe073324f | ||
|
|
31221bb7e8 | ||
|
|
dcb73e7735 | ||
|
|
30468d8eb4 | ||
|
|
59244447e3 | ||
|
|
4091597c67 | ||
|
|
842cdc11dd | ||
|
|
0ea57115a3 | ||
|
|
35e5689e11 | ||
|
|
32a6dd9a41 | ||
|
|
158da7a6ee | ||
|
|
d0d484b132 | ||
|
|
95604d13e9 | ||
|
|
4978caaa6f | ||
|
|
ee1818081f | ||
|
|
e2547103fd | ||
|
|
e242fa4664 | ||
|
|
bc7d1661a8 | ||
|
|
f184bee77b | ||
|
|
e1de88c8c7 | ||
|
|
fc53839bfa | ||
|
|
e07aa8669d | ||
|
|
8ec3e72551 | ||
|
|
43defe9872 | ||
|
|
d94aa2f7db | ||
|
|
27054d837b | ||
|
|
0cead31d10 | ||
|
|
5bf941966b | ||
|
|
1eded07f00 | ||
|
|
cbe0239270 | ||
|
|
08b8cea69f | ||
|
|
b2ba06856a | ||
|
|
8189c184ec | ||
|
|
ce8e88d2d7 | ||
|
|
16a5ac5b69 | ||
|
|
01013ae766 | ||
|
|
b1ad6ebba8 | ||
|
|
7fd9813b9f | ||
|
|
73ff15c07b | ||
|
|
dd9eca1c58 | ||
|
|
3cfc7515fc | ||
|
|
128f532858 | ||
|
|
e79df377c5 | ||
|
|
5d4fddfa52 | ||
|
|
cad99a6031 | ||
|
|
7d13f307ff | ||
|
|
55e03b67df | ||
|
|
d50c10ed37 | ||
|
|
301da345ae | ||
|
|
4e37251b4d | ||
|
|
856ac24484 | ||
|
|
6a4ec41fed | ||
|
|
349c7cceff | ||
|
|
1fb2e274c5 | ||
|
|
d56ac22b44 | ||
|
|
75714b4c08 | ||
|
|
ffc9f84649 | ||
|
|
f3a99e8a4d | ||
|
|
8641e9adf7 | ||
|
|
cd227d74ba | ||
|
|
e686caffec | ||
|
|
0ed98bf89e | ||
|
|
42135523a0 | ||
|
|
a3ab59d030 | ||
|
|
cf852fdf55 | ||
|
|
9e2c804fe6 | ||
|
|
a07175af65 | ||
|
|
77a8d4d476 | ||
|
|
8c79f79208 | ||
|
|
74f7d7551e | ||
|
|
e4feb78606 | ||
|
|
073285363e | ||
|
|
565b5bdc42 | ||
|
|
ace543a815 | ||
|
|
e8210e44da | ||
|
|
b9d6eccac1 | ||
|
|
7d65bd5ce4 | ||
|
|
f8d5c1b012 | ||
|
|
ec68091d19 | ||
|
|
f0dda35d7d | ||
|
|
46e3209e0d | ||
|
|
b1288cdc24 | ||
|
|
ca32fc1718 | ||
|
|
80bf8975fd | ||
|
|
e0be1bc4f1 | ||
|
|
da1c64b6e7 | ||
|
|
02bfc5831e | ||
|
|
8bf73208a4 | ||
|
|
71a69d9b68 | ||
|
|
1b0c9ce396 | ||
|
|
257b458121 | ||
|
|
671a7eb36f | ||
|
|
c15df15f07 | ||
|
|
68d949b3e3 | ||
|
|
81b6b09f6b | ||
|
|
311cd8cf2f | ||
|
|
75a333ad2d | ||
|
|
a62c6626e0 | ||
|
|
84001cfa96 | ||
|
|
abf931afa6 | ||
|
|
a914a1c6dc | ||
|
|
4ed3c1eb88 | ||
|
|
d6dfe516ff | ||
|
|
8d2b74d287 | ||
|
|
349542332a | ||
|
|
4fce44fc8b | ||
|
|
67e5b5420e | ||
|
|
2670fe8c73 | ||
|
|
a8b9891f95 | ||
|
|
0aa17c1002 | ||
|
|
4862720e08 | ||
|
|
ae25546a7a | ||
|
|
49666d76cf | ||
|
|
cad248f5c6 | ||
|
|
d3daa0db8b | ||
|
|
dde3ef1116 | ||
|
|
f8943ec003 | ||
|
|
8261e0026b | ||
|
|
64aabc9183 | ||
|
|
10e7de7705 | ||
|
|
21fca397cc | ||
|
|
f307479ba6 | ||
|
|
1b01d23be2 | ||
|
|
4048762cbe | ||
|
|
0463f61837 | ||
|
|
c41c60be13 | ||
|
|
b878f93c70 |
No files matched your search
@@ -47,6 +47,7 @@ rocksdb_undump
|
||||
db_test2
|
||||
trace_analyzer
|
||||
trace_analyzer_test
|
||||
.DS_Store
|
||||
|
||||
java/out
|
||||
java/target
|
||||
|
||||
+5
-5
@@ -1,5 +1,4 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
@@ -9,7 +8,7 @@ compiler:
|
||||
- gcc
|
||||
osx_image: xcode8.3
|
||||
jdk:
|
||||
- oraclejdk7
|
||||
- openjdk7
|
||||
cache:
|
||||
- ccache
|
||||
- apt
|
||||
@@ -71,7 +70,7 @@ install:
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
mkdir cmake-dist && curl --silent --fail --show-error --location https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
mkdir cmake-dist && curl -sfSL https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == java_test ]]; then
|
||||
java -version && echo "JAVA_HOME=${JAVA_HOME}";
|
||||
@@ -104,7 +103,7 @@ script:
|
||||
esac
|
||||
- case $JOB_NAME in
|
||||
java_test)
|
||||
OPT=-DTRAVIS V=1 make clean jclean && make rocksdbjava jtest
|
||||
OPT=-DTRAVIS V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 static_lib tools
|
||||
@@ -113,6 +112,7 @@ script:
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4
|
||||
;;
|
||||
cmake-mingw)
|
||||
sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix;
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
cmake*)
|
||||
|
||||
+115
-94
@@ -44,6 +44,12 @@ endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif(CCACHE_FOUND)
|
||||
|
||||
option(WITH_JEMALLOC "build with JeMalloc" OFF)
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
option(WITH_LZ4 "build with lz4" OFF)
|
||||
@@ -92,9 +98,15 @@ else()
|
||||
endif()
|
||||
|
||||
if(WITH_ZLIB)
|
||||
find_package(zlib REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
add_definitions(-DZLIB)
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
if(ZLIB_INCLUDE_DIRS)
|
||||
# CMake 3
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
else()
|
||||
# CMake 2
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
endif()
|
||||
list(APPEND THIRDPARTY_LIBS ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
@@ -171,6 +183,7 @@ else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers -Wno-strict-aliasing")
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
|
||||
add_definitions(-D_POSIX_C_SOURCE=1)
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
@@ -195,6 +208,15 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
endif(HAS_ALTIVEC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
CHECK_C_COMPILER_FLAG("-march=armv8-a+crc" HAS_ARMV8_CRC)
|
||||
if(HAS_ARMV8_CRC)
|
||||
message(STATUS " HAS_ARMV8_CRC yes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+crc -Wno-unused-function")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+crc -Wno-unused-function")
|
||||
endif(HAS_ARMV8_CRC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
@@ -207,7 +229,7 @@ else()
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
if(NOT HAVE_POWER8)
|
||||
if(NOT HAVE_POWER8 AND NOT HAS_ARMV8_CRC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
@@ -311,6 +333,10 @@ if(DISABLE_STALL_NOTIF)
|
||||
add_definitions(-DROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
endif()
|
||||
|
||||
option(WITH_DYNAMIC_EXTENSION "build with dynamic extension support" OFF)
|
||||
if(NOT WITH_DYNAMIC_EXTENSION)
|
||||
add_definitions(-DROCKSDB_NO_DYNAMIC_EXTENSION)
|
||||
endif()
|
||||
|
||||
if(DEFINED USE_RTTI)
|
||||
if(USE_RTTI)
|
||||
@@ -410,7 +436,7 @@ if(WITH_FALLOCATE)
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
int fd = open(\"/dev/null\", 0);
|
||||
fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 0, 1024);
|
||||
fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, 1024);
|
||||
}
|
||||
" HAVE_FALLOCATE)
|
||||
if(HAVE_FALLOCATE)
|
||||
@@ -455,8 +481,6 @@ include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.7.0/fused-src)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_subdirectory(third-party/gtest-1.7.0/fused-src/gtest)
|
||||
|
||||
# Main library source code
|
||||
|
||||
set(SOURCES
|
||||
@@ -467,22 +491,24 @@ set(SOURCES
|
||||
db/c.cc
|
||||
db/column_family.cc
|
||||
db/compacted_db_impl.cc
|
||||
db/compaction.cc
|
||||
db/compaction_iterator.cc
|
||||
db/compaction_job.cc
|
||||
db/compaction_picker.cc
|
||||
db/compaction_picker_fifo.cc
|
||||
db/compaction_picker_universal.cc
|
||||
db/compaction/compaction.cc
|
||||
db/compaction/compaction_iterator.cc
|
||||
db/compaction/compaction_picker.cc
|
||||
db/compaction/compaction_job.cc
|
||||
db/compaction/compaction_picker_fifo.cc
|
||||
db/compaction/compaction_picker_level.cc
|
||||
db/compaction/compaction_picker_universal.cc
|
||||
db/convenience.cc
|
||||
db/db_filesnapshot.cc
|
||||
db/db_impl.cc
|
||||
db/db_impl_write.cc
|
||||
db/db_impl_compaction_flush.cc
|
||||
db/db_impl_files.cc
|
||||
db/db_impl_open.cc
|
||||
db/db_impl_debug.cc
|
||||
db/db_impl_experimental.cc
|
||||
db/db_impl_readonly.cc
|
||||
db/db_impl/db_impl.cc
|
||||
db/db_impl/db_impl_write.cc
|
||||
db/db_impl/db_impl_compaction_flush.cc
|
||||
db/db_impl/db_impl_files.cc
|
||||
db/db_impl/db_impl_open.cc
|
||||
db/db_impl/db_impl_debug.cc
|
||||
db/db_impl/db_impl_experimental.cc
|
||||
db/db_impl/db_impl_readonly.cc
|
||||
db/db_impl/db_impl_secondary.cc
|
||||
db/db_info_dumper.cc
|
||||
db/db_iter.cc
|
||||
db/dbformat.cc
|
||||
@@ -523,8 +549,17 @@ set(SOURCES
|
||||
env/env_encryption.cc
|
||||
env/env_hdfs.cc
|
||||
env/mock_env.cc
|
||||
file/delete_scheduler.cc
|
||||
file/file_util.cc
|
||||
file/filename.cc
|
||||
file/sst_file_manager_impl.cc
|
||||
logging/auto_roll_logger.cc
|
||||
logging/event_logger.cc
|
||||
logging/log_buffer.cc
|
||||
memory/arena.cc
|
||||
memory/concurrent_arena.cc
|
||||
memory/jemalloc_nodump_allocator.cc
|
||||
memtable/alloc_tracker.cc
|
||||
memtable/hash_cuckoo_rep.cc
|
||||
memtable/hash_linklist_rep.cc
|
||||
memtable/hash_skiplist_rep.cc
|
||||
memtable/skiplistrep.cc
|
||||
@@ -532,10 +567,12 @@ set(SOURCES
|
||||
memtable/write_buffer_manager.cc
|
||||
monitoring/histogram.cc
|
||||
monitoring/histogram_windowing.cc
|
||||
monitoring/in_memory_stats_history.cc
|
||||
monitoring/instrumented_mutex.cc
|
||||
monitoring/iostats_context.cc
|
||||
monitoring/perf_context.cc
|
||||
monitoring/perf_level.cc
|
||||
monitoring/persistent_stats_history.cc
|
||||
monitoring/statistics.cc
|
||||
monitoring/thread_status_impl.cc
|
||||
monitoring/thread_status_updater.cc
|
||||
@@ -548,79 +585,72 @@ set(SOURCES
|
||||
options/options_parser.cc
|
||||
options/options_sanity_check.cc
|
||||
port/stack_trace.cc
|
||||
table/adaptive_table_factory.cc
|
||||
table/block.cc
|
||||
table/block_based_filter_block.cc
|
||||
table/block_based_table_builder.cc
|
||||
table/block_based_table_factory.cc
|
||||
table/block_based_table_reader.cc
|
||||
table/block_builder.cc
|
||||
table/adaptive/adaptive_table_factory.cc
|
||||
table/block_based/block.cc
|
||||
table/block_based/block_based_filter_block.cc
|
||||
table/block_based/block_based_table_builder.cc
|
||||
table/block_based/block_based_table_factory.cc
|
||||
table/block_based/block_based_table_reader.cc
|
||||
table/block_based/block_builder.cc
|
||||
table/block_based/block_prefix_index.cc
|
||||
table/block_based/data_block_hash_index.cc
|
||||
table/block_based/data_block_footer.cc
|
||||
table/block_based/flush_block_policy.cc
|
||||
table/block_based/full_filter_block.cc
|
||||
table/block_based/index_builder.cc
|
||||
table/block_based/partitioned_filter_block.cc
|
||||
table/block_fetcher.cc
|
||||
table/block_prefix_index.cc
|
||||
table/bloom_block.cc
|
||||
table/cuckoo_table_builder.cc
|
||||
table/cuckoo_table_factory.cc
|
||||
table/cuckoo_table_reader.cc
|
||||
table/data_block_hash_index.cc
|
||||
table/data_block_footer.cc
|
||||
table/flush_block_policy.cc
|
||||
table/cuckoo/cuckoo_table_builder.cc
|
||||
table/cuckoo/cuckoo_table_factory.cc
|
||||
table/cuckoo/cuckoo_table_reader.cc
|
||||
table/format.cc
|
||||
table/full_filter_block.cc
|
||||
table/get_context.cc
|
||||
table/index_builder.cc
|
||||
table/iterator.cc
|
||||
table/merging_iterator.cc
|
||||
table/meta_blocks.cc
|
||||
table/partitioned_filter_block.cc
|
||||
table/persistent_cache_helper.cc
|
||||
table/plain_table_builder.cc
|
||||
table/plain_table_factory.cc
|
||||
table/plain_table_index.cc
|
||||
table/plain_table_key_coding.cc
|
||||
table/plain_table_reader.cc
|
||||
table/plain/plain_table_builder.cc
|
||||
table/plain/plain_table_factory.cc
|
||||
table/plain/plain_table_index.cc
|
||||
table/plain/plain_table_key_coding.cc
|
||||
table/plain/plain_table_reader.cc
|
||||
table/sst_file_reader.cc
|
||||
table/sst_file_writer.cc
|
||||
table/table_properties.cc
|
||||
table/two_level_iterator.cc
|
||||
test_util/sync_point.cc
|
||||
test_util/sync_point_impl.cc
|
||||
test_util/testutil.cc
|
||||
test_util/transaction_test_util.cc
|
||||
tools/block_cache_trace_analyzer.cc
|
||||
tools/db_bench_tool.cc
|
||||
tools/dump/db_dump_tool.cc
|
||||
tools/ldb_cmd.cc
|
||||
tools/ldb_tool.cc
|
||||
tools/sst_dump_tool.cc
|
||||
tools/trace_analyzer_tool.cc
|
||||
util/arena.cc
|
||||
util/auto_roll_logger.cc
|
||||
trace_replay/trace_replay.cc
|
||||
trace_replay/block_cache_tracer.cc
|
||||
util/bloom.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
util/compression_context_cache.cc
|
||||
util/concurrent_arena.cc
|
||||
util/concurrent_task_limiter_impl.cc
|
||||
util/crc32c.cc
|
||||
util/delete_scheduler.cc
|
||||
util/dynamic_bloom.cc
|
||||
util/event_logger.cc
|
||||
util/file_reader_writer.cc
|
||||
util/file_util.cc
|
||||
util/filename.cc
|
||||
util/filter_policy.cc
|
||||
util/hash.cc
|
||||
util/jemalloc_nodump_allocator.cc
|
||||
util/log_buffer.cc
|
||||
util/murmurhash.cc
|
||||
util/random.cc
|
||||
util/rate_limiter.cc
|
||||
util/slice.cc
|
||||
util/sst_file_manager_impl.cc
|
||||
util/status.cc
|
||||
util/string_util.cc
|
||||
util/sync_point.cc
|
||||
util/sync_point_impl.cc
|
||||
util/testutil.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool_imp.cc
|
||||
util/trace_replay.cc
|
||||
util/transaction_test_util.cc
|
||||
util/xxhash.cc
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/blob_db/blob_compaction_filter.cc
|
||||
@@ -636,20 +666,11 @@ set(SOURCES
|
||||
utilities/cassandra/format.cc
|
||||
utilities/cassandra/merge_operator.cc
|
||||
utilities/checkpoint/checkpoint_impl.cc
|
||||
utilities/col_buf_decoder.cc
|
||||
utilities/col_buf_encoder.cc
|
||||
utilities/column_aware_encoding_util.cc
|
||||
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc
|
||||
utilities/date_tiered/date_tiered_db_impl.cc
|
||||
utilities/debug.cc
|
||||
utilities/document/document_db.cc
|
||||
utilities/document/json_document.cc
|
||||
utilities/document/json_document_builder.cc
|
||||
utilities/env_mirror.cc
|
||||
utilities/env_timed.cc
|
||||
utilities/geodb/geodb_impl.cc
|
||||
utilities/leveldb_options/leveldb_options.cc
|
||||
utilities/lua/rocks_lua_compaction_filter.cc
|
||||
utilities/memory/memory_util.cc
|
||||
utilities/merge_operators/bytesxor.cc
|
||||
utilities/merge_operators/max.cc
|
||||
@@ -664,9 +685,7 @@ set(SOURCES
|
||||
utilities/persistent_cache/block_cache_tier_metadata.cc
|
||||
utilities/persistent_cache/persistent_cache_tier.cc
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/redis/redis_lists.cc
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/spatialdb/spatial_db.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
utilities/transactions/optimistic_transaction_db_impl.cc
|
||||
@@ -699,6 +718,11 @@ if(HAVE_POWER8)
|
||||
util/crc32c_ppc_asm.S)
|
||||
endif(HAVE_POWER8)
|
||||
|
||||
if(HAS_ARMV8_CRC)
|
||||
list(APPEND SOURCES
|
||||
util/crc32c_arm64.cc)
|
||||
endif(HAS_ARMV8_CRC)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES
|
||||
port/win/io_win.cc
|
||||
@@ -822,6 +846,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
TARGETS ${ROCKSDB_SHARED_LIB}
|
||||
EXPORT RocksDBTargets
|
||||
COMPONENT runtime
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
|
||||
@@ -845,15 +870,16 @@ endif()
|
||||
|
||||
option(WITH_TESTS "build with tests" ON)
|
||||
if(WITH_TESTS)
|
||||
add_subdirectory(third-party/gtest-1.7.0/fused-src/gtest)
|
||||
set(TESTS
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
db/column_family_test.cc
|
||||
db/compact_files_test.cc
|
||||
db/compaction_iterator_test.cc
|
||||
db/compaction_job_stats_test.cc
|
||||
db/compaction_job_test.cc
|
||||
db/compaction_picker_test.cc
|
||||
db/compaction/compaction_job_stats_test.cc
|
||||
db/compaction/compaction_job_test.cc
|
||||
db/compaction/compaction_iterator_test.cc
|
||||
db/compaction/compaction_picker_test.cc
|
||||
db/comparator_db_test.cc
|
||||
db/corruption_test.cc
|
||||
db/cuckoo_table_db_test.cc
|
||||
@@ -876,6 +902,7 @@ if(WITH_TESTS)
|
||||
db/db_options_test.cc
|
||||
db/db_properties_test.cc
|
||||
db/db_range_del_test.cc
|
||||
db/db_impl/db_secondary_test.cc
|
||||
db/db_sst_test.cc
|
||||
db/db_statistics_test.cc
|
||||
db/db_table_properties_test.cc
|
||||
@@ -919,37 +946,40 @@ if(WITH_TESTS)
|
||||
env/env_basic_test.cc
|
||||
env/env_test.cc
|
||||
env/mock_env_test.cc
|
||||
file/delete_scheduler_test.cc
|
||||
logging/auto_roll_logger_test.cc
|
||||
logging/event_logger_test.cc
|
||||
memory/arena_test.cc
|
||||
memtable/inlineskiplist_test.cc
|
||||
memtable/skiplist_test.cc
|
||||
memtable/write_buffer_manager_test.cc
|
||||
monitoring/histogram_test.cc
|
||||
monitoring/iostats_context_test.cc
|
||||
monitoring/statistics_test.cc
|
||||
monitoring/stats_history_test.cc
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based_filter_block_test.cc
|
||||
table/block_test.cc
|
||||
table/block_based/block_based_filter_block_test.cc
|
||||
table/block_based/block_test.cc
|
||||
table/block_based/data_block_hash_index_test.cc
|
||||
table/block_based/full_filter_block_test.cc
|
||||
table/block_based/partitioned_filter_block_test.cc
|
||||
table/cleanable_test.cc
|
||||
table/cuckoo_table_builder_test.cc
|
||||
table/cuckoo_table_reader_test.cc
|
||||
table/data_block_hash_index_test.cc
|
||||
table/full_filter_block_test.cc
|
||||
table/cuckoo/cuckoo_table_builder_test.cc
|
||||
table/cuckoo/cuckoo_table_reader_test.cc
|
||||
table/merger_test.cc
|
||||
table/sst_file_reader_test.cc
|
||||
table/table_test.cc
|
||||
tools/block_cache_trace_analyzer_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
tools/sst_dump_test.cc
|
||||
tools/trace_analyzer_test.cc
|
||||
util/arena_test.cc
|
||||
util/auto_roll_logger_test.cc
|
||||
util/autovector_test.cc
|
||||
util/bloom_test.cc
|
||||
util/coding_test.cc
|
||||
util/crc32c_test.cc
|
||||
util/delete_scheduler_test.cc
|
||||
util/dynamic_bloom_test.cc
|
||||
util/event_logger_test.cc
|
||||
util/file_reader_writer_test.cc
|
||||
util/filelock_test.cc
|
||||
util/hash_test.cc
|
||||
@@ -967,12 +997,6 @@ if(WITH_TESTS)
|
||||
utilities/cassandra/cassandra_row_merge_test.cc
|
||||
utilities/cassandra/cassandra_serialize_test.cc
|
||||
utilities/checkpoint/checkpoint_test.cc
|
||||
utilities/column_aware_encoding_test.cc
|
||||
utilities/date_tiered/date_tiered_test.cc
|
||||
utilities/document/document_db_test.cc
|
||||
utilities/document/json_document_test.cc
|
||||
utilities/geodb/geodb_test.cc
|
||||
utilities/lua/rocks_lua_test.cc
|
||||
utilities/memory/memory_test.cc
|
||||
utilities/merge_operators/string_append/stringappend_test.cc
|
||||
utilities/object_registry_test.cc
|
||||
@@ -980,8 +1004,6 @@ if(WITH_TESTS)
|
||||
utilities/options/options_util_test.cc
|
||||
utilities/persistent_cache/hash_table_test.cc
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
utilities/redis/redis_lists_test.cc
|
||||
utilities/spatialdb/spatial_db_test.cc
|
||||
utilities/simulator_cache/sim_cache_test.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
@@ -1001,9 +1023,8 @@ if(WITH_TESTS)
|
||||
db/range_del_aggregator_bench.cc
|
||||
tools/db_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
utilities/column_aware_encoding_exp.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
add_library(testharness OBJECT util/testharness.cc)
|
||||
add_library(testharness OBJECT test_util/testharness.cc)
|
||||
foreach(sourcefile ${BENCHMARKS})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
@@ -1017,7 +1038,7 @@ if(WITH_TESTS)
|
||||
db/db_test_util.cc
|
||||
monitoring/thread_status_updater_debug.cc
|
||||
table/mock_table.cc
|
||||
util/fault_injection_test_env.cc
|
||||
test_util/fault_injection_test_env.cc
|
||||
utilities/cassandra/test_utils.cc
|
||||
)
|
||||
# test utilities are only build in debug
|
||||
|
||||
+145
-9
@@ -1,17 +1,156 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.18.4 (3/3/2020)
|
||||
* Various fixes for ARM64 support (#6250)
|
||||
* Fix JEMALLOC_CXX_THROW macro missing from older Jemalloc versions, causing build failures on some platforms.
|
||||
## 6.3.6 (10/1/2019)
|
||||
* Revert the feature "Merging iterator to avoid child iterator reseek for some cases (#5286)" since it might cause strong results when reseek happens with a different iterator upper bound.
|
||||
|
||||
## 5.18.3 (2/11/2019)
|
||||
## 6.3.5 (9/17/2019)
|
||||
* Fix a bug introduced 6.3 which could cause wrong results in a corner case when prefix bloom filter is used and the iterator is reseeked.
|
||||
|
||||
## 6.3.4 (9/3/2019)
|
||||
### Bug Fixes
|
||||
* Fix possible LSM corruption when both range deletions and subcompactions are used. The symptom of this corruption is L1+ files overlapping in the user key space.
|
||||
* Fix a bug in file ingestion caused by incorrect file number allocation when the number of column families involved in the ingestion exceeds 2.
|
||||
|
||||
## 5.18.2 (01/31/2019)
|
||||
## 6.3.3 (8/20/2019)
|
||||
### Bug Fixes
|
||||
* Fix a bug where the compaction snapshot refresh feature is not disabled as advertised when `snap_refresh_nanos` is set to 0..
|
||||
|
||||
## 6.3.2 (8/15/2019)
|
||||
### Public API Change
|
||||
* The semantics of the per-block-type block read counts in the performance context now match those of the generic block_read_count.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a regression where the fill_cache read option also affected index blocks.
|
||||
* Fixed an issue where using cache_index_and_filter_blocks==false affected partitions of partitioned indexes as well.
|
||||
|
||||
## 6.3.1 (7/24/2019)
|
||||
### Bug Fixes
|
||||
* Fix auto rolling bug introduced in 6.3.0, which causes segfault if log file creation fails.
|
||||
|
||||
## 6.3.0 (6/18/2019)
|
||||
### Public API Change
|
||||
* Now DB::Close() will return Aborted() error when there is unreleased snapshot. Users can retry after all snapshots are released.
|
||||
* Index blocks are now handled similarly to data blocks with regards to the block cache: instead of storing objects in the cache, only the blocks themselves are cached. In addition, index blocks no longer get evicted from the cache when a table is closed, can now use the compressed block cache (if any), and can be shared among multiple table readers.
|
||||
* Partitions of partitioned indexes no longer affect the read amplification statistics.
|
||||
* Due to the above refactoring, block cache eviction statistics for indexes are temporarily broken. We plan to reintroduce them in a later phase.
|
||||
* options.keep_log_file_num will be enforced strictly all the time. File names of all log files will be tracked, which may take significantly amount of memory if options.keep_log_file_num is large and either of options.max_log_file_size or options.log_file_time_to_roll is set.
|
||||
* Add initial support for Get/Put with user timestamps. Users can specify timestamps via ReadOptions and WriteOptions when calling DB::Get and DB::Put.
|
||||
* Accessing a partition of a partitioned filter or index through a pinned reference is no longer considered a cache hit.
|
||||
* Add C bindings for secondary instance, i.e. DBImplSecondary.
|
||||
* Rate limited deletion of WALs is only enabled if DBOptions::wal_dir is not set, or explicitly set to db_name passed to DB::Open and DBOptions::db_paths is empty, or same as db_paths[0].path
|
||||
|
||||
### New Features
|
||||
* Add an option `snap_refresh_nanos` (default to 0) to periodically refresh the snapshot list in compaction jobs. Assign to 0 to disable the feature.
|
||||
* Add an option `unordered_write` which trades snapshot guarantees with higher write throughput. When used with WRITE_PREPARED transactions with two_write_queues=true, it offers higher throughput with however no compromise on guarantees.
|
||||
* Allow DBImplSecondary to remove memtables with obsolete data after replaying MANIFEST and WAL.
|
||||
* Add an option `failed_move_fall_back_to_copy` (default is true) for external SST ingestion. When `move_files` is true and hard link fails, ingestion falls back to copy if `failed_move_fall_back_to_copy` is true. Otherwise, ingestion reports an error.
|
||||
|
||||
### Performance Improvements
|
||||
* Reduce binary search when iterator reseek into the same data block.
|
||||
* DBIter::Next() can skip user key checking if previous entry's seqnum is 0.
|
||||
* Merging iterator to avoid child iterator reseek for some cases
|
||||
* Log Writer will flush after finishing the whole record, rather than a fragment.
|
||||
* Lower MultiGet batching API latency by reading data blocks from disk in parallel
|
||||
|
||||
### General Improvements
|
||||
* Added new status code kColumnFamilyDropped to distinguish between Column Family Dropped and DB Shutdown in progress.
|
||||
* Improve ColumnFamilyOptions validation when creating a new column family.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in WAL replay of secondary instance by skipping write batches with older sequence numbers than the current last sequence number.
|
||||
* Fix flush's/compaction's merge processing logic which allowed `Put`s covered by range tombstones to reappear. Note `Put`s may exist even if the user only ever called `Merge()` due to an internal conversion during compaction to the bottommost level.
|
||||
* Fix/improve memtable earliest sequence assignment and WAL replay so that WAL entries of unflushed column families will not be skipped after replaying the MANIFEST and increasing db sequence due to another flushed/compacted column family.
|
||||
* Fix a bug caused by secondary not skipping the beginning of new MANIFEST.
|
||||
* On DB open, delete WAL trash files left behind in wal_dir
|
||||
|
||||
## 6.2.0 (4/30/2019)
|
||||
### New Features
|
||||
* Add an option `strict_bytes_per_sync` that causes a file-writing thread to block rather than exceed the limit on bytes pending writeback specified by `bytes_per_sync` or `wal_bytes_per_sync`.
|
||||
* Improve range scan performance by avoiding per-key upper bound check in BlockBasedTableIterator.
|
||||
* Introduce Periodic Compaction for Level style compaction. Files are re-compacted periodically and put in the same level.
|
||||
* Block-based table index now contains exact highest key in the file, rather than an upper bound. This may improve Get() and iterator Seek() performance in some situations, especially when direct IO is enabled and block cache is disabled. A setting BlockBasedTableOptions::index_shortening is introduced to control this behavior. Set it to kShortenSeparatorsAndSuccessor to get the old behavior.
|
||||
* When reading from option file/string/map, customized envs can be filled according to object registry.
|
||||
* Improve range scan performance when using explicit user readahead by not creating new table readers for every iterator.
|
||||
|
||||
### Public API Change
|
||||
* Change the behavior of OptimizeForPointLookup(): move away from hash-based block-based-table index, and use whole key memtable filtering.
|
||||
* Change the behavior of OptimizeForSmallDb(): use a 16MB block cache, put index and filter blocks into it, and cost the memtable size to it. DBOptions.OptimizeForSmallDb() and ColumnFamilyOptions.OptimizeForSmallDb() start to take an optional cache object.
|
||||
* Added BottommostLevelCompaction::kForceOptimized to avoid double compacting newly compacted files in the bottommost level compaction of manual compaction. Note this option may prohibit the manual compaction to produce a single file in the bottommost level.
|
||||
|
||||
### Bug Fixes
|
||||
* Adjust WriteBufferManager's dummy entry size to block cache from 1MB to 256KB.
|
||||
* Fix a race condition between WritePrepared::Get and ::Put with duplicate keys.
|
||||
* Fix crash when memtable prefix bloom is enabled and read/write a key out of domain of prefix extractor.
|
||||
* Close a WAL file before another thread deletes it.
|
||||
* Fix an assertion failure `IsFlushPending() == true` caused by one bg thread releasing the db mutex in ~ColumnFamilyData and another thread clearing `flush_requested_` flag.
|
||||
|
||||
## 6.1.1 (4/9/2019)
|
||||
### New Features
|
||||
* When reading from option file/string/map, customized comparators and/or merge operators can be filled according to object registry.
|
||||
|
||||
### Public API Change
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a bug in 2PC where a sequence of txn prepare, memtable flush, and crash could result in losing the prepared transaction.
|
||||
* Fix a bug in Encryption Env which could cause encrypted files to be read beyond file boundaries.
|
||||
|
||||
## 6.1.0 (3/27/2019)
|
||||
### New Features
|
||||
* Introduce two more stats levels, kExceptHistogramOrTimers and kExceptTimers.
|
||||
* Added a feature to perform data-block sampling for compressibility, and report stats to user.
|
||||
* Add support for trace filtering.
|
||||
* Add DBOptions.avoid_unnecessary_blocking_io. If true, we avoid file deletion when destorying ColumnFamilyHandle and Iterator. Instead, a job is scheduled to delete the files in background.
|
||||
|
||||
### Public API Change
|
||||
* Remove bundled fbson library.
|
||||
* statistics.stats_level_ becomes atomic. It is preferred to use statistics.set_stats_level() and statistics.get_stats_level() to access it.
|
||||
* Introduce a new IOError subcode, PathNotFound, to indicate trying to open a nonexistent file or directory for read.
|
||||
* Add initial support for multiple db instances sharing the same data in single-writer, multi-reader mode.
|
||||
* Removed some "using std::xxx" from public headers.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix JEMALLOC_CXX_THROW macro missing from older Jemalloc versions, causing build failures on some platforms.
|
||||
* Fix SstFileReader not able to open file ingested with write_glbal_seqno=true.
|
||||
|
||||
## 6.0.0 (2/19/2019)
|
||||
### New Features
|
||||
* Enabled checkpoint on readonly db (DBImplReadOnly).
|
||||
* Make DB ignore dropped column families while committing results of atomic flush.
|
||||
* RocksDB may choose to preopen some files even if options.max_open_files != -1. This may make DB open slightly longer.
|
||||
* For users of dictionary compression with ZSTD v0.7.0+, we now reuse the same digested dictionary when compressing each of an SST file's data blocks for faster compression speeds.
|
||||
* For all users of dictionary compression who set `cache_index_and_filter_blocks == true`, we now store dictionary data used for decompression in the block cache for better control over memory usage. For users of ZSTD v1.1.4+ who compile with -DZSTD_STATIC_LINKING_ONLY, this includes a digested dictionary, which is used to increase decompression speed.
|
||||
* Add support for block checksums verification for external SST files before ingestion.
|
||||
* Introduce stats history which periodically saves Statistics snapshots and added `GetStatsHistory` API to retrieve these snapshots.
|
||||
* Add a place holder in manifest which indicate a record from future that can be safely ignored.
|
||||
* Add support for trace sampling.
|
||||
* Enable properties block checksum verification for block-based tables.
|
||||
* For all users of dictionary compression, we now generate a separate dictionary for compressing each bottom-level SST file. Previously we reused a single dictionary for a whole compaction to bottom level. The new approach achieves better compression ratios; however, it uses more memory and CPU for buffering/sampling data blocks and training dictionaries.
|
||||
* Add whole key bloom filter support in memtable.
|
||||
* Files written by `SstFileWriter` will now use dictionary compression if it is configured in the file writer's `CompressionOptions`.
|
||||
|
||||
### Public API Change
|
||||
* Disallow CompactionFilter::IgnoreSnapshots() = false, because it is not very useful and the behavior is confusing. The filter will filter everything if there is no snapshot declared by the time the compaction starts. However, users can define a snapshot after the compaction starts and before it finishes and this new snapshot won't be repeatable, because after the compaction finishes, some keys may be dropped.
|
||||
* CompactionPri = kMinOverlappingRatio also uses compensated file size, which boosts file with lots of tombstones to be compacted first.
|
||||
* Transaction::GetForUpdate is extended with a do_validate parameter with default value of true. If false it skips validating the snapshot before doing the read. Similarly ::Merge, ::Put, ::Delete, and ::SingleDelete are extended with assume_tracked with default value of false. If true it indicates that call is assumed to be after a ::GetForUpdate.
|
||||
* `TableProperties::num_entries` and `TableProperties::num_deletions` now also account for number of range tombstones.
|
||||
* Remove geodb, spatial_db, document_db, json_document, date_tiered_db, and redis_lists.
|
||||
* With "ldb ----try_load_options", when wal_dir specified by the option file doesn't exist, ignore it.
|
||||
* Change time resolution in FileOperationInfo.
|
||||
* Deleting Blob files also go through SStFileManager.
|
||||
* Remove CuckooHash memtable.
|
||||
* The counter stat `number.block.not_compressed` now also counts blocks not compressed due to poor compression ratio.
|
||||
* Remove ttl option from `CompactionOptionsFIFO`. The option has been deprecated and ttl in `ColumnFamilyOptions` is used instead.
|
||||
* Support SST file ingestion across multiple column families via DB::IngestExternalFiles. See the function's comment about atomicity.
|
||||
* Remove Lua compaction filter.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a deadlock caused by compaction and file ingestion waiting for each other in the event of write stalls.
|
||||
* Fix a memory leak when files with range tombstones are read in mmap mode and block cache is enabled
|
||||
* Fix handling of corrupt range tombstone blocks such that corruptions cannot cause deleted keys to reappear
|
||||
* Lock free MultiGet
|
||||
* Fix incorrect `NotFound` point lookup result when querying the endpoint of a file that has been extended by a range tombstone.
|
||||
* Fix with pipelined write, write leaders's callback failure lead to the whole write group fail.
|
||||
|
||||
### Change Default Options
|
||||
* Change options.compaction_pri's default to kMinOverlappingRatio
|
||||
|
||||
## 5.18.0 (11/30/2018)
|
||||
### New Features
|
||||
@@ -24,7 +163,6 @@
|
||||
* Add xxhash64 checksum support
|
||||
* Introduced `MemoryAllocator`, which lets the user specify custom memory allocator for block based table.
|
||||
* Improved `DeleteRange` to prevent read performance degradation. The feature is no longer marked as experimental.
|
||||
* Enabled checkpoint on readonly db (DBImplReadOnly).
|
||||
|
||||
### Public API Change
|
||||
* `DBOptions::use_direct_reads` now affects reads issued by `BackupEngine` on the database's SSTs.
|
||||
@@ -41,8 +179,6 @@
|
||||
* Fixed Get correctness bug in the presence of range tombstones where merge operands covered by a range tombstone always result in NotFound.
|
||||
* Start populating `NO_FILE_CLOSES` ticker statistic, which was always zero previously.
|
||||
* The default value of NewBloomFilterPolicy()'s argument use_block_based_builder is changed to false. Note that this new default may cause large temp memory usage when building very large SST files.
|
||||
* Fix a deadlock caused by compaction and file ingestion waiting for each other in the event of write stalls.
|
||||
* Make DB ignore dropped column families while committing results of atomic flush.
|
||||
|
||||
## 5.17.0 (10/05/2018)
|
||||
### Public API Change
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
This is the list of all known third-party language bindings for RocksDB. If something is missing, please open a pull request to add it.
|
||||
|
||||
* Java - https://github.com/facebook/rocksdb/tree/master/java
|
||||
* Python - http://pyrocksdb.readthedocs.org/en/latest/
|
||||
* Python
|
||||
* http://python-rocksdb.readthedocs.io/en/latest/
|
||||
* http://pyrocksdb.readthedocs.org/en/latest/ (unmaintained)
|
||||
* Perl - https://metacpan.org/pod/RocksDB
|
||||
* Node.js - https://npmjs.org/package/rocksdb
|
||||
* Go - https://github.com/tecbot/gorocksdb
|
||||
|
||||
@@ -82,17 +82,23 @@ ifeq ($(MAKECMDGOALS),rocksdbjavastatic)
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticrelease)
|
||||
DEBUG_LEVEL=0
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticreleasedocker)
|
||||
DEBUG_LEVEL=0
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticpublish)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
|
||||
$(info $$DEBUG_LEVEL is ${DEBUG_LEVEL})
|
||||
|
||||
# Lite build flag.
|
||||
LITE ?= 0
|
||||
ifeq ($(LITE), 0)
|
||||
@@ -204,7 +210,7 @@ include make_config.mk
|
||||
CLEAN_FILES += make_config.mk
|
||||
|
||||
missing_make_config_paths := $(shell \
|
||||
grep "\/\S*" -o $(CURDIR)/make_config.mk | \
|
||||
grep "\./\S*\|/\S*" -o $(CURDIR)/make_config.mk | \
|
||||
while read path; \
|
||||
do [ -e $$path ] || echo $$path; \
|
||||
done | sort | uniq)
|
||||
@@ -398,8 +404,8 @@ LIBOBJECTS += $(TOOL_LIB_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_LIB_SOURCES:.cc=.o)
|
||||
|
||||
GTEST = $(GTEST_DIR)/gtest/gtest-all.o
|
||||
TESTUTIL = ./util/testutil.o
|
||||
TESTHARNESS = ./util/testharness.o $(TESTUTIL) $(MOCKOBJECTS) $(GTEST)
|
||||
TESTUTIL = ./test_util/testutil.o
|
||||
TESTHARNESS = ./test_util/testharness.o $(TESTUTIL) $(MOCKOBJECTS) $(GTEST)
|
||||
VALGRIND_ERROR = 2
|
||||
VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
|
||||
|
||||
@@ -409,7 +415,7 @@ BENCHTOOLOBJECTS = $(BENCH_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
ANALYZETOOLOBJECTS = $(ANALYZER_LIB_SOURCES:.cc=.o)
|
||||
|
||||
EXPOBJECTS = $(EXP_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
EXPOBJECTS = $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
TESTS = \
|
||||
db_basic_test \
|
||||
@@ -449,6 +455,7 @@ TESTS = \
|
||||
db_merge_operator_test \
|
||||
db_options_test \
|
||||
db_range_del_test \
|
||||
db_secondary_test \
|
||||
db_sst_test \
|
||||
db_tailing_iter_test \
|
||||
db_io_failure_test \
|
||||
@@ -488,7 +495,6 @@ TESTS = \
|
||||
merger_test \
|
||||
util_merge_operators_test \
|
||||
options_file_test \
|
||||
redis_test \
|
||||
reduce_levels_test \
|
||||
plain_table_db_test \
|
||||
comparator_db_test \
|
||||
@@ -502,12 +508,8 @@ TESTS = \
|
||||
cassandra_row_merge_test \
|
||||
cassandra_serialize_test \
|
||||
ttl_test \
|
||||
date_tiered_test \
|
||||
backupable_db_test \
|
||||
document_db_test \
|
||||
json_document_test \
|
||||
sim_cache_test \
|
||||
spatial_db_test \
|
||||
version_edit_test \
|
||||
version_set_test \
|
||||
compaction_picker_test \
|
||||
@@ -519,7 +521,6 @@ TESTS = \
|
||||
deletefile_test \
|
||||
obsolete_files_test \
|
||||
table_test \
|
||||
geodb_test \
|
||||
delete_scheduler_test \
|
||||
options_test \
|
||||
options_settable_test \
|
||||
@@ -536,7 +537,6 @@ TESTS = \
|
||||
compaction_job_test \
|
||||
thread_list_test \
|
||||
sst_dump_test \
|
||||
column_aware_encoding_test \
|
||||
compact_files_test \
|
||||
optimistic_transaction_test \
|
||||
write_callback_test \
|
||||
@@ -548,7 +548,7 @@ TESTS = \
|
||||
ldb_cmd_test \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
lua_test \
|
||||
stats_history_test \
|
||||
lru_cache_test \
|
||||
object_registry_test \
|
||||
repair_test \
|
||||
@@ -561,6 +561,9 @@ TESTS = \
|
||||
range_tombstone_fragmenter_test \
|
||||
range_del_aggregator_test \
|
||||
sst_file_reader_test \
|
||||
db_secondary_test \
|
||||
block_cache_tracer_test \
|
||||
block_cache_trace_analyzer_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -605,12 +608,13 @@ TOOLS = \
|
||||
rocksdb_undump \
|
||||
blob_dump \
|
||||
trace_analyzer \
|
||||
block_cache_trace_analyzer \
|
||||
|
||||
TEST_LIBS = \
|
||||
librocksdb_env_basic_test.a
|
||||
|
||||
# TODO: add back forward_iterator_bench, after making it build in all environemnts.
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench column_aware_encoding_exp persistent_cache_bench range_del_aggregator_bench
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench persistent_cache_bench range_del_aggregator_bench
|
||||
|
||||
# if user didn't config LIBNAME, set the default
|
||||
ifeq ($(LIBNAME),)
|
||||
@@ -700,7 +704,8 @@ endif # PLATFORM_SHARED_EXT
|
||||
.PHONY: blackbox_crash_test check clean coverage crash_test ldb_tests package \
|
||||
release tags tags0 valgrind_check whitebox_crash_test format static_lib shared_lib all \
|
||||
dbg rocksdbjavastatic rocksdbjava install install-static install-shared uninstall \
|
||||
analyze tools tools_lib
|
||||
analyze tools tools_lib \
|
||||
blackbox_crash_test_with_atomic_flush whitebox_crash_test_with_atomic_flush
|
||||
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
@@ -830,7 +835,6 @@ J ?= 100%
|
||||
# Use this regexp to select the subset of tests whose names match.
|
||||
tests-regexp = .
|
||||
|
||||
t_run = $(wildcard t/run-*)
|
||||
.PHONY: check_0
|
||||
check_0:
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
@@ -840,7 +844,7 @@ check_0:
|
||||
test -t 1 && eta=--eta || eta=; \
|
||||
{ \
|
||||
printf './%s\n' $(filter-out $(PARALLEL_TEST),$(TESTS)); \
|
||||
printf '%s\n' $(t_run); \
|
||||
find t -name 'run-*' -print; \
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
@@ -857,7 +861,7 @@ valgrind_check_0:
|
||||
test -t 1 && eta=--eta || eta=; \
|
||||
{ \
|
||||
printf './%s\n' $(filter-out $(PARALLEL_TEST) %skiplist_test options_settable_test, $(TESTS)); \
|
||||
printf '%s\n' $(t_run); \
|
||||
find t -name 'run-*' -print; \
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
@@ -909,23 +913,29 @@ ldb_tests: ldb
|
||||
|
||||
crash_test: whitebox_crash_test blackbox_crash_test
|
||||
|
||||
crash_test_with_atomic_flush: whitebox_crash_test_with_atomic_flush blackbox_crash_test_with_atomic_flush
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_atomic_flush: db_stress
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
python -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
whitebox_crash_test_with_atomic_flush: db_stress
|
||||
python -u tools/db_crashtest.py --enable_atomic_flush whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD) $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
asan_check:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) check -j32
|
||||
@@ -936,6 +946,11 @@ asan_crash_test:
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_atomic_flush:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_check:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) check -j32
|
||||
@@ -946,6 +961,11 @@ ubsan_crash_test:
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_crash_test_with_atomic_flush:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test_with_atomic_flush
|
||||
$(MAKE) clean
|
||||
|
||||
valgrind_test:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
@@ -1090,6 +1110,9 @@ db_bench: tools/db_bench.o $(BENCHTOOLOBJECTS)
|
||||
trace_analyzer: tools/trace_analyzer.o $(ANALYZETOOLOBJECTS) $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_trace_analyzer: tools/block_cache_trace_analyzer_tool.o $(ANALYZETOOLOBJECTS) $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_bench: cache/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1111,7 +1134,7 @@ db_sanity_test: tools/db_sanity_test.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
db_repl_stress: tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
arena_test: memory/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1159,9 +1182,6 @@ cassandra_row_merge_test: utilities/cassandra/cassandra_row_merge_test.o utiliti
|
||||
cassandra_serialize_test: utilities/cassandra/cassandra_serialize_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
redis_test: utilities/redis/redis_lists_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
hash_table_test: utilities/persistent_cache/hash_table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1300,18 +1320,9 @@ backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TE
|
||||
checkpoint_test: utilities/checkpoint/checkpoint_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
document_db_test: utilities/document/document_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
json_document_test: utilities/document/json_document_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sim_cache_test: utilities/simulator_cache/sim_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
spatial_db_test: utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_mirror_test: utilities/env_mirror_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1329,22 +1340,19 @@ object_registry_test: utilities/object_registry_test.o $(LIBOBJECTS) $(TESTHARNE
|
||||
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
date_tiered_test: utilities/date_tiered/date_tiered_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
write_batch_with_index_test: utilities/write_batch_with_index/write_batch_with_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
flush_job_test: db/flush_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_iterator_test: db/compaction_iterator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compaction_iterator_test: db/compaction/compaction_iterator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_job_test: db/compaction_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compaction_job_test: db/compaction/compaction_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_job_stats_test: db/compaction_job_stats_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compaction_job_stats_test: db/compaction/compaction_job_stats_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_on_deletion_collector_test: utilities/table_properties_collectors/compact_on_deletion_collector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1368,7 +1376,7 @@ fault_injection_test: db/fault_injection_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
rate_limiter_test: util/rate_limiter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
delete_scheduler_test: util/delete_scheduler_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
delete_scheduler_test: file/delete_scheduler_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1377,13 +1385,13 @@ filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
file_reader_writer_test: util/file_reader_writer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_filter_block_test: table/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
block_based_filter_block_test: table/block_based/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
full_filter_block_test: table/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
full_filter_block_test: table/block_based/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
partitioned_filter_block_test: table/partitioned_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
partitioned_filter_block_test: table/block_based/partitioned_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1395,10 +1403,10 @@ cleanable_test: table/cleanable_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_test: table/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
block_test: table/block_based/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
data_block_hash_index_test: table/data_block_hash_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
data_block_hash_index_test: table/block_based/data_block_hash_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
inlineskiplist_test: memtable/inlineskiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1416,7 +1424,7 @@ version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_picker_test: db/compaction_picker_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
compaction_picker_test: db/compaction/compaction_picker_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
version_builder_test: db/version_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1458,19 +1466,16 @@ deletefile_test: db/deletefile_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
obsolete_files_test: db/obsolete_files_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
geodb_test: utilities/geodb/geodb_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
rocksdb_dump: tools/dump/rocksdb_dump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
rocksdb_undump: tools/dump/rocksdb_undump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_builder_test: table/cuckoo_table_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cuckoo_table_builder_test: table/cuckoo/cuckoo_table_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_reader_test: table/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
cuckoo_table_reader_test: table/cuckoo/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_db_test: db/cuckoo_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1500,7 +1505,7 @@ db_bench_tool_test: tools/db_bench_tool_test.o $(BENCHTOOLOBJECTS) $(TESTHARNESS
|
||||
trace_analyzer_test: tools/trace_analyzer_test.o $(LIBOBJECTS) $(ANALYZETOOLOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
event_logger_test: util/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
event_logger_test: logging/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
timer_queue_test: util/timer_queue_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1509,9 +1514,6 @@ timer_queue_test: util/timer_queue_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
sst_dump_test: tools/sst_dump_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_aware_encoding_test: utilities/column_aware_encoding_test.o $(TESTHARNESS) $(EXPOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
optimistic_transaction_test: utilities/transactions/optimistic_transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1524,7 +1526,7 @@ manual_compaction_test: db/manual_compaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
filelock_test: util/filelock_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
auto_roll_logger_test: util/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
auto_roll_logger_test: logging/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memtable_list_test: db/memtable_list_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1551,9 +1553,6 @@ sst_dump: tools/sst_dump.o $(LIBOBJECTS)
|
||||
blob_dump: tools/blob_dump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_aware_encoding_exp: utilities/column_aware_encoding_exp.o $(EXPOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
repair_test: db/repair_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1572,10 +1571,10 @@ persistent_cache_test: utilities/persistent_cache/persistent_cache_test.o db/db
|
||||
statistics_test: monitoring/statistics_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_cache_test: cache/lru_cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
stats_history_test: monitoring/stats_history_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lua_test: utilities/lua/rocks_lua_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
lru_cache_test: cache/lru_cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
range_del_aggregator_test: db/range_del_aggregator_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1596,6 +1595,15 @@ range_tombstone_fragmenter_test: db/range_tombstone_fragmenter_test.o db/db_test
|
||||
sst_file_reader_test: table/sst_file_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_secondary_test: db/db_impl/db_secondary_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_tracer_test: trace_replay/block_cache_tracer_test.o trace_replay/block_cache_tracer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_cache_trace_analyzer_test: tools/block_cache_trace_analyzer_test.o tools/block_cache_trace_analyzer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
@@ -1641,7 +1649,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
else ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ifneq (,$(filter $(MACHINE), amd64 arm64 aarch64 sparc64))
|
||||
ARCH := 64
|
||||
else
|
||||
ARCH := 32
|
||||
@@ -1650,10 +1658,10 @@ else
|
||||
ARCH := $(shell getconf LONG_BIT)
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ppc% arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
ifeq (,$(filter $(MACHINE), ppc arm64 aarch64 sparc64))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH).so
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE).so
|
||||
endif
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux$(ARCH).jar
|
||||
ROCKSDB_JAR_ALL = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
@@ -1666,15 +1674,15 @@ ZLIB_SHA256 ?= c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1
|
||||
ZLIB_DOWNLOAD_BASE ?= http://zlib.net
|
||||
BZIP2_VER ?= 1.0.6
|
||||
BZIP2_SHA256 ?= a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
|
||||
BZIP2_DOWNLOAD_BASE ?= https://downloads.sourceforge.net/project/bzip2
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
BZIP2_DOWNLOAD_BASE ?= https://web.archive.org/web/20180624184835/http://www.bzip.org
|
||||
SNAPPY_VER ?= 1.1.7
|
||||
SNAPPY_SHA256 ?= 3dfa02e873ff51a11ee02b9ca391807f0c8ea0529a4924afa645fbf97163f9d4
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.2
|
||||
LZ4_SHA256 ?= 658ba6191fa44c92280d4aa2c271b0f4fbc0e34d249578dd05e50e76d0e5efcc
|
||||
LZ4_VER ?= 1.8.3
|
||||
LZ4_SHA256 ?= 33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.4.4
|
||||
ZSTD_SHA256 ?= a364f5162c7d1a455cc915e8e3cf5f4bd8b75d09bc0f53965b0c9ca1383c52c8
|
||||
ZSTD_VER ?= 1.4.0
|
||||
ZSTD_SHA256 ?= 63be339137d2b683c6d19a9e34f4fb684790e864fee13c7dd40e197a64c705c1
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -1714,7 +1722,7 @@ endif
|
||||
libz.a:
|
||||
-rm -rf zlib-$(ZLIB_VER)
|
||||
ifeq (,$(wildcard ./zlib-$(ZLIB_VER).tar.gz))
|
||||
curl --fail --output zlib-$(ZLIB_VER).tar.gz --location ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
curl --output zlib-$(ZLIB_VER).tar.gz -L ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
endif
|
||||
ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZLIB_SHA256)" != "$$ZLIB_SHA256_ACTUAL" ]; then \
|
||||
@@ -1728,7 +1736,7 @@ endif
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-$(BZIP2_VER)
|
||||
ifeq (,$(wildcard ./bzip2-$(BZIP2_VER).tar.gz))
|
||||
curl --fail --output bzip2-$(BZIP2_VER).tar.gz --location ${CURL_SSL_OPTS} ${BZIP2_DOWNLOAD_BASE}/bzip2-$(BZIP2_VER).tar.gz
|
||||
curl --output bzip2-$(BZIP2_VER).tar.gz -L ${BZIP2_DOWNLOAD_BASE}/$(BZIP2_VER)/bzip2-$(BZIP2_VER).tar.gz
|
||||
endif
|
||||
BZIP2_SHA256_ACTUAL=`$(SHA256_CMD) bzip2-$(BZIP2_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(BZIP2_SHA256)" != "$$BZIP2_SHA256_ACTUAL" ]; then \
|
||||
@@ -1742,7 +1750,7 @@ endif
|
||||
libsnappy.a:
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
ifeq (,$(wildcard ./snappy-$(SNAPPY_VER).tar.gz))
|
||||
curl --fail --output snappy-$(SNAPPY_VER).tar.gz --location ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER).tar.gz
|
||||
curl --output snappy-$(SNAPPY_VER).tar.gz -L ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER).tar.gz
|
||||
endif
|
||||
SNAPPY_SHA256_ACTUAL=`$(SHA256_CMD) snappy-$(SNAPPY_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(SNAPPY_SHA256)" != "$$SNAPPY_SHA256_ACTUAL" ]; then \
|
||||
@@ -1757,7 +1765,7 @@ endif
|
||||
liblz4.a:
|
||||
-rm -rf lz4-$(LZ4_VER)
|
||||
ifeq (,$(wildcard ./lz4-$(LZ4_VER).tar.gz))
|
||||
curl --fail --output lz4-$(LZ4_VER).tar.gz --location ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
|
||||
curl --output lz4-$(LZ4_VER).tar.gz -L ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
|
||||
endif
|
||||
LZ4_SHA256_ACTUAL=`$(SHA256_CMD) lz4-$(LZ4_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(LZ4_SHA256)" != "$$LZ4_SHA256_ACTUAL" ]; then \
|
||||
@@ -1771,7 +1779,7 @@ endif
|
||||
libzstd.a:
|
||||
-rm -rf zstd-$(ZSTD_VER)
|
||||
ifeq (,$(wildcard ./zstd-$(ZSTD_VER).tar.gz))
|
||||
curl --fail --output zstd-$(ZSTD_VER).tar.gz --location ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(ZSTD_VER).tar.gz
|
||||
curl --output zstd-$(ZSTD_VER).tar.gz -L ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(ZSTD_VER).tar.gz
|
||||
endif
|
||||
ZSTD_SHA256_ACTUAL=`$(SHA256_CMD) zstd-$(ZSTD_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZSTD_SHA256)" != "$$ZSTD_SHA256_ACTUAL" ]; then \
|
||||
@@ -1841,27 +1849,15 @@ rocksdbjavastaticreleasedocker: rocksdbjavastatic rocksdbjavastaticdockerx86 roc
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_X86_CONTAINER=`docker ps -aqf name=rocksdb_linux_x86-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X86_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x86-be evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x86-be
|
||||
docker run --rm --name rocksdb_linux_x86-be --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_X64_CONTAINER=`docker ps -aqf name=rocksdb_linux_x64-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_X64_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_x64-be evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_x64-be
|
||||
docker run --rm --name rocksdb_linux_x64-be --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x64-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerppc64le:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_PPC64LE_CONTAINER=`docker ps -aqf name=rocksdb_linux_ppc64le-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_PPC64LE_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_ppc64le-be evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_ppc64le-be
|
||||
docker run --rm --name rocksdb_linux_ppc64le-be --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerarm64v8:
|
||||
mkdir -p java/target
|
||||
@@ -1949,7 +1945,8 @@ commit_prereq: build_tools/rocksdb-lego-determinator \
|
||||
ifeq ($(PLATFORM), IOS)
|
||||
# For iOS, create universal object files to be used on both the simulator and
|
||||
# a device.
|
||||
PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/Platforms
|
||||
XCODEROOT=$(shell xcode-select -print-path)
|
||||
PLATFORMSROOT=$(XCODEROOT)/Platforms
|
||||
SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer
|
||||
DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer
|
||||
IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString)
|
||||
@@ -1986,7 +1983,7 @@ endif
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(EXP_LIB_SOURCES) $(ANALYZER_LIB_SOURCES)
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(ANALYZER_LIB_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.cc.d)
|
||||
|
||||
# Add proper dependency support so changing a .h file forces a .cc file to
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[](http://140.211.168.68:8080/job/Rocksdb)
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
It is built on earlier work on LevelDB by Sanjay Ghemawat (sanjay@google.com)
|
||||
It is built on earlier work on [LevelDB](https://github.com/google/leveldb) by Sanjay Ghemawat (sanjay@google.com)
|
||||
and Jeff Dean (jeff@google.com)
|
||||
|
||||
This code is a library that forms the core building block for a fast
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
load("@fbcode_macros//build_defs:auto_headers.bzl", "AutoHeaders")
|
||||
load("@fbcode_macros//build_defs:cpp_library.bzl", "cpp_library")
|
||||
load(":defs.bzl", "test_binary")
|
||||
|
||||
REPO_PATH = package_name() + "/"
|
||||
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
|
||||
rocksdb_compiler_flags = [
|
||||
ROCKSDB_COMPILER_FLAGS = [
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
@@ -22,6 +20,7 @@ rocksdb_compiler_flags = [
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DZSTD_STATIC_LINKING_ONLY",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-DTBB",
|
||||
@@ -31,9 +30,10 @@ rocksdb_compiler_flags = [
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
"-Wnarrowing",
|
||||
"-DROCKSDB_NO_DYNAMIC_EXTENSION",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
ROCKSDB_EXTERNAL_DEPS = [
|
||||
("bzip2", None, "bz2"),
|
||||
("snappy", None, "snappy"),
|
||||
("zlib", None, "z"),
|
||||
@@ -45,13 +45,13 @@ rocksdb_external_deps = [
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
rocksdb_preprocessor_flags = [
|
||||
ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
|
||||
rocksdb_arch_preprocessor_flags = {
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
"x86_64": [
|
||||
"-DHAVE_SSE42",
|
||||
"-DHAVE_PCLMUL",
|
||||
@@ -64,16 +64,15 @@ is_opt_mode = build_mode.startswith("opt")
|
||||
|
||||
# -DNDEBUG is added by default in opt mode in fbcode. But adding it twice
|
||||
# doesn't harm and avoid forgetting to add it.
|
||||
if is_opt_mode:
|
||||
rocksdb_compiler_flags.append("-DNDEBUG")
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DNDEBUG"] if is_opt_mode else [])
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
if sanitizer == "":
|
||||
rocksdb_compiler_flags.append("-DROCKSDB_JEMALLOC")
|
||||
rocksdb_external_deps.append(("jemalloc", None, "headers"))
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DROCKSDB_JEMALLOC"] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS += ([("jemalloc", None, "headers")] if sanitizer == "" else [])
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
@@ -85,22 +84,24 @@ cpp_library(
|
||||
"db/c.cc",
|
||||
"db/column_family.cc",
|
||||
"db/compacted_db_impl.cc",
|
||||
"db/compaction.cc",
|
||||
"db/compaction_iterator.cc",
|
||||
"db/compaction_job.cc",
|
||||
"db/compaction_picker.cc",
|
||||
"db/compaction_picker_fifo.cc",
|
||||
"db/compaction_picker_universal.cc",
|
||||
"db/compaction/compaction.cc",
|
||||
"db/compaction/compaction_iterator.cc",
|
||||
"db/compaction/compaction_job.cc",
|
||||
"db/compaction/compaction_picker.cc",
|
||||
"db/compaction/compaction_picker_fifo.cc",
|
||||
"db/compaction/compaction_picker_level.cc",
|
||||
"db/compaction/compaction_picker_universal.cc",
|
||||
"db/convenience.cc",
|
||||
"db/db_filesnapshot.cc",
|
||||
"db/db_impl.cc",
|
||||
"db/db_impl_compaction_flush.cc",
|
||||
"db/db_impl_debug.cc",
|
||||
"db/db_impl_experimental.cc",
|
||||
"db/db_impl_files.cc",
|
||||
"db/db_impl_open.cc",
|
||||
"db/db_impl_readonly.cc",
|
||||
"db/db_impl_write.cc",
|
||||
"db/db_impl/db_impl.cc",
|
||||
"db/db_impl/db_impl_compaction_flush.cc",
|
||||
"db/db_impl/db_impl_debug.cc",
|
||||
"db/db_impl/db_impl_experimental.cc",
|
||||
"db/db_impl/db_impl_files.cc",
|
||||
"db/db_impl/db_impl_open.cc",
|
||||
"db/db_impl/db_impl_readonly.cc",
|
||||
"db/db_impl/db_impl_secondary.cc",
|
||||
"db/db_impl/db_impl_write.cc",
|
||||
"db/db_info_dumper.cc",
|
||||
"db/db_iter.cc",
|
||||
"db/dbformat.cc",
|
||||
@@ -143,8 +144,17 @@ cpp_library(
|
||||
"env/env_posix.cc",
|
||||
"env/io_posix.cc",
|
||||
"env/mock_env.cc",
|
||||
"file/delete_scheduler.cc",
|
||||
"file/file_util.cc",
|
||||
"file/filename.cc",
|
||||
"file/sst_file_manager_impl.cc",
|
||||
"logging/auto_roll_logger.cc",
|
||||
"logging/event_logger.cc",
|
||||
"logging/log_buffer.cc",
|
||||
"memory/arena.cc",
|
||||
"memory/concurrent_arena.cc",
|
||||
"memory/jemalloc_nodump_allocator.cc",
|
||||
"memtable/alloc_tracker.cc",
|
||||
"memtable/hash_cuckoo_rep.cc",
|
||||
"memtable/hash_linklist_rep.cc",
|
||||
"memtable/hash_skiplist_rep.cc",
|
||||
"memtable/skiplistrep.cc",
|
||||
@@ -152,10 +162,12 @@ cpp_library(
|
||||
"memtable/write_buffer_manager.cc",
|
||||
"monitoring/histogram.cc",
|
||||
"monitoring/histogram_windowing.cc",
|
||||
"monitoring/in_memory_stats_history.cc",
|
||||
"monitoring/instrumented_mutex.cc",
|
||||
"monitoring/iostats_context.cc",
|
||||
"monitoring/perf_context.cc",
|
||||
"monitoring/perf_level.cc",
|
||||
"monitoring/persistent_stats_history.cc",
|
||||
"monitoring/statistics.cc",
|
||||
"monitoring/thread_status_impl.cc",
|
||||
"monitoring/thread_status_updater.cc",
|
||||
@@ -170,77 +182,69 @@ cpp_library(
|
||||
"options/options_sanity_check.cc",
|
||||
"port/port_posix.cc",
|
||||
"port/stack_trace.cc",
|
||||
"table/adaptive_table_factory.cc",
|
||||
"table/block.cc",
|
||||
"table/block_based_filter_block.cc",
|
||||
"table/block_based_table_builder.cc",
|
||||
"table/block_based_table_factory.cc",
|
||||
"table/block_based_table_reader.cc",
|
||||
"table/block_builder.cc",
|
||||
"table/adaptive/adaptive_table_factory.cc",
|
||||
"table/block_based/block.cc",
|
||||
"table/block_based/block_based_filter_block.cc",
|
||||
"table/block_based/block_based_table_builder.cc",
|
||||
"table/block_based/block_based_table_factory.cc",
|
||||
"table/block_based/block_based_table_reader.cc",
|
||||
"table/block_based/block_builder.cc",
|
||||
"table/block_based/block_prefix_index.cc",
|
||||
"table/block_based/data_block_footer.cc",
|
||||
"table/block_based/data_block_hash_index.cc",
|
||||
"table/block_based/flush_block_policy.cc",
|
||||
"table/block_based/full_filter_block.cc",
|
||||
"table/block_based/index_builder.cc",
|
||||
"table/block_based/partitioned_filter_block.cc",
|
||||
"table/block_fetcher.cc",
|
||||
"table/block_prefix_index.cc",
|
||||
"table/bloom_block.cc",
|
||||
"table/cuckoo_table_builder.cc",
|
||||
"table/cuckoo_table_factory.cc",
|
||||
"table/cuckoo_table_reader.cc",
|
||||
"table/data_block_footer.cc",
|
||||
"table/data_block_hash_index.cc",
|
||||
"table/flush_block_policy.cc",
|
||||
"table/cuckoo/cuckoo_table_builder.cc",
|
||||
"table/cuckoo/cuckoo_table_factory.cc",
|
||||
"table/cuckoo/cuckoo_table_reader.cc",
|
||||
"table/format.cc",
|
||||
"table/full_filter_block.cc",
|
||||
"table/get_context.cc",
|
||||
"table/index_builder.cc",
|
||||
"table/iterator.cc",
|
||||
"table/merging_iterator.cc",
|
||||
"table/meta_blocks.cc",
|
||||
"table/partitioned_filter_block.cc",
|
||||
"table/persistent_cache_helper.cc",
|
||||
"table/plain_table_builder.cc",
|
||||
"table/plain_table_factory.cc",
|
||||
"table/plain_table_index.cc",
|
||||
"table/plain_table_key_coding.cc",
|
||||
"table/plain_table_reader.cc",
|
||||
"table/plain/plain_table_builder.cc",
|
||||
"table/plain/plain_table_factory.cc",
|
||||
"table/plain/plain_table_index.cc",
|
||||
"table/plain/plain_table_key_coding.cc",
|
||||
"table/plain/plain_table_reader.cc",
|
||||
"table/sst_file_reader.cc",
|
||||
"table/sst_file_writer.cc",
|
||||
"table/table_properties.cc",
|
||||
"table/two_level_iterator.cc",
|
||||
"test_util/sync_point.cc",
|
||||
"test_util/sync_point_impl.cc",
|
||||
"test_util/transaction_test_util.cc",
|
||||
"tools/dump/db_dump_tool.cc",
|
||||
"tools/ldb_cmd.cc",
|
||||
"tools/ldb_tool.cc",
|
||||
"tools/sst_dump_tool.cc",
|
||||
"util/arena.cc",
|
||||
"util/auto_roll_logger.cc",
|
||||
"trace_replay/block_cache_tracer.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/bloom.cc",
|
||||
"util/build_version.cc",
|
||||
"util/coding.cc",
|
||||
"util/compaction_job_stats_impl.cc",
|
||||
"util/comparator.cc",
|
||||
"util/compression_context_cache.cc",
|
||||
"util/concurrent_arena.cc",
|
||||
"util/concurrent_task_limiter_impl.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/delete_scheduler.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
"util/event_logger.cc",
|
||||
"util/file_reader_writer.cc",
|
||||
"util/file_util.cc",
|
||||
"util/filename.cc",
|
||||
"util/filter_policy.cc",
|
||||
"util/hash.cc",
|
||||
"util/jemalloc_nodump_allocator.cc",
|
||||
"util/log_buffer.cc",
|
||||
"util/murmurhash.cc",
|
||||
"util/random.cc",
|
||||
"util/rate_limiter.cc",
|
||||
"util/slice.cc",
|
||||
"util/sst_file_manager_impl.cc",
|
||||
"util/status.cc",
|
||||
"util/string_util.cc",
|
||||
"util/sync_point.cc",
|
||||
"util/sync_point_impl.cc",
|
||||
"util/thread_local.cc",
|
||||
"util/threadpool_imp.cc",
|
||||
"util/trace_replay.cc",
|
||||
"util/transaction_test_util.cc",
|
||||
"util/xxhash.cc",
|
||||
"utilities/backupable/backupable_db.cc",
|
||||
"utilities/blob_db/blob_compaction_filter.cc",
|
||||
@@ -258,16 +262,10 @@ cpp_library(
|
||||
"utilities/checkpoint/checkpoint_impl.cc",
|
||||
"utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc",
|
||||
"utilities/convenience/info_log_finder.cc",
|
||||
"utilities/date_tiered/date_tiered_db_impl.cc",
|
||||
"utilities/debug.cc",
|
||||
"utilities/document/document_db.cc",
|
||||
"utilities/document/json_document.cc",
|
||||
"utilities/document/json_document_builder.cc",
|
||||
"utilities/env_mirror.cc",
|
||||
"utilities/env_timed.cc",
|
||||
"utilities/geodb/geodb_impl.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/lua/rocks_lua_compaction_filter.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators/bytesxor.cc",
|
||||
"utilities/merge_operators/max.cc",
|
||||
@@ -282,9 +280,7 @@ cpp_library(
|
||||
"utilities/persistent_cache/block_cache_tier_metadata.cc",
|
||||
"utilities/persistent_cache/persistent_cache_tier.cc",
|
||||
"utilities/persistent_cache/volatile_tier_impl.cc",
|
||||
"utilities/redis/redis_lists.cc",
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/spatialdb/spatial_db.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/transactions/optimistic_transaction.cc",
|
||||
@@ -305,11 +301,11 @@ cpp_library(
|
||||
"utilities/write_batch_with_index/write_batch_with_index_internal.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -317,59 +313,58 @@ cpp_library(
|
||||
srcs = [
|
||||
"db/db_test_util.cc",
|
||||
"table/mock_table.cc",
|
||||
"test_util/fault_injection_test_env.cc",
|
||||
"test_util/testharness.cc",
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_trace_analyzer.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"util/fault_injection_test_env.cc",
|
||||
"util/testharness.cc",
|
||||
"util/testutil.cc",
|
||||
"utilities/cassandra/test_utils.cc",
|
||||
"utilities/col_buf_decoder.cc",
|
||||
"utilities/col_buf_encoder.cc",
|
||||
"utilities/column_aware_encoding_util.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_tools_lib",
|
||||
srcs = [
|
||||
"test_util/testutil.cc",
|
||||
"tools/block_cache_trace_analyzer.cc",
|
||||
"tools/db_bench_tool.cc",
|
||||
"tools/trace_analyzer_tool.cc",
|
||||
"util/testutil.cc",
|
||||
],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "env_basic_test_lib",
|
||||
srcs = ["env/env_basic_test.cc"],
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
# [test_name, test_src, test_type]
|
||||
ROCKS_TESTS = [
|
||||
[
|
||||
"arena_test",
|
||||
"util/arena_test.cc",
|
||||
"memory/arena_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"auto_roll_logger_test",
|
||||
"util/auto_roll_logger_test.cc",
|
||||
"logging/auto_roll_logger_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -389,12 +384,22 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"block_based_filter_block_test",
|
||||
"table/block_based_filter_block_test.cc",
|
||||
"table/block_based/block_based_filter_block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"block_cache_trace_analyzer_test",
|
||||
"tools/block_cache_trace_analyzer_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"block_cache_tracer_test",
|
||||
"trace_replay/block_cache_tracer_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"block_test",
|
||||
"table/block_test.cc",
|
||||
"table/block_based/block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -447,11 +452,6 @@ ROCKS_TESTS = [
|
||||
"util/coding_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"column_aware_encoding_test",
|
||||
"utilities/column_aware_encoding_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"column_family_test",
|
||||
"db/column_family_test.cc",
|
||||
@@ -469,22 +469,22 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"compaction_iterator_test",
|
||||
"db/compaction_iterator_test.cc",
|
||||
"db/compaction/compaction_iterator_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"compaction_job_stats_test",
|
||||
"db/compaction_job_stats_test.cc",
|
||||
"db/compaction/compaction_job_stats_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"compaction_job_test",
|
||||
"db/compaction_job_test.cc",
|
||||
"db/compaction/compaction_job_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"compaction_picker_test",
|
||||
"db/compaction_picker_test.cc",
|
||||
"db/compaction/compaction_picker_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -504,7 +504,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"cuckoo_table_builder_test",
|
||||
"table/cuckoo_table_builder_test.cc",
|
||||
"table/cuckoo/cuckoo_table_builder_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -514,17 +514,12 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"cuckoo_table_reader_test",
|
||||
"table/cuckoo_table_reader_test.cc",
|
||||
"table/cuckoo/cuckoo_table_reader_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"data_block_hash_index_test",
|
||||
"table/data_block_hash_index_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"date_tiered_test",
|
||||
"utilities/date_tiered/date_tiered_test.cc",
|
||||
"table/block_based/data_block_hash_index_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -545,7 +540,7 @@ ROCKS_TESTS = [
|
||||
[
|
||||
"db_bloom_filter_test",
|
||||
"db/db_bloom_filter_test.cc",
|
||||
"serial",
|
||||
"parallel",
|
||||
],
|
||||
[
|
||||
"db_compaction_filter_test",
|
||||
@@ -627,6 +622,11 @@ ROCKS_TESTS = [
|
||||
"db/db_range_del_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"db_secondary_test",
|
||||
"db/db_impl/db_secondary_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"db_sst_test",
|
||||
"db/db_sst_test.cc",
|
||||
@@ -679,7 +679,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"delete_scheduler_test",
|
||||
"util/delete_scheduler_test.cc",
|
||||
"file/delete_scheduler_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -687,11 +687,6 @@ ROCKS_TESTS = [
|
||||
"db/deletefile_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"document_db_test",
|
||||
"utilities/document/document_db_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"dynamic_bloom_test",
|
||||
"util/dynamic_bloom_test.cc",
|
||||
@@ -719,7 +714,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"event_logger_test",
|
||||
"util/event_logger_test.cc",
|
||||
"logging/event_logger_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -764,12 +759,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"full_filter_block_test",
|
||||
"table/full_filter_block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"geodb_test",
|
||||
"utilities/geodb/geodb_test.cc",
|
||||
"table/block_based/full_filter_block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -802,11 +792,6 @@ ROCKS_TESTS = [
|
||||
"monitoring/iostats_context_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"json_document_test",
|
||||
"utilities/document/json_document_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"ldb_cmd_test",
|
||||
"tools/ldb_cmd_test.cc",
|
||||
@@ -904,7 +889,7 @@ ROCKS_TESTS = [
|
||||
],
|
||||
[
|
||||
"partitioned_filter_block_test",
|
||||
"table/partitioned_filter_block_test.cc",
|
||||
"table/block_based/partitioned_filter_block_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
@@ -972,11 +957,6 @@ ROCKS_TESTS = [
|
||||
"util/slice_transform_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"spatial_db_test",
|
||||
"utilities/spatialdb/spatial_db_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"sst_dump_test",
|
||||
"tools/sst_dump_test.cc",
|
||||
@@ -992,6 +972,11 @@ ROCKS_TESTS = [
|
||||
"monitoring/statistics_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"stats_history_test",
|
||||
"monitoring/stats_history_test.cc",
|
||||
"serial",
|
||||
],
|
||||
[
|
||||
"stringappend_test",
|
||||
"utilities/merge_operators/string_append/stringappend_test.cc",
|
||||
@@ -1102,26 +1087,16 @@ ROCKS_TESTS = [
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
if not is_opt_mode:
|
||||
for test_cfg in ROCKS_TESTS:
|
||||
test_name = test_cfg[0]
|
||||
test_cc = test_cfg[1]
|
||||
ttype = "gtest" if test_cfg[2] == "parallel" else "simple"
|
||||
test_bin = test_name + "_bin"
|
||||
|
||||
cpp_binary(
|
||||
name = test_bin,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = test_name,
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin],
|
||||
type = ttype,
|
||||
deps = [":" + test_bin],
|
||||
)
|
||||
[
|
||||
test_binary(
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
)
|
||||
for test_name, test_cc, parallelism in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
]
|
||||
@@ -50,6 +50,10 @@ Check out their blog post: http://blog.cloudera.com/blog/2015/08/inside-santande
|
||||
## Airbnb
|
||||
Airbnb is using RocksDB as a storage engine for their personalized search service. You can learn more about it here: https://www.youtube.com/watch?v=ASQ6XMtogMs
|
||||
|
||||
## Alluxio
|
||||
[Alluxio](https://www.alluxio.io) uses RocksDB to serve and scale file system metadata to beyond 1 Billion files. The detailed design and implementation is described in this engineering blog:
|
||||
https://www.alluxio.io/blog/scalable-metadata-service-in-alluxio-storing-billions-of-files/
|
||||
|
||||
## Pinterest
|
||||
Pinterest's Object Retrieval System uses RocksDB for storage: https://www.youtube.com/watch?v=MtFEVEs_2Vo
|
||||
|
||||
@@ -91,4 +95,4 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
[ProfaneDB](https://profanedb.gitlab.io/) is a database for Protocol Buffers, and uses RocksDB for storage. It is accessible via gRPC, and the schema is defined using directly `.proto` files.
|
||||
|
||||
## IOTA Foundation
|
||||
[IOTA Foundation](https://www.iota.org/) is using RocksDB in the [IOTA Reference Implementation (IRI)](https://github.com/iotaledger/iri) to store the local state of the Tangle. The Tangle is the first open-source distributed ledger powering the future of the Internet of Things.
|
||||
[IOTA Foundation](https://www.iota.org/) is using RocksDB in the [IOTA Reference Implementation (IRI)](https://github.com/iotaledger/iri) to store the local state of the Tangle. The Tangle is the first open-source distributed ledger powering the future of the Internet of Things.
|
||||
+1
-1
@@ -43,7 +43,7 @@ We plan to use this port for our business purposes here at Bing and this provide
|
||||
|
||||
* Certain headers that are not present and not necessary on Windows were simply `#ifndef OS_WIN` in a few places (`unistd.h`)
|
||||
* All posix specific headers were replaced to port/port.h which worked well
|
||||
* Replaced `dirent.h` for `port/dirent.h` (very few places) with the implementation of the relevant interfaces within `rocksdb::port` namespace
|
||||
* Replaced `dirent.h` for `port/port_dirent.h` (very few places) with the implementation of the relevant interfaces within `rocksdb::port` namespace
|
||||
* Replaced `sys/time.h` to `port/sys_time.h` (few places) implemented equivalents within `rocksdb::port`
|
||||
* `printf %z` specification is not supported on Windows. To imitate existing standards we came up with a string macro `ROCKSDB_PRIszt` which expands to `zu` on posix systems and to `Iu` on windows.
|
||||
* in class member initialization were moved to a __ctors in some cases
|
||||
|
||||
+5
-65
@@ -1,75 +1,15 @@
|
||||
version: 1.0.{build}
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
|
||||
THIRDPARTY_HOME: $(APPVEYOR_BUILD_FOLDER)\thirdparty
|
||||
SNAPPY_HOME: $(THIRDPARTY_HOME)\snappy-1.1.7
|
||||
SNAPPY_INCLUDE: $(SNAPPY_HOME);$(SNAPPY_HOME)\build
|
||||
SNAPPY_LIB_DEBUG: $(SNAPPY_HOME)\build\Debug\snappy.lib
|
||||
SNAPPY_LIB_RELEASE: $(SNAPPY_HOME)\build\Release\snappy.lib
|
||||
LZ4_HOME: $(THIRDPARTY_HOME)\lz4-1.8.3
|
||||
LZ4_INCLUDE: $(LZ4_HOME)\lib
|
||||
LZ4_LIB_DEBUG: $(LZ4_HOME)\visual\VS2010\bin\x64_Debug\liblz4_static.lib
|
||||
LZ4_LIB_RELEASE: $(LZ4_HOME)\visual\VS2010\bin\x64_Release\liblz4_static.lib
|
||||
ZSTD_HOME: $(THIRDPARTY_HOME)\zstd-1.4.0
|
||||
ZSTD_INCLUDE: $(ZSTD_HOME)\lib;$(ZSTD_HOME)\lib\dictBuilder
|
||||
ZSTD_LIB_DEBUG: $(ZSTD_HOME)\build\VS2010\bin\x64_Debug\libzstd_static.lib
|
||||
ZSTD_LIB_RELEASE: $(ZSTD_HOME)\build\VS2010\bin\x64_Release\libzstd_static.lib
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
CMAKE_GENERATOR: Visual Studio 14 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.com
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
CMAKE_GENERATOR: Visual Studio 15 Win64
|
||||
DEV_ENV: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\devenv.com
|
||||
|
||||
install:
|
||||
- md %THIRDPARTY_HOME%
|
||||
- echo "Building Snappy dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output snappy-1.1.7.zip --location https://github.com/google/snappy/archive/1.1.7.zip
|
||||
- unzip snappy-1.1.7.zip
|
||||
- cd snappy-1.1.7
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -G "%CMAKE_GENERATOR%" ..
|
||||
- msbuild Snappy.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild Snappy.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building LZ4 dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output lz4-1.8.3.zip --location https://github.com/lz4/lz4/archive/v1.8.3.zip
|
||||
- unzip lz4-1.8.3.zip
|
||||
- cd lz4-1.8.3\visual\VS2010
|
||||
- ps: $CMD="$Env:DEV_ENV"; & $CMD lz4.sln /upgrade
|
||||
- msbuild lz4.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild lz4.sln /p:Configuration=Release /p:Platform=x64
|
||||
- echo "Building ZStd dependency..."
|
||||
- cd %THIRDPARTY_HOME%
|
||||
- curl --fail --silent --show-error --output zstd-1.4.0.zip --location https://github.com/facebook/zstd/archive/v1.4.0.zip
|
||||
- unzip zstd-1.4.0.zip
|
||||
- cd zstd-1.4.0\build\VS2010
|
||||
- ps: $CMD="$Env:DEV_ENV"; & $CMD zstd.sln /upgrade
|
||||
- msbuild zstd.sln /p:Configuration=Debug /p:Platform=x64
|
||||
- msbuild zstd.sln /p:Configuration=Release /p:Platform=x64
|
||||
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "%CMAKE_GENERATOR%" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 15 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 -DJNI=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
parallel: true
|
||||
verbosity: normal
|
||||
|
||||
test:
|
||||
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test,db_merge_operand_test -Concurrency 8
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a build-failed.zip %APPVEYOR_BUILD_FOLDER%\build\ && appveyor PushArtifact build-failed.zip
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
@@ -81,7 +82,7 @@ def get_tests(repo_path):
|
||||
else:
|
||||
# we consumed all the parallel tests
|
||||
break
|
||||
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
@@ -117,7 +118,7 @@ def generate_targets(repo_path):
|
||||
"rocksdb_tools_lib",
|
||||
src_mk.get("BENCH_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []) +
|
||||
["util/testutil.cc"],
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
|
||||
# test for every test we found in the Makefile
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# Create a tmp directory for the test to use
|
||||
TEST_DIR=$(mktemp -d /dev/shm/fbcode_rocksdb_XXXXXXX)
|
||||
# shellcheck disable=SC2068
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
+34
-44
@@ -1,16 +1,15 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
rocksdb_target_header = """load("@fbcode_macros//build_defs:auto_headers.bzl", "AutoHeaders")
|
||||
load("@fbcode_macros//build_defs:cpp_library.bzl", "cpp_library")
|
||||
load(":defs.bzl", "test_binary")
|
||||
|
||||
REPO_PATH = package_name() + "/"
|
||||
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
|
||||
rocksdb_compiler_flags = [
|
||||
ROCKSDB_COMPILER_FLAGS = [
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
@@ -26,6 +25,7 @@ rocksdb_compiler_flags = [
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DZSTD_STATIC_LINKING_ONLY",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-DTBB",
|
||||
@@ -35,9 +35,10 @@ rocksdb_compiler_flags = [
|
||||
"-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX",
|
||||
"-DROCKSDB_BACKTRACE",
|
||||
"-Wnarrowing",
|
||||
"-DROCKSDB_NO_DYNAMIC_EXTENSION",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
ROCKSDB_EXTERNAL_DEPS = [
|
||||
("bzip2", None, "bz2"),
|
||||
("snappy", None, "snappy"),
|
||||
("zlib", None, "z"),
|
||||
@@ -49,13 +50,13 @@ rocksdb_external_deps = [
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
rocksdb_preprocessor_flags = [
|
||||
ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
|
||||
rocksdb_arch_preprocessor_flags = {
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
"x86_64": [
|
||||
"-DHAVE_SSE42",
|
||||
"-DHAVE_PCLMUL",
|
||||
@@ -68,16 +69,15 @@ is_opt_mode = build_mode.startswith("opt")
|
||||
|
||||
# -DNDEBUG is added by default in opt mode in fbcode. But adding it twice
|
||||
# doesn't harm and avoid forgetting to add it.
|
||||
if is_opt_mode:
|
||||
rocksdb_compiler_flags.append("-DNDEBUG")
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DNDEBUG"] if is_opt_mode else [])
|
||||
|
||||
sanitizer = read_config("fbcode", "sanitizer")
|
||||
|
||||
# Do not enable jemalloc if sanitizer presents. RocksDB will further detect
|
||||
# whether the binary is linked with jemalloc at runtime.
|
||||
if sanitizer == "":
|
||||
rocksdb_compiler_flags.append("-DROCKSDB_JEMALLOC")
|
||||
rocksdb_external_deps.append(("jemalloc", None, "headers"))
|
||||
ROCKSDB_COMPILER_FLAGS += (["-DROCKSDB_JEMALLOC"] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_EXTERNAL_DEPS += ([("jemalloc", None, "headers")] if sanitizer == "" else [])
|
||||
"""
|
||||
|
||||
|
||||
@@ -86,11 +86,11 @@ cpp_library(
|
||||
name = "{name}",
|
||||
srcs = [{srcs}],
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [{deps}],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -98,11 +98,11 @@ binary_template = """
|
||||
cpp_binary(
|
||||
name = "%s",
|
||||
srcs = [%s],
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [%s],
|
||||
external_deps = rocksdb_external_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -121,27 +121,17 @@ ROCKS_TESTS = [
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
if not is_opt_mode:
|
||||
for test_cfg in ROCKS_TESTS:
|
||||
test_name = test_cfg[0]
|
||||
test_cc = test_cfg[1]
|
||||
ttype = "gtest" if test_cfg[2] == "parallel" else "simple"
|
||||
test_bin = test_name + "_bin"
|
||||
|
||||
cpp_binary(
|
||||
name = test_bin,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = test_name,
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin],
|
||||
type = ttype,
|
||||
deps = [":" + test_bin],
|
||||
)
|
||||
[
|
||||
test_binary(
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
)
|
||||
for test_name, test_cc, parallelism in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
]
|
||||
"""
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
"""
|
||||
This module keeps commonly used components.
|
||||
"""
|
||||
|
||||
@@ -97,7 +97,7 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
}
|
||||
|
||||
// fbcode is a sub-repo. We cannot patch until we add it to ignore otherwise
|
||||
// Git thinks it is an uncommited change.
|
||||
// Git thinks it is an uncommitted change.
|
||||
$fix_git_ignore = array(
|
||||
"name" => "Fix git ignore",
|
||||
"shell" => "echo fbcode >> .git/info/exclude",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
# amalgamate.py creates an amalgamation from a unity build.
|
||||
# It can be run with either Python 2 or 3.
|
||||
|
||||
@@ -234,7 +234,7 @@ else
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
int fd = open("/dev/null", 0);
|
||||
fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 0, 1024);
|
||||
fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, 1024);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -370,6 +370,16 @@ EOF
|
||||
# JEMALLOC can be enabled either using the flag (like here) or by
|
||||
# providing direct link to the jemalloc library
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
# check for JEMALLOC installed with HomeBrew
|
||||
if [ "$PLATFORM" == "OS_MACOSX" ]; then
|
||||
if hash brew 2>/dev/null && brew ls --versions jemalloc > /dev/null; then
|
||||
JEMALLOC_VER=$(brew ls --versions jemalloc | tail -n 1 | cut -f 2 -d ' ')
|
||||
JEMALLOC_INCLUDE="-I/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/include"
|
||||
JEMALLOC_LIB="/usr/local/Cellar/jemalloc/${JEMALLOC_VER}/lib/libjemalloc_pic.a"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS $JEMALLOC_LIB"
|
||||
JAVA_STATIC_LDFLAGS="$JAVA_STATIC_LDFLAGS $JEMALLOC_LIB"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if ! test $JEMALLOC && ! test $ROCKSDB_DISABLE_TCMALLOC; then
|
||||
@@ -387,6 +397,7 @@ EOF
|
||||
#include <malloc.h>
|
||||
int main() {
|
||||
size_t res = malloc_usable_size(0);
|
||||
(void)res;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
@@ -401,6 +412,7 @@ EOF
|
||||
#include <pthread.h>
|
||||
int main() {
|
||||
int x = PTHREAD_MUTEX_ADAPTIVE_NP;
|
||||
(void)x;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
@@ -412,7 +424,7 @@ EOF
|
||||
if ! test $ROCKSDB_DISABLE_BACKTRACE; then
|
||||
# Test whether backtrace is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <execinfo.h>>
|
||||
#include <execinfo.h>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
backtrace_symbols(frames, backtrace(frames, 1));
|
||||
@@ -470,6 +482,7 @@ EOF
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
int cpuid = sched_getcpu();
|
||||
(void)cpuid;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -505,11 +518,11 @@ fi
|
||||
|
||||
if test "$USE_HDFS"; then
|
||||
if test -z "$JAVA_HOME"; then
|
||||
echo "JAVA_HOME has to be set for HDFS usage."
|
||||
echo "JAVA_HOME has to be set for HDFS usage." >&2
|
||||
exit 1
|
||||
fi
|
||||
HDFS_CCFLAGS="$HDFS_CCFLAGS -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -DUSE_HDFS"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -lhdfs -L$JAVA_HOME/jre/lib/amd64"
|
||||
HDFS_CCFLAGS="$HDFS_CCFLAGS -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -DUSE_HDFS -I$HADOOP_HOME/include"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -lhdfs -L$JAVA_HOME/jre/lib/amd64 -L$HADOOP_HOME/lib/native"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -L$JAVA_HOME/jre/lib/amd64/server -L$GLIBC_RUNTIME_PATH/lib"
|
||||
HDFS_LDFLAGS="$HDFS_LDFLAGS -ldl -lverify -ljava -ljvm"
|
||||
COMMON_FLAGS="$COMMON_FLAGS $HDFS_CCFLAGS"
|
||||
@@ -524,7 +537,7 @@ if test -z "$PORTABLE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mcpu=$POWER -mtune=$POWER "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z10 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^arm`"; then
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep -e^arm -e^aarch64`"; then
|
||||
# TODO: Handle this with approprite options.
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^aarch64`"; then
|
||||
@@ -545,12 +558,13 @@ $CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <nmmintrin.h>
|
||||
int main() {
|
||||
volatile uint32_t x = _mm_crc32_u32(0, 0);
|
||||
(void)x;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SSE42"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling"
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -562,12 +576,13 @@ $CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
(void)d;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_PCLMUL"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling"
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -581,6 +596,7 @@ if [ "$PLATFORM" != IOS ]; then
|
||||
#endif
|
||||
int main() {
|
||||
static __thread int tls;
|
||||
(void)tls;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -588,6 +604,19 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$FBCODE_BUILD" != "true" -a "$PLATFORM" = OS_LINUX ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_CFLAGS -x c++ -c - -o test_dl.o 2>/dev/null <<EOF
|
||||
void dummy_func() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
$CXX $COMMON_FLAGS $PLATFORM_SHARED_LDFLAGS test_dl.o -o /dev/null 2>/dev/null
|
||||
if [ "$?" = 0 ]; then
|
||||
EXEC_LDFLAGS+="-ldl"
|
||||
rm -f test_dl.o
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/112ec378fec7002ad3e09afde022e656049f7191/5.x/centos7-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/04999bdb3ce81a11073535dcb00b5e13dc1cbaf5/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/92b0c8e5c8eecc71eb042594ce1ab3413799b385/5.x/gcc-5-glibc-2.23/339d858
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# shellcheck disable=SC2148
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/6e8e715624fd15256a7970073387793dfcf79b46/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/ef37e1faa1c29782abfac1ae65a291b9b7966f6d/stable/centos7-native/c9f9104
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/c67031f0f739ac61575a061518d6ef5038f99f90/7.x/platform007/5620abc
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
docker run -v $PWD:/rocks -w /rocks buildpack-deps make
|
||||
@@ -132,11 +132,14 @@ _TEST_NAME_TO_PARSERS = {
|
||||
'lite': [CompilerErrorParser],
|
||||
'lite_test': [CompilerErrorParser, GTestErrorParser],
|
||||
'stress_crash': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'stress_crash_with_atomic_flush': [CompilerErrorParser, DbCrashErrorParser],
|
||||
'write_stress': [CompilerErrorParser, WriteStressErrorParser],
|
||||
'asan': [CompilerErrorParser, GTestErrorParser, AsanErrorParser],
|
||||
'asan_crash': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'asan_crash_with_atomic_flush': [CompilerErrorParser, AsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan': [CompilerErrorParser, GTestErrorParser, UbsanErrorParser],
|
||||
'ubsan_crash': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'ubsan_crash_with_atomic_flush': [CompilerErrorParser, UbsanErrorParser, DbCrashErrorParser],
|
||||
'valgrind': [CompilerErrorParser, GTestErrorParser, ValgrindErrorParser],
|
||||
'tsan': [CompilerErrorParser, GTestErrorParser, TsanErrorParser],
|
||||
'format_compatible': [CompilerErrorParser, CompatErrorParser],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# fail early
|
||||
set -e
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
@@ -51,7 +52,7 @@ if test -z $PIC_BUILD; then
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
CFLAGS+=" -DZSTD -DZSTD_STATIC_LINKING_ONLY"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# If clang_format_diff.py command is not specfied, we assume we are able to
|
||||
# access directly without any path.
|
||||
if [ -z $CLANG_FORMAT_DIFF ]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# shellcheck disable=SC1113
|
||||
#/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/local/fbcode/gcc-4.9-glibc-2.20-fb/bin/python2.7
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
set -e
|
||||
|
||||
@@ -377,7 +378,7 @@ function send_to_ods {
|
||||
echo >&2 "ERROR: Key $key doesn't have a value."
|
||||
return
|
||||
fi
|
||||
curl --silent "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build$git_br&key=$key&value=$value" \
|
||||
curl -s "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build$git_br&key=$key&value=$value" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
|
||||
@@ -109,13 +109,6 @@ else
|
||||
TASK_CREATION_TOOL="false"
|
||||
fi
|
||||
|
||||
ARTIFACTS=" 'artifacts': [
|
||||
{
|
||||
'name':'database',
|
||||
'paths':[ '/dev/shm/rocksdb' ],
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# A mechanism to disable tests temporarily
|
||||
#
|
||||
@@ -140,6 +133,7 @@ UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -160,6 +154,7 @@ UNIT_TEST_NON_SHM_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -182,6 +177,7 @@ RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -202,6 +198,7 @@ UNIT_TEST_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -222,6 +219,7 @@ RELEASE_BUILD_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Release on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -242,6 +240,7 @@ CLANG_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -262,6 +261,7 @@ CLANG_RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb CLANG Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -282,6 +282,7 @@ CLANG_ANALYZE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb analyze',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -302,6 +303,7 @@ CODE_COV_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test Code Coverage',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -322,6 +324,7 @@ UNITY_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unity',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -342,6 +345,7 @@ LITE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Lite build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -361,6 +365,7 @@ REPORT_LITE_BINARY_SIZE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Lite Binary Size',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -376,8 +381,9 @@ REPORT_LITE_BINARY_SIZE_COMMANDS="[
|
||||
#
|
||||
STRESS_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Stress/Crash Test',
|
||||
'name':'Rocksdb Stress and Crash Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -395,7 +401,35 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
$PARSER
|
||||
}
|
||||
],
|
||||
$ARTIFACTS,
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB stress/crash test with atomic flush
|
||||
#
|
||||
STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Stress and Crash Test with atomic flush',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests with atomic flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
@@ -407,6 +441,7 @@ WRITE_STRESS_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Write Stress Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -429,6 +464,7 @@ ASAN_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -449,6 +485,7 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -464,6 +501,29 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash testing with atomic flush under address sanitizer
|
||||
#
|
||||
ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test with atomic flush under ASAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test_with_atomic_flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 asan_crash_test_with_atomic_flush || $CONTRUN_NAME=asan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB test under undefined behavior sanitizer
|
||||
#
|
||||
@@ -471,6 +531,7 @@ UBSAN_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -491,6 +552,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -506,6 +568,29 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash testing with atomic flush under undefined behavior sanitizer
|
||||
#
|
||||
UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb crash test with atomic flush under UBSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test_with_atomic_flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 ubsan_crash_test_with_atomic_flush || $CONTRUN_NAME=ubsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test under valgrind
|
||||
#
|
||||
@@ -513,6 +598,7 @@ VALGRIND_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under valgrind',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -535,6 +621,7 @@ TSAN_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -557,6 +644,7 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Crash Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
@@ -572,6 +660,29 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB crash test with atomic flush under TSAN
|
||||
#
|
||||
TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Crash Test with atomic flush under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=tsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB format compatible
|
||||
#
|
||||
@@ -589,6 +700,7 @@ FORMAT_COMPATIBLE_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Format Compatible tests',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -611,7 +723,7 @@ run_no_compression()
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
make clean
|
||||
cat build_tools/fbcode_config.sh | grep -iv dzlib | grep -iv dlz4 | grep -iv dsnappy | grep -iv dbzip2 > .tmp.fbcode_config.sh
|
||||
cat build_tools/fbcode_config.sh | grep -iv dzstd | grep -iv dzlib | grep -iv dlz4 | grep -iv dsnappy | grep -iv dbzip2 > .tmp.fbcode_config.sh
|
||||
mv .tmp.fbcode_config.sh build_tools/fbcode_config.sh
|
||||
cat Makefile | grep -v tools/ldb_test.py > .tmp.Makefile
|
||||
mv .tmp.Makefile Makefile
|
||||
@@ -622,6 +734,7 @@ NO_COMPRESSION_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb No Compression tests',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -646,7 +759,7 @@ run_regression()
|
||||
|
||||
# parameters: $1 -- key, $2 -- value
|
||||
function send_size_to_ods {
|
||||
curl --silent "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=rocksdb.build_size.$1&value=$2" \
|
||||
curl -s "https://www.intern.facebook.com/intern/agent/ods_set.php?entity=rocksdb_build&key=rocksdb.build_size.$1&value=$2" \
|
||||
--connect-timeout 60
|
||||
}
|
||||
|
||||
@@ -699,6 +812,7 @@ JAVA_BUILD_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Java Build',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
@@ -753,6 +867,9 @@ case $1 in
|
||||
stress_crash)
|
||||
echo $STRESS_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
stress_crash_with_atomic_flush)
|
||||
echo $STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
write_stress)
|
||||
echo $WRITE_STRESS_COMMANDS
|
||||
;;
|
||||
@@ -762,12 +879,18 @@ case $1 in
|
||||
asan_crash)
|
||||
echo $ASAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
asan_crash_with_atomic_flush)
|
||||
echo $ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
ubsan)
|
||||
echo $UBSAN_TEST_COMMANDS
|
||||
;;
|
||||
ubsan_crash)
|
||||
echo $UBSAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
ubsan_crash_with_atomic_flush)
|
||||
echo $UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
valgrind)
|
||||
echo $VALGRIND_TEST_COMMANDS
|
||||
;;
|
||||
@@ -777,6 +900,9 @@ case $1 in
|
||||
tsan_crash)
|
||||
echo $TSAN_CRASH_TEST_COMMANDS
|
||||
;;
|
||||
tsan_crash_with_atomic_flush)
|
||||
echo $TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS
|
||||
;;
|
||||
format_compatible)
|
||||
echo $FORMAT_COMPATIBLE_COMMANDS
|
||||
;;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
# This script enables you running RocksDB tests by running
|
||||
# All the tests concurrently and utilizing all the cores
|
||||
Param(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
set -e
|
||||
|
||||
ROCKSDB_VERSION="5.10.3"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
#
|
||||
# Update dependencies.sh file with the latest avaliable versions
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
if [ "$#" = "0" ]; then
|
||||
echo "Usage: $0 major|minor|patch|full"
|
||||
exit 1
|
||||
|
||||
Vendored
+1
-4
@@ -3,9 +3,6 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#ifndef GFLAGS
|
||||
#include <cstdio>
|
||||
int main() {
|
||||
@@ -14,7 +11,7 @@ int main() {
|
||||
}
|
||||
#else
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
Vendored
+18
-10
@@ -16,9 +16,9 @@
|
||||
#include <vector>
|
||||
#include "cache/clock_cache.h"
|
||||
#include "cache/lru_cache.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -73,8 +73,7 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
current_ = this;
|
||||
}
|
||||
|
||||
~CacheTest() {
|
||||
}
|
||||
~CacheTest() override {}
|
||||
|
||||
std::shared_ptr<Cache> NewCache(size_t capacity) {
|
||||
auto type = GetParam();
|
||||
@@ -99,7 +98,7 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Lookup(shared_ptr<Cache> cache, int key) {
|
||||
int Lookup(std::shared_ptr<Cache> cache, int key) {
|
||||
Cache::Handle* handle = cache->Lookup(EncodeKey(key));
|
||||
const int r = (handle == nullptr) ? -1 : DecodeValue(cache->Value(handle));
|
||||
if (handle != nullptr) {
|
||||
@@ -108,16 +107,16 @@ class CacheTest : public testing::TestWithParam<std::string> {
|
||||
return r;
|
||||
}
|
||||
|
||||
void Insert(shared_ptr<Cache> cache, int key, int value, int charge = 1) {
|
||||
void Insert(std::shared_ptr<Cache> cache, int key, int value,
|
||||
int charge = 1) {
|
||||
cache->Insert(EncodeKey(key), EncodeValue(value), charge,
|
||||
&CacheTest::Deleter);
|
||||
}
|
||||
|
||||
void Erase(shared_ptr<Cache> cache, int key) {
|
||||
void Erase(std::shared_ptr<Cache> cache, int key) {
|
||||
cache->Erase(EncodeKey(key));
|
||||
}
|
||||
|
||||
|
||||
int Lookup(int key) {
|
||||
return Lookup(cache_, key);
|
||||
}
|
||||
@@ -307,7 +306,7 @@ TEST_P(CacheTest, EvictionPolicy) {
|
||||
Insert(200, 201);
|
||||
|
||||
// Frequently used entry must be kept around
|
||||
for (int i = 0; i < kCacheSize + 100; i++) {
|
||||
for (int i = 0; i < kCacheSize + 200; i++) {
|
||||
Insert(1000+i, 2000+i);
|
||||
ASSERT_EQ(101, Lookup(100));
|
||||
}
|
||||
@@ -360,7 +359,7 @@ TEST_P(CacheTest, EvictionPolicyRef) {
|
||||
Insert(303, 104);
|
||||
|
||||
// Insert entries much more than Cache capacity
|
||||
for (int i = 0; i < kCacheSize + 100; i++) {
|
||||
for (int i = 0; i < kCacheSize + 200; i++) {
|
||||
Insert(1000 + i, 2000 + i);
|
||||
}
|
||||
|
||||
@@ -687,8 +686,17 @@ TEST_P(CacheTest, DefaultShardBits) {
|
||||
ASSERT_EQ(6, sc->GetNumShardBits());
|
||||
}
|
||||
|
||||
TEST_P(CacheTest, GetCharge) {
|
||||
Insert(1, 2);
|
||||
Cache::Handle* h1 = cache_->Lookup(EncodeKey(1));
|
||||
ASSERT_EQ(2, DecodeValue(cache_->Value(h1)));
|
||||
ASSERT_EQ(1, cache_->GetCharge(h1));
|
||||
cache_->Release(h1);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_CLOCK_CACHE
|
||||
shared_ptr<Cache> (*new_clock_cache_func)(size_t, int, bool) = NewClockCache;
|
||||
std::shared_ptr<Cache> (*new_clock_cache_func)(size_t, int,
|
||||
bool) = NewClockCache;
|
||||
INSTANTIATE_TEST_CASE_P(CacheTestInstance, CacheTest,
|
||||
testing::Values(kLRU, kClock));
|
||||
#else
|
||||
|
||||
Vendored
+25
-28
@@ -234,38 +234,35 @@ struct CleanupContext {
|
||||
};
|
||||
|
||||
// A cache shard which maintains its own CLOCK cache.
|
||||
class ClockCacheShard : public CacheShard {
|
||||
class ClockCacheShard final : public CacheShard {
|
||||
public:
|
||||
// Hash map type.
|
||||
typedef tbb::concurrent_hash_map<CacheKey, CacheHandle*, CacheKey> HashTable;
|
||||
|
||||
ClockCacheShard();
|
||||
~ClockCacheShard();
|
||||
~ClockCacheShard() override;
|
||||
|
||||
// Interfaces
|
||||
virtual void SetCapacity(size_t capacity) override;
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override;
|
||||
virtual Status Insert(const Slice& key, uint32_t hash, void* value,
|
||||
size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Cache::Handle** handle,
|
||||
Cache::Priority priority) override;
|
||||
virtual Cache::Handle* Lookup(const Slice& key, uint32_t hash) override;
|
||||
void SetCapacity(size_t capacity) override;
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit) override;
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Cache::Handle** handle, Cache::Priority priority) override;
|
||||
Cache::Handle* Lookup(const Slice& key, uint32_t hash) override;
|
||||
// If the entry in in cache, increase reference count and return true.
|
||||
// Return false otherwise.
|
||||
//
|
||||
// Not necessary to hold mutex_ before being called.
|
||||
virtual bool Ref(Cache::Handle* handle) override;
|
||||
virtual bool Release(Cache::Handle* handle,
|
||||
bool force_erase = false) override;
|
||||
virtual void Erase(const Slice& key, uint32_t hash) override;
|
||||
bool Ref(Cache::Handle* handle) override;
|
||||
bool Release(Cache::Handle* handle, bool force_erase = false) override;
|
||||
void Erase(const Slice& key, uint32_t hash) override;
|
||||
bool EraseAndConfirm(const Slice& key, uint32_t hash,
|
||||
CleanupContext* context);
|
||||
virtual size_t GetUsage() const override;
|
||||
virtual size_t GetPinnedUsage() const override;
|
||||
virtual void EraseUnRefEntries() override;
|
||||
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override;
|
||||
size_t GetUsage() const override;
|
||||
size_t GetPinnedUsage() const override;
|
||||
void EraseUnRefEntries() override;
|
||||
void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override;
|
||||
|
||||
private:
|
||||
static const uint32_t kInCacheBit = 1;
|
||||
@@ -675,7 +672,7 @@ void ClockCacheShard::EraseUnRefEntries() {
|
||||
Cleanup(context);
|
||||
}
|
||||
|
||||
class ClockCache : public ShardedCache {
|
||||
class ClockCache final : public ShardedCache {
|
||||
public:
|
||||
ClockCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit) {
|
||||
@@ -685,31 +682,31 @@ class ClockCache : public ShardedCache {
|
||||
SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
virtual ~ClockCache() { delete[] shards_; }
|
||||
~ClockCache() override { delete[] shards_; }
|
||||
|
||||
virtual const char* Name() const override { return "ClockCache"; }
|
||||
const char* Name() const override { return "ClockCache"; }
|
||||
|
||||
virtual CacheShard* GetShard(int shard) override {
|
||||
CacheShard* GetShard(int shard) override {
|
||||
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
||||
}
|
||||
|
||||
virtual const CacheShard* GetShard(int shard) const override {
|
||||
const CacheShard* GetShard(int shard) const override {
|
||||
return reinterpret_cast<CacheShard*>(&shards_[shard]);
|
||||
}
|
||||
|
||||
virtual void* Value(Handle* handle) override {
|
||||
void* Value(Handle* handle) override {
|
||||
return reinterpret_cast<const CacheHandle*>(handle)->value;
|
||||
}
|
||||
|
||||
virtual size_t GetCharge(Handle* handle) const override {
|
||||
size_t GetCharge(Handle* handle) const override {
|
||||
return reinterpret_cast<const CacheHandle*>(handle)->charge;
|
||||
}
|
||||
|
||||
virtual uint32_t GetHash(Handle* handle) const override {
|
||||
uint32_t GetHash(Handle* handle) const override {
|
||||
return reinterpret_cast<const CacheHandle*>(handle)->hash;
|
||||
}
|
||||
|
||||
virtual void DisownData() override { shards_ = nullptr; }
|
||||
void DisownData() override { shards_ = nullptr; }
|
||||
|
||||
private:
|
||||
ClockCacheShard* shards_;
|
||||
|
||||
Vendored
+14
-11
@@ -7,10 +7,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
|
||||
#include <assert.h>
|
||||
@@ -100,14 +96,16 @@ void LRUHandleTable::Resize() {
|
||||
}
|
||||
|
||||
LRUCacheShard::LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio)
|
||||
double high_pri_pool_ratio,
|
||||
bool use_adaptive_mutex)
|
||||
: capacity_(0),
|
||||
high_pri_pool_usage_(0),
|
||||
strict_capacity_limit_(strict_capacity_limit),
|
||||
high_pri_pool_ratio_(high_pri_pool_ratio),
|
||||
high_pri_pool_capacity_(0),
|
||||
usage_(0),
|
||||
lru_usage_(0) {
|
||||
lru_usage_(0),
|
||||
mutex_(use_adaptive_mutex) {
|
||||
// Make empty circular linked list
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
@@ -462,7 +460,8 @@ std::string LRUCacheShard::GetPrintableOptions() const {
|
||||
|
||||
LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit, double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> allocator)
|
||||
std::shared_ptr<MemoryAllocator> allocator,
|
||||
bool use_adaptive_mutex)
|
||||
: ShardedCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
std::move(allocator)) {
|
||||
num_shards_ = 1 << num_shard_bits;
|
||||
@@ -471,7 +470,8 @@ LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
size_t per_shard = (capacity + (num_shards_ - 1)) / num_shards_;
|
||||
for (int i = 0; i < num_shards_; i++) {
|
||||
new (&shards_[i])
|
||||
LRUCacheShard(per_shard, strict_capacity_limit, high_pri_pool_ratio);
|
||||
LRUCacheShard(per_shard, strict_capacity_limit, high_pri_pool_ratio,
|
||||
use_adaptive_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,13 +540,15 @@ std::shared_ptr<Cache> NewLRUCache(const LRUCacheOptions& cache_opts) {
|
||||
return NewLRUCache(cache_opts.capacity, cache_opts.num_shard_bits,
|
||||
cache_opts.strict_capacity_limit,
|
||||
cache_opts.high_pri_pool_ratio,
|
||||
cache_opts.memory_allocator);
|
||||
cache_opts.memory_allocator,
|
||||
cache_opts.use_adaptive_mutex);
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator) {
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator,
|
||||
bool use_adaptive_mutex) {
|
||||
if (num_shard_bits >= 20) {
|
||||
return nullptr; // the cache cannot be sharded into too many fine pieces
|
||||
}
|
||||
@@ -559,7 +561,8 @@ std::shared_ptr<Cache> NewLRUCache(
|
||||
}
|
||||
return std::make_shared<LRUCache>(capacity, num_shard_bits,
|
||||
strict_capacity_limit, high_pri_pool_ratio,
|
||||
std::move(memory_allocator));
|
||||
std::move(memory_allocator),
|
||||
use_adaptive_mutex);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
Vendored
+32
-19
@@ -55,10 +55,18 @@ struct LRUHandle {
|
||||
// cache itself is counted as 1
|
||||
|
||||
// Include the following flags:
|
||||
// in_cache: whether this entry is referenced by the hash table.
|
||||
// is_high_pri: whether this entry is high priority entry.
|
||||
// in_high_pri_pool: whether this entry is in high-pri pool.
|
||||
char flags;
|
||||
// IN_CACHE: whether this entry is referenced by the hash table.
|
||||
// IS_HIGH_PRI: whether this entry is high priority entry.
|
||||
// IN_HIGH_PRI_POOL: whether this entry is in high-pri pool.
|
||||
// HAS_HIT: whether this entry has had any lookups (hits).
|
||||
enum Flags : uint8_t {
|
||||
IN_CACHE = (1 << 0),
|
||||
IS_HIGH_PRI = (1 << 1),
|
||||
IN_HIGH_PRI_POOL = (1 << 2),
|
||||
HAS_HIT = (1 << 3),
|
||||
};
|
||||
|
||||
uint8_t flags;
|
||||
|
||||
uint32_t hash; // Hash of key(); used for fast sharding and comparisons
|
||||
|
||||
@@ -74,36 +82,36 @@ struct LRUHandle {
|
||||
}
|
||||
}
|
||||
|
||||
bool InCache() { return flags & 1; }
|
||||
bool IsHighPri() { return flags & 2; }
|
||||
bool InHighPriPool() { return flags & 4; }
|
||||
bool HasHit() { return flags & 8; }
|
||||
bool InCache() const { return flags & IN_CACHE; }
|
||||
bool IsHighPri() const { return flags & IS_HIGH_PRI; }
|
||||
bool InHighPriPool() const { return flags & IN_HIGH_PRI_POOL; }
|
||||
bool HasHit() const { return flags & HAS_HIT; }
|
||||
|
||||
void SetInCache(bool in_cache) {
|
||||
if (in_cache) {
|
||||
flags |= 1;
|
||||
flags |= IN_CACHE;
|
||||
} else {
|
||||
flags &= ~1;
|
||||
flags &= ~IN_CACHE;
|
||||
}
|
||||
}
|
||||
|
||||
void SetPriority(Cache::Priority priority) {
|
||||
if (priority == Cache::Priority::HIGH) {
|
||||
flags |= 2;
|
||||
flags |= IS_HIGH_PRI;
|
||||
} else {
|
||||
flags &= ~2;
|
||||
flags &= ~IS_HIGH_PRI;
|
||||
}
|
||||
}
|
||||
|
||||
void SetInHighPriPool(bool in_high_pri_pool) {
|
||||
if (in_high_pri_pool) {
|
||||
flags |= 4;
|
||||
flags |= IN_HIGH_PRI_POOL;
|
||||
} else {
|
||||
flags &= ~4;
|
||||
flags &= ~IN_HIGH_PRI_POOL;
|
||||
}
|
||||
}
|
||||
|
||||
void SetHit() { flags |= 8; }
|
||||
void SetHit() { flags |= HAS_HIT; }
|
||||
|
||||
void Free() {
|
||||
assert((refs == 1 && InCache()) || (refs == 0 && !InCache()));
|
||||
@@ -157,10 +165,10 @@ class LRUHandleTable {
|
||||
};
|
||||
|
||||
// A single shard of sharded cache.
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard {
|
||||
class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
public:
|
||||
LRUCacheShard(size_t capacity, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio);
|
||||
double high_pri_pool_ratio, bool use_adaptive_mutex);
|
||||
virtual ~LRUCacheShard();
|
||||
|
||||
// Separate from constructor so caller can easily make an array of LRUCache
|
||||
@@ -276,11 +284,16 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard {
|
||||
mutable port::Mutex mutex_;
|
||||
};
|
||||
|
||||
class LRUCache : public ShardedCache {
|
||||
class LRUCache
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public ShardedCache {
|
||||
public:
|
||||
LRUCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr);
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator = nullptr,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex);
|
||||
virtual ~LRUCache();
|
||||
virtual const char* Name() const override { return "LRUCache"; }
|
||||
virtual CacheShard* GetShard(int shard) override;
|
||||
|
||||
Vendored
+5
-4
@@ -8,14 +8,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "port/port.h"
|
||||
#include "util/testharness.h"
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class LRUCacheTest : public testing::Test {
|
||||
public:
|
||||
LRUCacheTest() {}
|
||||
~LRUCacheTest() { DeleteCache(); }
|
||||
~LRUCacheTest() override { DeleteCache(); }
|
||||
|
||||
void DeleteCache() {
|
||||
if (cache_ != nullptr) {
|
||||
@@ -25,12 +25,13 @@ class LRUCacheTest : public testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
void NewCache(size_t capacity, double high_pri_pool_ratio = 0.0) {
|
||||
void NewCache(size_t capacity, double high_pri_pool_ratio = 0.0,
|
||||
bool use_adaptive_mutex = kDefaultToAdaptiveMutex) {
|
||||
DeleteCache();
|
||||
cache_ = reinterpret_cast<LRUCacheShard*>(
|
||||
port::cacheline_aligned_alloc(sizeof(LRUCacheShard)));
|
||||
new (cache_) LRUCacheShard(capacity, false /*strict_capcity_limit*/,
|
||||
high_pri_pool_ratio);
|
||||
high_pri_pool_ratio, use_adaptive_mutex);
|
||||
}
|
||||
|
||||
void Insert(const std::string& key,
|
||||
|
||||
Vendored
-4
@@ -7,10 +7,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include "cache/sharded_cache.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
Vendored
+3
-2
@@ -54,7 +54,8 @@ class ShardedCache : public Cache {
|
||||
virtual CacheShard* GetShard(int shard) = 0;
|
||||
virtual const CacheShard* GetShard(int shard) const = 0;
|
||||
virtual void* Value(Handle* handle) override = 0;
|
||||
virtual size_t GetCharge(Handle* handle) const = 0;
|
||||
virtual size_t GetCharge(Handle* handle) const override = 0;
|
||||
|
||||
virtual uint32_t GetHash(Handle* handle) const = 0;
|
||||
virtual void DisownData() override = 0;
|
||||
|
||||
@@ -83,7 +84,7 @@ class ShardedCache : public Cache {
|
||||
|
||||
private:
|
||||
static inline uint32_t HashSlice(const Slice& s) {
|
||||
return Hash(s.data(), s.size(), 0);
|
||||
return static_cast<uint32_t>(GetSliceNPHash64(s));
|
||||
}
|
||||
|
||||
uint32_t Shard(uint32_t hash) {
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# - Find zlib
|
||||
# Find the zlib compression library and includes
|
||||
#
|
||||
# ZLIB_INCLUDE_DIR - where to find zlib.h, etc.
|
||||
# ZLIB_LIBRARIES - List of libraries when using zlib.
|
||||
# ZLIB_FOUND - True if zlib found.
|
||||
|
||||
find_path(ZLIB_INCLUDE_DIR
|
||||
NAMES zlib.h
|
||||
HINTS ${ZLIB_ROOT_DIR}/include)
|
||||
|
||||
find_library(ZLIB_LIBRARIES
|
||||
NAMES z
|
||||
HINTS ${ZLIB_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zlib DEFAULT_MSG ZLIB_LIBRARIES ZLIB_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
ZLIB_LIBRARIES
|
||||
ZLIB_INCLUDE_DIR)
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
# Exit on error.
|
||||
set -e
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
+30
-21
@@ -13,7 +13,7 @@
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/internal_stats.h"
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "file/filename.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -28,13 +29,12 @@
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block_based_table_builder.h"
|
||||
#include "table/block_based/block_based_table_builder.h"
|
||||
#include "table/format.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -47,18 +47,20 @@ TableBuilder* NewTableBuilder(
|
||||
int_tbl_prop_collector_factories,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts, int level,
|
||||
const std::string* compression_dict, const bool skip_filters,
|
||||
const uint64_t creation_time, const uint64_t oldest_key_time) {
|
||||
uint64_t sample_for_compression, const CompressionOptions& compression_opts,
|
||||
int level, const bool skip_filters, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, const uint64_t target_file_size,
|
||||
const uint64_t file_creation_time) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
return ioptions.table_factory->NewTableBuilder(
|
||||
TableBuilderOptions(ioptions, moptions, internal_comparator,
|
||||
int_tbl_prop_collector_factories, compression_type,
|
||||
compression_opts, compression_dict, skip_filters,
|
||||
column_family_name, level, creation_time,
|
||||
oldest_key_time),
|
||||
sample_for_compression, compression_opts,
|
||||
skip_filters, column_family_name, level,
|
||||
creation_time, oldest_key_time, target_file_size,
|
||||
file_creation_time),
|
||||
column_family_id, file);
|
||||
}
|
||||
|
||||
@@ -75,11 +77,12 @@ Status BuildTable(
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, const CompressionType compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
EventLogger* event_logger, int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, int level, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, Env::WriteLifeTimeHint write_hint) {
|
||||
uint64_t sample_for_compression, const CompressionOptions& compression_opts,
|
||||
bool paranoid_file_checks, InternalStats* internal_stats,
|
||||
TableFileCreationReason reason, EventLogger* event_logger, int job_id,
|
||||
const Env::IOPriority io_priority, TableProperties* table_properties,
|
||||
int level, const uint64_t creation_time, const uint64_t oldest_key_time,
|
||||
Env::WriteLifeTimeHint write_hint, const uint64_t file_creation_time) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -105,6 +108,11 @@ Status BuildTable(
|
||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||
TableBuilder* builder;
|
||||
std::unique_ptr<WritableFileWriter> file_writer;
|
||||
// Currently we only enable dictionary compression during compaction to the
|
||||
// bottommost level.
|
||||
CompressionOptions compression_opts_for_flush(compression_opts);
|
||||
compression_opts_for_flush.max_dict_bytes = 0;
|
||||
compression_opts_for_flush.zstd_max_train_bytes = 0;
|
||||
{
|
||||
std::unique_ptr<WritableFile> file;
|
||||
#ifndef NDEBUG
|
||||
@@ -121,15 +129,16 @@ Status BuildTable(
|
||||
file->SetIOPriority(io_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
|
||||
file_writer.reset(new WritableFileWriter(std::move(file), fname,
|
||||
env_options, ioptions.statistics,
|
||||
ioptions.listeners));
|
||||
file_writer.reset(
|
||||
new WritableFileWriter(std::move(file), fname, env_options, env,
|
||||
ioptions.statistics, ioptions.listeners));
|
||||
builder = NewTableBuilder(
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
int_tbl_prop_collector_factories, column_family_id,
|
||||
column_family_name, file_writer.get(), compression, compression_opts,
|
||||
level, nullptr /* compression_dict */, false /* skip_filters */,
|
||||
creation_time, oldest_key_time);
|
||||
column_family_name, file_writer.get(), compression,
|
||||
sample_for_compression, compression_opts_for_flush, level,
|
||||
false /* skip_filters */, creation_time, oldest_key_time,
|
||||
0 /*target_file_size*/, file_creation_time);
|
||||
}
|
||||
|
||||
MergeHelper merge(env, internal_comparator.user_comparator(),
|
||||
|
||||
+7
-6
@@ -11,6 +11,7 @@
|
||||
#include <vector>
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "logging/event_logger.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -20,7 +21,6 @@
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/event_logger.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -40,8 +40,6 @@ class InternalStats;
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown. It must outlive the
|
||||
// TableBuilder returned by this function.
|
||||
// @param compression_dict Data for presetting the compression library's
|
||||
// dictionary, or nullptr.
|
||||
TableBuilder* NewTableBuilder(
|
||||
const ImmutableCFOptions& options, const MutableCFOptions& moptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
@@ -49,10 +47,11 @@ TableBuilder* NewTableBuilder(
|
||||
int_tbl_prop_collector_factories,
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const uint64_t sample_for_compression,
|
||||
const CompressionOptions& compression_opts, int level,
|
||||
const std::string* compression_dict = nullptr,
|
||||
const bool skip_filters = false, const uint64_t creation_time = 0,
|
||||
const uint64_t oldest_key_time = 0);
|
||||
const uint64_t oldest_key_time = 0, const uint64_t target_file_size = 0,
|
||||
const uint64_t file_creation_time = 0);
|
||||
|
||||
// Build a Table file from the contents of *iter. The generated file
|
||||
// will be named according to number specified in meta. On success, the rest of
|
||||
@@ -75,12 +74,14 @@ extern Status BuildTable(
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
SnapshotChecker* snapshot_checker, const CompressionType compression,
|
||||
const uint64_t sample_for_compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH,
|
||||
TableProperties* table_properties = nullptr, int level = -1,
|
||||
const uint64_t creation_time = 0, const uint64_t oldest_key_time = 0,
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET);
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const uint64_t file_creation_time = 0);
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -208,13 +208,10 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
const char* (*name_)(void*);
|
||||
unsigned char ignore_snapshots_;
|
||||
|
||||
virtual ~rocksdb_compactionfilter_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_compactionfilter_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& existing_value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
bool Filter(int level, const Slice& key, const Slice& existing_value,
|
||||
std::string* new_value, bool* value_changed) const override {
|
||||
char* c_new_value = nullptr;
|
||||
size_t new_value_length = 0;
|
||||
unsigned char c_value_changed = 0;
|
||||
@@ -231,9 +228,9 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return ignore_snapshots_; }
|
||||
bool IgnoreSnapshots() const override { return ignore_snapshots_; }
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
@@ -243,9 +240,9 @@ struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
void*, rocksdb_compactionfiltercontext_t* context);
|
||||
const char* (*name_)(void*);
|
||||
|
||||
virtual ~rocksdb_compactionfilterfactory_t() { (*destructor_)(state_); }
|
||||
~rocksdb_compactionfilterfactory_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
rocksdb_compactionfiltercontext_t ccontext;
|
||||
ccontext.rep = context;
|
||||
@@ -253,7 +250,7 @@ struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
return std::unique_ptr<CompactionFilter>(cf);
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
};
|
||||
|
||||
struct rocksdb_comparator_t : public Comparator {
|
||||
@@ -265,20 +262,17 @@ struct rocksdb_comparator_t : public Comparator {
|
||||
const char* b, size_t blen);
|
||||
const char* (*name_)(void*);
|
||||
|
||||
virtual ~rocksdb_comparator_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_comparator_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
return (*compare_)(state_, a.data(), a.size(), b.data(), b.size());
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
// No-ops since the C binding does not support key shortening methods.
|
||||
virtual void FindShortestSeparator(std::string*,
|
||||
const Slice&) const override {}
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
void FindShortestSeparator(std::string*, const Slice&) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
@@ -298,14 +292,11 @@ struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
void*,
|
||||
const char* filter, size_t filter_length);
|
||||
|
||||
virtual ~rocksdb_filterpolicy_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_filterpolicy_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual void CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const override {
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const override {
|
||||
std::vector<const char*> key_pointers(n);
|
||||
std::vector<size_t> key_sizes(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
@@ -323,8 +314,7 @@ struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool KeyMayMatch(const Slice& key,
|
||||
const Slice& filter) const override {
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const override {
|
||||
return (*key_match_)(state_, key.data(), key.size(),
|
||||
filter.data(), filter.size());
|
||||
}
|
||||
@@ -349,14 +339,12 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
void*,
|
||||
const char* value, size_t value_length);
|
||||
|
||||
virtual ~rocksdb_mergeoperator_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_mergeoperator_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
size_t n = merge_in.operand_list.size();
|
||||
std::vector<const char*> operand_pointers(n);
|
||||
std::vector<size_t> operand_sizes(n);
|
||||
@@ -390,10 +378,10 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
return success;
|
||||
}
|
||||
|
||||
virtual bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* /*logger*/) const override {
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* /*logger*/) const override {
|
||||
size_t operand_count = operand_list.size();
|
||||
std::vector<const char*> operand_pointers(operand_count);
|
||||
std::vector<size_t> operand_sizes(operand_count);
|
||||
@@ -444,23 +432,21 @@ struct rocksdb_slicetransform_t : public SliceTransform {
|
||||
void*,
|
||||
const char* key, size_t length);
|
||||
|
||||
virtual ~rocksdb_slicetransform_t() {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
~rocksdb_slicetransform_t() override { (*destructor_)(state_); }
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual Slice Transform(const Slice& src) const override {
|
||||
Slice Transform(const Slice& src) const override {
|
||||
size_t len;
|
||||
char* dst = (*transform_)(state_, src.data(), src.size(), &len);
|
||||
return Slice(dst, len);
|
||||
}
|
||||
|
||||
virtual bool InDomain(const Slice& src) const override {
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return (*in_domain_)(state_, src.data(), src.size());
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& src) const override {
|
||||
bool InRange(const Slice& src) const override {
|
||||
return (*in_range_)(state_, src.data(), src.size());
|
||||
}
|
||||
};
|
||||
@@ -531,6 +517,21 @@ rocksdb_t* rocksdb_open_for_read_only(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_as_secondary(const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
const char* secondary_path,
|
||||
char** errptr) {
|
||||
DB* db;
|
||||
if (SaveError(errptr,
|
||||
DB::OpenAsSecondary(options->rep, std::string(name),
|
||||
std::string(secondary_path), &db))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_backup_engine_t* rocksdb_backup_engine_open(
|
||||
const rocksdb_options_t* options, const char* path, char** errptr) {
|
||||
BackupEngine* be;
|
||||
@@ -731,6 +732,37 @@ rocksdb_t* rocksdb_open_for_read_only_column_families(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_open_as_secondary_column_families(
|
||||
const rocksdb_options_t* db_options, const char* name,
|
||||
const char* secondary_path, int num_column_families,
|
||||
const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i != num_column_families; ++i) {
|
||||
column_families.emplace_back(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep));
|
||||
}
|
||||
DB* db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
if (SaveError(errptr, DB::OpenAsSecondary(DBOptions(db_options->rep),
|
||||
std::string(name),
|
||||
std::string(secondary_path),
|
||||
column_families, &handles, &db))) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i != handles.size(); ++i) {
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = db;
|
||||
return result;
|
||||
}
|
||||
|
||||
char** rocksdb_list_column_families(
|
||||
const rocksdb_options_t* options,
|
||||
const char* name,
|
||||
@@ -1084,6 +1116,18 @@ int rocksdb_property_int(
|
||||
}
|
||||
}
|
||||
|
||||
int rocksdb_property_int_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* propname,
|
||||
uint64_t *out_val) {
|
||||
if (db->rep->GetIntProperty(column_family->rep, Slice(propname), out_val)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
char* rocksdb_property_value_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -1197,6 +1241,14 @@ void rocksdb_flush(
|
||||
SaveError(errptr, db->rep->Flush(options->rep));
|
||||
}
|
||||
|
||||
void rocksdb_flush_cf(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_flushoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
char** errptr) {
|
||||
SaveError(errptr, db->rep->Flush(options->rep, column_family->rep));
|
||||
}
|
||||
|
||||
void rocksdb_disable_file_deletions(
|
||||
rocksdb_t* db,
|
||||
char** errptr) {
|
||||
@@ -1495,10 +1547,10 @@ class H : public WriteBatch::Handler {
|
||||
void* state_;
|
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen);
|
||||
void (*deleted_)(void*, const char* k, size_t klen);
|
||||
virtual void Put(const Slice& key, const Slice& value) override {
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
virtual void Delete(const Slice& key) override {
|
||||
void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
@@ -1784,11 +1836,11 @@ rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base(
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_writebatch_wi_create_iterator_with_base_cf(
|
||||
rocksdb_writebatch_wi_t* wbwi,
|
||||
rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_writebatch_wi_t* wbwi, rocksdb_iterator_t* base_iterator,
|
||||
rocksdb_column_family_handle_t* column_family) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = wbwi->rep->NewIteratorWithBase(column_family->rep, base_iterator->rep);
|
||||
result->rep =
|
||||
wbwi->rep->NewIteratorWithBase(column_family->rep, base_iterator->rep);
|
||||
delete base_iterator;
|
||||
return result;
|
||||
}
|
||||
@@ -2067,6 +2119,15 @@ void rocksdb_set_options(
|
||||
db->rep->SetOptions(options_map));
|
||||
}
|
||||
|
||||
void rocksdb_set_options_cf(
|
||||
rocksdb_t* db, rocksdb_column_family_handle_t* handle, int count, const char* const keys[], const char* const values[], char** errptr) {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
for (int i=0; i<count; i++)
|
||||
options_map[keys[i]] = values[i];
|
||||
SaveError(errptr,
|
||||
db->rep->SetOptions(handle->rep, options_map));
|
||||
}
|
||||
|
||||
rocksdb_options_t* rocksdb_options_create() {
|
||||
return new rocksdb_options_t;
|
||||
}
|
||||
@@ -2211,6 +2272,11 @@ void rocksdb_options_set_max_bytes_for_level_base(
|
||||
opt->rep.max_bytes_for_level_base = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_snap_refresh_nanos(rocksdb_options_t* opt,
|
||||
uint64_t n) {
|
||||
opt->rep.snap_refresh_nanos = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.level_compaction_dynamic_level_bytes = v;
|
||||
@@ -2453,6 +2519,11 @@ void rocksdb_options_set_enable_pipelined_write(rocksdb_options_t* opt,
|
||||
opt->rep.enable_pipelined_write = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_unordered_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.unordered_write = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_subcompactions(rocksdb_options_t* opt,
|
||||
uint32_t n) {
|
||||
opt->rep.max_subcompactions = n;
|
||||
@@ -2857,7 +2928,7 @@ rocksdb_compactionfilter_t* rocksdb_compactionfilter_create(
|
||||
result->state_ = state;
|
||||
result->destructor_ = destructor;
|
||||
result->filter_ = filter;
|
||||
result->ignore_snapshots_ = false;
|
||||
result->ignore_snapshots_ = true;
|
||||
result->name_ = name;
|
||||
return result;
|
||||
}
|
||||
@@ -2957,7 +3028,7 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom_format(int bits_per_ke
|
||||
// supplied C functions.
|
||||
struct Wrapper : public rocksdb_filterpolicy_t {
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
void CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const override {
|
||||
@@ -3248,6 +3319,22 @@ void rocksdb_env_join_all_threads(rocksdb_env_t* env) {
|
||||
env->rep->WaitForJoin();
|
||||
}
|
||||
|
||||
void rocksdb_env_lower_thread_pool_io_priority(rocksdb_env_t* env) {
|
||||
env->rep->LowerThreadPoolIOPriority();
|
||||
}
|
||||
|
||||
void rocksdb_env_lower_high_priority_thread_pool_io_priority(rocksdb_env_t* env) {
|
||||
env->rep->LowerThreadPoolIOPriority(Env::HIGH);
|
||||
}
|
||||
|
||||
void rocksdb_env_lower_thread_pool_cpu_priority(rocksdb_env_t* env) {
|
||||
env->rep->LowerThreadPoolCPUPriority();
|
||||
}
|
||||
|
||||
void rocksdb_env_lower_high_priority_thread_pool_cpu_priority(rocksdb_env_t* env) {
|
||||
env->rep->LowerThreadPoolCPUPriority(Env::HIGH);
|
||||
}
|
||||
|
||||
void rocksdb_env_destroy(rocksdb_env_t* env) {
|
||||
if (!env->is_default) delete env->rep;
|
||||
delete env;
|
||||
@@ -3382,6 +3469,10 @@ void rocksdb_ingest_external_file_cf(
|
||||
SaveError(errptr, db->rep->IngestExternalFile(handle->rep, files, opt->rep));
|
||||
}
|
||||
|
||||
void rocksdb_try_catch_up_with_primary(rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, db->rep->TryCatchUpWithPrimary());
|
||||
}
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create(
|
||||
void* state,
|
||||
void (*destructor)(void*),
|
||||
@@ -3412,7 +3503,7 @@ void rocksdb_slicetransform_destroy(rocksdb_slicetransform_t* st) {
|
||||
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
@@ -3707,6 +3798,38 @@ rocksdb_transactiondb_t* rocksdb_transactiondb_open(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_transactiondb_t* rocksdb_transactiondb_open_column_families(
|
||||
const rocksdb_options_t* options,
|
||||
const rocksdb_transactiondb_options_t* txn_db_options, const char* name,
|
||||
int num_column_families, const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
TransactionDB* txn_db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
if (SaveError(errptr, TransactionDB::Open(options->rep, txn_db_options->rep,
|
||||
std::string(name), column_families,
|
||||
&handles, &txn_db))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < handles.size(); i++) {
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_transactiondb_t* result = new rocksdb_transactiondb_t;
|
||||
result->rep = txn_db;
|
||||
return result;
|
||||
}
|
||||
|
||||
const rocksdb_snapshot_t* rocksdb_transactiondb_create_snapshot(
|
||||
rocksdb_transactiondb_t* txn_db) {
|
||||
rocksdb_snapshot_t* result = new rocksdb_snapshot_t;
|
||||
@@ -3827,6 +3950,26 @@ char* rocksdb_transaction_get_for_update(rocksdb_transaction_t* txn,
|
||||
return result;
|
||||
}
|
||||
|
||||
char* rocksdb_transaction_get_for_update_cf(
|
||||
rocksdb_transaction_t* txn, const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
|
||||
size_t* vlen, unsigned char exclusive, char** errptr) {
|
||||
char* result = nullptr;
|
||||
std::string tmp;
|
||||
Status s = txn->rep->GetForUpdate(options->rep, column_family->rep,
|
||||
Slice(key, klen), &tmp, exclusive);
|
||||
if (s.ok()) {
|
||||
*vlen = tmp.size();
|
||||
result = CopyString(tmp);
|
||||
} else {
|
||||
*vlen = 0;
|
||||
if (!s.IsNotFound()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read a key outside a transaction
|
||||
char* rocksdb_transactiondb_get(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
@@ -3919,6 +4062,14 @@ void rocksdb_transaction_merge(rocksdb_transaction_t* txn, const char* key,
|
||||
SaveError(errptr, txn->rep->Merge(Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transaction_merge_cf(rocksdb_transaction_t* txn,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Merge(column_family->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
// Merge a key outside a transaction
|
||||
void rocksdb_transactiondb_merge(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
@@ -3928,6 +4079,14 @@ void rocksdb_transactiondb_merge(rocksdb_transactiondb_t* txn_db,
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_merge_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_writeoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family, const char* key, size_t klen,
|
||||
const char* val, size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Merge(options->rep, column_family->rep,
|
||||
Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
// Delete a key inside a transaction
|
||||
void rocksdb_transaction_delete(rocksdb_transaction_t* txn, const char* key,
|
||||
size_t klen, char** errptr) {
|
||||
@@ -3980,6 +4139,14 @@ rocksdb_iterator_t* rocksdb_transactiondb_create_iterator(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_iterator_t* rocksdb_transactiondb_create_iterator_cf(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = txn_db->rep->NewIterator(options->rep, column_family->rep);
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_close(rocksdb_transactiondb_t* txn_db) {
|
||||
delete txn_db->rep;
|
||||
delete txn_db;
|
||||
|
||||
+64
-2
@@ -1,6 +1,7 @@
|
||||
/* Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style license that can be
|
||||
found in the LICENSE file. See the AUTHORS file for names of contributors. */
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -44,6 +45,7 @@ static char sstfilename[200];
|
||||
static char dbbackupname[200];
|
||||
static char dbcheckpointname[200];
|
||||
static char dbpathname[200];
|
||||
static char secondary_path[200];
|
||||
|
||||
static void StartPhase(const char* name) {
|
||||
fprintf(stderr, "=== Test %s\n", name);
|
||||
@@ -917,7 +919,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_wi_t* wbi = rocksdb_writebatch_wi_create(0, 1);
|
||||
rocksdb_writebatch_wi_put(wbi, "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_wi_delete(wbi, "foo", 3);
|
||||
rocksdb_iterator_t* iter = rocksdb_writebatch_wi_create_iterator_with_base(wbi, base_iter);
|
||||
rocksdb_iterator_t* iter =
|
||||
rocksdb_writebatch_wi_create_iterator_with_base(wbi, base_iter);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
@@ -1186,6 +1189,12 @@ int main(int argc, char** argv) {
|
||||
rocksdb_put_cf(db, woptions, handles[1], "foo", 3, "hello", 5, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_flushoptions_t *flush_options = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_options, 1);
|
||||
rocksdb_flush_cf(db, flush_options, handles[1], &err);
|
||||
CheckNoError(err)
|
||||
rocksdb_flushoptions_destroy(flush_options);
|
||||
|
||||
CheckGetCF(db, roptions, handles[1], "foo", "hello");
|
||||
CheckPinGetCF(db, roptions, handles[1], "foo", "hello");
|
||||
|
||||
@@ -1527,7 +1536,7 @@ int main(int argc, char** argv) {
|
||||
const rocksdb_snapshot_t* snapshot;
|
||||
snapshot = rocksdb_transactiondb_create_snapshot(txn_db);
|
||||
rocksdb_readoptions_set_snapshot(roptions, snapshot);
|
||||
|
||||
|
||||
rocksdb_transactiondb_put(txn_db, woptions, "foo", 3, "hey", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
@@ -1714,6 +1723,59 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
// Check that secondary instance works.
|
||||
StartPhase("open_as_secondary");
|
||||
{
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
|
||||
rocksdb_options_t* db_options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(db_options, 1);
|
||||
db = rocksdb_open(db_options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_t* db1;
|
||||
rocksdb_options_t* opts = rocksdb_options_create();
|
||||
rocksdb_options_set_max_open_files(opts, -1);
|
||||
rocksdb_options_set_create_if_missing(opts, 1);
|
||||
snprintf(secondary_path, sizeof(secondary_path),
|
||||
"%s/rocksdb_c_test_secondary-%d", GetTempDir(), ((int)geteuid()));
|
||||
db1 = rocksdb_open_as_secondary(opts, dbname, secondary_path, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_writeoptions_set_sync(woptions, 0);
|
||||
rocksdb_writeoptions_disable_WAL(woptions, 1);
|
||||
rocksdb_put(db, woptions, "key0", 4, "value0", 6, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_flushoptions_t* flush_opts = rocksdb_flushoptions_create();
|
||||
rocksdb_flushoptions_set_wait(flush_opts, 1);
|
||||
rocksdb_flush(db, flush_opts, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_try_catch_up_with_primary(db1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_readoptions_t* ropts = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_verify_checksums(ropts, 1);
|
||||
rocksdb_readoptions_set_snapshot(ropts, NULL);
|
||||
CheckGet(db, ropts, "key0", "value0");
|
||||
CheckGet(db1, ropts, "key0", "value0");
|
||||
|
||||
rocksdb_writeoptions_disable_WAL(woptions, 0);
|
||||
rocksdb_put(db, woptions, "key1", 4, "value1", 6, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_try_catch_up_with_primary(db1, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db1, ropts, "key0", "value0");
|
||||
CheckGet(db1, ropts, "key1", "value1");
|
||||
|
||||
rocksdb_close(db1);
|
||||
rocksdb_destroy_db(opts, secondary_path, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_destroy(db_options);
|
||||
rocksdb_options_destroy(opts);
|
||||
rocksdb_readoptions_destroy(ropts);
|
||||
rocksdb_flushoptions_destroy(flush_opts);
|
||||
}
|
||||
|
||||
// Simple sanity check that options setting db_paths work.
|
||||
StartPhase("open_db_paths");
|
||||
{
|
||||
|
||||
+90
-33
@@ -9,34 +9,31 @@
|
||||
|
||||
#include "db/column_family.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#include "db/compaction_picker_universal.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -63,12 +60,24 @@ ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
JobContext job_context(0);
|
||||
mutex_->Lock();
|
||||
if (cfd_->Unref()) {
|
||||
bool dropped = cfd_->IsDropped();
|
||||
|
||||
delete cfd_;
|
||||
|
||||
if (dropped) {
|
||||
db_->FindObsoleteFiles(&job_context, false, true);
|
||||
}
|
||||
}
|
||||
db_->FindObsoleteFiles(&job_context, false, true);
|
||||
mutex_->Unlock();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(job_context);
|
||||
bool defer_purge =
|
||||
db_->immutable_db_options().avoid_unnecessary_blocking_io;
|
||||
db_->PurgeObsoleteFiles(job_context, defer_purge);
|
||||
if (defer_purge) {
|
||||
mutex_->Lock();
|
||||
db_->SchedulePurge();
|
||||
mutex_->Unlock();
|
||||
}
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
@@ -129,14 +138,10 @@ Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
|
||||
}
|
||||
}
|
||||
if (cf_options.compression_opts.zstd_max_train_bytes > 0) {
|
||||
if (!CompressionTypeSupported(CompressionType::kZSTD)) {
|
||||
// Dictionary trainer is available since v0.6.1, but ZSTD was marked
|
||||
// stable only since v0.8.0. For now we enable the feature in stable
|
||||
// versions only.
|
||||
if (!ZSTD_TrainDictionarySupported()) {
|
||||
return Status::InvalidArgument(
|
||||
"zstd dictionary trainer cannot be used because " +
|
||||
CompressionTypeToString(CompressionType::kZSTD) +
|
||||
" is not linked with the binary.");
|
||||
"zstd dictionary trainer cannot be used because ZSTD 1.1.3+ "
|
||||
"is not linked with the binary.");
|
||||
}
|
||||
if (cf_options.compression_opts.max_dict_bytes == 0) {
|
||||
return Status::InvalidArgument(
|
||||
@@ -400,7 +405,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* _dummy_versions,
|
||||
Cache* _table_cache, WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, ColumnFamilySet* column_family_set)
|
||||
const EnvOptions& env_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(_dummy_versions),
|
||||
@@ -440,7 +446,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
if (_dummy_versions != nullptr) {
|
||||
internal_stats_.reset(
|
||||
new InternalStats(ioptions_.num_levels, db_options.env, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, env_options, _table_cache));
|
||||
table_cache_.reset(new TableCache(ioptions_, env_options, _table_cache,
|
||||
block_cache_tracer));
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
new LevelCompactionPicker(ioptions_, &internal_comparator_));
|
||||
@@ -986,13 +993,14 @@ const int ColumnFamilyData::kCompactToBaseLevel = -2;
|
||||
|
||||
Compaction* ColumnFamilyData::CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options, int input_level,
|
||||
int output_level, uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
int output_level, const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* conflict) {
|
||||
InternalKey** compaction_end, bool* conflict,
|
||||
uint64_t max_file_num_to_ignore) {
|
||||
auto* result = compaction_picker_->CompactRange(
|
||||
GetName(), mutable_cf_options, current_->storage_info(), input_level,
|
||||
output_level, output_path_id, max_subcompactions, begin, end,
|
||||
compaction_end, conflict);
|
||||
output_level, compact_range_options, begin, end, compaction_end, conflict,
|
||||
max_file_num_to_ignore);
|
||||
if (result != nullptr) {
|
||||
result->SetInputVersion(current_);
|
||||
}
|
||||
@@ -1138,13 +1146,60 @@ void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
}
|
||||
}
|
||||
|
||||
Status ColumnFamilyData::ValidateOptions(
|
||||
const DBOptions& db_options, const ColumnFamilyOptions& cf_options) {
|
||||
Status s;
|
||||
s = CheckCompressionSupported(cf_options);
|
||||
if (s.ok() && db_options.allow_concurrent_memtable_write) {
|
||||
s = CheckConcurrentWritesSupported(cf_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = CheckCFPathsSupported(db_options, cf_options);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (cf_options.ttl > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cf_options.table_factory->Name() != BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported in Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
|
||||
if (cf_options.periodic_compaction_seconds > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"Periodic Compaction is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cf_options.table_factory->Name() != BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"Periodic Compaction is only supported in "
|
||||
"Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status ColumnFamilyData::SetOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
const DBOptions& db_options,
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
MutableCFOptions new_mutable_cf_options;
|
||||
Status s =
|
||||
GetMutableOptionsFromStrings(mutable_cf_options_, options_map,
|
||||
ioptions_.info_log, &new_mutable_cf_options);
|
||||
if (s.ok()) {
|
||||
ColumnFamilyOptions cf_options =
|
||||
BuildColumnFamilyOptions(initial_cf_options_, new_mutable_cf_options);
|
||||
s = ValidateOptions(db_options, cf_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
mutable_cf_options_ = new_mutable_cf_options;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
@@ -1201,18 +1256,20 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
const EnvOptions& env_options,
|
||||
Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller)
|
||||
WriteController* write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer)
|
||||
: max_column_family_(0),
|
||||
dummy_cfd_(new ColumnFamilyData(0, "", nullptr, nullptr, nullptr,
|
||||
ColumnFamilyOptions(), *db_options,
|
||||
env_options, nullptr)),
|
||||
dummy_cfd_(new ColumnFamilyData(
|
||||
0, "", nullptr, nullptr, nullptr, ColumnFamilyOptions(), *db_options,
|
||||
env_options, nullptr, block_cache_tracer)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
table_cache_(table_cache),
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
write_controller_(write_controller) {
|
||||
write_controller_(write_controller),
|
||||
block_cache_tracer_(block_cache_tracer) {
|
||||
// initialize linked list
|
||||
dummy_cfd_->prev_ = dummy_cfd_;
|
||||
dummy_cfd_->next_ = dummy_cfd_;
|
||||
@@ -1280,7 +1337,7 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
*db_options_, env_options_, this);
|
||||
*db_options_, env_options_, this, block_cache_tracer_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
|
||||
+121
-4
@@ -24,6 +24,7 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "trace_replay/block_cache_tracer.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -45,6 +46,112 @@ class InstrumentedMutexLock;
|
||||
struct SuperVersionContext;
|
||||
|
||||
extern const double kIncSlowdownRatio;
|
||||
// This file contains a list of data structures for managing column family
|
||||
// level metadata.
|
||||
//
|
||||
// The basic relationships among classes declared here are illustrated as
|
||||
// following:
|
||||
//
|
||||
// +----------------------+ +----------------------+ +--------+
|
||||
// +---+ ColumnFamilyHandle 1 | +--+ ColumnFamilyHandle 2 | | DBImpl |
|
||||
// | +----------------------+ | +----------------------+ +----+---+
|
||||
// | +--------------------------+ |
|
||||
// | | +-----------------------------+
|
||||
// | | |
|
||||
// | | +-----------------------------v-------------------------------+
|
||||
// | | | |
|
||||
// | | | ColumnFamilySet |
|
||||
// | | | |
|
||||
// | | +-------------+--------------------------+----------------+---+
|
||||
// | | | | |
|
||||
// | +-------------------------------------+ | |
|
||||
// | | | | v
|
||||
// | +-------------v-------------+ +-----v----v---------+
|
||||
// | | | | |
|
||||
// | | ColumnFamilyData 1 | | ColumnFamilyData 2 | ......
|
||||
// | | | | |
|
||||
// +---> | | |
|
||||
// | +---------+ | |
|
||||
// | | MemTable| | |
|
||||
// | | List | | |
|
||||
// +--------+---+--+-+----+----+ +--------------------++
|
||||
// | | | |
|
||||
// | | | |
|
||||
// | | | +-----------------------+
|
||||
// | | +-----------+ |
|
||||
// v +--------+ | |
|
||||
// +--------+--------+ | | |
|
||||
// | | | | +----------v----------+
|
||||
// +---> |SuperVersion 1.a +-----------------> |
|
||||
// | +------+ | | MemTableListVersion |
|
||||
// +---+-------------+ | | | | |
|
||||
// | | | | +----+------------+---+
|
||||
// | current | | | | |
|
||||
// | +-------------+ | |mem | |
|
||||
// | | | | | |
|
||||
// +-v---v-------+ +---v--v---+ +-----v----+ +----v-----+
|
||||
// | | | | | | | |
|
||||
// | Version 1.a | | memtable | | memtable | | memtable |
|
||||
// | | | 1.a | | 1.b | | 1.c |
|
||||
// +-------------+ | | | | | |
|
||||
// +----------+ +----------+ +----------+
|
||||
//
|
||||
// DBImpl keeps a ColumnFamilySet, which references to all column families by
|
||||
// pointing to respective ColumnFamilyData object of each column family.
|
||||
// This is how DBImpl can list and operate on all the column families.
|
||||
// ColumnFamilyHandle also points to ColumnFamilyData directly, so that
|
||||
// when a user executes a query, it can directly find memtables and Version
|
||||
// as well as SuperVersion to the column family, without going through
|
||||
// ColumnFamilySet.
|
||||
//
|
||||
// ColumnFamilySet points to the latest view of the LSM-tree (list of memtables
|
||||
// and SST files) indirectly, while ongoing operations may hold references
|
||||
// to a current or an out-of-date SuperVersion, which in turn points to a
|
||||
// point-in-time view of the LSM-tree. This guarantees the memtables and SST
|
||||
// files being operated on will not go away, until the SuperVersion is
|
||||
// unreferenced to 0 and destoryed.
|
||||
//
|
||||
// The following graph illustrates a possible referencing relationships:
|
||||
//
|
||||
// Column +--------------+ current +-----------+
|
||||
// Family +---->+ +------------------->+ |
|
||||
// Data | SuperVersion +----------+ | Version A |
|
||||
// | 3 | imm | | |
|
||||
// Iter2 +----->+ | +-------v------+ +-----------+
|
||||
// +-----+--------+ | MemtableList +----------------> Empty
|
||||
// | | Version r | +-----------+
|
||||
// | +--------------+ | |
|
||||
// +------------------+ current| Version B |
|
||||
// +--------------+ | +----->+ |
|
||||
// | | | | +-----+-----+
|
||||
// Compaction +>+ SuperVersion +-------------+ ^
|
||||
// Job | 2 +------+ | |current
|
||||
// | +----+ | | mem | +------------+
|
||||
// +--------------+ | | +---------------------> |
|
||||
// | +------------------------> MemTable a |
|
||||
// | mem | | |
|
||||
// +--------------+ | | +------------+
|
||||
// | +--------------------------+
|
||||
// Iter1 +-----> SuperVersion | | +------------+
|
||||
// | 1 +------------------------------>+ |
|
||||
// | +-+ | mem | MemTable b |
|
||||
// +--------------+ | | | |
|
||||
// | | +--------------+ +-----^------+
|
||||
// | |imm | MemtableList | |
|
||||
// | +--->+ Version s +------------+
|
||||
// | +--------------+
|
||||
// | +--------------+
|
||||
// | | MemtableList |
|
||||
// +------>+ Version t +--------> Empty
|
||||
// imm +--------------+
|
||||
//
|
||||
// In this example, even if the current LSM-tree consists of Version A and
|
||||
// memtable a, which is also referenced by SuperVersion, two older SuperVersion
|
||||
// SuperVersion2 and Superversion1 still exist, and are referenced by a
|
||||
// compaction job and an old iterator Iter1, respectively. SuperVersion2
|
||||
// contains Version B, memtable a and memtable b; SuperVersion1 contains
|
||||
// Version B and memtable b (mutable). As a result, Version B and memtable b
|
||||
// are prevented from being destroyed or deleted.
|
||||
|
||||
// ColumnFamilyHandleImpl is the class that clients use to access different
|
||||
// column families. It has non-trivial destructor, which gets called when client
|
||||
@@ -232,9 +339,13 @@ class ColumnFamilyData {
|
||||
|
||||
bool is_delete_range_supported() { return is_delete_range_supported_; }
|
||||
|
||||
// Validate CF options against DB options
|
||||
static Status ValidateOptions(const DBOptions& db_options,
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
#ifndef ROCKSDB_LITE
|
||||
// REQUIRES: DB mutex held
|
||||
Status SetOptions(
|
||||
const DBOptions& db_options,
|
||||
const std::unordered_map<std::string, std::string>& options_map);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -296,9 +407,10 @@ class ColumnFamilyData {
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* CompactRange(const MutableCFOptions& mutable_cf_options,
|
||||
int input_level, int output_level,
|
||||
uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict);
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore);
|
||||
|
||||
CompactionPicker* compaction_picker() { return compaction_picker_.get(); }
|
||||
// thread-safe
|
||||
@@ -383,6 +495,8 @@ class ColumnFamilyData {
|
||||
|
||||
Directory* GetDataDir(size_t path_id) const;
|
||||
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
@@ -391,7 +505,8 @@ class ColumnFamilyData {
|
||||
const ColumnFamilyOptions& options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options,
|
||||
ColumnFamilySet* column_family_set);
|
||||
ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer);
|
||||
|
||||
uint32_t id_;
|
||||
const std::string name_;
|
||||
@@ -519,7 +634,8 @@ class ColumnFamilySet {
|
||||
const ImmutableDBOptions* db_options,
|
||||
const EnvOptions& env_options, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller);
|
||||
WriteController* write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer);
|
||||
~ColumnFamilySet();
|
||||
|
||||
ColumnFamilyData* GetDefault() const;
|
||||
@@ -578,6 +694,7 @@ class ColumnFamilySet {
|
||||
Cache* table_cache_;
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
WriteController* write_controller_;
|
||||
BlockCacheTracer* const block_cache_tracer_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
|
||||
+32
-28
@@ -12,19 +12,20 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -68,7 +69,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
DestroyDB(dbname_, Options(db_options_, column_family_options_));
|
||||
}
|
||||
|
||||
virtual ~ColumnFamilyTestBase() {
|
||||
~ColumnFamilyTestBase() override {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (auto h : handles_) {
|
||||
ColumnFamilyDescriptor cfdescriptor;
|
||||
@@ -1206,29 +1207,32 @@ TEST_P(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // Cuckoo is not supported in lite
|
||||
TEST_P(ColumnFamilyTest, MemtableNotSupportSnapshot) {
|
||||
db_options_.allow_concurrent_memtable_write = false;
|
||||
Open();
|
||||
auto* s1 = dbfull()->GetSnapshot();
|
||||
ASSERT_TRUE(s1 != nullptr);
|
||||
dbfull()->ReleaseSnapshot(s1);
|
||||
// The test is commented out because we want to test that snapshot is
|
||||
// not created for memtables not supported it, but There isn't a memtable
|
||||
// that doesn't support snapshot right now. If we have one later, we can
|
||||
// re-enable the test.
|
||||
//
|
||||
// #ifndef ROCKSDB_LITE // Cuckoo is not supported in lite
|
||||
// TEST_P(ColumnFamilyTest, MemtableNotSupportSnapshot) {
|
||||
// db_options_.allow_concurrent_memtable_write = false;
|
||||
// Open();
|
||||
// auto* s1 = dbfull()->GetSnapshot();
|
||||
// ASSERT_TRUE(s1 != nullptr);
|
||||
// dbfull()->ReleaseSnapshot(s1);
|
||||
|
||||
// Add a column family that doesn't support snapshot
|
||||
ColumnFamilyOptions first;
|
||||
first.memtable_factory.reset(NewHashCuckooRepFactory(1024 * 1024));
|
||||
CreateColumnFamilies({"first"}, {first});
|
||||
auto* s2 = dbfull()->GetSnapshot();
|
||||
ASSERT_TRUE(s2 == nullptr);
|
||||
// // Add a column family that doesn't support snapshot
|
||||
// ColumnFamilyOptions first;
|
||||
// first.memtable_factory.reset(new DummyMemtableNotSupportingSnapshot());
|
||||
// CreateColumnFamilies({"first"}, {first});
|
||||
// auto* s2 = dbfull()->GetSnapshot();
|
||||
// ASSERT_TRUE(s2 == nullptr);
|
||||
|
||||
// Add a column family that supports snapshot. Snapshot stays not supported.
|
||||
ColumnFamilyOptions second;
|
||||
CreateColumnFamilies({"second"}, {second});
|
||||
auto* s3 = dbfull()->GetSnapshot();
|
||||
ASSERT_TRUE(s3 == nullptr);
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
// // Add a column family that supports snapshot. Snapshot stays not
|
||||
// supported. ColumnFamilyOptions second; CreateColumnFamilies({"second"},
|
||||
// {second}); auto* s3 = dbfull()->GetSnapshot(); ASSERT_TRUE(s3 == nullptr);
|
||||
// Close();
|
||||
// }
|
||||
// #endif // !ROCKSDB_LITE
|
||||
|
||||
class TestComparator : public Comparator {
|
||||
int Compare(const rocksdb::Slice& /*a*/,
|
||||
@@ -2042,7 +2046,7 @@ TEST_P(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // Tailing interator not supported
|
||||
#ifndef ROCKSDB_LITE // Tailing iterator not supported
|
||||
namespace {
|
||||
std::string IterStatus(Iterator* iter) {
|
||||
std::string result;
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -35,9 +35,9 @@ class CompactFilesTest : public testing::Test {
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(info.file_path);
|
||||
}
|
||||
@@ -256,9 +256,9 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
class FilterWithGet : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
if (db_ == nullptr) {
|
||||
return true;
|
||||
}
|
||||
@@ -271,7 +271,7 @@ TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
db_ = db;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "FilterWithGet"; }
|
||||
const char* Name() const override { return "FilterWithGet"; }
|
||||
|
||||
private:
|
||||
DB* db_;
|
||||
@@ -357,6 +357,51 @@ TEST_F(CompactFilesTest, SentinelCompressionType) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, GetCompactionJobInfo) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Disable RocksDB background compaction.
|
||||
options.compaction_style = kCompactionStyleNone;
|
||||
options.level0_slowdown_writes_trigger = 1000;
|
||||
options.level0_stop_writes_trigger = 1000;
|
||||
options.write_buffer_size = 65536;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.compression = kNoCompression;
|
||||
options.max_compaction_bytes = 5000;
|
||||
|
||||
// Add listener
|
||||
FlushedFileCollector* collector = new FlushedFileCollector();
|
||||
options.listeners.emplace_back(collector);
|
||||
|
||||
DB* db = nullptr;
|
||||
DestroyDB(db_name_, options);
|
||||
Status s = DB::Open(options, db_name_, &db);
|
||||
assert(s.ok());
|
||||
assert(db);
|
||||
|
||||
// create couple files
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
db->Put(WriteOptions(), ToString(i), std::string(1000, 'a' + (i % 26)));
|
||||
}
|
||||
reinterpret_cast<DBImpl*>(db)->TEST_WaitForFlushMemTable();
|
||||
auto l0_files_1 = collector->GetFlushedFiles();
|
||||
CompactionOptions co;
|
||||
co.compression = CompressionType::kLZ4Compression;
|
||||
CompactionJobInfo compaction_job_info;
|
||||
ASSERT_OK(
|
||||
db->CompactFiles(co, l0_files_1, 0, -1, nullptr, &compaction_job_info));
|
||||
ASSERT_EQ(compaction_job_info.base_input_level, 0);
|
||||
ASSERT_EQ(compaction_job_info.cf_id, db->DefaultColumnFamily()->GetID());
|
||||
ASSERT_EQ(compaction_job_info.cf_name, db->DefaultColumnFamily()->GetName());
|
||||
ASSERT_EQ(compaction_job_info.compaction_reason,
|
||||
CompactionReason::kManualCompaction);
|
||||
ASSERT_EQ(compaction_job_info.compression, CompressionType::kLZ4Compression);
|
||||
ASSERT_EQ(compaction_job_info.output_level, 0);
|
||||
ASSERT_OK(compaction_job_info.status);
|
||||
// no assertion failure
|
||||
delete db;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "db/compacted_db_impl.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/version_set.h"
|
||||
#include "table/get_context.h"
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
#include "db/db_impl.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
@@ -7,19 +7,14 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -250,6 +245,12 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
if (max_subcompactions_ == 0) {
|
||||
max_subcompactions_ = immutable_cf_options_.max_subcompactions;
|
||||
}
|
||||
if (!bottommost_level_) {
|
||||
// Currently we only enable dictionary compression during compaction to the
|
||||
// bottommost level.
|
||||
output_compression_opts_.max_dict_bytes = 0;
|
||||
output_compression_opts_.zstd_max_train_bytes = 0;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i < inputs_.size(); ++i) {
|
||||
@@ -373,8 +374,8 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) {
|
||||
// We've advanced far enough
|
||||
if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) {
|
||||
// Key falls in this file's range, so definitely
|
||||
// exists beyond output level
|
||||
// Key falls in this file's range, so it may
|
||||
// exist beyond output level
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -9,11 +9,13 @@
|
||||
|
||||
#pragma once
|
||||
#include "db/version_set.h"
|
||||
#include "memory/arena.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace rocksdb {
|
||||
// The file contains class Compaction, as well as some helper functions
|
||||
// and data structures used by the class.
|
||||
|
||||
// Utility for comparing sstable boundary keys. Returns -1 if either a or b is
|
||||
// null which provides the property that a==null indicates a key that is less
|
||||
@@ -63,7 +65,7 @@ class ColumnFamilyData;
|
||||
class VersionStorageInfo;
|
||||
class CompactionFilter;
|
||||
|
||||
// A Compaction encapsulates information about a compaction.
|
||||
// A Compaction encapsulates metadata about a compaction.
|
||||
class Compaction {
|
||||
public:
|
||||
Compaction(VersionStorageInfo* input_version,
|
||||
@@ -376,7 +378,7 @@ class Compaction {
|
||||
CompactionReason compaction_reason_;
|
||||
};
|
||||
|
||||
// Utility function
|
||||
// Return sum of sizes of all files in `files`.
|
||||
extern uint64_t TotalFileSize(const std::vector<FileMetaData*>& files);
|
||||
|
||||
} // namespace rocksdb
|
||||
File renamed without changes.
@@ -3,12 +3,28 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
#define DEFINITELY_IN_SNAPSHOT(seq, snapshot) \
|
||||
((seq) <= (snapshot) && \
|
||||
(snapshot_checker_ == nullptr || \
|
||||
LIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) == \
|
||||
SnapshotCheckerResult::kInSnapshot)))
|
||||
|
||||
#define DEFINITELY_NOT_IN_SNAPSHOT(seq, snapshot) \
|
||||
((seq) > (snapshot) || \
|
||||
(snapshot_checker_ != nullptr && \
|
||||
UNLIKELY(snapshot_checker_->CheckInSnapshot((seq), (snapshot)) == \
|
||||
SnapshotCheckerResult::kNotInSnapshot)))
|
||||
|
||||
#define IN_EARLIEST_SNAPSHOT(seq) \
|
||||
((seq) <= earliest_snapshot_ && \
|
||||
(snapshot_checker_ == nullptr || LIKELY(IsInEarliestSnapshot(seq))))
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -21,14 +37,16 @@ CompactionIterator::CompactionIterator(
|
||||
CompactionRangeDelAggregator* range_del_agg, const Compaction* compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum)
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
SnapshotListFetchCallback* snap_list_callback)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, env,
|
||||
report_detailed_time, expect_valid_internal_key, range_del_agg,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new CompactionProxy(compaction) : nullptr),
|
||||
compaction_filter, shutting_down, preserve_deletes_seqnum) {}
|
||||
compaction_filter, shutting_down, preserve_deletes_seqnum,
|
||||
snap_list_callback) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
@@ -40,7 +58,8 @@ CompactionIterator::CompactionIterator(
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum)
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
SnapshotListFetchCallback* snap_list_callback)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
@@ -55,42 +74,21 @@ CompactionIterator::CompactionIterator(
|
||||
compaction_filter_(compaction_filter),
|
||||
shutting_down_(shutting_down),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
ignore_snapshots_(false),
|
||||
current_user_key_sequence_(0),
|
||||
current_user_key_snapshot_(0),
|
||||
merge_out_iter_(merge_helper_),
|
||||
current_key_committed_(false) {
|
||||
current_key_committed_(false),
|
||||
snap_list_callback_(snap_list_callback) {
|
||||
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
||||
assert(snapshots_ != nullptr);
|
||||
bottommost_level_ =
|
||||
compaction_ == nullptr ? false : compaction_->bottommost_level();
|
||||
if (compaction_ != nullptr) {
|
||||
level_ptrs_ = std::vector<size_t>(compaction_->number_levels(), 0);
|
||||
}
|
||||
|
||||
if (snapshots_->size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = true;
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
latest_snapshot_ = 0;
|
||||
} else {
|
||||
visible_at_tip_ = false;
|
||||
earliest_snapshot_ = snapshots_->at(0);
|
||||
latest_snapshot_ = snapshots_->back();
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
// findEarliestVisibleSnapshot assumes this ordering.
|
||||
for (size_t i = 1; i < snapshots_->size(); ++i) {
|
||||
assert(snapshots_->at(i - 1) <= snapshots_->at(i));
|
||||
}
|
||||
#endif
|
||||
if (compaction_filter_ != nullptr) {
|
||||
if (compaction_filter_->IgnoreSnapshots()) {
|
||||
ignore_snapshots_ = true;
|
||||
}
|
||||
} else {
|
||||
ignore_snapshots_ = false;
|
||||
}
|
||||
ProcessSnapshotList();
|
||||
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator:AfterInit", compaction_.get());
|
||||
}
|
||||
|
||||
CompactionIterator::~CompactionIterator() {
|
||||
@@ -160,12 +158,7 @@ void CompactionIterator::Next() {
|
||||
void CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
Slice* skip_until) {
|
||||
if (compaction_filter_ != nullptr &&
|
||||
(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex) &&
|
||||
(visible_at_tip_ || ignore_snapshots_ ||
|
||||
ikey_.sequence > latest_snapshot_ ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
latest_snapshot_))))) {
|
||||
(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex)) {
|
||||
// If the user has specified a compaction filter and the sequence
|
||||
// number is greater than any external snapshot, then invoke the
|
||||
// filter. If the return value of the compaction filter is true,
|
||||
@@ -215,6 +208,28 @@ void CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionIterator::ProcessSnapshotList() {
|
||||
#ifndef NDEBUG
|
||||
// findEarliestVisibleSnapshot assumes this ordering.
|
||||
for (size_t i = 1; i < snapshots_->size(); ++i) {
|
||||
assert(snapshots_->at(i - 1) < snapshots_->at(i));
|
||||
}
|
||||
#endif
|
||||
if (snapshots_->size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = true;
|
||||
earliest_snapshot_iter_ = snapshots_->end();
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
latest_snapshot_ = 0;
|
||||
} else {
|
||||
visible_at_tip_ = false;
|
||||
earliest_snapshot_iter_ = snapshots_->begin();
|
||||
earliest_snapshot_ = snapshots_->at(0);
|
||||
latest_snapshot_ = snapshots_->back();
|
||||
}
|
||||
released_snapshots_.clear();
|
||||
}
|
||||
|
||||
void CompactionIterator::NextFromInput() {
|
||||
at_next_ = false;
|
||||
valid_ = false;
|
||||
@@ -241,6 +256,7 @@ void CompactionIterator::NextFromInput() {
|
||||
valid_ = true;
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator:ProcessKV", &ikey_);
|
||||
|
||||
// Update input statistics
|
||||
if (ikey_.type == kTypeDeletion || ikey_.type == kTypeSingleDeletion) {
|
||||
@@ -261,6 +277,13 @@ void CompactionIterator::NextFromInput() {
|
||||
// compaction filter). ikey_.user_key is pointing to the copy.
|
||||
if (!has_current_user_key_ ||
|
||||
!cmp_->Equal(ikey_.user_key, current_user_key_)) {
|
||||
num_keys_++;
|
||||
// Use num_keys_ to reduce the overhead of reading current time
|
||||
if (snap_list_callback_ && snapshots_->size() &&
|
||||
snap_list_callback_->TimeToRefresh(num_keys_)) {
|
||||
snap_list_callback_->Refresh(snapshots_, latest_snapshot_);
|
||||
ProcessSnapshotList();
|
||||
}
|
||||
// First occurrence of this user key
|
||||
// Copy key for output
|
||||
key_ = current_key_.SetInternalKey(key_, &ikey_);
|
||||
@@ -269,9 +292,7 @@ void CompactionIterator::NextFromInput() {
|
||||
has_outputted_key_ = false;
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
current_key_committed_ =
|
||||
(snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(ikey_.sequence, kMaxSequenceNumber));
|
||||
current_key_committed_ = KeyCommitted(ikey_.sequence);
|
||||
|
||||
// Apply the compaction filter to the first committed version of the user
|
||||
// key.
|
||||
@@ -293,8 +314,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// to query snapshot_checker_ in that case.
|
||||
if (UNLIKELY(!current_key_committed_)) {
|
||||
assert(snapshot_checker_ != nullptr);
|
||||
current_key_committed_ =
|
||||
snapshot_checker_->IsInSnapshot(ikey_.sequence, kMaxSequenceNumber);
|
||||
current_key_committed_ = KeyCommitted(ikey_.sequence);
|
||||
// Apply the compaction filter to the first committed version of the
|
||||
// user key.
|
||||
if (current_key_committed_) {
|
||||
@@ -378,10 +398,8 @@ void CompactionIterator::NextFromInput() {
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key)) {
|
||||
// Check whether the next key belongs to the same snapshot as the
|
||||
// SingleDelete.
|
||||
if (prev_snapshot == 0 || next_ikey.sequence > prev_snapshot ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(next_ikey.sequence,
|
||||
prev_snapshot)))) {
|
||||
if (prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot)) {
|
||||
if (next_ikey.type == kTypeSingleDeletion) {
|
||||
// We encountered two SingleDeletes in a row. This could be due to
|
||||
// unexpected user input.
|
||||
@@ -393,11 +411,8 @@ void CompactionIterator::NextFromInput() {
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
} else if (has_outputted_key_ ||
|
||||
(ikey_.sequence <= earliest_write_conflict_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(
|
||||
ikey_.sequence,
|
||||
earliest_write_conflict_snapshot_))))) {
|
||||
DEFINITELY_IN_SNAPSHOT(
|
||||
ikey_.sequence, earliest_write_conflict_snapshot_)) {
|
||||
// Found a matching value, we can drop the single delete and the
|
||||
// value. It is safe to drop both records since we've already
|
||||
// outputted a key in this snapshot, or there is no earlier
|
||||
@@ -407,7 +422,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// is an unexpected Merge or Delete. We will compact it out
|
||||
// either way. We will maintain counts of how many mismatches
|
||||
// happened
|
||||
if (next_ikey.type != kTypeValue) {
|
||||
if (next_ikey.type != kTypeValue &&
|
||||
next_ikey.type != kTypeBlobIndex) {
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
}
|
||||
|
||||
@@ -444,10 +460,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// iteration. If the next key is corrupt, we return before the
|
||||
// comparison, so the value of has_current_user_key does not matter.
|
||||
has_current_user_key_ = false;
|
||||
if (compaction_ != nullptr && ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) &&
|
||||
if (compaction_ != nullptr && IN_EARLIEST_SNAPSHOT(ikey_.sequence) &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
// Key doesn't exist outside of this range.
|
||||
@@ -466,24 +479,23 @@ void CompactionIterator::NextFromInput() {
|
||||
if (valid_) {
|
||||
at_next_ = true;
|
||||
}
|
||||
} else if (last_snapshot == current_user_key_snapshot_) {
|
||||
} else if (last_snapshot == current_user_key_snapshot_ ||
|
||||
(last_snapshot > 0 &&
|
||||
last_snapshot < current_user_key_snapshot_)) {
|
||||
// If the earliest snapshot is which this key is visible in
|
||||
// is the same as the visibility of a previous instance of the
|
||||
// same key, then this kv is not visible in any snapshot.
|
||||
// Hidden by an newer entry for same user key
|
||||
// TODO(noetzli): why not > ?
|
||||
//
|
||||
// Note: Dropping this key will not affect TransactionDB write-conflict
|
||||
// checking since there has already been a record returned for this key
|
||||
// in this snapshot.
|
||||
assert(last_sequence >= current_user_key_sequence_);
|
||||
|
||||
++iter_stats_.num_record_drop_hidden; // (A)
|
||||
input_->Next();
|
||||
} else if (compaction_ != nullptr && ikey_.type == kTypeDeletion &&
|
||||
ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) &&
|
||||
IN_EARLIEST_SNAPSHOT(ikey_.sequence) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
@@ -520,13 +532,10 @@ void CompactionIterator::NextFromInput() {
|
||||
input_->Next();
|
||||
// Skip over all versions of this key that happen to occur in the same snapshot
|
||||
// range as the delete
|
||||
while (input_->Valid() &&
|
||||
ParseInternalKey(input_->key(), &next_ikey) &&
|
||||
while (input_->Valid() && ParseInternalKey(input_->key(), &next_ikey) &&
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key) &&
|
||||
(prev_snapshot == 0 || next_ikey.sequence > prev_snapshot ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(next_ikey.sequence,
|
||||
prev_snapshot))))) {
|
||||
(prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot))) {
|
||||
input_->Next();
|
||||
}
|
||||
// If you find you still need to output a row with this key, we need to output the
|
||||
@@ -617,14 +626,9 @@ void CompactionIterator::PrepareOutput() {
|
||||
//
|
||||
// Can we do the same for levels above bottom level as long as
|
||||
// KeyNotExistsBeyondOutputLevel() return true?
|
||||
if ((compaction_ != nullptr &&
|
||||
!compaction_->allow_ingest_behind()) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
bottommost_level_ && valid_ && ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr || LIKELY(snapshot_checker_->IsInSnapshot(
|
||||
ikey_.sequence, earliest_snapshot_))) &&
|
||||
ikey_.type != kTypeMerge &&
|
||||
!cmp_->Equal(compaction_->GetLargestUserKey(), ikey_.user_key)) {
|
||||
if ((compaction_ != nullptr && !compaction_->allow_ingest_behind()) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() && bottommost_level_ && valid_ &&
|
||||
IN_EARLIEST_SNAPSHOT(ikey_.sequence) && ikey_.type != kTypeMerge) {
|
||||
assert(ikey_.type != kTypeDeletion && ikey_.type != kTypeSingleDeletion);
|
||||
ikey_.sequence = 0;
|
||||
current_key_.UpdateInternalKey(0, ikey_.type);
|
||||
@@ -642,12 +646,23 @@ inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
*prev_snapshot = *std::prev(snapshots_iter);
|
||||
assert(*prev_snapshot < in);
|
||||
}
|
||||
if (snapshot_checker_ == nullptr) {
|
||||
return snapshots_iter != snapshots_->end()
|
||||
? *snapshots_iter : kMaxSequenceNumber;
|
||||
}
|
||||
bool has_released_snapshot = !released_snapshots_.empty();
|
||||
for (; snapshots_iter != snapshots_->end(); ++snapshots_iter) {
|
||||
auto cur = *snapshots_iter;
|
||||
assert(in <= cur);
|
||||
if (snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(in, cur)) {
|
||||
// Skip if cur is in released_snapshots.
|
||||
if (has_released_snapshot && released_snapshots_.count(cur) > 0) {
|
||||
continue;
|
||||
}
|
||||
auto res = snapshot_checker_->CheckInSnapshot(in, cur);
|
||||
if (res == SnapshotCheckerResult::kInSnapshot) {
|
||||
return cur;
|
||||
} else if (res == SnapshotCheckerResult::kSnapshotReleased) {
|
||||
released_snapshots_.insert(cur);
|
||||
}
|
||||
*prev_snapshot = cur;
|
||||
}
|
||||
@@ -661,4 +676,30 @@ inline bool CompactionIterator::ikeyNotNeededForIncrementalSnapshot() {
|
||||
(ikey_.sequence < preserve_deletes_seqnum_);
|
||||
}
|
||||
|
||||
bool CompactionIterator::IsInEarliestSnapshot(SequenceNumber sequence) {
|
||||
assert(snapshot_checker_ != nullptr);
|
||||
assert(earliest_snapshot_ == kMaxSequenceNumber ||
|
||||
(earliest_snapshot_iter_ != snapshots_->end() &&
|
||||
*earliest_snapshot_iter_ == earliest_snapshot_));
|
||||
auto in_snapshot =
|
||||
snapshot_checker_->CheckInSnapshot(sequence, earliest_snapshot_);
|
||||
while (UNLIKELY(in_snapshot == SnapshotCheckerResult::kSnapshotReleased)) {
|
||||
// Avoid the the current earliest_snapshot_ being return as
|
||||
// earliest visible snapshot for the next value. So if a value's sequence
|
||||
// is zero-ed out by PrepareOutput(), the next value will be compact out.
|
||||
released_snapshots_.insert(earliest_snapshot_);
|
||||
earliest_snapshot_iter_++;
|
||||
|
||||
if (earliest_snapshot_iter_ == snapshots_->end()) {
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
} else {
|
||||
earliest_snapshot_ = *earliest_snapshot_iter_;
|
||||
}
|
||||
in_snapshot =
|
||||
snapshot_checker_->CheckInSnapshot(sequence, earliest_snapshot_);
|
||||
}
|
||||
assert(in_snapshot != SnapshotCheckerResult::kSnapshotReleased);
|
||||
return in_snapshot == SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -7,10 +7,11 @@
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction_iteration_stats.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/compaction/compaction_iteration_stats.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
@@ -20,6 +21,54 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// This callback can be used to refresh the snapshot list from the db. It
|
||||
// includes logics to exponentially decrease the refresh rate to limit the
|
||||
// overhead of refresh.
|
||||
class SnapshotListFetchCallback {
|
||||
public:
|
||||
SnapshotListFetchCallback(Env* env, uint64_t snap_refresh_nanos,
|
||||
size_t every_nth_key = 1024)
|
||||
: timer_(env, /*auto restart*/ true),
|
||||
snap_refresh_nanos_(snap_refresh_nanos),
|
||||
every_nth_key_minus_one_(every_nth_key - 1) {
|
||||
assert(every_nth_key > 0);
|
||||
assert((ceil(log2(every_nth_key)) == floor(log2(every_nth_key))));
|
||||
}
|
||||
// Refresh the snapshot list. snapshots will bre replacted with the new list.
|
||||
// max is the upper bound. Note: this function will acquire the db_mutex_.
|
||||
virtual void Refresh(std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber max) = 0;
|
||||
inline bool TimeToRefresh(const size_t key_index) {
|
||||
assert(snap_refresh_nanos_ != 0);
|
||||
// skip the key if key_index % every_nth_key (which is of power 2) is not 0.
|
||||
if ((key_index & every_nth_key_minus_one_) != 0) {
|
||||
return false;
|
||||
}
|
||||
const uint64_t elapsed = timer_.ElapsedNanos();
|
||||
auto ret = elapsed > snap_refresh_nanos_;
|
||||
// pre-compute the next time threshold
|
||||
if (ret) {
|
||||
// inc next refresh period exponentially (by x4)
|
||||
auto next_refresh_threshold = snap_refresh_nanos_ << 2;
|
||||
// make sure the shift has not overflown the highest 1 bit
|
||||
snap_refresh_nanos_ =
|
||||
std::max(snap_refresh_nanos_, next_refresh_threshold);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
static constexpr SnapshotListFetchCallback* kDisabled = nullptr;
|
||||
|
||||
virtual ~SnapshotListFetchCallback() {}
|
||||
|
||||
private:
|
||||
// Time since the callback was created
|
||||
StopWatchNano timer_;
|
||||
// The delay before calling ::Refresh. To be increased exponentially.
|
||||
uint64_t snap_refresh_nanos_;
|
||||
// Skip evey nth key. Number n if of power 2. The math will require n-1.
|
||||
const uint64_t every_nth_key_minus_one_;
|
||||
};
|
||||
|
||||
class CompactionIterator {
|
||||
public:
|
||||
// A wrapper around Compaction. Has a much smaller interface, only what
|
||||
@@ -68,7 +117,8 @@ class CompactionIterator {
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0);
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
SnapshotListFetchCallback* snap_list_callback = nullptr);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
@@ -81,7 +131,8 @@ class CompactionIterator {
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0);
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
SnapshotListFetchCallback* snap_list_callback = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
@@ -109,6 +160,8 @@ class CompactionIterator {
|
||||
private:
|
||||
// Processes the input stream to find the next output
|
||||
void NextFromInput();
|
||||
// Process snapshots_ and assign related variables
|
||||
void ProcessSnapshotList();
|
||||
|
||||
// Do last preparations before presenting the output to the callee. At this
|
||||
// point this only zeroes out the sequence number if possible for better
|
||||
@@ -132,10 +185,25 @@ class CompactionIterator {
|
||||
// or seqnum be zero-ed out even if all other conditions for it are met.
|
||||
inline bool ikeyNotNeededForIncrementalSnapshot();
|
||||
|
||||
inline bool KeyCommitted(SequenceNumber sequence) {
|
||||
return snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->CheckInSnapshot(sequence, kMaxSequenceNumber) ==
|
||||
SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
bool IsInEarliestSnapshot(SequenceNumber sequence);
|
||||
|
||||
InternalIterator* input_;
|
||||
const Comparator* cmp_;
|
||||
MergeHelper* merge_helper_;
|
||||
const std::vector<SequenceNumber>* snapshots_;
|
||||
std::vector<SequenceNumber>* snapshots_;
|
||||
// List of snapshots released during compaction.
|
||||
// findEarliestVisibleSnapshot() find them out from return of
|
||||
// snapshot_checker, and make sure they will not be returned as
|
||||
// earliest visible snapshot of an older value.
|
||||
// See WritePreparedTransactionTest::ReleaseSnapshotDuringCompaction3.
|
||||
std::unordered_set<SequenceNumber> released_snapshots_;
|
||||
std::vector<SequenceNumber>::const_iterator earliest_snapshot_iter_;
|
||||
const SequenceNumber earliest_write_conflict_snapshot_;
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
Env* env_;
|
||||
@@ -151,7 +219,6 @@ class CompactionIterator {
|
||||
bool visible_at_tip_;
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber latest_snapshot_;
|
||||
bool ignore_snapshots_;
|
||||
|
||||
// State
|
||||
//
|
||||
@@ -204,6 +271,9 @@ class CompactionIterator {
|
||||
// Used to avoid purging uncommitted values. The application can specify
|
||||
// uncommitted values by providing a SnapshotChecker object.
|
||||
bool current_key_committed_;
|
||||
SnapshotListFetchCallback* snap_list_callback_;
|
||||
// number of distinct keys processed
|
||||
size_t num_keys_ = 0;
|
||||
|
||||
bool IsShuttingDown() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
@@ -3,15 +3,15 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "port/port.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -112,39 +112,39 @@ class LoggingForwardVectorIterator : public InternalIterator {
|
||||
assert(keys_.size() == values_.size());
|
||||
}
|
||||
|
||||
virtual bool Valid() const override { return current_ < keys_.size(); }
|
||||
bool Valid() const override { return current_ < keys_.size(); }
|
||||
|
||||
virtual void SeekToFirst() override {
|
||||
void SeekToFirst() override {
|
||||
log.emplace_back(Action::Type::SEEK_TO_FIRST);
|
||||
current_ = 0;
|
||||
}
|
||||
virtual void SeekToLast() override { assert(false); }
|
||||
void SeekToLast() override { assert(false); }
|
||||
|
||||
virtual void Seek(const Slice& target) override {
|
||||
void Seek(const Slice& target) override {
|
||||
log.emplace_back(Action::Type::SEEK, target.ToString());
|
||||
current_ = std::lower_bound(keys_.begin(), keys_.end(), target.ToString()) -
|
||||
keys_.begin();
|
||||
}
|
||||
|
||||
virtual void SeekForPrev(const Slice& /*target*/) override { assert(false); }
|
||||
void SeekForPrev(const Slice& /*target*/) override { assert(false); }
|
||||
|
||||
virtual void Next() override {
|
||||
void Next() override {
|
||||
assert(Valid());
|
||||
log.emplace_back(Action::Type::NEXT);
|
||||
current_++;
|
||||
}
|
||||
virtual void Prev() override { assert(false); }
|
||||
void Prev() override { assert(false); }
|
||||
|
||||
virtual Slice key() const override {
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return Slice(keys_[current_]);
|
||||
}
|
||||
virtual Slice value() const override {
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
return Slice(values_[current_]);
|
||||
}
|
||||
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
Status status() const override { return Status::OK(); }
|
||||
|
||||
std::vector<Action> log;
|
||||
|
||||
@@ -158,22 +158,20 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
public:
|
||||
FakeCompaction() = default;
|
||||
|
||||
virtual int level(size_t /*compaction_input_level*/) const override {
|
||||
return 0;
|
||||
}
|
||||
virtual bool KeyNotExistsBeyondOutputLevel(
|
||||
int level(size_t /*compaction_input_level*/) const override { return 0; }
|
||||
bool KeyNotExistsBeyondOutputLevel(
|
||||
const Slice& /*user_key*/,
|
||||
std::vector<size_t>* /*level_ptrs*/) const override {
|
||||
return is_bottommost_level || key_not_exists_beyond_output_level;
|
||||
}
|
||||
virtual bool bottommost_level() const override { return is_bottommost_level; }
|
||||
virtual int number_levels() const override { return 1; }
|
||||
virtual Slice GetLargestUserKey() const override {
|
||||
bool bottommost_level() const override { return is_bottommost_level; }
|
||||
int number_levels() const override { return 1; }
|
||||
Slice GetLargestUserKey() const override {
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
virtual bool allow_ingest_behind() const override { return false; }
|
||||
bool allow_ingest_behind() const override { return false; }
|
||||
|
||||
virtual bool preserve_deletes() const override { return false; }
|
||||
bool preserve_deletes() const override { return false; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
@@ -190,13 +188,17 @@ class TestSnapshotChecker : public SnapshotChecker {
|
||||
: last_committed_sequence_(last_committed_sequence),
|
||||
snapshots_(snapshots) {}
|
||||
|
||||
bool IsInSnapshot(SequenceNumber seq,
|
||||
SequenceNumber snapshot_seq) const override {
|
||||
SnapshotCheckerResult CheckInSnapshot(
|
||||
SequenceNumber seq, SequenceNumber snapshot_seq) const override {
|
||||
if (snapshot_seq == kMaxSequenceNumber) {
|
||||
return seq <= last_committed_sequence_;
|
||||
return seq <= last_committed_sequence_
|
||||
? SnapshotCheckerResult::kInSnapshot
|
||||
: SnapshotCheckerResult::kNotInSnapshot;
|
||||
}
|
||||
assert(snapshots_.count(snapshot_seq) > 0);
|
||||
return seq <= snapshots_.at(snapshot_seq);
|
||||
return seq <= snapshots_.at(snapshot_seq)
|
||||
? SnapshotCheckerResult::kInSnapshot
|
||||
: SnapshotCheckerResult::kNotInSnapshot;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -373,10 +375,9 @@ TEST_P(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
|
||||
TEST_P(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
// See InitIterators() call below for the sequence of keys and their
|
||||
@@ -556,10 +557,9 @@ TEST_P(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
|
||||
TEST_P(CompactionIteratorTest, SingleMergeOperand) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* /*new_value*/,
|
||||
std::string* /*skip_until*/) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
|
||||
@@ -7,13 +7,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction_job.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
@@ -25,7 +19,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/error_handler.h"
|
||||
@@ -38,6 +33,10 @@
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
@@ -47,21 +46,17 @@
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -97,6 +92,8 @@ const char* GetCompactionReasonString(CompactionReason compaction_reason) {
|
||||
return "Flush";
|
||||
case CompactionReason::kExternalSstIngestion:
|
||||
return "ExternalSstIngestion";
|
||||
case CompactionReason::kPeriodicCompaction:
|
||||
return "PeriodicCompaction";
|
||||
case CompactionReason::kNumOfReasons:
|
||||
// fall through
|
||||
default:
|
||||
@@ -157,7 +154,6 @@ struct CompactionJob::SubcompactionState {
|
||||
uint64_t overlapped_bytes = 0;
|
||||
// A flag determine whether the key has been seen in ShouldStopBefore()
|
||||
bool seen_key = false;
|
||||
std::string compression_dict;
|
||||
|
||||
SubcompactionState(Compaction* c, Slice* _start, Slice* _end,
|
||||
uint64_t size = 0)
|
||||
@@ -173,8 +169,7 @@ struct CompactionJob::SubcompactionState {
|
||||
approx_size(size),
|
||||
grandparent_index(0),
|
||||
overlapped_bytes(0),
|
||||
seen_key(false),
|
||||
compression_dict() {
|
||||
seen_key(false) {
|
||||
assert(compaction != nullptr);
|
||||
}
|
||||
|
||||
@@ -197,7 +192,6 @@ struct CompactionJob::SubcompactionState {
|
||||
grandparent_index = std::move(o.grandparent_index);
|
||||
overlapped_bytes = std::move(o.overlapped_bytes);
|
||||
seen_key = std::move(o.seen_key);
|
||||
compression_dict = std::move(o.compression_dict);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -315,7 +309,8 @@ CompactionJob::CompactionJob(
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, std::shared_ptr<Cache> table_cache,
|
||||
EventLogger* event_logger, bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats)
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, SnapshotListFetchCallback* snap_list_callback)
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_job_stats_(compaction_job_stats),
|
||||
@@ -324,7 +319,7 @@ CompactionJob::CompactionJob(
|
||||
db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
env_(db_options.env),
|
||||
env_optiosn_for_read_(
|
||||
env_options_for_read_(
|
||||
env_->OptimizeForCompactionTableRead(env_options, db_options_)),
|
||||
versions_(versions),
|
||||
shutting_down_(shutting_down),
|
||||
@@ -336,6 +331,7 @@ CompactionJob::CompactionJob(
|
||||
db_mutex_(db_mutex),
|
||||
db_error_handler_(db_error_handler),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
snap_list_callback_(snap_list_callback),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
table_cache_(std::move(table_cache)),
|
||||
@@ -343,7 +339,8 @@ CompactionJob::CompactionJob(
|
||||
bottommost_level_(false),
|
||||
paranoid_file_checks_(paranoid_file_checks),
|
||||
measure_io_stats_(measure_io_stats),
|
||||
write_hint_(Env::WLTH_NOT_SET) {
|
||||
write_hint_(Env::WLTH_NOT_SET),
|
||||
thread_pri_(thread_pri) {
|
||||
assert(log_buffer_ != nullptr);
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
@@ -413,15 +410,13 @@ void CompactionJob::Prepare() {
|
||||
|
||||
write_hint_ =
|
||||
c->column_family_data()->CalculateSSTWriteHint(c->output_level());
|
||||
// Is this compaction producing files at the bottommost level?
|
||||
bottommost_level_ = c->bottommost_level();
|
||||
|
||||
if (c->ShouldFormSubcompactions()) {
|
||||
const uint64_t start_micros = env_->NowMicros();
|
||||
GenSubcompactionBoundaries();
|
||||
MeasureTime(stats_, SUBCOMPACTION_SETUP_TIME,
|
||||
env_->NowMicros() - start_micros);
|
||||
|
||||
{
|
||||
StopWatch sw(env_, stats_, SUBCOMPACTION_SETUP_TIME);
|
||||
GenSubcompactionBoundaries();
|
||||
}
|
||||
assert(sizes_.size() == boundaries_.size() + 1);
|
||||
|
||||
for (size_t i = 0; i <= boundaries_.size(); i++) {
|
||||
@@ -429,8 +424,8 @@ void CompactionJob::Prepare() {
|
||||
Slice* end = i == boundaries_.size() ? nullptr : &boundaries_[i];
|
||||
compact_->sub_compact_states.emplace_back(c, start, end, sizes_[i]);
|
||||
}
|
||||
MeasureTime(stats_, NUM_SUBCOMPACTIONS_SCHEDULED,
|
||||
compact_->sub_compact_states.size());
|
||||
RecordInHistogram(stats_, NUM_SUBCOMPACTIONS_SCHEDULED,
|
||||
compact_->sub_compact_states.size());
|
||||
} else {
|
||||
compact_->sub_compact_states.emplace_back(c, nullptr, nullptr);
|
||||
}
|
||||
@@ -444,11 +439,6 @@ struct RangeWithSize {
|
||||
: range(a, b), size(s) {}
|
||||
};
|
||||
|
||||
// Generates a histogram representing potential divisions of key ranges from
|
||||
// the input. It adds the starting and/or ending keys of certain input files
|
||||
// to the working set and then finds the approximate size of data in between
|
||||
// each consecutive pair of slices. Then it divides these ranges into
|
||||
// consecutive groups such that each group has a similar size.
|
||||
void CompactionJob::GenSubcompactionBoundaries() {
|
||||
auto* c = compact_->compaction;
|
||||
auto* cfd = c->column_family_data();
|
||||
@@ -518,7 +508,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
auto* v = compact_->compaction->input_version();
|
||||
for (auto it = bounds.begin();;) {
|
||||
const Slice a = *it;
|
||||
it++;
|
||||
++it;
|
||||
|
||||
if (it == bounds.end()) {
|
||||
break;
|
||||
@@ -530,7 +520,8 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// to the index block and may incur I/O cost in the process. Unlock db
|
||||
// mutex to reduce contention
|
||||
db_mutex_->Unlock();
|
||||
uint64_t size = versions_->ApproximateSize(v, a, b, start_lvl, out_lvl + 1);
|
||||
uint64_t size = versions_->ApproximateSize(v, a, b, start_lvl, out_lvl + 1,
|
||||
/*for_compaction*/ true);
|
||||
db_mutex_->Lock();
|
||||
ranges.emplace_back(a, b, size);
|
||||
sum += size;
|
||||
@@ -604,7 +595,15 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
|
||||
compaction_stats_.micros = env_->NowMicros() - start_micros;
|
||||
MeasureTime(stats_, COMPACTION_TIME, compaction_stats_.micros);
|
||||
compaction_stats_.cpu_micros = 0;
|
||||
for (size_t i = 0; i < compact_->sub_compact_states.size(); i++) {
|
||||
compaction_stats_.cpu_micros +=
|
||||
compact_->sub_compact_states[i].compaction_job_stats.cpu_micros;
|
||||
}
|
||||
|
||||
RecordTimeToHistogram(stats_, COMPACTION_TIME, compaction_stats_.micros);
|
||||
RecordTimeToHistogram(stats_, COMPACTION_CPU_TIME,
|
||||
compaction_stats_.cpu_micros);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run:BeforeVerify");
|
||||
|
||||
@@ -713,7 +712,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
Status status = compact_->status;
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
cfd->internal_stats()->AddCompactionStats(
|
||||
compact_->compaction->output_level(), compaction_stats_);
|
||||
compact_->compaction->output_level(), thread_pri_, compaction_stats_);
|
||||
|
||||
if (status.ok()) {
|
||||
status = InstallCompactionResults(mutable_cf_options);
|
||||
@@ -767,6 +766,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
stream << "job" << job_id_ << "event"
|
||||
<< "compaction_finished"
|
||||
<< "compaction_time_micros" << compaction_stats_.micros
|
||||
<< "compaction_time_cpu_micros" << compaction_stats_.cpu_micros
|
||||
<< "output_level" << compact_->compaction->output_level()
|
||||
<< "num_output_files" << compact_->NumOutputFiles()
|
||||
<< "total_output_size" << compact_->total_bytes << "num_input_records"
|
||||
@@ -804,14 +804,35 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
|
||||
void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact != nullptr);
|
||||
|
||||
uint64_t prev_cpu_micros = env_->NowCPUNanos() / 1000;
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
|
||||
// Create compaction filter and fail the compaction if
|
||||
// IgnoreSnapshots() = false because it is not supported anymore
|
||||
const CompactionFilter* compaction_filter =
|
||||
cfd->ioptions()->compaction_filter;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_from_factory = nullptr;
|
||||
if (compaction_filter == nullptr) {
|
||||
compaction_filter_from_factory =
|
||||
sub_compact->compaction->CreateCompactionFilter();
|
||||
compaction_filter = compaction_filter_from_factory.get();
|
||||
}
|
||||
if (compaction_filter != nullptr && !compaction_filter->IgnoreSnapshots()) {
|
||||
sub_compact->status = Status::NotSupported(
|
||||
"CompactionFilter::IgnoreSnapshots() = false is not supported "
|
||||
"anymore.");
|
||||
return;
|
||||
}
|
||||
|
||||
CompactionRangeDelAggregator range_del_agg(&cfd->internal_comparator(),
|
||||
existing_snapshots_);
|
||||
|
||||
// Although the v2 aggregator is what the level iterator(s) know about,
|
||||
// the AddTombstones calls will be propagated down to the v1 aggregator.
|
||||
std::unique_ptr<InternalIterator> input(versions_->MakeInputIterator(
|
||||
sub_compact->compaction, &range_del_agg, env_optiosn_for_read_));
|
||||
sub_compact->compaction, &range_del_agg, env_options_for_read_));
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -823,58 +844,19 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
uint64_t prev_fsync_nanos = 0;
|
||||
uint64_t prev_range_sync_nanos = 0;
|
||||
uint64_t prev_prepare_write_nanos = 0;
|
||||
uint64_t prev_cpu_write_nanos = 0;
|
||||
uint64_t prev_cpu_read_nanos = 0;
|
||||
if (measure_io_stats_) {
|
||||
prev_perf_level = GetPerfLevel();
|
||||
SetPerfLevel(PerfLevel::kEnableTime);
|
||||
SetPerfLevel(PerfLevel::kEnableTimeAndCPUTimeExceptForMutex);
|
||||
prev_write_nanos = IOSTATS(write_nanos);
|
||||
prev_fsync_nanos = IOSTATS(fsync_nanos);
|
||||
prev_range_sync_nanos = IOSTATS(range_sync_nanos);
|
||||
prev_prepare_write_nanos = IOSTATS(prepare_write_nanos);
|
||||
prev_cpu_write_nanos = IOSTATS(cpu_write_nanos);
|
||||
prev_cpu_read_nanos = IOSTATS(cpu_read_nanos);
|
||||
}
|
||||
|
||||
const MutableCFOptions* mutable_cf_options =
|
||||
sub_compact->compaction->mutable_cf_options();
|
||||
|
||||
// To build compression dictionary, we sample the first output file, assuming
|
||||
// it'll reach the maximum length. We optionally pass these samples through
|
||||
// zstd's dictionary trainer, or just use them directly. Then, the dictionary
|
||||
// is used for compressing subsequent output files in the same subcompaction.
|
||||
const bool kUseZstdTrainer =
|
||||
sub_compact->compaction->output_compression_opts().zstd_max_train_bytes >
|
||||
0;
|
||||
const size_t kSampleBytes =
|
||||
kUseZstdTrainer
|
||||
? sub_compact->compaction->output_compression_opts()
|
||||
.zstd_max_train_bytes
|
||||
: sub_compact->compaction->output_compression_opts().max_dict_bytes;
|
||||
const int kSampleLenShift = 6; // 2^6 = 64-byte samples
|
||||
std::set<size_t> sample_begin_offsets;
|
||||
if (bottommost_level_ && kSampleBytes > 0) {
|
||||
const size_t kMaxSamples = kSampleBytes >> kSampleLenShift;
|
||||
const size_t kOutFileLen =
|
||||
static_cast<size_t>(MaxFileSizeForLevel(*mutable_cf_options,
|
||||
compact_->compaction->output_level(),
|
||||
cfd->ioptions()->compaction_style,
|
||||
compact_->compaction->GetInputBaseLevel(),
|
||||
cfd->ioptions()->level_compaction_dynamic_level_bytes));
|
||||
if (kOutFileLen != port::kMaxSizet) {
|
||||
const size_t kOutFileNumSamples = kOutFileLen >> kSampleLenShift;
|
||||
Random64 generator{versions_->NewFileNumber()};
|
||||
for (size_t i = 0; i < kMaxSamples; ++i) {
|
||||
sample_begin_offsets.insert(
|
||||
static_cast<size_t>(generator.Uniform(kOutFileNumSamples))
|
||||
<< kSampleLenShift);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto compaction_filter = cfd->ioptions()->compaction_filter;
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_from_factory = nullptr;
|
||||
if (compaction_filter == nullptr) {
|
||||
compaction_filter_from_factory =
|
||||
sub_compact->compaction->CreateCompactionFilter();
|
||||
compaction_filter = compaction_filter_from_factory.get();
|
||||
}
|
||||
MergeHelper merge(
|
||||
env_, cfd->user_comparator(), cfd->ioptions()->merge_operator,
|
||||
compaction_filter, db_options_.info_log.get(),
|
||||
@@ -901,7 +883,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_), false,
|
||||
&range_del_agg, sub_compact->compaction, compaction_filter,
|
||||
shutting_down_, preserve_deletes_seqnum_));
|
||||
shutting_down_, preserve_deletes_seqnum_,
|
||||
// Currently range_del_agg is incompatible with snapshot refresh feature.
|
||||
range_del_agg.IsEmpty() ? snap_list_callback_ : nullptr));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
@@ -912,12 +896,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
sub_compact->current_output_file_size);
|
||||
}
|
||||
const auto& c_iter_stats = c_iter->iter_stats();
|
||||
auto sample_begin_offset_iter = sample_begin_offsets.cbegin();
|
||||
// data_begin_offset and dict_sample_data are only valid while generating
|
||||
// dictionary from the first output file.
|
||||
size_t data_begin_offset = 0;
|
||||
std::string dict_sample_data;
|
||||
dict_sample_data.reserve(kSampleBytes);
|
||||
|
||||
while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
|
||||
// Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
|
||||
@@ -953,55 +931,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
key, c_iter->ikey().sequence);
|
||||
sub_compact->num_output_records++;
|
||||
|
||||
if (sub_compact->outputs.size() == 1) { // first output file
|
||||
// Check if this key/value overlaps any sample intervals; if so, appends
|
||||
// overlapping portions to the dictionary.
|
||||
for (const auto& data_elmt : {key, value}) {
|
||||
size_t data_end_offset = data_begin_offset + data_elmt.size();
|
||||
while (sample_begin_offset_iter != sample_begin_offsets.cend() &&
|
||||
*sample_begin_offset_iter < data_end_offset) {
|
||||
size_t sample_end_offset =
|
||||
*sample_begin_offset_iter + (1 << kSampleLenShift);
|
||||
// Invariant: Because we advance sample iterator while processing the
|
||||
// data_elmt containing the sample's last byte, the current sample
|
||||
// cannot end before the current data_elmt.
|
||||
assert(data_begin_offset < sample_end_offset);
|
||||
|
||||
size_t data_elmt_copy_offset, data_elmt_copy_len;
|
||||
if (*sample_begin_offset_iter <= data_begin_offset) {
|
||||
// The sample starts before data_elmt starts, so take bytes starting
|
||||
// at the beginning of data_elmt.
|
||||
data_elmt_copy_offset = 0;
|
||||
} else {
|
||||
// data_elmt starts before the sample starts, so take bytes starting
|
||||
// at the below offset into data_elmt.
|
||||
data_elmt_copy_offset =
|
||||
*sample_begin_offset_iter - data_begin_offset;
|
||||
}
|
||||
if (sample_end_offset <= data_end_offset) {
|
||||
// The sample ends before data_elmt ends, so take as many bytes as
|
||||
// needed.
|
||||
data_elmt_copy_len =
|
||||
sample_end_offset - (data_begin_offset + data_elmt_copy_offset);
|
||||
} else {
|
||||
// data_elmt ends before the sample ends, so take all remaining
|
||||
// bytes in data_elmt.
|
||||
data_elmt_copy_len =
|
||||
data_end_offset - (data_begin_offset + data_elmt_copy_offset);
|
||||
}
|
||||
dict_sample_data.append(&data_elmt.data()[data_elmt_copy_offset],
|
||||
data_elmt_copy_len);
|
||||
if (sample_end_offset > data_end_offset) {
|
||||
// Didn't finish sample. Try to finish it with the next data_elmt.
|
||||
break;
|
||||
}
|
||||
// Next sample may require bytes from same data_elmt.
|
||||
sample_begin_offset_iter++;
|
||||
}
|
||||
data_begin_offset = data_end_offset;
|
||||
}
|
||||
}
|
||||
|
||||
// Close output file if it is big enough. Two possibilities determine it's
|
||||
// time to close it: (1) the current key should be this file's last key, (2)
|
||||
// the next key should not be in this file.
|
||||
@@ -1043,18 +972,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
&range_del_out_stats, next_key);
|
||||
RecordDroppedKeys(range_del_out_stats,
|
||||
&sub_compact->compaction_job_stats);
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
// Use samples from first output file to create dictionary for
|
||||
// compression of subsequent files.
|
||||
if (kUseZstdTrainer) {
|
||||
sub_compact->compression_dict = ZSTD_TrainDictionary(
|
||||
dict_sample_data, kSampleLenShift,
|
||||
sub_compact->compaction->output_compression_opts()
|
||||
.max_dict_bytes);
|
||||
} else {
|
||||
sub_compact->compression_dict = std::move(dict_sample_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1077,10 +994,13 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
|
||||
RecordCompactionIOStats();
|
||||
|
||||
if (status.ok() &&
|
||||
(shutting_down_->load(std::memory_order_relaxed) || cfd->IsDropped())) {
|
||||
status = Status::ShutdownInProgress(
|
||||
"Database shutdown or Column family drop during compaction");
|
||||
if (status.ok() && cfd->IsDropped()) {
|
||||
status =
|
||||
Status::ColumnFamilyDropped("Column family dropped during compaction");
|
||||
}
|
||||
if ((status.ok() || status.IsColumnFamilyDropped()) &&
|
||||
shutting_down_->load(std::memory_order_relaxed)) {
|
||||
status = Status::ShutdownInProgress("Database shutdown");
|
||||
}
|
||||
if (status.ok()) {
|
||||
status = input->status();
|
||||
@@ -1107,6 +1027,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
RecordDroppedKeys(range_del_out_stats, &sub_compact->compaction_job_stats);
|
||||
}
|
||||
|
||||
sub_compact->compaction_job_stats.cpu_micros =
|
||||
env_->NowCPUNanos() / 1000 - prev_cpu_micros;
|
||||
|
||||
if (measure_io_stats_) {
|
||||
sub_compact->compaction_job_stats.file_write_nanos +=
|
||||
IOSTATS(write_nanos) - prev_write_nanos;
|
||||
@@ -1116,7 +1039,11 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
IOSTATS(range_sync_nanos) - prev_range_sync_nanos;
|
||||
sub_compact->compaction_job_stats.file_prepare_write_nanos +=
|
||||
IOSTATS(prepare_write_nanos) - prev_prepare_write_nanos;
|
||||
if (prev_perf_level != PerfLevel::kEnableTime) {
|
||||
sub_compact->compaction_job_stats.cpu_micros -=
|
||||
(IOSTATS(cpu_write_nanos) - prev_cpu_write_nanos +
|
||||
IOSTATS(cpu_read_nanos) - prev_cpu_read_nanos) /
|
||||
1000;
|
||||
if (prev_perf_level != PerfLevel::kEnableTimeAndCPUTimeExceptForMutex) {
|
||||
SetPerfLevel(prev_perf_level);
|
||||
}
|
||||
}
|
||||
@@ -1189,10 +1116,12 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
Slice lower_bound_guard, upper_bound_guard;
|
||||
std::string smallest_user_key;
|
||||
const Slice *lower_bound, *upper_bound;
|
||||
bool lower_bound_from_sub_compact = false;
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
// For the first output table, include range tombstones before the min key
|
||||
// but after the subcompaction boundary.
|
||||
lower_bound = sub_compact->start;
|
||||
lower_bound_from_sub_compact = true;
|
||||
} else if (meta->smallest.size() > 0) {
|
||||
// For subsequent output tables, only include range tombstones from min
|
||||
// key onwards since the previous file was extended to contain range
|
||||
@@ -1286,11 +1215,24 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// (the max key in the previous table or subcompaction) in order for
|
||||
// files to appear key-space partitioned.
|
||||
//
|
||||
// Choose lowest seqnum so this file's smallest internal key comes
|
||||
// after the previous file's/subcompaction's largest. The fake seqnum
|
||||
// is OK because the read path's file-picking code only considers user
|
||||
// key.
|
||||
smallest_candidate = InternalKey(*lower_bound, 0, kTypeRangeDeletion);
|
||||
// When lower_bound is chosen by a subcompaction, we know that
|
||||
// subcompactions over smaller keys cannot contain any keys at
|
||||
// lower_bound. We also know that smaller subcompactions exist, because
|
||||
// otherwise the subcompaction woud be unbounded on the left. As a
|
||||
// result, we know that no other files on the output level will contain
|
||||
// actual keys at lower_bound (an output file may have a largest key of
|
||||
// lower_bound@kMaxSequenceNumber, but this only indicates a large range
|
||||
// tombstone was truncated). Therefore, it is safe to use the
|
||||
// tombstone's sequence number, to ensure that keys at lower_bound at
|
||||
// lower levels are covered by truncated tombstones.
|
||||
//
|
||||
// If lower_bound was chosen by the smallest data key in the file,
|
||||
// choose lowest seqnum so this file's smallest internal key comes after
|
||||
// the previous file's largest. The fake seqnum is OK because the read
|
||||
// path's file-picking code only considers user key.
|
||||
smallest_candidate = InternalKey(
|
||||
*lower_bound, lower_bound_from_sub_compact ? tombstone.seq_ : 0,
|
||||
kTypeRangeDeletion);
|
||||
}
|
||||
InternalKey largest_candidate = tombstone.SerializeEndKey();
|
||||
if (upper_bound != nullptr &&
|
||||
@@ -1312,9 +1254,23 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
largest_candidate =
|
||||
InternalKey(*upper_bound, kMaxSequenceNumber, kTypeRangeDeletion);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
SequenceNumber smallest_ikey_seqnum = kMaxSequenceNumber;
|
||||
if (meta->smallest.size() > 0) {
|
||||
smallest_ikey_seqnum = GetInternalKeySeqno(meta->smallest.Encode());
|
||||
}
|
||||
#endif
|
||||
meta->UpdateBoundariesForRange(smallest_candidate, largest_candidate,
|
||||
tombstone.seq_,
|
||||
cfd->internal_comparator());
|
||||
|
||||
// The smallest key in a file is used for range tombstone truncation, so
|
||||
// it cannot have a seqnum of 0 (unless the smallest data key in a file
|
||||
// has a seqnum of 0). Otherwise, the truncated tombstone may expose
|
||||
// deleted keys at lower levels.
|
||||
assert(smallest_ikey_seqnum == 0 ||
|
||||
ExtractInternalKeyFooter(meta->smallest.Encode()) !=
|
||||
PackSequenceAndType(0, kTypeRangeDeletion));
|
||||
}
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
}
|
||||
@@ -1392,10 +1348,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
auto sfm =
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
if (sfm && meta != nullptr && meta->fd.GetPathId() == 0) {
|
||||
auto fn =
|
||||
TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
|
||||
meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
sfm->OnAddFile(fn);
|
||||
sfm->OnAddFile(fname);
|
||||
if (sfm->IsMaxAllowedSpaceReached()) {
|
||||
// TODO(ajkr): should we return OK() if max space was reached by the final
|
||||
// compaction output file (similarly to how flush works when full)?
|
||||
@@ -1517,7 +1470,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->compaction->immutable_cf_options()->listeners;
|
||||
sub_compact->outfile.reset(
|
||||
new WritableFileWriter(std::move(writable_file), fname, env_options_,
|
||||
db_options_.statistics.get(), listeners));
|
||||
env_, db_options_.statistics.get(), listeners));
|
||||
|
||||
// If the Column family flag is to only optimize filters for hits,
|
||||
// we can skip creating filters if this is the bottommost_level where
|
||||
@@ -1525,20 +1478,20 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
bool skip_filters =
|
||||
cfd->ioptions()->optimize_filters_for_hits && bottommost_level_;
|
||||
|
||||
uint64_t output_file_creation_time =
|
||||
int64_t temp_current_time = 0;
|
||||
auto get_time_status = env_->GetCurrentTime(&temp_current_time);
|
||||
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
|
||||
if (!get_time_status.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Failed to get current time. Status: %s",
|
||||
get_time_status.ToString().c_str());
|
||||
}
|
||||
uint64_t current_time = static_cast<uint64_t>(temp_current_time);
|
||||
|
||||
uint64_t latest_key_time =
|
||||
sub_compact->compaction->MaxInputFileCreationTime();
|
||||
if (output_file_creation_time == 0) {
|
||||
int64_t _current_time = 0;
|
||||
auto status = db_options_.env->GetCurrentTime(&_current_time);
|
||||
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Failed to get current time to populate creation_time property. "
|
||||
"Status: %s",
|
||||
status.ToString().c_str());
|
||||
}
|
||||
output_file_creation_time = static_cast<uint64_t>(_current_time);
|
||||
if (latest_key_time == 0) {
|
||||
latest_key_time = current_time;
|
||||
}
|
||||
|
||||
sub_compact->builder.reset(NewTableBuilder(
|
||||
@@ -1546,9 +1499,11 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
cfd->internal_comparator(), cfd->int_tbl_prop_collector_factories(),
|
||||
cfd->GetID(), cfd->GetName(), sub_compact->outfile.get(),
|
||||
sub_compact->compaction->output_compression(),
|
||||
0 /*sample_for_compression */,
|
||||
sub_compact->compaction->output_compression_opts(),
|
||||
sub_compact->compaction->output_level(), &sub_compact->compression_dict,
|
||||
skip_filters, output_file_creation_time));
|
||||
sub_compact->compaction->output_level(), skip_filters, latest_key_time,
|
||||
0 /* oldest_key_time */, sub_compact->compaction->max_output_file_size(),
|
||||
current_time));
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction_iterator.h"
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/internal_stats.h"
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "logging/event_logger.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "options/db_options.h"
|
||||
#include "port/port.h"
|
||||
@@ -40,7 +41,6 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
@@ -55,24 +55,27 @@ class Version;
|
||||
class VersionEdit;
|
||||
class VersionSet;
|
||||
|
||||
// CompactionJob is responsible for executing the compaction. Each (manual or
|
||||
// automated) compaction corresponds to a CompactionJob object, and usually
|
||||
// goes through the stages of `Prepare()`->`Run()`->`Install()`. CompactionJob
|
||||
// will divide the compaction into subcompactions and execute them in parallel
|
||||
// if needed.
|
||||
class CompactionJob {
|
||||
public:
|
||||
CompactionJob(int job_id, Compaction* compaction,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory,
|
||||
Statistics* stats, InstrumentedMutex* db_mutex,
|
||||
ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname,
|
||||
CompactionJobStats* compaction_job_stats);
|
||||
CompactionJob(
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, SnapshotListFetchCallback* snap_list_callback);
|
||||
|
||||
~CompactionJob();
|
||||
|
||||
@@ -82,17 +85,28 @@ class CompactionJob {
|
||||
CompactionJob& operator=(const CompactionJob& job) = delete;
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Prepare for the compaction by setting up boundaries for each subcompaction
|
||||
void Prepare();
|
||||
// REQUIRED mutex not held
|
||||
// Launch threads for each subcompaction and wait for them to finish. After
|
||||
// that, verify table is usable and finally do bookkeeping to unify
|
||||
// subcompaction results
|
||||
Status Run();
|
||||
|
||||
// REQUIRED: mutex held
|
||||
// Add compaction input/output to the current version
|
||||
Status Install(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
private:
|
||||
struct SubcompactionState;
|
||||
|
||||
void AggregateStatistics();
|
||||
|
||||
// Generates a histogram representing potential divisions of key ranges from
|
||||
// the input. It adds the starting and/or ending keys of certain input files
|
||||
// to the working set and then finds the approximate size of data in between
|
||||
// each consecutive pair of slices. Then it divides these ranges into
|
||||
// consecutive groups such that each group has a similar size.
|
||||
void GenSubcompactionBoundaries();
|
||||
|
||||
// update the thread status for starting a compaction.
|
||||
@@ -137,7 +151,7 @@ class CompactionJob {
|
||||
|
||||
Env* env_;
|
||||
// env_option optimized for compaction table reads
|
||||
EnvOptions env_optiosn_for_read_;
|
||||
EnvOptions env_options_for_read_;
|
||||
VersionSet* versions_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
@@ -152,6 +166,7 @@ class CompactionJob {
|
||||
// entirely within s1 and s2, then the earlier version of k1 can be safely
|
||||
// deleted because that version is not visible in any snapshot.
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
SnapshotListFetchCallback* snap_list_callback_;
|
||||
|
||||
// This is the earliest snapshot that could be used for write-conflict
|
||||
// checking by a transaction. For any user-key newer than this snapshot, we
|
||||
@@ -164,6 +179,7 @@ class CompactionJob {
|
||||
|
||||
EventLogger* event_logger_;
|
||||
|
||||
// Is this compaction creating a file in the bottom most level?
|
||||
bool bottommost_level_;
|
||||
bool paranoid_file_checks_;
|
||||
bool measure_io_stats_;
|
||||
@@ -172,6 +188,7 @@ class CompactionJob {
|
||||
// Stores the approx size of keys covered in the range of each subcompaction
|
||||
std::vector<uint64_t> sizes_;
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
Env::Priority thread_pri_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -7,11 +7,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
@@ -21,12 +17,14 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/logging.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
@@ -47,20 +45,18 @@
|
||||
#include "rocksdb/thread_status.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "table/plain_table_factory.h"
|
||||
#include "table/plain/plain_table_factory.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
#if !defined(IOS_CROSS_COMPILE)
|
||||
@@ -113,7 +109,7 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
~CompactionJobStatsTest() {
|
||||
~CompactionJobStatsTest() override {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({});
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -426,7 +422,7 @@ class CompactionJobStatsChecker : public EventListener {
|
||||
// Once a compaction completed, this function will verify the returned
|
||||
// CompactionJobInfo with the oldest CompactionJobInfo added earlier
|
||||
// in "expected_stats_" which has not yet being used for verification.
|
||||
virtual void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) {
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
if (verify_next_comp_io_stats_) {
|
||||
ASSERT_GT(ci.stats.file_write_nanos, 0);
|
||||
ASSERT_GT(ci.stats.file_range_sync_nanos, 0);
|
||||
@@ -523,7 +519,7 @@ class CompactionJobDeletionStatsChecker : public CompactionJobStatsChecker {
|
||||
public:
|
||||
// Verifies whether two CompactionJobStats match.
|
||||
void Verify(const CompactionJobStats& current_stats,
|
||||
const CompactionJobStats& stats) {
|
||||
const CompactionJobStats& stats) override {
|
||||
ASSERT_EQ(
|
||||
current_stats.num_input_deletion_records,
|
||||
stats.num_input_deletion_records);
|
||||
@@ -5,13 +5,15 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <cinttypes>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction_job.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/cache.h"
|
||||
@@ -19,10 +21,10 @@
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -75,7 +77,8 @@ class CompactionJobTest : public testing::Test {
|
||||
write_buffer_manager_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_)),
|
||||
&write_controller_,
|
||||
/*block_cache_tracer=*/nullptr)),
|
||||
shutting_down_(false),
|
||||
preserve_deletes_seqnum_(0),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
@@ -172,7 +175,7 @@ class CompactionJobTest : public testing::Test {
|
||||
// This is how the key will look like once it's written in bottommost
|
||||
// file
|
||||
InternalKey bottommost_internal_key(
|
||||
key, (key == "9999") ? sequence_number : 0, kTypeValue);
|
||||
key, 0, kTypeValue);
|
||||
|
||||
if (corrupt_id(k)) {
|
||||
test::CorruptKeyType(&internal_key);
|
||||
@@ -194,6 +197,14 @@ class CompactionJobTest : public testing::Test {
|
||||
}
|
||||
|
||||
void NewDB() {
|
||||
DestroyDB(dbname_, Options());
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
versions_.reset(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_,
|
||||
/*block_cache_tracer=*/nullptr));
|
||||
compaction_job_stats_.Reset();
|
||||
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
new_db.SetNextFile(2);
|
||||
@@ -230,7 +241,10 @@ class CompactionJobTest : public testing::Test {
|
||||
const std::vector<std::vector<FileMetaData*>>& input_files,
|
||||
const stl_wrappers::KVMap& expected_results,
|
||||
const std::vector<SequenceNumber>& snapshots = {},
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber) {
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
|
||||
int output_level = 1, bool verify = true,
|
||||
SnapshotListFetchCallback* snapshot_fetcher =
|
||||
SnapshotListFetchCallback::kDisabled) {
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
|
||||
size_t num_input_files = 0;
|
||||
@@ -247,7 +261,7 @@ class CompactionJobTest : public testing::Test {
|
||||
|
||||
Compaction compaction(cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
compaction_input_files, 1, 1024 * 1024,
|
||||
compaction_input_files, output_level, 1024 * 1024,
|
||||
10 * 1024 * 1024, 0, kNoCompression,
|
||||
cfd->ioptions()->compression_opts, 0, {}, true);
|
||||
compaction.SetInputVersion(cfd->current());
|
||||
@@ -262,7 +276,8 @@ class CompactionJobTest : public testing::Test {
|
||||
&shutting_down_, preserve_deletes_seqnum_, &log_buffer, nullptr,
|
||||
nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, table_cache_,
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_);
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_,
|
||||
Env::Priority::USER, snapshot_fetcher);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
compaction_job.Prepare();
|
||||
@@ -274,15 +289,17 @@ class CompactionJobTest : public testing::Test {
|
||||
ASSERT_OK(compaction_job.Install(*cfd->GetLatestMutableCFOptions()));
|
||||
mutex_.Unlock();
|
||||
|
||||
if (expected_results.size() == 0) {
|
||||
ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
|
||||
ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
|
||||
ASSERT_EQ(compaction_job_stats_.num_output_files, 0U);
|
||||
} else {
|
||||
ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
|
||||
ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
|
||||
ASSERT_EQ(compaction_job_stats_.num_output_files, 1U);
|
||||
mock_table_factory_->AssertLatestFile(expected_results);
|
||||
if (verify) {
|
||||
if (expected_results.size() == 0) {
|
||||
ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
|
||||
ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
|
||||
ASSERT_EQ(compaction_job_stats_.num_output_files, 0U);
|
||||
} else {
|
||||
ASSERT_GE(compaction_job_stats_.elapsed_micros, 0U);
|
||||
ASSERT_EQ(compaction_job_stats_.num_input_files, num_input_files);
|
||||
ASSERT_EQ(compaction_job_stats_.num_output_files, 1U);
|
||||
mock_table_factory_->AssertLatestFile(expected_results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +396,7 @@ TEST_F(CompactionJobTest, SimpleOverwrite) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "val2"},
|
||||
{KeyStr("b", 4U, kTypeValue), "val3"}});
|
||||
{KeyStr("b", 0U, kTypeValue), "val3"}});
|
||||
|
||||
SetLastSequence(4U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -432,7 +449,7 @@ TEST_F(CompactionJobTest, SimpleMerge) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "3,4,5"},
|
||||
{KeyStr("b", 2U, kTypeValue), "1,2"}});
|
||||
{KeyStr("b", 0U, kTypeValue), "1,2"}});
|
||||
|
||||
SetLastSequence(5U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -456,8 +473,7 @@ TEST_F(CompactionJobTest, NonAssocMerge) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), "3,4,5"},
|
||||
{KeyStr("b", 2U, kTypeMerge), "2"},
|
||||
{KeyStr("b", 1U, kTypeMerge), "1"}});
|
||||
{KeyStr("b", 0U, kTypeValue), "1,2"}});
|
||||
|
||||
SetLastSequence(5U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -484,7 +500,7 @@ TEST_F(CompactionJobTest, MergeOperandFilter) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 0U, kTypeValue), test::EncodeInt(8U)},
|
||||
{KeyStr("b", 2U, kTypeMerge), test::EncodeInt(2U)}});
|
||||
{KeyStr("b", 0U, kTypeValue), test::EncodeInt(2U)}});
|
||||
|
||||
SetLastSequence(5U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
@@ -747,7 +763,7 @@ TEST_F(CompactionJobTest, SingleDeleteZeroSeq) {
|
||||
AddMockFile(file2);
|
||||
|
||||
auto expected_results = mock::MakeMockFile({
|
||||
{KeyStr("dummy", 5U, kTypeValue), "val2"},
|
||||
{KeyStr("dummy", 0U, kTypeValue), "val2"},
|
||||
});
|
||||
|
||||
SetLastSequence(22U);
|
||||
@@ -931,13 +947,112 @@ TEST_F(CompactionJobTest, CorruptionAfterDeletion) {
|
||||
mock::MakeMockFile({{test::KeyStr("A", 0U, kTypeValue), "val3"},
|
||||
{test::KeyStr("a", 0U, kTypeValue, true), "val"},
|
||||
{test::KeyStr("b", 0U, kTypeValue, true), "val"},
|
||||
{test::KeyStr("c", 1U, kTypeValue), "val2"}});
|
||||
{test::KeyStr("c", 0U, kTypeValue), "val2"}});
|
||||
|
||||
SetLastSequence(6U);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
// Test the snapshot fetcher in compaction
|
||||
TEST_F(CompactionJobTest, SnapshotRefresh) {
|
||||
uint64_t time_seed = env_->NowMicros();
|
||||
printf("time_seed is %" PRIu64 "\n", time_seed); // would help to reproduce
|
||||
Random64 rand(time_seed);
|
||||
std::vector<SequenceNumber> db_snapshots;
|
||||
class SnapshotListFetchCallbackTest : public SnapshotListFetchCallback {
|
||||
public:
|
||||
SnapshotListFetchCallbackTest(Env* env, Random64& rand,
|
||||
std::vector<SequenceNumber>* snapshots)
|
||||
: SnapshotListFetchCallback(env, 1 /*short time delay*/,
|
||||
1 /*fetch after each key*/),
|
||||
rand_(rand),
|
||||
snapshots_(snapshots) {}
|
||||
virtual void Refresh(std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber) override {
|
||||
assert(snapshots->size());
|
||||
assert(snapshots_->size());
|
||||
assert(snapshots_->size() == snapshots->size());
|
||||
if (rand_.OneIn(2)) {
|
||||
uint64_t release_index = rand_.Uniform(snapshots_->size());
|
||||
snapshots_->erase(snapshots_->begin() + release_index);
|
||||
*snapshots = *snapshots_;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Random64 rand_;
|
||||
std::vector<SequenceNumber>* snapshots_;
|
||||
} snapshot_fetcher(env_, rand, &db_snapshots);
|
||||
|
||||
std::vector<std::pair<const std::string, std::string>> file1_kvs, file2_kvs;
|
||||
std::array<ValueType, 4> types = {kTypeValue, kTypeDeletion,
|
||||
kTypeSingleDeletion};
|
||||
SequenceNumber last_seq = 0;
|
||||
for (int i = 1; i < 100; i++) {
|
||||
SequenceNumber seq = last_seq + 1;
|
||||
last_seq = seq;
|
||||
if (rand.OneIn(2)) {
|
||||
auto type = types[rand.Uniform(types.size())];
|
||||
file1_kvs.push_back(
|
||||
{test::KeyStr("k" + ToString(i), seq, type), "v" + ToString(i)});
|
||||
}
|
||||
}
|
||||
auto file1 = mock::MakeMockFile(file1_kvs);
|
||||
for (int i = 1; i < 100; i++) {
|
||||
SequenceNumber seq = last_seq + 1;
|
||||
last_seq++;
|
||||
if (rand.OneIn(2)) {
|
||||
auto type = types[rand.Uniform(types.size())];
|
||||
file2_kvs.push_back(
|
||||
{test::KeyStr("k" + ToString(i), seq, type), "v" + ToString(i)});
|
||||
}
|
||||
}
|
||||
auto file2 = mock::MakeMockFile(file2_kvs);
|
||||
for (SequenceNumber i = 1; i < last_seq + 1; i++) {
|
||||
if (rand.OneIn(5)) {
|
||||
db_snapshots.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
const bool kVerify = true;
|
||||
const int output_level_0 = 0;
|
||||
NewDB();
|
||||
AddMockFile(file1);
|
||||
AddMockFile(file2);
|
||||
SetLastSequence(last_seq);
|
||||
auto files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
// put the output on L0 since it is easier to feed them again to the 2nd
|
||||
// compaction
|
||||
RunCompaction({files}, file1, db_snapshots, kMaxSequenceNumber,
|
||||
output_level_0, !kVerify, &snapshot_fetcher);
|
||||
|
||||
// Now db_snapshots are changed. Run the compaction again without snapshot
|
||||
// fetcher but with the updated snapshot list.
|
||||
compaction_job_stats_.Reset();
|
||||
files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, file1, db_snapshots, kMaxSequenceNumber,
|
||||
output_level_0 + 1, !kVerify);
|
||||
// The result should be what we get if we run compaction without snapshot
|
||||
// fetcher on the updated list of snapshots
|
||||
auto expected = mock_table_factory_->output();
|
||||
|
||||
NewDB();
|
||||
AddMockFile(file1);
|
||||
AddMockFile(file2);
|
||||
SetLastSequence(last_seq);
|
||||
files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, expected, db_snapshots, kMaxSequenceNumber,
|
||||
output_level_0, !kVerify);
|
||||
// The 2nd compaction above would get rid of useless delete markers. To get
|
||||
// the output here exactly as what we got above after two compactions, we also
|
||||
// run the compaction for 2nd time.
|
||||
compaction_job_stats_.Reset();
|
||||
files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, expected, db_snapshots, kMaxSequenceNumber,
|
||||
output_level_0 + 1, !kVerify);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -7,25 +7,21 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -42,19 +38,23 @@ uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
|
||||
bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
size_t min_files_to_compact,
|
||||
uint64_t max_compact_bytes_per_del_file,
|
||||
uint64_t max_compaction_bytes,
|
||||
CompactionInputFiles* comp_inputs) {
|
||||
size_t compact_bytes = static_cast<size_t>(level_files[0]->fd.file_size);
|
||||
uint64_t compensated_compact_bytes = level_files[0]->compensated_file_size;
|
||||
size_t compact_bytes_per_del_file = port::kMaxSizet;
|
||||
// compaction range will be [0, span_len).
|
||||
// Compaction range will be [0, span_len).
|
||||
size_t span_len;
|
||||
// pull in files until the amount of compaction work per deleted file begins
|
||||
// increasing.
|
||||
// Pull in files until the amount of compaction work per deleted file begins
|
||||
// increasing or maximum total compaction size is reached.
|
||||
size_t new_compact_bytes_per_del_file = 0;
|
||||
for (span_len = 1; span_len < level_files.size(); ++span_len) {
|
||||
compact_bytes += static_cast<size_t>(level_files[span_len]->fd.file_size);
|
||||
compensated_compact_bytes += level_files[span_len]->compensated_file_size;
|
||||
new_compact_bytes_per_del_file = compact_bytes / span_len;
|
||||
if (level_files[span_len]->being_compacted ||
|
||||
new_compact_bytes_per_del_file > compact_bytes_per_del_file) {
|
||||
new_compact_bytes_per_del_file > compact_bytes_per_del_file ||
|
||||
compensated_compact_bytes > max_compaction_bytes) {
|
||||
break;
|
||||
}
|
||||
compact_bytes_per_del_file = new_compact_bytes_per_del_file;
|
||||
@@ -543,9 +543,9 @@ void CompactionPicker::GetGrandparents(
|
||||
Compaction* CompactionPicker::CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict) {
|
||||
const CompactRangeOptions& compact_range_options, const InternalKey* begin,
|
||||
const InternalKey* end, InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore) {
|
||||
// CompactionPickerFIFO has its own implementation of compact range
|
||||
assert(ioptions_.compaction_style != kCompactionStyleFIFO);
|
||||
|
||||
@@ -609,11 +609,13 @@ Compaction* CompactionPicker::CompactRange(
|
||||
output_level,
|
||||
MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
ioptions_.compaction_style),
|
||||
/* max_compaction_bytes */ LLONG_MAX, output_path_id,
|
||||
/* max_compaction_bytes */ LLONG_MAX,
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
max_subcompactions, /* grandparents */ {}, /* is manual */ true);
|
||||
compact_range_options.max_subcompactions, /* grandparents */ {},
|
||||
/* is manual */ true);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
@@ -658,7 +660,43 @@ Compaction* CompactionPicker::CompactRange(
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(output_path_id < static_cast<uint32_t>(ioptions_.cf_paths.size()));
|
||||
assert(compact_range_options.target_path_id <
|
||||
static_cast<uint32_t>(ioptions_.cf_paths.size()));
|
||||
|
||||
// for BOTTOM LEVEL compaction only, use max_file_num_to_ignore to filter out
|
||||
// files that are created during the current compaction.
|
||||
if (compact_range_options.bottommost_level_compaction ==
|
||||
BottommostLevelCompaction::kForceOptimized &&
|
||||
max_file_num_to_ignore != port::kMaxUint64) {
|
||||
assert(input_level == output_level);
|
||||
// inputs_shrunk holds a continuous subset of input files which were all
|
||||
// created before the current manual compaction
|
||||
std::vector<FileMetaData*> inputs_shrunk;
|
||||
size_t skip_input_index = inputs.size();
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
if (inputs[i]->fd.GetNumber() < max_file_num_to_ignore) {
|
||||
inputs_shrunk.push_back(inputs[i]);
|
||||
} else if (!inputs_shrunk.empty()) {
|
||||
// inputs[i] was created during the current manual compaction and
|
||||
// need to be skipped
|
||||
skip_input_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (inputs_shrunk.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (inputs.size() != inputs_shrunk.size()) {
|
||||
inputs.files.swap(inputs_shrunk);
|
||||
}
|
||||
// set covering_the_whole_range to false if there is any file that need to
|
||||
// be compacted in the range of inputs[skip_input_index+1, inputs.size())
|
||||
for (size_t i = skip_input_index + 1; i < inputs.size(); ++i) {
|
||||
if (inputs[i]->fd.GetNumber() < max_file_num_to_ignore) {
|
||||
covering_the_whole_range = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InternalKey key_storage;
|
||||
InternalKey* next_smallest = &key_storage;
|
||||
@@ -724,11 +762,12 @@ Compaction* CompactionPicker::CompactRange(
|
||||
MaxFileSizeForLevel(mutable_cf_options, output_level,
|
||||
ioptions_.compaction_style, vstorage->base_level(),
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id,
|
||||
mutable_cf_options.max_compaction_bytes,
|
||||
compact_range_options.target_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
GetCompressionOptions(ioptions_, vstorage, output_level),
|
||||
max_subcompactions, std::move(grandparents),
|
||||
compact_range_options.max_subcompactions, std::move(grandparents),
|
||||
/* is manual compaction */ true);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::CompactRange:Return", compaction);
|
||||
@@ -1068,484 +1107,4 @@ bool CompactionPicker::GetOverlappingL0Files(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevelCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
if (!vstorage->ExpiredTtlFiles().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->BottommostFilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i <= vstorage->MaxInputLevel(); i++) {
|
||||
if (vstorage->CompactionScore(i) >= 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A class to build a leveled compaction step-by-step.
|
||||
class LevelCompactionBuilder {
|
||||
public:
|
||||
LevelCompactionBuilder(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionPicker* compaction_picker,
|
||||
LogBuffer* log_buffer,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableCFOptions& ioptions)
|
||||
: cf_name_(cf_name),
|
||||
vstorage_(vstorage),
|
||||
compaction_picker_(compaction_picker),
|
||||
log_buffer_(log_buffer),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
ioptions_(ioptions) {}
|
||||
|
||||
// Pick and return a compaction.
|
||||
Compaction* PickCompaction();
|
||||
|
||||
// Pick the initial files to compact to the next level. (or together
|
||||
// in Intra-L0 compactions)
|
||||
void SetupInitialFiles();
|
||||
|
||||
// If the initial files are from L0 level, pick other L0
|
||||
// files if needed.
|
||||
bool SetupOtherL0FilesIfNeeded();
|
||||
|
||||
// Based on initial files, setup other files need to be compacted
|
||||
// in this compaction, accordingly.
|
||||
bool SetupOtherInputsIfNeeded();
|
||||
|
||||
Compaction* GetCompaction();
|
||||
|
||||
// For the specfied level, pick a file that we want to compact.
|
||||
// Returns false if there is no file to compact.
|
||||
// If it returns true, inputs->files.size() will be exactly one.
|
||||
// If level is 0 and there is already a compaction on that level, this
|
||||
// function will return false.
|
||||
bool PickFileToCompact();
|
||||
|
||||
// For L0->L0, picks the longest span of files that aren't currently
|
||||
// undergoing compaction for which work-per-deleted-file decreases. The span
|
||||
// always starts from the newest L0 file.
|
||||
//
|
||||
// Intra-L0 compaction is independent of all other files, so it can be
|
||||
// performed even when L0->base_level compactions are blocked.
|
||||
//
|
||||
// Returns true if `inputs` is populated with a span of files to be compacted;
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
void PickExpiredTtlFiles();
|
||||
|
||||
const std::string& cf_name_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
CompactionPicker* compaction_picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
int start_level_ = -1;
|
||||
int output_level_ = -1;
|
||||
int parent_index_ = -1;
|
||||
int base_index_ = -1;
|
||||
double start_level_score_ = 0;
|
||||
bool is_manual_ = false;
|
||||
CompactionInputFiles start_level_inputs_;
|
||||
std::vector<CompactionInputFiles> compaction_inputs_;
|
||||
CompactionInputFiles output_level_inputs_;
|
||||
std::vector<FileMetaData*> grandparents_;
|
||||
CompactionReason compaction_reason_ = CompactionReason::kUnknown;
|
||||
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
// Pick a path ID to place a newly generated file, with its level
|
||||
static uint32_t GetPathId(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int level);
|
||||
|
||||
static const int kMinFilesForIntraL0Compaction = 4;
|
||||
};
|
||||
|
||||
void LevelCompactionBuilder::PickExpiredTtlFiles() {
|
||||
if (vstorage_->ExpiredTtlFiles().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
start_level_ = level_file.first;
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
|
||||
if ((start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
(start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->ExpiredTtlFiles()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
// Find the compactions by size on all levels.
|
||||
bool skipped_l0_to_base = false;
|
||||
for (int i = 0; i < compaction_picker_->NumberLevels() - 1; i++) {
|
||||
start_level_score_ = vstorage_->CompactionScore(i);
|
||||
start_level_ = vstorage_->CompactionScoreLevel(i);
|
||||
assert(i == 0 || start_level_score_ <= vstorage_->CompactionScore(i - 1));
|
||||
if (start_level_score_ >= 1) {
|
||||
if (skipped_l0_to_base && start_level_ == vstorage_->base_level()) {
|
||||
// If L0->base_level compaction is pending, don't schedule further
|
||||
// compaction from base level. Otherwise L0->base_level compaction
|
||||
// may starve.
|
||||
continue;
|
||||
}
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
if (PickFileToCompact()) {
|
||||
// found the compaction!
|
||||
if (start_level_ == 0) {
|
||||
// L0 score = `num L0 files` / `level0_file_num_compaction_trigger`
|
||||
compaction_reason_ = CompactionReason::kLevelL0FilesNum;
|
||||
} else {
|
||||
// L1+ score = `Level files size` / `MaxBytesForLevel`
|
||||
compaction_reason_ = CompactionReason::kLevelMaxLevelSize;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// didn't find the compaction, clear the inputs
|
||||
start_level_inputs_.clear();
|
||||
if (start_level_ == 0) {
|
||||
skipped_l0_to_base = true;
|
||||
// L0->base_level may be blocked due to ongoing L0->base_level
|
||||
// compactions. It may also be blocked by an ongoing compaction from
|
||||
// base_level downwards.
|
||||
//
|
||||
// In these cases, to reduce L0 file count and thus reduce likelihood
|
||||
// of write stalls, we can attempt compacting a span of files within
|
||||
// L0.
|
||||
if (PickIntraL0Compaction()) {
|
||||
output_level_ = 0;
|
||||
compaction_reason_ = CompactionReason::kLevelL0FilesNum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find a compaction, check if there are any files marked for
|
||||
// compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
// PickFilesMarkedForCompaction();
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
is_manual_ = true;
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file = vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(start_level_inputs_.empty());
|
||||
PickExpiredTtlFiles();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherL0FilesIfNeeded() {
|
||||
if (start_level_ == 0 && output_level_ != 0) {
|
||||
return compaction_picker_->GetOverlappingL0Files(
|
||||
vstorage_, &start_level_inputs_, output_level_, &parent_index_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
// Setup input files from output level. For output to L0, we only compact
|
||||
// spans of files that do not interact with any pending compactions, so don't
|
||||
// need to consider other levels.
|
||||
if (output_level_ != 0) {
|
||||
output_level_inputs_.level = output_level_;
|
||||
if (!compaction_picker_->SetupOtherInputs(
|
||||
cf_name_, mutable_cf_options_, vstorage_, &start_level_inputs_,
|
||||
&output_level_inputs_, &parent_index_, base_index_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
compaction_inputs_.push_back(start_level_inputs_);
|
||||
if (!output_level_inputs_.empty()) {
|
||||
compaction_inputs_.push_back(output_level_inputs_);
|
||||
}
|
||||
|
||||
// In some edge cases we could pick a compaction that will be compacting
|
||||
// a key range that overlap with another running compaction, and both
|
||||
// of them have the same output level. This could happen if
|
||||
// (1) we are running a non-exclusive manual compaction
|
||||
// (2) AddFile ingest a new file into the LSM tree
|
||||
// We need to disallow this from happening.
|
||||
if (compaction_picker_->FilesRangeOverlapWithCompaction(compaction_inputs_,
|
||||
output_level_)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
return false;
|
||||
}
|
||||
compaction_picker_->GetGrandparents(vstorage_, start_level_inputs_,
|
||||
output_level_inputs_, &grandparents_);
|
||||
} else {
|
||||
compaction_inputs_.push_back(start_level_inputs_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::PickCompaction() {
|
||||
// Pick up the first file to start compaction. It may have been extended
|
||||
// to a clean cut.
|
||||
SetupInitialFiles();
|
||||
if (start_level_inputs_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(start_level_ >= 0 && output_level_ >= 0);
|
||||
|
||||
// If it is a L0 -> base level compaction, we need to set up other L0
|
||||
// files if needed.
|
||||
if (!SetupOtherL0FilesIfNeeded()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Pick files in the output level and expand more files in the start level
|
||||
// if needed.
|
||||
if (!SetupOtherInputsIfNeeded()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Form a compaction object containing the files we picked.
|
||||
Compaction* c = GetCompaction();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("LevelCompactionPicker::PickCompaction:Return", c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
auto c = new Compaction(
|
||||
vstorage_, ioptions_, mutable_cf_options_, std::move(compaction_inputs_),
|
||||
output_level_,
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level_,
|
||||
ioptions_.compaction_style, vstorage_->base_level(),
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level_),
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
compaction_picker_->RegisterCompaction(c);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the optimal path to place a file
|
||||
* Given a level, finds the path where levels up to it will fit in levels
|
||||
* up to and including this path
|
||||
*/
|
||||
uint32_t LevelCompactionBuilder::GetPathId(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, int level) {
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.cf_paths.empty());
|
||||
|
||||
// size remaining in the most recent path
|
||||
uint64_t current_path_size = ioptions.cf_paths[0].target_size;
|
||||
|
||||
uint64_t level_size;
|
||||
int cur_level = 0;
|
||||
|
||||
// max_bytes_for_level_base denotes L1 size.
|
||||
// We estimate L0 size to be the same as L1.
|
||||
level_size = mutable_cf_options.max_bytes_for_level_base;
|
||||
|
||||
// Last path is the fallback
|
||||
while (p < ioptions.cf_paths.size() - 1) {
|
||||
if (level_size <= current_path_size) {
|
||||
if (cur_level == level) {
|
||||
// Does desired level fit in this path?
|
||||
return p;
|
||||
} else {
|
||||
current_path_size -= level_size;
|
||||
if (cur_level > 0) {
|
||||
if (ioptions.level_compaction_dynamic_level_bytes) {
|
||||
// Currently, level_compaction_dynamic_level_bytes is ignored when
|
||||
// multiple db paths are specified. https://github.com/facebook/
|
||||
// rocksdb/blob/master/db/column_family.cc.
|
||||
// Still, adding this check to avoid accidentally using
|
||||
// max_bytes_for_level_multiplier_additional
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier);
|
||||
} else {
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier *
|
||||
mutable_cf_options.MaxBytesMultiplerAdditional(cur_level));
|
||||
}
|
||||
}
|
||||
cur_level++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
current_path_size = ioptions.cf_paths[p].target_size;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
// level 0 files are overlapping. So we cannot pick more
|
||||
// than one concurrent compactions at this level. This
|
||||
// could be made better by looking at key-ranges that are
|
||||
// being compacted at level 0.
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
TEST_SYNC_POINT("LevelCompactionPicker::PickCompactionBySize:0");
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.clear();
|
||||
|
||||
assert(start_level_ >= 0);
|
||||
|
||||
// Pick the largest file in this level that is not already
|
||||
// being compacted
|
||||
const std::vector<int>& file_size =
|
||||
vstorage_->FilesByCompactionPri(start_level_);
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
|
||||
unsigned int cmp_idx;
|
||||
for (cmp_idx = vstorage_->NextCompactionIndex(start_level_);
|
||||
cmp_idx < file_size.size(); cmp_idx++) {
|
||||
int index = file_size[cmp_idx];
|
||||
auto* f = level_files[index];
|
||||
|
||||
// do not pick a file to compact if it is being compacted
|
||||
// from n-1 level.
|
||||
if (f->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
start_level_inputs_.files.push_back(f);
|
||||
start_level_inputs_.level = start_level_;
|
||||
if (!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_)) {
|
||||
// A locked (pending compaction) input-level file was pulled in due to
|
||||
// user-key overlap.
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now that input level is fully expanded, we check whether any output files
|
||||
// are locked due to pending compaction.
|
||||
//
|
||||
// Note we rely on ExpandInputsToCleanCut() to tell us whether any output-
|
||||
// level files are locked, not just the extra ones pulled in for user-key
|
||||
// overlap.
|
||||
InternalKey smallest, largest;
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
CompactionInputFiles output_level_inputs;
|
||||
output_level_inputs.level = output_level_;
|
||||
vstorage_->GetOverlappingInputs(output_level_, &smallest, &largest,
|
||||
&output_level_inputs.files);
|
||||
if (!output_level_inputs.empty() &&
|
||||
!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&output_level_inputs)) {
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
base_index_ = index;
|
||||
break;
|
||||
}
|
||||
|
||||
// store where to start the iteration in the next call to PickCompaction
|
||||
vstorage_->SetNextCompactionIndex(start_level_, cmp_idx);
|
||||
|
||||
return start_level_inputs_.size() > 0;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickIntraL0Compaction() {
|
||||
start_level_inputs_.clear();
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(0 /* level */);
|
||||
if (level_files.size() <
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger + 2) ||
|
||||
level_files[0]->being_compacted) {
|
||||
// If L0 isn't accumulating much files beyond the regular trigger, don't
|
||||
// resort to L0->L0 compaction yet.
|
||||
return false;
|
||||
}
|
||||
return FindIntraL0Compaction(level_files, kMinFilesForIntraL0Compaction,
|
||||
port::kMaxUint64, &start_level_inputs_);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
|
||||
mutable_cf_options, ioptions_);
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/version_set.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -24,11 +24,26 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// The file contains an abstract class CompactionPicker, and its two
|
||||
// sub-classes LevelCompactionPicker and NullCompactionPicker, as
|
||||
// well as some helper functions used by them.
|
||||
|
||||
class LogBuffer;
|
||||
class Compaction;
|
||||
class VersionStorageInfo;
|
||||
struct CompactionInputFiles;
|
||||
|
||||
// An abstract class to pick compactions from an existing LSM-tree.
|
||||
//
|
||||
// Each compaction style inherits the class and implement the
|
||||
// interface to form automatic compactions. If NeedCompaction() is true,
|
||||
// then call PickCompaction() to find what files need to be compacted
|
||||
// and where to put the output files.
|
||||
//
|
||||
// Non-virtual functions CompactRange() and CompactFiles() are used to
|
||||
// pick files to compact based on users' DB::CompactRange() and
|
||||
// DB::CompactFiles() requests, respectively. There is little
|
||||
// compaction style specific logic for them.
|
||||
class CompactionPicker {
|
||||
public:
|
||||
CompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
@@ -58,9 +73,10 @@ class CompactionPicker {
|
||||
virtual Compaction* CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict);
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore);
|
||||
|
||||
// The maximum allowed output level. Default value is NumberLevels() - 1.
|
||||
virtual int MaxOutputLevel() const { return NumberLevels() - 1; }
|
||||
@@ -220,21 +236,9 @@ class CompactionPicker {
|
||||
const InternalKeyComparator* const icmp_;
|
||||
};
|
||||
|
||||
class LevelCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
LevelCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// A dummy compaction that never triggers any automatic
|
||||
// compaction.
|
||||
class NullCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
NullCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
@@ -255,12 +259,12 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
const MutableCFOptions& /*mutable_cf_options*/,
|
||||
VersionStorageInfo* /*vstorage*/,
|
||||
int /*input_level*/, int /*output_level*/,
|
||||
uint32_t /*output_path_id*/,
|
||||
uint32_t /*max_subcompactions*/,
|
||||
const CompactRangeOptions& /*compact_range_options*/,
|
||||
const InternalKey* /*begin*/,
|
||||
const InternalKey* /*end*/,
|
||||
InternalKey** /*compaction_end*/,
|
||||
bool* /*manual_conflict*/) override {
|
||||
bool* /*manual_conflict*/,
|
||||
uint64_t /*max_file_num_to_ignore*/) override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -272,9 +276,26 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
};
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Attempts to find an intra L0 compaction conforming to the given parameters.
|
||||
//
|
||||
// @param level_files Metadata for L0 files.
|
||||
// @param min_files_to_compact Minimum number of files required to
|
||||
// do the compaction.
|
||||
// @param max_compact_bytes_per_del_file Maximum average size in bytes per
|
||||
// file that is going to get deleted by
|
||||
// the compaction.
|
||||
// @param max_compaction_bytes Maximum total size in bytes (in terms
|
||||
// of compensated file size) for files
|
||||
// to be compacted.
|
||||
// @param [out] comp_inputs If a compaction was found, will be
|
||||
// initialized with corresponding input
|
||||
// files. Cannot be nullptr.
|
||||
//
|
||||
// @return true iff compaction was found.
|
||||
bool FindIntraL0Compaction(const std::vector<FileMetaData*>& level_files,
|
||||
size_t min_files_to_compact,
|
||||
uint64_t max_compact_bytes_per_del_file,
|
||||
uint64_t max_compaction_bytes,
|
||||
CompactionInputFiles* comp_inputs);
|
||||
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
@@ -7,18 +7,14 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/column_family.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -41,7 +37,7 @@ bool FIFOCompactionPicker::NeedsCompaction(
|
||||
Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(mutable_cf_options.compaction_options_fifo.ttl > 0);
|
||||
assert(mutable_cf_options.ttl > 0);
|
||||
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
@@ -63,7 +59,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
inputs[0].level = 0;
|
||||
|
||||
// avoid underflow
|
||||
if (current_time > mutable_cf_options.compaction_options_fifo.ttl) {
|
||||
if (current_time > mutable_cf_options.ttl) {
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
if (f->fd.table_reader != nullptr &&
|
||||
@@ -71,8 +67,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
auto creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time == 0 ||
|
||||
creation_time >= (current_time -
|
||||
mutable_cf_options.compaction_options_fifo.ttl)) {
|
||||
creation_time >= (current_time - mutable_cf_options.ttl)) {
|
||||
break;
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
@@ -135,7 +130,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
mutable_cf_options
|
||||
.level0_file_num_compaction_trigger /* min_files_to_compact */
|
||||
,
|
||||
max_compact_bytes_per_del_file, &comp_inputs)) {
|
||||
max_compact_bytes_per_del_file,
|
||||
mutable_cf_options.max_compaction_bytes, &comp_inputs)) {
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, ioptions_, mutable_cf_options, {comp_inputs}, 0,
|
||||
16 * 1024 * 1024 /* output file size limit */,
|
||||
@@ -201,7 +197,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
assert(vstorage->num_levels() == 1);
|
||||
|
||||
Compaction* c = nullptr;
|
||||
if (mutable_cf_options.compaction_options_fifo.ttl > 0) {
|
||||
if (mutable_cf_options.ttl > 0) {
|
||||
c = PickTTLCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
if (c == nullptr) {
|
||||
@@ -214,9 +210,10 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
Compaction* FIFOCompactionPicker::CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t /*output_path_id*/, uint32_t /*max_subcompactions*/,
|
||||
const CompactRangeOptions& /*compact_range_options*/,
|
||||
const InternalKey* /*begin*/, const InternalKey* /*end*/,
|
||||
InternalKey** compaction_end, bool* /*manual_conflict*/) {
|
||||
InternalKey** compaction_end, bool* /*manual_conflict*/,
|
||||
uint64_t /*max_file_num_to_ignore*/) {
|
||||
#ifdef NDEBUG
|
||||
(void)input_level;
|
||||
(void)output_level;
|
||||
@@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
|
||||
namespace rocksdb {
|
||||
class FIFOCompactionPicker : public CompactionPicker {
|
||||
@@ -27,9 +27,10 @@ class FIFOCompactionPicker : public CompactionPicker {
|
||||
virtual Compaction* CompactRange(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, int input_level, int output_level,
|
||||
uint32_t output_path_id, uint32_t max_subcompactions,
|
||||
const CompactRangeOptions& compact_range_options,
|
||||
const InternalKey* begin, const InternalKey* end,
|
||||
InternalKey** compaction_end, bool* manual_conflict) override;
|
||||
InternalKey** compaction_end, bool* manual_conflict,
|
||||
uint64_t max_file_num_to_ignore) override;
|
||||
|
||||
// The maximum allowed output level. Always returns 0.
|
||||
virtual int MaxOutputLevel() const override { return 0; }
|
||||
@@ -0,0 +1,553 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
bool LevelCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
if (!vstorage->ExpiredTtlFiles().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->BottommostFilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i <= vstorage->MaxInputLevel(); i++) {
|
||||
if (vstorage->CompactionScore(i) >= 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A class to build a leveled compaction step-by-step.
|
||||
class LevelCompactionBuilder {
|
||||
public:
|
||||
LevelCompactionBuilder(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionPicker* compaction_picker,
|
||||
LogBuffer* log_buffer,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const ImmutableCFOptions& ioptions)
|
||||
: cf_name_(cf_name),
|
||||
vstorage_(vstorage),
|
||||
compaction_picker_(compaction_picker),
|
||||
log_buffer_(log_buffer),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
ioptions_(ioptions) {}
|
||||
|
||||
// Pick and return a compaction.
|
||||
Compaction* PickCompaction();
|
||||
|
||||
// Pick the initial files to compact to the next level. (or together
|
||||
// in Intra-L0 compactions)
|
||||
void SetupInitialFiles();
|
||||
|
||||
// If the initial files are from L0 level, pick other L0
|
||||
// files if needed.
|
||||
bool SetupOtherL0FilesIfNeeded();
|
||||
|
||||
// Based on initial files, setup other files need to be compacted
|
||||
// in this compaction, accordingly.
|
||||
bool SetupOtherInputsIfNeeded();
|
||||
|
||||
Compaction* GetCompaction();
|
||||
|
||||
// For the specfied level, pick a file that we want to compact.
|
||||
// Returns false if there is no file to compact.
|
||||
// If it returns true, inputs->files.size() will be exactly one.
|
||||
// If level is 0 and there is already a compaction on that level, this
|
||||
// function will return false.
|
||||
bool PickFileToCompact();
|
||||
|
||||
// For L0->L0, picks the longest span of files that aren't currently
|
||||
// undergoing compaction for which work-per-deleted-file decreases. The span
|
||||
// always starts from the newest L0 file.
|
||||
//
|
||||
// Intra-L0 compaction is independent of all other files, so it can be
|
||||
// performed even when L0->base_level compactions are blocked.
|
||||
//
|
||||
// Returns true if `inputs` is populated with a span of files to be compacted;
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
void PickExpiredTtlFiles();
|
||||
|
||||
void PickFilesMarkedForPeriodicCompaction();
|
||||
|
||||
const std::string& cf_name_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
CompactionPicker* compaction_picker_;
|
||||
LogBuffer* log_buffer_;
|
||||
int start_level_ = -1;
|
||||
int output_level_ = -1;
|
||||
int parent_index_ = -1;
|
||||
int base_index_ = -1;
|
||||
double start_level_score_ = 0;
|
||||
bool is_manual_ = false;
|
||||
CompactionInputFiles start_level_inputs_;
|
||||
std::vector<CompactionInputFiles> compaction_inputs_;
|
||||
CompactionInputFiles output_level_inputs_;
|
||||
std::vector<FileMetaData*> grandparents_;
|
||||
CompactionReason compaction_reason_ = CompactionReason::kUnknown;
|
||||
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const ImmutableCFOptions& ioptions_;
|
||||
// Pick a path ID to place a newly generated file, with its level
|
||||
static uint32_t GetPathId(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int level);
|
||||
|
||||
static const int kMinFilesForIntraL0Compaction = 4;
|
||||
};
|
||||
|
||||
void LevelCompactionBuilder::PickExpiredTtlFiles() {
|
||||
if (vstorage_->ExpiredTtlFiles().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
start_level_ = level_file.first;
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
|
||||
if ((start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
(start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->ExpiredTtlFiles()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::PickFilesMarkedForPeriodicCompaction() {
|
||||
if (vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
output_level_ = start_level_ = level_file.first;
|
||||
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->FilesMarkedForPeriodicCompaction()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
// Find the compactions by size on all levels.
|
||||
bool skipped_l0_to_base = false;
|
||||
for (int i = 0; i < compaction_picker_->NumberLevels() - 1; i++) {
|
||||
start_level_score_ = vstorage_->CompactionScore(i);
|
||||
start_level_ = vstorage_->CompactionScoreLevel(i);
|
||||
assert(i == 0 || start_level_score_ <= vstorage_->CompactionScore(i - 1));
|
||||
if (start_level_score_ >= 1) {
|
||||
if (skipped_l0_to_base && start_level_ == vstorage_->base_level()) {
|
||||
// If L0->base_level compaction is pending, don't schedule further
|
||||
// compaction from base level. Otherwise L0->base_level compaction
|
||||
// may starve.
|
||||
continue;
|
||||
}
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
if (PickFileToCompact()) {
|
||||
// found the compaction!
|
||||
if (start_level_ == 0) {
|
||||
// L0 score = `num L0 files` / `level0_file_num_compaction_trigger`
|
||||
compaction_reason_ = CompactionReason::kLevelL0FilesNum;
|
||||
} else {
|
||||
// L1+ score = `Level files size` / `MaxBytesForLevel`
|
||||
compaction_reason_ = CompactionReason::kLevelMaxLevelSize;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// didn't find the compaction, clear the inputs
|
||||
start_level_inputs_.clear();
|
||||
if (start_level_ == 0) {
|
||||
skipped_l0_to_base = true;
|
||||
// L0->base_level may be blocked due to ongoing L0->base_level
|
||||
// compactions. It may also be blocked by an ongoing compaction from
|
||||
// base_level downwards.
|
||||
//
|
||||
// In these cases, to reduce L0 file count and thus reduce likelihood
|
||||
// of write stalls, we can attempt compacting a span of files within
|
||||
// L0.
|
||||
if (PickIntraL0Compaction()) {
|
||||
output_level_ = 0;
|
||||
compaction_reason_ = CompactionReason::kLevelL0FilesNum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find a compaction, check if there are any files marked for
|
||||
// compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_,
|
||||
&start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
is_manual_ = true;
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Bottommost Files Compaction on deleting tombstones
|
||||
if (start_level_inputs_.empty()) {
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file = vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TTL Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickExpiredTtlFiles();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Periodic Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickFilesMarkedForPeriodicCompaction();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherL0FilesIfNeeded() {
|
||||
if (start_level_ == 0 && output_level_ != 0) {
|
||||
return compaction_picker_->GetOverlappingL0Files(
|
||||
vstorage_, &start_level_inputs_, output_level_, &parent_index_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::SetupOtherInputsIfNeeded() {
|
||||
// Setup input files from output level. For output to L0, we only compact
|
||||
// spans of files that do not interact with any pending compactions, so don't
|
||||
// need to consider other levels.
|
||||
if (output_level_ != 0) {
|
||||
output_level_inputs_.level = output_level_;
|
||||
if (!compaction_picker_->SetupOtherInputs(
|
||||
cf_name_, mutable_cf_options_, vstorage_, &start_level_inputs_,
|
||||
&output_level_inputs_, &parent_index_, base_index_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
compaction_inputs_.push_back(start_level_inputs_);
|
||||
if (!output_level_inputs_.empty()) {
|
||||
compaction_inputs_.push_back(output_level_inputs_);
|
||||
}
|
||||
|
||||
// In some edge cases we could pick a compaction that will be compacting
|
||||
// a key range that overlap with another running compaction, and both
|
||||
// of them have the same output level. This could happen if
|
||||
// (1) we are running a non-exclusive manual compaction
|
||||
// (2) AddFile ingest a new file into the LSM tree
|
||||
// We need to disallow this from happening.
|
||||
if (compaction_picker_->FilesRangeOverlapWithCompaction(compaction_inputs_,
|
||||
output_level_)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
return false;
|
||||
}
|
||||
compaction_picker_->GetGrandparents(vstorage_, start_level_inputs_,
|
||||
output_level_inputs_, &grandparents_);
|
||||
} else {
|
||||
compaction_inputs_.push_back(start_level_inputs_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::PickCompaction() {
|
||||
// Pick up the first file to start compaction. It may have been extended
|
||||
// to a clean cut.
|
||||
SetupInitialFiles();
|
||||
if (start_level_inputs_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
assert(start_level_ >= 0 && output_level_ >= 0);
|
||||
|
||||
// If it is a L0 -> base level compaction, we need to set up other L0
|
||||
// files if needed.
|
||||
if (!SetupOtherL0FilesIfNeeded()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Pick files in the output level and expand more files in the start level
|
||||
// if needed.
|
||||
if (!SetupOtherInputsIfNeeded()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Form a compaction object containing the files we picked.
|
||||
Compaction* c = GetCompaction();
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("LevelCompactionPicker::PickCompaction:Return", c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionBuilder::GetCompaction() {
|
||||
auto c = new Compaction(
|
||||
vstorage_, ioptions_, mutable_cf_options_, std::move(compaction_inputs_),
|
||||
output_level_,
|
||||
MaxFileSizeForLevel(mutable_cf_options_, output_level_,
|
||||
ioptions_.compaction_style, vstorage_->base_level(),
|
||||
ioptions_.level_compaction_dynamic_level_bytes),
|
||||
mutable_cf_options_.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options_, output_level_),
|
||||
GetCompressionType(ioptions_, vstorage_, mutable_cf_options_,
|
||||
output_level_, vstorage_->base_level()),
|
||||
GetCompressionOptions(ioptions_, vstorage_, output_level_),
|
||||
/* max_subcompactions */ 0, std::move(grandparents_), is_manual_,
|
||||
start_level_score_, false /* deletion_compaction */, compaction_reason_);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
compaction_picker_->RegisterCompaction(c);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the optimal path to place a file
|
||||
* Given a level, finds the path where levels up to it will fit in levels
|
||||
* up to and including this path
|
||||
*/
|
||||
uint32_t LevelCompactionBuilder::GetPathId(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, int level) {
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.cf_paths.empty());
|
||||
|
||||
// size remaining in the most recent path
|
||||
uint64_t current_path_size = ioptions.cf_paths[0].target_size;
|
||||
|
||||
uint64_t level_size;
|
||||
int cur_level = 0;
|
||||
|
||||
// max_bytes_for_level_base denotes L1 size.
|
||||
// We estimate L0 size to be the same as L1.
|
||||
level_size = mutable_cf_options.max_bytes_for_level_base;
|
||||
|
||||
// Last path is the fallback
|
||||
while (p < ioptions.cf_paths.size() - 1) {
|
||||
if (level_size <= current_path_size) {
|
||||
if (cur_level == level) {
|
||||
// Does desired level fit in this path?
|
||||
return p;
|
||||
} else {
|
||||
current_path_size -= level_size;
|
||||
if (cur_level > 0) {
|
||||
if (ioptions.level_compaction_dynamic_level_bytes) {
|
||||
// Currently, level_compaction_dynamic_level_bytes is ignored when
|
||||
// multiple db paths are specified. https://github.com/facebook/
|
||||
// rocksdb/blob/master/db/column_family.cc.
|
||||
// Still, adding this check to avoid accidentally using
|
||||
// max_bytes_for_level_multiplier_additional
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier);
|
||||
} else {
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier *
|
||||
mutable_cf_options.MaxBytesMultiplerAdditional(cur_level));
|
||||
}
|
||||
}
|
||||
cur_level++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
current_path_size = ioptions.cf_paths[p].target_size;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickFileToCompact() {
|
||||
// level 0 files are overlapping. So we cannot pick more
|
||||
// than one concurrent compactions at this level. This
|
||||
// could be made better by looking at key-ranges that are
|
||||
// being compacted at level 0.
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
TEST_SYNC_POINT("LevelCompactionPicker::PickCompactionBySize:0");
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.clear();
|
||||
|
||||
assert(start_level_ >= 0);
|
||||
|
||||
// Pick the largest file in this level that is not already
|
||||
// being compacted
|
||||
const std::vector<int>& file_size =
|
||||
vstorage_->FilesByCompactionPri(start_level_);
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(start_level_);
|
||||
|
||||
unsigned int cmp_idx;
|
||||
for (cmp_idx = vstorage_->NextCompactionIndex(start_level_);
|
||||
cmp_idx < file_size.size(); cmp_idx++) {
|
||||
int index = file_size[cmp_idx];
|
||||
auto* f = level_files[index];
|
||||
|
||||
// do not pick a file to compact if it is being compacted
|
||||
// from n-1 level.
|
||||
if (f->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
start_level_inputs_.files.push_back(f);
|
||||
start_level_inputs_.level = start_level_;
|
||||
if (!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_) ||
|
||||
compaction_picker_->FilesRangeOverlapWithCompaction(
|
||||
{start_level_inputs_}, output_level_)) {
|
||||
// A locked (pending compaction) input-level file was pulled in due to
|
||||
// user-key overlap.
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now that input level is fully expanded, we check whether any output files
|
||||
// are locked due to pending compaction.
|
||||
//
|
||||
// Note we rely on ExpandInputsToCleanCut() to tell us whether any output-
|
||||
// level files are locked, not just the extra ones pulled in for user-key
|
||||
// overlap.
|
||||
InternalKey smallest, largest;
|
||||
compaction_picker_->GetRange(start_level_inputs_, &smallest, &largest);
|
||||
CompactionInputFiles output_level_inputs;
|
||||
output_level_inputs.level = output_level_;
|
||||
vstorage_->GetOverlappingInputs(output_level_, &smallest, &largest,
|
||||
&output_level_inputs.files);
|
||||
if (!output_level_inputs.empty() &&
|
||||
!compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&output_level_inputs)) {
|
||||
start_level_inputs_.clear();
|
||||
continue;
|
||||
}
|
||||
base_index_ = index;
|
||||
break;
|
||||
}
|
||||
|
||||
// store where to start the iteration in the next call to PickCompaction
|
||||
vstorage_->SetNextCompactionIndex(start_level_, cmp_idx);
|
||||
|
||||
return start_level_inputs_.size() > 0;
|
||||
}
|
||||
|
||||
bool LevelCompactionBuilder::PickIntraL0Compaction() {
|
||||
start_level_inputs_.clear();
|
||||
const std::vector<FileMetaData*>& level_files =
|
||||
vstorage_->LevelFiles(0 /* level */);
|
||||
if (level_files.size() <
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger + 2) ||
|
||||
level_files[0]->being_compacted) {
|
||||
// If L0 isn't accumulating much files beyond the regular trigger, don't
|
||||
// resort to L0->L0 compaction yet.
|
||||
return false;
|
||||
}
|
||||
return FindIntraL0Compaction(
|
||||
level_files, kMinFilesForIntraL0Compaction, port::kMaxUint64,
|
||||
mutable_cf_options_.max_compaction_bytes, &start_level_inputs_);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
LevelCompactionBuilder builder(cf_name, vstorage, this, log_buffer,
|
||||
mutable_cf_options, ioptions_);
|
||||
return builder.PickCompaction();
|
||||
}
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
|
||||
namespace rocksdb {
|
||||
// Picking compactions for leveled compaction. See wiki page
|
||||
// https://github.com/facebook/rocksdb/wiki/Leveled-Compaction
|
||||
// for description of Leveled compaction.
|
||||
class LevelCompactionPicker : public CompactionPicker {
|
||||
public:
|
||||
LevelCompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: CompactionPicker(ioptions, icmp) {}
|
||||
virtual Compaction* PickCompaction(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
LogBuffer* log_buffer) override;
|
||||
|
||||
virtual bool NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const override;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -3,28 +3,26 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction_picker_fifo.h"
|
||||
#include "db/compaction_picker_universal.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
|
||||
#include "util/logging.h"
|
||||
#include "logging/logging.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* /*format*/, va_list /*ap*/) override {
|
||||
log_count++;
|
||||
}
|
||||
void Logv(const char* /*format*/, va_list /*ap*/) override { log_count++; }
|
||||
size_t log_count;
|
||||
};
|
||||
|
||||
@@ -59,14 +57,16 @@ class CompactionPickerTest : public testing::Test {
|
||||
log_buffer_(InfoLogLevel::INFO_LEVEL, &logger_),
|
||||
file_num_(1),
|
||||
vstorage_(nullptr) {
|
||||
// ioptions_.compaction_pri = kMinOverlappingRatio has its own set of
|
||||
// tests to cover.
|
||||
ioptions_.compaction_pri = kByCompensatedSize;
|
||||
fifo_options_.max_table_files_size = 1;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
ioptions_.cf_paths.emplace_back("dummy",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
~CompactionPickerTest() {
|
||||
}
|
||||
~CompactionPickerTest() override {}
|
||||
|
||||
void NewVersionStorage(int num_levels, CompactionStyle style) {
|
||||
DeleteVersionStorage();
|
||||
@@ -85,8 +85,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100) {
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0) {
|
||||
assert(level < vstorage_->num_levels());
|
||||
FileMetaData* f = new FileMetaData;
|
||||
f->fd = FileDescriptor(file_number, path_id, file_size);
|
||||
@@ -94,7 +94,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
f->largest = InternalKey(largest, largest_seq, kTypeValue);
|
||||
f->fd.smallest_seqno = smallest_seq;
|
||||
f->fd.largest_seqno = largest_seq;
|
||||
f->compensated_file_size = file_size;
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->refs = 0;
|
||||
vstorage_->AddFile(level, f);
|
||||
files_.emplace_back(f);
|
||||
@@ -617,6 +618,35 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping3) {
|
||||
ASSERT_EQ(8U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping4) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000000;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
|
||||
// file 7 and 8 over lap with the same file, but file 8 is smaller so
|
||||
// it will be picked.
|
||||
// Overlaps with file 26, 27. And the file is compensated so will be
|
||||
// picked up.
|
||||
Add(2, 6U, "150", "167", 60000000U, 0, 100, 100, 180000000U);
|
||||
Add(2, 7U, "168", "169", 60000000U); // Overlaps with file 27
|
||||
Add(2, 8U, "201", "300", 61000000U); // Overlaps with file 28
|
||||
|
||||
Add(3, 26U, "160", "165", 60000000U);
|
||||
// Boosted file size in output level is not considered.
|
||||
Add(3, 27U, "166", "170", 60000000U, 0, 100, 100, 260000000U);
|
||||
Add(3, 28U, "180", "400", 60000000U);
|
||||
Add(3, 29U, "401", "500", 60000000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
// Picking file 8 because overlapping ratio is the biggest.
|
||||
ASSERT_EQ(6U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
// This test exhibits the bug where we don't properly reset parent_index in
|
||||
// PickCompaction()
|
||||
TEST_F(CompactionPickerTest, ParentIndexResetBug) {
|
||||
@@ -1448,6 +1478,65 @@ TEST_F(CompactionPickerTest, CacheNextCompactionIndex) {
|
||||
ASSERT_EQ(4, vstorage_->NextCompactionIndex(1 /* level */));
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IntraL0MaxCompactionBytesNotHit) {
|
||||
// Intra L0 compaction triggers only if there are at least
|
||||
// level0_file_num_compaction_trigger + 2 L0 files.
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 3;
|
||||
mutable_cf_options_.max_compaction_bytes = 1000000u;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
|
||||
// All 5 L0 files will be picked for intra L0 compaction. The one L1 file
|
||||
// spans entire L0 key range and is marked as being compacted to avoid
|
||||
// L0->L1 compaction.
|
||||
Add(0, 1U, "100", "150", 200000U);
|
||||
Add(0, 2U, "151", "200", 200000U);
|
||||
Add(0, 3U, "201", "250", 200000U);
|
||||
Add(0, 4U, "251", "300", 200000U);
|
||||
Add(0, 5U, "301", "350", 200000U);
|
||||
Add(1, 6U, "100", "350", 200000U);
|
||||
vstorage_->LevelFiles(1)[0]->being_compacted = true;
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(5U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0U, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IntraL0MaxCompactionBytesHit) {
|
||||
// Intra L0 compaction triggers only if there are at least
|
||||
// level0_file_num_compaction_trigger + 2 L0 files.
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 3;
|
||||
mutable_cf_options_.max_compaction_bytes = 999999u;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
|
||||
// 4 out of 5 L0 files will be picked for intra L0 compaction due to
|
||||
// max_compaction_bytes limit (the minimum number of files for triggering
|
||||
// intra L0 compaction is 4). The one L1 file spans entire L0 key range and
|
||||
// is marked as being compacted to avoid L0->L1 compaction.
|
||||
Add(0, 1U, "100", "150", 200000U);
|
||||
Add(0, 2U, "151", "200", 200000U);
|
||||
Add(0, 3U, "201", "250", 200000U);
|
||||
Add(0, 4U, "251", "300", 200000U);
|
||||
Add(0, 5U, "301", "350", 200000U);
|
||||
Add(1, 6U, "100", "350", 200000U);
|
||||
vstorage_->LevelFiles(1)[0]->being_compacted = true;
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_levels());
|
||||
ASSERT_EQ(4U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(CompactionReason::kLevelL0FilesNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0U, compaction->output_level());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -7,25 +7,21 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/compaction_picker_universal.h"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "db/column_family.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/log_buffer.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
namespace {
|
||||
@@ -395,8 +391,8 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
}
|
||||
#endif
|
||||
// update statistics
|
||||
MeasureTime(ioptions_.statistics, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
c->inputs(0)->size());
|
||||
RecordInHistogram(ioptions_.statistics, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
c->inputs(0)->size());
|
||||
|
||||
RegisterCompaction(c);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
@@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
|
||||
namespace rocksdb {
|
||||
class UniversalCompactionPicker : public CompactionPicker {
|
||||
+28
-28
@@ -9,11 +9,11 @@
|
||||
#include "memtable/stl_wrappers.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/kv_map.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using std::unique_ptr;
|
||||
@@ -27,24 +27,24 @@ class KVIter : public Iterator {
|
||||
public:
|
||||
explicit KVIter(const stl_wrappers::KVMap* map)
|
||||
: map_(map), iter_(map_->end()) {}
|
||||
virtual bool Valid() const override { return iter_ != map_->end(); }
|
||||
virtual void SeekToFirst() override { iter_ = map_->begin(); }
|
||||
virtual void SeekToLast() override {
|
||||
bool Valid() const override { return iter_ != map_->end(); }
|
||||
void SeekToFirst() override { iter_ = map_->begin(); }
|
||||
void SeekToLast() override {
|
||||
if (map_->empty()) {
|
||||
iter_ = map_->end();
|
||||
} else {
|
||||
iter_ = map_->find(map_->rbegin()->first);
|
||||
}
|
||||
}
|
||||
virtual void Seek(const Slice& k) override {
|
||||
void Seek(const Slice& k) override {
|
||||
iter_ = map_->lower_bound(k.ToString());
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
void SeekForPrev(const Slice& k) override {
|
||||
iter_ = map_->upper_bound(k.ToString());
|
||||
Prev();
|
||||
}
|
||||
virtual void Next() override { ++iter_; }
|
||||
virtual void Prev() override {
|
||||
void Next() override { ++iter_; }
|
||||
void Prev() override {
|
||||
if (iter_ == map_->begin()) {
|
||||
iter_ = map_->end();
|
||||
return;
|
||||
@@ -52,9 +52,9 @@ class KVIter : public Iterator {
|
||||
--iter_;
|
||||
}
|
||||
|
||||
virtual Slice key() const override { return iter_->first; }
|
||||
virtual Slice value() const override { return iter_->second; }
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
Slice key() const override { return iter_->first; }
|
||||
Slice value() const override { return iter_->second; }
|
||||
Status status() const override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
const stl_wrappers::KVMap* const map_;
|
||||
@@ -171,9 +171,9 @@ class DoubleComparator : public Comparator {
|
||||
public:
|
||||
DoubleComparator() {}
|
||||
|
||||
virtual const char* Name() const override { return "DoubleComparator"; }
|
||||
const char* Name() const override { return "DoubleComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
#ifndef CYGWIN
|
||||
double da = std::stod(a.ToString());
|
||||
double db = std::stod(b.ToString());
|
||||
@@ -189,19 +189,19 @@ class DoubleComparator : public Comparator {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
class HashComparator : public Comparator {
|
||||
public:
|
||||
HashComparator() {}
|
||||
|
||||
virtual const char* Name() const override { return "HashComparator"; }
|
||||
const char* Name() const override { return "HashComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
uint32_t ha = Hash(a.data(), a.size(), 66);
|
||||
uint32_t hb = Hash(b.data(), b.size(), 66);
|
||||
if (ha == hb) {
|
||||
@@ -212,19 +212,19 @@ class HashComparator : public Comparator {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
|
||||
class TwoStrComparator : public Comparator {
|
||||
public:
|
||||
TwoStrComparator() {}
|
||||
|
||||
virtual const char* Name() const override { return "TwoStrComparator"; }
|
||||
const char* Name() const override { return "TwoStrComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
assert(a.size() >= 2);
|
||||
assert(b.size() >= 2);
|
||||
size_t size_a1 = static_cast<size_t>(a[0]);
|
||||
@@ -244,10 +244,10 @@ class TwoStrComparator : public Comparator {
|
||||
}
|
||||
return a2.compare(b2);
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
void FindShortestSeparator(std::string* /*start*/,
|
||||
const Slice& /*limit*/) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
void FindShortSuccessor(std::string* /*key*/) const override {}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -272,7 +272,7 @@ class ComparatorDBTest
|
||||
EXPECT_OK(DestroyDB(dbname_, last_options_));
|
||||
}
|
||||
|
||||
~ComparatorDBTest() {
|
||||
~ComparatorDBTest() override {
|
||||
delete db_;
|
||||
EXPECT_OK(DestroyDB(dbname_, last_options_));
|
||||
comparator = BytewiseComparator();
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "rocksdb/convenience.h"
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "util/cast_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
+43
-8
@@ -13,21 +13,23 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/filename.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "util/filename.h"
|
||||
#include "table/block_based/block_based_table_builder.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -60,9 +62,9 @@ class CorruptionTest : public testing::Test {
|
||||
options_.create_if_missing = false;
|
||||
}
|
||||
|
||||
~CorruptionTest() {
|
||||
delete db_;
|
||||
DestroyDB(dbname_, Options());
|
||||
~CorruptionTest() override {
|
||||
delete db_;
|
||||
DestroyDB(dbname_, Options());
|
||||
}
|
||||
|
||||
void CloseDb() {
|
||||
@@ -467,6 +469,39 @@ TEST_F(CorruptionTest, UnrelatedKeys) {
|
||||
ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "b"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
ASSERT_EQ(static_cast<size_t>(1), metadata.size());
|
||||
std::string filename = dbname_ + metadata[0].name;
|
||||
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
ASSERT_OK(options_.env->NewRandomAccessFile(filename, &file, EnvOptions()));
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), filename));
|
||||
|
||||
uint64_t file_size;
|
||||
ASSERT_OK(options_.env->GetFileSize(filename, &file_size));
|
||||
|
||||
BlockHandle range_del_handle;
|
||||
ASSERT_OK(FindMetaBlock(
|
||||
file_reader.get(), file_size, kBlockBasedTableMagicNumber,
|
||||
ImmutableCFOptions(options_), kRangeDelBlock, &range_del_handle));
|
||||
|
||||
ASSERT_OK(TryReopen());
|
||||
CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
// The test case does not fail on TryReopen because failure to preload table
|
||||
// handlers is not considered critical.
|
||||
ASSERT_OK(TryReopen());
|
||||
std::string val;
|
||||
// However, it does fail on any read involving that file since that file
|
||||
// cannot be opened with a corrupt range deletion meta-block.
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "a", &val).IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
Options options;
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "table/cuckoo_table_factory.h"
|
||||
#include "table/cuckoo_table_reader.h"
|
||||
#include "table/cuckoo/cuckoo_table_factory.h"
|
||||
#include "table/cuckoo/cuckoo_table_reader.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -31,7 +31,7 @@ class CuckooTableDBTest : public testing::Test {
|
||||
Reopen();
|
||||
}
|
||||
|
||||
~CuckooTableDBTest() {
|
||||
~CuckooTableDBTest() override {
|
||||
delete db_;
|
||||
EXPECT_OK(DestroyDB(dbname_, Options()));
|
||||
}
|
||||
|
||||
+796
-28
@@ -6,12 +6,14 @@
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
// #include <iostream>
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "table/block_based/block_builder.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#endif
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -214,11 +216,11 @@ TEST_F(DBBasicTest, PutSingleDeleteGet) {
|
||||
ASSERT_EQ("v2", Get(1, "foo2"));
|
||||
ASSERT_OK(SingleDelete(1, "foo"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo"));
|
||||
// Skip HashCuckooRep as it does not support single delete. FIFO and
|
||||
// universal compaction do not apply to the test case. Skip MergePut
|
||||
// because single delete does not get removed when it encounters a merge.
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
// Ski FIFO and universal compaction because they do not apply to the test
|
||||
// case. Skip MergePut because single delete does not get removed when it
|
||||
// encounters a merge.
|
||||
} while (ChangeOptions(kSkipFIFOCompaction | kSkipUniversalCompaction |
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, EmptyFlush) {
|
||||
@@ -236,11 +238,11 @@ TEST_F(DBBasicTest, EmptyFlush) {
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
ASSERT_EQ("[ ]", AllEntriesFor("a", 1));
|
||||
// Skip HashCuckooRep as it does not support single delete. FIFO and
|
||||
// universal compaction do not apply to the test case. Skip MergePut
|
||||
// because merges cannot be combined with single deletions.
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction |
|
||||
kSkipUniversalCompaction | kSkipMergePut));
|
||||
// Skip FIFO and universal compaction as they do not apply to the test
|
||||
// case. Skip MergePut because merges cannot be combined with single
|
||||
// deletions.
|
||||
} while (ChangeOptions(kSkipFIFOCompaction | kSkipUniversalCompaction |
|
||||
kSkipMergePut));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, GetFromVersions) {
|
||||
@@ -264,11 +266,6 @@ TEST_F(DBBasicTest, GetSnapshot) {
|
||||
std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x');
|
||||
ASSERT_OK(Put(1, key, "v1"));
|
||||
const Snapshot* s1 = db_->GetSnapshot();
|
||||
if (option_config_ == kHashCuckoo) {
|
||||
// Unsupported case.
|
||||
ASSERT_TRUE(s1 == nullptr);
|
||||
break;
|
||||
}
|
||||
ASSERT_OK(Put(1, key, "v2"));
|
||||
ASSERT_EQ("v2", Get(1, key));
|
||||
ASSERT_EQ("v1", Get(1, key, s1));
|
||||
@@ -509,7 +506,7 @@ TEST_F(DBBasicTest, Snapshot) {
|
||||
ASSERT_EQ(0U, GetNumSnapshots());
|
||||
ASSERT_EQ("0v4", Get(0, "foo"));
|
||||
ASSERT_EQ("1v4", Get(1, "foo"));
|
||||
} while (ChangeOptions(kSkipHashCuckoo));
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -565,8 +562,7 @@ TEST_F(DBBasicTest, CompactBetweenSnapshots) {
|
||||
nullptr);
|
||||
ASSERT_EQ("sixth", Get(1, "foo"));
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ sixth ]");
|
||||
// skip HashCuckooRep as it does not support snapshot
|
||||
} while (ChangeOptions(kSkipHashCuckoo | kSkipFIFOCompaction));
|
||||
} while (ChangeOptions(kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
@@ -857,18 +853,17 @@ class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
TestLogger(TestEnv *env_ptr) : Logger() { env = env_ptr; }
|
||||
~TestLogger() {
|
||||
~TestLogger() override {
|
||||
if (!closed_) {
|
||||
CloseHelper();
|
||||
}
|
||||
}
|
||||
virtual void Logv(const char* /*format*/, va_list /*ap*/) override{};
|
||||
void Logv(const char* /*format*/, va_list /*ap*/) override{};
|
||||
|
||||
protected:
|
||||
virtual Status CloseImpl() override {
|
||||
return CloseHelper();
|
||||
}
|
||||
private:
|
||||
Status CloseImpl() override { return CloseHelper(); }
|
||||
|
||||
private:
|
||||
Status CloseHelper() {
|
||||
env->CloseCountInc();;
|
||||
return Status::IOError();
|
||||
@@ -880,8 +875,8 @@ class TestEnv : public EnvWrapper {
|
||||
|
||||
int GetCloseCount() { return close_count; }
|
||||
|
||||
virtual Status NewLogger(const std::string& /*fname*/,
|
||||
std::shared_ptr<Logger>* result) {
|
||||
Status NewLogger(const std::string& /*fname*/,
|
||||
std::shared_ptr<Logger>* result) override {
|
||||
result->reset(new TestLogger(this));
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -956,6 +951,779 @@ TEST_F(DBBasicTest, DBCloseFlushError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetMultiCF) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
options);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val"));
|
||||
}
|
||||
|
||||
int get_sv_count = 0;
|
||||
rocksdb::DBImpl* db = reinterpret_cast<DBImpl*>(db_);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (++get_sv_count == 2) {
|
||||
// After MultiGet refs a couple of CFs, flush all CFs so MultiGet
|
||||
// is forced to repeat the process
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val2"));
|
||||
}
|
||||
}
|
||||
if (get_sv_count == 11) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
db->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_EQ(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse);
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<int> cfs;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
cfs.push_back(i);
|
||||
keys.push_back("cf" + std::to_string(i) + "_key");
|
||||
}
|
||||
|
||||
values = MultiGet(cfs, keys, nullptr);
|
||||
ASSERT_EQ(values.size(), 8);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
ASSERT_EQ(values[j], "cf" + std::to_string(j) + "_val2");
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
reinterpret_cast<DBImpl*>(db_)->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_NE(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse);
|
||||
ASSERT_NE(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVObsolete);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetMultiCFMutex) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
options);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val"));
|
||||
}
|
||||
|
||||
int get_sv_count = 0;
|
||||
int retries = 0;
|
||||
bool last_try = false;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiGet::LastTry", [&](void* /*arg*/) {
|
||||
last_try = true;
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (last_try) {
|
||||
return;
|
||||
}
|
||||
if (++get_sv_count == 2) {
|
||||
++retries;
|
||||
get_sv_count = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_OK(Put(
|
||||
i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val" + std::to_string(retries)));
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<int> cfs;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
cfs.push_back(i);
|
||||
keys.push_back("cf" + std::to_string(i) + "_key");
|
||||
}
|
||||
|
||||
values = MultiGet(cfs, keys, nullptr);
|
||||
ASSERT_TRUE(last_try);
|
||||
ASSERT_EQ(values.size(), 8);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
ASSERT_EQ(values[j],
|
||||
"cf" + std::to_string(j) + "_val" + std::to_string(retries));
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
reinterpret_cast<DBImpl*>(db_)->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_NE(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetMultiCFSnapshot) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu", "ilya", "muromec", "dobrynia", "nikitich",
|
||||
"alyosha", "popovich"},
|
||||
options);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val"));
|
||||
}
|
||||
|
||||
int get_sv_count = 0;
|
||||
rocksdb::DBImpl* db = reinterpret_cast<DBImpl*>(db_);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::MultiGet::AfterRefSV", [&](void* /*arg*/) {
|
||||
if (++get_sv_count == 2) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_OK(Put(i, "cf" + std::to_string(i) + "_key",
|
||||
"cf" + std::to_string(i) + "_val2"));
|
||||
}
|
||||
}
|
||||
if (get_sv_count == 8) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
db->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_TRUE(
|
||||
(cfd->TEST_GetLocalSV()->Get() == SuperVersion::kSVInUse) ||
|
||||
(cfd->TEST_GetLocalSV()->Get() == SuperVersion::kSVObsolete));
|
||||
}
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<int> cfs;
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
cfs.push_back(i);
|
||||
keys.push_back("cf" + std::to_string(i) + "_key");
|
||||
}
|
||||
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
values = MultiGet(cfs, keys, snapshot);
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
ASSERT_EQ(values.size(), 8);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
ASSERT_EQ(values[j], "cf" + std::to_string(j) + "_val");
|
||||
}
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
reinterpret_cast<DBImpl*>(db_)->GetColumnFamilyHandle(i))
|
||||
->cfd();
|
||||
ASSERT_NE(cfd->TEST_GetLocalSV()->Get(), SuperVersion::kSVInUse);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSimpleUnsorted) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_OK(Put(1, "k1", "v1"));
|
||||
ASSERT_OK(Put(1, "k2", "v2"));
|
||||
ASSERT_OK(Put(1, "k3", "v3"));
|
||||
ASSERT_OK(Put(1, "k4", "v4"));
|
||||
ASSERT_OK(Delete(1, "k4"));
|
||||
ASSERT_OK(Put(1, "k5", "v5"));
|
||||
ASSERT_OK(Delete(1, "no_key"));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
|
||||
std::vector<Slice> keys({"no_key", "k5", "k4", "k3", "k2", "k1"});
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
db_->MultiGet(ReadOptions(), handles_[1], keys.size(), keys.data(),
|
||||
values.data(), s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
ASSERT_EQ(std::string(values[5].data(), values[5].size()), "v1");
|
||||
ASSERT_EQ(std::string(values[4].data(), values[4].size()), "v2");
|
||||
ASSERT_EQ(std::string(values[3].data(), values[3].size()), "v3");
|
||||
ASSERT_EQ(std::string(values[1].data(), values[1].size()), "v5");
|
||||
// four kv pairs * two bytes per value
|
||||
ASSERT_EQ(8, (int)get_perf_context()->multiget_read_bytes);
|
||||
|
||||
ASSERT_TRUE(s[0].IsNotFound());
|
||||
ASSERT_OK(s[1]);
|
||||
ASSERT_TRUE(s[2].IsNotFound());
|
||||
ASSERT_OK(s[3]);
|
||||
ASSERT_OK(s[4]);
|
||||
ASSERT_OK(s[5]);
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSimpleSorted) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_OK(Put(1, "k1", "v1"));
|
||||
ASSERT_OK(Put(1, "k2", "v2"));
|
||||
ASSERT_OK(Put(1, "k3", "v3"));
|
||||
ASSERT_OK(Put(1, "k4", "v4"));
|
||||
ASSERT_OK(Delete(1, "k4"));
|
||||
ASSERT_OK(Put(1, "k5", "v5"));
|
||||
ASSERT_OK(Delete(1, "no_key"));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
|
||||
std::vector<Slice> keys({"k1", "k2", "k3", "k4", "k5", "no_key"});
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
db_->MultiGet(ReadOptions(), handles_[1], keys.size(), keys.data(),
|
||||
values.data(), s.data(), true);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
ASSERT_EQ(std::string(values[0].data(), values[0].size()), "v1");
|
||||
ASSERT_EQ(std::string(values[1].data(), values[1].size()), "v2");
|
||||
ASSERT_EQ(std::string(values[2].data(), values[2].size()), "v3");
|
||||
ASSERT_EQ(std::string(values[4].data(), values[4].size()), "v5");
|
||||
// four kv pairs * two bytes per value
|
||||
ASSERT_EQ(8, (int)get_perf_context()->multiget_read_bytes);
|
||||
|
||||
ASSERT_OK(s[0]);
|
||||
ASSERT_OK(s[1]);
|
||||
ASSERT_OK(s[2]);
|
||||
ASSERT_TRUE(s[3].IsNotFound());
|
||||
ASSERT_OK(s[4]);
|
||||
ASSERT_TRUE(s[5].IsNotFound());
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedMultiLevel) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
int num_keys = 0;
|
||||
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
ASSERT_OK(Put("key_" + std::to_string(i), "val_l2_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 128; i += 3) {
|
||||
ASSERT_OK(Put("key_" + std::to_string(i), "val_l1_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
for (int i = 0; i < 128; i += 5) {
|
||||
ASSERT_OK(Put("key_" + std::to_string(i), "val_l0_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
ASSERT_EQ(0, num_keys);
|
||||
|
||||
for (int i = 0; i < 128; i += 9) {
|
||||
ASSERT_OK(Put("key_" + std::to_string(i), "val_mem_" + std::to_string(i)));
|
||||
}
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
|
||||
for (int i = 64; i < 80; ++i) {
|
||||
keys.push_back("key_" + std::to_string(i));
|
||||
}
|
||||
|
||||
values = MultiGet(keys, nullptr);
|
||||
ASSERT_EQ(values.size(), 16);
|
||||
for (unsigned int j = 0; j < values.size(); ++j) {
|
||||
int key = j + 64;
|
||||
if (key % 9 == 0) {
|
||||
ASSERT_EQ(values[j], "val_mem_" + std::to_string(key));
|
||||
} else if (key % 5 == 0) {
|
||||
ASSERT_EQ(values[j], "val_l0_" + std::to_string(key));
|
||||
} else if (key % 3 == 0) {
|
||||
ASSERT_EQ(values[j], "val_l1_" + std::to_string(key));
|
||||
} else {
|
||||
ASSERT_EQ(values[j], "val_l2_" + std::to_string(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DBBasicTestWithParallelIO
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<bool,bool,bool,bool>> {
|
||||
public:
|
||||
DBBasicTestWithParallelIO()
|
||||
: DBTestBase("/db_basic_test_with_parallel_io") {
|
||||
bool compressed_cache = std::get<0>(GetParam());
|
||||
bool uncompressed_cache = std::get<1>(GetParam());
|
||||
compression_enabled_ = std::get<2>(GetParam());
|
||||
fill_cache_ = std::get<3>(GetParam());
|
||||
|
||||
if (compressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
compressed_cache_ = std::make_shared<MyBlockCache>(cache);
|
||||
}
|
||||
if (uncompressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
uncompressed_cache_ = std::make_shared<MyBlockCache>(cache);
|
||||
}
|
||||
|
||||
env_->count_random_reads_ = true;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
Random rnd(301);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.block_cache = uncompressed_cache_;
|
||||
table_options.block_cache_compressed = compressed_cache_;
|
||||
table_options.flush_block_policy_factory.reset(
|
||||
new MyFlushBlockPolicyFactory());
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
if (!compression_enabled_) {
|
||||
options.compression = kNoCompression;
|
||||
}
|
||||
Reopen(options);
|
||||
|
||||
std::string zero_str(128, '\0');
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// Make the value compressible. A purely random string doesn't compress
|
||||
// and the resultant data block will not be compressed
|
||||
values_.emplace_back(RandomString(&rnd, 128) + zero_str);
|
||||
assert(Put(Key(i), values_[i]) == Status::OK());
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
bool CheckValue(int i, const std::string& value) {
|
||||
if (values_[i].compare(value) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int num_lookups() { return uncompressed_cache_->num_lookups(); }
|
||||
int num_found() { return uncompressed_cache_->num_found(); }
|
||||
int num_inserts() { return uncompressed_cache_->num_inserts(); }
|
||||
|
||||
int num_lookups_compressed() {
|
||||
return compressed_cache_->num_lookups();
|
||||
}
|
||||
int num_found_compressed() {
|
||||
return compressed_cache_->num_found();
|
||||
}
|
||||
int num_inserts_compressed() {
|
||||
return compressed_cache_->num_inserts();
|
||||
}
|
||||
|
||||
bool fill_cache() { return fill_cache_; }
|
||||
|
||||
static void SetUpTestCase() {}
|
||||
static void TearDownTestCase() {}
|
||||
|
||||
private:
|
||||
class MyFlushBlockPolicyFactory
|
||||
: public FlushBlockPolicyFactory {
|
||||
public:
|
||||
MyFlushBlockPolicyFactory() {}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "MyFlushBlockPolicyFactory";
|
||||
}
|
||||
|
||||
virtual FlushBlockPolicy* NewFlushBlockPolicy(
|
||||
const BlockBasedTableOptions& /*table_options*/,
|
||||
const BlockBuilder& data_block_builder) const override {
|
||||
return new MyFlushBlockPolicy(data_block_builder);
|
||||
}
|
||||
};
|
||||
|
||||
class MyFlushBlockPolicy
|
||||
: public FlushBlockPolicy {
|
||||
public:
|
||||
explicit MyFlushBlockPolicy(const BlockBuilder& data_block_builder)
|
||||
: num_keys_(0), data_block_builder_(data_block_builder) {}
|
||||
|
||||
bool Update(const Slice& /*key*/, const Slice& /*value*/) override {
|
||||
if (data_block_builder_.empty()) {
|
||||
// First key in this block
|
||||
num_keys_ = 1;
|
||||
return false;
|
||||
}
|
||||
// Flush every 10 keys
|
||||
if (num_keys_ == 10) {
|
||||
num_keys_ = 1;
|
||||
return true;
|
||||
}
|
||||
num_keys_++;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int num_keys_;
|
||||
const BlockBuilder& data_block_builder_;
|
||||
};
|
||||
|
||||
class MyBlockCache
|
||||
: public Cache {
|
||||
public:
|
||||
explicit MyBlockCache(std::shared_ptr<Cache>& target)
|
||||
: target_(target), num_lookups_(0), num_found_(0), num_inserts_(0) {}
|
||||
|
||||
virtual const char* Name() const override { return "MyBlockCache"; }
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
num_inserts_++;
|
||||
return target_->Insert(key, value, charge, deleter, handle, priority);
|
||||
}
|
||||
|
||||
virtual Handle* Lookup(const Slice& key,
|
||||
Statistics* stats = nullptr) override {
|
||||
num_lookups_++;
|
||||
Handle* handle = target_->Lookup(key, stats);
|
||||
if (handle != nullptr) {
|
||||
num_found_++;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
virtual bool Ref(Handle* handle) override {
|
||||
return target_->Ref(handle);
|
||||
}
|
||||
|
||||
virtual bool Release(Handle* handle, bool force_erase = false) override {
|
||||
return target_->Release(handle, force_erase);
|
||||
}
|
||||
|
||||
virtual void* Value(Handle* handle) override {
|
||||
return target_->Value(handle);
|
||||
}
|
||||
|
||||
virtual void Erase(const Slice& key) override {
|
||||
target_->Erase(key);
|
||||
}
|
||||
virtual uint64_t NewId() override {
|
||||
return target_->NewId();
|
||||
}
|
||||
|
||||
virtual void SetCapacity(size_t capacity) override {
|
||||
target_->SetCapacity(capacity);
|
||||
}
|
||||
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) override {
|
||||
target_->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
virtual bool HasStrictCapacityLimit() const override {
|
||||
return target_->HasStrictCapacityLimit();
|
||||
}
|
||||
|
||||
virtual size_t GetCapacity() const override {
|
||||
return target_->GetCapacity();
|
||||
}
|
||||
|
||||
virtual size_t GetUsage() const override {
|
||||
return target_->GetUsage();
|
||||
}
|
||||
|
||||
virtual size_t GetUsage(Handle* handle) const override {
|
||||
return target_->GetUsage(handle);
|
||||
}
|
||||
|
||||
virtual size_t GetPinnedUsage() const override {
|
||||
return target_->GetPinnedUsage();
|
||||
}
|
||||
|
||||
virtual size_t GetCharge(Handle* /*handle*/) const override { return 0; }
|
||||
|
||||
virtual void ApplyToAllCacheEntries(void (*callback)(void*, size_t),
|
||||
bool thread_safe) override {
|
||||
return target_->ApplyToAllCacheEntries(callback, thread_safe);
|
||||
}
|
||||
|
||||
virtual void EraseUnRefEntries() override {
|
||||
return target_->EraseUnRefEntries();
|
||||
}
|
||||
|
||||
int num_lookups() { return num_lookups_; }
|
||||
|
||||
int num_found() { return num_found_; }
|
||||
|
||||
int num_inserts() { return num_inserts_; }
|
||||
private:
|
||||
std::shared_ptr<Cache> target_;
|
||||
int num_lookups_;
|
||||
int num_found_;
|
||||
int num_inserts_;
|
||||
};
|
||||
|
||||
std::shared_ptr<MyBlockCache> compressed_cache_;
|
||||
std::shared_ptr<MyBlockCache> uncompressed_cache_;
|
||||
bool compression_enabled_;
|
||||
std::vector<std::string> values_;
|
||||
bool fill_cache_;
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithParallelIO, MultiGet) {
|
||||
std::vector<std::string> key_data(10);
|
||||
std::vector<Slice> keys;
|
||||
// We cannot resize a PinnableSlice vector, so just set initial size to
|
||||
// largest we think we will need
|
||||
std::vector<PinnableSlice> values(10);
|
||||
std::vector<Status> statuses;
|
||||
ReadOptions ro;
|
||||
ro.fill_cache = fill_cache();
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
ASSERT_TRUE(CheckValue(0, values[0].ToString()));
|
||||
ASSERT_TRUE(CheckValue(50, values[1].ToString()));
|
||||
|
||||
int random_reads = env_->random_read_counter_.Read();
|
||||
key_data[0] = Key(1);
|
||||
key_data[1] = Key(51);
|
||||
keys[0] = Slice(key_data[0]);
|
||||
keys[1] = Slice(key_data[1]);
|
||||
values[0].Reset();
|
||||
values[1].Reset();
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
ASSERT_TRUE(CheckValue(1, values[0].ToString()));
|
||||
ASSERT_TRUE(CheckValue(51, values[1].ToString()));
|
||||
|
||||
int expected_reads = random_reads + (fill_cache() ? 0 : 2);
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), expected_reads);
|
||||
|
||||
keys.resize(10);
|
||||
statuses.resize(10);
|
||||
std::vector<int> key_ints{1,2,15,16,55,81,82,83,84,85};
|
||||
for (size_t i = 0; i < key_ints.size(); ++i) {
|
||||
key_data[i] = Key(key_ints[i]);
|
||||
keys[i] = Slice(key_data[i]);
|
||||
statuses[i] = Status::OK();
|
||||
values[i].Reset();
|
||||
}
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
for (size_t i = 0; i < key_ints.size(); ++i) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
ASSERT_TRUE(CheckValue(key_ints[i], values[i].ToString()));
|
||||
}
|
||||
expected_reads += (fill_cache() ? 2 : 4);
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), expected_reads);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ParallelIO, DBBasicTestWithParallelIO,
|
||||
// Params are as follows -
|
||||
// Param 0 - Compressed cache enabled
|
||||
// Param 1 - Uncompressed cache enabled
|
||||
// Param 2 - Data compression enabled
|
||||
// Param 3 - ReadOptions::fill_cache
|
||||
::testing::Values(std::make_tuple(false, true, true, true),
|
||||
std::make_tuple(true, true, true, true),
|
||||
std::make_tuple(false, true, false, true),
|
||||
std::make_tuple(false, true, true, false),
|
||||
std::make_tuple(true, true, true, false),
|
||||
std::make_tuple(false, true, false, false)));
|
||||
|
||||
class DBBasicTestWithTimestampWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBBasicTestWithTimestampWithParam()
|
||||
: DBTestBase("/db_basic_test_with_timestamp") {}
|
||||
|
||||
protected:
|
||||
class TestComparator : public Comparator {
|
||||
private:
|
||||
const Comparator* cmp_without_ts_;
|
||||
|
||||
public:
|
||||
explicit TestComparator(size_t ts_sz)
|
||||
: Comparator(ts_sz), cmp_without_ts_(nullptr) {
|
||||
cmp_without_ts_ = BytewiseComparator();
|
||||
}
|
||||
|
||||
const char* Name() const override { return "TestComparator"; }
|
||||
|
||||
void FindShortSuccessor(std::string*) const override {}
|
||||
|
||||
void FindShortestSeparator(std::string*, const Slice&) const override {}
|
||||
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
int r = CompareWithoutTimestamp(a, b);
|
||||
if (r != 0 || 0 == timestamp_size()) {
|
||||
return r;
|
||||
}
|
||||
return CompareTimestamp(
|
||||
Slice(a.data() + a.size() - timestamp_size(), timestamp_size()),
|
||||
Slice(b.data() + b.size() - timestamp_size(), timestamp_size()));
|
||||
}
|
||||
|
||||
int CompareWithoutTimestamp(const Slice& a, const Slice& b) const override {
|
||||
assert(a.size() >= timestamp_size());
|
||||
assert(b.size() >= timestamp_size());
|
||||
Slice k1 = StripTimestampFromUserKey(a, timestamp_size());
|
||||
Slice k2 = StripTimestampFromUserKey(b, timestamp_size());
|
||||
|
||||
return cmp_without_ts_->Compare(k1, k2);
|
||||
}
|
||||
|
||||
int CompareTimestamp(const Slice& ts1, const Slice& ts2) const override {
|
||||
if (!ts1.data() && !ts2.data()) {
|
||||
return 0;
|
||||
} else if (ts1.data() && !ts2.data()) {
|
||||
return 1;
|
||||
} else if (!ts1.data() && ts2.data()) {
|
||||
return -1;
|
||||
}
|
||||
assert(ts1.size() == ts2.size());
|
||||
uint64_t low1 = 0;
|
||||
uint64_t low2 = 0;
|
||||
uint64_t high1 = 0;
|
||||
uint64_t high2 = 0;
|
||||
auto* ptr1 = const_cast<Slice*>(&ts1);
|
||||
auto* ptr2 = const_cast<Slice*>(&ts2);
|
||||
if (!GetFixed64(ptr1, &low1) || !GetFixed64(ptr1, &high1) ||
|
||||
!GetFixed64(ptr2, &low2) || !GetFixed64(ptr2, &high2)) {
|
||||
assert(false);
|
||||
}
|
||||
if (high1 < high2) {
|
||||
return 1;
|
||||
} else if (high1 > high2) {
|
||||
return -1;
|
||||
}
|
||||
if (low1 < low2) {
|
||||
return 1;
|
||||
} else if (low1 > low2) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
Slice EncodeTimestamp(uint64_t low, uint64_t high, std::string* ts) {
|
||||
assert(nullptr != ts);
|
||||
ts->clear();
|
||||
PutFixed64(ts, low);
|
||||
PutFixed64(ts, high);
|
||||
assert(ts->size() == sizeof(low) + sizeof(high));
|
||||
return Slice(*ts);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampWithParam, PutAndGet) {
|
||||
const int kNumKeysPerFile = 8192;
|
||||
const size_t kNumTimestamps = 6;
|
||||
bool memtable_only = GetParam();
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
std::string tmp;
|
||||
size_t ts_sz = EncodeTimestamp(0, 0, &tmp).size();
|
||||
TestComparator test_cmp(ts_sz);
|
||||
options.comparator = &test_cmp;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(
|
||||
10 /*bits_per_key*/, false /*use_block_based_builder*/));
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(2, num_cfs);
|
||||
std::vector<std::string> write_ts_strs(kNumTimestamps);
|
||||
std::vector<std::string> read_ts_strs(kNumTimestamps);
|
||||
std::vector<Slice> write_ts_list;
|
||||
std::vector<Slice> read_ts_list;
|
||||
|
||||
for (size_t i = 0; i != kNumTimestamps; ++i) {
|
||||
write_ts_list.emplace_back(EncodeTimestamp(i * 2, 0, &write_ts_strs[i]));
|
||||
read_ts_list.emplace_back(EncodeTimestamp(1 + i * 2, 0, &read_ts_strs[i]));
|
||||
const Slice& write_ts = write_ts_list.back();
|
||||
WriteOptions wopts;
|
||||
wopts.timestamp = &write_ts;
|
||||
for (int cf = 0; cf != static_cast<int>(num_cfs); ++cf) {
|
||||
for (size_t j = 0; j != (kNumKeysPerFile - 1) / kNumTimestamps; ++j) {
|
||||
ASSERT_OK(Put(cf, "key" + std::to_string(j),
|
||||
"value_" + std::to_string(j) + "_" + std::to_string(i),
|
||||
wopts));
|
||||
}
|
||||
if (!memtable_only) {
|
||||
ASSERT_OK(Flush(cf));
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto& verify_db_func = [&]() {
|
||||
for (size_t i = 0; i != kNumTimestamps; ++i) {
|
||||
ReadOptions ropts;
|
||||
ropts.timestamp = &read_ts_list[i];
|
||||
for (int cf = 0; cf != static_cast<int>(num_cfs); ++cf) {
|
||||
ColumnFamilyHandle* cfh = handles_[cf];
|
||||
for (size_t j = 0; j != (kNumKeysPerFile - 1) / kNumTimestamps; ++j) {
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ropts, cfh, "key" + std::to_string(j), &value));
|
||||
ASSERT_EQ("value_" + std::to_string(j) + "_" + std::to_string(i),
|
||||
value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
verify_db_func();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Timestamp, DBBasicTestWithTimestampWithParam,
|
||||
::testing::Bool());
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+87
-11
@@ -173,7 +173,7 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
delete iter;
|
||||
iter = nullptr;
|
||||
|
||||
// Release interators and access cache again.
|
||||
// Release iterators and access cache again.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
iterators[i].reset();
|
||||
CheckCacheCounters(options, 0, 0, 0, 0);
|
||||
@@ -346,14 +346,14 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
// 200 bytes are enough to hold the first two blocks
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(200, 0, false);
|
||||
// 500 bytes are enough to hold the first two blocks
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(500, 0, false);
|
||||
table_options.block_cache = cache;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20, true));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(1, "key", "val"));
|
||||
ASSERT_OK(Put(1, "longer_key", "val"));
|
||||
// Create a new table
|
||||
ASSERT_OK(Flush(1));
|
||||
size_t index_bytes_insert =
|
||||
@@ -365,9 +365,14 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
ASSERT_EQ(cache->GetUsage(), index_bytes_insert + filter_bytes_insert);
|
||||
// set the cache capacity to the current usage
|
||||
cache->SetCapacity(index_bytes_insert + filter_bytes_insert);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT), 0);
|
||||
// The index eviction statistics were broken by the refactoring that moved
|
||||
// the index readers out of the block cache. Disabling these until we can
|
||||
// bring the stats back.
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT), 0);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT), 0);
|
||||
ASSERT_OK(Put(1, "key2", "val"));
|
||||
// Note that the second key needs to be no longer than the first one.
|
||||
// Otherwise the second index block may not fit in cache.
|
||||
ASSERT_OK(Put(1, "key", "val"));
|
||||
// Create a new table
|
||||
ASSERT_OK(Flush(1));
|
||||
// cache evicted old index and block entries
|
||||
@@ -375,8 +380,11 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
index_bytes_insert);
|
||||
ASSERT_GT(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_INSERT),
|
||||
filter_bytes_insert);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT),
|
||||
index_bytes_insert);
|
||||
// The index eviction statistics were broken by the refactoring that moved
|
||||
// the index readers out of the block cache. Disabling these until we can
|
||||
// bring the stats back.
|
||||
// ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_INDEX_BYTES_EVICT),
|
||||
// index_bytes_insert);
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOCK_CACHE_FILTER_BYTES_EVICT),
|
||||
filter_bytes_insert);
|
||||
}
|
||||
@@ -395,9 +403,9 @@ class MockCache : public LRUCache {
|
||||
false /*strict_capacity_limit*/, 0.0 /*high_pri_pool_ratio*/) {
|
||||
}
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle, Priority priority) override {
|
||||
Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value), Handle** handle,
|
||||
Priority priority) override {
|
||||
if (priority == Priority::LOW) {
|
||||
low_pri_insert_count++;
|
||||
} else {
|
||||
@@ -631,6 +639,74 @@ TEST_F(DBBlockCacheTest, CompressedCache) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
const int kNumFiles = 4;
|
||||
const int kNumEntriesPerFile = 128;
|
||||
const int kNumBytesPerEntry = 1024;
|
||||
|
||||
// Try all the available libraries that support dictionary compression
|
||||
std::vector<CompressionType> compression_types;
|
||||
#ifdef ZLIB
|
||||
compression_types.push_back(kZlibCompression);
|
||||
#endif // ZLIB
|
||||
#if LZ4_VERSION_NUMBER >= 10400
|
||||
compression_types.push_back(kLZ4Compression);
|
||||
compression_types.push_back(kLZ4HCCompression);
|
||||
#endif // LZ4_VERSION_NUMBER >= 10400
|
||||
#if ZSTD_VERSION_NUMBER >= 500
|
||||
compression_types.push_back(kZSTD);
|
||||
#endif // ZSTD_VERSION_NUMBER >= 500
|
||||
Random rnd(301);
|
||||
for (auto compression_type : compression_types) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = compression_type;
|
||||
options.compression_opts.max_dict_bytes = 4096;
|
||||
options.create_if_missing = true;
|
||||
options.num_levels = 2;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache.reset(new MockCache());
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
ASSERT_EQ(i, NumTableFilesAtLevel(0, 0));
|
||||
for (int j = 0; j < kNumEntriesPerFile; ++j) {
|
||||
std::string value = RandomString(&rnd, kNumBytesPerEntry);
|
||||
ASSERT_OK(Put(Key(j * kNumFiles + i), value.c_str()));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
// Seek to a key in a file. It should cause the SST's dictionary meta-block
|
||||
// to be read.
|
||||
RecordCacheCounters(options);
|
||||
ASSERT_EQ(0,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_ADD));
|
||||
ASSERT_EQ(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
ReadOptions read_options;
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(kNumFiles * kNumEntriesPerFile - 1)));
|
||||
// Two blocks missed/added: dictionary and data block
|
||||
// One block hit: index since it's prefetched
|
||||
CheckCacheCounters(options, 2 /* expected_misses */, 1 /* expected_hits */,
|
||||
2 /* expected_inserts */, 0 /* expected_failures */);
|
||||
ASSERT_EQ(1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_ADD));
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -32,7 +32,7 @@ class DBBloomFilterTestWithParam
|
||||
public:
|
||||
DBBloomFilterTestWithParam() : DBTestBase("/db_bloom_filter_tests") {}
|
||||
|
||||
~DBBloomFilterTestWithParam() {}
|
||||
~DBBloomFilterTestWithParam() override {}
|
||||
|
||||
void SetUp() override {
|
||||
use_block_based_filter_ = std::get<0>(GetParam());
|
||||
@@ -642,7 +642,7 @@ class WrappedBloom : public FilterPolicy {
|
||||
explicit WrappedBloom(int bits_per_key)
|
||||
: filter_(NewBloomFilterPolicy(bits_per_key)), counter_(0) {}
|
||||
|
||||
~WrappedBloom() { delete filter_; }
|
||||
~WrappedBloom() override { delete filter_; }
|
||||
|
||||
const char* Name() const override { return "WrappedRocksDbFilterPolicy"; }
|
||||
|
||||
@@ -786,6 +786,75 @@ TEST_F(DBBloomFilterTest, PrefixExtractorBlockFilter) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, MemtableWholeKeyBloomFilter) {
|
||||
// regression test for #2743. the range delete tombstones in memtable should
|
||||
// be added even when Get() skips searching due to its prefix bloom filter
|
||||
const int kMemtableSize = 1 << 20; // 1MB
|
||||
const int kMemtablePrefixFilterSize = 1 << 13; // 8KB
|
||||
const int kPrefixLen = 4;
|
||||
Options options = CurrentOptions();
|
||||
options.memtable_prefix_bloom_size_ratio =
|
||||
static_cast<double>(kMemtablePrefixFilterSize) / kMemtableSize;
|
||||
options.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(kPrefixLen));
|
||||
options.write_buffer_size = kMemtableSize;
|
||||
options.memtable_whole_key_filtering = false;
|
||||
Reopen(options);
|
||||
std::string key1("AAAABBBB");
|
||||
std::string key2("AAAACCCC"); // not in DB
|
||||
std::string key3("AAAADDDD");
|
||||
std::string key4("AAAAEEEE");
|
||||
std::string value1("Value1");
|
||||
std::string value3("Value3");
|
||||
std::string value4("Value4");
|
||||
|
||||
ASSERT_OK(Put(key1, value1, WriteOptions()));
|
||||
|
||||
// check memtable bloom stats
|
||||
ASSERT_EQ("NOT_FOUND", Get(key2));
|
||||
ASSERT_EQ(0, get_perf_context()->bloom_memtable_miss_count);
|
||||
// same prefix, bloom filter false positive
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_hit_count);
|
||||
|
||||
// enable whole key bloom filter
|
||||
options.memtable_whole_key_filtering = true;
|
||||
Reopen(options);
|
||||
// check memtable bloom stats
|
||||
ASSERT_OK(Put(key3, value3, WriteOptions()));
|
||||
ASSERT_EQ("NOT_FOUND", Get(key2));
|
||||
// whole key bloom filter kicks in and determines it's a miss
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_miss_count);
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_hit_count);
|
||||
|
||||
// verify whole key filtering does not depend on prefix_extractor
|
||||
options.prefix_extractor.reset();
|
||||
Reopen(options);
|
||||
// check memtable bloom stats
|
||||
ASSERT_OK(Put(key4, value4, WriteOptions()));
|
||||
ASSERT_EQ("NOT_FOUND", Get(key2));
|
||||
// whole key bloom filter kicks in and determines it's a miss
|
||||
ASSERT_EQ(2, get_perf_context()->bloom_memtable_miss_count);
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_memtable_hit_count);
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, MemtablePrefixBloomOutOfDomain) {
|
||||
constexpr size_t kPrefixSize = 8;
|
||||
const std::string kKey = "key";
|
||||
assert(kKey.size() < kPrefixSize);
|
||||
Options options = CurrentOptions();
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(kPrefixSize));
|
||||
options.memtable_prefix_bloom_size_ratio = 0.25;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put(kKey, "v"));
|
||||
ASSERT_EQ("v", Get(kKey));
|
||||
std::unique_ptr<Iterator> iter(dbfull()->NewIterator(ReadOptions()));
|
||||
iter->Seek(kKey);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(kKey, iter->key());
|
||||
iter->SeekForPrev(kKey);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(kKey, iter->key());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class BloomStatsTestWithParam
|
||||
: public DBBloomFilterTest,
|
||||
@@ -823,7 +892,7 @@ class BloomStatsTestWithParam
|
||||
DestroyAndReopen(options_);
|
||||
}
|
||||
|
||||
~BloomStatsTestWithParam() {
|
||||
~BloomStatsTestWithParam() override {
|
||||
get_perf_context()->Reset();
|
||||
Destroy(options_);
|
||||
}
|
||||
@@ -935,13 +1004,16 @@ TEST_P(BloomStatsTestWithParam, BloomStatsTestWithIter) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(value3, iter->value().ToString());
|
||||
ASSERT_EQ(2, get_perf_context()->bloom_sst_hit_count);
|
||||
// The seek doesn't check block-based bloom filter because last index key
|
||||
// starts with the same prefix we're seeking to.
|
||||
uint64_t expected_hits = use_block_based_builder_ ? 1 : 2;
|
||||
ASSERT_EQ(expected_hits, get_perf_context()->bloom_sst_hit_count);
|
||||
|
||||
iter->Seek(key2);
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_EQ(1, get_perf_context()->bloom_sst_miss_count);
|
||||
ASSERT_EQ(2, get_perf_context()->bloom_sst_hit_count);
|
||||
ASSERT_EQ(expected_hits, get_perf_context()->bloom_sst_hit_count);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(BloomStatsTestWithParam, BloomStatsTestWithParam,
|
||||
@@ -1023,6 +1095,8 @@ TEST_F(DBBloomFilterTest, PrefixScan) {
|
||||
options.max_background_compactions = 2;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
assert(!options.unordered_write);
|
||||
// It is incompatible with allow_concurrent_memtable_write=false
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -1266,6 +1340,8 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.filter_policy.reset(
|
||||
NewBloomFilterPolicy(10, use_block_based_builder));
|
||||
table_options.index_shortening = BlockBasedTableOptions::
|
||||
IndexShorteningMode::kShortenSeparatorsAndSuccessor;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
|
||||
@@ -63,33 +63,33 @@ INSTANTIATE_TEST_CASE_P(DBTestCompactionFilterWithCompactOption,
|
||||
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "KeepFilter"; }
|
||||
const char* Name() const override { return "KeepFilter"; }
|
||||
};
|
||||
|
||||
class DeleteFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilter"; }
|
||||
const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
class DeleteISFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int /*level*/, const Slice& key, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& key, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
cfilter_count++;
|
||||
int i = std::stoi(key.ToString());
|
||||
if (i > 5 && i <= 105) {
|
||||
@@ -98,20 +98,18 @@ class DeleteISFilter : public CompactionFilter {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return true; }
|
||||
bool IgnoreSnapshots() const override { return true; }
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilter"; }
|
||||
const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
// Skip x if floor(x/10) is even, use range skips. Requires that keys are
|
||||
// zero-padded to length 10.
|
||||
class SkipEvenFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual Decision FilterV2(int /*level*/, const Slice& key,
|
||||
ValueType /*value_type*/,
|
||||
const Slice& /*existing_value*/,
|
||||
std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
Decision FilterV2(int /*level*/, const Slice& key, ValueType /*value_type*/,
|
||||
const Slice& /*existing_value*/, std::string* /*new_value*/,
|
||||
std::string* skip_until) const override {
|
||||
cfilter_count++;
|
||||
int i = std::stoi(key.ToString());
|
||||
if (i / 10 % 2 == 0) {
|
||||
@@ -124,22 +122,22 @@ class SkipEvenFilter : public CompactionFilter {
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return true; }
|
||||
bool IgnoreSnapshots() const override { return true; }
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilter"; }
|
||||
const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
db_test->env_->addon_time_.fetch_add(1000);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayFilter"; }
|
||||
const char* Name() const override { return "DelayFilter"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
@@ -149,13 +147,13 @@ class ConditionalFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ConditionalFilter(const std::string* filtered_value)
|
||||
: filtered_value_(filtered_value) {}
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/, const Slice& value,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& value,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return value.ToString() == *filtered_value_;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "ConditionalFilter"; }
|
||||
const char* Name() const override { return "ConditionalFilter"; }
|
||||
|
||||
private:
|
||||
const std::string* filtered_value_;
|
||||
@@ -165,16 +163,15 @@ class ChangeFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit ChangeFilter() {}
|
||||
|
||||
virtual bool Filter(int /*level*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/, std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* new_value, bool* value_changed) const override {
|
||||
assert(new_value != nullptr);
|
||||
*new_value = NEW_VALUE;
|
||||
*value_changed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "ChangeFilter"; }
|
||||
const char* Name() const override { return "ChangeFilter"; }
|
||||
};
|
||||
|
||||
class KeepFilterFactory : public CompactionFilterFactory {
|
||||
@@ -185,7 +182,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
check_context_cf_id_(check_context_cf_id),
|
||||
compaction_filter_created_(false) {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (check_context_) {
|
||||
EXPECT_EQ(expect_full_compaction_.load(), context.is_full_compaction);
|
||||
@@ -200,7 +197,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
|
||||
bool compaction_filter_created() const { return compaction_filter_created_; }
|
||||
|
||||
virtual const char* Name() const override { return "KeepFilterFactory"; }
|
||||
const char* Name() const override { return "KeepFilterFactory"; }
|
||||
bool check_context_;
|
||||
bool check_context_cf_id_;
|
||||
std::atomic_bool expect_full_compaction_;
|
||||
@@ -211,7 +208,7 @@ class KeepFilterFactory : public CompactionFilterFactory {
|
||||
|
||||
class DeleteFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (context.is_manual_compaction) {
|
||||
return std::unique_ptr<CompactionFilter>(new DeleteFilter());
|
||||
@@ -220,13 +217,13 @@ class DeleteFilterFactory : public CompactionFilterFactory {
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
};
|
||||
|
||||
// Delete Filter Factory which ignores snapshots
|
||||
class DeleteISFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (context.is_manual_compaction) {
|
||||
return std::unique_ptr<CompactionFilter>(new DeleteISFilter());
|
||||
@@ -235,12 +232,12 @@ class DeleteISFilterFactory : public CompactionFilterFactory {
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
};
|
||||
|
||||
class SkipEvenFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (context.is_manual_compaction) {
|
||||
return std::unique_ptr<CompactionFilter>(new SkipEvenFilter());
|
||||
@@ -249,18 +246,18 @@ class SkipEvenFilterFactory : public CompactionFilterFactory {
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "SkipEvenFilterFactory"; }
|
||||
const char* Name() const override { return "SkipEvenFilterFactory"; }
|
||||
};
|
||||
|
||||
class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new DelayFilter(db_test));
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "DelayFilterFactory"; }
|
||||
const char* Name() const override { return "DelayFilterFactory"; }
|
||||
|
||||
private:
|
||||
DBTestBase* db_test;
|
||||
@@ -271,15 +268,13 @@ class ConditionalFilterFactory : public CompactionFilterFactory {
|
||||
explicit ConditionalFilterFactory(const Slice& filtered_value)
|
||||
: filtered_value_(filtered_value.ToString()) {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(
|
||||
new ConditionalFilter(&filtered_value_));
|
||||
}
|
||||
|
||||
virtual const char* Name() const override {
|
||||
return "ConditionalFilterFactory";
|
||||
}
|
||||
const char* Name() const override { return "ConditionalFilterFactory"; }
|
||||
|
||||
private:
|
||||
std::string filtered_value_;
|
||||
@@ -289,12 +284,12 @@ class ChangeFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit ChangeFilterFactory() {}
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& /*context*/) override {
|
||||
return std::unique_ptr<CompactionFilter>(new ChangeFilter());
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "ChangeFilterFactory"; }
|
||||
const char* Name() const override { return "ChangeFilterFactory"; }
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -357,7 +352,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(total, 100000);
|
||||
ASSERT_EQ(count, 1);
|
||||
ASSERT_EQ(count, 0);
|
||||
|
||||
// overwrite all the 100K keys once again.
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
@@ -664,7 +659,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_EQ(total, 700);
|
||||
ASSERT_EQ(count, 1);
|
||||
ASSERT_EQ(count, 0);
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -699,44 +694,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextCfId) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Compaction filters should only be applied to records that are newer than the
|
||||
// latest snapshot. This test inserts records and applies a delete filter.
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterSnapshot) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_filter_factory = std::make_shared<DeleteFilterFactory>();
|
||||
options.disable_auto_compactions = true;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Put some data.
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Put(ToString(table * 100 + i), "val");
|
||||
}
|
||||
Flush();
|
||||
|
||||
if (table == 0) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
}
|
||||
}
|
||||
assert(snapshot != nullptr);
|
||||
|
||||
cfilter_count = 0;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
// The filter should delete 10 records.
|
||||
ASSERT_EQ(30U, cfilter_count);
|
||||
|
||||
// Release the snapshot and compact again -> now all records should be
|
||||
// removed.
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(0U, CountLiveFiles());
|
||||
}
|
||||
|
||||
// Compaction filters should only be applied to records that are newer than the
|
||||
// latest snapshot. However, if the compaction filter asks to ignore snapshots
|
||||
// records newer than the snapshot will also be processed
|
||||
// Compaction filters aplies to all records, regardless snapshots.
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
std::string five = ToString(5);
|
||||
Options options = CurrentOptions();
|
||||
@@ -874,6 +832,38 @@ TEST_F(DBTestCompactionFilter, SkipUntilWithBloomFilter) {
|
||||
EXPECT_EQ("v50", val);
|
||||
}
|
||||
|
||||
class TestNotSupportedFilter : public CompactionFilter {
|
||||
public:
|
||||
bool Filter(int /*level*/, const Slice& /*key*/, const Slice& /*value*/,
|
||||
std::string* /*new_value*/,
|
||||
bool* /*value_changed*/) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "NotSupported"; }
|
||||
bool IgnoreSnapshots() const override { return false; }
|
||||
};
|
||||
|
||||
TEST_F(DBTestCompactionFilter, IgnoreSnapshotsFalse) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_filter = new TestNotSupportedFilter();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Put("a", "v10");
|
||||
Put("z", "v20");
|
||||
Flush();
|
||||
|
||||
Put("a", "v10");
|
||||
Put("z", "v20");
|
||||
Flush();
|
||||
|
||||
// Comapction should fail because IgnoreSnapshots() = false
|
||||
EXPECT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
.IsNotSupported());
|
||||
|
||||
delete options.compaction_filter;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+547
-31
@@ -10,10 +10,12 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/concurrent_task_limiter.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/utilities/convenience.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/concurrent_task_limiter_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -53,9 +55,9 @@ namespace {
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
~FlushedFileCollector() override {}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
void OnFlushCompleted(DB* /*db*/, const FlushJobInfo& info) override {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(info.file_path);
|
||||
}
|
||||
@@ -85,24 +87,23 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~CompactionStatsCollector() {}
|
||||
~CompactionStatsCollector() override {}
|
||||
|
||||
virtual void OnCompactionCompleted(DB* /* db */,
|
||||
const CompactionJobInfo& info) override {
|
||||
void OnCompactionCompleted(DB* /* db */,
|
||||
const CompactionJobInfo& info) override {
|
||||
int k = static_cast<int>(info.compaction_reason);
|
||||
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
|
||||
assert(k >= 0 && k < num_of_reasons);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
|
||||
virtual void OnExternalFileIngested(DB* /* db */,
|
||||
const ExternalFileIngestionInfo& /* info */) override {
|
||||
void OnExternalFileIngested(
|
||||
DB* /* db */, const ExternalFileIngestionInfo& /* info */) override {
|
||||
int k = static_cast<int>(CompactionReason::kExternalSstIngestion);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
|
||||
virtual void OnFlushCompleted(DB* /* db */,
|
||||
const FlushJobInfo& /* info */) override {
|
||||
void OnFlushCompleted(DB* /* db */, const FlushJobInfo& /* info */) override {
|
||||
int k = static_cast<int>(CompactionReason::kFlush);
|
||||
compaction_completed_[k]++;
|
||||
}
|
||||
@@ -352,7 +353,8 @@ TEST_P(DBCompactionTestWithParam, CompactionsPreserveDeletes) {
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
cro.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
@@ -412,6 +414,7 @@ TEST_F(DBCompactionTest, SkipStatsUpdateTest) {
|
||||
|
||||
// Reopen the DB with stats-update disabled
|
||||
options.skip_stats_update_on_db_open = true;
|
||||
options.max_open_files = 20;
|
||||
env_->random_file_open_counter_.store(0);
|
||||
Reopen(options);
|
||||
|
||||
@@ -437,7 +440,7 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.new_table_reader_for_compaction_inputs = true;
|
||||
options.max_open_files = 100;
|
||||
options.max_open_files = 20;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
@@ -466,15 +469,16 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// preloading iterator issues one table cache lookup and create
|
||||
// a new table reader.
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
// a new table reader, if not preloaded.
|
||||
int old_num_table_cache_lookup = num_table_cache_lookup;
|
||||
ASSERT_GE(num_table_cache_lookup, 1);
|
||||
ASSERT_EQ(num_new_table_reader, 1);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(k), Get(Key(k)));
|
||||
// lookup iterator from table cache and no need to create a new one.
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
ASSERT_EQ(old_num_table_cache_lookup + num_table_cache_lookup, 2);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
}
|
||||
}
|
||||
@@ -487,7 +491,10 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
// a new table reader. One file is created for flush and one for compaction.
|
||||
// Compaction inputs make no table cache look-up for data/range deletion
|
||||
// iterators
|
||||
ASSERT_EQ(num_table_cache_lookup, 2);
|
||||
// May preload table cache too.
|
||||
ASSERT_GE(num_table_cache_lookup, 2);
|
||||
int old_num_table_cache_lookup2 = num_table_cache_lookup;
|
||||
|
||||
// Create new iterator for:
|
||||
// (1) 1 for verifying flush results
|
||||
// (2) 3 for compaction input files
|
||||
@@ -497,7 +504,7 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(1), Get(Key(1)));
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 3);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
@@ -505,18 +512,20 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
db_->CompactRange(cro, nullptr, nullptr);
|
||||
// Only verifying compaction outputs issues one table cache lookup
|
||||
// for both data block and range deletion block).
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
// May preload table cache too.
|
||||
ASSERT_GE(num_table_cache_lookup, 1);
|
||||
old_num_table_cache_lookup2 = num_table_cache_lookup;
|
||||
// One for compaction input, one for verifying compaction results.
|
||||
ASSERT_EQ(num_new_table_reader, 2);
|
||||
|
||||
num_table_cache_lookup = 0;
|
||||
num_new_table_reader = 0;
|
||||
ASSERT_EQ(Key(1), Get(Key(1)));
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 2);
|
||||
ASSERT_EQ(num_new_table_reader, 0);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -1594,7 +1603,8 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
// Note that we don't delete level 0 files
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = 1;
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
|
||||
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_OK(
|
||||
DeleteFilesInRange(db_, db_->DefaultColumnFamily(), nullptr, nullptr));
|
||||
@@ -2251,6 +2261,8 @@ TEST_P(DBCompactionTestWithParam, ConvertCompactionStyle) {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = 0;
|
||||
// cannot use kForceOptimized here because the compaction here is expected
|
||||
// to generate one output file
|
||||
compact_options.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForce;
|
||||
compact_options.exclusive_manual_compaction = exclusive_manual_compaction_;
|
||||
@@ -3030,7 +3042,7 @@ TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
|
||||
// then compacte the bottommost level L3=>L3 (non trivial move)
|
||||
compact_options = CompactRangeOptions();
|
||||
compact_options.bottommost_level_compaction =
|
||||
BottommostLevelCompaction::kForce;
|
||||
BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,0,1", FilesPerLevel(0));
|
||||
ASSERT_EQ(trivial_move, 4);
|
||||
@@ -3339,7 +3351,7 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
options.level0_file_num_compaction_trigger = kNumLevelFiles;
|
||||
// inflate it a bit to account for key/metadata overhead
|
||||
options.target_file_size_base = 120 * kNumKeysPerFile * kValueSize / 100;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"one"}, options);
|
||||
|
||||
Random rnd(301);
|
||||
const Snapshot* snapshot = nullptr;
|
||||
@@ -3370,10 +3382,12 @@ TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
// just need to bump seqnum so ReleaseSnapshot knows the newest key in the SST
|
||||
// files does not need to be preserved in case of a future snapshot.
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
ASSERT_NE(kMaxSequenceNumber, dbfull()->bottommost_files_mark_threshold_);
|
||||
// release snapshot and wait for compactions to finish. Single-file
|
||||
// compactions should be triggered, which reduce the size of each bottom-level
|
||||
// file without changing file count.
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
ASSERT_EQ(kMaxSequenceNumber, dbfull()->bottommost_files_mark_threshold_);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
@@ -3506,6 +3520,234 @@ TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
|
||||
const int kNumKeysPerFile = 32;
|
||||
const int kNumLevelFiles = 2;
|
||||
const int kValueSize = 100;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.periodic_compaction_seconds = 48 * 60 * 60; // 2 days
|
||||
options.max_open_files = -1; // needed for ttl compaction
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int periodic_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), RandomString(&rnd, kValueSize)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_EQ("2", FilesPerLevel());
|
||||
ASSERT_EQ(0, periodic_compactions);
|
||||
|
||||
// Add 50 hours and do a write
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Assert that the files stay in the same level
|
||||
ASSERT_EQ("3", FilesPerLevel());
|
||||
// The two old files go through the periodic compaction process
|
||||
ASSERT_EQ(2, periodic_compactions);
|
||||
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_EQ("0,3", FilesPerLevel());
|
||||
|
||||
// Add another 50 hours and do another write
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
ASSERT_OK(Put("b", "2"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ("1,3", FilesPerLevel());
|
||||
// The three old files now go through the periodic compaction process. 2 + 3.
|
||||
ASSERT_EQ(5, periodic_compactions);
|
||||
|
||||
// Add another 50 hours and do another write
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
ASSERT_OK(Put("c", "3"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ("2,3", FilesPerLevel());
|
||||
// The four old files now go through the periodic compaction process. 5 + 4.
|
||||
ASSERT_EQ(9, periodic_compactions);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
|
||||
// This test makes sure that periodic compactions are working with a DB
|
||||
// where file_creation_time of some files is 0.
|
||||
// After compactions the new files are created with a valid file_creation_time
|
||||
|
||||
const int kNumKeysPerFile = 32;
|
||||
const int kNumFiles = 4;
|
||||
const int kValueSize = 100;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.max_open_files = -1; // needed for ttl compaction
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int periodic_compactions = 0;
|
||||
bool set_file_creation_time_to_zero = true;
|
||||
bool set_creation_time_to_zero = true;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* arg) {
|
||||
TableProperties* props = reinterpret_cast<TableProperties*>(arg);
|
||||
if (set_file_creation_time_to_zero) {
|
||||
props->file_creation_time = 0;
|
||||
}
|
||||
if (set_creation_time_to_zero) {
|
||||
props->creation_time = 0;
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), RandomString(&rnd, kValueSize)));
|
||||
}
|
||||
Flush();
|
||||
// Move the first two files to L2.
|
||||
if (i == 1) {
|
||||
MoveFilesToLevel(2);
|
||||
set_creation_time_to_zero = false;
|
||||
}
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_EQ("2,0,2", FilesPerLevel());
|
||||
ASSERT_EQ(0, periodic_compactions);
|
||||
|
||||
Close();
|
||||
|
||||
set_file_creation_time_to_zero = false;
|
||||
// Forward the clock by 2 days.
|
||||
env_->addon_time_.fetch_add(2 * 24 * 60 * 60);
|
||||
options.periodic_compaction_seconds = 1 * 24 * 60 * 60; // 1 day
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ("2,0,2", FilesPerLevel());
|
||||
// Make sure that all files go through periodic compaction.
|
||||
ASSERT_EQ(kNumFiles, periodic_compactions);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
const int kNumKeysPerFile = 32;
|
||||
const int kNumLevelFiles = 2;
|
||||
const int kValueSize = 100;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.ttl = 10 * 60 * 60; // 10 hours
|
||||
options.periodic_compaction_seconds = 48 * 60 * 60; // 2 days
|
||||
options.max_open_files = -1; // needed for both periodic and ttl compactions
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
env_->addon_time_.store(0);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int periodic_compactions = 0;
|
||||
int ttl_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
Compaction* compaction = reinterpret_cast<Compaction*>(arg);
|
||||
auto compaction_reason = compaction->compaction_reason();
|
||||
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
|
||||
periodic_compactions++;
|
||||
} else if (compaction_reason == CompactionReason::kTtl) {
|
||||
ttl_compactions++;
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), RandomString(&rnd, kValueSize)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
MoveFilesToLevel(3);
|
||||
|
||||
ASSERT_EQ("0,0,0,2", FilesPerLevel());
|
||||
ASSERT_EQ(0, periodic_compactions);
|
||||
ASSERT_EQ(0, ttl_compactions);
|
||||
|
||||
// Add some time greater than periodic_compaction_time.
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Files in the bottom level go through periodic compactions.
|
||||
ASSERT_EQ("1,0,0,2", FilesPerLevel());
|
||||
ASSERT_EQ(2, periodic_compactions);
|
||||
ASSERT_EQ(0, ttl_compactions);
|
||||
|
||||
// Add a little more time than ttl
|
||||
env_->addon_time_.fetch_add(11 * 60 * 60);
|
||||
ASSERT_OK(Put("b", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Notice that the previous file in level 1 falls down to the bottom level
|
||||
// due to ttl compactions, one level at a time.
|
||||
// And bottom level files don't get picked up for ttl compactions.
|
||||
ASSERT_EQ("1,0,0,3", FilesPerLevel());
|
||||
ASSERT_EQ(2, periodic_compactions);
|
||||
ASSERT_EQ(3, ttl_compactions);
|
||||
|
||||
// Add some time greater than periodic_compaction_time.
|
||||
env_->addon_time_.fetch_add(50 * 60 * 60);
|
||||
ASSERT_OK(Put("c", "1"));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Previous L0 file falls one level at a time to bottom level due to ttl.
|
||||
// And all 4 bottom files go through periodic compactions.
|
||||
ASSERT_EQ("1,0,0,4", FilesPerLevel());
|
||||
ASSERT_EQ(6, periodic_compactions);
|
||||
ASSERT_EQ(6, ttl_compactions);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DBCompactionTest, CompactRangeDelayedByL0FileCount) {
|
||||
// Verify that, when `CompactRangeOptions::allow_write_stall == false`, manual
|
||||
// compaction only triggers flush after it's sure stall won't be triggered for
|
||||
@@ -3648,11 +3890,17 @@ TEST_F(DBCompactionTest, CompactRangeShutdownWhileDelayed) {
|
||||
}
|
||||
Flush(1);
|
||||
}
|
||||
auto manual_compaction_thread = port::Thread([this]() {
|
||||
auto manual_compaction_thread = port::Thread([this, i]() {
|
||||
CompactRangeOptions cro;
|
||||
cro.allow_write_stall = false;
|
||||
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
|
||||
.IsShutdownInProgress());
|
||||
Status s = db_->CompactRange(cro, handles_[1], nullptr, nullptr);
|
||||
if (i == 0) {
|
||||
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
|
||||
.IsColumnFamilyDropped());
|
||||
} else {
|
||||
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
|
||||
.IsShutdownInProgress());
|
||||
}
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
@@ -3890,6 +4138,192 @@ TEST_F(DBCompactionTest, CompactionHasEmptyOutput) {
|
||||
ASSERT_EQ(2, collector->num_ssts_creation_started());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionLimiter) {
|
||||
const int kNumKeysPerFile = 10;
|
||||
const int kMaxBackgroundThreads = 64;
|
||||
|
||||
struct CompactionLimiter {
|
||||
std::string name;
|
||||
int limit_tasks;
|
||||
int max_tasks;
|
||||
int tasks;
|
||||
std::shared_ptr<ConcurrentTaskLimiter> limiter;
|
||||
};
|
||||
|
||||
std::vector<CompactionLimiter> limiter_settings;
|
||||
limiter_settings.push_back({"limiter_1", 1, 0, 0, nullptr});
|
||||
limiter_settings.push_back({"limiter_2", 2, 0, 0, nullptr});
|
||||
limiter_settings.push_back({"limiter_3", 3, 0, 0, nullptr});
|
||||
|
||||
for (auto& ls : limiter_settings) {
|
||||
ls.limiter.reset(NewConcurrentTaskLimiter(ls.name, ls.limit_tasks));
|
||||
}
|
||||
|
||||
std::shared_ptr<ConcurrentTaskLimiter> unique_limiter(
|
||||
NewConcurrentTaskLimiter("unique_limiter", -1));
|
||||
|
||||
const char* cf_names[] = {"default", "0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
|
||||
const int cf_count = sizeof cf_names / sizeof cf_names[0];
|
||||
|
||||
std::unordered_map<std::string, CompactionLimiter*> cf_to_limiter;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 110 * 1024; // 110KB
|
||||
options.arena_block_size = 4096;
|
||||
options.num_levels = 3;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.level0_slowdown_writes_trigger = 64;
|
||||
options.level0_stop_writes_trigger = 64;
|
||||
options.max_background_jobs = kMaxBackgroundThreads; // Enough threads
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
options.max_write_buffer_number = 10; // Enough memtables
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::vector<Options> option_vector;
|
||||
option_vector.reserve(cf_count);
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
ColumnFamilyOptions cf_opt(options);
|
||||
if (cf == 0) {
|
||||
// "Default" CF does't use compaction limiter
|
||||
cf_opt.compaction_thread_limiter = nullptr;
|
||||
} else if (cf == 1) {
|
||||
// "1" CF uses bypass compaction limiter
|
||||
unique_limiter->SetMaxOutstandingTask(-1);
|
||||
cf_opt.compaction_thread_limiter = unique_limiter;
|
||||
} else {
|
||||
// Assign limiter by mod
|
||||
auto& ls = limiter_settings[cf % 3];
|
||||
cf_opt.compaction_thread_limiter = ls.limiter;
|
||||
cf_to_limiter[cf_names[cf]] = &ls;
|
||||
}
|
||||
option_vector.emplace_back(DBOptions(options), cf_opt);
|
||||
}
|
||||
|
||||
for (int cf = 1; cf < cf_count; cf++) {
|
||||
CreateColumnFamilies({cf_names[cf]}, option_vector[cf]);
|
||||
}
|
||||
|
||||
ReopenWithColumnFamilies(std::vector<std::string>(cf_names,
|
||||
cf_names + cf_count),
|
||||
option_vector);
|
||||
|
||||
port::Mutex mutex;
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:BeforeCompaction", [&](void* arg) {
|
||||
const auto& cf_name = static_cast<ColumnFamilyData*>(arg)->GetName();
|
||||
auto iter = cf_to_limiter.find(cf_name);
|
||||
if (iter != cf_to_limiter.end()) {
|
||||
MutexLock l(&mutex);
|
||||
ASSERT_GE(iter->second->limit_tasks, ++iter->second->tasks);
|
||||
iter->second->max_tasks = std::max(iter->second->max_tasks,
|
||||
iter->second->limit_tasks);
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:AfterCompaction", [&](void* arg) {
|
||||
const auto& cf_name = static_cast<ColumnFamilyData*>(arg)->GetName();
|
||||
auto iter = cf_to_limiter.find(cf_name);
|
||||
if (iter != cf_to_limiter.end()) {
|
||||
MutexLock l(&mutex);
|
||||
ASSERT_GE(--iter->second->tasks, 0);
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Block all compact threads in thread pool.
|
||||
const size_t kTotalFlushTasks = kMaxBackgroundThreads / 4;
|
||||
const size_t kTotalCompactTasks = kMaxBackgroundThreads - kTotalFlushTasks;
|
||||
env_->SetBackgroundThreads((int)kTotalFlushTasks, Env::HIGH);
|
||||
env_->SetBackgroundThreads((int)kTotalCompactTasks, Env::LOW);
|
||||
|
||||
test::SleepingBackgroundTask sleeping_compact_tasks[kTotalCompactTasks];
|
||||
|
||||
// Block all compaction threads in thread pool.
|
||||
for (size_t i = 0; i < kTotalCompactTasks; i++) {
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_compact_tasks[i], Env::LOW);
|
||||
sleeping_compact_tasks[i].WaitUntilSleeping();
|
||||
}
|
||||
|
||||
int keyIndex = 0;
|
||||
|
||||
for (int n = 0; n < options.level0_file_num_compaction_trigger; n++) {
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(cf, Key(keyIndex++), ""));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put(cf, "", ""));
|
||||
}
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf]);
|
||||
}
|
||||
}
|
||||
|
||||
// Enough L0 files to trigger compaction
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, cf),
|
||||
options.level0_file_num_compaction_trigger);
|
||||
}
|
||||
|
||||
// Create more files for one column family, which triggers speed up
|
||||
// condition, all compactions will be scheduled.
|
||||
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(0, Key(i), ""));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put(0, "", ""));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
ASSERT_EQ(options.level0_file_num_compaction_trigger + num + 1,
|
||||
NumTableFilesAtLevel(0, 0));
|
||||
}
|
||||
|
||||
// All CFs are pending compaction
|
||||
ASSERT_EQ(cf_count, env_->GetThreadPoolQueueLen(Env::LOW));
|
||||
|
||||
// Unblock all compaction threads
|
||||
for (size_t i = 0; i < kTotalCompactTasks; i++) {
|
||||
sleeping_compact_tasks[i].WakeUp();
|
||||
sleeping_compact_tasks[i].WaitUntilDone();
|
||||
}
|
||||
|
||||
for (int cf = 0; cf < cf_count; cf++) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf]);
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// Max outstanding compact tasks reached limit
|
||||
for (auto& ls : limiter_settings) {
|
||||
ASSERT_EQ(ls.limit_tasks, ls.max_tasks);
|
||||
ASSERT_EQ(0, ls.limiter->GetOutstandingTask());
|
||||
}
|
||||
|
||||
// test manual compaction under a fully throttled limiter
|
||||
int cf_test = 1;
|
||||
unique_limiter->SetMaxOutstandingTask(0);
|
||||
|
||||
// flush one more file to cf 1
|
||||
for (int i = 0; i < kNumKeysPerFile; i++) {
|
||||
ASSERT_OK(Put(cf_test, Key(keyIndex++), ""));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put(cf_test, "", ""));
|
||||
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf_test]);
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0, cf_test));
|
||||
|
||||
Compact(cf_test, Key(0), Key(keyIndex));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithParam, DBCompactionTestWithParam,
|
||||
::testing::Values(std::make_tuple(1, true),
|
||||
std::make_tuple(1, false),
|
||||
@@ -3991,14 +4425,14 @@ class NoopMergeOperator : public MergeOperator {
|
||||
public:
|
||||
NoopMergeOperator() {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
bool FullMergeV2(const MergeOperationInput& /*merge_in*/,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
std::string val("bar");
|
||||
merge_out->new_value = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "Noop"; }
|
||||
const char* Name() const override { return "Noop"; }
|
||||
};
|
||||
|
||||
TEST_F(DBCompactionTest, PartialManualCompaction) {
|
||||
@@ -4027,7 +4461,7 @@ TEST_F(DBCompactionTest, PartialManualCompaction) {
|
||||
{{"max_compaction_bytes", std::to_string(max_compaction_bytes)}}));
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
}
|
||||
|
||||
@@ -4071,6 +4505,88 @@ TEST_F(DBCompactionTest, ManualCompactionFailsInReadOnlyMode) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// ManualCompactionBottomLevelOptimization tests the bottom level manual
|
||||
// compaction optimization to skip recompacting files created by Ln-1 to Ln
|
||||
// compaction
|
||||
TEST_F(DBCompactionTest, ManualCompactionBottomLevelOptimized) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.num_levels = 3;
|
||||
opts.level0_file_num_compaction_trigger = 5;
|
||||
opts.compression = kNoCompression;
|
||||
opts.merge_operator.reset(new NoopMergeOperator());
|
||||
opts.target_file_size_base = 1024;
|
||||
opts.max_bytes_for_level_multiplier = 2;
|
||||
opts.disable_auto_compactions = true;
|
||||
DestroyAndReopen(opts);
|
||||
ColumnFamilyHandleImpl* cfh =
|
||||
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily());
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
InternalStats* internal_stats_ptr = cfd->internal_stats();
|
||||
ASSERT_NE(internal_stats_ptr, nullptr);
|
||||
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 8; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
ASSERT_OK(
|
||||
Put("foo" + std::to_string(i * 10 + j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (auto i = 0; i < 8; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
ASSERT_OK(
|
||||
Put("bar" + std::to_string(i * 10 + j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
const std::vector<InternalStats::CompactionStats>& comp_stats =
|
||||
internal_stats_ptr->TEST_GetCompactionStats();
|
||||
int num = comp_stats[2].num_input_files_in_output_level;
|
||||
ASSERT_EQ(num, 0);
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
|
||||
const std::vector<InternalStats::CompactionStats>& comp_stats2 =
|
||||
internal_stats_ptr->TEST_GetCompactionStats();
|
||||
num = comp_stats2[2].num_input_files_in_output_level;
|
||||
ASSERT_EQ(num, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionDuringShutdown) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.level0_file_num_compaction_trigger = 2;
|
||||
opts.disable_auto_compactions = true;
|
||||
DestroyAndReopen(opts);
|
||||
ColumnFamilyHandleImpl* cfh =
|
||||
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily());
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
InternalStats* internal_stats_ptr = cfd->internal_stats();
|
||||
ASSERT_NE(internal_stats_ptr, nullptr);
|
||||
|
||||
Random rnd(301);
|
||||
for (auto i = 0; i < 2; ++i) {
|
||||
for (auto j = 0; j < 10; ++j) {
|
||||
ASSERT_OK(
|
||||
Put("foo" + std::to_string(i * 10 + j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun",
|
||||
[&](void* /*arg*/) {
|
||||
dbfull()->shutting_down_.store(true);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->error_handler_.GetBGError());
|
||||
}
|
||||
|
||||
// FixFileIngestionCompactionDeadlock tests and verifies that compaction and
|
||||
// file ingestion do not cause deadlock in the event of write stall triggered
|
||||
// by number of L0 files reaching level0_stop_writes_trigger.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
+8
-10
@@ -6,24 +6,20 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/filename.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -61,7 +57,9 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
}
|
||||
if (file_deletion_enabled) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Enabled");
|
||||
PurgeObsoleteFiles(job_context);
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
} else {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
|
||||
+136
-3
@@ -9,8 +9,8 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -290,6 +290,39 @@ TEST_F(DBFlushTest, ManualFlushFailsInReadOnlyMode) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, CFDropRaceWithWaitForFlushMemTables) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:AfterScheduleFlush",
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop"},
|
||||
{"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BackgroundCallFlush:start",
|
||||
"DBImpl::FlushMemTable:BeforeWaitForBgFlush"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(Put(1, "key", "value"));
|
||||
auto* cfd = static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
|
||||
port::Thread drop_cf_thr([&]() {
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop");
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_OK(dbfull()->DestroyColumnFamilyHandle(handles_[1]));
|
||||
handles_.resize(1);
|
||||
TEST_SYNC_POINT(
|
||||
"DBFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree");
|
||||
});
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
ASSERT_NOK(dbfull()->TEST_FlushMemTable(cfd, flush_opts));
|
||||
drop_cf_thr.join();
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -431,7 +464,7 @@ TEST_P(DBAtomicFlushTest, FlushMultipleCFs_DropSomeBeforeRequestFlush) {
|
||||
cf_ids.push_back(cf_id);
|
||||
}
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_TRUE(Flush(cf_ids).IsShutdownInProgress());
|
||||
ASSERT_TRUE(Flush(cf_ids).IsColumnFamilyDropped());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
@@ -488,6 +521,106 @@ TEST_P(DBAtomicFlushTest,
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, TriggerFlushAndClose) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
const int kNumKeysTriggerFlush = 4;
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(kNumKeysTriggerFlush));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
for (int i = 0; i != kNumKeysTriggerFlush; ++i) {
|
||||
ASSERT_OK(Put(0, "key" + std::to_string(i), "value" + std::to_string(i)));
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(Put(0, "key", "value"));
|
||||
Close();
|
||||
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
ASSERT_EQ("value", Get(0, "key"));
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, PickMemtablesRaceWithBackgroundFlush) {
|
||||
bool atomic_flush = GetParam();
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
options.max_write_buffer_number = 4;
|
||||
// Set min_write_buffer_number_to_merge to be greater than 1, so that
|
||||
// a column family with one memtable in the imm will not cause IsFlushPending
|
||||
// to return true when flush_requested_ is false.
|
||||
options.min_write_buffer_number_to_merge = 2;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(dbfull()->PauseBackgroundWork());
|
||||
ASSERT_OK(Put(0, "key00", "value00"));
|
||||
ASSERT_OK(Put(1, "key10", "value10"));
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts, handles_));
|
||||
ASSERT_OK(Put(0, "key01", "value01"));
|
||||
// Since max_write_buffer_number is 4, the following flush won't cause write
|
||||
// stall.
|
||||
ASSERT_OK(dbfull()->Flush(flush_opts));
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
ASSERT_OK(dbfull()->DestroyColumnFamilyHandle(handles_[1]));
|
||||
handles_[1] = nullptr;
|
||||
ASSERT_OK(dbfull()->ContinueBackgroundWork());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[0]));
|
||||
delete handles_[0];
|
||||
handles_.clear();
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, CFDropRaceWithWaitForFlushMemTables) {
|
||||
bool atomic_flush = GetParam();
|
||||
if (!atomic_flush) {
|
||||
return;
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = atomic_flush;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::AtomicFlushMemTables:AfterScheduleFlush",
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop"},
|
||||
{"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BackgroundCallFlush:start",
|
||||
"DBImpl::AtomicFlushMemTables:BeforeWaitForBgFlush"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_EQ(2, handles_.size());
|
||||
ASSERT_OK(Put(0, "key", "value"));
|
||||
ASSERT_OK(Put(1, "key", "value"));
|
||||
auto* cfd_default =
|
||||
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily())
|
||||
->cfd();
|
||||
auto* cfd_pikachu = static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
|
||||
port::Thread drop_cf_thr([&]() {
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:BeforeDrop");
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(handles_[1]));
|
||||
delete handles_[1];
|
||||
handles_.resize(1);
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::CFDropRaceWithWaitForFlushMemTables:AfterFree");
|
||||
});
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.allow_write_stall = true;
|
||||
ASSERT_OK(dbfull()->TEST_AtomicFlushMemTables({cfd_default, cfd_pikachu},
|
||||
flush_opts));
|
||||
drop_cf_thr.join();
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBFlushDirectIOTest, DBFlushDirectIOTest,
|
||||
testing::Bool());
|
||||
|
||||
|
||||
+1007
-279
File diff suppressed because it is too large.
Load diff
+667
-403
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -9,7 +9,7 @@
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "monitoring/thread_status_updater.h"
|
||||
|
||||
@@ -26,10 +26,16 @@ void DBImpl::TEST_SwitchWAL() {
|
||||
SwitchWAL(&write_context);
|
||||
}
|
||||
|
||||
bool DBImpl::TEST_WALBufferIsEmpty() {
|
||||
InstrumentedMutexLock wl(&log_write_mutex_);
|
||||
bool DBImpl::TEST_WALBufferIsEmpty(bool lock) {
|
||||
if (lock) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
log::Writer* cur_log_writer = logs_.back().writer;
|
||||
return cur_log_writer->TEST_BufferIsEmpty();
|
||||
auto res = cur_log_writer->TEST_BufferIsEmpty();
|
||||
if (lock) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
@@ -87,8 +93,9 @@ Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO)
|
||||
? level
|
||||
: level + 1;
|
||||
return RunManualCompaction(cfd, level, output_level, 0, 0, begin, end, true,
|
||||
disallow_trivial_move);
|
||||
return RunManualCompaction(cfd, level, output_level, CompactRangeOptions(),
|
||||
begin, end, true, disallow_trivial_move,
|
||||
port::kMaxUint64 /*max_file_num_to_ignore*/);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_SwitchMemtable(ColumnFamilyData* cfd) {
|
||||
@@ -101,7 +108,7 @@ Status DBImpl::TEST_SwitchMemtable(ColumnFamilyData* cfd) {
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_FlushMemTable(bool wait, bool allow_write_stall,
|
||||
ColumnFamilyHandle* cfh) {
|
||||
ColumnFamilyHandle* cfh) {
|
||||
FlushOptions fo;
|
||||
fo.wait = wait;
|
||||
fo.allow_write_stall = allow_write_stall;
|
||||
@@ -115,6 +122,16 @@ Status DBImpl::TEST_FlushMemTable(bool wait, bool allow_write_stall,
|
||||
return FlushMemTable(cfd, fo, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_opts) {
|
||||
return FlushMemTable(cfd, flush_opts, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_AtomicFlushMemTables(
|
||||
const autovector<ColumnFamilyData*>& cfds, const FlushOptions& flush_opts) {
|
||||
return AtomicFlushMemTables(cfds, flush_opts, FlushReason::kTest);
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForFlushMemTable(ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
@@ -143,13 +160,9 @@ Status DBImpl::TEST_WaitForCompact(bool wait_unscheduled) {
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
void DBImpl::TEST_LockMutex() {
|
||||
mutex_.Lock();
|
||||
}
|
||||
void DBImpl::TEST_LockMutex() { mutex_.Lock(); }
|
||||
|
||||
void DBImpl::TEST_UnlockMutex() {
|
||||
mutex_.Unlock();
|
||||
}
|
||||
void DBImpl::TEST_UnlockMutex() { mutex_.Unlock(); }
|
||||
|
||||
void* DBImpl::TEST_BeginWrite() {
|
||||
auto w = new WriteThread::Writer();
|
||||
@@ -243,10 +256,24 @@ size_t DBImpl::TEST_GetWalPreallocateBlockSize(
|
||||
return GetWalPreallocateBlockSize(write_buffer_size);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_WaitForTimedTaskRun(std::function<void()> callback) const {
|
||||
void DBImpl::TEST_WaitForDumpStatsRun(std::function<void()> callback) const {
|
||||
if (thread_dump_stats_ != nullptr) {
|
||||
thread_dump_stats_->TEST_WaitForRun(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void DBImpl::TEST_WaitForPersistStatsRun(std::function<void()> callback) const {
|
||||
if (thread_persist_stats_ != nullptr) {
|
||||
thread_persist_stats_->TEST_WaitForRun(callback);
|
||||
}
|
||||
}
|
||||
|
||||
bool DBImpl::TEST_IsPersistentStatsEnabled() const {
|
||||
return thread_persist_stats_ && thread_persist_stats_->IsRunning();
|
||||
}
|
||||
|
||||
size_t DBImpl::TEST_EstimateInMemoryStatsHistorySize() const {
|
||||
return EstimateInMemoryStatsHistorySize();
|
||||
}
|
||||
} // namespace rocksdb
|
||||
#endif // NDEBUG
|
||||
@@ -7,13 +7,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
@@ -6,18 +6,15 @@
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -108,7 +105,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
versions_->AddLiveFiles(&job_context->sst_live);
|
||||
if (doing_the_full_scan) {
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
dbname_);
|
||||
std::set<std::string> paths;
|
||||
for (size_t path_id = 0; path_id < immutable_db_options_.db_paths.size();
|
||||
path_id++) {
|
||||
@@ -152,8 +149,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
|
||||
// TODO(icanadi) clean up this mess to avoid having one-off "/" prefixes
|
||||
job_context->full_scan_candidate_files.emplace_back(
|
||||
"/" + file, path);
|
||||
job_context->full_scan_candidate_files.emplace_back("/" + file, path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,8 +159,8 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
env_->GetChildren(immutable_db_options_.wal_dir,
|
||||
&log_files); // Ignore errors
|
||||
for (const std::string& log_file : log_files) {
|
||||
job_context->full_scan_candidate_files.emplace_back(log_file,
|
||||
immutable_db_options_.wal_dir);
|
||||
job_context->full_scan_candidate_files.emplace_back(
|
||||
log_file, immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
// Add info log files in db_log_dir
|
||||
@@ -174,8 +170,8 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// Ignore errors
|
||||
env_->GetChildren(immutable_db_options_.db_log_dir, &info_log_files);
|
||||
for (std::string& log_file : info_log_files) {
|
||||
job_context->full_scan_candidate_files.emplace_back(log_file,
|
||||
immutable_db_options_.db_log_dir);
|
||||
job_context->full_scan_candidate_files.emplace_back(
|
||||
log_file, immutable_db_options_.db_log_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,14 +256,15 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
const std::string& path_to_sync,
|
||||
FileType type, uint64_t number) {
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile) {
|
||||
if (type == kTableFile || type == kLogFile) {
|
||||
file_deletion_status =
|
||||
DeleteSSTFile(&immutable_db_options_, fname, path_to_sync);
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path_);
|
||||
} else {
|
||||
file_deletion_status = env_->DeleteFile(fname);
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DeleteObsoleteFileImpl:AfterDeletion",
|
||||
&file_deletion_status);
|
||||
&file_deletion_status);
|
||||
if (file_deletion_status.ok()) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[JOB %d] Delete %s type=%d #%" PRIu64 " -- %s\n", job_id,
|
||||
@@ -322,7 +319,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
const char* kDumbDbName = "";
|
||||
for (auto& file : state.sst_delete_files) {
|
||||
candidate_files.emplace_back(
|
||||
MakeTableFileName(kDumbDbName, file.metadata->fd.GetNumber()), file.path);
|
||||
MakeTableFileName(kDumbDbName, file.metadata->fd.GetNumber()),
|
||||
file.path);
|
||||
if (file.metadata->table_reader_handle) {
|
||||
table_cache_->Release(file.metadata->table_reader_handle);
|
||||
}
|
||||
@@ -332,7 +330,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
for (auto file_num : state.log_delete_files) {
|
||||
if (file_num > 0) {
|
||||
candidate_files.emplace_back(LogFileName(kDumbDbName, file_num),
|
||||
immutable_db_options_.wal_dir);
|
||||
immutable_db_options_.wal_dir);
|
||||
}
|
||||
}
|
||||
for (const auto& filename : state.manifest_delete_files) {
|
||||
@@ -465,13 +463,12 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
} else {
|
||||
dir_to_sync =
|
||||
(type == kLogFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
fname = dir_to_sync
|
||||
+ (
|
||||
(!dir_to_sync.empty() && dir_to_sync.back() == '/') ||
|
||||
(!to_delete.empty() && to_delete.front() == '/')
|
||||
? "" : "/"
|
||||
)
|
||||
+ to_delete;
|
||||
fname = dir_to_sync +
|
||||
((!dir_to_sync.empty() && dir_to_sync.back() == '/') ||
|
||||
(!to_delete.empty() && to_delete.front() == '/')
|
||||
? ""
|
||||
: "/") +
|
||||
to_delete;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -482,6 +479,11 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
for (const auto w : state.logs_to_free) {
|
||||
// TODO: maybe check the return value of Close.
|
||||
w->Close();
|
||||
}
|
||||
|
||||
Status file_deletion_status;
|
||||
if (schedule_only) {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
@@ -6,21 +6,19 @@
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "db/error_handler.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "monitoring/persistent_stats_history.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/wal_filter.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
Options SanitizeOptions(const std::string& dbname, const Options& src) {
|
||||
@@ -41,6 +39,8 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
max_max_open_files = 0x400000;
|
||||
}
|
||||
ClipToRange(&result.max_open_files, 20, max_max_open_files);
|
||||
TEST_SYNC_POINT_CALLBACK("SanitizeOptions::AfterChangeMaxOpenFiles",
|
||||
&result.max_open_files);
|
||||
}
|
||||
|
||||
if (result.info_log == nullptr) {
|
||||
@@ -122,6 +122,25 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ImmutableDBOptions immutable_db_options(result);
|
||||
if (!IsWalDirSameAsDBPath(&immutable_db_options)) {
|
||||
// Either the WAL dir and db_paths[0]/db_name are not the same, or we
|
||||
// cannot tell for sure. In either case, assume they're different and
|
||||
// explicitly cleanup the trash log files (bypass DeleteScheduler)
|
||||
// Do this first so even if we end up calling
|
||||
// DeleteScheduler::CleanupDirectory on the same dir later, it will be
|
||||
// safe
|
||||
std::vector<std::string> filenames;
|
||||
result.env->GetChildren(result.wal_dir, &filenames);
|
||||
for (std::string& filename : filenames) {
|
||||
if (filename.find(".log.trash",
|
||||
filename.length() - std::string(".log.trash").length()) !=
|
||||
std::string::npos) {
|
||||
std::string trash_file = result.wal_dir + "/" + filename;
|
||||
result.env->DeleteFile(trash_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
// When the DB is stopped, it's possible that there are some .trash files that
|
||||
// were not deleted yet, when we open the DB we will find these .trash files
|
||||
// and schedule them to be deleted (or delete immediately if SstFileManager
|
||||
@@ -143,7 +162,6 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Status SanitizeOptionsByTable(
|
||||
const DBOptions& db_opts,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families) {
|
||||
@@ -156,38 +174,23 @@ Status SanitizeOptionsByTable(
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static Status ValidateOptions(
|
||||
Status DBImpl::ValidateOptions(
|
||||
const DBOptions& db_options,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families) {
|
||||
Status s;
|
||||
|
||||
for (auto& cfd : column_families) {
|
||||
s = CheckCompressionSupported(cfd.options);
|
||||
if (s.ok() && db_options.allow_concurrent_memtable_write) {
|
||||
s = CheckConcurrentWritesSupported(cfd.options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = CheckCFPathsSupported(db_options, cfd.options);
|
||||
}
|
||||
s = ColumnFamilyData::ValidateOptions(db_options, cfd.options);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (cfd.options.ttl > 0 || cfd.options.compaction_options_fifo.ttl > 0) {
|
||||
if (db_options.max_open_files != -1) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported when files are always "
|
||||
"kept open (set max_open_files = -1). ");
|
||||
}
|
||||
if (cfd.options.table_factory->Name() !=
|
||||
BlockBasedTableFactory().Name()) {
|
||||
return Status::NotSupported(
|
||||
"TTL is only supported in Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
}
|
||||
s = ValidateOptions(db_options);
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::ValidateOptions(const DBOptions& db_options) {
|
||||
if (db_options.db_paths.size() > 4) {
|
||||
return Status::NotSupported(
|
||||
"More than four DB paths are not supported yet. ");
|
||||
@@ -212,9 +215,20 @@ static Status ValidateOptions(
|
||||
return Status::InvalidArgument("keep_log_file_num must be greater than 0");
|
||||
}
|
||||
|
||||
if (db_options.unordered_write &&
|
||||
!db_options.allow_concurrent_memtable_write) {
|
||||
return Status::InvalidArgument(
|
||||
"unordered_write is incompatible with !allow_concurrent_memtable_write");
|
||||
}
|
||||
|
||||
if (db_options.unordered_write && db_options.enable_pipelined_write) {
|
||||
return Status::InvalidArgument(
|
||||
"unordered_write is incompatible with enable_pipelined_write");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status DBImpl::NewDB() {
|
||||
VersionEdit new_db;
|
||||
new_db.SetLogNumber(0);
|
||||
@@ -235,7 +249,7 @@ Status DBImpl::NewDB() {
|
||||
file->SetPreallocationBlockSize(
|
||||
immutable_db_options_.manifest_preallocation_size);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), manifest, env_options, nullptr /* stats */,
|
||||
std::move(file), manifest, env_options, env_, nullptr /* stats */,
|
||||
immutable_db_options_.listeners));
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
@@ -381,6 +395,7 @@ Status DBImpl::Recover(
|
||||
}
|
||||
|
||||
Status s = versions_->Recover(column_families, read_only);
|
||||
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
@@ -392,6 +407,10 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
}
|
||||
// DB mutex is already held
|
||||
if (s.ok() && immutable_db_options_.persist_stats_to_disk) {
|
||||
s = InitPersistStatsColumnFamily();
|
||||
}
|
||||
|
||||
// Initial max_total_in_memory_state_ before recovery logs. Log recovery
|
||||
// may check this value to decide whether to flush.
|
||||
@@ -407,6 +426,8 @@ Status DBImpl::Recover(
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), this, &mutex_);
|
||||
default_cf_internal_stats_ = default_cf_handle_->cfd()->internal_stats();
|
||||
// TODO(Zhongyi): handle single_column_family_mode_ when
|
||||
// persistent_stats is enabled
|
||||
single_column_family_mode_ =
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1;
|
||||
|
||||
@@ -419,7 +440,10 @@ Status DBImpl::Recover(
|
||||
// produced by an older version of rocksdb.
|
||||
std::vector<std::string> filenames;
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &filenames);
|
||||
if (!s.ok()) {
|
||||
if (s.IsNotFound()) {
|
||||
return Status::InvalidArgument("wal_dir not found",
|
||||
immutable_db_options_.wal_dir);
|
||||
} else if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -499,6 +523,98 @@ Status DBImpl::Recover(
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::PersistentStatsProcessFormatVersion() {
|
||||
mutex_.AssertHeld();
|
||||
Status s;
|
||||
// persist version when stats CF doesn't exist
|
||||
bool should_persist_format_version = !persistent_stats_cfd_exists_;
|
||||
mutex_.Unlock();
|
||||
if (persistent_stats_cfd_exists_) {
|
||||
// Check persistent stats format version compatibility. Drop and recreate
|
||||
// persistent stats CF if format version is incompatible
|
||||
uint64_t format_version_recovered = 0;
|
||||
Status s_format = DecodePersistentStatsVersionNumber(
|
||||
this, StatsVersionKeyType::kFormatVersion, &format_version_recovered);
|
||||
uint64_t compatible_version_recovered = 0;
|
||||
Status s_compatible = DecodePersistentStatsVersionNumber(
|
||||
this, StatsVersionKeyType::kCompatibleVersion,
|
||||
&compatible_version_recovered);
|
||||
// abort reading from existing stats CF if any of following is true:
|
||||
// 1. failed to read format version or compatible version from disk
|
||||
// 2. sst's format version is greater than current format version, meaning
|
||||
// this sst is encoded with a newer RocksDB release, and current compatible
|
||||
// version is below the sst's compatible version
|
||||
if (!s_format.ok() || !s_compatible.ok() ||
|
||||
(kStatsCFCurrentFormatVersion < format_version_recovered &&
|
||||
kStatsCFCompatibleFormatVersion < compatible_version_recovered)) {
|
||||
if (!s_format.ok() || !s_compatible.ok()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Reading persistent stats version key failed. Format key: %s, "
|
||||
"compatible key: %s",
|
||||
s_format.ToString().c_str(), s_compatible.ToString().c_str());
|
||||
} else {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Disable persistent stats due to corrupted or incompatible format "
|
||||
"version\n");
|
||||
}
|
||||
DropColumnFamily(persist_stats_cf_handle_);
|
||||
DestroyColumnFamilyHandle(persist_stats_cf_handle_);
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ColumnFamilyOptions cfo;
|
||||
OptimizeForPersistentStats(&cfo);
|
||||
s = CreateColumnFamily(cfo, kPersistentStatsColumnFamilyName, &handle);
|
||||
persist_stats_cf_handle_ = static_cast<ColumnFamilyHandleImpl*>(handle);
|
||||
// should also persist version here because old stats CF is discarded
|
||||
should_persist_format_version = true;
|
||||
}
|
||||
}
|
||||
if (s.ok() && should_persist_format_version) {
|
||||
// Persistent stats CF being created for the first time, need to write
|
||||
// format version key
|
||||
WriteBatch batch;
|
||||
batch.Put(persist_stats_cf_handle_, kFormatVersionKeyString,
|
||||
ToString(kStatsCFCurrentFormatVersion));
|
||||
batch.Put(persist_stats_cf_handle_, kCompatibleVersionKeyString,
|
||||
ToString(kStatsCFCompatibleFormatVersion));
|
||||
WriteOptions wo;
|
||||
wo.low_pri = true;
|
||||
wo.no_slowdown = true;
|
||||
wo.sync = false;
|
||||
s = Write(wo, &batch);
|
||||
}
|
||||
mutex_.Lock();
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::InitPersistStatsColumnFamily() {
|
||||
mutex_.AssertHeld();
|
||||
assert(!persist_stats_cf_handle_);
|
||||
ColumnFamilyData* persistent_stats_cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(
|
||||
kPersistentStatsColumnFamilyName);
|
||||
persistent_stats_cfd_exists_ = persistent_stats_cfd != nullptr;
|
||||
|
||||
Status s;
|
||||
if (persistent_stats_cfd != nullptr) {
|
||||
// We are recovering from a DB which already contains persistent stats CF,
|
||||
// the CF is already created in VersionSet::ApplyOneVersionEdit, but
|
||||
// column family handle was not. Need to explicitly create handle here.
|
||||
persist_stats_cf_handle_ =
|
||||
new ColumnFamilyHandleImpl(persistent_stats_cfd, this, &mutex_);
|
||||
} else {
|
||||
mutex_.Unlock();
|
||||
ColumnFamilyHandle* handle = nullptr;
|
||||
ColumnFamilyOptions cfo;
|
||||
OptimizeForPersistentStats(&cfo);
|
||||
s = CreateColumnFamily(cfo, kPersistentStatsColumnFamilyName, &handle);
|
||||
persist_stats_cf_handle_ = static_cast<ColumnFamilyHandleImpl*>(handle);
|
||||
mutex_.Lock();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only) {
|
||||
@@ -507,7 +623,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
Logger* info_log;
|
||||
const char* fname;
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
virtual void Corruption(size_t bytes, const Status& s) override {
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
|
||||
(this->status == nullptr ? "(ignoring error) " : ""),
|
||||
fname, static_cast<int>(bytes), s.ToString().c_str());
|
||||
@@ -558,12 +674,13 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
bool stop_replay_for_corruption = false;
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_log_number = kMaxSequenceNumber;
|
||||
uint64_t min_log_number = MinLogNumberToKeep();
|
||||
for (auto log_number : log_numbers) {
|
||||
if (log_number < versions_->min_log_number_to_keep_2pc()) {
|
||||
if (log_number < min_log_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Skipping log #%" PRIu64
|
||||
" since it is older than min log to keep #%" PRIu64,
|
||||
log_number, versions_->min_log_number_to_keep_2pc());
|
||||
log_number, min_log_number);
|
||||
continue;
|
||||
}
|
||||
// The previous incarnation may not have written any MANIFEST
|
||||
@@ -575,7 +692,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Recovering log #%" PRIu64 " mode %d", log_number,
|
||||
immutable_db_options_.wal_recovery_mode);
|
||||
static_cast<int>(immutable_db_options_.wal_recovery_mode));
|
||||
auto logFileDropped = [this, &fname]() {
|
||||
uint64_t bytes;
|
||||
if (env_->GetFileSize(fname, &bytes).ok()) {
|
||||
@@ -624,8 +741,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// to be skipped instead of propagating bad information (like overly
|
||||
// large sequence numbers).
|
||||
log::Reader reader(immutable_db_options_.info_log, std::move(file_reader),
|
||||
&reporter, true /*checksum*/, log_number,
|
||||
false /* retry_after_eof */);
|
||||
&reporter, true /*checksum*/, log_number);
|
||||
|
||||
// Determine if we should tolerate incomplete records at the tail end of the
|
||||
// Read all the records and add to a memtable
|
||||
@@ -719,7 +835,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
" mode %d log filter %s returned "
|
||||
"more records (%d) than original (%d) which is not allowed. "
|
||||
"Aborting recovery.",
|
||||
log_number, immutable_db_options_.wal_recovery_mode,
|
||||
log_number,
|
||||
static_cast<int>(immutable_db_options_.wal_recovery_mode),
|
||||
immutable_db_options_.wal_filter->Name(), new_count,
|
||||
original_count);
|
||||
status = Status::NotSupported(
|
||||
@@ -1024,6 +1141,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
mutable_cf_options.sample_for_compression,
|
||||
cfd->ioptions()->compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery,
|
||||
&event_logger_, job_id, Env::IO_HIGH, nullptr /* table_properties */,
|
||||
@@ -1053,7 +1171,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
stats.micros = env_->NowMicros() - start_micros;
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
cfd->internal_stats()->AddCompactionStats(level, stats);
|
||||
cfd->internal_stats()->AddCompactionStats(level, Env::Priority::USER, stats);
|
||||
cfd->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
meta.fd.GetFileSize());
|
||||
RecordTick(stats_, COMPACT_WRITE_BYTES, meta.fd.GetFileSize());
|
||||
@@ -1066,12 +1184,23 @@ Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
if (db_options.persist_stats_to_disk) {
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kPersistentStatsColumnFamilyName, cf_options));
|
||||
}
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
Status s = DB::Open(db_options, dbname, column_families, &handles, dbptr);
|
||||
if (s.ok()) {
|
||||
assert(handles.size() == 1);
|
||||
if (db_options.persist_stats_to_disk) {
|
||||
assert(handles.size() == 2);
|
||||
} else {
|
||||
assert(handles.size() == 1);
|
||||
}
|
||||
// i can delete the handle since DBImpl is always holding a reference to
|
||||
// default column family
|
||||
if (db_options.persist_stats_to_disk && handles[1] != nullptr) {
|
||||
delete handles[1];
|
||||
}
|
||||
delete handles[0];
|
||||
}
|
||||
return s;
|
||||
@@ -1086,6 +1215,45 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
!kSeqPerBatch, kBatchPerTxn);
|
||||
}
|
||||
|
||||
Status DBImpl::CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
size_t preallocate_block_size, log::Writer** new_log) {
|
||||
Status s;
|
||||
std::unique_ptr<WritableFile> lfile;
|
||||
|
||||
DBOptions db_options =
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_);
|
||||
EnvOptions opt_env_options =
|
||||
env_->OptimizeForLogWrite(env_options_, db_options);
|
||||
std::string log_fname =
|
||||
LogFileName(immutable_db_options_.wal_dir, log_file_num);
|
||||
|
||||
if (recycle_log_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"reusing log %" PRIu64 " from recycle list\n",
|
||||
recycle_log_number);
|
||||
std::string old_log_fname =
|
||||
LogFileName(immutable_db_options_.wal_dir, recycle_log_number);
|
||||
s = env_->ReuseWritableFile(log_fname, old_log_fname, &lfile,
|
||||
opt_env_options);
|
||||
} else {
|
||||
s = NewWritableFile(env_, log_fname, &lfile, opt_env_options);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
lfile->SetWriteLifeTimeHint(CalculateWALWriteHint());
|
||||
lfile->SetPreallocationBlockSize(preallocate_block_size);
|
||||
|
||||
const auto& listeners = immutable_db_options_.listeners;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), log_fname, opt_env_options,
|
||||
env_, nullptr /* stats */, listeners));
|
||||
*new_log = new log::Writer(std::move(file_writer), log_file_num,
|
||||
immutable_db_options_.recycle_log_file_num > 0,
|
||||
immutable_db_options_.manual_wal_flush);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
@@ -1145,41 +1313,28 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
delete impl;
|
||||
return s;
|
||||
}
|
||||
|
||||
impl->wal_in_db_path_ =
|
||||
IsWalDirSameAsDBPath(&impl->immutable_db_options_);
|
||||
|
||||
impl->mutex_.Lock();
|
||||
auto write_hint = impl->CalculateWALWriteHint();
|
||||
// Handles create_if_missing, error_if_exists
|
||||
s = impl->Recover(column_families);
|
||||
if (s.ok()) {
|
||||
uint64_t new_log_number = impl->versions_->NewFileNumber();
|
||||
std::unique_ptr<WritableFile> lfile;
|
||||
EnvOptions soptions(db_options);
|
||||
EnvOptions opt_env_options =
|
||||
impl->immutable_db_options_.env->OptimizeForLogWrite(
|
||||
soptions, BuildDBOptions(impl->immutable_db_options_,
|
||||
impl->mutable_db_options_));
|
||||
std::string log_fname =
|
||||
LogFileName(impl->immutable_db_options_.wal_dir, new_log_number);
|
||||
s = NewWritableFile(impl->immutable_db_options_.env, log_fname, &lfile,
|
||||
opt_env_options);
|
||||
log::Writer* new_log = nullptr;
|
||||
const size_t preallocate_block_size =
|
||||
impl->GetWalPreallocateBlockSize(max_write_buffer_size);
|
||||
s = impl->CreateWAL(new_log_number, 0 /*recycle_log_number*/,
|
||||
preallocate_block_size, &new_log);
|
||||
if (s.ok()) {
|
||||
lfile->SetWriteLifeTimeHint(write_hint);
|
||||
lfile->SetPreallocationBlockSize(
|
||||
impl->GetWalPreallocateBlockSize(max_write_buffer_size));
|
||||
{
|
||||
InstrumentedMutexLock wl(&impl->log_write_mutex_);
|
||||
impl->logfile_number_ = new_log_number;
|
||||
const auto& listeners = impl->immutable_db_options_.listeners;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), log_fname, opt_env_options,
|
||||
nullptr /* stats */, listeners));
|
||||
impl->logs_.emplace_back(
|
||||
new_log_number,
|
||||
new log::Writer(
|
||||
std::move(file_writer), new_log_number,
|
||||
impl->immutable_db_options_.recycle_log_file_num > 0,
|
||||
impl->immutable_db_options_.manual_wal_flush));
|
||||
}
|
||||
InstrumentedMutexLock wl(&impl->log_write_mutex_);
|
||||
impl->logfile_number_ = new_log_number;
|
||||
assert(new_log != nullptr);
|
||||
impl->logs_.emplace_back(new_log_number, new_log);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
// set column family handles
|
||||
for (auto cf : column_families) {
|
||||
auto cfd =
|
||||
@@ -1226,6 +1381,10 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
s = impl->directories_.GetDbDir()->Fsync();
|
||||
}
|
||||
}
|
||||
if (s.ok() && impl->immutable_db_options_.persist_stats_to_disk) {
|
||||
// try to read format version but no need to fail Open() even if it fails
|
||||
s = impl->PersistentStatsProcessFormatVersion();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
@@ -1311,7 +1470,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
if (s.ok()) {
|
||||
ROCKS_LOG_INFO(impl->immutable_db_options_.info_log, "DB pointer %p", impl);
|
||||
ROCKS_LOG_HEADER(impl->immutable_db_options_.info_log, "DB pointer %p",
|
||||
impl);
|
||||
LogFlush(impl->immutable_db_options_.info_log);
|
||||
assert(impl->TEST_WALBufferIsEmpty());
|
||||
// If the assert above fails then we need to FlushWAL before returning
|
||||
@@ -3,10 +3,10 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_impl_readonly.h"
|
||||
#include "db/db_impl/db_impl_readonly.h"
|
||||
|
||||
#include "db/compacted_db_impl.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
@@ -60,7 +60,7 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
size_t size = pinnable_val->size();
|
||||
RecordTick(stats_, BYTES_READ, size);
|
||||
MeasureTime(stats_, BYTES_PER_READ, size);
|
||||
RecordInHistogram(stats_, BYTES_PER_READ, size);
|
||||
PERF_COUNTER_ADD(get_read_bytes, size);
|
||||
return s;
|
||||
}
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -77,8 +77,8 @@ class DBImplReadOnly : public DBImpl {
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*input_file_names*/,
|
||||
const int /*output_level*/, const int /*output_path_id*/ = -1,
|
||||
std::vector<std::string>* const /*output_file_names*/ = nullptr
|
||||
) override {
|
||||
std::vector<std::string>* const /*output_file_names*/ = nullptr,
|
||||
CompactionJobInfo* /*compaction_job_info*/ = nullptr) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
@@ -122,6 +122,6 @@ class DBImplReadOnly : public DBImpl {
|
||||
DBImplReadOnly(const DBImplReadOnly&);
|
||||
void operator=(const DBImplReadOnly&);
|
||||
};
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
@@ -0,0 +1,645 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_impl/db_impl_secondary.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "db/db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "logging/auto_roll_logger.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
DBImplSecondary::DBImplSecondary(const DBOptions& db_options,
|
||||
const std::string& dbname)
|
||||
: DBImpl(db_options, dbname) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Opening the db in secondary mode");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
DBImplSecondary::~DBImplSecondary() {}
|
||||
|
||||
Status DBImplSecondary::Recover(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool /*readonly*/, bool /*error_if_log_file_exist*/,
|
||||
bool /*error_if_data_exists_in_logs*/) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
JobContext job_context(0);
|
||||
Status s;
|
||||
s = static_cast<ReactiveVersionSet*>(versions_.get())
|
||||
->Recover(column_families, &manifest_reader_, &manifest_reporter_,
|
||||
&manifest_reader_status_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
s = CheckConsistency();
|
||||
}
|
||||
// Initial max_total_in_memory_state_ before recovery logs.
|
||||
max_total_in_memory_state_ = 0;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
auto* mutable_cf_options = cfd->GetLatestMutableCFOptions();
|
||||
max_total_in_memory_state_ += mutable_cf_options->write_buffer_size *
|
||||
mutable_cf_options->max_write_buffer_number;
|
||||
}
|
||||
if (s.ok()) {
|
||||
default_cf_handle_ = new ColumnFamilyHandleImpl(
|
||||
versions_->GetColumnFamilySet()->GetDefault(), this, &mutex_);
|
||||
default_cf_internal_stats_ = default_cf_handle_->cfd()->internal_stats();
|
||||
single_column_family_mode_ =
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1;
|
||||
|
||||
std::unordered_set<ColumnFamilyData*> cfds_changed;
|
||||
s = FindAndRecoverLogFiles(&cfds_changed, &job_context);
|
||||
}
|
||||
|
||||
if (s.IsPathNotFound()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Secondary tries to read WAL, but WAL file(s) have already "
|
||||
"been purged by primary.");
|
||||
s = Status::OK();
|
||||
}
|
||||
// TODO: update options_file_number_ needed?
|
||||
|
||||
job_context.Clean();
|
||||
return s;
|
||||
}
|
||||
|
||||
// find new WAL and apply them in order to the secondary instance
|
||||
Status DBImplSecondary::FindAndRecoverLogFiles(
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
JobContext* job_context) {
|
||||
assert(nullptr != cfds_changed);
|
||||
assert(nullptr != job_context);
|
||||
Status s;
|
||||
std::vector<uint64_t> logs;
|
||||
s = FindNewLogNumbers(&logs);
|
||||
if (s.ok() && !logs.empty()) {
|
||||
SequenceNumber next_sequence(kMaxSequenceNumber);
|
||||
s = RecoverLogFiles(logs, &next_sequence, cfds_changed, job_context);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// List wal_dir and find all new WALs, return these log numbers
|
||||
Status DBImplSecondary::FindNewLogNumbers(std::vector<uint64_t>* logs) {
|
||||
assert(logs != nullptr);
|
||||
std::vector<std::string> filenames;
|
||||
Status s;
|
||||
s = env_->GetChildren(immutable_db_options_.wal_dir, &filenames);
|
||||
if (s.IsNotFound()) {
|
||||
return Status::InvalidArgument("Failed to open wal_dir",
|
||||
immutable_db_options_.wal_dir);
|
||||
} else if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// if log_readers_ is non-empty, it means we have applied all logs with log
|
||||
// numbers smaller than the smallest log in log_readers_, so there is no
|
||||
// need to pass these logs to RecoverLogFiles
|
||||
uint64_t log_number_min = 0;
|
||||
if (!log_readers_.empty()) {
|
||||
log_number_min = log_readers_.begin()->first;
|
||||
}
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == kLogFile &&
|
||||
number >= log_number_min) {
|
||||
logs->push_back(number);
|
||||
}
|
||||
}
|
||||
// Recover logs in the order that they were generated
|
||||
if (!logs->empty()) {
|
||||
std::sort(logs->begin(), logs->end());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImplSecondary::MaybeInitLogReader(
|
||||
uint64_t log_number, log::FragmentBufferedReader** log_reader) {
|
||||
auto iter = log_readers_.find(log_number);
|
||||
// make sure the log file is still present
|
||||
if (iter == log_readers_.end() ||
|
||||
iter->second->reader_->GetLogNumber() != log_number) {
|
||||
// delete the obsolete log reader if log number mismatch
|
||||
if (iter != log_readers_.end()) {
|
||||
log_readers_.erase(iter);
|
||||
}
|
||||
// initialize log reader from log_number
|
||||
// TODO: min_log_number_to_keep_2pc check needed?
|
||||
// Open the log file
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, log_number);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Recovering log #%" PRIu64 " mode %d", log_number,
|
||||
static_cast<int>(immutable_db_options_.wal_recovery_mode));
|
||||
|
||||
std::unique_ptr<SequentialFileReader> file_reader;
|
||||
{
|
||||
std::unique_ptr<SequentialFile> file;
|
||||
Status status = env_->NewSequentialFile(
|
||||
fname, &file, env_->OptimizeForLogRead(env_options_));
|
||||
if (!status.ok()) {
|
||||
*log_reader = nullptr;
|
||||
return status;
|
||||
}
|
||||
file_reader.reset(new SequentialFileReader(std::move(file), fname));
|
||||
}
|
||||
|
||||
// Create the log reader.
|
||||
LogReaderContainer* log_reader_container = new LogReaderContainer(
|
||||
env_, immutable_db_options_.info_log, std::move(fname),
|
||||
std::move(file_reader), log_number);
|
||||
log_readers_.insert(std::make_pair(
|
||||
log_number, std::unique_ptr<LogReaderContainer>(log_reader_container)));
|
||||
}
|
||||
iter = log_readers_.find(log_number);
|
||||
assert(iter != log_readers_.end());
|
||||
*log_reader = iter->second->reader_;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// After manifest recovery, replay WALs and refresh log_readers_ if necessary
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status DBImplSecondary::RecoverLogFiles(
|
||||
const std::vector<uint64_t>& log_numbers, SequenceNumber* next_sequence,
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
JobContext* job_context) {
|
||||
assert(nullptr != cfds_changed);
|
||||
assert(nullptr != job_context);
|
||||
mutex_.AssertHeld();
|
||||
Status status;
|
||||
for (auto log_number : log_numbers) {
|
||||
log::FragmentBufferedReader* reader = nullptr;
|
||||
status = MaybeInitLogReader(log_number, &reader);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
assert(reader != nullptr);
|
||||
}
|
||||
for (auto log_number : log_numbers) {
|
||||
auto it = log_readers_.find(log_number);
|
||||
assert(it != log_readers_.end());
|
||||
log::FragmentBufferedReader* reader = it->second->reader_;
|
||||
// Manually update the file number allocation counter in VersionSet.
|
||||
versions_->MarkFileNumberUsed(log_number);
|
||||
|
||||
// Determine if we should tolerate incomplete records at the tail end of the
|
||||
// Read all the records and add to a memtable
|
||||
std::string scratch;
|
||||
Slice record;
|
||||
WriteBatch batch;
|
||||
|
||||
while (reader->ReadRecord(&record, &scratch,
|
||||
immutable_db_options_.wal_recovery_mode) &&
|
||||
status.ok()) {
|
||||
if (record.size() < WriteBatchInternal::kHeader) {
|
||||
reader->GetReporter()->Corruption(
|
||||
record.size(), Status::Corruption("log record too small"));
|
||||
continue;
|
||||
}
|
||||
WriteBatchInternal::SetContents(&batch, record);
|
||||
SequenceNumber seq_of_batch = WriteBatchInternal::Sequence(&batch);
|
||||
std::vector<uint32_t> column_family_ids;
|
||||
status = CollectColumnFamilyIdsFromWriteBatch(batch, &column_family_ids);
|
||||
if (status.ok()) {
|
||||
for (const auto id : column_family_ids) {
|
||||
ColumnFamilyData* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(id);
|
||||
if (cfd == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
}
|
||||
const std::vector<FileMetaData*>& l0_files =
|
||||
cfd->current()->storage_info()->LevelFiles(0);
|
||||
SequenceNumber seq =
|
||||
l0_files.empty() ? 0 : l0_files.back()->fd.largest_seqno;
|
||||
// If the write batch's sequence number is smaller than the last
|
||||
// sequence number of the largest sequence persisted for this column
|
||||
// family, then its data must reside in an SST that has already been
|
||||
// added in the prior MANIFEST replay.
|
||||
if (seq_of_batch <= seq) {
|
||||
continue;
|
||||
}
|
||||
auto curr_log_num = port::kMaxUint64;
|
||||
if (cfd_to_current_log_.count(cfd) > 0) {
|
||||
curr_log_num = cfd_to_current_log_[cfd];
|
||||
}
|
||||
// If the active memtable contains records added by replaying an
|
||||
// earlier WAL, then we need to seal the memtable, add it to the
|
||||
// immutable memtable list and create a new active memtable.
|
||||
if (!cfd->mem()->IsEmpty() && (curr_log_num == port::kMaxUint64 ||
|
||||
curr_log_num != log_number)) {
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MemTable* new_mem =
|
||||
cfd->ConstructNewMemtable(mutable_cf_options, seq_of_batch);
|
||||
cfd->mem()->SetNextLogNumber(log_number);
|
||||
cfd->imm()->Add(cfd->mem(), &job_context->memtables_to_free);
|
||||
new_mem->Ref();
|
||||
cfd->SetMemtable(new_mem);
|
||||
}
|
||||
}
|
||||
bool has_valid_writes = false;
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
&batch, column_family_memtables_.get(),
|
||||
nullptr /* flush_scheduler */, true, log_number, this,
|
||||
false /* concurrent_memtable_writes */, next_sequence,
|
||||
&has_valid_writes, seq_per_batch_, batch_per_txn_);
|
||||
}
|
||||
// If column family was not found, it might mean that the WAL write
|
||||
// batch references to the column family that was dropped after the
|
||||
// insert. We don't want to fail the whole write batch in that case --
|
||||
// we just ignore the update.
|
||||
// That's why we set ignore missing column families to true
|
||||
// passing null flush_scheduler will disable memtable flushing which is
|
||||
// needed for secondary instances
|
||||
if (status.ok()) {
|
||||
for (const auto id : column_family_ids) {
|
||||
ColumnFamilyData* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(id);
|
||||
if (cfd == nullptr) {
|
||||
continue;
|
||||
}
|
||||
std::unordered_map<ColumnFamilyData*, uint64_t>::iterator iter =
|
||||
cfd_to_current_log_.find(cfd);
|
||||
if (iter == cfd_to_current_log_.end()) {
|
||||
cfd_to_current_log_.insert({cfd, log_number});
|
||||
} else if (log_number > iter->second) {
|
||||
iter->second = log_number;
|
||||
}
|
||||
}
|
||||
auto last_sequence = *next_sequence - 1;
|
||||
if ((*next_sequence != kMaxSequenceNumber) &&
|
||||
(versions_->LastSequence() <= last_sequence)) {
|
||||
versions_->SetLastAllocatedSequence(last_sequence);
|
||||
versions_->SetLastPublishedSequence(last_sequence);
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
} else {
|
||||
// We are treating this as a failure while reading since we read valid
|
||||
// blocks that do not form coherent data
|
||||
reader->GetReporter()->Corruption(record.size(), status);
|
||||
}
|
||||
}
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
// remove logreaders from map after successfully recovering the WAL
|
||||
if (log_readers_.size() > 1) {
|
||||
auto erase_iter = log_readers_.begin();
|
||||
std::advance(erase_iter, log_readers_.size() - 1);
|
||||
log_readers_.erase(log_readers_.begin(), erase_iter);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// Implementation of the DB interface
|
||||
Status DBImplSecondary::Get(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* value) {
|
||||
return GetImpl(read_options, column_family, key, value);
|
||||
}
|
||||
|
||||
Status DBImplSecondary::GetImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* pinnable_val) {
|
||||
assert(pinnable_val != nullptr);
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
tracer_->Get(column_family, key);
|
||||
}
|
||||
}
|
||||
// Acquire SuperVersion
|
||||
SuperVersion* super_version = GetAndRefSuperVersion(cfd);
|
||||
SequenceNumber snapshot = versions_->LastSequence();
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
Status s;
|
||||
LookupKey lkey(key, snapshot);
|
||||
PERF_TIMER_STOP(get_snapshot_time);
|
||||
|
||||
bool done = false;
|
||||
if (super_version->mem->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
super_version->imm->Get(
|
||||
lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&max_covering_tombstone_seq, read_options)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
if (!done && !s.ok() && !s.IsMergeInProgress()) {
|
||||
ReturnAndCleanupSuperVersion(cfd, super_version);
|
||||
return s;
|
||||
}
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(read_options, lkey, pinnable_val, &s,
|
||||
&merge_context, &max_covering_tombstone_seq);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
{
|
||||
PERF_TIMER_GUARD(get_post_process_time);
|
||||
ReturnAndCleanupSuperVersion(cfd, super_version);
|
||||
RecordTick(stats_, NUMBER_KEYS_READ);
|
||||
size_t size = pinnable_val->size();
|
||||
RecordTick(stats_, BYTES_READ, size);
|
||||
RecordTimeToHistogram(stats_, BYTES_PER_READ, size);
|
||||
PERF_COUNTER_ADD(get_read_bytes, size);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Iterator* DBImplSecondary::NewIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
if (read_options.managed) {
|
||||
return NewErrorIterator(
|
||||
Status::NotSupported("Managed iterator is not supported anymore."));
|
||||
}
|
||||
if (read_options.read_tier == kPersistedTier) {
|
||||
return NewErrorIterator(Status::NotSupported(
|
||||
"ReadTier::kPersistedData is not yet supported in iterators."));
|
||||
}
|
||||
Iterator* result = nullptr;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (read_options.tailing) {
|
||||
return NewErrorIterator(Status::NotSupported(
|
||||
"tailing iterator not supported in secondary mode"));
|
||||
} else if (read_options.snapshot != nullptr) {
|
||||
// TODO (yanqin) support snapshot.
|
||||
return NewErrorIterator(
|
||||
Status::NotSupported("snapshot not supported in secondary mode"));
|
||||
} else {
|
||||
auto snapshot = versions_->LastSequence();
|
||||
result = NewIteratorImpl(read_options, cfd, snapshot, read_callback);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
|
||||
const ReadOptions& read_options, ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot, ReadCallback* read_callback) {
|
||||
assert(nullptr != cfd);
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), super_version->mutable_cf_options,
|
||||
snapshot,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator(), snapshot);
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
Status DBImplSecondary::NewIterators(
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) {
|
||||
if (read_options.managed) {
|
||||
return Status::NotSupported("Managed iterator is not supported anymore.");
|
||||
}
|
||||
if (read_options.read_tier == kPersistedTier) {
|
||||
return Status::NotSupported(
|
||||
"ReadTier::kPersistedData is not yet supported in iterators.");
|
||||
}
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (iterators == nullptr) {
|
||||
return Status::InvalidArgument("iterators not allowed to be nullptr");
|
||||
}
|
||||
iterators->clear();
|
||||
iterators->reserve(column_families.size());
|
||||
if (read_options.tailing) {
|
||||
return Status::NotSupported(
|
||||
"tailing iterator not supported in secondary mode");
|
||||
} else if (read_options.snapshot != nullptr) {
|
||||
// TODO (yanqin) support snapshot.
|
||||
return Status::NotSupported("snapshot not supported in secondary mode");
|
||||
} else {
|
||||
SequenceNumber read_seq = versions_->LastSequence();
|
||||
for (auto cfh : column_families) {
|
||||
ColumnFamilyData* cfd = static_cast<ColumnFamilyHandleImpl*>(cfh)->cfd();
|
||||
iterators->push_back(
|
||||
NewIteratorImpl(read_options, cfd, read_seq, read_callback));
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImplSecondary::CheckConsistency() {
|
||||
mutex_.AssertHeld();
|
||||
Status s = DBImpl::CheckConsistency();
|
||||
// If DBImpl::CheckConsistency() which is stricter returns success, then we
|
||||
// do not need to give a second chance.
|
||||
if (s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// It's possible that DBImpl::CheckConssitency() can fail because the primary
|
||||
// may have removed certain files, causing the GetFileSize(name) call to
|
||||
// fail and returning a PathNotFound. In this case, we take a best-effort
|
||||
// approach and just proceed.
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImplSecondary::CheckConsistency:AfterFirstAttempt", &s);
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
versions_->GetLiveFilesMetaData(&metadata);
|
||||
|
||||
std::string corruption_messages;
|
||||
for (const auto& md : metadata) {
|
||||
// md.name has a leading "/".
|
||||
std::string file_path = md.db_path + md.name;
|
||||
|
||||
uint64_t fsize = 0;
|
||||
s = env_->GetFileSize(file_path, &fsize);
|
||||
if (!s.ok() &&
|
||||
(env_->GetFileSize(Rocks2LevelTableFileName(file_path), &fsize).ok() ||
|
||||
s.IsPathNotFound())) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok()) {
|
||||
corruption_messages +=
|
||||
"Can't access " + md.name + ": " + s.ToString() + "\n";
|
||||
}
|
||||
}
|
||||
return corruption_messages.empty() ? Status::OK()
|
||||
: Status::Corruption(corruption_messages);
|
||||
}
|
||||
|
||||
Status DBImplSecondary::TryCatchUpWithPrimary() {
|
||||
assert(versions_.get() != nullptr);
|
||||
assert(manifest_reader_.get() != nullptr);
|
||||
Status s;
|
||||
// read the manifest and apply new changes to the secondary instance
|
||||
std::unordered_set<ColumnFamilyData*> cfds_changed;
|
||||
JobContext job_context(0, true /*create_superversion*/);
|
||||
InstrumentedMutexLock lock_guard(&mutex_);
|
||||
s = static_cast<ReactiveVersionSet*>(versions_.get())
|
||||
->ReadAndApply(&mutex_, &manifest_reader_, &cfds_changed);
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Last sequence is %" PRIu64,
|
||||
static_cast<uint64_t>(versions_->LastSequence()));
|
||||
for (ColumnFamilyData* cfd : cfds_changed) {
|
||||
if (cfd->IsDropped()) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] is dropped\n",
|
||||
cfd->GetName().c_str());
|
||||
continue;
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] Level summary: %s\n",
|
||||
cfd->GetName().c_str(),
|
||||
cfd->current()->storage_info()->LevelSummary(&tmp));
|
||||
}
|
||||
|
||||
// list wal_dir to discover new WALs and apply new changes to the secondary
|
||||
// instance
|
||||
if (s.ok()) {
|
||||
s = FindAndRecoverLogFiles(&cfds_changed, &job_context);
|
||||
}
|
||||
if (s.IsPathNotFound()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Secondary tries to read WAL, but WAL file(s) have already "
|
||||
"been purged by primary.");
|
||||
s = Status::OK();
|
||||
}
|
||||
if (s.ok()) {
|
||||
for (auto cfd : cfds_changed) {
|
||||
cfd->imm()->RemoveOldMemTables(cfd->GetLogNumber(),
|
||||
&job_context.memtables_to_free);
|
||||
auto& sv_context = job_context.superversion_contexts.back();
|
||||
cfd->InstallSuperVersion(&sv_context, &mutex_);
|
||||
sv_context.NewSuperVersion();
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DB::OpenAsSecondary(const Options& options, const std::string& dbname,
|
||||
const std::string& secondary_path, DB** dbptr) {
|
||||
*dbptr = nullptr;
|
||||
|
||||
DBOptions db_options(options);
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options);
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
Status s = DB::OpenAsSecondary(db_options, dbname, secondary_path,
|
||||
column_families, &handles, dbptr);
|
||||
if (s.ok()) {
|
||||
assert(handles.size() == 1);
|
||||
delete handles[0];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DB::OpenAsSecondary(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::string& secondary_path,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr) {
|
||||
*dbptr = nullptr;
|
||||
if (db_options.max_open_files != -1) {
|
||||
// TODO (yanqin) maybe support max_open_files != -1 by creating hard links
|
||||
// on SST files so that db secondary can still have access to old SSTs
|
||||
// while primary instance may delete original.
|
||||
return Status::InvalidArgument("require max_open_files to be -1");
|
||||
}
|
||||
|
||||
DBOptions tmp_opts(db_options);
|
||||
Status s;
|
||||
if (nullptr == tmp_opts.info_log) {
|
||||
s = CreateLoggerFromOptions(secondary_path, tmp_opts, &tmp_opts.info_log);
|
||||
if (!s.ok()) {
|
||||
tmp_opts.info_log = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
handles->clear();
|
||||
DBImplSecondary* impl = new DBImplSecondary(tmp_opts, dbname);
|
||||
impl->versions_.reset(new ReactiveVersionSet(
|
||||
dbname, &impl->immutable_db_options_, impl->env_options_,
|
||||
impl->table_cache_.get(), impl->write_buffer_manager_,
|
||||
&impl->write_controller_));
|
||||
impl->column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(impl->versions_->GetColumnFamilySet()));
|
||||
impl->mutex_.Lock();
|
||||
s = impl->Recover(column_families, true, false, false);
|
||||
if (s.ok()) {
|
||||
for (auto cf : column_families) {
|
||||
auto cfd =
|
||||
impl->versions_->GetColumnFamilySet()->GetColumnFamily(cf.name);
|
||||
if (nullptr == cfd) {
|
||||
s = Status::InvalidArgument("Column family not found: ", cf.name);
|
||||
break;
|
||||
}
|
||||
handles->push_back(new ColumnFamilyHandleImpl(cfd, impl, &impl->mutex_));
|
||||
}
|
||||
}
|
||||
SuperVersionContext sv_context(true /* create_superversion */);
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
sv_context.NewSuperVersion();
|
||||
cfd->InstallSuperVersion(&sv_context, &impl->mutex_);
|
||||
}
|
||||
}
|
||||
impl->mutex_.Unlock();
|
||||
sv_context.Clean();
|
||||
if (s.ok()) {
|
||||
*dbptr = impl;
|
||||
for (auto h : *handles) {
|
||||
impl->NewThreadStatusCfInfo(
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(h)->cfd());
|
||||
}
|
||||
} else {
|
||||
for (auto h : *handles) {
|
||||
delete h;
|
||||
}
|
||||
handles->clear();
|
||||
delete impl;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
#else // !ROCKSDB_LITE
|
||||
|
||||
Status DB::OpenAsSecondary(const Options& /*options*/,
|
||||
const std::string& /*name*/,
|
||||
const std::string& /*secondary_path*/,
|
||||
DB** /*dbptr*/) {
|
||||
return Status::NotSupported("Not supported in ROCKSDB_LITE.");
|
||||
}
|
||||
|
||||
Status DB::OpenAsSecondary(
|
||||
const DBOptions& /*db_options*/, const std::string& /*dbname*/,
|
||||
const std::string& /*secondary_path*/,
|
||||
const std::vector<ColumnFamilyDescriptor>& /*column_families*/,
|
||||
std::vector<ColumnFamilyHandle*>* /*handles*/, DB** /*dbptr*/) {
|
||||
return Status::NotSupported("Not supported in ROCKSDB_LITE.");
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,306 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// A wrapper class to hold log reader, log reporter, log status.
|
||||
class LogReaderContainer {
|
||||
public:
|
||||
LogReaderContainer()
|
||||
: reader_(nullptr), reporter_(nullptr), status_(nullptr) {}
|
||||
LogReaderContainer(Env* env, std::shared_ptr<Logger> info_log,
|
||||
std::string fname,
|
||||
std::unique_ptr<SequentialFileReader>&& file_reader,
|
||||
uint64_t log_number) {
|
||||
LogReporter* reporter = new LogReporter();
|
||||
status_ = new Status();
|
||||
reporter->env = env;
|
||||
reporter->info_log = info_log.get();
|
||||
reporter->fname = std::move(fname);
|
||||
reporter->status = status_;
|
||||
reporter_ = reporter;
|
||||
// We intentially make log::Reader do checksumming even if
|
||||
// paranoid_checks==false so that corruptions cause entire commits
|
||||
// to be skipped instead of propagating bad information (like overly
|
||||
// large sequence numbers).
|
||||
reader_ = new log::FragmentBufferedReader(info_log, std::move(file_reader),
|
||||
reporter, true /*checksum*/,
|
||||
log_number);
|
||||
}
|
||||
log::FragmentBufferedReader* reader_;
|
||||
log::Reader::Reporter* reporter_;
|
||||
Status* status_;
|
||||
~LogReaderContainer() {
|
||||
delete reader_;
|
||||
delete reporter_;
|
||||
delete status_;
|
||||
}
|
||||
private:
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
std::string fname;
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
|
||||
(this->status == nullptr ? "(ignoring error) " : ""),
|
||||
fname.c_str(), static_cast<int>(bytes),
|
||||
s.ToString().c_str());
|
||||
if (this->status != nullptr && this->status->ok()) {
|
||||
*this->status = s;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// The secondary instance shares access to the storage as the primary.
|
||||
// The secondary is able to read and replay changes described in both the
|
||||
// MANIFEST and the WAL files without coordination with the primary.
|
||||
// The secondary instance can be opened using `DB::OpenAsSecondary`. After
|
||||
// that, it can call `DBImplSecondary::TryCatchUpWithPrimary` to make best
|
||||
// effort attempts to catch up with the primary.
|
||||
class DBImplSecondary : public DBImpl {
|
||||
public:
|
||||
DBImplSecondary(const DBOptions& options, const std::string& dbname);
|
||||
~DBImplSecondary() override;
|
||||
|
||||
// Recover by replaying MANIFEST and WAL. Also initialize manifest_reader_
|
||||
// and log_readers_ to facilitate future operations.
|
||||
Status Recover(const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
bool read_only, bool error_if_log_file_exist,
|
||||
bool error_if_data_exists_in_logs) override;
|
||||
|
||||
// Implementations of the DB interface
|
||||
using DB::Get;
|
||||
Status Get(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value) override;
|
||||
|
||||
Status GetImpl(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value);
|
||||
|
||||
using DBImpl::NewIterator;
|
||||
Iterator* NewIterator(const ReadOptions&,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
ReadCallback* read_callback);
|
||||
|
||||
Status NewIterators(const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
|
||||
using DBImpl::Put;
|
||||
Status Put(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Merge;
|
||||
Status Merge(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/, const Slice& /*key*/,
|
||||
const Slice& /*value*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Delete;
|
||||
Status Delete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::SingleDelete;
|
||||
Status SingleDelete(const WriteOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice& /*key*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status Write(const WriteOptions& /*options*/,
|
||||
WriteBatch* /*updates*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::CompactRange;
|
||||
Status CompactRange(const CompactRangeOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const Slice* /*begin*/, const Slice* /*end*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::CompactFiles;
|
||||
Status CompactFiles(
|
||||
const CompactionOptions& /*compact_options*/,
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*input_file_names*/,
|
||||
const int /*output_level*/, const int /*output_path_id*/ = -1,
|
||||
std::vector<std::string>* const /*output_file_names*/ = nullptr,
|
||||
CompactionJobInfo* /*compaction_job_info*/ = nullptr) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status EnableFileDeletions(bool /*force*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* /*manifest_file_size*/,
|
||||
bool /*flush_memtable*/ = true) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::Flush;
|
||||
Status Flush(const FlushOptions& /*options*/,
|
||||
ColumnFamilyHandle* /*column_family*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DBImpl::SyncWAL;
|
||||
Status SyncWAL() override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
Status IngestExternalFile(
|
||||
ColumnFamilyHandle* /*column_family*/,
|
||||
const std::vector<std::string>& /*external_files*/,
|
||||
const IngestExternalFileOptions& /*ingestion_options*/) override {
|
||||
return Status::NotSupported("Not supported operation in secondary mode.");
|
||||
}
|
||||
|
||||
// Try to catch up with the primary by reading as much as possible from the
|
||||
// log files until there is nothing more to read or encounters an error. If
|
||||
// the amount of information in the log files to process is huge, this
|
||||
// method can take long time due to all the I/O and CPU costs.
|
||||
Status TryCatchUpWithPrimary() override;
|
||||
|
||||
|
||||
// Try to find log reader using log_number from log_readers_ map, initialize
|
||||
// if it doesn't exist
|
||||
Status MaybeInitLogReader(uint64_t log_number,
|
||||
log::FragmentBufferedReader** log_reader);
|
||||
|
||||
// Check if all live files exist on file system and that their file sizes
|
||||
// matche to the in-memory records. It is possible that some live files may
|
||||
// have been deleted by the primary. In this case, CheckConsistency() does
|
||||
// not flag the missing file as inconsistency.
|
||||
Status CheckConsistency() override;
|
||||
|
||||
protected:
|
||||
// ColumnFamilyCollector is a write batch handler which does nothing
|
||||
// except recording unique column family IDs
|
||||
class ColumnFamilyCollector : public WriteBatch::Handler {
|
||||
std::unordered_set<uint32_t> column_family_ids_;
|
||||
|
||||
Status AddColumnFamilyId(uint32_t column_family_id) {
|
||||
if (column_family_ids_.find(column_family_id) ==
|
||||
column_family_ids_.end()) {
|
||||
column_family_ids_.insert(column_family_id);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ColumnFamilyCollector() {}
|
||||
|
||||
~ColumnFamilyCollector() override {}
|
||||
|
||||
Status PutCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status DeleteCF(uint32_t column_family_id, const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status SingleDeleteCF(uint32_t column_family_id, const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status DeleteRangeCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status MergeCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status PutBlobIndexCF(uint32_t column_family_id, const Slice&,
|
||||
const Slice&) override {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
const std::unordered_set<uint32_t>& column_families() const {
|
||||
return column_family_ids_;
|
||||
}
|
||||
};
|
||||
|
||||
Status CollectColumnFamilyIdsFromWriteBatch(
|
||||
const WriteBatch& batch, std::vector<uint32_t>* column_family_ids) {
|
||||
assert(column_family_ids != nullptr);
|
||||
column_family_ids->clear();
|
||||
ColumnFamilyCollector handler;
|
||||
Status s = batch.Iterate(&handler);
|
||||
if (s.ok()) {
|
||||
for (const auto& cf : handler.column_families()) {
|
||||
column_family_ids->push_back(cf);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
// No copying allowed
|
||||
DBImplSecondary(const DBImplSecondary&);
|
||||
void operator=(const DBImplSecondary&);
|
||||
|
||||
using DBImpl::Recover;
|
||||
|
||||
Status FindAndRecoverLogFiles(
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
JobContext* job_context);
|
||||
Status FindNewLogNumbers(std::vector<uint64_t>* logs);
|
||||
// After manifest recovery, replay WALs and refresh log_readers_ if necessary
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* next_sequence,
|
||||
std::unordered_set<ColumnFamilyData*>* cfds_changed,
|
||||
JobContext* job_context);
|
||||
|
||||
std::unique_ptr<log::FragmentBufferedReader> manifest_reader_;
|
||||
std::unique_ptr<log::Reader::Reporter> manifest_reporter_;
|
||||
std::unique_ptr<Status> manifest_reader_status_;
|
||||
|
||||
// Cache log readers for each log number, used for continue WAL replay
|
||||
// after recovery
|
||||
std::map<uint64_t, std::unique_ptr<LogReaderContainer>> log_readers_;
|
||||
|
||||
// Current WAL number replayed for each column family.
|
||||
std::unordered_map<ColumnFamilyData*, uint64_t> cfd_to_current_log_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
Loaded 100 of 874 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user