mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab33322619 | ||
|
|
ab33b1f711 | ||
|
|
6f2b441d58 | ||
|
|
75b968cc97 | ||
|
|
495c075f75 | ||
|
|
429f02b8a8 | ||
|
|
a3c162ca09 | ||
|
|
d77b69ac21 | ||
|
|
d7515f81d5 | ||
|
|
810d2bcc62 | ||
|
|
c7c2e1968f | ||
|
|
a11863cb76 | ||
|
|
98a2fd2f2d | ||
|
|
149ef19517 | ||
|
|
8893171698 | ||
|
|
82e5c842b6 | ||
|
|
be55065086 | ||
|
|
159a99f6c3 | ||
|
|
b573aa8535 | ||
|
|
3e32a88fe7 | ||
|
|
56b9e4aae3 | ||
|
|
ff1ff7c624 | ||
|
|
39d508e34c | ||
|
|
727684bf97 | ||
|
|
ca25e86efc | ||
|
|
986bdc6801 | ||
|
|
1e57f2bf2b | ||
|
|
2d417e52df | ||
|
|
cbd6a20738 | ||
|
|
211ca26aee | ||
|
|
2495f93966 | ||
|
|
f06de5f232 | ||
|
|
cd987c383a | ||
|
|
8f104ae5e0 | ||
|
|
3d1a924ff3 | ||
|
|
afc51649e2 | ||
|
|
4806cc1269 | ||
|
|
6284eef4c0 | ||
|
|
248c063ba1 | ||
|
|
315abac945 | ||
|
|
a057bb2a8e | ||
|
|
868968c8e5 | ||
|
|
689391406a | ||
|
|
8d8656243e | ||
|
|
f8c505b23a | ||
|
|
46443bfa94 | ||
|
|
864b7e88f4 | ||
|
|
2b2394cbbb | ||
|
|
fd8804f979 | ||
|
|
004b89fba4 | ||
|
|
51da3aab4a | ||
|
|
b7e88cfb5f | ||
|
|
38d286f146 | ||
|
|
28bc6de989 | ||
|
|
9405b5ef8f | ||
|
|
220d0dff7c | ||
|
|
b088c83e6e | ||
|
|
17ae3fcbca | ||
|
|
39f4271be0 | ||
|
|
5615e23d8c | ||
|
|
836bcc2f00 | ||
|
|
67d995808a | ||
|
|
52e0f3353f | ||
|
|
6b626ff24c | ||
|
|
9720ea4dee | ||
|
|
0831a35994 | ||
|
|
f7ed654641 | ||
|
|
51301b869f | ||
|
|
652db51a31 | ||
|
|
dfccc7b4e2 | ||
|
|
c345d1ee88 | ||
|
|
12350115da | ||
|
|
230e68727a | ||
|
|
155d468c56 | ||
|
|
81345b90f9 | ||
|
|
46214df4a1 | ||
|
|
c88ff4ca76 | ||
|
|
b4b69e4f77 | ||
|
|
413e35273e | ||
|
|
969aa806b7 | ||
|
|
87c7d49d67 | ||
|
|
c86e5d7b93 | ||
|
|
98c37fda5d | ||
|
|
9fd6edf81c | ||
|
|
d4d42c02ea | ||
|
|
b2b3086524 | ||
|
|
a7aba2ef6b | ||
|
|
95344346af | ||
|
|
bd4963e64d | ||
|
|
56337faf3e | ||
|
|
eafa1bfc3c | ||
|
|
ac03c4d511 | ||
|
|
c6967a1a5e | ||
|
|
c8da670325 | ||
|
|
04778a94c5 | ||
|
|
57f2a00c6f | ||
|
|
814627af3d | ||
|
|
a3bd4142f2 | ||
|
|
58878f1c6a | ||
|
|
12134139e3 | ||
|
|
fd1b3f385a | ||
|
|
56c4a9c760 | ||
|
|
417367c42d | ||
|
|
e25ff039c8 | ||
|
|
f690712652 | ||
|
|
c1b3cde18a | ||
|
|
8c12426c93 | ||
|
|
b16ead531d | ||
|
|
a5e60bafc2 | ||
|
|
cb2c91850c | ||
|
|
c594b0e89d | ||
|
|
2623b2cf6d | ||
|
|
52d8347a91 | ||
|
|
756532daf5 | ||
|
|
47a2b3a406 | ||
|
|
9d22a1f136 | ||
|
|
2c84303aaa | ||
|
|
2dc636f62b | ||
|
|
f210b0f6c5 | ||
|
|
3ebebfccd8 | ||
|
|
53996149d4 | ||
|
|
b411d06031 | ||
|
|
1d43bc41fb | ||
|
|
e9de8b65a6 | ||
|
|
2b785d76b8 | ||
|
|
6f55798683 | ||
|
|
05d92efa75 | ||
|
|
2884b100ba | ||
|
|
284be570c8 | ||
|
|
2ddf53b2ca | ||
|
|
480b284766 | ||
|
|
89597bb66b | ||
|
|
37921b4997 | ||
|
|
485ac0dbd0 | ||
|
|
e126e0da5b | ||
|
|
dc4532c497 | ||
|
|
34c75e984b | ||
|
|
ebc647de87 | ||
|
|
492f6d27ed | ||
|
|
0d13bbe272 | ||
|
|
67533809fd | ||
|
|
1b7b997b88 | ||
|
|
f862b38124 | ||
|
|
a01b592597 | ||
|
|
22c73d15b2 | ||
|
|
694988b627 | ||
|
|
b8d23cdcb8 | ||
|
|
db03739340 | ||
|
|
f29b33c73b | ||
|
|
c4bd03a97e | ||
|
|
3cf7f353d9 | ||
|
|
216a9e16f4 | ||
|
|
b9ff6b050d | ||
|
|
afa8156af9 | ||
|
|
f9c14a42e8 | ||
|
|
a9f0e2b5b6 | ||
|
|
e7c434c364 | ||
|
|
ba9d1737a8 | ||
|
|
4ba119df5c | ||
|
|
8984e5f848 | ||
|
|
03b432d4b8 | ||
|
|
ccef2a7660 | ||
|
|
9fcf1a7b00 | ||
|
|
03bbf718cb | ||
|
|
62247ffa3b | ||
|
|
1e06a40685 | ||
|
|
b74ad66320 | ||
|
|
d9f4875e52 | ||
|
|
182b4ceacd | ||
|
|
a360bb61b5 | ||
|
|
8c76844742 | ||
|
|
7f0c77cb31 | ||
|
|
819e787bb9 | ||
|
|
5139e678b0 | ||
|
|
4ade89962d | ||
|
|
ace3d85068 | ||
|
|
7b8f348e56 | ||
|
|
d85993998c | ||
|
|
30e93c9b9c | ||
|
|
217854dc4a | ||
|
|
bd339a9798 | ||
|
|
174a79c993 | ||
|
|
6fdda8ac4d | ||
|
|
96d989f70d | ||
|
|
62f7a1be4f | ||
|
|
5636c4a80b | ||
|
|
a047409ae9 | ||
|
|
4f514a53d6 | ||
|
|
a2b911b63f | ||
|
|
1b4082581c | ||
|
|
c6d54b5037 | ||
|
|
aa5d8e6d95 | ||
|
|
c37937a9ce | ||
|
|
9283c7afd2 | ||
|
|
06a766de56 | ||
|
|
92416fa7f2 | ||
|
|
96ab15d306 | ||
|
|
73711f956c | ||
|
|
98870c7b9c | ||
|
|
a42324e370 | ||
|
|
d45a6a4002 | ||
|
|
11581b7415 | ||
|
|
b8ac71ba18 | ||
|
|
7ce1b2c19c | ||
|
|
7d817268b9 | ||
|
|
b4b8c25a5a | ||
|
|
dc885c6e95 | ||
|
|
a37b46ae12 | ||
|
|
55277c3287 | ||
|
|
3ad6b794cf | ||
|
|
12753130ec | ||
|
|
f0c36da6ee | ||
|
|
daebb1f916 | ||
|
|
e60bc99fe0 | ||
|
|
b9a0213cda | ||
|
|
4e4b857841 | ||
|
|
b3fd162267 | ||
|
|
e7ea51a8e7 | ||
|
|
6a0e737d9d | ||
|
|
4397012707 | ||
|
|
2d62e80511 | ||
|
|
eaf39568e5 | ||
|
|
5d1151deba | ||
|
|
5f00af4570 | ||
|
|
68af7811ea | ||
|
|
fd5970b454 | ||
|
|
513ad866b2 | ||
|
|
933973dc2d | ||
|
|
5e8e453d59 | ||
|
|
677d02427f | ||
|
|
d1cafc0892 | ||
|
|
6d6305dd7d | ||
|
|
863009b5a5 | ||
|
|
1851f977c2 | ||
|
|
91ac3b2067 | ||
|
|
bee4e51244 | ||
|
|
cfe8837e43 | ||
|
|
aaceef3638 | ||
|
|
ee4aa9a0ee | ||
|
|
82faa377a8 | ||
|
|
d090330c8e | ||
|
|
9651308307 | ||
|
|
bbb52b21f3 | ||
|
|
7e50ed8c24 | ||
|
|
218c3ecea3 | ||
|
|
8f679c2901 | ||
|
|
da9cbce731 | ||
|
|
2a979822b6 | ||
|
|
8e83a9d315 | ||
|
|
6f10130354 | ||
|
|
7de4e99a8e | ||
|
|
9a52e06a02 | ||
|
|
181191a1e4 | ||
|
|
f36d394aed | ||
|
|
53ae09c398 | ||
|
|
fe9f691194 | ||
|
|
b37f5ffc76 | ||
|
|
108470e963 | ||
|
|
3e53760fc4 | ||
|
|
e39f4f6cf9 | ||
|
|
e63140d52b | ||
|
|
678503ebcf | ||
|
|
4d98e29352 | ||
|
|
756e1f151e | ||
|
|
dad98dd4ae | ||
|
|
9898f63988 | ||
|
|
829363b449 | ||
|
|
b04408c47b | ||
|
|
8d3819369f | ||
|
|
2c2d5ab7e8 | ||
|
|
0b8dec7172 | ||
|
|
868bfa4033 | ||
|
|
9900f3821f | ||
|
|
e6eaf938c3 | ||
|
|
f33f3955ee | ||
|
|
ad325517fc | ||
|
|
d6187d07bb | ||
|
|
157768890c | ||
|
|
dd8d5471ea | ||
|
|
353db6daef | ||
|
|
98cb501bc0 | ||
|
|
7479a62a7a | ||
|
|
4a4e4279f0 | ||
|
|
384cb6619e | ||
|
|
ca52a67cfb | ||
|
|
114d21878d | ||
|
|
cb5c3159f0 | ||
|
|
ea189b320c | ||
|
|
391f85fc82 | ||
|
|
68cd93b873 | ||
|
|
caedd40ddd | ||
|
|
b39006e3db | ||
|
|
9a456fba20 | ||
|
|
d3a736761b | ||
|
|
939bb36597 | ||
|
|
86e2a1eeea | ||
|
|
db9ed5fdb4 | ||
|
|
5917de0bae | ||
|
|
6c6037f60c | ||
|
|
2fd8f750ab | ||
|
|
173c52a97f | ||
|
|
d07fec3bdc | ||
|
|
4bdf38b16e | ||
|
|
e84299c769 | ||
|
|
e5aab4c2b2 | ||
|
|
2113ecd3c2 | ||
|
|
10af17f3d7 | ||
|
|
0c4d1053df | ||
|
|
cc0d8be011 | ||
|
|
5257c9c42c | ||
|
|
560ed402bd | ||
|
|
551a41df37 | ||
|
|
d2a2b058f0 | ||
|
|
f8f040ccc7 | ||
|
|
e8bf2310a0 | ||
|
|
ea7d0b943a | ||
|
|
d6c7300ccf | ||
|
|
f9758e0129 | ||
|
|
e919ecedfc | ||
|
|
4c49fedaf1 | ||
|
|
1b43ab58d9 | ||
|
|
ca2b00277e | ||
|
|
f8dc5c459f | ||
|
|
b3c1331488 | ||
|
|
7ffcc457ff | ||
|
|
c4fb83441c | ||
|
|
be8f0b12ed | ||
|
|
c1de6c42a0 | ||
|
|
d888c95748 | ||
|
|
58f34edfce | ||
|
|
f1c8862479 | ||
|
|
b08b2fe732 | ||
|
|
e61f38e5a0 | ||
|
|
26b50783d3 | ||
|
|
42189612c3 | ||
|
|
f5a8398352 | ||
|
|
43ec4e68ba | ||
|
|
c2e8e8c1c0 | ||
|
|
b4c13a868a | ||
|
|
a52dd00243 | ||
|
|
910186c278 | ||
|
|
401d4205ef | ||
|
|
b068f0a673 | ||
|
|
a76d928629 | ||
|
|
bef7821f07 | ||
|
|
3b494a6103 | ||
|
|
912c52e825 | ||
|
|
cd4c071973 | ||
|
|
46a7048dcd | ||
|
|
aed0286984 | ||
|
|
e5df90f5d0 | ||
|
|
dd53428f8b | ||
|
|
908258a4f2 | ||
|
|
2efe228499 | ||
|
|
4e48753b73 | ||
|
|
e204a5a16c | ||
|
|
96264784d9 | ||
|
|
ae82849bc9 | ||
|
|
423dee8418 | ||
|
|
206237d121 | ||
|
|
5e98e53249 | ||
|
|
4ffe0be414 | ||
|
|
e828567541 | ||
|
|
859c54a03d | ||
|
|
5ff8aec4db | ||
|
|
ca47da9e63 | ||
|
|
1190ebe5aa | ||
|
|
ea25ff7158 | ||
|
|
d68e83c356 | ||
|
|
0ddf5f73ec | ||
|
|
4d9d5955ac | ||
|
|
ceaea2b72d | ||
|
|
3d628f8f22 | ||
|
|
155bec4cf2 | ||
|
|
c75c02e7a2 | ||
|
|
c787fb50b8 | ||
|
|
b229f970df | ||
|
|
f2ddb8b452 | ||
|
|
d10f1de2b4 | ||
|
|
2bb059007b | ||
|
|
9ab5adfc59 | ||
|
|
2355931c6f | ||
|
|
3d246c89cc | ||
|
|
2d0dd8db3b | ||
|
|
de678b288e | ||
|
|
e01acb3a04 | ||
|
|
56f24941ab | ||
|
|
95d5f98487 | ||
|
|
ef5b34dee0 | ||
|
|
c6e5545612 | ||
|
|
be905491bf | ||
|
|
2241e3f4d5 | ||
|
|
a8cfa7ace8 | ||
|
|
45e43b81df | ||
|
|
516a04267e | ||
|
|
2ccc54301d | ||
|
|
2a7bd0ea45 | ||
|
|
bb128bfec3 | ||
|
|
96b8240bc5 | ||
|
|
53f615df6a | ||
|
|
02b30202cd | ||
|
|
2159484dd6 | ||
|
|
e7dd88c57d | ||
|
|
d2c018fd5b | ||
|
|
bf9aa4dfcd | ||
|
|
c91cdd59c1 | ||
|
|
402c1152a7 | ||
|
|
a9ea65d652 | ||
|
|
23ad5f401a | ||
|
|
0aab1005f8 | ||
|
|
15d2abbec3 | ||
|
|
abb9b95ffe | ||
|
|
9132e52ea4 | ||
|
|
93b35c299b | ||
|
|
628a67b007 | ||
|
|
ef39016424 | ||
|
|
b89d58dfa3 | ||
|
|
242b9769c3 | ||
|
|
73ee4febab | ||
|
|
2dca48f550 | ||
|
|
4b57d9a820 | ||
|
|
9ef59a09a5 | ||
|
|
4d16a9a633 | ||
|
|
7731d51c82 | ||
|
|
07aa4e0e35 | ||
|
|
9d5bd411be | ||
|
|
62ad0a9b19 | ||
|
|
fa0b126c0c | ||
|
|
d7b4bb62a7 | ||
|
|
9619081d9b | ||
|
|
49376bfe87 | ||
|
|
a8c5564a9d | ||
|
|
caa1fd0e0e | ||
|
|
e9ca358157 | ||
|
|
bf287b76e0 | ||
|
|
a801c1fb09 | ||
|
|
2067058a60 | ||
|
|
ddc81440d5 | ||
|
|
a944afd356 | ||
|
|
7ea7bdf04d | ||
|
|
ae508df90e | ||
|
|
b623009619 | ||
|
|
ded26605f4 | ||
|
|
98490bccf6 | ||
|
|
4d99729741 | ||
|
|
5045c43944 | ||
|
|
d232cb156b | ||
|
|
45bab305f9 | ||
|
|
4fd26f287c | ||
|
|
0acc738810 | ||
|
|
d8c4ce6b50 | ||
|
|
949bd71fd0 | ||
|
|
f8999fcf31 | ||
|
|
ade4034a9d | ||
|
|
fdb6be4e24 | ||
|
|
a3001b1d3d | ||
|
|
e27c84522f | ||
|
|
1fed1282ad | ||
|
|
5b9ceef01d | ||
|
|
5fbba60b6a | ||
|
|
b0230d7e09 | ||
|
|
b015ed0ca6 | ||
|
|
4d422db010 | ||
|
|
04c4e49691 | ||
|
|
62d19b7b59 | ||
|
|
28424d734b | ||
|
|
7198ed5a2e | ||
|
|
91c58752fa | ||
|
|
25f70a5abb | ||
|
|
7661e5a76e | ||
|
|
17e84f2151 | ||
|
|
eda0dcdd97 | ||
|
|
efc94ceb27 | ||
|
|
69188ff449 | ||
|
|
48adce77cc | ||
|
|
153f4f0719 | ||
|
|
06eed650a0 | ||
|
|
cef6f84393 | ||
|
|
7ab1526c0e | ||
|
|
74b3fb6d97 | ||
|
|
d7a486668c | ||
|
|
ee95cae9a4 | ||
|
|
0ab0242f37 | ||
|
|
e93f044d99 | ||
|
|
cb82d7b081 | ||
|
|
c6f31a2893 | ||
|
|
ac4ed1e305 | ||
|
|
d7f5ccb0c2 | ||
|
|
9c34d5e361 | ||
|
|
0a9a7e753c |
No files matched your search
+6
-1
@@ -6,5 +6,10 @@
|
||||
"linters"
|
||||
],
|
||||
"lint.engine" : "FacebookFbcodeLintEngine",
|
||||
"lint.engine.single.linter" : "FbcodeCppLinter"
|
||||
"lint.engine.single.linter" : "FbcodeCppLinter",
|
||||
"base" : "git:HEAD^, hg:.^",
|
||||
"git.default-relative-commit" : "HEAD^",
|
||||
"git:arc.feature.start.default" : "origin/master",
|
||||
"arc.feature.start.default" : "master",
|
||||
"history.immutable" : false
|
||||
}
|
||||
+7
-1
@@ -1,5 +1,5 @@
|
||||
TARGETS
|
||||
build_config.mk
|
||||
make_config.mk
|
||||
|
||||
*.a
|
||||
*.arc
|
||||
@@ -20,6 +20,7 @@ build_config.mk
|
||||
*.d-e
|
||||
*.o-*
|
||||
*.swp
|
||||
*~
|
||||
|
||||
ldb
|
||||
manifest_dump
|
||||
@@ -30,9 +31,12 @@ coverage/COVERAGE_REPORT
|
||||
.gdbhistory
|
||||
package/
|
||||
.phutil_module_cache
|
||||
unity
|
||||
tags
|
||||
|
||||
java/out
|
||||
java/target
|
||||
java/test-libs
|
||||
java/*.log
|
||||
java/include/org_rocksdb_*.h
|
||||
|
||||
@@ -44,3 +48,5 @@ java/crossbuild/.vagrant
|
||||
.vagrant/
|
||||
java/**.asc
|
||||
java/javadoc
|
||||
|
||||
scan_build_report/
|
||||
+4
-4
@@ -5,15 +5,15 @@ before_install:
|
||||
# which needs the following ugly dependency incantations to build RocksDB:
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y -qq gcc-4.8 g++-4.8 zlib1g-dev libbz2-dev libsnappy-dev libjemalloc-dev
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 50
|
||||
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50
|
||||
- sudo apt-get install -y -qq gcc-4.9 g++-4.9 zlib1g-dev libbz2-dev libsnappy-dev libjemalloc-dev
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 50
|
||||
- sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 50
|
||||
- wget https://gflags.googlecode.com/files/libgflags0_2.0-1_amd64.deb
|
||||
- sudo dpkg -i libgflags0_2.0-1_amd64.deb
|
||||
- wget https://gflags.googlecode.com/files/libgflags-dev_2.0-1_amd64.deb
|
||||
- sudo dpkg -i libgflags-dev_2.0-1_amd64.deb
|
||||
# Lousy hack to disable use and testing of fallocate, which doesn't behave quite
|
||||
# as EnvPosixTest::AllocateTest expects within the Travis OpenVZ environment.
|
||||
script: OPT=-DTRAVIS make unity && make clean && OPT=-DTRAVIS make check
|
||||
script: OPT=-DTRAVIS V=1 make unity && make clean && OPT=-DTRAVIS V=1 make db_test && ./db_test
|
||||
notifications:
|
||||
email: false
|
||||
+45
-3
@@ -1,17 +1,59 @@
|
||||
# Rocksdb Change Log
|
||||
|
||||
### 3.9.0 (12/8/2014)
|
||||
## 3.10.2 (5/1/2015)
|
||||
* Make sure RocksDB is compiled with fallocate support
|
||||
* Fix possible hang in DB::Write()
|
||||
* Fix a possibility of SIGSEGV in CompactRange()
|
||||
|
||||
## 3.10.0 (3/24/2015)
|
||||
### New Features
|
||||
* GetThreadStatus() is now able to report detailed thread status, including:
|
||||
- Thread Operation including flush and compaction.
|
||||
- The stage of the current thread operation.
|
||||
- The elapsed time in micros since the current thread operation started.
|
||||
More information can be found in include/rocksdb/thread_status.h. In addition, when running db_bench with --thread_status_per_interval, db_bench will also report thread status periodically.
|
||||
* Changed the LRU caching algorithm so that referenced blocks (by iterators) are never evicted. This change made parameter removeScanCountLimit obsolete. Because of that NewLRUCache doesn't take three arguments anymore. table_cache_remove_scan_limit option is also removed
|
||||
* By default we now optimize the compilation for the compilation platform (using -march=native). If you want to build portable binary, use 'PORTABLE=1' before the make command.
|
||||
* We now allow level-compaction to place files in different paths by
|
||||
specifying them in db_paths along with the target_size.
|
||||
Lower numbered levels will be placed earlier in the db_paths and higher
|
||||
numbered levels will be placed later in the db_paths vector.
|
||||
* Potentially big performance improvements if you're using RocksDB with lots of column families (100-1000)
|
||||
* Added BlockBasedTableOptions.format_version option, which allows user to specify which version of block based table he wants. As a general guidline, newer versions have more features, but might not be readable by older versions of RocksDB.
|
||||
* Added new block based table format (version 2), which you can enable by setting BlockBasedTableOptions.format_version = 2. This format changes how we encode size information in compressed blocks and should help with memory allocations if you're using Zlib or BZip2 compressions.
|
||||
* MemEnv (env that stores data in memory) is now available in default library build. You can create it by calling NewMemEnv().
|
||||
* Add SliceTransform.SameResultWhenAppended() to help users determine it is safe to apply prefix bloom/hash.
|
||||
* Block based table now makes use of prefix bloom filter if it is a full fulter.
|
||||
* Block based table remembers whether a whole key or prefix based bloom filter is supported in SST files. Do a sanity check when reading the file with users' configuration.
|
||||
* Fixed a bug in ReadOnlyBackupEngine that deleted corrupted backups in some cases, even though the engine was ReadOnly
|
||||
* options.level_compaction_dynamic_level_bytes, a feature to allow RocksDB to pick dynamic base of bytes for levels. With this feature turned on, we will automatically adjust max bytes for each level. The goal of this feature is to have lower bound on size amplification. For more details, see comments in options.h.
|
||||
* Added an abstract base class WriteBatchBase for write batches
|
||||
* Fixed a bug where we start deleting files of a dropped column families even if there are still live references to it
|
||||
|
||||
### Public API changes
|
||||
* Deprecated skip_log_error_on_recovery and table_cache_remove_scan_count_limit options.
|
||||
* Logger method logv with log level parameter is now virtual
|
||||
|
||||
### RocksJava
|
||||
* Added compression per level API.
|
||||
* MemEnv is now available in RocksJava via RocksMemEnv class.
|
||||
* lz4 compression is now included in rocksjava static library when running `make rocksdbjavastatic`.
|
||||
* Overflowing a size_t when setting rocksdb options now throws an IllegalArgumentException, which removes the necessity for a developer to catch these Exceptions explicitly.
|
||||
|
||||
## 3.9.0 (12/8/2014)
|
||||
|
||||
### New Features
|
||||
* Add rocksdb::GetThreadList(), which in the future will return the current status of all
|
||||
rocksdb-related threads. We will have more code instruments in the following RocksDB
|
||||
releases.
|
||||
* Change convert function in rocksdb/utilities/convenience.h to return Status instead of boolean.
|
||||
Also add support for nested options in convert function
|
||||
|
||||
### Public API changes
|
||||
* New API to create a checkpoint added. Given a directory name, creates a new
|
||||
database which is an image of the existing database.
|
||||
*New API LinkFile added to Env. If you implement your own Env class, an
|
||||
implementation of the API LinkFile will have to be provided.
|
||||
* New API LinkFile added to Env. If you implement your own Env class, an
|
||||
implementation of the API LinkFile will have to be provided.
|
||||
* MemTableRep takes MemTableAllocator instead of Arena
|
||||
|
||||
### Improvements
|
||||
|
||||
+5
-11
@@ -2,7 +2,7 @@
|
||||
|
||||
RocksDB's library should be able to compile without any dependency installed,
|
||||
although we recommend installing some compression libraries (see below).
|
||||
We do depend on newer gcc with C++11 support.
|
||||
We do depend on newer gcc/clang with C++11 support.
|
||||
|
||||
There are few options when compiling RocksDB:
|
||||
|
||||
@@ -15,9 +15,9 @@ There are few options when compiling RocksDB:
|
||||
* `make all` will compile our static library, and all our tools and unit tests. Our tools
|
||||
depend on gflags. You will need to have gflags installed to run `make all`.
|
||||
|
||||
* if Intel SSE instruction set is supported, set USE_SSE=" -msse -msse4.2 " to make sure
|
||||
SSE4.2 is used to speed up CRC32 when calculating data checksum.
|
||||
|
||||
* By default the binary we produce is optimized for the platform you're compiling on
|
||||
(-march=native). If you want to build a portable binary, add 'PORTABLE=1' before
|
||||
your make commands, like this: `PORTABLE=1 make static_lib`
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -76,13 +76,7 @@ SSE4.2 is used to speed up CRC32 when calculating data checksum.
|
||||
* Install via [homebrew](http://brew.sh/).
|
||||
* If you're first time developer in MacOS, you still need to run: `xcode-select --install` in your command line.
|
||||
* run `brew tap homebrew/dupes; brew install gcc47 --use-llvm` to install gcc 4.7 (or higher).
|
||||
* Install zlib, bzip2 and snappy libraries for compression.
|
||||
* Install gflags. We have included a script
|
||||
`build_tools/mac-install-gflags.sh`, which should automatically install it (execute this file instead of runing using "source" command).
|
||||
If you installed gflags by other means (for example, `brew install gflags`),
|
||||
please set `LIBRARY_PATH` and `CPATH` accordingly.
|
||||
* Please note that some of the optimizations/features are disabled in OSX.
|
||||
We did not run any production workloads on it.
|
||||
* run `brew install rocksdb`
|
||||
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define two important pre-processing macros: `ROCKSDB_LITE` and `IOS_CROSS_COMPILE`.
|
||||
@@ -10,6 +10,7 @@ CFLAGS += ${EXTRA_CFLAGS}
|
||||
CXXFLAGS += ${EXTRA_CXXFLAGS}
|
||||
LDFLAGS += $(EXTRA_LDFLAGS)
|
||||
MACHINE ?= $(shell uname -m)
|
||||
ARFLAGS = rs
|
||||
|
||||
ifneq ($(MAKECMDGOALS),dbg)
|
||||
OPT += -O2 -fno-omit-frame-pointer
|
||||
@@ -29,11 +30,40 @@ OPT += -DNDEBUG
|
||||
endif
|
||||
|
||||
#-----------------------------------------------
|
||||
include src.mk
|
||||
|
||||
AM_DEFAULT_VERBOSITY = 0
|
||||
|
||||
AM_V_GEN = $(am__v_GEN_$(V))
|
||||
am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_GEN_0 = @echo " GEN " $@;
|
||||
am__v_GEN_1 =
|
||||
AM_V_at = $(am__v_at_$(V))
|
||||
am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_at_0 = @
|
||||
am__v_at_1 =
|
||||
|
||||
AM_V_CC = $(am__v_CC_$(V))
|
||||
am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_CC_0 = @echo " CC " $@;
|
||||
am__v_CC_1 =
|
||||
CCLD = $(CC)
|
||||
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
AM_V_CCLD = $(am__v_CCLD_$(V))
|
||||
am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_CCLD_0 = @echo " CCLD " $@;
|
||||
am__v_CCLD_1 =
|
||||
AM_V_AR = $(am__v_AR_$(V))
|
||||
am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_AR_0 = @echo " AR " $@;
|
||||
am__v_AR_1 =
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
# detect what platform we're building on
|
||||
$(shell (export ROCKSDB_ROOT="$(CURDIR)"; "$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/build_config.mk"))
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; "$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include build_config.mk
|
||||
include make_config.mk
|
||||
|
||||
ifneq ($(PLATFORM), IOS)
|
||||
CFLAGS += -g
|
||||
@@ -43,7 +73,7 @@ else
|
||||
OPT += -DNDEBUG
|
||||
endif
|
||||
|
||||
ifneq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
ifneq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
# found
|
||||
CFLAGS += -fno-exceptions
|
||||
CXXFLAGS += -fno-exceptions
|
||||
@@ -51,54 +81,100 @@ endif
|
||||
|
||||
# ASAN doesn't work well with jemalloc. If we're compiling with ASAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_ASAN
|
||||
# ASAN compile flags
|
||||
DISABLE_JEMALLOC=1
|
||||
EXEC_LDFLAGS += -fsanitize=address
|
||||
PLATFORM_CCFLAGS += -fsanitize=address
|
||||
PLATFORM_CXXFLAGS += -fsanitize=address
|
||||
endif
|
||||
|
||||
# TSAN doesn't work well with jemalloc. If we're compiling with TSAN, we should use regular malloc.
|
||||
ifdef COMPILE_WITH_TSAN
|
||||
DISABLE_JEMALLOC=1
|
||||
EXEC_LDFLAGS += -fsanitize=thread -pie
|
||||
PLATFORM_CCFLAGS += -fsanitize=thread -fPIC -DROCKSDB_TSAN_RUN
|
||||
PLATFORM_CXXFLAGS += -fsanitize=thread -fPIC -DROCKSDB_TSAN_RUN
|
||||
# Turn off -pg when enabling TSAN testing, because that induces
|
||||
# a link failure. TODO: find the root cause
|
||||
pg =
|
||||
else
|
||||
# if we're not compiling with ASAN, use jemalloc
|
||||
pg = -pg
|
||||
endif
|
||||
|
||||
ifndef DISABLE_JEMALLOC
|
||||
EXEC_LDFLAGS := $(JEMALLOC_LIB) $(EXEC_LDFLAGS)
|
||||
PLATFORM_CXXFLAGS += $(JEMALLOC_INCLUDE) -DHAVE_JEMALLOC
|
||||
PLATFORM_CCFLAGS += $(JEMALLOC_INCLUDE) -DHAVE_JEMALLOC
|
||||
endif
|
||||
|
||||
export GTEST_THROW_ON_FAILURE=1 GTEST_HAS_EXCEPTIONS=1
|
||||
GTEST_DIR = ./third-party/gtest-1.7.0/fused-src
|
||||
PLATFORM_CCFLAGS += -isystem $(GTEST_DIR)
|
||||
PLATFORM_CXXFLAGS += -isystem $(GTEST_DIR)
|
||||
|
||||
# This (the first rule) must depend on "all".
|
||||
default: all
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
|
||||
uninstall:
|
||||
@rm -rf $(INSTALL_PATH)/include/rocksdb
|
||||
@rm -rf $(INSTALL_PATH)/lib/$(LIBRARY)
|
||||
@rm -rf $(INSTALL_PATH)/lib/$(SHARED)
|
||||
rm -rf $(INSTALL_PATH)/include/rocksdb \
|
||||
$(INSTALL_PATH)/lib/$(LIBRARY) \
|
||||
$(INSTALL_PATH)/lib/$(SHARED)
|
||||
|
||||
install:
|
||||
@install -d $(INSTALL_PATH)/lib
|
||||
@for header_dir in `find "include/rocksdb" -type d`; do \
|
||||
install -d $(INSTALL_PATH)/lib
|
||||
for header_dir in `find "include/rocksdb" -type d`; do \
|
||||
install -d $(INSTALL_PATH)/$$header_dir; \
|
||||
done
|
||||
@for header in `find "include/rocksdb" -type f -name *.h`; do \
|
||||
for header in `find "include/rocksdb" -type f -name *.h`; do \
|
||||
install -C -m 644 $$header $(INSTALL_PATH)/$$header; \
|
||||
done
|
||||
@[ ! -e $(LIBRARY) ] || install -C -m 644 $(LIBRARY) $(INSTALL_PATH)/lib
|
||||
@[ ! -e $(SHARED) ] || install -C -m 644 $(SHARED) $(INSTALL_PATH)/lib
|
||||
[ ! -e $(LIBRARY) ] || install -C -m 644 $(LIBRARY) $(INSTALL_PATH)/lib
|
||||
[ ! -e $(SHARED) ] || install -C -m 644 $(SHARED) $(INSTALL_PATH)/lib
|
||||
#-------------------------------------------------
|
||||
|
||||
WARNING_FLAGS = -Wall -Werror -Wsign-compare -Wshadow
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
|
||||
CFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverloaded-virtual -Wnon-virtual-dtor
|
||||
CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers
|
||||
|
||||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
|
||||
LIBOBJECTS = $(SOURCES:.cc=.o)
|
||||
MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o)
|
||||
date := $(shell date +%F)
|
||||
git_sha := $(shell git describe HEAD 2>/dev/null)
|
||||
gen_build_version = \
|
||||
printf '%s\n' \
|
||||
'\#include "build_version.h"' \
|
||||
'const char* rocksdb_build_git_sha = \
|
||||
"rocksdb_build_git_sha:$(git_sha)";' \
|
||||
'const char* rocksdb_build_git_date = \
|
||||
"rocksdb_build_git_date:$(date)";' \
|
||||
'const char* rocksdb_build_compile_date = __DATE__;'
|
||||
|
||||
# Record the version of the source that we are compiling.
|
||||
# We keep a record of the git revision in this file. It is then built
|
||||
# as a regular source file as part of the compilation process.
|
||||
# One can run "strings executable_filename | grep _build_" to find
|
||||
# the version of the source that we used to build the executable file.
|
||||
util/build_version.cc:
|
||||
$(AM_V_GEN)$(gen_build_version) > $@.tmp
|
||||
$(AM_V_at)if test -f $@; then \
|
||||
cmp -s $@.tmp $@ && : || mv -f $@.tmp $@; else mv -f $@.tmp $@; fi
|
||||
|
||||
LIBOBJECTS = $(LIB_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_SOURCES:.cc=.o)
|
||||
|
||||
GTEST = $(GTEST_DIR)/gtest/gtest-all.o
|
||||
TESTUTIL = ./util/testutil.o
|
||||
TESTHARNESS = ./util/testharness.o $(TESTUTIL) $(MOCKOBJECTS)
|
||||
TESTHARNESS = ./util/testharness.o $(TESTUTIL) $(MOCKOBJECTS) $(GTEST)
|
||||
BENCHHARNESS = ./util/benchharness.o
|
||||
VALGRIND_ERROR = 2
|
||||
VALGRIND_DIR = build_tools/VALGRIND_LOGS
|
||||
VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
|
||||
|
||||
VALGRIND_OPTS = --error-exitcode=$(VALGRIND_ERROR) --leak-check=full
|
||||
|
||||
TESTS = \
|
||||
@@ -119,9 +195,10 @@ TESTS = \
|
||||
coding_test \
|
||||
corruption_test \
|
||||
crc32c_test \
|
||||
slice_transform_test \
|
||||
dbformat_test \
|
||||
env_test \
|
||||
blob_store_test \
|
||||
fault_injection_test \
|
||||
filelock_test \
|
||||
filename_test \
|
||||
block_based_filter_block_test \
|
||||
@@ -158,6 +235,7 @@ TESTS = \
|
||||
geodb_test \
|
||||
rate_limiter_test \
|
||||
options_test \
|
||||
event_logger_test \
|
||||
cuckoo_table_builder_test \
|
||||
cuckoo_table_reader_test \
|
||||
cuckoo_table_db_test \
|
||||
@@ -165,7 +243,9 @@ TESTS = \
|
||||
wal_manager_test \
|
||||
listener_test \
|
||||
compaction_job_test \
|
||||
thread_list_test
|
||||
thread_list_test \
|
||||
sst_dump_test \
|
||||
compact_files_test
|
||||
|
||||
SUBSET := $(shell echo $(TESTS) |sed s/^.*$(ROCKSDBTESTS_START)/$(ROCKSDBTESTS_START)/)
|
||||
|
||||
@@ -175,10 +255,9 @@ TOOLS = \
|
||||
db_stress \
|
||||
ldb \
|
||||
db_repl_stress \
|
||||
options_test \
|
||||
blob_store_bench
|
||||
options_test \
|
||||
|
||||
PROGRAMS = db_bench signal_test table_reader_bench log_and_apply_bench cache_bench perf_context_test $(TOOLS)
|
||||
PROGRAMS = db_bench signal_test table_reader_bench log_and_apply_bench cache_bench perf_context_test memtablerep_bench $(TOOLS)
|
||||
|
||||
# The library name is configurable since we are maintaining libraries of both
|
||||
# debug/release mode.
|
||||
@@ -186,7 +265,6 @@ ifeq ($(LIBNAME),)
|
||||
LIBNAME=librocksdb
|
||||
endif
|
||||
LIBRARY = ${LIBNAME}.a
|
||||
MEMENVLIBRARY = libmemenv.a
|
||||
|
||||
ROCKSDB_MAJOR = $(shell egrep "ROCKSDB_MAJOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_MINOR = $(shell egrep "ROCKSDB_MINOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
@@ -223,13 +301,13 @@ $(SHARED3): $(SHARED4)
|
||||
endif
|
||||
|
||||
$(SHARED4):
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) $(LDFLAGS) -o $@
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(LIB_SOURCES) $(LDFLAGS) -o $@
|
||||
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
|
||||
.PHONY: blackbox_crash_test check clean coverage crash_test ldb_tests package \
|
||||
release tags valgrind_check whitebox_crash_test format static_lib shared_lib all \
|
||||
dbg rocksdbjavastatic rocksdbjava install uninstall
|
||||
dbg rocksdbjavastatic rocksdbjava install uninstall analyze
|
||||
|
||||
all: $(LIBRARY) $(PROGRAMS) $(TESTS)
|
||||
|
||||
@@ -251,7 +329,7 @@ coverage:
|
||||
# Delete intermediate files
|
||||
find . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm {} \;
|
||||
|
||||
check: $(TESTS) ldb
|
||||
check: $(TESTS) $(PROGRAMS)
|
||||
for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done
|
||||
python tools/ldb_test.py
|
||||
|
||||
@@ -294,18 +372,31 @@ valgrind_check: all $(PROGRAMS) $(TESTS)
|
||||
echo $$t $$((etime - stime)) >> $(VALGRIND_DIR)/valgrind_tests_times; \
|
||||
done
|
||||
|
||||
unity.cc:
|
||||
$(shell (export ROCKSDB_ROOT="$(CURDIR)"; "$(CURDIR)/build_tools/unity" "$(CURDIR)/unity.cc"))
|
||||
analyze: clean
|
||||
$(CLANG_SCAN_BUILD) --use-analyzer=$(CLANG_ANALYZER) \
|
||||
--use-c++=$(CXX) --use-cc=$(CC) --status-bugs \
|
||||
-o $(CURDIR)/scan_build_report \
|
||||
$(MAKE) dbg
|
||||
|
||||
unity: unity.cc unity.o
|
||||
$(CXX) unity.o $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
unity.cc:
|
||||
rm -f $@ $@-t
|
||||
for source_file in $(LIB_SOURCES); do \
|
||||
echo "#include <$$source_file>" >> $@-t; \
|
||||
done
|
||||
echo 'int main(int argc, char** argv){ return 0; }' >> $@-t
|
||||
chmod a=r $@-t
|
||||
mv $@-t $@
|
||||
|
||||
unity: unity.o
|
||||
$(AM_LINK)
|
||||
|
||||
clean:
|
||||
-rm -f $(PROGRAMS) $(TESTS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) build_config.mk unity.cc
|
||||
-rm -rf ios-x86/* ios-arm/*
|
||||
-find . -name "*.[oda]" -exec rm {} \;
|
||||
-find . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm {} \;
|
||||
-rm -rf bzip2* snappy* zlib*
|
||||
rm -f $(PROGRAMS) $(TESTS) $(LIBRARY) $(SHARED) make_config.mk unity.cc
|
||||
rm -rf ios-x86 ios-arm scan_build_report
|
||||
find . -name "*.[oda]" -exec rm {} \;
|
||||
find . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm {} \;
|
||||
rm -rf bzip2* snappy* zlib* lz4*
|
||||
|
||||
tags:
|
||||
ctags * -R
|
||||
cscope -b `find . -name '*.cc'` `find . -name '*.h'`
|
||||
@@ -320,255 +411,258 @@ package:
|
||||
# Unit tests and tools
|
||||
# ---------------------------------------------------------------------------
|
||||
$(LIBRARY): $(LIBOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(LIBOBJECTS)
|
||||
$(AM_V_AR)rm -f $@
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $(LIBOBJECTS)
|
||||
|
||||
db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) db/db_bench.o $(LIBOBJECTS) $(TESTUTIL) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_bench: util/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) util/cache_bench.o $(LIBOBJECTS) $(TESTUTIL) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
memtablerep_bench: db/memtablerep_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
block_hash_index_test: table/block_hash_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/block_hash_index_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_stress: tools/db_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) tools/db_stress.o $(LIBOBJECTS) $(TESTUTIL) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_sanity_test: tools/db_sanity_test.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) tools/db_sanity_test.o $(LIBOBJECTS) $(TESTUTIL) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_repl_stress: tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
blob_store_bench: tools/blob_store_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(CXX) tools/blob_store_bench.o $(LIBOBJECTS) $(TESTUTIL) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
signal_test: util/signal_test.o $(LIBOBJECTS)
|
||||
$(CXX) util/signal_test.o $(LIBOBJECTS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_family_test: db/column_family_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/column_family_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
table_properties_collector_test: db/table_properties_collector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/table_properties_collector_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
bloom_test: util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
dynamic_bloom_test: util/dynamic_bloom_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/dynamic_bloom_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
c_test: db/c_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/c_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
blob_store_test: util/blob_store_test.o $(LIBOBJECTS) $(TESTHARNESS) $(TESTUTIL)
|
||||
$(CXX) util/blob_store_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o$@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
stringappend_test: utilities/merge_operators/string_append/stringappend_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/merge_operators/string_append/stringappend_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
redis_test: utilities/redis/redis_lists_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/redis/redis_lists_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
benchharness_test: util/benchharness_test.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS)
|
||||
$(CXX) util/benchharness_test.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS) $(EXEC_LDFLAGS) -o$@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
histogram_test: util/histogram_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/histogram_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o$@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
thread_local_test: util/thread_local_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/thread_local_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
corruption_test: db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/corruption_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
crc32c_test: util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/crc32c_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
slice_transform_test: util/slice_transform_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
|
||||
db_test: db/db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_iter_test: db/db_iter_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/db_iter_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
log_write_bench: util/log_write_bench.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/log_write_bench.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS) -pg
|
||||
$(AM_LINK) $(pg)
|
||||
|
||||
plain_table_db_test: db/plain_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/plain_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
comparator_db_test: db/comparator_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/comparator_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
table_reader_bench: table/table_reader_bench.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/table_reader_bench.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS) -pg
|
||||
$(AM_LINK) $(pg)
|
||||
|
||||
log_and_apply_bench: db/log_and_apply_bench.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS)
|
||||
$(CXX) db/log_and_apply_bench.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS) -pg
|
||||
$(AM_LINK) $(pg)
|
||||
|
||||
perf_context_test: db/perf_context_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/perf_context_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
|
||||
prefix_test: db/prefix_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/prefix_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
|
||||
backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
document_db_test: utilities/document/document_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/document/document_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
json_document_test: utilities/document/json_document_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/document/json_document_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
spatial_db_test: utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
write_batch_with_index_test: utilities/write_batch_with_index/write_batch_with_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/write_batch_with_index/write_batch_with_index_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
flush_job_test: db/flush_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/flush_job_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_job_test: db/compaction_job_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/compaction_job_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
wal_manager_test: db/wal_manager_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/wal_manager_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
dbformat_test: db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/dbformat_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_test: util/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/env_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
fault_injection_test: db/fault_injection_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
rate_limiter_test: util/rate_limiter_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/rate_limiter_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
filename_test: db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/filename_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_filter_block_test: table/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
full_filter_block_test: table/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/log_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/table_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_test: table/block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/block_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
skiplist_test: db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/skiplist_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
version_edit_test: db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/version_edit_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
version_set_test: db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/version_set_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
compaction_picker_test: db/compaction_picker_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/compaction_picker_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
version_builder_test: db/version_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/version_builder_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
file_indexer_test : db/file_indexer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/file_indexer_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
file_indexer_test: db/file_indexer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
reduce_levels_test: tools/reduce_levels_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) tools/reduce_levels_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
write_controller_test: db/write_controller_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/write_controller_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
merge_test: db/merge_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/merge_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
merger_test: table/merger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/merger_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
deletefile_test: db/deletefile_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/deletefile_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
geodb_test: utilities/geodb/geodb_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/geodb/geodb_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_builder_test: table/cuckoo_table_builder_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) table/cuckoo_table_builder_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_reader_test: table/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS)
|
||||
$(CXX) table/cuckoo_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS) $(BENCHHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
cuckoo_table_db_test: db/cuckoo_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/cuckoo_table_db_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
listener_test: db/listener_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) db/listener_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
thread_list_test: util/thread_list_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/thread_list_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
compactor_test: utilities/compaction/compactor_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) utilities/compaction/compactor_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
compact_files_test: db/compact_files_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
options_test: util/options_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/options_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
$(MEMENVLIBRARY) : $(MEMENVOBJECTS)
|
||||
rm -f $@
|
||||
$(AR) -rs $@ $(MEMENVOBJECTS)
|
||||
event_logger_test: util/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memenv_test : helpers/memenv/memenv_test.o $(MEMENVOBJECTS) $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) helpers/memenv/memenv_test.o $(MEMENVOBJECTS) $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
sst_dump_test: util/sst_dump_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
memenv_test : util/memenv_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
mock_env_test : util/mock_env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/mock_env_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
manual_compaction_test: util/manual_compaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/manual_compaction_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
rocksdb_shell: tools/shell/ShellContext.o tools/shell/ShellState.o tools/shell/LeveldbShell.o tools/shell/DBClientProxy.o tools/shell/ShellContext.h tools/shell/ShellState.h tools/shell/DBClientProxy.h $(LIBOBJECTS)
|
||||
$(CXX) tools/shell/ShellContext.o tools/shell/ShellState.o tools/shell/LeveldbShell.o tools/shell/DBClientProxy.o $(LIBOBJECTS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
DBClientProxy_test: tools/shell/test/DBClientProxyTest.o tools/shell/DBClientProxy.o $(LIBRARY)
|
||||
$(CXX) tools/shell/test/DBClientProxyTest.o tools/shell/DBClientProxy.o $(LIBRARY) $(EXEC_LDFLAGS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
filelock_test: util/filelock_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/filelock_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
auto_roll_logger_test: util/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(CXX) util/auto_roll_logger_test.o $(LIBOBJECTS) $(TESTHARNESS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
sst_dump: tools/sst_dump.o $(LIBOBJECTS)
|
||||
$(CXX) tools/sst_dump.o $(LIBOBJECTS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
ldb: tools/ldb.o $(LIBOBJECTS)
|
||||
$(CXX) tools/ldb.o $(LIBOBJECTS) $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_LINK)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Jni stuff
|
||||
@@ -615,39 +709,52 @@ libsnappy.a:
|
||||
cd snappy-1.1.1 && make
|
||||
cp snappy-1.1.1/.libs/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
-rm -rf lz4-r127
|
||||
curl -O https://codeload.github.com/Cyan4973/lz4/tar.gz/r127
|
||||
mv r127 lz4-r127.tar.gz
|
||||
tar xvzf lz4-r127.tar.gz
|
||||
cd lz4-r127/lib && make CFLAGS='-fPIC' all
|
||||
cp lz4-r127/lib/liblz4.a .
|
||||
|
||||
rocksdbjavastatic: libz.a libbz2.a libsnappy.a
|
||||
|
||||
rocksdbjavastatic: libz.a libbz2.a libsnappy.a liblz4.a
|
||||
OPT="-fPIC -DNDEBUG -O2" $(MAKE) $(LIBRARY) -j
|
||||
cd java;$(MAKE) javalib;
|
||||
rm -f ./java/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIBOBJECTS) $(COVERAGEFLAGS) libz.a libbz2.a libsnappy.a
|
||||
cd java;strip -S -x $(ROCKSDBJNILIB)
|
||||
cd java;jar -cf $(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class HISTORY*.md $(ROCKSDBJNILIB)
|
||||
cd java/javadoc;jar -cf ../$(ROCKSDB_JAVADOCS_JAR) *
|
||||
cd java;jar -cf $(ROCKSDB_SOURCES_JAR) org
|
||||
rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIBOBJECTS) $(COVERAGEFLAGS) libz.a libbz2.a libsnappy.a liblz4.a
|
||||
cd java/target;strip -S -x $(ROCKSDBJNILIB)
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
cd java/target/apidocs;jar -cf ../$(ROCKSDB_JAVADOCS_JAR) *
|
||||
cd java/src/main/java;jar -cf ../../../target/$(ROCKSDB_SOURCES_JAR) org
|
||||
|
||||
rocksdbjavastaticrelease: rocksdbjavastatic
|
||||
cd java/crossbuild && vagrant destroy -f && vagrant up linux32 && vagrant halt linux32 && vagrant up linux64 && vagrant halt linux64
|
||||
cd java;jar -cf $(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class HISTORY*.md librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-javadoc.jar -Dclassifier=javadoc
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-sources.jar -Dclassifier=sources
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux64.jar -Dclassifier=linux64
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux32.jar -Dclassifier=linux32
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-osx.jar -Dclassifier=osx
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-javadoc.jar -Dclassifier=javadoc
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-sources.jar -Dclassifier=sources
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux64.jar -Dclassifier=linux64
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-linux32.jar -Dclassifier=linux32
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-osx.jar -Dclassifier=osx
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
|
||||
rocksdbjava:
|
||||
OPT="-fPIC -DNDEBUG -O2" $(MAKE) $(LIBRARY) -j32
|
||||
cd java;$(MAKE) javalib;
|
||||
rm -f ./java/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIBOBJECTS) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
cd java;jar -cf $(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class HISTORY*.md $(ROCKSDBJNILIB)
|
||||
rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIBOBJECTS) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR) $(ROCKSDBJNILIB)
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
|
||||
jclean:
|
||||
cd java;$(MAKE) clean;
|
||||
rm -f $(ROCKSDBJNILIB)
|
||||
|
||||
jtest:
|
||||
cd java;$(MAKE) sample;$(MAKE) test;
|
||||
@@ -655,6 +762,12 @@ jtest:
|
||||
jdb_bench:
|
||||
cd java;$(MAKE) db_bench;
|
||||
|
||||
commit-prereq:
|
||||
$(MAKE) clean && $(MAKE) all check;
|
||||
$(MAKE) clean && $(MAKE) rocksdbjava;
|
||||
$(MAKE) clean && USE_CLANG=1 $(MAKE) all;
|
||||
$(MAKE) clean && OPT=-DROCKSDB_LITE $(MAKE) release;
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform-specific compilation
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -683,10 +796,10 @@ IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBu
|
||||
|
||||
else
|
||||
.cc.o:
|
||||
$(CXX) $(CXXFLAGS) -c $< -o $@ $(COVERAGEFLAGS)
|
||||
$(AM_V_CC)$(CXX) $(CXXFLAGS) -c $< -o $@ $(COVERAGEFLAGS)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
endif
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -698,17 +811,12 @@ endif
|
||||
|
||||
# The .d file indicates .cc file's dependencies on .h files. We generate such
|
||||
# dependency by g++'s -MM option, whose output is a make dependency rule.
|
||||
# The sed command makes sure the "target" file in the generated .d file has
|
||||
# the correct path prefix.
|
||||
%.d: %.cc
|
||||
$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -MM $< -o $@
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
@sed -i '' -e 's,.*:,$*.o:,' $@
|
||||
else
|
||||
@sed -i -e 's,.*:,$*.o:,' $@
|
||||
endif
|
||||
@$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) \
|
||||
-MM -MT'$@' -MT'$(<:.cc=.o)' "$<" -o '$@'
|
||||
|
||||
DEPFILES = $(filter-out util/build_version.d,$(SOURCES:.cc=.d))
|
||||
all_sources = $(LIB_SOURCES) $(TEST_BENCH_SOURCES) $(MOCK_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.d)
|
||||
|
||||
depend: $(DEPFILES)
|
||||
|
||||
@@ -721,9 +829,11 @@ ifneq ($(MAKECMDGOALS),format)
|
||||
ifneq ($(MAKECMDGOALS),jclean)
|
||||
ifneq ($(MAKECMDGOALS),jtest)
|
||||
ifneq ($(MAKECMDGOALS),package)
|
||||
ifneq ($(MAKECMDGOALS),analyze)
|
||||
-include $(DEPFILES)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
Vendored
+17
@@ -13,4 +13,21 @@ Vagrant.configure("2") do |config|
|
||||
box.vm.box = "chef/centos-6.5"
|
||||
end
|
||||
|
||||
config.vm.define "FreeBSD10" do |box|
|
||||
box.vm.guest = :freebsd
|
||||
box.vm.box = "robin/freebsd-10"
|
||||
# FreeBSD does not support 'mount_virtualbox_shared_folder', use NFS
|
||||
box.vm.synced_folder ".", "/vagrant", :nfs => true, id: "vagrant-root"
|
||||
box.vm.network "private_network", ip: "10.0.1.10"
|
||||
|
||||
# build everything after creating VM, skip using --no-provision
|
||||
box.vm.provision "shell", inline: <<-SCRIPT
|
||||
pkg install -y gmake clang35
|
||||
export CXX=/usr/local/bin/clang++35
|
||||
cd /vagrant
|
||||
gmake clean
|
||||
gmake all OPT=-g
|
||||
SCRIPT
|
||||
end
|
||||
|
||||
end
|
||||
@@ -32,7 +32,7 @@
|
||||
# 2. Once install, add the include path/lib path for gflags to CPATH and
|
||||
# LIBRARY_PATH respectively. If installed with default mode, the
|
||||
# lib and include path will be /usr/local/lib and /usr/local/include
|
||||
# Mac user can do this by running build_tools/mac-install-gflags.sh
|
||||
# Mac user can do this by having brew installed and running brew install gflags
|
||||
|
||||
OUTPUT=$1
|
||||
if test -z "$OUTPUT"; then
|
||||
@@ -48,6 +48,8 @@ COMMON_FLAGS="-DROCKSDB_PLATFORM_POSIX"
|
||||
# Default to fbcode gcc on internal fb machines
|
||||
if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
fi
|
||||
|
||||
@@ -68,6 +70,18 @@ if test -z "$TARGET_OS"; then
|
||||
TARGET_OS=`uname -s`
|
||||
fi
|
||||
|
||||
if test -z "$TARGET_ARCHITECTURE"; then
|
||||
TARGET_ARCHITECTURE=`uname -m`
|
||||
fi
|
||||
|
||||
if test -z "$CLANG_SCAN_BUILD"; then
|
||||
CLANG_SCAN_BUILD=scan-build
|
||||
fi
|
||||
|
||||
if test -z "$CLANG_ANALYZER"; then
|
||||
CLANG_ANALYZER=$(which clang++ 2> /dev/null)
|
||||
fi
|
||||
|
||||
COMMON_FLAGS="$COMMON_FLAGS ${CFLAGS}"
|
||||
CROSS_COMPILE=
|
||||
PLATFORM_CCFLAGS=
|
||||
@@ -150,30 +164,6 @@ esac
|
||||
|
||||
JAVA_LDFLAGS="$PLATFORM_LDFLAGS"
|
||||
|
||||
if test -z "$DO_NOT_RUN_BUILD_DETECT_VERSION"; then
|
||||
"$PWD/build_tools/build_detect_version"
|
||||
fi
|
||||
|
||||
# We want to make a list of all cc files within util, db, table, and helpers
|
||||
# except for the test and benchmark files. By default, find will output a list
|
||||
# of all files matching either rule, so we need to append -print to make the
|
||||
# prune take effect.
|
||||
DIRS="util db table utilities"
|
||||
|
||||
set -f # temporarily disable globbing so that our patterns arent expanded
|
||||
PRUNE_TEST="-name *test*.cc -prune"
|
||||
PRUNE_BENCH="-name *bench*.cc -prune"
|
||||
PRUNE_MOCK="-name *mock*.cc -prune"
|
||||
PORTABLE_FILES=`cd "$ROCKSDB_ROOT"; find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o $PRUNE_MOCK -o -name '*.cc' -print | sort | tr "\n" " "`
|
||||
MOCK_SOURCES=`cd "$ROCKSDB_ROOT"; find $DIRS -name '*mock*.cc' -print | grep -v "test" | sort | tr "\n" " "`
|
||||
set +f # re-enable globbing
|
||||
|
||||
# The sources consist of the portable files, plus the platform-specific port
|
||||
# file.
|
||||
echo "SOURCES=$PORTABLE_FILES $GENERIC_PORT_FILES $PORT_FILES" >> "$OUTPUT"
|
||||
echo "MOCK_SOURCES=$MOCK_SOURCES" >> "$OUTPUT"
|
||||
echo "MEMENV_SOURCES=helpers/memenv/memenv.cc" >> "$OUTPUT"
|
||||
|
||||
if [ "$CROSS_COMPILE" = "true" -o "$FBCODE_BUILD" = "true" ]; then
|
||||
# Cross-compiling; do not try any compilation tests.
|
||||
# Also don't need any compilation tests if compiling on fbcode
|
||||
@@ -182,9 +172,10 @@ else
|
||||
# Test whether fallocate is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
int fd = open("/dev/null", 0);
|
||||
fallocate(fd, 0, 0, 1024);
|
||||
fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 0, 1024);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -284,12 +275,16 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether -Wshorten-64-to-32 is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
# -Wshorten-64-to-32 breaks compilation on FreeBSD i386
|
||||
if ! [ "$TARGET_OS" = FreeBSD -a "$TARGET_ARCHITECTURE" = i386 ]; then
|
||||
# Test whether -Wshorten-64-to-32 is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -Wshorten-64-to-32 2>/dev/null <<EOF
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -Wshorten-64-to-32"
|
||||
fi
|
||||
fi
|
||||
|
||||
# shall we use HDFS?
|
||||
@@ -308,8 +303,17 @@ if test "$USE_HDFS"; then
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS $HDFS_LDFLAGS"
|
||||
fi
|
||||
|
||||
# if Intel SSE instruction set is supported, set USE_SSE=" -msse -msse4.2 "
|
||||
COMMON_FLAGS="$COMMON_FLAGS $USE_SSE"
|
||||
if [ "$TARGET_OS" = FreeBSD -a "$TARGET_ARCHITECTURE" = i386 ]; then
|
||||
# Intel SSE instructions breaks compilation on FreeBSD i386
|
||||
unset USE_SSE
|
||||
fi
|
||||
|
||||
if test "$USE_SSE"; then
|
||||
# if Intel SSE instruction set is supported, set USE_SSE=1
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse -msse4.2 "
|
||||
elif test -z "$PORTABLE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
fi
|
||||
|
||||
PLATFORM_CCFLAGS="$PLATFORM_CCFLAGS $COMMON_FLAGS"
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS $COMMON_FLAGS"
|
||||
@@ -338,3 +342,5 @@ echo "JEMALLOC_LIB=$JEMALLOC_LIB" >> "$OUTPUT"
|
||||
echo "ROCKSDB_MAJOR=$ROCKSDB_MAJOR" >> "$OUTPUT"
|
||||
echo "ROCKSDB_MINOR=$ROCKSDB_MINOR" >> "$OUTPUT"
|
||||
echo "ROCKSDB_PATCH=$ROCKSDB_PATCH" >> "$OUTPUT"
|
||||
echo "CLANG_SCAN_BUILD=$CLANG_SCAN_BUILD" >> "$OUTPUT"
|
||||
echo "CLANG_ANALYZER=$CLANG_ANALYZER" >> "$OUTPUT"
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Record the version of the source that we are compiling.
|
||||
# We keep a record of the git revision in util/version.cc. This source file
|
||||
# is then built as a regular source file as part of the compilation process.
|
||||
# One can run "strings executable_filename | grep _build_" to find the version of
|
||||
# the source that we used to build the executable file.
|
||||
|
||||
OUTFILE="$PWD/util/build_version.cc"
|
||||
|
||||
GIT_SHA=""
|
||||
if command -v git >/dev/null 2>&1; then
|
||||
GIT_SHA=$(git rev-parse HEAD 2>/dev/null)
|
||||
fi
|
||||
|
||||
cat > "${OUTFILE}" <<EOF
|
||||
#include "build_version.h"
|
||||
const char* rocksdb_build_git_sha = "rocksdb_build_git_sha:${GIT_SHA}";
|
||||
const char* rocksdb_build_git_datetime = "rocksdb_build_git_datetime:$(date)";
|
||||
const char* rocksdb_build_compile_date = __DATE__;
|
||||
const char* rocksdb_build_compile_time = __TIME__;
|
||||
EOF
|
||||
@@ -0,0 +1,19 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/963d9aeda70cc4779885b1277484fe7544a04e3e/9.0.0/platform007/9e92d53/
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/ab9f09bba370e7066cafd4eb59752db93f2e8312/2.29.1/platform007/15a3614
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
@@ -1,105 +1,148 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
# fbcode settings. It uses the latest g++ and clang compilers and also
|
||||
# uses jemalloc
|
||||
# Environment variables that change the behavior of this script:
|
||||
# PIC_BUILD -- if true, it will only take pic versions of libraries from fbcode. libraries that don't have pic variant will not be included
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_BASE="/mnt/gvfs/third-party2/libgcc/7712e757d7355cb51292454ee0b7b46a467fdfed/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/libs"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_REV=6e40560b4e0b6d690fd1cf8c7a43ad7452b04cfa
|
||||
GLIBC_INCLUDE="/mnt/gvfs/third-party2/glibc/$GLIBC_REV/2.17/gcc-4.8.1-glibc-2.17/99df8fc/include"
|
||||
GLIBC_LIBS=" -L /mnt/gvfs/third-party2/glibc/$GLIBC_REV/2.17/gcc-4.8.1-glibc-2.17/99df8fc/lib"
|
||||
BASEDIR=`dirname $BASH_SOURCE`
|
||||
source "$BASEDIR/dependencies.sh"
|
||||
|
||||
# location of snappy headers and libraries
|
||||
SNAPPY_INCLUDE=" -I /mnt/gvfs/third-party2/snappy/aef17f6c0b44b4fe408bd06f67c93701ab0a6ceb/1.0.3/gcc-4.8.1-glibc-2.17/43d84e2/include"
|
||||
SNAPPY_LIBS=" /mnt/gvfs/third-party2/snappy/aef17f6c0b44b4fe408bd06f67c93701ab0a6ceb/1.0.3/gcc-4.8.1-glibc-2.17/43d84e2/lib/libsnappy.a"
|
||||
CFLAGS=""
|
||||
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I /mnt/gvfs/third-party2/zlib/25c6216928b4d77b59ddeca0990ff6fe9ac16b81/1.2.5/gcc-4.8.1-glibc-2.17/c3f970a/include"
|
||||
ZLIB_LIBS=" /mnt/gvfs/third-party2/zlib/25c6216928b4d77b59ddeca0990ff6fe9ac16b81/1.2.5/gcc-4.8.1-glibc-2.17/c3f970a/lib/libz.a"
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I /mnt/gvfs/third-party2/bzip2/c9ef7629c2aa0024f7a416e87602f06eb88f5eac/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a/include/"
|
||||
BZIP_LIBS=" /mnt/gvfs/third-party2/bzip2/c9ef7629c2aa0024f7a416e87602f06eb88f5eac/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a/lib/libbz2.a"
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
GLIBC_LIBS=" -L $GLIBC_BASE/lib"
|
||||
|
||||
LZ4_REV=065ec7e38fe83329031f6668c43bef83eff5808b
|
||||
LZ4_INCLUDE=" -I /mnt/gvfs/third-party2/lz4/$LZ4_REV/r108/gcc-4.8.1-glibc-2.17/c3f970a/include"
|
||||
LZ4_LIBS=" /mnt/gvfs/third-party2/lz4/$LZ4_REV/r108/gcc-4.8.1-glibc-2.17/c3f970a/lib/liblz4.a"
|
||||
# snappy
|
||||
SNAPPY_INCLUDE=" -I $SNAPPY_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy.a"
|
||||
else
|
||||
SNAPPY_LIBS=" $SNAPPY_BASE/lib/libsnappy_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DSNAPPY"
|
||||
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I $ZLIB_BASE/include/"
|
||||
ZLIB_LIBS=" $ZLIB_BASE/lib/libz.a"
|
||||
CFLAGS+=" -DZLIB"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I $BZIP2_BASE/include/"
|
||||
BZIP_LIBS=" $BZIP2_BASE/lib/libbz2.a"
|
||||
CFLAGS+=" -DBZIP2"
|
||||
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I /mnt/gvfs/third-party2/gflags/1ad047a6e6f6673991918ecadc670868205a243a/1.6/gcc-4.8.1-glibc-2.17/c3f970a/include/"
|
||||
GFLAGS_LIBS=" /mnt/gvfs/third-party2/gflags/1ad047a6e6f6673991918ecadc670868205a243a/1.6/gcc-4.8.1-glibc-2.17/c3f970a/lib/libgflags.a"
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags.a"
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I /mnt/gvfs/third-party2/jemalloc/c60d854f7824f334195fe7fd34b2bc9057e3c1f9/3.6.0/gcc-4.8.1-glibc-2.17/4d53c6f/include"
|
||||
JEMALLOC_LIB=" -Wl,--whole-archive /mnt/gvfs/third-party2/jemalloc/c60d854f7824f334195fe7fd34b2bc9057e3c1f9/3.6.0/gcc-4.8.1-glibc-2.17/4d53c6f/lib/libjemalloc.a"
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
JEMALLOC_LIB=" $JEMALLOC_BASE/lib/libjemalloc.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_REV=829d10dac0230f99cd7e1778869d2adf3da24b65
|
||||
NUMA_INCLUDE=" -I /mnt/gvfs/third-party2/numa/$NUMA_REV/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a/include/"
|
||||
NUMA_LIB=" /mnt/gvfs/third-party2/numa/$NUMA_REV/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a/lib/libnuma.a"
|
||||
if test -z $PIC_BUILD; then
|
||||
# location of numa
|
||||
NUMA_INCLUDE=" -I $NUMA_BASE/include/"
|
||||
NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
CFLAGS+=" -DNUMA"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND_REV=2c060e64064559905d46fd194000d61592087bdc
|
||||
LIBUNWIND="/mnt/gvfs/third-party2/libunwind/$LIBUNWIND_REV/1.1/gcc-4.8.1-glibc-2.17/675d945/lib/libunwind.a"
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
fi
|
||||
|
||||
# location of TBB
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
else
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=" -msse -msse4.2 "
|
||||
|
||||
BINUTILS="/mnt/gvfs/third-party2/binutils/2aff2e7b474cd3e6ab23495ad1224b7d214b9f8e/2.21.1/centos6-native/da39a3e/bin"
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
GCC_BASE="/mnt/gvfs/third-party2/gcc/1ec615e23800f0815d474478ba476a0adc3fe788/4.8.1/centos6-native/cc6c9dc"
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
CLANG_BIN="$CLANG_BASE/bin"
|
||||
CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -m64 -mtune=generic"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
CLANG_BASE="/mnt/gvfs/third-party2/clang/9ab68376f938992c4eb5946ca68f90c3185cffc8/3.4"
|
||||
CLANG_INCLUDE="$CLANG_BASE/gcc-4.8.1-glibc-2.17/fb0f730/lib/clang/3.4/include"
|
||||
CC="$CLANG_BASE/centos6-native/9cefd8a/bin/clang"
|
||||
CXX="$CLANG_BASE/centos6-native/9cefd8a/bin/clang++"
|
||||
# clang
|
||||
CLANG_INCLUDE="$CLANG_LIB/clang/stable/include"
|
||||
CC="$CLANG_BIN/clang"
|
||||
CXX="$CLANG_BIN/clang++"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="/mnt/gvfs/third-party2/kernel-headers/a683ed7135276731065a9d76d3016c9731f4e2f9/3.2.18_70_fbk11_00129_gc8882d0/gcc-4.8.1-glibc-2.17/da39a3e/include/"
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1 "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1/x86_64-facebook-linux "
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CFLAGS+=" -Wno-expansion-to-defined "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_FALLOCATE_PRESENT"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DNUMA"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE -DROCKSDB_RANGESYNC_PRESENT -DROCKSDB_SCHED_GETCPU_PRESENT -DROCKSDB_SUPPORT_THREAD_LOCAL -DHAVE_SSE42"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--no-whole-archive $LIBUNWIND"
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS"
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_REV=b2a9f85e4b70cd03abc85a7f3027fbc4cef35bd0
|
||||
VALGRIND_VER="/mnt/gvfs/third-party2/valgrind/$VALGRIND_REV/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a/bin/"
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Set environment variables so that we can compile rocksdb using
|
||||
# fbcode settings. It uses the latest g++ compiler and also
|
||||
# uses jemalloc
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_BASE="/mnt/gvfs/third-party2/libgcc/7712e757d7355cb51292454ee0b7b46a467fdfed/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/libs"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_REV=6e40560b4e0b6d690fd1cf8c7a43ad7452b04cfa
|
||||
GLIBC_INCLUDE="/mnt/gvfs/third-party2/glibc/$GLIBC_REV/2.17/gcc-4.8.1-glibc-2.17/99df8fc/include"
|
||||
GLIBC_LIBS=" -L /mnt/gvfs/third-party2/glibc/$GLIBC_REV/2.17/gcc-4.8.1-glibc-2.17/99df8fc/lib"
|
||||
|
||||
# location of snappy headers and libraries
|
||||
SNAPPY_INCLUDE=" -I /mnt/gvfs/third-party2/snappy/aef17f6c0b44b4fe408bd06f67c93701ab0a6ceb/1.0.3/gcc-4.8.1-glibc-2.17/43d84e2/include"
|
||||
SNAPPY_LIBS=" /mnt/gvfs/third-party2/snappy/aef17f6c0b44b4fe408bd06f67c93701ab0a6ceb/1.0.3/gcc-4.8.1-glibc-2.17/43d84e2/lib/libsnappy.a"
|
||||
|
||||
# location of zlib headers and libraries
|
||||
ZLIB_INCLUDE=" -I /mnt/gvfs/third-party2/zlib/25c6216928b4d77b59ddeca0990ff6fe9ac16b81/1.2.5/gcc-4.8.1-glibc-2.17/c3f970a/include"
|
||||
ZLIB_LIBS=" /mnt/gvfs/third-party2/zlib/25c6216928b4d77b59ddeca0990ff6fe9ac16b81/1.2.5/gcc-4.8.1-glibc-2.17/c3f970a/lib/libz.a"
|
||||
|
||||
# location of bzip headers and libraries
|
||||
BZIP_INCLUDE=" -I /mnt/gvfs/third-party2/bzip2/c9ef7629c2aa0024f7a416e87602f06eb88f5eac/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a/include/"
|
||||
BZIP_LIBS=" /mnt/gvfs/third-party2/bzip2/c9ef7629c2aa0024f7a416e87602f06eb88f5eac/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a/lib/libbz2.a"
|
||||
|
||||
LZ4_REV=065ec7e38fe83329031f6668c43bef83eff5808b
|
||||
LZ4_INCLUDE=" -I /mnt/gvfs/third-party2/lz4/$LZ4_REV/r108/gcc-4.8.1-glibc-2.17/c3f970a/include"
|
||||
LZ4_LIBS=" /mnt/gvfs/third-party2/lz4/$LZ4_REV/r108/gcc-4.8.1-glibc-2.17/c3f970a/lib/liblz4.a"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I /mnt/gvfs/third-party2/gflags/1ad047a6e6f6673991918ecadc670868205a243a/1.6/gcc-4.8.1-glibc-2.17/c3f970a/include/"
|
||||
GFLAGS_LIBS=" /mnt/gvfs/third-party2/gflags/1ad047a6e6f6673991918ecadc670868205a243a/1.6/gcc-4.8.1-glibc-2.17/c3f970a/lib/libgflags.a"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I /mnt/gvfs/third-party2/jemalloc/c60d854f7824f334195fe7fd34b2bc9057e3c1f9/3.6.0/gcc-4.8.1-glibc-2.17/4d53c6f/include"
|
||||
JEMALLOC_LIB=" -Wl,--whole-archive /mnt/gvfs/third-party2/jemalloc/c60d854f7824f334195fe7fd34b2bc9057e3c1f9/3.6.0/gcc-4.8.1-glibc-2.17/4d53c6f/lib/libjemalloc.a"
|
||||
|
||||
# location of numa
|
||||
NUMA_REV=829d10dac0230f99cd7e1778869d2adf3da24b65
|
||||
NUMA_INCLUDE=" -I /mnt/gvfs/third-party2/numa/$NUMA_REV/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a/include/"
|
||||
NUMA_LIB=" /mnt/gvfs/third-party2/numa/$NUMA_REV/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a/lib/libnuma.a"
|
||||
|
||||
# location of libunwind
|
||||
LIBUNWIND_REV=2c060e64064559905d46fd194000d61592087bdc
|
||||
LIBUNWIND="/mnt/gvfs/third-party2/libunwind/$LIBUNWIND_REV/1.1/gcc-4.8.1-glibc-2.17/675d945/lib/libunwind.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
|
||||
BINUTILS="/mnt/gvfs/third-party2/binutils/2aff2e7b474cd3e6ab23495ad1224b7d214b9f8e/2.21.1/centos6-native/da39a3e/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
|
||||
GCC_BASE="/mnt/gvfs/third-party2/gcc/1ec615e23800f0815d474478ba476a0adc3fe788/4.8.1/centos6-native/cc6c9dc"
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
CC="$GCC_BASE/bin/gcc"
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -m64 -mtune=generic"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
else
|
||||
# clang
|
||||
CLANG_BASE="/mnt/gvfs/third-party2/clang/9ab68376f938992c4eb5946ca68f90c3185cffc8/3.4"
|
||||
CLANG_INCLUDE="$CLANG_BASE/gcc-4.8.1-glibc-2.17/fb0f730/lib/clang/3.4/include"
|
||||
CC="$CLANG_BASE/centos6-native/9cefd8a/bin/clang"
|
||||
CXX="$CLANG_BASE/centos6-native/9cefd8a/bin/clang++"
|
||||
|
||||
KERNEL_HEADERS_INCLUDE="/mnt/gvfs/third-party2/kernel-headers/a683ed7135276731065a9d76d3016c9731f4e2f9/3.2.18_70_fbk11_00129_gc8882d0/gcc-4.8.1-glibc-2.17/da39a3e/include/"
|
||||
|
||||
CFLAGS="-B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1 "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.8.1/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE/linux "
|
||||
CFLAGS+=" -isystem $KERNEL_HEADERS_INCLUDE "
|
||||
CXXFLAGS="-nostdinc++"
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_FALLOCATE_PRESENT"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DNUMA"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--no-whole-archive $LIBUNWIND"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $GFLAGS_LIBS"
|
||||
|
||||
VALGRIND_REV=b2a9f85e4b70cd03abc85a7f3027fbc4cef35bd0
|
||||
VALGRIND_VER="/mnt/gvfs/third-party2/valgrind/$VALGRIND_REV/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a/bin/"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Install gflags for mac developers.
|
||||
|
||||
set -e
|
||||
|
||||
DIR=`mktemp -d /tmp/rocksdb_gflags_XXXX`
|
||||
|
||||
cd $DIR
|
||||
wget https://gflags.googlecode.com/files/gflags-2.0.tar.gz
|
||||
tar xvfz gflags-2.0.tar.gz
|
||||
cd gflags-2.0
|
||||
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
# Add include/lib path for g++
|
||||
echo 'export LIBRARY_PATH+=":/usr/local/lib"' >> ~/.bash_profile
|
||||
echo 'export CPATH+=":/usr/local/include"' >> ~/.bash_profile
|
||||
|
||||
echo ""
|
||||
echo "-----------------------------------------------------------------------------"
|
||||
echo "| Installation Completed |"
|
||||
echo "-----------------------------------------------------------------------------"
|
||||
echo "Please run \`. ~/.bash_profile\` to be able to compile with gflags"
|
||||
@@ -37,7 +37,7 @@ fi
|
||||
|
||||
title "Adding new tag for this release ..."
|
||||
BRANCH="$ROCKSDB_VERSION.fb"
|
||||
$GIT co -b $BRANCH
|
||||
$GIT checkout -b $BRANCH
|
||||
|
||||
# Setting up the proxy for remote repo access
|
||||
title "Pushing new branch to remote repo ..."
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Create the unity file
|
||||
#
|
||||
|
||||
OUTPUT=$1
|
||||
if test -z "$OUTPUT"; then
|
||||
echo "usage: $0 <output-filename>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Delete existing file, if it exists
|
||||
rm -f "$OUTPUT"
|
||||
touch "$OUTPUT"
|
||||
|
||||
# Detect OS
|
||||
if test -z "$TARGET_OS"; then
|
||||
TARGET_OS=`uname -s`
|
||||
fi
|
||||
|
||||
# generic port files (working on all platform by #ifdef) go directly in /port
|
||||
GENERIC_PORT_FILES=`cd "$ROCKSDB_ROOT"; find port -name '*.cc' | tr "\n" " "`
|
||||
|
||||
# On GCC, we pick libc's memcmp over GCC's memcmp via -fno-builtin-memcmp
|
||||
case "$TARGET_OS" in
|
||||
Darwin)
|
||||
# PORT_FILES=port/darwin/darwin_specific.cc
|
||||
;;
|
||||
IOS)
|
||||
;;
|
||||
Linux)
|
||||
# PORT_FILES=port/linux/linux_specific.cc
|
||||
;;
|
||||
SunOS)
|
||||
# PORT_FILES=port/sunos/sunos_specific.cc
|
||||
;;
|
||||
FreeBSD)
|
||||
# PORT_FILES=port/freebsd/freebsd_specific.cc
|
||||
;;
|
||||
NetBSD)
|
||||
# PORT_FILES=port/netbsd/netbsd_specific.cc
|
||||
;;
|
||||
OpenBSD)
|
||||
# PORT_FILES=port/openbsd/openbsd_specific.cc
|
||||
;;
|
||||
DragonFly)
|
||||
# PORT_FILES=port/dragonfly/dragonfly_specific.cc
|
||||
;;
|
||||
OS_ANDROID_CROSSCOMPILE)
|
||||
# PORT_FILES=port/android/android.cc
|
||||
;;
|
||||
*)
|
||||
echo "Unknown platform!" >&2
|
||||
exit 1
|
||||
esac
|
||||
|
||||
# We want to make a list of all cc files within util, db, table, and helpers
|
||||
# except for the test and benchmark files. By default, find will output a list
|
||||
# of all files matching either rule, so we need to append -print to make the
|
||||
# prune take effect.
|
||||
DIRS="util db table utilities"
|
||||
|
||||
set -f # temporarily disable globbing so that our patterns arent expanded
|
||||
PRUNE_TEST="-name *test*.cc -prune"
|
||||
PRUNE_BENCH="-name *bench*.cc -prune"
|
||||
PORTABLE_FILES=`cd "$ROCKSDB_ROOT"; find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cc' -print | sort`
|
||||
PORTABLE_CPP=`cd "$ROCKSDB_ROOT"; find $DIRS $PRUNE_TEST -o $PRUNE_BENCH -o -name '*.cpp' -print | sort`
|
||||
set +f # re-enable globbing
|
||||
|
||||
# The sources consist of the portable files, plus the platform-specific port
|
||||
# file.
|
||||
for SOURCE_FILE in $PORTABLE_FILES $GENERIC_PORT_FILES $PORT_FILES $PORTABLE_CPP
|
||||
do
|
||||
echo "#include <$SOURCE_FILE>" >> "$OUTPUT"
|
||||
done
|
||||
|
||||
echo "int main(int argc, char** argv){ return 0; }" >> "$OUTPUT"
|
||||
|
||||
+6
-3
@@ -30,9 +30,12 @@ TableBuilder* NewTableBuilder(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file,
|
||||
const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts) {
|
||||
return ioptions.table_factory->NewTableBuilder(
|
||||
ioptions, internal_comparator, file, compression_type, compression_opts);
|
||||
const CompressionOptions& compression_opts,
|
||||
const bool skip_filters) {
|
||||
return ioptions.table_factory->NewTableBuilder(ioptions, internal_comparator,
|
||||
file, compression_type,
|
||||
compression_opts,
|
||||
skip_filters);
|
||||
}
|
||||
|
||||
Status BuildTable(const std::string& dbname, Env* env,
|
||||
|
||||
+6
-5
@@ -26,11 +26,12 @@ class VersionEdit;
|
||||
class TableBuilder;
|
||||
class WritableFile;
|
||||
|
||||
extern TableBuilder* NewTableBuilder(
|
||||
const ImmutableCFOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts);
|
||||
TableBuilder* NewTableBuilder(const ImmutableCFOptions& options,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
WritableFile* file,
|
||||
const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
const bool skip_filters = false);
|
||||
|
||||
// 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
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/utilities/backupable_db.h"
|
||||
|
||||
using rocksdb::Cache;
|
||||
using rocksdb::ColumnFamilyDescriptor;
|
||||
@@ -69,12 +70,19 @@ using rocksdb::WritableFile;
|
||||
using rocksdb::WriteBatch;
|
||||
using rocksdb::WriteOptions;
|
||||
using rocksdb::LiveFileMetaData;
|
||||
using rocksdb::BackupEngine;
|
||||
using rocksdb::BackupableDBOptions;
|
||||
using rocksdb::BackupInfo;
|
||||
using rocksdb::RestoreOptions;
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct rocksdb_t { DB* rep; };
|
||||
struct rocksdb_backup_engine_t { BackupEngine* rep; };
|
||||
struct rocksdb_backup_engine_info_t { std::vector<BackupInfo> rep; };
|
||||
struct rocksdb_restore_options_t { RestoreOptions rep; };
|
||||
struct rocksdb_iterator_t { Iterator* rep; };
|
||||
struct rocksdb_writebatch_t { WriteBatch rep; };
|
||||
struct rocksdb_snapshot_t { const Snapshot* rep; };
|
||||
@@ -117,12 +125,9 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
|
||||
virtual bool Filter(
|
||||
int level,
|
||||
const Slice& key,
|
||||
const Slice& existing_value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const {
|
||||
virtual 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;
|
||||
@@ -139,9 +144,7 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual const char* Name() const {
|
||||
return (*name_)(state_);
|
||||
}
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
@@ -154,14 +157,14 @@ struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
virtual ~rocksdb_compactionfilterfactory_t() { (*destructor_)(state_); }
|
||||
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) {
|
||||
const CompactionFilter::Context& context) override {
|
||||
rocksdb_compactionfiltercontext_t ccontext;
|
||||
ccontext.rep = context;
|
||||
CompactionFilter* cf = (*create_compaction_filter_)(state_, &ccontext);
|
||||
return std::unique_ptr<CompactionFilter>(cf);
|
||||
}
|
||||
|
||||
virtual const char* Name() const { return (*name_)(state_); }
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilterv2_t : public CompactionFilterV2 {
|
||||
@@ -178,15 +181,12 @@ struct rocksdb_compactionfilterv2_t : public CompactionFilterV2 {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
|
||||
virtual const char* Name() const {
|
||||
return (*name_)(state_);
|
||||
}
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual std::vector<bool> Filter(int level,
|
||||
const SliceVector& keys,
|
||||
const SliceVector& existing_values,
|
||||
std::vector<std::string>* new_values,
|
||||
std::vector<bool>* values_changed) const {
|
||||
virtual std::vector<bool> Filter(
|
||||
int level, const SliceVector& keys, const SliceVector& existing_values,
|
||||
std::vector<std::string>* new_values,
|
||||
std::vector<bool>* values_changed) const override {
|
||||
// Make a vector pointing to the underlying key data.
|
||||
size_t num_keys = keys.size();
|
||||
std::vector<const char*> keys_list(num_keys);
|
||||
@@ -248,12 +248,10 @@ struct rocksdb_compactionfilterfactoryv2_t : public CompactionFilterFactoryV2 {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
|
||||
virtual const char* Name() const {
|
||||
return (*name_)(state_);
|
||||
}
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual std::unique_ptr<CompactionFilterV2> CreateCompactionFilterV2(
|
||||
const CompactionFilterContext& context) {
|
||||
const CompactionFilterContext& context) override {
|
||||
struct rocksdb_compactionfiltercontext_t c_context;
|
||||
c_context.rep.is_full_compaction = context.is_full_compaction;
|
||||
c_context.rep.is_manual_compaction = context.is_manual_compaction;
|
||||
@@ -275,17 +273,16 @@ struct rocksdb_comparator_t : public Comparator {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
virtual 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 {
|
||||
return (*name_)(state_);
|
||||
}
|
||||
virtual 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 { }
|
||||
virtual void FindShortSuccessor(std::string* key) const { }
|
||||
virtual void FindShortestSeparator(std::string*,
|
||||
const Slice&) const override {}
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
};
|
||||
|
||||
struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
@@ -309,11 +306,10 @@ struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
|
||||
virtual const char* Name() const {
|
||||
return (*name_)(state_);
|
||||
}
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual void CreateFilter(const Slice* keys, int n, std::string* dst) const {
|
||||
virtual 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++) {
|
||||
@@ -331,7 +327,8 @@ struct rocksdb_filterpolicy_t : public FilterPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const {
|
||||
virtual bool KeyMayMatch(const Slice& key,
|
||||
const Slice& filter) const override {
|
||||
return (*key_match_)(state_, key.data(), key.size(),
|
||||
filter.data(), filter.size());
|
||||
}
|
||||
@@ -360,17 +357,12 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
|
||||
virtual const char* Name() const {
|
||||
return (*name_)(state_);
|
||||
}
|
||||
|
||||
virtual bool FullMerge(
|
||||
const Slice& key,
|
||||
const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const {
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool FullMerge(const Slice& key, const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
size_t n = operand_list.size();
|
||||
std::vector<const char*> operand_pointers(n);
|
||||
std::vector<size_t> operand_sizes(n);
|
||||
@@ -406,7 +398,8 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
|
||||
virtual bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value, Logger* logger) const {
|
||||
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);
|
||||
@@ -457,21 +450,19 @@ struct rocksdb_slicetransform_t : public SliceTransform {
|
||||
(*destructor_)(state_);
|
||||
}
|
||||
|
||||
virtual const char* Name() const {
|
||||
return (*name_)(state_);
|
||||
}
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual Slice Transform(const Slice& src) const {
|
||||
virtual 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 {
|
||||
virtual bool InDomain(const Slice& src) const override {
|
||||
return (*in_domain_)(state_, src.data(), src.size());
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& src) const {
|
||||
virtual bool InRange(const Slice& src) const override {
|
||||
return (*in_range_)(state_, src.data(), src.size());
|
||||
}
|
||||
};
|
||||
@@ -527,6 +518,85 @@ rocksdb_t* rocksdb_open_for_read_only(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_backup_engine_t* rocksdb_backup_engine_open(
|
||||
const rocksdb_options_t* options, const char* path, char** errptr) {
|
||||
BackupEngine* be;
|
||||
if (SaveError(errptr, BackupEngine::Open(options->rep.env,
|
||||
BackupableDBOptions(path), &be))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_backup_engine_t* result = new rocksdb_backup_engine_t;
|
||||
result->rep = be;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_create_new_backup(rocksdb_backup_engine_t* be,
|
||||
rocksdb_t* db, char** errptr) {
|
||||
SaveError(errptr, be->rep->CreateNewBackup(db->rep));
|
||||
}
|
||||
|
||||
rocksdb_restore_options_t* rocksdb_restore_options_create() {
|
||||
return new rocksdb_restore_options_t;
|
||||
}
|
||||
|
||||
void rocksdb_restore_options_destroy(rocksdb_restore_options_t* opt) {
|
||||
delete opt;
|
||||
}
|
||||
|
||||
void rocksdb_restore_options_set_keep_log_files(rocksdb_restore_options_t* opt,
|
||||
int v) {
|
||||
opt->rep.keep_log_files = v;
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_restore_db_from_latest_backup(
|
||||
rocksdb_backup_engine_t* be, const char* db_dir, const char* wal_dir,
|
||||
const rocksdb_restore_options_t* restore_options, char** errptr) {
|
||||
SaveError(errptr, be->rep->RestoreDBFromLatestBackup(std::string(db_dir),
|
||||
std::string(wal_dir),
|
||||
restore_options->rep));
|
||||
}
|
||||
|
||||
const rocksdb_backup_engine_info_t* rocksdb_backup_engine_get_backup_info(
|
||||
rocksdb_backup_engine_t* be) {
|
||||
rocksdb_backup_engine_info_t* result = new rocksdb_backup_engine_info_t;
|
||||
be->rep->GetBackupInfo(&result->rep);
|
||||
return result;
|
||||
}
|
||||
|
||||
int rocksdb_backup_engine_info_count(const rocksdb_backup_engine_info_t* info) {
|
||||
return static_cast<int>(info->rep.size());
|
||||
}
|
||||
|
||||
int64_t rocksdb_backup_engine_info_timestamp(
|
||||
const rocksdb_backup_engine_info_t* info, int index) {
|
||||
return info->rep[index].timestamp;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_backup_engine_info_backup_id(
|
||||
const rocksdb_backup_engine_info_t* info, int index) {
|
||||
return info->rep[index].backup_id;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_backup_engine_info_size(
|
||||
const rocksdb_backup_engine_info_t* info, int index) {
|
||||
return info->rep[index].size;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_backup_engine_info_number_files(
|
||||
const rocksdb_backup_engine_info_t* info, int index) {
|
||||
return info->rep[index].number_files;
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_info_destroy(
|
||||
const rocksdb_backup_engine_info_t* info) {
|
||||
delete info;
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_close(rocksdb_backup_engine_t* be) {
|
||||
delete be->rep;
|
||||
delete be;
|
||||
}
|
||||
|
||||
void rocksdb_close(rocksdb_t* db) {
|
||||
delete db->rep;
|
||||
delete db;
|
||||
@@ -1039,10 +1109,10 @@ void rocksdb_writebatch_iterate(
|
||||
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) {
|
||||
virtual 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) {
|
||||
virtual void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
@@ -1543,7 +1613,7 @@ void rocksdb_options_set_table_cache_numshardbits(
|
||||
|
||||
void rocksdb_options_set_table_cache_remove_scan_count_limit(
|
||||
rocksdb_options_t* opt, int v) {
|
||||
opt->rep.table_cache_remove_scan_count_limit = v;
|
||||
// this option is deprecated
|
||||
}
|
||||
|
||||
void rocksdb_options_set_arena_block_size(
|
||||
@@ -1826,11 +1896,12 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom(int bits_per_key) {
|
||||
struct Wrapper : public rocksdb_filterpolicy_t {
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const { return rep_->Name(); }
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const {
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
void CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const override {
|
||||
return rep_->CreateFilter(keys, n, dst);
|
||||
}
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const {
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const override {
|
||||
return rep_->KeyMayMatch(key, filter);
|
||||
}
|
||||
static void DoNothing(void*) { }
|
||||
@@ -2012,16 +2083,14 @@ rocksdb_slicetransform_t* rocksdb_slicetransform_create_fixed_prefix(size_t pref
|
||||
struct Wrapper : public rocksdb_slicetransform_t {
|
||||
const SliceTransform* rep_;
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const {
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
Slice Transform(const Slice& src) const override {
|
||||
return rep_->Transform(src);
|
||||
}
|
||||
bool InDomain(const Slice& src) const {
|
||||
bool InDomain(const Slice& src) const override {
|
||||
return rep_->InDomain(src);
|
||||
}
|
||||
bool InRange(const Slice& src) const {
|
||||
return rep_->InRange(src);
|
||||
}
|
||||
bool InRange(const Slice& src) const override { return rep_->InRange(src); }
|
||||
static void DoNothing(void*) { }
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
|
||||
+43
-1
@@ -10,9 +10,11 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
const char* phase = "";
|
||||
static char dbname[200];
|
||||
static char dbbackupname[200];
|
||||
|
||||
static void StartPhase(const char* name) {
|
||||
fprintf(stderr, "=== Test %s\n", name);
|
||||
@@ -346,6 +348,11 @@ int main(int argc, char** argv) {
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
snprintf(dbbackupname, sizeof(dbbackupname),
|
||||
"%s/rocksdb_c_test-%d-backup",
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
StartPhase("create_objects");
|
||||
cmp = rocksdb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
|
||||
env = rocksdb_create_default_env();
|
||||
@@ -381,7 +388,7 @@ int main(int argc, char** argv) {
|
||||
Free(&err);
|
||||
|
||||
StartPhase("open_error");
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
rocksdb_open(options, dbname, &err);
|
||||
CheckCondition(err != NULL);
|
||||
Free(&err);
|
||||
|
||||
@@ -396,6 +403,41 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
StartPhase("backup_and_restore");
|
||||
{
|
||||
rocksdb_destroy_db(options, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_backup_engine_t *be = rocksdb_backup_engine_open(options, dbbackupname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_backup_engine_create_new_backup(be, db, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_delete(db, woptions, "foo", 3, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_close(db);
|
||||
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_restore_options_t *restore_options = rocksdb_restore_options_create();
|
||||
rocksdb_restore_options_set_keep_log_files(restore_options, 0);
|
||||
rocksdb_backup_engine_restore_db_from_latest_backup(be, dbname, dbname, restore_options, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_restore_options_destroy(restore_options);
|
||||
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_options_set_error_if_exists(options, 1);
|
||||
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
rocksdb_backup_engine_close(be);
|
||||
}
|
||||
|
||||
StartPhase("compactall");
|
||||
rocksdb_compact_range(db, NULL, 0, NULL, 0);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
+131
-54
@@ -66,7 +66,7 @@ uint64_t SlowdownAmount(int n, double bottom, double top) {
|
||||
} // namespace
|
||||
|
||||
ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(
|
||||
ColumnFamilyData* column_family_data, DBImpl* db, port::Mutex* mutex)
|
||||
ColumnFamilyData* column_family_data, DBImpl* db, InstrumentedMutex* mutex)
|
||||
: cfd_(column_family_data), db_(db), mutex_(mutex) {
|
||||
if (cfd_ != nullptr) {
|
||||
cfd_->Ref();
|
||||
@@ -75,7 +75,9 @@ ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(
|
||||
|
||||
ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
if (cfd_ != nullptr) {
|
||||
JobContext job_context;
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
mutex_->Lock();
|
||||
if (cfd_->Unref()) {
|
||||
delete cfd_;
|
||||
@@ -98,7 +100,8 @@ const Comparator* ColumnFamilyHandleImpl::user_comparator() const {
|
||||
return cfd()->user_comparator();
|
||||
}
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const InternalKeyComparator* icmp,
|
||||
ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
const InternalKeyComparator* icmp,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions result = src;
|
||||
result.comparator = icmp;
|
||||
@@ -161,6 +164,47 @@ ColumnFamilyOptions SanitizeOptions(const InternalKeyComparator* icmp,
|
||||
result.level0_stop_writes_trigger = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
if (result.level0_stop_writes_trigger <
|
||||
result.level0_slowdown_writes_trigger ||
|
||||
result.level0_slowdown_writes_trigger <
|
||||
result.level0_file_num_compaction_trigger) {
|
||||
Warn(db_options.info_log.get(),
|
||||
"This condition must be satisfied: "
|
||||
"level0_stop_writes_trigger(%d) >= "
|
||||
"level0_slowdown_writes_trigger(%d) >= "
|
||||
"level0_file_num_compaction_trigger(%d)",
|
||||
result.level0_stop_writes_trigger,
|
||||
result.level0_slowdown_writes_trigger,
|
||||
result.level0_file_num_compaction_trigger);
|
||||
if (result.level0_slowdown_writes_trigger <
|
||||
result.level0_file_num_compaction_trigger) {
|
||||
result.level0_slowdown_writes_trigger =
|
||||
result.level0_file_num_compaction_trigger;
|
||||
}
|
||||
if (result.level0_stop_writes_trigger <
|
||||
result.level0_slowdown_writes_trigger) {
|
||||
result.level0_stop_writes_trigger = result.level0_slowdown_writes_trigger;
|
||||
}
|
||||
Warn(db_options.info_log.get(),
|
||||
"Adjust the value to "
|
||||
"level0_stop_writes_trigger(%d)"
|
||||
"level0_slowdown_writes_trigger(%d)"
|
||||
"level0_file_num_compaction_trigger(%d)",
|
||||
result.level0_stop_writes_trigger,
|
||||
result.level0_slowdown_writes_trigger,
|
||||
result.level0_file_num_compaction_trigger);
|
||||
}
|
||||
if (result.level_compaction_dynamic_level_bytes) {
|
||||
if (result.compaction_style != kCompactionStyleLevel ||
|
||||
db_options.db_paths.size() > 1U) {
|
||||
// 1. level_compaction_dynamic_level_bytes only makes sense for
|
||||
// level-based compaction.
|
||||
// 2. we don't yet know how to make both of this feature and multiple
|
||||
// DB path work.
|
||||
result.level_compaction_dynamic_level_bytes = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -181,7 +225,7 @@ SuperVersion* SuperVersion::Ref() {
|
||||
|
||||
bool SuperVersion::Unref() {
|
||||
// fetch_sub returns the previous value of ref
|
||||
uint32_t previous_refs = refs.fetch_sub(1, std::memory_order_relaxed);
|
||||
uint32_t previous_refs = refs.fetch_sub(1);
|
||||
assert(previous_refs > 0);
|
||||
return previous_refs == 1;
|
||||
}
|
||||
@@ -223,14 +267,11 @@ void SuperVersionUnrefHandle(void* ptr) {
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* _dummy_versions,
|
||||
Cache* _table_cache,
|
||||
WriteBuffer* write_buffer,
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const DBOptions* db_options,
|
||||
const EnvOptions& env_options,
|
||||
ColumnFamilySet* column_family_set)
|
||||
ColumnFamilyData::ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* _dummy_versions,
|
||||
Cache* _table_cache, WriteBuffer* write_buffer,
|
||||
const ColumnFamilyOptions& cf_options, const DBOptions* db_options,
|
||||
const EnvOptions& env_options, ColumnFamilySet* column_family_set)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(_dummy_versions),
|
||||
@@ -238,7 +279,8 @@ ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
refs_(0),
|
||||
dropped_(false),
|
||||
internal_comparator_(cf_options.comparator),
|
||||
options_(*db_options, SanitizeOptions(&internal_comparator_, cf_options)),
|
||||
options_(*db_options,
|
||||
SanitizeOptions(*db_options, &internal_comparator_, cf_options)),
|
||||
ioptions_(options_),
|
||||
mutable_cf_options_(options_, ioptions_),
|
||||
write_buffer_(write_buffer),
|
||||
@@ -250,7 +292,9 @@ ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
next_(nullptr),
|
||||
prev_(nullptr),
|
||||
log_number_(0),
|
||||
column_family_set_(column_family_set) {
|
||||
column_family_set_(column_family_set),
|
||||
pending_flush_(false),
|
||||
pending_compaction_(false) {
|
||||
Ref();
|
||||
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
@@ -285,10 +329,14 @@ ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
new LevelCompactionPicker(ioptions_, &internal_comparator_));
|
||||
}
|
||||
|
||||
Log(InfoLogLevel::INFO_LEVEL,
|
||||
ioptions_.info_log, "Options for column family \"%s\":\n",
|
||||
name.c_str());
|
||||
options_.Dump(ioptions_.info_log);
|
||||
if (column_family_set_->NumberOfColumnFamilies() < 10) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"--------------- Options for column family [%s]:\n", name.c_str());
|
||||
options_.Dump(ioptions_.info_log);
|
||||
} else {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"\t(skipping printing options)\n");
|
||||
}
|
||||
}
|
||||
|
||||
RecalculateWriteStallConditions(mutable_cf_options_);
|
||||
@@ -296,16 +344,17 @@ ColumnFamilyData::ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
|
||||
// DB mutex held
|
||||
ColumnFamilyData::~ColumnFamilyData() {
|
||||
assert(refs_ == 0);
|
||||
assert(refs_.load(std::memory_order_relaxed) == 0);
|
||||
// remove from linked list
|
||||
auto prev = prev_;
|
||||
auto next = next_;
|
||||
prev->next_ = next;
|
||||
next->prev_ = prev;
|
||||
|
||||
// it's nullptr for dummy CFD
|
||||
if (column_family_set_ != nullptr) {
|
||||
// remove from column_family_set
|
||||
if (!dropped_ && column_family_set_ != nullptr) {
|
||||
// If it's dropped, it's already removed from column family set
|
||||
// If column_family_set_ == nullptr, this is dummy CFD and not in
|
||||
// ColumnFamilySet
|
||||
column_family_set_->RemoveColumnFamily(this);
|
||||
}
|
||||
|
||||
@@ -313,6 +362,11 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
current_->Unref();
|
||||
}
|
||||
|
||||
// It would be wrong if this ColumnFamilyData is in flush_queue_ or
|
||||
// compaction_queue_ and we destroyed it
|
||||
assert(!pending_flush_);
|
||||
assert(!pending_compaction_);
|
||||
|
||||
if (super_version_ != nullptr) {
|
||||
// Release SuperVersion reference kept in ThreadLocalPtr.
|
||||
// This must be done outside of mutex_ since unref handler can lock mutex.
|
||||
@@ -345,6 +399,16 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
}
|
||||
}
|
||||
|
||||
void ColumnFamilyData::SetDropped() {
|
||||
// can't drop default CF
|
||||
assert(id_ != 0);
|
||||
dropped_ = true;
|
||||
write_controller_token_.reset();
|
||||
|
||||
// remove from column_family_set
|
||||
column_family_set_->RemoveColumnFamily(this);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
if (current_ != nullptr) {
|
||||
@@ -387,8 +451,7 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
uint64_t kHardLimitSlowdown = 1000;
|
||||
write_controller_token_ =
|
||||
write_controller->GetDelayToken(kHardLimitSlowdown);
|
||||
internal_stats_->RecordLevelNSlowdown(max_level, kHardLimitSlowdown,
|
||||
false);
|
||||
internal_stats_->RecordLevelNSlowdown(max_level, false);
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stalling writes because we hit hard limit on level %d. "
|
||||
"(%" PRIu64 "us)",
|
||||
@@ -399,7 +462,7 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
mutable_cf_options.soft_rate_limit,
|
||||
mutable_cf_options.hard_rate_limit);
|
||||
write_controller_token_ = write_controller->GetDelayToken(slowdown);
|
||||
internal_stats_->RecordLevelNSlowdown(max_level, slowdown, true);
|
||||
internal_stats_->RecordLevelNSlowdown(max_level, true);
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stalling writes because we hit soft limit on level %d (%" PRIu64
|
||||
"us)",
|
||||
@@ -418,6 +481,10 @@ void ColumnFamilyData::SetCurrent(Version* current_version) {
|
||||
current_ = current_version;
|
||||
}
|
||||
|
||||
uint64_t ColumnFamilyData::GetNumLiveVersions() const {
|
||||
return VersionSet::GetNumLiveVersions(dummy_versions_);
|
||||
}
|
||||
|
||||
MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
assert(current_ != nullptr);
|
||||
@@ -434,6 +501,10 @@ void ColumnFamilyData::CreateNewMemtable(
|
||||
mem_->Ref();
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::NeedsCompaction() const {
|
||||
return compaction_picker_->NeedsCompaction(current_->storage_info());
|
||||
}
|
||||
|
||||
Compaction* ColumnFamilyData::PickCompaction(
|
||||
const MutableCFOptions& mutable_options, LogBuffer* log_buffer) {
|
||||
auto* result = compaction_picker_->PickCompaction(
|
||||
@@ -459,7 +530,7 @@ Compaction* ColumnFamilyData::CompactRange(
|
||||
}
|
||||
|
||||
SuperVersion* ColumnFamilyData::GetReferencedSuperVersion(
|
||||
port::Mutex* db_mutex) {
|
||||
InstrumentedMutex* db_mutex) {
|
||||
SuperVersion* sv = nullptr;
|
||||
sv = GetThreadLocalSuperVersion(db_mutex);
|
||||
sv->Ref();
|
||||
@@ -470,7 +541,7 @@ SuperVersion* ColumnFamilyData::GetReferencedSuperVersion(
|
||||
}
|
||||
|
||||
SuperVersion* ColumnFamilyData::GetThreadLocalSuperVersion(
|
||||
port::Mutex* db_mutex) {
|
||||
InstrumentedMutex* db_mutex) {
|
||||
SuperVersion* sv = nullptr;
|
||||
// The SuperVersion is cached in thread local storage to avoid acquiring
|
||||
// mutex when SuperVersion does not change since the last use. When a new
|
||||
@@ -533,6 +604,32 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColumnFamilyData::NotifyOnCompactionCompleted(
|
||||
DB* db, Compaction* c, const Status& status) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
auto listeners = ioptions()->listeners;
|
||||
CompactionJobInfo info;
|
||||
info.cf_name = c->column_family_data()->GetName();
|
||||
info.status = status;
|
||||
info.output_level = c->output_level();
|
||||
for (const auto fmd : *c->inputs(c->level())) {
|
||||
info.input_files.push_back(
|
||||
TableFileName(options_.db_paths,
|
||||
fmd->fd.GetNumber(),
|
||||
fmd->fd.GetPathId()));
|
||||
}
|
||||
for (const auto newf : c->edit()->GetNewFiles()) {
|
||||
info.input_files.push_back(
|
||||
TableFileName(options_.db_paths,
|
||||
newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()));
|
||||
}
|
||||
for (auto listener : listeners) {
|
||||
listener->OnCompactionCompleted(db, info);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void ColumnFamilyData::NotifyOnFlushCompleted(
|
||||
DB* db, const std::string& file_path,
|
||||
bool triggered_flush_slowdown,
|
||||
@@ -550,13 +647,13 @@ void ColumnFamilyData::NotifyOnFlushCompleted(
|
||||
}
|
||||
|
||||
SuperVersion* ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersion* new_superversion, port::Mutex* db_mutex) {
|
||||
SuperVersion* new_superversion, InstrumentedMutex* db_mutex) {
|
||||
db_mutex->AssertHeld();
|
||||
return InstallSuperVersion(new_superversion, db_mutex, mutable_cf_options_);
|
||||
}
|
||||
|
||||
SuperVersion* ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersion* new_superversion, port::Mutex* db_mutex,
|
||||
SuperVersion* new_superversion, InstrumentedMutex* db_mutex,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
new_superversion->db_mutex = db_mutex;
|
||||
new_superversion->mutable_cf_options = mutable_cf_options;
|
||||
@@ -623,8 +720,7 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
env_options_(env_options),
|
||||
table_cache_(table_cache),
|
||||
write_buffer_(write_buffer),
|
||||
write_controller_(write_controller),
|
||||
spin_lock_(ATOMIC_FLAG_INIT) {
|
||||
write_controller_(write_controller) {
|
||||
// initialize linked list
|
||||
dummy_cfd_->prev_ = dummy_cfd_;
|
||||
dummy_cfd_->next_ = dummy_cfd_;
|
||||
@@ -681,7 +777,7 @@ size_t ColumnFamilySet::NumberOfColumnFamilies() const {
|
||||
return column_families_.size();
|
||||
}
|
||||
|
||||
// under a DB mutex
|
||||
// under a DB mutex AND write thread
|
||||
ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
const std::string& name, uint32_t id, Version* dummy_versions,
|
||||
const ColumnFamilyOptions& options) {
|
||||
@@ -690,10 +786,8 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
new ColumnFamilyData(id, name, dummy_versions, table_cache_,
|
||||
write_buffer_, options, db_options_,
|
||||
env_options_, this);
|
||||
Lock();
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
Unlock();
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
// add to linked list
|
||||
new_cfd->next_ = dummy_cfd_;
|
||||
@@ -707,19 +801,11 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
return new_cfd;
|
||||
}
|
||||
|
||||
void ColumnFamilySet::Lock() {
|
||||
// spin lock
|
||||
while (spin_lock_.test_and_set(std::memory_order_acquire)) {
|
||||
}
|
||||
}
|
||||
|
||||
void ColumnFamilySet::Unlock() { spin_lock_.clear(std::memory_order_release); }
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
void ColumnFamilySet::FreeDeadColumnFamilies() {
|
||||
autovector<ColumnFamilyData*> to_delete;
|
||||
for (auto cfd = dummy_cfd_->next_; cfd != dummy_cfd_; cfd = cfd->next_) {
|
||||
if (cfd->refs_ == 0) {
|
||||
if (cfd->refs_.load(std::memory_order_relaxed) == 0) {
|
||||
to_delete.push_back(cfd);
|
||||
}
|
||||
}
|
||||
@@ -729,30 +815,21 @@ void ColumnFamilySet::FreeDeadColumnFamilies() {
|
||||
}
|
||||
}
|
||||
|
||||
// under a DB mutex
|
||||
// under a DB mutex AND from a write thread
|
||||
void ColumnFamilySet::RemoveColumnFamily(ColumnFamilyData* cfd) {
|
||||
auto cfd_iter = column_family_data_.find(cfd->GetID());
|
||||
assert(cfd_iter != column_family_data_.end());
|
||||
Lock();
|
||||
column_family_data_.erase(cfd_iter);
|
||||
column_families_.erase(cfd->GetName());
|
||||
Unlock();
|
||||
}
|
||||
|
||||
// under a DB mutex OR from a write thread
|
||||
bool ColumnFamilyMemTablesImpl::Seek(uint32_t column_family_id) {
|
||||
if (column_family_id == 0) {
|
||||
// optimization for common case
|
||||
current_ = column_family_set_->GetDefault();
|
||||
} else {
|
||||
// maybe outside of db mutex, should lock
|
||||
column_family_set_->Lock();
|
||||
current_ = column_family_set_->GetColumnFamily(column_family_id);
|
||||
column_family_set_->Unlock();
|
||||
// TODO(icanadi) Maybe remove column family from the hash table when it's
|
||||
// dropped?
|
||||
if (current_ != nullptr && current_->IsDropped()) {
|
||||
current_ = nullptr;
|
||||
}
|
||||
}
|
||||
handle_.SetCFD(current_);
|
||||
return current_ != nullptr;
|
||||
|
||||
+82
-44
@@ -21,9 +21,10 @@
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -38,6 +39,8 @@ class InternalStats;
|
||||
class ColumnFamilyData;
|
||||
class DBImpl;
|
||||
class LogBuffer;
|
||||
class InstrumentedMutex;
|
||||
class InstrumentedMutexLock;
|
||||
|
||||
// ColumnFamilyHandleImpl is the class that clients use to access different
|
||||
// column families. It has non-trivial destructor, which gets called when client
|
||||
@@ -45,19 +48,20 @@ class LogBuffer;
|
||||
class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
public:
|
||||
// create while holding the mutex
|
||||
ColumnFamilyHandleImpl(ColumnFamilyData* cfd, DBImpl* db, port::Mutex* mutex);
|
||||
ColumnFamilyHandleImpl(
|
||||
ColumnFamilyData* cfd, DBImpl* db, InstrumentedMutex* mutex);
|
||||
// destroy without mutex
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
virtual const Comparator* user_comparator() const;
|
||||
|
||||
virtual uint32_t GetID() const;
|
||||
virtual uint32_t GetID() const override;
|
||||
virtual const std::string& GetName() const override;
|
||||
|
||||
private:
|
||||
ColumnFamilyData* cfd_;
|
||||
DBImpl* db_;
|
||||
port::Mutex* mutex_;
|
||||
InstrumentedMutex* mutex_;
|
||||
};
|
||||
|
||||
// Does not ref-count ColumnFamilyData
|
||||
@@ -91,7 +95,7 @@ struct SuperVersion {
|
||||
autovector<MemTable*> to_delete;
|
||||
// Version number of the current SuperVersion
|
||||
uint64_t version_number;
|
||||
port::Mutex* db_mutex;
|
||||
InstrumentedMutex* db_mutex;
|
||||
|
||||
// should be called outside the mutex
|
||||
SuperVersion() = default;
|
||||
@@ -118,13 +122,13 @@ struct SuperVersion {
|
||||
static void* const kSVObsolete;
|
||||
};
|
||||
|
||||
extern ColumnFamilyOptions SanitizeOptions(const InternalKeyComparator* icmp,
|
||||
extern ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
const InternalKeyComparator* icmp,
|
||||
const ColumnFamilyOptions& src);
|
||||
|
||||
class ColumnFamilySet;
|
||||
|
||||
// This class keeps all the data that a column family needs. It's mosly dumb and
|
||||
// used just to provide access to metadata.
|
||||
// This class keeps all the data that a column family needs.
|
||||
// Most methods require DB mutex held, unless otherwise noted
|
||||
class ColumnFamilyData {
|
||||
public:
|
||||
@@ -135,35 +139,37 @@ class ColumnFamilyData {
|
||||
// thread-safe
|
||||
const std::string& GetName() const { return name_; }
|
||||
|
||||
void Ref() { ++refs_; }
|
||||
// Ref() can only be called whily holding a DB mutex or during a
|
||||
// single-threaded write.
|
||||
void Ref() { refs_.fetch_add(1, std::memory_order_relaxed); }
|
||||
// will just decrease reference count to 0, but will not delete it. returns
|
||||
// true if the ref count was decreased to zero. in that case, it can be
|
||||
// deleted by the caller immediately, or later, by calling
|
||||
// FreeDeadColumnFamilies()
|
||||
// Unref() can only be called while holding a DB mutex
|
||||
bool Unref() {
|
||||
assert(refs_ > 0);
|
||||
return --refs_ == 0;
|
||||
int old_refs = refs_.fetch_sub(1, std::memory_order_relaxed);
|
||||
assert(old_refs > 0);
|
||||
return old_refs == 1;
|
||||
}
|
||||
|
||||
// This can only be called from single-threaded VersionSet::LogAndApply()
|
||||
// SetDropped() can only be called under following conditions:
|
||||
// 1) Holding a DB mutex,
|
||||
// 2) from single-threaded write thread, AND
|
||||
// 3) from single-threaded VersionSet::LogAndApply()
|
||||
// After dropping column family no other operation on that column family
|
||||
// will be executed. All the files and memory will be, however, kept around
|
||||
// until client drops the column family handle. That way, client can still
|
||||
// access data from dropped column family.
|
||||
// Column family can be dropped and still alive. In that state:
|
||||
// *) Column family is not included in the iteration.
|
||||
// *) Compaction and flush is not executed on the dropped column family.
|
||||
// *) Client can continue writing and reading from column family. However, all
|
||||
// writes stay in the current memtable.
|
||||
// *) Client can continue reading from column family. Writes will fail unless
|
||||
// WriteOptions::ignore_missing_column_families is true
|
||||
// When the dropped column family is unreferenced, then we:
|
||||
// *) Remove column family from the linked list maintained by ColumnFamilySet
|
||||
// *) delete all memory associated with that column family
|
||||
// *) delete all the files associated with that column family
|
||||
void SetDropped() {
|
||||
// can't drop default CF
|
||||
assert(id_ != 0);
|
||||
dropped_ = true;
|
||||
write_controller_token_.reset();
|
||||
}
|
||||
void SetDropped();
|
||||
bool IsDropped() const { return dropped_; }
|
||||
|
||||
// thread-safe
|
||||
@@ -202,6 +208,8 @@ class ColumnFamilyData {
|
||||
Version* current() { return current_; }
|
||||
Version* dummy_versions() { return dummy_versions_; }
|
||||
void SetCurrent(Version* current);
|
||||
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
|
||||
|
||||
MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options);
|
||||
void SetMemtable(MemTable* new_mem) { mem_ = new_mem; }
|
||||
void CreateNewMemtable(const MutableCFOptions& mutable_cf_options);
|
||||
@@ -210,8 +218,11 @@ class ColumnFamilyData {
|
||||
|
||||
// See documentation in compaction_picker.h
|
||||
// REQUIRES: DB mutex held
|
||||
bool NeedsCompaction() const;
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* PickCompaction(const MutableCFOptions& mutable_options,
|
||||
LogBuffer* log_buffer);
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* CompactRange(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
int input_level, int output_level, uint32_t output_path_id,
|
||||
@@ -231,11 +242,11 @@ class ColumnFamilyData {
|
||||
SuperVersion* GetSuperVersion() { return super_version_; }
|
||||
// thread-safe
|
||||
// Return a already referenced SuperVersion to be used safely.
|
||||
SuperVersion* GetReferencedSuperVersion(port::Mutex* db_mutex);
|
||||
SuperVersion* GetReferencedSuperVersion(InstrumentedMutex* db_mutex);
|
||||
// thread-safe
|
||||
// Get SuperVersion stored in thread local storage. If it does not exist,
|
||||
// get a reference from a current SuperVersion.
|
||||
SuperVersion* GetThreadLocalSuperVersion(port::Mutex* db_mutex);
|
||||
SuperVersion* GetThreadLocalSuperVersion(InstrumentedMutex* db_mutex);
|
||||
// Try to return SuperVersion back to thread local storage. Retrun true on
|
||||
// success and false on failure. It fails when the thread local storage
|
||||
// contains anything other than SuperVersion::kSVInUse flag.
|
||||
@@ -248,19 +259,28 @@ class ColumnFamilyData {
|
||||
// if its reference count is zero and needs deletion or nullptr if not
|
||||
// As argument takes a pointer to allocated SuperVersion to enable
|
||||
// the clients to allocate SuperVersion outside of mutex.
|
||||
// IMPORTANT: Only call this from DBImpl::InstallSuperVersion()
|
||||
SuperVersion* InstallSuperVersion(SuperVersion* new_superversion,
|
||||
port::Mutex* db_mutex,
|
||||
InstrumentedMutex* db_mutex,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
SuperVersion* InstallSuperVersion(SuperVersion* new_superversion,
|
||||
port::Mutex* db_mutex);
|
||||
InstrumentedMutex* db_mutex);
|
||||
|
||||
void ResetThreadLocalSuperVersions();
|
||||
|
||||
void NotifyOnCompactionCompleted(DB* db, Compaction* c, const Status& status);
|
||||
|
||||
void NotifyOnFlushCompleted(
|
||||
DB* db, const std::string& file_path,
|
||||
bool triggered_flush_slowdown,
|
||||
bool triggered_flush_stop);
|
||||
|
||||
// Protected by DB mutex
|
||||
void set_pending_flush(bool value) { pending_flush_ = value; }
|
||||
void set_pending_compaction(bool value) { pending_compaction_ = value; }
|
||||
bool pending_flush() { return pending_flush_; }
|
||||
bool pending_compaction() { return pending_compaction_; }
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
@@ -283,7 +303,7 @@ class ColumnFamilyData {
|
||||
Version* dummy_versions_; // Head of circular doubly-linked list of versions.
|
||||
Version* current_; // == dummy_versions->prev_
|
||||
|
||||
int refs_; // outstanding references to ColumnFamilyData
|
||||
std::atomic<int> refs_; // outstanding references to ColumnFamilyData
|
||||
bool dropped_; // true if client dropped it
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
@@ -311,8 +331,9 @@ class ColumnFamilyData {
|
||||
// This needs to be destructed before mutex_
|
||||
std::unique_ptr<ThreadLocalPtr> local_sv_;
|
||||
|
||||
// pointers for a circular linked list. we use it to support iterations
|
||||
// that can be concurrent with writes
|
||||
// pointers for a circular linked list. we use it to support iterations over
|
||||
// all column families that are alive (note: dropped column families can also
|
||||
// be alive as long as client holds a reference)
|
||||
ColumnFamilyData* next_;
|
||||
ColumnFamilyData* prev_;
|
||||
|
||||
@@ -328,21 +349,31 @@ class ColumnFamilyData {
|
||||
ColumnFamilySet* column_family_set_;
|
||||
|
||||
std::unique_ptr<WriteControllerToken> write_controller_token_;
|
||||
|
||||
// If true --> this ColumnFamily is currently present in DBImpl::flush_queue_
|
||||
bool pending_flush_;
|
||||
|
||||
// If true --> this ColumnFamily is currently present in
|
||||
// DBImpl::compaction_queue_
|
||||
bool pending_compaction_;
|
||||
};
|
||||
|
||||
// ColumnFamilySet has interesting thread-safety requirements
|
||||
// * CreateColumnFamily() or RemoveColumnFamily() -- need to protect by DB
|
||||
// mutex. Inside, column_family_data_ and column_families_ will be protected
|
||||
// by Lock() and Unlock(). CreateColumnFamily() should ONLY be called from
|
||||
// VersionSet::LogAndApply() in the normal runtime. It is also called
|
||||
// during Recovery and in DumpManifest(). RemoveColumnFamily() is called
|
||||
// from ColumnFamilyData destructor
|
||||
// * CreateColumnFamily() or RemoveColumnFamily() -- need to be protected by DB
|
||||
// mutex AND executed in the write thread.
|
||||
// CreateColumnFamily() should ONLY be called from VersionSet::LogAndApply() AND
|
||||
// single-threaded write thread. It is also called during Recovery and in
|
||||
// DumpManifest().
|
||||
// RemoveColumnFamily() is only called from SetDropped(). DB mutex needs to be
|
||||
// held and it needs to be executed from the write thread. SetDropped() also
|
||||
// guarantees that it will be called only from single-threaded LogAndApply(),
|
||||
// but this condition is not that important.
|
||||
// * Iteration -- hold DB mutex, but you can release it in the body of
|
||||
// iteration. If you release DB mutex in body, reference the column
|
||||
// family before the mutex and unreference after you unlock, since the column
|
||||
// family might get dropped when the DB mutex is released
|
||||
// * GetDefault() -- thread safe
|
||||
// * GetColumnFamily() -- either inside of DB mutex or call Lock() <-> Unlock()
|
||||
// * GetColumnFamily() -- either inside of DB mutex or from a write thread
|
||||
// * GetNextColumnFamilyID(), GetMaxColumnFamily(), UpdateMaxColumnFamily(),
|
||||
// NumberOfColumnFamilies -- inside of DB mutex
|
||||
class ColumnFamilySet {
|
||||
@@ -353,10 +384,13 @@ class ColumnFamilySet {
|
||||
explicit iterator(ColumnFamilyData* cfd)
|
||||
: current_(cfd) {}
|
||||
iterator& operator++() {
|
||||
// dummy is never dead or dropped, so this will never be infinite
|
||||
// dropped column families might still be included in this iteration
|
||||
// (we're only removing them when client drops the last reference to the
|
||||
// column family).
|
||||
// dummy is never dead, so this will never be infinite
|
||||
do {
|
||||
current_ = current_->next_;
|
||||
} while (current_->refs_ == 0 || current_->IsDropped());
|
||||
} while (current_->refs_.load(std::memory_order_relaxed) == 0);
|
||||
return *this;
|
||||
}
|
||||
bool operator!=(const iterator& other) {
|
||||
@@ -393,9 +427,6 @@ class ColumnFamilySet {
|
||||
iterator begin() { return iterator(dummy_cfd_->next_); }
|
||||
iterator end() { return iterator(dummy_cfd_); }
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
// Don't call while iterating over ColumnFamilySet
|
||||
void FreeDeadColumnFamilies();
|
||||
@@ -407,9 +438,12 @@ class ColumnFamilySet {
|
||||
void RemoveColumnFamily(ColumnFamilyData* cfd);
|
||||
|
||||
// column_families_ and column_family_data_ need to be protected:
|
||||
// * when mutating: 1. DB mutex locked first, 2. spinlock locked second
|
||||
// * when reading, either: 1. lock DB mutex, or 2. lock spinlock
|
||||
// (if both, respect the ordering to avoid deadlock!)
|
||||
// * when mutating both conditions have to be satisfied:
|
||||
// 1. DB mutex locked
|
||||
// 2. thread currently in single-threaded write thread
|
||||
// * when reading, at least one condition needs to be satisfied:
|
||||
// 1. DB mutex locked
|
||||
// 2. accessed from a single-threaded write thread
|
||||
std::unordered_map<std::string, uint32_t> column_families_;
|
||||
std::unordered_map<uint32_t, ColumnFamilyData*> column_family_data_;
|
||||
|
||||
@@ -427,7 +461,6 @@ class ColumnFamilySet {
|
||||
Cache* table_cache_;
|
||||
WriteBuffer* write_buffer_;
|
||||
WriteController* write_controller_;
|
||||
std::atomic_flag spin_lock_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
@@ -442,17 +475,22 @@ class ColumnFamilyMemTablesImpl : public ColumnFamilyMemTables {
|
||||
|
||||
// sets current_ to ColumnFamilyData with column_family_id
|
||||
// returns false if column family doesn't exist
|
||||
// REQUIRES: under a DB mutex OR from a write thread
|
||||
bool Seek(uint32_t column_family_id) override;
|
||||
|
||||
// Returns log number of the selected column family
|
||||
// REQUIRES: under a DB mutex OR from a write thread
|
||||
uint64_t GetLogNumber() const override;
|
||||
|
||||
// REQUIRES: Seek() called first
|
||||
// REQUIRES: under a DB mutex OR from a write thread
|
||||
virtual MemTable* GetMemTable() const override;
|
||||
|
||||
// Returns column family handle for the selected column family
|
||||
// REQUIRES: under a DB mutex OR from a write thread
|
||||
virtual ColumnFamilyHandle* GetColumnFamilyHandle() override;
|
||||
|
||||
// REQUIRES: under a DB mutex OR from a write thread
|
||||
virtual void CheckMemtableFull() override;
|
||||
|
||||
private:
|
||||
|
||||
+132
-23
@@ -12,8 +12,10 @@
|
||||
#include <string>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "util/coding.h"
|
||||
@@ -38,7 +40,7 @@ class EnvCounter : public EnvWrapper {
|
||||
return num_new_writable_file_;
|
||||
}
|
||||
Status NewWritableFile(const std::string& f, unique_ptr<WritableFile>* r,
|
||||
const EnvOptions& soptions) {
|
||||
const EnvOptions& soptions) override {
|
||||
++num_new_writable_file_;
|
||||
return EnvWrapper::NewWritableFile(f, r, soptions);
|
||||
}
|
||||
@@ -47,7 +49,7 @@ class EnvCounter : public EnvWrapper {
|
||||
int num_new_writable_file_;
|
||||
};
|
||||
|
||||
class ColumnFamilyTest {
|
||||
class ColumnFamilyTest : public testing::Test {
|
||||
public:
|
||||
ColumnFamilyTest() : rnd_(139) {
|
||||
env_ = new EnvCounter(Env::Default());
|
||||
@@ -115,7 +117,7 @@ class ColumnFamilyTest {
|
||||
|
||||
int GetProperty(int cf, std::string property) {
|
||||
std::string value;
|
||||
ASSERT_TRUE(dbfull()->GetProperty(handles_[cf], property, &value));
|
||||
EXPECT_TRUE(dbfull()->GetProperty(handles_[cf], property, &value));
|
||||
return std::stoi(value);
|
||||
}
|
||||
|
||||
@@ -274,7 +276,7 @@ class ColumnFamilyTest {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
EXPECT_OK(s);
|
||||
for (const auto& wal : wal_files) {
|
||||
if (wal->Type() == kAliveLogFile) {
|
||||
++ret;
|
||||
@@ -326,7 +328,14 @@ class ColumnFamilyTest {
|
||||
Random rnd_;
|
||||
};
|
||||
|
||||
TEST(ColumnFamilyTest, DontReuseColumnFamilyID) {
|
||||
class DumbLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* format, va_list ap) override {}
|
||||
virtual size_t GetLogFileSize() const override { return 0; }
|
||||
};
|
||||
|
||||
TEST_F(ColumnFamilyTest, DontReuseColumnFamilyID) {
|
||||
for (int iter = 0; iter < 3; ++iter) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two", "three"});
|
||||
@@ -353,8 +362,7 @@ TEST(ColumnFamilyTest, DontReuseColumnFamilyID) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(ColumnFamilyTest, AddDrop) {
|
||||
TEST_F(ColumnFamilyTest, AddDrop) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two", "three"});
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "fodor"));
|
||||
@@ -380,7 +388,7 @@ TEST(ColumnFamilyTest, AddDrop) {
|
||||
std::vector<std::string>({"default", "four", "three"}));
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, DropTest) {
|
||||
TEST_F(ColumnFamilyTest, DropTest) {
|
||||
// first iteration - dont reopen DB before dropping
|
||||
// second iteration - reopen DB before dropping
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
@@ -404,7 +412,7 @@ TEST(ColumnFamilyTest, DropTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, WriteBatchFailure) {
|
||||
TEST_F(ColumnFamilyTest, WriteBatchFailure) {
|
||||
Open();
|
||||
CreateColumnFamiliesAndReopen({"one", "two"});
|
||||
WriteBatch batch;
|
||||
@@ -422,7 +430,7 @@ TEST(ColumnFamilyTest, WriteBatchFailure) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, ReadWrite) {
|
||||
TEST_F(ColumnFamilyTest, ReadWrite) {
|
||||
Open();
|
||||
CreateColumnFamiliesAndReopen({"one", "two"});
|
||||
ASSERT_OK(Put(0, "foo", "v1"));
|
||||
@@ -446,7 +454,7 @@ TEST(ColumnFamilyTest, ReadWrite) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, IgnoreRecoveredLog) {
|
||||
TEST_F(ColumnFamilyTest, IgnoreRecoveredLog) {
|
||||
std::string backup_logs = dbname_ + "/backup_logs";
|
||||
|
||||
// delete old files in backup_logs directory
|
||||
@@ -521,7 +529,7 @@ TEST(ColumnFamilyTest, IgnoreRecoveredLog) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, FlushTest) {
|
||||
TEST_F(ColumnFamilyTest, FlushTest) {
|
||||
Open();
|
||||
CreateColumnFamiliesAndReopen({"one", "two"});
|
||||
ASSERT_OK(Put(0, "foo", "v1"));
|
||||
@@ -570,7 +578,7 @@ TEST(ColumnFamilyTest, FlushTest) {
|
||||
}
|
||||
|
||||
// Makes sure that obsolete log files get deleted
|
||||
TEST(ColumnFamilyTest, LogDeletionTest) {
|
||||
TEST_F(ColumnFamilyTest, LogDeletionTest) {
|
||||
db_options_.max_total_wal_size = std::numeric_limits<uint64_t>::max();
|
||||
column_family_options_.write_buffer_size = 100000; // 100KB
|
||||
Open();
|
||||
@@ -637,7 +645,7 @@ TEST(ColumnFamilyTest, LogDeletionTest) {
|
||||
}
|
||||
|
||||
// Makes sure that obsolete log files get deleted
|
||||
TEST(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
TEST_F(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
// disable flushing stale column families
|
||||
db_options_.max_total_wal_size = std::numeric_limits<uint64_t>::max();
|
||||
Open();
|
||||
@@ -731,7 +739,28 @@ TEST(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, DifferentMergeOperators) {
|
||||
TEST_F(ColumnFamilyTest, MemtableNotSupportSnapshot) {
|
||||
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 supports snapshot. Snapshot stays not supported.
|
||||
ColumnFamilyOptions second;
|
||||
CreateColumnFamilies({"second"}, {second});
|
||||
auto* s3 = dbfull()->GetSnapshot();
|
||||
ASSERT_TRUE(s3 == nullptr);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, DifferentMergeOperators) {
|
||||
Open();
|
||||
CreateColumnFamilies({"first", "second"});
|
||||
ColumnFamilyOptions default_cf, first, second;
|
||||
@@ -761,7 +790,7 @@ TEST(ColumnFamilyTest, DifferentMergeOperators) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
TEST_F(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
@@ -836,7 +865,7 @@ std::string IterStatus(Iterator* iter) {
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
TEST(ColumnFamilyTest, NewIteratorsTest) {
|
||||
TEST_F(ColumnFamilyTest, NewIteratorsTest) {
|
||||
// iter == 0 -- no tailing
|
||||
// iter == 2 -- tailing
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
@@ -881,7 +910,7 @@ TEST(ColumnFamilyTest, NewIteratorsTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, ReadOnlyDBTest) {
|
||||
TEST_F(ColumnFamilyTest, ReadOnlyDBTest) {
|
||||
Open();
|
||||
CreateColumnFamiliesAndReopen({"one", "two", "three", "four"});
|
||||
ASSERT_OK(Put(0, "a", "b"));
|
||||
@@ -931,7 +960,7 @@ TEST(ColumnFamilyTest, ReadOnlyDBTest) {
|
||||
ASSERT_TRUE(!s.ok());
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, DontRollEmptyLogs) {
|
||||
TEST_F(ColumnFamilyTest, DontRollEmptyLogs) {
|
||||
Open();
|
||||
CreateColumnFamiliesAndReopen({"one", "two", "three", "four"});
|
||||
|
||||
@@ -953,7 +982,7 @@ TEST(ColumnFamilyTest, DontRollEmptyLogs) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, FlushStaleColumnFamilies) {
|
||||
TEST_F(ColumnFamilyTest, FlushStaleColumnFamilies) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
ColumnFamilyOptions default_cf, one, two;
|
||||
@@ -982,7 +1011,7 @@ TEST(ColumnFamilyTest, FlushStaleColumnFamilies) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
TEST_F(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
Status s = TryOpen({"one", "two"});
|
||||
ASSERT_TRUE(!s.ok());
|
||||
db_options_.create_missing_column_families = true;
|
||||
@@ -991,8 +1020,88 @@ TEST(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, SanitizeOptions) {
|
||||
DBOptions db_options;
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
for (int j = 1; j <= 3; j++) {
|
||||
for (int k = 1; k <= 3; k++) {
|
||||
ColumnFamilyOptions original;
|
||||
original.level0_stop_writes_trigger = i;
|
||||
original.level0_slowdown_writes_trigger = j;
|
||||
original.level0_file_num_compaction_trigger = k;
|
||||
ColumnFamilyOptions result =
|
||||
SanitizeOptions(db_options, nullptr, original);
|
||||
ASSERT_TRUE(result.level0_stop_writes_trigger >=
|
||||
result.level0_slowdown_writes_trigger);
|
||||
ASSERT_TRUE(result.level0_slowdown_writes_trigger >=
|
||||
result.level0_file_num_compaction_trigger);
|
||||
ASSERT_TRUE(result.level0_file_num_compaction_trigger ==
|
||||
original.level0_file_num_compaction_trigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, ReadDroppedColumnFamily) {
|
||||
// iter 0 -- drop CF, don't reopen
|
||||
// iter 1 -- delete CF, reopen
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
db_options_.create_missing_column_families = true;
|
||||
db_options_.max_open_files = 20;
|
||||
// delete obsolete files always
|
||||
db_options_.delete_obsolete_files_period_micros = 0;
|
||||
Open({"default", "one", "two"});
|
||||
ColumnFamilyOptions options;
|
||||
options.level0_file_num_compaction_trigger = 100;
|
||||
options.level0_slowdown_writes_trigger = 200;
|
||||
options.level0_stop_writes_trigger = 200;
|
||||
options.write_buffer_size = 100000; // small write buffer size
|
||||
Reopen({options, options, options});
|
||||
|
||||
// 1MB should create ~10 files for each CF
|
||||
int kKeysNum = 10000;
|
||||
PutRandomData(0, kKeysNum, 100);
|
||||
PutRandomData(1, kKeysNum, 100);
|
||||
PutRandomData(2, kKeysNum, 100);
|
||||
|
||||
if (iter == 0) {
|
||||
// Drop CF two
|
||||
ASSERT_OK(db_->DropColumnFamily(handles_[2]));
|
||||
} else {
|
||||
// delete CF two
|
||||
delete handles_[2];
|
||||
handles_[2] = nullptr;
|
||||
}
|
||||
|
||||
// Add bunch more data to other CFs
|
||||
PutRandomData(0, kKeysNum, 100);
|
||||
PutRandomData(1, kKeysNum, 100);
|
||||
|
||||
if (iter == 1) {
|
||||
Reopen();
|
||||
}
|
||||
|
||||
// Since we didn't delete CF handle, RocksDB's contract guarantees that
|
||||
// we're still able to read dropped CF
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
std::unique_ptr<Iterator> iterator(
|
||||
db_->NewIterator(ReadOptions(), handles_[i]));
|
||||
int count = 0;
|
||||
for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next()) {
|
||||
ASSERT_OK(iterator->status());
|
||||
++count;
|
||||
}
|
||||
ASSERT_EQ(count, kKeysNum * ((i == 2) ? 1 : 2));
|
||||
}
|
||||
|
||||
Close();
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class CompactFilesTest : public testing::Test {
|
||||
public:
|
||||
CompactFilesTest() {
|
||||
env_ = Env::Default();
|
||||
db_name_ = test::TmpDir(env_) + "/compact_files_test";
|
||||
}
|
||||
|
||||
std::string db_name_;
|
||||
Env* env_;
|
||||
};
|
||||
|
||||
// A class which remembers the name of each flushed file.
|
||||
class FlushedFileCollector : public EventListener {
|
||||
public:
|
||||
FlushedFileCollector() {}
|
||||
~FlushedFileCollector() {}
|
||||
|
||||
virtual void OnFlushCompleted(
|
||||
DB* db, const std::string& column_family_name,
|
||||
const std::string& file_path,
|
||||
bool triggered_writes_slowdown,
|
||||
bool triggered_writes_stop) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.push_back(file_path);
|
||||
}
|
||||
|
||||
std::vector<std::string> GetFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> result;
|
||||
for (auto fname : flushed_files_) {
|
||||
result.push_back(fname);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> flushed_files_;
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
Options options;
|
||||
// to trigger compaction more easily
|
||||
const int kWriteBufferSize = 10000;
|
||||
options.create_if_missing = true;
|
||||
// Disable RocksDB background compaction.
|
||||
options.compaction_style = kCompactionStyleNone;
|
||||
// Small slowdown and stop trigger for experimental purpose.
|
||||
options.level0_slowdown_writes_trigger = 20;
|
||||
options.level0_stop_writes_trigger = 20;
|
||||
options.write_buffer_size = kWriteBufferSize;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.compression = kNoCompression;
|
||||
|
||||
// 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 = 1000; i < 2000; ++i) {
|
||||
db->Put(WriteOptions(),
|
||||
std::to_string(i),
|
||||
std::string(kWriteBufferSize / 10, 'a' + (i % 26)));
|
||||
}
|
||||
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
CompactionOptions compact_opt;
|
||||
compact_opt.compression = kNoCompression;
|
||||
compact_opt.output_file_size_limit = kWriteBufferSize * 5;
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files, 1));
|
||||
|
||||
// verify all compaction input files are deleted
|
||||
for (auto fname : l0_files) {
|
||||
ASSERT_TRUE(!env_->FileExists(fname));
|
||||
}
|
||||
delete db;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+25
-11
@@ -39,13 +39,13 @@ void Compaction::SetInputVersion(Version* _input_version) {
|
||||
edit_->SetColumnFamily(cfd_->GetID());
|
||||
}
|
||||
|
||||
Compaction::Compaction(int number_levels, int start_level, int out_level,
|
||||
Compaction::Compaction(int number_levels, int _start_level, int out_level,
|
||||
uint64_t target_file_size,
|
||||
uint64_t max_grandparent_overlap_bytes,
|
||||
uint32_t output_path_id,
|
||||
CompressionType output_compression, bool seek_compaction,
|
||||
bool deletion_compaction)
|
||||
: start_level_(start_level),
|
||||
: start_level_(_start_level),
|
||||
output_level_(out_level),
|
||||
max_output_file_size_(target_file_size),
|
||||
max_grandparent_overlap_bytes_(max_grandparent_overlap_bytes),
|
||||
@@ -140,6 +140,7 @@ bool Compaction::IsTrivialMove() const {
|
||||
num_input_levels() == 2 &&
|
||||
num_input_files(0) == 1 &&
|
||||
num_input_files(1) == 0 &&
|
||||
input(0, 0)->fd.GetPathId() == GetOutputPathId() &&
|
||||
TotalFileSize(grandparents_) <= max_grandparent_overlap_bytes_);
|
||||
}
|
||||
|
||||
@@ -241,17 +242,30 @@ void Compaction::SetupBottomMostLevel(VersionStorageInfo* vstorage,
|
||||
}
|
||||
}
|
||||
|
||||
void Compaction::ReleaseInputs() {
|
||||
if (input_version_ != nullptr) {
|
||||
input_version_->Unref();
|
||||
input_version_ = nullptr;
|
||||
}
|
||||
if (cfd_ != nullptr) {
|
||||
if (cfd_->Unref()) {
|
||||
delete cfd_;
|
||||
// Sample output:
|
||||
// If compacting 3 L0 files, 2 L3 files and 1 L4 file, and outputting to L5,
|
||||
// print: "3@0 + 2@3 + 1@4 files to L5"
|
||||
const char* Compaction::InputLevelSummary(
|
||||
InputLevelSummaryBuffer* scratch) const {
|
||||
int len = 0;
|
||||
bool is_first = true;
|
||||
for (auto& input_level : inputs_) {
|
||||
if (input_level.empty()) {
|
||||
continue;
|
||||
}
|
||||
cfd_ = nullptr;
|
||||
if (!is_first) {
|
||||
len +=
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, " + ");
|
||||
} else {
|
||||
is_first = false;
|
||||
}
|
||||
len += snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
|
||||
"%zu@%d", input_level.size(), input_level.level);
|
||||
}
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
|
||||
" files to L%d", output_level());
|
||||
|
||||
return scratch->buffer;
|
||||
}
|
||||
|
||||
void Compaction::ReleaseCompactionFiles(Status status) {
|
||||
|
||||
+10
-6
@@ -52,6 +52,8 @@ class Compaction {
|
||||
return inputs_[compaction_input_level].level;
|
||||
}
|
||||
|
||||
int start_level() const { return start_level_; }
|
||||
|
||||
// Outputs will go to this level
|
||||
int output_level() const { return output_level_; }
|
||||
|
||||
@@ -92,7 +94,7 @@ class Compaction {
|
||||
// input level.
|
||||
// REQUIREMENT: "compaction_input_level" must be >= 0 and
|
||||
// < "input_levels()"
|
||||
std::vector<FileMetaData*>* const inputs(size_t compaction_input_level) {
|
||||
const std::vector<FileMetaData*>* inputs(size_t compaction_input_level) {
|
||||
assert(compaction_input_level < inputs_.size());
|
||||
return &inputs_[compaction_input_level].files;
|
||||
}
|
||||
@@ -119,7 +121,7 @@ class Compaction {
|
||||
// moving a single input file to the next level (no merging or splitting)
|
||||
bool IsTrivialMove() const;
|
||||
|
||||
// If true, then the comaction can be done by simply deleting input files.
|
||||
// If true, then the compaction can be done by simply deleting input files.
|
||||
bool IsDeletionCompaction() const {
|
||||
return deletion_compaction_;
|
||||
}
|
||||
@@ -135,10 +137,6 @@ class Compaction {
|
||||
// before processing "internal_key".
|
||||
bool ShouldStopBefore(const Slice& internal_key);
|
||||
|
||||
// Release the input version for the compaction, once the compaction
|
||||
// is successful.
|
||||
void ReleaseInputs();
|
||||
|
||||
// Clear all files to indicate that they are not being compacted
|
||||
// Delete this compaction from the list of running compactions.
|
||||
void ReleaseCompactionFiles(Status status);
|
||||
@@ -193,6 +191,12 @@ class Compaction {
|
||||
return &inputs_[l];
|
||||
}
|
||||
|
||||
struct InputLevelSummaryBuffer {
|
||||
char buffer[128];
|
||||
};
|
||||
|
||||
const char* InputLevelSummary(InputLevelSummaryBuffer* scratch) const;
|
||||
|
||||
private:
|
||||
friend class CompactionPicker;
|
||||
friend class UniversalCompactionPicker;
|
||||
|
||||
+111
-52
@@ -50,6 +50,7 @@
|
||||
#include "util/iostats_context_imp.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/thread_status_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -201,14 +202,15 @@ struct CompactionJob::CompactionState {
|
||||
};
|
||||
|
||||
CompactionJob::CompactionJob(
|
||||
Compaction* compaction, const DBOptions& db_options,
|
||||
int job_id, Compaction* compaction, const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
VersionSet* versions, std::atomic<bool>* shutting_down,
|
||||
LogBuffer* log_buffer, Directory* db_directory, Statistics* stats,
|
||||
SnapshotList* snapshots, bool is_snapshot_supported,
|
||||
LogBuffer* log_buffer, Directory* db_directory, Directory* output_directory,
|
||||
Statistics* stats, SnapshotList* snapshots, bool is_snapshot_supported,
|
||||
std::shared_ptr<Cache> table_cache,
|
||||
std::function<uint64_t()> yield_callback)
|
||||
: compact_(new CompactionState(compaction)),
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_stats_(1),
|
||||
db_options_(db_options),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
@@ -218,25 +220,41 @@ CompactionJob::CompactionJob(
|
||||
shutting_down_(shutting_down),
|
||||
log_buffer_(log_buffer),
|
||||
db_directory_(db_directory),
|
||||
output_directory_(output_directory),
|
||||
stats_(stats),
|
||||
snapshots_(snapshots),
|
||||
is_snapshot_supported_(is_snapshot_supported),
|
||||
table_cache_(std::move(table_cache)),
|
||||
yield_callback_(std::move(yield_callback)) {}
|
||||
yield_callback_(std::move(yield_callback)) {
|
||||
ThreadStatusUtil::SetColumnFamily(
|
||||
compact_->compaction->column_family_data());
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
|
||||
}
|
||||
|
||||
CompactionJob::~CompactionJob() {
|
||||
assert(compact_ == nullptr);
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
}
|
||||
|
||||
void CompactionJob::Prepare() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PREPARE);
|
||||
compact_->CleanupBatchBuffer();
|
||||
compact_->CleanupMergedBuffer();
|
||||
|
||||
auto* compaction = compact_->compaction;
|
||||
|
||||
// Generate file_levels_ for compaction berfore making Iterator
|
||||
compact_->compaction->GenerateFileLevels();
|
||||
compaction->GenerateFileLevels();
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
assert(cfd != nullptr);
|
||||
LogToBuffer(
|
||||
log_buffer_, "[%s] Compacting %d@%d + %d@%d files, score %.2f",
|
||||
cfd->GetName().c_str(), compact_->compaction->num_input_files(0),
|
||||
compact_->compaction->level(), compact_->compaction->num_input_files(1),
|
||||
compact_->compaction->output_level(), compact_->compaction->score());
|
||||
{
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
LogToBuffer(log_buffer_, "[%s] [JOB %d] Compacting %s, score %.2f",
|
||||
cfd->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary),
|
||||
compaction->score());
|
||||
}
|
||||
char scratch[2345];
|
||||
compact_->compaction->Summary(scratch, sizeof(scratch));
|
||||
LogToBuffer(log_buffer_, "[%s] Compaction start summary: %s\n",
|
||||
@@ -268,6 +286,9 @@ void CompactionJob::Prepare() {
|
||||
}
|
||||
|
||||
Status CompactionJob::Run() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_RUN);
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Start");
|
||||
log_buffer_->FlushBufferToLog();
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
|
||||
@@ -301,6 +322,7 @@ Status CompactionJob::Run() {
|
||||
shared_ptr<Iterator> backup_input(
|
||||
versions_->MakeInputIterator(compact_->compaction));
|
||||
backup_input->SeekToFirst();
|
||||
uint64_t total_filter_time = 0;
|
||||
while (backup_input->Valid() &&
|
||||
!shutting_down_->load(std::memory_order_acquire) &&
|
||||
!cfd->IsDropped()) {
|
||||
@@ -317,8 +339,8 @@ Status CompactionJob::Run() {
|
||||
if (!ParseInternalKey(key, &ikey)) {
|
||||
// log error
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"[%s] Failed to parse key: %s",
|
||||
cfd->GetName().c_str(), key.ToString().c_str());
|
||||
"[%s] [JOB %d] Failed to parse key: %s", cfd->GetName().c_str(),
|
||||
job_id_, key.ToString().c_str());
|
||||
continue;
|
||||
} else {
|
||||
const SliceTransform* transformer =
|
||||
@@ -348,7 +370,9 @@ Status CompactionJob::Run() {
|
||||
// Now prefix changes, this batch is done.
|
||||
// Call compaction filter on the buffered values to change the value
|
||||
if (compact_->key_str_buf_.size() > 0) {
|
||||
CallCompactionFilterV2(compaction_filter_v2);
|
||||
uint64_t time = 0;
|
||||
CallCompactionFilterV2(compaction_filter_v2, &time);
|
||||
total_filter_time += time;
|
||||
}
|
||||
compact_->cur_prefix_ = key_prefix.ToString();
|
||||
}
|
||||
@@ -380,7 +404,9 @@ Status CompactionJob::Run() {
|
||||
if (!backup_input->Valid()) {
|
||||
// If this is the single last value, we need to merge it.
|
||||
if (compact_->key_str_buf_.size() > 0) {
|
||||
CallCompactionFilterV2(compaction_filter_v2);
|
||||
uint64_t time = 0;
|
||||
CallCompactionFilterV2(compaction_filter_v2, &time);
|
||||
total_filter_time += time;
|
||||
}
|
||||
compact_->MergeKeyValueSliceBuffer(&cfd->internal_comparator());
|
||||
|
||||
@@ -396,11 +422,14 @@ Status CompactionJob::Run() {
|
||||
// finish the last batch
|
||||
if (status.ok()) {
|
||||
if (compact_->key_str_buf_.size() > 0) {
|
||||
CallCompactionFilterV2(compaction_filter_v2);
|
||||
uint64_t time = 0;
|
||||
CallCompactionFilterV2(compaction_filter_v2, &time);
|
||||
total_filter_time += time;
|
||||
}
|
||||
compact_->MergeKeyValueSliceBuffer(&cfd->internal_comparator());
|
||||
status = ProcessKeyValueCompaction(&imm_micros, input.get(), true);
|
||||
}
|
||||
RecordTick(stats_, FILTER_OPERATION_TOTAL_TIME, total_filter_time);
|
||||
} // checking for compaction filter v2
|
||||
|
||||
if (status.ok() &&
|
||||
@@ -416,8 +445,8 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
input.reset();
|
||||
|
||||
if (db_directory_ && !db_options_.disableDataSync) {
|
||||
db_directory_->Fsync();
|
||||
if (output_directory_ && !db_options_.disableDataSync) {
|
||||
output_directory_->Fsync();
|
||||
}
|
||||
|
||||
compaction_stats_.micros = env_->NowMicros() - start_micros - imm_micros;
|
||||
@@ -459,10 +488,13 @@ Status CompactionJob::Run() {
|
||||
RecordCompactionIOStats();
|
||||
|
||||
LogFlush(db_options_.info_log);
|
||||
TEST_SYNC_POINT("CompactionJob::Run():End");
|
||||
return status;
|
||||
}
|
||||
|
||||
void CompactionJob::Install(Status* status, port::Mutex* db_mutex) {
|
||||
void CompactionJob::Install(Status* status, InstrumentedMutex* db_mutex) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_INSTALL);
|
||||
db_mutex->AssertHeld();
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
cfd->internal_stats()->AddCompactionStats(
|
||||
@@ -499,6 +531,8 @@ void CompactionJob::Install(Status* status, port::Mutex* db_mutex) {
|
||||
Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
|
||||
Iterator* input,
|
||||
bool is_compaction_v2) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
size_t combined_idx = 0;
|
||||
Status status;
|
||||
std::string compaction_filter_value;
|
||||
@@ -524,10 +558,15 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
|
||||
compaction_filter = compaction_filter_from_factory.get();
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
|
||||
int64_t key_drop_user = 0;
|
||||
int64_t key_drop_newer_entry = 0;
|
||||
int64_t key_drop_obsolete = 0;
|
||||
int64_t loop_cnt = 0;
|
||||
|
||||
StopWatchNano timer(env_, stats_ != nullptr);
|
||||
uint64_t total_filter_time = 0;
|
||||
while (input->Valid() && !shutting_down_->load(std::memory_order_acquire) &&
|
||||
!cfd->IsDropped() && status.ok()) {
|
||||
compact_->num_input_records++;
|
||||
@@ -614,9 +653,13 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
|
||||
// the entry with a delete marker.
|
||||
bool value_changed = false;
|
||||
compaction_filter_value.clear();
|
||||
if (stats_ != nullptr) {
|
||||
timer.Start();
|
||||
}
|
||||
bool to_delete = compaction_filter->Filter(
|
||||
compact_->compaction->level(), ikey.user_key, value,
|
||||
&compaction_filter_value, &value_changed);
|
||||
total_filter_time += timer.ElapsedNanos();
|
||||
if (to_delete) {
|
||||
// make a copy of the original key and convert it to a delete
|
||||
delete_key.SetInternalKey(ExtractUserKey(key), ikey.sequence,
|
||||
@@ -684,7 +727,7 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
|
||||
// optimization in BuildTable.
|
||||
int steps = 0;
|
||||
merge.MergeUntil(input, prev_snapshot, bottommost_level_,
|
||||
db_options_.statistics.get(), &steps);
|
||||
db_options_.statistics.get(), &steps, env_);
|
||||
// Skip the Merge ops
|
||||
combined_idx = combined_idx - 1 + steps;
|
||||
|
||||
@@ -816,6 +859,7 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
|
||||
input->Next();
|
||||
}
|
||||
}
|
||||
RecordTick(stats_, FILTER_OPERATION_TOTAL_TIME, total_filter_time);
|
||||
if (key_drop_user > 0) {
|
||||
RecordTick(stats_, COMPACTION_KEY_DROP_USER, key_drop_user);
|
||||
}
|
||||
@@ -831,10 +875,12 @@ Status CompactionJob::ProcessKeyValueCompaction(int64_t* imm_micros,
|
||||
}
|
||||
|
||||
void CompactionJob::CallCompactionFilterV2(
|
||||
CompactionFilterV2* compaction_filter_v2) {
|
||||
CompactionFilterV2* compaction_filter_v2, uint64_t* time) {
|
||||
if (compact_ == nullptr || compaction_filter_v2 == nullptr) {
|
||||
return;
|
||||
}
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_FILTER_V2);
|
||||
|
||||
// Assemble slice vectors for user keys and existing values.
|
||||
// We also keep track of our parsed internal key structs because
|
||||
@@ -859,10 +905,11 @@ void CompactionJob::CallCompactionFilterV2(
|
||||
// filter.
|
||||
// If the return value of the compaction filter is true, replace
|
||||
// the entry with a delete marker.
|
||||
StopWatchNano timer(env_, stats_ != nullptr);
|
||||
compact_->to_delete_buf_ = compaction_filter_v2->Filter(
|
||||
compact_->compaction->level(), user_key_buf, existing_value_buf,
|
||||
&compact_->new_value_buf_, &compact_->value_changed_buf_);
|
||||
|
||||
*time = timer.ElapsedNanos();
|
||||
// new_value_buf_.size() <= to_delete__buf_.size(). "=" iff all
|
||||
// kv-pairs in this compaction run needs to be deleted.
|
||||
assert(compact_->to_delete_buf_.size() == compact_->key_str_buf_.size());
|
||||
@@ -893,6 +940,8 @@ void CompactionJob::CallCompactionFilterV2(
|
||||
}
|
||||
|
||||
Status CompactionJob::FinishCompactionOutputFile(Iterator* input) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_SYNC_FILE);
|
||||
assert(compact_ != nullptr);
|
||||
assert(compact_->outfile);
|
||||
assert(compact_->builder != nullptr);
|
||||
@@ -938,53 +987,53 @@ Status CompactionJob::FinishCompactionOutputFile(Iterator* input) {
|
||||
s = iter->status();
|
||||
delete iter;
|
||||
if (s.ok()) {
|
||||
Log(InfoLogLevel::DEBUG_LEVEL, db_options_.info_log,
|
||||
"[%s] Generated table #%" PRIu64 ": %" PRIu64
|
||||
" keys, %" PRIu64 " bytes", cfd->GetName().c_str(),
|
||||
output_number, current_entries, current_bytes);
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Generated table #%" PRIu64 ": %" PRIu64
|
||||
" keys, %" PRIu64 " bytes",
|
||||
cfd->GetName().c_str(), job_id_, output_number, current_entries,
|
||||
current_bytes);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status CompactionJob::InstallCompactionResults(port::Mutex* db_mutex) {
|
||||
Status CompactionJob::InstallCompactionResults(InstrumentedMutex* db_mutex) {
|
||||
db_mutex->AssertHeld();
|
||||
|
||||
auto* compaction = compact_->compaction;
|
||||
// paranoia: verify that the files that we started with
|
||||
// still exist in the current version and in the same original level.
|
||||
// This ensures that a concurrent compaction did not erroneously
|
||||
// pick the same files to compact_.
|
||||
if (!versions_->VerifyCompactionFileConsistency(compact_->compaction)) {
|
||||
if (!versions_->VerifyCompactionFileConsistency(compaction)) {
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"[%s] Compaction %d@%d + %d@%d files aborted",
|
||||
compact_->compaction->column_family_data()->GetName().c_str(),
|
||||
compact_->compaction->num_input_files(0), compact_->compaction->level(),
|
||||
compact_->compaction->num_input_files(1),
|
||||
compact_->compaction->output_level());
|
||||
"[%s] [JOB %d] Compaction %s aborted",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary));
|
||||
return Status::Corruption("Compaction input files inconsistent");
|
||||
}
|
||||
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Compacted %d@%d + %d@%d files => %" PRIu64 " bytes",
|
||||
compact_->compaction->column_family_data()->GetName().c_str(),
|
||||
compact_->compaction->num_input_files(0),
|
||||
compact_->compaction->level(),
|
||||
compact_->compaction->num_input_files(1),
|
||||
compact_->compaction->output_level(),
|
||||
compact_->total_bytes);
|
||||
{
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Compacted %s => %" PRIu64 " bytes",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary), compact_->total_bytes);
|
||||
}
|
||||
|
||||
// Add compaction outputs
|
||||
compact_->compaction->AddInputDeletions(compact_->compaction->edit());
|
||||
compaction->AddInputDeletions(compact_->compaction->edit());
|
||||
for (size_t i = 0; i < compact_->outputs.size(); i++) {
|
||||
const CompactionState::Output& out = compact_->outputs[i];
|
||||
compact_->compaction->edit()->AddFile(
|
||||
compact_->compaction->output_level(), out.number, out.path_id,
|
||||
out.file_size, out.smallest, out.largest, out.smallest_seqno,
|
||||
out.largest_seqno);
|
||||
compaction->edit()->AddFile(
|
||||
compaction->output_level(), out.number, out.path_id, out.file_size,
|
||||
out.smallest, out.largest, out.smallest_seqno, out.largest_seqno);
|
||||
}
|
||||
return versions_->LogAndApply(
|
||||
compact_->compaction->column_family_data(), mutable_cf_options_,
|
||||
compact_->compaction->edit(), db_mutex, db_directory_);
|
||||
return versions_->LogAndApply(compaction->column_family_data(),
|
||||
mutable_cf_options_, compaction->edit(),
|
||||
db_mutex, db_directory_);
|
||||
}
|
||||
|
||||
// Given a sequence number, return the sequence number of the
|
||||
@@ -1035,9 +1084,9 @@ Status CompactionJob::OpenCompactionOutputFile() {
|
||||
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"[%s] OpenCompactionOutputFiles for table #%" PRIu64
|
||||
"[%s] [JOB %d] OpenCompactionOutputFiles for table #%" PRIu64
|
||||
" fails at NewWritableFile with status %s",
|
||||
compact_->compaction->column_family_data()->GetName().c_str(),
|
||||
compact_->compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
file_number, s.ToString().c_str());
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
@@ -1055,10 +1104,20 @@ Status CompactionJob::OpenCompactionOutputFile() {
|
||||
compact_->compaction->OutputFilePreallocationSize(mutable_cf_options_)));
|
||||
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
bool skip_filters = false;
|
||||
|
||||
// If the Column family flag is to only optimize filters for hits,
|
||||
// we can skip creating filters if this is the bottommost_level where
|
||||
// data is going to be found
|
||||
//
|
||||
if (cfd->ioptions()->optimize_filters_for_hits && bottommost_level_) {
|
||||
skip_filters = true;
|
||||
}
|
||||
|
||||
compact_->builder.reset(NewTableBuilder(
|
||||
*cfd->ioptions(), cfd->internal_comparator(), compact_->outfile.get(),
|
||||
compact_->compaction->OutputCompressionType(),
|
||||
cfd->ioptions()->compression_opts));
|
||||
cfd->ioptions()->compression_opts, skip_filters));
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
|
||||
+12
-8
@@ -53,16 +53,16 @@ class CompactionJob {
|
||||
// TODO(icanadi) make effort to reduce number of parameters here
|
||||
// IMPORTANT: mutable_cf_options needs to be alive while CompactionJob is
|
||||
// alive
|
||||
CompactionJob(Compaction* compaction, const DBOptions& db_options,
|
||||
CompactionJob(int job_id, Compaction* compaction, const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Statistics* stats,
|
||||
SnapshotList* snapshot_list, bool is_snapshot_supported,
|
||||
std::shared_ptr<Cache> table_cache,
|
||||
Directory* db_directory, Directory* output_directory,
|
||||
Statistics* stats, SnapshotList* snapshot_list,
|
||||
bool is_snapshot_supported, std::shared_ptr<Cache> table_cache,
|
||||
std::function<uint64_t()> yield_callback);
|
||||
|
||||
~CompactionJob() { assert(compact_ == nullptr); }
|
||||
~CompactionJob();
|
||||
|
||||
// no copy/move
|
||||
CompactionJob(CompactionJob&& job) = delete;
|
||||
@@ -75,7 +75,7 @@ class CompactionJob {
|
||||
Status Run();
|
||||
// REQUIRED: mutex held
|
||||
// status is the return of Run()
|
||||
void Install(Status* status, port::Mutex* db_mutex);
|
||||
void Install(Status* status, InstrumentedMutex* db_mutex);
|
||||
|
||||
private:
|
||||
void AllocateCompactionOutputFileNumbers();
|
||||
@@ -84,9 +84,10 @@ class CompactionJob {
|
||||
Status ProcessKeyValueCompaction(int64_t* imm_micros, Iterator* input,
|
||||
bool is_compaction_v2);
|
||||
// Call compaction_filter_v2->Filter() on kv-pairs in compact
|
||||
void CallCompactionFilterV2(CompactionFilterV2* compaction_filter_v2);
|
||||
void CallCompactionFilterV2(CompactionFilterV2* compaction_filter_v2,
|
||||
uint64_t* time);
|
||||
Status FinishCompactionOutputFile(Iterator* input);
|
||||
Status InstallCompactionResults(port::Mutex* db_mutex);
|
||||
Status InstallCompactionResults(InstrumentedMutex* db_mutex);
|
||||
SequenceNumber findEarliestVisibleSnapshot(
|
||||
SequenceNumber in, const std::vector<SequenceNumber>& snapshots,
|
||||
SequenceNumber* prev_snapshot);
|
||||
@@ -94,6 +95,8 @@ class CompactionJob {
|
||||
Status OpenCompactionOutputFile();
|
||||
void CleanupCompaction(const Status& status);
|
||||
|
||||
int job_id_;
|
||||
|
||||
// CompactionJob state
|
||||
struct CompactionState;
|
||||
CompactionState* compact_;
|
||||
@@ -114,6 +117,7 @@ class CompactionJob {
|
||||
std::atomic<bool>* shutting_down_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
Directory* output_directory_;
|
||||
Statistics* stats_;
|
||||
SnapshotList* snapshots_;
|
||||
bool is_snapshot_supported_;
|
||||
|
||||
+17
-13
@@ -13,6 +13,7 @@
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "table/mock_table.h"
|
||||
@@ -20,20 +21,20 @@
|
||||
namespace rocksdb {
|
||||
|
||||
// TODO(icanadi) Make it simpler once we mock out VersionSet
|
||||
class CompactionJobTest {
|
||||
class CompactionJobTest : public testing::Test {
|
||||
public:
|
||||
CompactionJobTest()
|
||||
: env_(Env::Default()),
|
||||
dbname_(test::TmpDir() + "/compaction_job_test"),
|
||||
mutable_cf_options_(Options(), ImmutableCFOptions(Options())),
|
||||
table_cache_(NewLRUCache(50000, 16, 8)),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_,
|
||||
&write_controller_)),
|
||||
shutting_down_(false),
|
||||
mock_table_factory_(new mock::MockTableFactory()) {
|
||||
ASSERT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
NewDB();
|
||||
@@ -41,8 +42,7 @@ class CompactionJobTest {
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options_);
|
||||
|
||||
|
||||
ASSERT_OK(versions_->Recover(column_families, false));
|
||||
EXPECT_OK(versions_->Recover(column_families, false));
|
||||
}
|
||||
|
||||
std::string GenerateFileName(uint64_t file_number) {
|
||||
@@ -82,7 +82,7 @@ class CompactionJobTest {
|
||||
}
|
||||
|
||||
uint64_t file_number = versions_->NewFileNumber();
|
||||
ASSERT_OK(mock_table_factory_->CreateMockTable(
|
||||
EXPECT_OK(mock_table_factory_->CreateMockTable(
|
||||
env_, GenerateFileName(file_number), std::move(contents)));
|
||||
|
||||
VersionEdit edit;
|
||||
@@ -130,12 +130,12 @@ class CompactionJobTest {
|
||||
ColumnFamilyOptions cf_options_;
|
||||
WriteBuffer write_buffer_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
port::Mutex mutex_;
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
std::shared_ptr<mock::MockTableFactory> mock_table_factory_;
|
||||
};
|
||||
|
||||
TEST(CompactionJobTest, Simple) {
|
||||
TEST_F(CompactionJobTest, Simple) {
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
|
||||
auto expected_results = CreateTwoFiles();
|
||||
@@ -160,10 +160,11 @@ TEST(CompactionJobTest, Simple) {
|
||||
};
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
mutex_.Lock();
|
||||
CompactionJob compaction_job(
|
||||
compaction.get(), db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &shutting_down_, &log_buffer, nullptr,
|
||||
nullptr, &snapshots, true, table_cache_, std::move(yield_callback));
|
||||
CompactionJob compaction_job(0, compaction.get(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), env_options_,
|
||||
versions_.get(), &shutting_down_, &log_buffer,
|
||||
nullptr, nullptr, nullptr, &snapshots, true,
|
||||
table_cache_, std::move(yield_callback));
|
||||
compaction_job.Prepare();
|
||||
mutex_.Unlock();
|
||||
ASSERT_OK(compaction_job.Run());
|
||||
@@ -179,4 +180,7 @@ TEST(CompactionJobTest, Simple) {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+141
-89
@@ -23,6 +23,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
|
||||
uint64_t sum = 0;
|
||||
for (size_t i = 0; i < files.size() && files[i]; i++) {
|
||||
@@ -31,15 +32,14 @@ uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files) {
|
||||
return sum;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Determine compression type, based on user options, level of the output
|
||||
// file and whether compression is disabled.
|
||||
// If enable_compression is false, then compression is always disabled no
|
||||
// matter what the values of the other two parameters are.
|
||||
// Otherwise, the compression type is determined based on options and level.
|
||||
CompressionType GetCompressionType(
|
||||
const ImmutableCFOptions& ioptions, int level,
|
||||
const bool enable_compression = true) {
|
||||
CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
int level, int base_level,
|
||||
const bool enable_compression = true) {
|
||||
if (!enable_compression) {
|
||||
// disable compression
|
||||
return kNoCompression;
|
||||
@@ -47,13 +47,16 @@ CompressionType GetCompressionType(
|
||||
// If the use has specified a different compression level for each level,
|
||||
// then pick the compression for that level.
|
||||
if (!ioptions.compression_per_level.empty()) {
|
||||
assert(level == 0 || level >= base_level);
|
||||
int idx = (level == 0) ? 0 : level - base_level + 1;
|
||||
|
||||
const int n = static_cast<int>(ioptions.compression_per_level.size()) - 1;
|
||||
// It is possible for level_ to be -1; in that case, we use level
|
||||
// 0's compression. This occurs mostly in backwards compatibility
|
||||
// situations when the builder doesn't know what level the file
|
||||
// belongs to. Likewise, if level is beyond the end of the
|
||||
// specified compression levels, use the last value.
|
||||
return ioptions.compression_per_level[std::max(0, std::min(level, n))];
|
||||
return ioptions.compression_per_level[std::max(0, std::min(idx, n))];
|
||||
} else {
|
||||
return ioptions.compression;
|
||||
}
|
||||
@@ -64,31 +67,17 @@ CompressionType GetCompressionType(
|
||||
|
||||
CompactionPicker::CompactionPicker(const ImmutableCFOptions& ioptions,
|
||||
const InternalKeyComparator* icmp)
|
||||
: ioptions_(ioptions),
|
||||
compactions_in_progress_(ioptions_.num_levels),
|
||||
icmp_(icmp) {
|
||||
}
|
||||
: ioptions_(ioptions), icmp_(icmp) {}
|
||||
|
||||
CompactionPicker::~CompactionPicker() {}
|
||||
|
||||
void CompactionPicker::SizeBeingCompacted(std::vector<uint64_t>& sizes) {
|
||||
for (int level = 0; level < NumberLevels() - 1; level++) {
|
||||
uint64_t total = 0;
|
||||
for (auto c : compactions_in_progress_[level]) {
|
||||
assert(c->level() == level);
|
||||
for (size_t i = 0; i < c->num_input_files(0); i++) {
|
||||
total += c->input(0, i)->compensated_file_size;
|
||||
}
|
||||
}
|
||||
sizes[level] = total;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all files to indicate that they are not being compacted
|
||||
// Delete this compaction from the list of running compactions.
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
|
||||
c->MarkFilesBeingCompacted(false);
|
||||
compactions_in_progress_[c->level()].erase(c);
|
||||
if (c->level() == 0) {
|
||||
level0_compactions_in_progress_.erase(c);
|
||||
}
|
||||
if (!status.ok()) {
|
||||
c->ResetNextCompactionIndex();
|
||||
}
|
||||
@@ -129,7 +118,7 @@ bool CompactionPicker::ExpandWhileOverlapping(const std::string& cf_name,
|
||||
assert(c != nullptr);
|
||||
// If inputs are empty then there is nothing to expand.
|
||||
if (c->inputs_[0].empty()) {
|
||||
assert(c->inputs_[1].empty());
|
||||
assert(c->inputs(c->num_input_levels() - 1)->empty());
|
||||
// This isn't good compaction
|
||||
return false;
|
||||
}
|
||||
@@ -170,10 +159,10 @@ bool CompactionPicker::ExpandWhileOverlapping(const std::string& cf_name,
|
||||
}
|
||||
if (c->inputs_[0].empty() || FilesInCompaction(c->inputs_[0].files) ||
|
||||
(c->level() != c->output_level() &&
|
||||
ParentRangeInCompaction(vstorage, &smallest, &largest, level,
|
||||
&parent_index))) {
|
||||
RangeInCompaction(vstorage, &smallest, &largest, c->output_level(),
|
||||
&parent_index))) {
|
||||
c->inputs_[0].clear();
|
||||
c->inputs_[1].clear();
|
||||
c->inputs_[c->num_input_levels() - 1].clear();
|
||||
if (!c->inputs_[0].empty()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] ExpandWhileOverlapping() failure because some of the necessary"
|
||||
@@ -280,22 +269,24 @@ Status CompactionPicker::GetCompactionInputsFromFileNumbers(
|
||||
|
||||
|
||||
// Returns true if any one of the parent files are being compacted
|
||||
bool CompactionPicker::ParentRangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest,
|
||||
int level, int* parent_index) {
|
||||
bool CompactionPicker::RangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level,
|
||||
int* level_index) {
|
||||
std::vector<FileMetaData*> inputs;
|
||||
assert(level + 1 < NumberLevels());
|
||||
assert(level < NumberLevels());
|
||||
|
||||
vstorage->GetOverlappingInputs(level + 1, smallest, largest, &inputs,
|
||||
*parent_index, parent_index);
|
||||
vstorage->GetOverlappingInputs(level, smallest, largest, &inputs,
|
||||
*level_index, level_index);
|
||||
return FilesInCompaction(inputs);
|
||||
}
|
||||
|
||||
// Populates the set of inputs from "level+1" that overlap with "level".
|
||||
// Will also attempt to expand "level" if that doesn't expand "level+1"
|
||||
// or cause "level" to include a file for compaction that has an overlapping
|
||||
// user-key with another file.
|
||||
// Populates the set of inputs of all other levels that overlap with the
|
||||
// start level.
|
||||
// Now we assume all levels except start level and output level are empty.
|
||||
// Will also attempt to expand "start level" if that doesn't expand
|
||||
// "output level" or cause "level" to include a file for compaction that has an
|
||||
// overlapping user-key with another file.
|
||||
void CompactionPicker::SetupOtherInputs(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, Compaction* c) {
|
||||
@@ -306,32 +297,41 @@ void CompactionPicker::SetupOtherInputs(
|
||||
return;
|
||||
}
|
||||
|
||||
// For now, we only support merging two levels, start level and output level.
|
||||
// We need to assert other levels are empty.
|
||||
for (int l = c->start_level() + 1; l < c->output_level(); l++) {
|
||||
assert(vstorage->NumLevelFiles(l) == 0);
|
||||
}
|
||||
|
||||
const int level = c->level();
|
||||
InternalKey smallest, largest;
|
||||
|
||||
// Get the range one last time.
|
||||
GetRange(c->inputs_[0].files, &smallest, &largest);
|
||||
|
||||
// Populate the set of next-level files (inputs_[1]) to include in compaction
|
||||
vstorage->GetOverlappingInputs(level + 1, &smallest, &largest,
|
||||
&c->inputs_[1].files, c->parent_index_,
|
||||
&c->parent_index_);
|
||||
// Populate the set of next-level files (inputs_GetOutputLevelInputs()) to
|
||||
// include in compaction
|
||||
vstorage->GetOverlappingInputs(c->output_level(), &smallest, &largest,
|
||||
&c->inputs_[c->num_input_levels() - 1].files,
|
||||
c->parent_index_, &c->parent_index_);
|
||||
|
||||
// Get entire range covered by compaction
|
||||
InternalKey all_start, all_limit;
|
||||
GetRange(c->inputs_[0].files, c->inputs_[1].files, &all_start, &all_limit);
|
||||
GetRange(c->inputs_[0].files, c->inputs_[c->num_input_levels() - 1].files,
|
||||
&all_start, &all_limit);
|
||||
|
||||
// See if we can further grow the number of inputs in "level" without
|
||||
// changing the number of "level+1" files we pick up. We also choose NOT
|
||||
// to expand if this would cause "level" to include some entries for some
|
||||
// user key, while excluding other entries for the same user key. This
|
||||
// can happen when one user key spans multiple files.
|
||||
if (!c->inputs_[1].empty()) {
|
||||
if (!c->inputs(c->num_input_levels() - 1)->empty()) {
|
||||
std::vector<FileMetaData*> expanded0;
|
||||
vstorage->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0,
|
||||
c->base_index_, nullptr);
|
||||
const uint64_t inputs0_size = TotalCompensatedFileSize(c->inputs_[0].files);
|
||||
const uint64_t inputs1_size = TotalCompensatedFileSize(c->inputs_[1].files);
|
||||
const uint64_t inputs1_size =
|
||||
TotalCompensatedFileSize(c->inputs_[c->num_input_levels() - 1].files);
|
||||
const uint64_t expanded0_size = TotalCompensatedFileSize(expanded0);
|
||||
uint64_t limit = mutable_cf_options.ExpandedCompactionByteSizeLimit(level);
|
||||
if (expanded0.size() > c->inputs_[0].size() &&
|
||||
@@ -341,32 +341,34 @@ void CompactionPicker::SetupOtherInputs(
|
||||
InternalKey new_start, new_limit;
|
||||
GetRange(expanded0, &new_start, &new_limit);
|
||||
std::vector<FileMetaData*> expanded1;
|
||||
vstorage->GetOverlappingInputs(level + 1, &new_start, &new_limit,
|
||||
vstorage->GetOverlappingInputs(c->output_level(), &new_start, &new_limit,
|
||||
&expanded1, c->parent_index_,
|
||||
&c->parent_index_);
|
||||
if (expanded1.size() == c->inputs_[1].size() &&
|
||||
if (expanded1.size() == c->inputs(c->num_input_levels() - 1)->size() &&
|
||||
!FilesInCompaction(expanded1)) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"[%s] Expanding@%d %zu+%zu (%" PRIu64 "+%" PRIu64
|
||||
" bytes) to %zu+%zu (%" PRIu64 "+%" PRIu64 "bytes)\n",
|
||||
cf_name.c_str(), level, c->inputs_[0].size(), c->inputs_[1].size(),
|
||||
inputs0_size, inputs1_size, expanded0.size(), expanded1.size(),
|
||||
expanded0_size, inputs1_size);
|
||||
cf_name.c_str(), level, c->inputs_[0].size(),
|
||||
c->inputs(c->num_input_levels() - 1)->size(), inputs0_size,
|
||||
inputs1_size, expanded0.size(), expanded1.size(), expanded0_size,
|
||||
inputs1_size);
|
||||
smallest = new_start;
|
||||
largest = new_limit;
|
||||
c->inputs_[0].files = expanded0;
|
||||
c->inputs_[1].files = expanded1;
|
||||
GetRange(c->inputs_[0].files, c->inputs_[1].files,
|
||||
&all_start, &all_limit);
|
||||
c->inputs_[c->num_input_levels() - 1].files = expanded1;
|
||||
GetRange(c->inputs_[0].files,
|
||||
c->inputs_[c->num_input_levels() - 1].files, &all_start,
|
||||
&all_limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the set of grandparent files that overlap this compaction
|
||||
// (parent == level+1; grandparent == level+2)
|
||||
if (level + 2 < NumberLevels()) {
|
||||
vstorage->GetOverlappingInputs(level + 2, &all_start, &all_limit,
|
||||
&c->grandparents_);
|
||||
if (c->output_level() + 1 < NumberLevels()) {
|
||||
vstorage->GetOverlappingInputs(c->output_level() + 1, &all_start,
|
||||
&all_limit, &c->grandparents_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +419,8 @@ Compaction* CompactionPicker::CompactRange(
|
||||
vstorage->num_levels(), input_level, output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(input_level),
|
||||
output_path_id, GetCompressionType(ioptions_, output_level));
|
||||
output_path_id,
|
||||
GetCompressionType(ioptions_, output_level, vstorage->base_level()));
|
||||
|
||||
c->inputs_[0].files = inputs;
|
||||
if (ExpandWhileOverlapping(cf_name, vstorage, c) == false) {
|
||||
@@ -695,18 +698,6 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
Compaction* c = nullptr;
|
||||
int level = -1;
|
||||
|
||||
// Compute the compactions needed. It is better to do it here
|
||||
// and also in LogAndApply(), otherwise the values could be stale.
|
||||
std::vector<uint64_t> size_being_compacted(NumberLevels() - 1);
|
||||
SizeBeingCompacted(size_being_compacted);
|
||||
|
||||
CompactionOptionsFIFO dummy_compaction_options_fifo;
|
||||
vstorage->ComputeCompactionScore(
|
||||
mutable_cf_options, dummy_compaction_options_fifo, size_being_compacted);
|
||||
|
||||
// We prefer compactions triggered by too much data in a level over
|
||||
// the compactions triggered by seeks.
|
||||
//
|
||||
// Find the compactions by size on all levels.
|
||||
for (int i = 0; i < NumberLevels() - 1; i++) {
|
||||
double score = vstorage->CompactionScore(i);
|
||||
@@ -731,7 +722,7 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
// Two level 0 compaction won't run at the same time, so don't need to worry
|
||||
// about files on level 0 being compacted.
|
||||
if (level == 0) {
|
||||
assert(compactions_in_progress_[0].empty());
|
||||
assert(level0_compactions_in_progress_.empty());
|
||||
InternalKey smallest, largest;
|
||||
GetRange(c->inputs_[0].files, &smallest, &largest);
|
||||
// Note that the next call will discard the file we placed in
|
||||
@@ -745,15 +736,15 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
// cause the 'smallest' and 'largest' key to get extended to a
|
||||
// larger range. So, re-invoke GetRange to get the new key range
|
||||
GetRange(c->inputs_[0].files, &smallest, &largest);
|
||||
if (ParentRangeInCompaction(vstorage, &smallest, &largest, level,
|
||||
&c->parent_index_)) {
|
||||
if (RangeInCompaction(vstorage, &smallest, &largest, c->output_level(),
|
||||
&c->parent_index_)) {
|
||||
delete c;
|
||||
return nullptr;
|
||||
}
|
||||
assert(!c->inputs_[0].empty());
|
||||
}
|
||||
|
||||
// Setup "level+1" files (inputs_[1])
|
||||
// Setup input files from output level
|
||||
SetupOtherInputs(cf_name, mutable_cf_options, vstorage, c);
|
||||
|
||||
// mark all the files that are being compacted
|
||||
@@ -762,13 +753,65 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
// Is this compaction creating a file at the bottommost level
|
||||
c->SetupBottomMostLevel(vstorage, false, false);
|
||||
|
||||
// remember this currently undergoing compaction
|
||||
compactions_in_progress_[level].insert(c);
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
if (level == 0) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
|
||||
c->mutable_cf_options_ = mutable_cf_options;
|
||||
|
||||
// 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
|
||||
{ // this piece of code recomputes compaction score
|
||||
CompactionOptionsFIFO dummy_compaction_options_fifo;
|
||||
vstorage->ComputeCompactionScore(mutable_cf_options,
|
||||
dummy_compaction_options_fifo);
|
||||
}
|
||||
|
||||
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 LevelCompactionPicker::GetPathId(
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, int level) {
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.db_paths.empty());
|
||||
|
||||
// size remaining in the most recent path
|
||||
uint64_t current_path_size = ioptions.db_paths[0].target_size;
|
||||
|
||||
uint64_t level_size;
|
||||
int cur_level = 0;
|
||||
|
||||
level_size = mutable_cf_options.max_bytes_for_level_base;
|
||||
|
||||
// Last path is the fallback
|
||||
while (p < ioptions.db_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;
|
||||
level_size *= mutable_cf_options.max_bytes_for_level_multiplier;
|
||||
cur_level++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
current_path_size = ioptions.db_paths[p].target_size;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
const MutableCFOptions& mutable_cf_options, VersionStorageInfo* vstorage,
|
||||
int level, double score) {
|
||||
@@ -778,16 +821,25 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
// 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 (level == 0 && compactions_in_progress_[level].size() == 1) {
|
||||
if (level == 0 && !level0_compactions_in_progress_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
assert(level >= 0);
|
||||
assert(level + 1 < NumberLevels());
|
||||
c = new Compaction(vstorage->num_levels(), level, level + 1,
|
||||
mutable_cf_options.MaxFileSizeForLevel(level + 1),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(level), 0,
|
||||
GetCompressionType(ioptions_, level + 1));
|
||||
int output_level;
|
||||
if (level == 0) {
|
||||
output_level = vstorage->base_level();
|
||||
} else {
|
||||
output_level = level + 1;
|
||||
}
|
||||
assert(output_level < NumberLevels());
|
||||
|
||||
c = new Compaction(
|
||||
vstorage->num_levels(), level, output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(level),
|
||||
GetPathId(ioptions_, mutable_cf_options, output_level),
|
||||
GetCompressionType(ioptions_, output_level, vstorage->base_level()));
|
||||
c->score_ = score;
|
||||
|
||||
// Pick the largest file in this level that is not already
|
||||
@@ -822,8 +874,8 @@ Compaction* LevelCompactionPicker::PickCompactionBySize(
|
||||
// Do not pick this file if its parents at level+1 are being compacted.
|
||||
// Maybe we can avoid redoing this work in SetupOtherInputs
|
||||
int parent_index = -1;
|
||||
if (ParentRangeInCompaction(vstorage, &f->smallest, &f->largest, level,
|
||||
&parent_index)) {
|
||||
if (RangeInCompaction(vstorage, &f->smallest, &f->largest,
|
||||
c->output_level(), &parent_index)) {
|
||||
continue;
|
||||
}
|
||||
c->inputs_[0].files.push_back(f);
|
||||
@@ -929,8 +981,7 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
// mark all the files that are being compacted
|
||||
c->MarkFilesBeingCompacted(true);
|
||||
|
||||
// remember this currently undergoing compaction
|
||||
compactions_in_progress_[kLevel0].insert(c);
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
|
||||
// Record whether this compaction includes all sst files.
|
||||
// For now, it is only relevant in universal compaction mode.
|
||||
@@ -960,6 +1011,7 @@ uint32_t UniversalCompactionPicker::GetPathId(
|
||||
uint64_t future_size = file_size *
|
||||
(100 - ioptions.compaction_options_universal.size_ratio) / 100;
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.db_paths.empty());
|
||||
for (; p < ioptions.db_paths.size() - 1; p++) {
|
||||
uint64_t target_size = ioptions.db_paths[p].target_size;
|
||||
if (target_size > file_size &&
|
||||
@@ -1114,7 +1166,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
Compaction* c = new Compaction(
|
||||
vstorage->num_levels(), kLevel0, kLevel0,
|
||||
mutable_cf_options.MaxFileSizeForLevel(kLevel0), LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, kLevel0, enable_compression));
|
||||
GetCompressionType(ioptions_, kLevel0, 1, enable_compression));
|
||||
c->score_ = score;
|
||||
|
||||
for (unsigned int i = start_index; i < first_index_after; i++) {
|
||||
@@ -1234,7 +1286,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
Compaction* c =
|
||||
new Compaction(vstorage->num_levels(), kLevel, kLevel,
|
||||
mutable_cf_options.MaxFileSizeForLevel(kLevel), LLONG_MAX,
|
||||
path_id, GetCompressionType(ioptions_, kLevel));
|
||||
path_id, GetCompressionType(ioptions_, kLevel, 1));
|
||||
c->score_ = score;
|
||||
for (unsigned int loop = start_index; loop < files.size(); loop++) {
|
||||
f = files[loop];
|
||||
@@ -1277,7 +1329,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (compactions_in_progress_[0].size() > 0) {
|
||||
if (!level0_compactions_in_progress_.empty()) {
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] FIFO compaction: Already executing compaction. No need "
|
||||
"to run parallel compactions since compactions are very fast",
|
||||
@@ -1303,7 +1355,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
}
|
||||
|
||||
c->MarkFilesBeingCompacted(true);
|
||||
compactions_in_progress_[0].insert(c);
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
c->mutable_cf_options_ = mutable_cf_options;
|
||||
return c;
|
||||
}
|
||||
|
||||
+12
-14
@@ -91,10 +91,6 @@ class CompactionPicker {
|
||||
// Free up the files that participated in a compaction
|
||||
void ReleaseCompactionFiles(Compaction* c, Status status);
|
||||
|
||||
// Return the total amount of data that is undergoing
|
||||
// compactions per level
|
||||
void SizeBeingCompacted(std::vector<uint64_t>& sizes);
|
||||
|
||||
// Returns true if any one of the specified files are being compacted
|
||||
bool FilesInCompaction(const std::vector<FileMetaData*>& files);
|
||||
|
||||
@@ -143,10 +139,9 @@ class CompactionPicker {
|
||||
VersionStorageInfo* vstorage, Compaction* c);
|
||||
|
||||
// Returns true if any one of the parent files are being compacted
|
||||
bool ParentRangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level,
|
||||
int* index);
|
||||
bool RangeInCompaction(VersionStorageInfo* vstorage,
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level, int* index);
|
||||
|
||||
void SetupOtherInputs(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
@@ -163,8 +158,9 @@ class CompactionPicker {
|
||||
const int output_level) const;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// record all the ongoing compactions for all levels
|
||||
std::vector<std::set<Compaction*>> compactions_in_progress_;
|
||||
// Keeps track of all compactions that are running on Level0.
|
||||
// It is protected by DB mutex
|
||||
std::set<Compaction*> level0_compactions_in_progress_;
|
||||
|
||||
const InternalKeyComparator* const icmp_;
|
||||
};
|
||||
@@ -188,6 +184,11 @@ class LevelCompactionPicker : public CompactionPicker {
|
||||
virtual bool NeedsCompaction(const VersionStorageInfo* vstorage) const
|
||||
override;
|
||||
|
||||
// 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);
|
||||
|
||||
private:
|
||||
// For the specfied level, pick a compaction.
|
||||
// Returns nullptr if there is no compaction to be done.
|
||||
@@ -297,7 +298,7 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
|
||||
// Given the current number of levels, returns the highest allowed level
|
||||
// for compaction input.
|
||||
virtual int MaxInputLevel(int current_num_levels) const {
|
||||
virtual int MaxInputLevel(int current_num_levels) const override {
|
||||
return current_num_levels - 2;
|
||||
}
|
||||
|
||||
@@ -309,7 +310,4 @@ class NullCompactionPicker : public CompactionPicker {
|
||||
};
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Utility function
|
||||
extern uint64_t TotalCompensatedFileSize(const std::vector<FileMetaData*>& files);
|
||||
|
||||
} // namespace rocksdb
|
||||
+156
-17
@@ -4,8 +4,10 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "db/compaction_picker.h"
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include "util/logging.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
@@ -13,11 +15,12 @@ namespace rocksdb {
|
||||
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* format, va_list ap) override { log_count++; }
|
||||
size_t log_count;
|
||||
};
|
||||
|
||||
class CompactionPickerTest {
|
||||
class CompactionPickerTest : public testing::Test {
|
||||
public:
|
||||
const Comparator* ucmp_;
|
||||
InternalKeyComparator icmp_;
|
||||
@@ -30,7 +33,6 @@ class CompactionPickerTest {
|
||||
LogBuffer log_buffer_;
|
||||
uint32_t file_num_;
|
||||
CompactionOptionsFIFO fifo_options_;
|
||||
std::vector<uint64_t> size_being_compacted_;
|
||||
std::unique_ptr<VersionStorageInfo> vstorage_;
|
||||
std::vector<std::unique_ptr<FileMetaData>> files_;
|
||||
|
||||
@@ -46,7 +48,8 @@ class CompactionPickerTest {
|
||||
vstorage_(nullptr) {
|
||||
fifo_options_.max_table_files_size = 1;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
size_being_compacted_.resize(options_.num_levels);
|
||||
ioptions_.db_paths.emplace_back("dummy",
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
}
|
||||
|
||||
~CompactionPickerTest() {
|
||||
@@ -57,6 +60,7 @@ class CompactionPickerTest {
|
||||
options_.num_levels = num_levels;
|
||||
vstorage_.reset(new VersionStorageInfo(
|
||||
&icmp_, ucmp_, options_.num_levels, style, nullptr));
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
void DeleteVersionStorage() {
|
||||
@@ -80,17 +84,17 @@ class CompactionPickerTest {
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
vstorage_->ComputeCompactionScore(mutable_cf_options_, fifo_options_,
|
||||
size_being_compacted_);
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateFilesBySize();
|
||||
vstorage_->UpdateNumNonEmptyLevels();
|
||||
vstorage_->GenerateFileIndexer();
|
||||
vstorage_->GenerateLevelFilesBrief();
|
||||
vstorage_->ComputeCompactionScore(mutable_cf_options_, fifo_options_);
|
||||
vstorage_->SetFinalized();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(CompactionPickerTest, Empty) {
|
||||
TEST_F(CompactionPickerTest, Empty) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
UpdateVersionStorageInfo();
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -98,7 +102,7 @@ TEST(CompactionPickerTest, Empty) {
|
||||
ASSERT_TRUE(compaction.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, Single) {
|
||||
TEST_F(CompactionPickerTest, Single) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
Add(0, 1U, "p", "q");
|
||||
@@ -109,7 +113,7 @@ TEST(CompactionPickerTest, Single) {
|
||||
ASSERT_TRUE(compaction.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, Level0Trigger) {
|
||||
TEST_F(CompactionPickerTest, Level0Trigger) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
Add(0, 1U, "150", "200");
|
||||
@@ -125,7 +129,7 @@ TEST(CompactionPickerTest, Level0Trigger) {
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, Level1Trigger) {
|
||||
TEST_F(CompactionPickerTest, Level1Trigger) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
Add(1, 66U, "150", "200", 1000000000U);
|
||||
UpdateVersionStorageInfo();
|
||||
@@ -137,7 +141,7 @@ TEST(CompactionPickerTest, Level1Trigger) {
|
||||
ASSERT_EQ(66U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, Level1Trigger2) {
|
||||
TEST_F(CompactionPickerTest, Level1Trigger2) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
Add(1, 66U, "150", "200", 1000000001U);
|
||||
Add(1, 88U, "201", "300", 1000000000U);
|
||||
@@ -156,7 +160,7 @@ TEST(CompactionPickerTest, Level1Trigger2) {
|
||||
ASSERT_EQ(7U, compaction->input(1, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, LevelMaxScore) {
|
||||
TEST_F(CompactionPickerTest, LevelMaxScore) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
@@ -182,12 +186,13 @@ TEST(CompactionPickerTest, LevelMaxScore) {
|
||||
ASSERT_EQ(7U, compaction->input(0, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, NeedsCompactionLevel) {
|
||||
TEST_F(CompactionPickerTest, NeedsCompactionLevel) {
|
||||
const int kLevels = 6;
|
||||
const int kFileCount = 20;
|
||||
|
||||
for (int level = 0; level < kLevels - 1; ++level) {
|
||||
uint64_t file_size =
|
||||
mutable_cf_options_.MaxBytesForLevel(level) * 2 / kFileCount;
|
||||
NewVersionStorage(kLevels, kCompactionStyleLevel);
|
||||
uint64_t file_size = vstorage_->MaxBytesForLevel(level) * 2 / kFileCount;
|
||||
for (int file_count = 1; file_count <= kFileCount; ++file_count) {
|
||||
// start a brand new version in each test.
|
||||
NewVersionStorage(kLevels, kCompactionStyleLevel);
|
||||
@@ -206,7 +211,138 @@ TEST(CompactionPickerTest, NeedsCompactionLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
TEST_F(CompactionPickerTest, Level0TriggerDynamic) {
|
||||
int num_levels = ioptions_.num_levels;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 200;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(0, 2U, "200", "250");
|
||||
|
||||
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(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(num_levels, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(num_levels - 1, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, Level0TriggerDynamic2) {
|
||||
int num_levels = ioptions_.num_levels;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 200;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(0, 2U, "200", "250");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(vstorage_->base_level(), num_levels - 2);
|
||||
|
||||
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(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(num_levels - 1, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(num_levels - 2, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, Level0TriggerDynamic3) {
|
||||
int num_levels = ioptions_.num_levels;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 200;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(0, 2U, "200", "250");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 4U, "300", "350", 3000U);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(vstorage_->base_level(), num_levels - 3);
|
||||
|
||||
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(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(num_levels - 2, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(num_levels - 3, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, Level0TriggerDynamic4) {
|
||||
int num_levels = ioptions_.num_levels;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 200;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(0, 2U, "200", "250");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 4U, "300", "350", 3000U);
|
||||
Add(num_levels - 3, 5U, "150", "180", 3U);
|
||||
Add(num_levels - 3, 6U, "181", "300", 3U);
|
||||
Add(num_levels - 3, 7U, "400", "450", 3U);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
ASSERT_EQ(vstorage_->base_level(), num_levels - 3);
|
||||
|
||||
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(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(num_levels - 3));
|
||||
ASSERT_EQ(5U, compaction->input(num_levels - 3, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(6U, compaction->input(num_levels - 3, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(num_levels - 2, static_cast<int>(compaction->num_input_levels()));
|
||||
ASSERT_EQ(num_levels - 3, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, LevelTriggerDynamic4) {
|
||||
int num_levels = ioptions_.num_levels;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 200;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 4U, "300", "350", 3000U);
|
||||
Add(num_levels - 1, 4U, "400", "450", 3U);
|
||||
Add(num_levels - 2, 5U, "150", "180", 300U);
|
||||
Add(num_levels - 2, 6U, "181", "350", 500U);
|
||||
Add(num_levels - 2, 7U, "400", "450", 200U);
|
||||
|
||||
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));
|
||||
ASSERT_EQ(6U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(4U, compaction->input(1, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(num_levels - 1, compaction->output_level());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
UniversalCompactionPicker universal_compaction_picker(
|
||||
ioptions_, &icmp_);
|
||||
@@ -225,7 +361,7 @@ TEST(CompactionPickerTest, NeedsCompactionUniversal) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
NewVersionStorage(1, kCompactionStyleFIFO);
|
||||
const int kFileCount =
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger * 3;
|
||||
@@ -255,4 +391,7 @@ TEST(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+37
-31
@@ -10,6 +10,7 @@
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
@@ -33,18 +34,20 @@ typedef std::map<std::string, std::string, MapComparator> KVMap;
|
||||
class KVIter : public Iterator {
|
||||
public:
|
||||
explicit KVIter(const KVMap* map) : map_(map), iter_(map_->end()) {}
|
||||
virtual bool Valid() const { return iter_ != map_->end(); }
|
||||
virtual void SeekToFirst() { iter_ = map_->begin(); }
|
||||
virtual void SeekToLast() {
|
||||
virtual bool Valid() const override { return iter_ != map_->end(); }
|
||||
virtual void SeekToFirst() override { iter_ = map_->begin(); }
|
||||
virtual void SeekToLast() override {
|
||||
if (map_->empty()) {
|
||||
iter_ = map_->end();
|
||||
} else {
|
||||
iter_ = map_->find(map_->rbegin()->first);
|
||||
}
|
||||
}
|
||||
virtual void Seek(const Slice& k) { iter_ = map_->lower_bound(k.ToString()); }
|
||||
virtual void Next() { ++iter_; }
|
||||
virtual void Prev() {
|
||||
virtual void Seek(const Slice& k) override {
|
||||
iter_ = map_->lower_bound(k.ToString());
|
||||
}
|
||||
virtual void Next() override { ++iter_; }
|
||||
virtual void Prev() override {
|
||||
if (iter_ == map_->begin()) {
|
||||
iter_ = map_->end();
|
||||
return;
|
||||
@@ -52,9 +55,9 @@ class KVIter : public Iterator {
|
||||
--iter_;
|
||||
}
|
||||
|
||||
virtual Slice key() const { return iter_->first; }
|
||||
virtual Slice value() const { return iter_->second; }
|
||||
virtual Status status() const { return Status::OK(); }
|
||||
virtual Slice key() const override { return iter_->first; }
|
||||
virtual Slice value() const override { return iter_->second; }
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
const KVMap* const map_;
|
||||
@@ -171,9 +174,9 @@ class DoubleComparator : public Comparator {
|
||||
public:
|
||||
DoubleComparator() {}
|
||||
|
||||
virtual const char* Name() const { return "DoubleComparator"; }
|
||||
virtual const char* Name() const override { return "DoubleComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
double da = std::stod(a.ToString());
|
||||
double db = std::stod(b.ToString());
|
||||
if (da == db) {
|
||||
@@ -185,18 +188,18 @@ class DoubleComparator : public Comparator {
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const {}
|
||||
const Slice& limit) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const {}
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
};
|
||||
|
||||
class HashComparator : public Comparator {
|
||||
public:
|
||||
HashComparator() {}
|
||||
|
||||
virtual const char* Name() const { return "HashComparator"; }
|
||||
virtual const char* Name() const override { return "HashComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
virtual 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) {
|
||||
@@ -208,18 +211,18 @@ class HashComparator : public Comparator {
|
||||
}
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const {}
|
||||
const Slice& limit) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const {}
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
};
|
||||
|
||||
class TwoStrComparator : public Comparator {
|
||||
public:
|
||||
TwoStrComparator() {}
|
||||
|
||||
virtual const char* Name() const { return "TwoStrComparator"; }
|
||||
virtual const char* Name() const override { return "TwoStrComparator"; }
|
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
virtual 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]);
|
||||
@@ -240,13 +243,13 @@ class TwoStrComparator : public Comparator {
|
||||
return a2.compare(b2);
|
||||
}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const {}
|
||||
const Slice& limit) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const {}
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class ComparatorDBTest {
|
||||
class ComparatorDBTest : public testing::Test {
|
||||
private:
|
||||
std::string dbname_;
|
||||
Env* env_;
|
||||
@@ -258,12 +261,12 @@ class ComparatorDBTest {
|
||||
ComparatorDBTest() : env_(Env::Default()), db_(nullptr) {
|
||||
comparator = BytewiseComparator();
|
||||
dbname_ = test::TmpDir() + "/comparator_db_test";
|
||||
ASSERT_OK(DestroyDB(dbname_, last_options_));
|
||||
EXPECT_OK(DestroyDB(dbname_, last_options_));
|
||||
}
|
||||
|
||||
~ComparatorDBTest() {
|
||||
delete db_;
|
||||
ASSERT_OK(DestroyDB(dbname_, last_options_));
|
||||
EXPECT_OK(DestroyDB(dbname_, last_options_));
|
||||
comparator = BytewiseComparator();
|
||||
}
|
||||
|
||||
@@ -299,7 +302,7 @@ class ComparatorDBTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST(ComparatorDBTest, Bytewise) {
|
||||
TEST_F(ComparatorDBTest, Bytewise) {
|
||||
for (int rand_seed = 301; rand_seed < 306; rand_seed++) {
|
||||
DestroyAndReopen();
|
||||
Random rnd(rand_seed);
|
||||
@@ -309,7 +312,7 @@ TEST(ComparatorDBTest, Bytewise) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, SimpleSuffixReverseComparator) {
|
||||
TEST_F(ComparatorDBTest, SimpleSuffixReverseComparator) {
|
||||
SetOwnedComparator(new test::SimpleSuffixReverseComparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
@@ -335,7 +338,7 @@ TEST(ComparatorDBTest, SimpleSuffixReverseComparator) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, Uint64Comparator) {
|
||||
TEST_F(ComparatorDBTest, Uint64Comparator) {
|
||||
SetOwnedComparator(test::Uint64Comparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
@@ -359,7 +362,7 @@ TEST(ComparatorDBTest, Uint64Comparator) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, DoubleComparator) {
|
||||
TEST_F(ComparatorDBTest, DoubleComparator) {
|
||||
SetOwnedComparator(new DoubleComparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
@@ -384,7 +387,7 @@ TEST(ComparatorDBTest, DoubleComparator) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, HashComparator) {
|
||||
TEST_F(ComparatorDBTest, HashComparator) {
|
||||
SetOwnedComparator(new HashComparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
@@ -403,7 +406,7 @@ TEST(ComparatorDBTest, HashComparator) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ComparatorDBTest, TwoStrComparator) {
|
||||
TEST_F(ComparatorDBTest, TwoStrComparator) {
|
||||
SetOwnedComparator(new TwoStrComparator());
|
||||
|
||||
for (int rnd_seed = 301; rnd_seed < 316; rnd_seed++) {
|
||||
@@ -431,4 +434,7 @@ TEST(ComparatorDBTest, TwoStrComparator) {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+18
-15
@@ -29,7 +29,7 @@ namespace rocksdb {
|
||||
|
||||
static const int kValueSize = 1000;
|
||||
|
||||
class CorruptionTest {
|
||||
class CorruptionTest : public testing::Test {
|
||||
public:
|
||||
test::ErrorEnv env_;
|
||||
std::string dbname_;
|
||||
@@ -226,24 +226,26 @@ class CorruptionTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST(CorruptionTest, Recovery) {
|
||||
TEST_F(CorruptionTest, Recovery) {
|
||||
Build(100);
|
||||
Check(100, 100);
|
||||
Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record
|
||||
Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block
|
||||
Reopen();
|
||||
ASSERT_TRUE(!TryReopen().ok());
|
||||
options_.paranoid_checks = false;
|
||||
Reopen(&options_);
|
||||
|
||||
// The 64 records in the first two log blocks are completely lost.
|
||||
Check(36, 36);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, RecoverWriteError) {
|
||||
TEST_F(CorruptionTest, RecoverWriteError) {
|
||||
env_.writable_file_error_ = true;
|
||||
Status s = TryReopen();
|
||||
ASSERT_TRUE(!s.ok());
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, NewFileErrorDuringWrite) {
|
||||
TEST_F(CorruptionTest, NewFileErrorDuringWrite) {
|
||||
// Do enough writing to force minor compaction
|
||||
env_.writable_file_error_ = true;
|
||||
const int num =
|
||||
@@ -266,7 +268,7 @@ TEST(CorruptionTest, NewFileErrorDuringWrite) {
|
||||
Reopen();
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, TableFile) {
|
||||
TEST_F(CorruptionTest, TableFile) {
|
||||
Build(100);
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_FlushMemTable();
|
||||
@@ -277,7 +279,7 @@ TEST(CorruptionTest, TableFile) {
|
||||
Check(99, 99);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, TableFileIndexData) {
|
||||
TEST_F(CorruptionTest, TableFileIndexData) {
|
||||
Build(10000); // Enough to build multiple Tables
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_FlushMemTable();
|
||||
@@ -287,14 +289,14 @@ TEST(CorruptionTest, TableFileIndexData) {
|
||||
Check(5000, 9999);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, MissingDescriptor) {
|
||||
TEST_F(CorruptionTest, MissingDescriptor) {
|
||||
Build(1000);
|
||||
RepairDB();
|
||||
Reopen();
|
||||
Check(1000, 1000);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, SequenceNumberRecovery) {
|
||||
TEST_F(CorruptionTest, SequenceNumberRecovery) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));
|
||||
@@ -315,7 +317,7 @@ TEST(CorruptionTest, SequenceNumberRecovery) {
|
||||
ASSERT_EQ("v6", v);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, CorruptedDescriptor) {
|
||||
TEST_F(CorruptionTest, CorruptedDescriptor) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_FlushMemTable();
|
||||
@@ -332,7 +334,7 @@ TEST(CorruptionTest, CorruptedDescriptor) {
|
||||
ASSERT_EQ("hello", v);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, CompactionInputError) {
|
||||
TEST_F(CorruptionTest, CompactionInputError) {
|
||||
Options options;
|
||||
options.max_background_flushes = 0;
|
||||
Reopen(&options);
|
||||
@@ -350,7 +352,7 @@ TEST(CorruptionTest, CompactionInputError) {
|
||||
Check(10000, 10000);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
Options options;
|
||||
options.paranoid_checks = true;
|
||||
options.write_buffer_size = 131072;
|
||||
@@ -393,7 +395,7 @@ TEST(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, UnrelatedKeys) {
|
||||
TEST_F(CorruptionTest, UnrelatedKeys) {
|
||||
Build(10);
|
||||
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
|
||||
dbi->TEST_FlushMemTable();
|
||||
@@ -409,7 +411,7 @@ TEST(CorruptionTest, UnrelatedKeys) {
|
||||
ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
|
||||
}
|
||||
|
||||
TEST(CorruptionTest, FileSystemStateCorrupted) {
|
||||
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
Options options;
|
||||
options.paranoid_checks = true;
|
||||
@@ -445,5 +447,6 @@ TEST(CorruptionTest, FileSystemStateCorrupted) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+15
-13
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class CuckooTableDBTest {
|
||||
class CuckooTableDBTest : public testing::Test {
|
||||
private:
|
||||
std::string dbname_;
|
||||
Env* env_;
|
||||
@@ -23,14 +23,14 @@ class CuckooTableDBTest {
|
||||
public:
|
||||
CuckooTableDBTest() : env_(Env::Default()) {
|
||||
dbname_ = test::TmpDir() + "/cuckoo_table_db_test";
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
EXPECT_OK(DestroyDB(dbname_, Options()));
|
||||
db_ = nullptr;
|
||||
Reopen();
|
||||
}
|
||||
|
||||
~CuckooTableDBTest() {
|
||||
delete db_;
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
EXPECT_OK(DestroyDB(dbname_, Options()));
|
||||
}
|
||||
|
||||
Options CurrentOptions() {
|
||||
@@ -83,9 +83,8 @@ class CuckooTableDBTest {
|
||||
|
||||
int NumTableFilesAtLevel(int level) {
|
||||
std::string property;
|
||||
ASSERT_TRUE(
|
||||
db_->GetProperty("rocksdb.num-files-at-level" + NumberToString(level),
|
||||
&property));
|
||||
EXPECT_TRUE(db_->GetProperty(
|
||||
"rocksdb.num-files-at-level" + NumberToString(level), &property));
|
||||
return atoi(property.c_str());
|
||||
}
|
||||
|
||||
@@ -107,7 +106,7 @@ class CuckooTableDBTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST(CuckooTableDBTest, Flush) {
|
||||
TEST_F(CuckooTableDBTest, Flush) {
|
||||
// Try with empty DB first.
|
||||
ASSERT_TRUE(dbfull() != nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("key2"));
|
||||
@@ -170,7 +169,7 @@ TEST(CuckooTableDBTest, Flush) {
|
||||
ASSERT_EQ("NOT_FOUND", Get("key6"));
|
||||
}
|
||||
|
||||
TEST(CuckooTableDBTest, FlushWithDuplicateKeys) {
|
||||
TEST_F(CuckooTableDBTest, FlushWithDuplicateKeys) {
|
||||
Options options = CurrentOptions();
|
||||
Reopen(&options);
|
||||
ASSERT_OK(Put("key1", "v1"));
|
||||
@@ -201,7 +200,7 @@ static std::string Uint64Key(uint64_t i) {
|
||||
}
|
||||
} // namespace.
|
||||
|
||||
TEST(CuckooTableDBTest, Uint64Comparator) {
|
||||
TEST_F(CuckooTableDBTest, Uint64Comparator) {
|
||||
Options options = CurrentOptions();
|
||||
options.comparator = test::Uint64Comparator();
|
||||
Reopen(&options);
|
||||
@@ -228,7 +227,7 @@ TEST(CuckooTableDBTest, Uint64Comparator) {
|
||||
ASSERT_EQ("v4", Get(Uint64Key(4)));
|
||||
}
|
||||
|
||||
TEST(CuckooTableDBTest, CompactionIntoMultipleFiles) {
|
||||
TEST_F(CuckooTableDBTest, CompactionIntoMultipleFiles) {
|
||||
// Create a big L0 file and check it compacts into multiple files in L1.
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 270 << 10;
|
||||
@@ -251,7 +250,7 @@ TEST(CuckooTableDBTest, CompactionIntoMultipleFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CuckooTableDBTest, SameKeyInsertedInTwoDifferentFilesAndCompacted) {
|
||||
TEST_F(CuckooTableDBTest, SameKeyInsertedInTwoDifferentFilesAndCompacted) {
|
||||
// Insert same key twice so that they go to different SST files. Then wait for
|
||||
// compaction and check if the latest value is stored and old value removed.
|
||||
Options options = CurrentOptions();
|
||||
@@ -279,7 +278,7 @@ TEST(CuckooTableDBTest, SameKeyInsertedInTwoDifferentFilesAndCompacted) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CuckooTableDBTest, AdaptiveTable) {
|
||||
TEST_F(CuckooTableDBTest, AdaptiveTable) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
// Write some keys using cuckoo table.
|
||||
@@ -316,4 +315,7 @@ TEST(CuckooTableDBTest, AdaptiveTable) {
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+386
-163
File diff suppressed because it is too large.
Load diff
+11
-3
@@ -31,7 +31,7 @@
|
||||
namespace rocksdb {
|
||||
|
||||
Status DBImpl::DisableFileDeletions() {
|
||||
MutexLock l(&mutex_);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
++disable_delete_obsolete_files_;
|
||||
if (disable_delete_obsolete_files_ == 1) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
@@ -45,10 +45,12 @@ Status DBImpl::DisableFileDeletions() {
|
||||
}
|
||||
|
||||
Status DBImpl::EnableFileDeletions(bool force) {
|
||||
JobContext job_context;
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
bool should_purge_files = false;
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
if (force) {
|
||||
// if force, we need to enable file deletions right away
|
||||
disable_delete_obsolete_files_ = 0;
|
||||
@@ -90,6 +92,9 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
// flush all dirty data to disk.
|
||||
Status status;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->Ref();
|
||||
mutex_.Unlock();
|
||||
status = FlushMemTable(cfd, FlushOptions());
|
||||
@@ -112,6 +117,9 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
// Make a set of all of the live *.sst files
|
||||
std::vector<FileDescriptor> live;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->current()->AddLiveFiles(&live);
|
||||
}
|
||||
|
||||
|
||||
+746
-447
File diff suppressed because it is too large.
Load diff
+147
-59
@@ -32,10 +32,13 @@
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "util/scoped_arena_iterator.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
@@ -61,30 +64,33 @@ class DBImpl : public DB {
|
||||
using DB::Put;
|
||||
virtual Status Put(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value);
|
||||
const Slice& value) override;
|
||||
using DB::Merge;
|
||||
virtual Status Merge(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
const Slice& value);
|
||||
const Slice& value) override;
|
||||
using DB::Delete;
|
||||
virtual Status Delete(const WriteOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key);
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice& key) override;
|
||||
using DB::Write;
|
||||
virtual Status Write(const WriteOptions& options, WriteBatch* updates);
|
||||
virtual Status Write(const WriteOptions& options,
|
||||
WriteBatch* updates) override;
|
||||
using DB::Get;
|
||||
virtual Status Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value);
|
||||
std::string* value) override;
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_family,
|
||||
const std::vector<Slice>& keys, std::vector<std::string>* values);
|
||||
const std::vector<Slice>& keys,
|
||||
std::vector<std::string>* values) override;
|
||||
|
||||
virtual Status CreateColumnFamily(const ColumnFamilyOptions& options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle);
|
||||
virtual Status DropColumnFamily(ColumnFamilyHandle* column_family);
|
||||
ColumnFamilyHandle** handle) override;
|
||||
virtual Status DropColumnFamily(ColumnFamilyHandle* column_family) override;
|
||||
|
||||
// Returns false if key doesn't exist in the database and true if it may.
|
||||
// If value_found is not passed in as null, then return the value if found in
|
||||
@@ -93,75 +99,81 @@ class DBImpl : public DB {
|
||||
using DB::KeyMayExist;
|
||||
virtual bool KeyMayExist(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
std::string* value, bool* value_found = nullptr);
|
||||
std::string* value,
|
||||
bool* value_found = nullptr) override;
|
||||
using DB::NewIterator;
|
||||
virtual Iterator* NewIterator(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family);
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual Status NewIterators(
|
||||
const ReadOptions& options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators);
|
||||
virtual const Snapshot* GetSnapshot();
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot);
|
||||
std::vector<Iterator*>* iterators) override;
|
||||
virtual const Snapshot* GetSnapshot() override;
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
using DB::GetProperty;
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value);
|
||||
const Slice& property, std::string* value) override;
|
||||
using DB::GetIntProperty;
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, uint64_t* value) override;
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes);
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes) override;
|
||||
using DB::CompactRange;
|
||||
virtual Status CompactRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end,
|
||||
bool reduce_level = false, int target_level = -1,
|
||||
uint32_t target_path_id = 0);
|
||||
uint32_t target_path_id = 0) override;
|
||||
|
||||
using DB::CompactFiles;
|
||||
virtual 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);
|
||||
virtual 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) override;
|
||||
|
||||
using DB::SetOptions;
|
||||
Status SetOptions(ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map);
|
||||
Status SetOptions(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
using DB::NumberLevels;
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family);
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
using DB::MaxMemCompactionLevel;
|
||||
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* column_family);
|
||||
virtual int MaxMemCompactionLevel(ColumnFamilyHandle* column_family) override;
|
||||
using DB::Level0StopWriteTrigger;
|
||||
virtual int Level0StopWriteTrigger(ColumnFamilyHandle* column_family);
|
||||
virtual const std::string& GetName() const;
|
||||
virtual Env* GetEnv() const;
|
||||
virtual int Level0StopWriteTrigger(
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Env* GetEnv() const override;
|
||||
using DB::GetOptions;
|
||||
virtual const Options& GetOptions(ColumnFamilyHandle* column_family) const;
|
||||
virtual const Options& GetOptions(
|
||||
ColumnFamilyHandle* column_family) const override;
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family);
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const;
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
virtual Status DisableFileDeletions();
|
||||
virtual Status EnableFileDeletions(bool force);
|
||||
virtual Status DisableFileDeletions() override;
|
||||
virtual Status EnableFileDeletions(bool force) override;
|
||||
virtual int IsFileDeletionsEnabled() const;
|
||||
// All the returned filenames start with "/"
|
||||
virtual Status GetLiveFiles(std::vector<std::string>&,
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable = true);
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& files);
|
||||
bool flush_memtable = true) override;
|
||||
virtual Status GetSortedWalFiles(VectorLogPtr& files) override;
|
||||
|
||||
virtual Status GetUpdatesSince(
|
||||
SequenceNumber seq_number, unique_ptr<TransactionLogIterator>* iter,
|
||||
const TransactionLogIterator::ReadOptions&
|
||||
read_options = TransactionLogIterator::ReadOptions());
|
||||
virtual Status DeleteFile(std::string name);
|
||||
read_options = TransactionLogIterator::ReadOptions()) override;
|
||||
virtual Status DeleteFile(std::string name) override;
|
||||
|
||||
virtual void GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata);
|
||||
virtual void GetLiveFilesMetaData(
|
||||
std::vector<LiveFileMetaData>* metadata) override;
|
||||
|
||||
// Obtains the meta data of the specified column family of the DB.
|
||||
// Status::NotFound() will be returned if the current DB does not have
|
||||
@@ -177,7 +189,7 @@ class DBImpl : public DB {
|
||||
// match to our in-memory records
|
||||
virtual Status CheckConsistency();
|
||||
|
||||
virtual Status GetDbIdentity(std::string& identity);
|
||||
virtual Status GetDbIdentity(std::string& identity) override;
|
||||
|
||||
Status RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
int output_level, uint32_t output_path_id,
|
||||
@@ -234,6 +246,7 @@ class DBImpl : public DB {
|
||||
uint64_t TEST_max_total_in_memory_state() {
|
||||
return max_total_in_memory_state_;
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
@@ -250,10 +263,12 @@ class DBImpl : public DB {
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
void PurgeObsoleteFiles(const JobContext& background_contet);
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const;
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override;
|
||||
|
||||
const SnapshotList& snapshots() const { return snapshots_; }
|
||||
|
||||
void CancelAllBackgroundWork();
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
@@ -267,6 +282,9 @@ class DBImpl : public DB {
|
||||
void NotifyOnFlushCompleted(ColumnFamilyData* cfd, uint64_t file_number,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
void NotifyOnCompactionCompleted(ColumnFamilyData* cfd,
|
||||
Compaction *c, const Status &st);
|
||||
|
||||
void NewThreadStatusCfInfo(ColumnFamilyData* cfd) const;
|
||||
|
||||
void EraseThreadStatusCfInfo(ColumnFamilyData* cfd) const;
|
||||
@@ -356,12 +374,15 @@ class DBImpl : public DB {
|
||||
Status CompactFilesImpl(
|
||||
const CompactionOptions& compact_options, ColumnFamilyData* cfd,
|
||||
Version* version, const std::vector<std::string>& input_file_names,
|
||||
const int output_level, int output_path_id);
|
||||
const int output_level, int output_path_id, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
ColumnFamilyData* GetColumnFamilyDataByName(const std::string& cf_name);
|
||||
|
||||
void MaybeScheduleFlushOrCompaction();
|
||||
void SchedulePendingFlush(ColumnFamilyData* cfd);
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
static void BGWorkCompaction(void* db);
|
||||
static void BGWorkFlush(void* db);
|
||||
void BackgroundCallCompaction();
|
||||
@@ -383,13 +404,6 @@ class DBImpl : public DB {
|
||||
// dump rocksdb.stats to LOG
|
||||
void MaybeDumpStats();
|
||||
|
||||
// Return true if the current db supports snapshot. If the current
|
||||
// DB does not support snapshot, then calling GetSnapshot() will always
|
||||
// return nullptr.
|
||||
//
|
||||
// @see GetSnapshot()
|
||||
virtual bool IsSnapshotSupported() const;
|
||||
|
||||
// Return the minimum empty level that could hold the total data in the
|
||||
// input level. Return the input level, if such level could not be found.
|
||||
int FindMinimumEmptyLevelFitting(ColumnFamilyData* cfd,
|
||||
@@ -400,6 +414,12 @@ class DBImpl : public DB {
|
||||
// hold the data set.
|
||||
Status ReFitLevel(ColumnFamilyData* cfd, int level, int target_level = -1);
|
||||
|
||||
// helper functions for adding and removing from flush & compaction queues
|
||||
void AddToCompactionQueue(ColumnFamilyData* cfd);
|
||||
ColumnFamilyData* PopFirstFromCompactionQueue();
|
||||
void AddToFlushQueue(ColumnFamilyData* cfd);
|
||||
ColumnFamilyData* PopFirstFromFlushQueue();
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
@@ -407,7 +427,7 @@ class DBImpl : public DB {
|
||||
FileLock* db_lock_;
|
||||
|
||||
// State below is protected by mutex_
|
||||
port::Mutex mutex_;
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
// This condition variable is signaled on these conditions:
|
||||
// * whenever bg_compaction_scheduled_ goes down to 0
|
||||
@@ -417,9 +437,10 @@ class DBImpl : public DB {
|
||||
// * whenever bg_flush_scheduled_ value decreases (i.e. whenever a flush is
|
||||
// done, even if it didn't make any progress)
|
||||
// * whenever there is an error in background flush or compaction
|
||||
port::CondVar bg_cv_;
|
||||
InstrumentedCondVar bg_cv_;
|
||||
uint64_t logfile_number_;
|
||||
unique_ptr<log::Writer> log_;
|
||||
bool log_dir_synced_;
|
||||
bool log_empty_;
|
||||
ColumnFamilyHandleImpl* default_cf_handle_;
|
||||
InternalStats* default_cf_internal_stats_;
|
||||
@@ -441,7 +462,36 @@ class DBImpl : public DB {
|
||||
// some code-paths
|
||||
bool single_column_family_mode_;
|
||||
|
||||
std::unique_ptr<Directory> db_directory_;
|
||||
bool is_snapshot_supported_;
|
||||
|
||||
// Class to maintain directories for all database paths other than main one.
|
||||
class Directories {
|
||||
public:
|
||||
Status SetDirectories(Env* env, const std::string& dbname,
|
||||
const std::string& wal_dir,
|
||||
const std::vector<DbPath>& data_paths);
|
||||
|
||||
Directory* GetDataDir(size_t path_id);
|
||||
|
||||
Directory* GetWalDir() {
|
||||
if (wal_dir_) {
|
||||
return wal_dir_.get();
|
||||
}
|
||||
return db_dir_.get();
|
||||
}
|
||||
|
||||
Directory* GetDbDir() { return db_dir_.get(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Directory> db_dir_;
|
||||
std::vector<std::unique_ptr<Directory>> data_dirs_;
|
||||
std::unique_ptr<Directory> wal_dir_;
|
||||
|
||||
Status CreateAndNewDirectory(Env* env, const std::string& dirname,
|
||||
std::unique_ptr<Directory>* directory) const;
|
||||
};
|
||||
|
||||
Directories directories_;
|
||||
|
||||
WriteBuffer write_buffer_;
|
||||
|
||||
@@ -465,9 +515,32 @@ class DBImpl : public DB {
|
||||
// State is protected with db mutex.
|
||||
std::list<uint64_t> pending_outputs_;
|
||||
|
||||
// At least one compaction or flush job is pending but not yet scheduled
|
||||
// because of the max background thread limit.
|
||||
bool bg_schedule_needed_;
|
||||
// flush_queue_ and compaction_queue_ hold column families that we need to
|
||||
// flush and compact, respectively.
|
||||
// A column family is inserted into flush_queue_ when it satisfies condition
|
||||
// cfd->imm()->IsFlushPending()
|
||||
// A column family is inserted into compaction_queue_ when it satisfied
|
||||
// condition cfd->NeedsCompaction()
|
||||
// Column families in this list are all Ref()-erenced
|
||||
// TODO(icanadi) Provide some kind of ReferencedColumnFamily class that will
|
||||
// do RAII on ColumnFamilyData
|
||||
// Column families are in this queue when they need to be flushed or
|
||||
// compacted. Consumers of these queues are flush and compaction threads. When
|
||||
// column family is put on this queue, we increase unscheduled_flushes_ and
|
||||
// unscheduled_compactions_. When these variables are bigger than zero, that
|
||||
// means we need to schedule background threads for compaction and thread.
|
||||
// Once the background threads are scheduled, we decrease unscheduled_flushes_
|
||||
// and unscheduled_compactions_. That way we keep track of number of
|
||||
// compaction and flush threads we need to schedule. This scheduling is done
|
||||
// in MaybeScheduleFlushOrCompaction()
|
||||
// invariant(column family present in flush_queue_ <==>
|
||||
// ColumnFamilyData::pending_flush_ == true)
|
||||
std::deque<ColumnFamilyData*> flush_queue_;
|
||||
// invariant(column family present in compaction_queue_ <==>
|
||||
// ColumnFamilyData::pending_compaction_ == true)
|
||||
std::deque<ColumnFamilyData*> compaction_queue_;
|
||||
int unscheduled_flushes_;
|
||||
int unscheduled_compactions_;
|
||||
|
||||
// count how many background compactions are running or have been scheduled
|
||||
int bg_compaction_scheduled_;
|
||||
@@ -506,12 +579,16 @@ class DBImpl : public DB {
|
||||
// without any synchronization
|
||||
int disable_delete_obsolete_files_;
|
||||
|
||||
// last time when DeleteObsoleteFiles was invoked
|
||||
uint64_t delete_obsolete_files_last_run_;
|
||||
// next time when we should run DeleteObsoleteFiles with full scan
|
||||
uint64_t delete_obsolete_files_next_run_;
|
||||
|
||||
// last time stats were dumped to LOG
|
||||
std::atomic<uint64_t> last_stats_dump_time_microsec_;
|
||||
|
||||
// Each flush or compaction gets its own job id. this counter makes sure
|
||||
// they're unique
|
||||
std::atomic<int> next_job_id_;
|
||||
|
||||
bool flush_on_destroy_; // Used when disableWAL is true.
|
||||
|
||||
static const int KEEP_LOG_FILE_NUM = 1000;
|
||||
@@ -524,6 +601,9 @@ class DBImpl : public DB {
|
||||
WalManager wal_manager_;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Unified interface for logging events
|
||||
EventLogger event_logger_;
|
||||
|
||||
// A value of true temporarily disables scheduling of background work
|
||||
bool bg_work_gate_closed_;
|
||||
|
||||
@@ -558,9 +638,17 @@ class DBImpl : public DB {
|
||||
ColumnFamilyData* cfd, JobContext* job_context,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
SuperVersion* InstallSuperVersion(
|
||||
ColumnFamilyData* cfd, SuperVersion* new_sv,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
// All ColumnFamily state changes go through this function. Here we analyze
|
||||
// the new state and we schedule background work if we detect that the new
|
||||
// state needs flush or compaction.
|
||||
// If dont_schedule_bg_work == true, then caller asks us to not schedule flush
|
||||
// or compaction here, but it also promises to schedule needed background
|
||||
// work. We use this to scheduling background compactions when we are in the
|
||||
// write thread, which is very performance critical. Caller schedules
|
||||
// background work as soon as it exits the write thread
|
||||
SuperVersion* InstallSuperVersion(ColumnFamilyData* cfd, SuperVersion* new_sv,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
bool dont_schedule_bg_work = false);
|
||||
|
||||
// Find Super version and reference it. Based on options, it might return
|
||||
// the thread local cached one.
|
||||
|
||||
+5
-4
@@ -10,11 +10,12 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "util/thread_status_updater.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
uint64_t DBImpl::TEST_GetLevel0TotalSize() {
|
||||
MutexLock l(&mutex_);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return default_cf_handle_->cfd()->current()->storage_info()->NumLevelBytes(0);
|
||||
}
|
||||
|
||||
@@ -44,7 +45,7 @@ int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
cfd = cfh->cfd();
|
||||
}
|
||||
MutexLock l(&mutex_);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return cfd->current()->storage_info()->MaxNextLevelOverlappingBytes();
|
||||
}
|
||||
|
||||
@@ -53,7 +54,7 @@ void DBImpl::TEST_GetFilesMetaData(
|
||||
std::vector<std::vector<FileMetaData>>* metadata) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
MutexLock l(&mutex_);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
metadata->resize(NumberLevels());
|
||||
for (int level = 0; level < NumberLevels(); level++) {
|
||||
const std::vector<FileMetaData*>& files =
|
||||
@@ -112,7 +113,7 @@ Status DBImpl::TEST_WaitForCompact() {
|
||||
// wait for compact. It actually waits for scheduled compaction
|
||||
// OR flush to finish.
|
||||
|
||||
MutexLock l(&mutex_);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while ((bg_compaction_scheduled_ || bg_flush_scheduled_) && bg_error_.ok()) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "db/merge_context.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/thread_status_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
||||
+54
-25
@@ -90,17 +90,17 @@ class DBIter: public Iterator {
|
||||
assert(iter_ == nullptr);
|
||||
iter_ = iter;
|
||||
}
|
||||
virtual bool Valid() const { return valid_; }
|
||||
virtual Slice key() const {
|
||||
virtual bool Valid() const override { return valid_; }
|
||||
virtual Slice key() const override {
|
||||
assert(valid_);
|
||||
return saved_key_.GetKey();
|
||||
}
|
||||
virtual Slice value() const {
|
||||
virtual Slice value() const override {
|
||||
assert(valid_);
|
||||
return (direction_ == kForward && !current_entry_is_merged_) ?
|
||||
iter_->value() : saved_value_;
|
||||
}
|
||||
virtual Status status() const {
|
||||
virtual Status status() const override {
|
||||
if (status_.ok()) {
|
||||
return iter_->status();
|
||||
} else {
|
||||
@@ -108,11 +108,11 @@ class DBIter: public Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Next();
|
||||
virtual void Prev();
|
||||
virtual void Seek(const Slice& target);
|
||||
virtual void SeekToFirst();
|
||||
virtual void SeekToLast();
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
|
||||
private:
|
||||
void PrevInternal();
|
||||
@@ -225,7 +225,6 @@ void DBIter::FindNextUserEntryInternal(bool skipping) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
} else {
|
||||
skipping = false;
|
||||
switch (ikey.type) {
|
||||
case kTypeDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
@@ -314,8 +313,14 @@ void DBIter::MergeValuesNewToOld() {
|
||||
// final result in saved_value_. We are done!
|
||||
// ignore corruption if there is any.
|
||||
const Slice val = iter_->value();
|
||||
user_merge_operator_->FullMerge(ikey.user_key, &val, operands,
|
||||
&saved_value_, logger_);
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(ikey.user_key, &val, operands,
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
}
|
||||
// iter_ is positioned after put
|
||||
iter_->Next();
|
||||
return;
|
||||
@@ -329,12 +334,17 @@ void DBIter::MergeValuesNewToOld() {
|
||||
}
|
||||
}
|
||||
|
||||
// we either exhausted all internal keys under this user key, or hit
|
||||
// a deletion marker.
|
||||
// feed null as the existing value to the merge operator, such that
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), nullptr, operands,
|
||||
&saved_value_, logger_);
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
// we either exhausted all internal keys under this user key, or hit
|
||||
// a deletion marker.
|
||||
// feed null as the existing value to the merge operator, such that
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), nullptr, operands,
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME, timer.ElapsedNanos());
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::Prev() {
|
||||
@@ -435,14 +445,24 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
return false;
|
||||
case kTypeMerge:
|
||||
if (last_not_merge_type == kTypeDeletion) {
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), nullptr, operands,
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
} else {
|
||||
assert(last_not_merge_type == kTypeValue);
|
||||
std::string last_put_value = saved_value_;
|
||||
Slice temp_slice(last_put_value);
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), &temp_slice,
|
||||
operands, &saved_value_, logger_);
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), &temp_slice,
|
||||
operands, &saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
@@ -493,9 +513,13 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
if (!iter_->Valid() ||
|
||||
(user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) != 0) ||
|
||||
ikey.type == kTypeDeletion) {
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), nullptr, operands,
|
||||
&saved_value_, logger_);
|
||||
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), nullptr, operands,
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME, timer.ElapsedNanos());
|
||||
}
|
||||
// Make iter_ valid and point to saved_key_
|
||||
if (!iter_->Valid() ||
|
||||
(user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) != 0)) {
|
||||
@@ -507,8 +531,13 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
}
|
||||
|
||||
const Slice& val = iter_->value();
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), &val, operands,
|
||||
&saved_value_, logger_);
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), &val, operands,
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME, timer.ElapsedNanos());
|
||||
}
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
+810
-819
File diff suppressed because it is too large.
Load diff
+2705
-377
File diff suppressed because it is too large.
Load diff
+9
-10
@@ -111,12 +111,11 @@ class InternalKeyComparator : public Comparator {
|
||||
}
|
||||
virtual ~InternalKeyComparator() {}
|
||||
|
||||
virtual const char* Name() const;
|
||||
virtual int Compare(const Slice& a, const Slice& b) const;
|
||||
virtual void FindShortestSeparator(
|
||||
std::string* start,
|
||||
const Slice& limit) const;
|
||||
virtual void FindShortSuccessor(std::string* key) const;
|
||||
virtual const char* Name() const override;
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override;
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override;
|
||||
virtual void FindShortSuccessor(std::string* key) const override;
|
||||
|
||||
const Comparator* user_comparator() const { return user_comparator_; }
|
||||
|
||||
@@ -371,19 +370,19 @@ class InternalKeySliceTransform : public SliceTransform {
|
||||
explicit InternalKeySliceTransform(const SliceTransform* transform)
|
||||
: transform_(transform) {}
|
||||
|
||||
virtual const char* Name() const { return transform_->Name(); }
|
||||
virtual const char* Name() const override { return transform_->Name(); }
|
||||
|
||||
virtual Slice Transform(const Slice& src) const {
|
||||
virtual Slice Transform(const Slice& src) const override {
|
||||
auto user_key = ExtractUserKey(src);
|
||||
return transform_->Transform(user_key);
|
||||
}
|
||||
|
||||
virtual bool InDomain(const Slice& src) const {
|
||||
virtual bool InDomain(const Slice& src) const override {
|
||||
auto user_key = ExtractUserKey(src);
|
||||
return transform_->InDomain(user_key);
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& dst) const {
|
||||
virtual bool InRange(const Slice& dst) const override {
|
||||
auto user_key = ExtractUserKey(dst);
|
||||
return transform_->InRange(user_key);
|
||||
}
|
||||
|
||||
+7
-6
@@ -49,9 +49,9 @@ static void TestKey(const std::string& key,
|
||||
ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded));
|
||||
}
|
||||
|
||||
class FormatTest { };
|
||||
class FormatTest : public testing::Test {};
|
||||
|
||||
TEST(FormatTest, InternalKey_EncodeDecode) {
|
||||
TEST_F(FormatTest, InternalKey_EncodeDecode) {
|
||||
const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" };
|
||||
const uint64_t seq[] = {
|
||||
1, 2, 3,
|
||||
@@ -67,7 +67,7 @@ TEST(FormatTest, InternalKey_EncodeDecode) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FormatTest, InternalKeyShortSeparator) {
|
||||
TEST_F(FormatTest, InternalKeyShortSeparator) {
|
||||
// When user keys are same
|
||||
ASSERT_EQ(IKey("foo", 100, kTypeValue),
|
||||
Shorten(IKey("foo", 100, kTypeValue),
|
||||
@@ -103,14 +103,14 @@ TEST(FormatTest, InternalKeyShortSeparator) {
|
||||
IKey("foo", 200, kTypeValue)));
|
||||
}
|
||||
|
||||
TEST(FormatTest, InternalKeyShortestSuccessor) {
|
||||
TEST_F(FormatTest, InternalKeyShortestSuccessor) {
|
||||
ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),
|
||||
ShortSuccessor(IKey("foo", 100, kTypeValue)));
|
||||
ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue),
|
||||
ShortSuccessor(IKey("\xff\xff", 100, kTypeValue)));
|
||||
}
|
||||
|
||||
TEST(FormatTest, IterKeyOperation) {
|
||||
TEST_F(FormatTest, IterKeyOperation) {
|
||||
IterKey k;
|
||||
const char p[] = "abcdefghijklmnopqrstuvwxyz";
|
||||
const char q[] = "0123456789";
|
||||
@@ -152,5 +152,6 @@ TEST(FormatTest, IterKeyOperation) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+10
-8
@@ -12,6 +12,7 @@
|
||||
#include "db/filename.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -23,7 +24,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DeleteFileTest {
|
||||
class DeleteFileTest : public testing::Test {
|
||||
public:
|
||||
std::string dbname_;
|
||||
Options options_;
|
||||
@@ -57,7 +58,7 @@ class DeleteFileTest {
|
||||
|
||||
DestroyDB(dbname_, options_);
|
||||
numlevels_ = 7;
|
||||
ASSERT_OK(ReopenDB(true));
|
||||
EXPECT_OK(ReopenDB(true));
|
||||
}
|
||||
|
||||
Status ReopenDB(bool create) {
|
||||
@@ -146,7 +147,7 @@ class DeleteFileTest {
|
||||
|
||||
};
|
||||
|
||||
TEST(DeleteFileTest, AddKeysAndQueryLevels) {
|
||||
TEST_F(DeleteFileTest, AddKeysAndQueryLevels) {
|
||||
CreateTwoLevels();
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
@@ -192,7 +193,7 @@ TEST(DeleteFileTest, AddKeysAndQueryLevels) {
|
||||
CloseDB();
|
||||
}
|
||||
|
||||
TEST(DeleteFileTest, PurgeObsoleteFilesTest) {
|
||||
TEST_F(DeleteFileTest, PurgeObsoleteFilesTest) {
|
||||
CreateTwoLevels();
|
||||
// there should be only one (empty) log file because CreateTwoLevels()
|
||||
// flushes the memtables to disk
|
||||
@@ -220,7 +221,7 @@ TEST(DeleteFileTest, PurgeObsoleteFilesTest) {
|
||||
CloseDB();
|
||||
}
|
||||
|
||||
TEST(DeleteFileTest, DeleteFileWithIterator) {
|
||||
TEST_F(DeleteFileTest, DeleteFileWithIterator) {
|
||||
CreateTwoLevels();
|
||||
ReadOptions options;
|
||||
Iterator* it = db_->NewIterator(options);
|
||||
@@ -251,7 +252,7 @@ TEST(DeleteFileTest, DeleteFileWithIterator) {
|
||||
CloseDB();
|
||||
}
|
||||
|
||||
TEST(DeleteFileTest, DeleteLogFiles) {
|
||||
TEST_F(DeleteFileTest, DeleteLogFiles) {
|
||||
AddKeys(10, 0);
|
||||
VectorLogPtr logfiles;
|
||||
db_->GetSortedWalFiles(logfiles);
|
||||
@@ -288,7 +289,7 @@ TEST(DeleteFileTest, DeleteLogFiles) {
|
||||
CloseDB();
|
||||
}
|
||||
|
||||
TEST(DeleteFileTest, DeleteNonDefaultColumnFamily) {
|
||||
TEST_F(DeleteFileTest, DeleteNonDefaultColumnFamily) {
|
||||
CloseDB();
|
||||
DBOptions db_options;
|
||||
db_options.create_if_missing = true;
|
||||
@@ -360,6 +361,7 @@ TEST(DeleteFileTest, DeleteNonDefaultColumnFamily) {
|
||||
} //namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,734 @@
|
||||
// Copyright (c) 2015, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// Copyright 2014 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.
|
||||
|
||||
// This test uses a custom Env to keep track of the state of a filesystem as of
|
||||
// the last "sync". It then checks for data loss errors by purposely dropping
|
||||
// file data (or entire files) not protected by a "sync".
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_format.h"
|
||||
#include "db/version_set.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mock_env.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
static const int kValueSize = 1000;
|
||||
static const int kMaxNumValues = 2000;
|
||||
static const size_t kNumIterations = 3;
|
||||
|
||||
class TestWritableFile;
|
||||
class FaultInjectionTestEnv;
|
||||
|
||||
namespace {
|
||||
|
||||
// Assume a filename, and not a directory name like "/foo/bar/"
|
||||
static std::string GetDirName(const std::string filename) {
|
||||
size_t found = filename.find_last_of("/\\");
|
||||
if (found == std::string::npos) {
|
||||
return "";
|
||||
} else {
|
||||
return filename.substr(0, found);
|
||||
}
|
||||
}
|
||||
|
||||
// Trim the tailing "/" in the end of `str`
|
||||
static std::string TrimDirname(const std::string& str) {
|
||||
size_t found = str.find_last_not_of("/");
|
||||
if (found == std::string::npos) {
|
||||
return str;
|
||||
}
|
||||
return str.substr(0, found + 1);
|
||||
}
|
||||
|
||||
// Return pair <parent directory name, file name> of a full path.
|
||||
static std::pair<std::string, std::string> GetDirAndName(
|
||||
const std::string& name) {
|
||||
std::string dirname = GetDirName(name);
|
||||
std::string fname = name.substr(dirname.size() + 1);
|
||||
return std::make_pair(dirname, fname);
|
||||
}
|
||||
|
||||
// A basic file truncation function suitable for this test.
|
||||
Status Truncate(Env* env, const std::string& filename, uint64_t length) {
|
||||
unique_ptr<SequentialFile> orig_file;
|
||||
const EnvOptions options;
|
||||
Status s = env->NewSequentialFile(filename, &orig_file, options);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Cannot truncate file %s: %s\n", filename.c_str(),
|
||||
s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
char* scratch = new char[length];
|
||||
rocksdb::Slice result;
|
||||
s = orig_file->Read(length, &result, scratch);
|
||||
if (s.ok()) {
|
||||
std::string tmp_name = GetDirName(filename) + "/truncate.tmp";
|
||||
unique_ptr<WritableFile> tmp_file;
|
||||
s = env->NewWritableFile(tmp_name, &tmp_file, options);
|
||||
if (s.ok()) {
|
||||
s = tmp_file->Append(result);
|
||||
if (s.ok()) {
|
||||
s = env->RenameFile(tmp_name, filename);
|
||||
} else {
|
||||
fprintf(stderr, "Cannot rename file %s to %s: %s\n", tmp_name.c_str(),
|
||||
filename.c_str(), s.ToString().c_str());
|
||||
env->DeleteFile(tmp_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Cannot truncate file %s: %s\n", filename.c_str(),
|
||||
s.ToString().c_str());
|
||||
}
|
||||
|
||||
delete[] scratch;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
struct FileState {
|
||||
std::string filename_;
|
||||
ssize_t pos_;
|
||||
ssize_t pos_at_last_sync_;
|
||||
ssize_t pos_at_last_flush_;
|
||||
|
||||
explicit FileState(const std::string& filename)
|
||||
: filename_(filename),
|
||||
pos_(-1),
|
||||
pos_at_last_sync_(-1),
|
||||
pos_at_last_flush_(-1) { }
|
||||
|
||||
FileState() : pos_(-1), pos_at_last_sync_(-1), pos_at_last_flush_(-1) {}
|
||||
|
||||
bool IsFullySynced() const { return pos_ <= 0 || pos_ == pos_at_last_sync_; }
|
||||
|
||||
Status DropUnsyncedData(Env* env) const;
|
||||
|
||||
Status DropRandomUnsyncedData(Env* env, Random* rand) const;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// A wrapper around WritableFile which informs another Env whenever this file
|
||||
// is written to or sync'ed.
|
||||
class TestWritableFile : public WritableFile {
|
||||
public:
|
||||
explicit TestWritableFile(const std::string& fname,
|
||||
unique_ptr<WritableFile>&& f,
|
||||
FaultInjectionTestEnv* env);
|
||||
virtual ~TestWritableFile();
|
||||
virtual Status Append(const Slice& data) override;
|
||||
virtual Status Close() override;
|
||||
virtual Status Flush() override;
|
||||
virtual Status Sync() override;
|
||||
|
||||
private:
|
||||
FileState state_;
|
||||
unique_ptr<WritableFile> target_;
|
||||
bool writable_file_opened_;
|
||||
FaultInjectionTestEnv* env_;
|
||||
};
|
||||
|
||||
class TestDirectory : public Directory {
|
||||
public:
|
||||
explicit TestDirectory(FaultInjectionTestEnv* env, std::string dirname,
|
||||
Directory* dir)
|
||||
: env_(env), dirname_(dirname), dir_(dir) {}
|
||||
~TestDirectory() {}
|
||||
|
||||
virtual Status Fsync() override;
|
||||
|
||||
private:
|
||||
FaultInjectionTestEnv* env_;
|
||||
std::string dirname_;
|
||||
unique_ptr<Directory> dir_;
|
||||
};
|
||||
|
||||
class FaultInjectionTestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit FaultInjectionTestEnv(Env* base)
|
||||
: EnvWrapper(base),
|
||||
filesystem_active_(true) {}
|
||||
virtual ~FaultInjectionTestEnv() { }
|
||||
|
||||
Status NewDirectory(const std::string& name,
|
||||
unique_ptr<Directory>* result) override {
|
||||
unique_ptr<Directory> r;
|
||||
Status s = target()->NewDirectory(name, &r);
|
||||
EXPECT_OK(s);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
result->reset(new TestDirectory(this, TrimDirname(name), r.release()));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewWritableFile(const std::string& fname,
|
||||
unique_ptr<WritableFile>* result,
|
||||
const EnvOptions& soptions) override {
|
||||
Status s = target()->NewWritableFile(fname, result, soptions);
|
||||
if (s.ok()) {
|
||||
result->reset(new TestWritableFile(fname, std::move(*result), this));
|
||||
// WritableFile doesn't append to files, so if the same file is opened
|
||||
// again then it will be truncated - so forget our saved state.
|
||||
UntrackFile(fname);
|
||||
MutexLock l(&mutex_);
|
||||
open_files_.insert(fname);
|
||||
auto dir_and_name = GetDirAndName(fname);
|
||||
auto& list = dir_to_new_files_since_last_sync_[dir_and_name.first];
|
||||
list.insert(dir_and_name.second);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(const std::string& f) override {
|
||||
Status s = EnvWrapper::DeleteFile(f);
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Cannot delete file %s: %s\n", f.c_str(),
|
||||
s.ToString().c_str());
|
||||
}
|
||||
EXPECT_OK(s);
|
||||
if (s.ok()) {
|
||||
UntrackFile(f);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual Status RenameFile(const std::string& s,
|
||||
const std::string& t) override {
|
||||
Status ret = EnvWrapper::RenameFile(s, t);
|
||||
|
||||
if (ret.ok()) {
|
||||
MutexLock l(&mutex_);
|
||||
if (db_file_state_.find(s) != db_file_state_.end()) {
|
||||
db_file_state_[t] = db_file_state_[s];
|
||||
db_file_state_.erase(s);
|
||||
}
|
||||
|
||||
auto sdn = GetDirAndName(s);
|
||||
auto tdn = GetDirAndName(t);
|
||||
if (dir_to_new_files_since_last_sync_[sdn.first].erase(sdn.second) != 0) {
|
||||
auto& tlist = dir_to_new_files_since_last_sync_[tdn.first];
|
||||
assert(tlist.find(tdn.second) == tlist.end());
|
||||
tlist.insert(tdn.second);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WritableFileClosed(const FileState& state) {
|
||||
MutexLock l(&mutex_);
|
||||
if (open_files_.find(state.filename_) != open_files_.end()) {
|
||||
db_file_state_[state.filename_] = state;
|
||||
open_files_.erase(state.filename_);
|
||||
}
|
||||
}
|
||||
|
||||
// For every file that is not fully synced, make a call to `func` with
|
||||
// FileState of the file as the parameter.
|
||||
Status DropFileData(std::function<Status(Env*, FileState)> func) {
|
||||
Status s;
|
||||
MutexLock l(&mutex_);
|
||||
for (std::map<std::string, FileState>::const_iterator it =
|
||||
db_file_state_.begin();
|
||||
s.ok() && it != db_file_state_.end(); ++it) {
|
||||
const FileState& state = it->second;
|
||||
if (!state.IsFullySynced()) {
|
||||
s = func(target(), state);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DropUnsyncedFileData() {
|
||||
return DropFileData([&](Env* env, const FileState& state) {
|
||||
return state.DropUnsyncedData(env);
|
||||
});
|
||||
}
|
||||
|
||||
Status DropRandomUnsyncedFileData(Random* rnd) {
|
||||
return DropFileData([&](Env* env, const FileState& state) {
|
||||
return state.DropRandomUnsyncedData(env, rnd);
|
||||
});
|
||||
}
|
||||
|
||||
Status DeleteFilesCreatedAfterLastDirSync() {
|
||||
// Because DeleteFile access this container make a copy to avoid deadlock
|
||||
std::map<std::string, std::set<std::string>> map_copy;
|
||||
{
|
||||
MutexLock l(&mutex_);
|
||||
map_copy.insert(dir_to_new_files_since_last_sync_.begin(),
|
||||
dir_to_new_files_since_last_sync_.end());
|
||||
}
|
||||
|
||||
for (auto& pair : map_copy) {
|
||||
for (std::string name : pair.second) {
|
||||
Status s = DeleteFile(pair.first + "/" + name);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
void ResetState() {
|
||||
MutexLock l(&mutex_);
|
||||
db_file_state_.clear();
|
||||
dir_to_new_files_since_last_sync_.clear();
|
||||
SetFilesystemActiveNoLock(true);
|
||||
}
|
||||
|
||||
void UntrackFile(const std::string& f) {
|
||||
MutexLock l(&mutex_);
|
||||
auto dir_and_name = GetDirAndName(f);
|
||||
dir_to_new_files_since_last_sync_[dir_and_name.first].erase(
|
||||
dir_and_name.second);
|
||||
db_file_state_.erase(f);
|
||||
open_files_.erase(f);
|
||||
}
|
||||
|
||||
void SyncDir(const std::string& dirname) {
|
||||
MutexLock l(&mutex_);
|
||||
dir_to_new_files_since_last_sync_.erase(dirname);
|
||||
}
|
||||
|
||||
// Setting the filesystem to inactive is the test equivalent to simulating a
|
||||
// system reset. Setting to inactive will freeze our saved filesystem state so
|
||||
// that it will stop being recorded. It can then be reset back to the state at
|
||||
// the time of the reset.
|
||||
bool IsFilesystemActive() {
|
||||
MutexLock l(&mutex_);
|
||||
return filesystem_active_;
|
||||
}
|
||||
void SetFilesystemActiveNoLock(bool active) { filesystem_active_ = active; }
|
||||
void SetFilesystemActive(bool active) {
|
||||
MutexLock l(&mutex_);
|
||||
SetFilesystemActiveNoLock(active);
|
||||
}
|
||||
void AssertNoOpenFile() { ASSERT_TRUE(open_files_.empty()); }
|
||||
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
std::map<std::string, FileState> db_file_state_;
|
||||
std::set<std::string> open_files_;
|
||||
std::unordered_map<std::string, std::set<std::string>>
|
||||
dir_to_new_files_since_last_sync_;
|
||||
bool filesystem_active_; // Record flushes, syncs, writes
|
||||
};
|
||||
|
||||
Status FileState::DropUnsyncedData(Env* env) const {
|
||||
ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
|
||||
return Truncate(env, filename_, sync_pos);
|
||||
}
|
||||
|
||||
Status FileState::DropRandomUnsyncedData(Env* env, Random* rand) const {
|
||||
ssize_t sync_pos = pos_at_last_sync_ == -1 ? 0 : pos_at_last_sync_;
|
||||
assert(pos_ >= sync_pos);
|
||||
int range = static_cast<int>(pos_ - sync_pos);
|
||||
uint64_t truncated_size =
|
||||
static_cast<uint64_t>(sync_pos) + rand->Uniform(range);
|
||||
return Truncate(env, filename_, truncated_size);
|
||||
}
|
||||
|
||||
Status TestDirectory::Fsync() {
|
||||
env_->SyncDir(dirname_);
|
||||
return dir_->Fsync();
|
||||
}
|
||||
|
||||
TestWritableFile::TestWritableFile(const std::string& fname,
|
||||
unique_ptr<WritableFile>&& f,
|
||||
FaultInjectionTestEnv* env)
|
||||
: state_(fname),
|
||||
target_(std::move(f)),
|
||||
writable_file_opened_(true),
|
||||
env_(env) {
|
||||
assert(target_ != nullptr);
|
||||
state_.pos_ = 0;
|
||||
}
|
||||
|
||||
TestWritableFile::~TestWritableFile() {
|
||||
if (writable_file_opened_) {
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
Status TestWritableFile::Append(const Slice& data) {
|
||||
Status s = target_->Append(data);
|
||||
if (s.ok() && env_->IsFilesystemActive()) {
|
||||
state_.pos_ += data.size();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status TestWritableFile::Close() {
|
||||
writable_file_opened_ = false;
|
||||
Status s = target_->Close();
|
||||
if (s.ok()) {
|
||||
env_->WritableFileClosed(state_);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status TestWritableFile::Flush() {
|
||||
Status s = target_->Flush();
|
||||
if (s.ok() && env_->IsFilesystemActive()) {
|
||||
state_.pos_at_last_flush_ = state_.pos_;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status TestWritableFile::Sync() {
|
||||
if (!env_->IsFilesystemActive()) {
|
||||
return Status::OK();
|
||||
}
|
||||
// No need to actual sync.
|
||||
state_.pos_at_last_sync_ = state_.pos_;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
class FaultInjectionTest : public testing::Test {
|
||||
protected:
|
||||
enum OptionConfig {
|
||||
kDefault,
|
||||
kDifferentDataDir,
|
||||
kWalDir,
|
||||
kSyncWal,
|
||||
kWalDirSyncWal,
|
||||
kMultiLevels,
|
||||
kEnd,
|
||||
};
|
||||
int option_config_;
|
||||
// When need to make sure data is persistent, sync WAL
|
||||
bool sync_use_wal_;
|
||||
// When need to make sure data is persistent, call DB::CompactRange()
|
||||
bool sync_use_compact_;
|
||||
|
||||
protected:
|
||||
public:
|
||||
enum ExpectedVerifResult { kValExpectFound, kValExpectNoError };
|
||||
enum ResetMethod {
|
||||
kResetDropUnsyncedData,
|
||||
kResetDropRandomUnsyncedData,
|
||||
kResetDeleteUnsyncedFiles,
|
||||
kResetDropAndDeleteUnsynced
|
||||
};
|
||||
|
||||
std::unique_ptr<Env> base_env_;
|
||||
FaultInjectionTestEnv* env_;
|
||||
std::string dbname_;
|
||||
shared_ptr<Cache> tiny_cache_;
|
||||
Options options_;
|
||||
DB* db_;
|
||||
|
||||
FaultInjectionTest()
|
||||
: option_config_(kDefault),
|
||||
sync_use_wal_(false),
|
||||
sync_use_compact_(true),
|
||||
base_env_(nullptr),
|
||||
env_(NULL),
|
||||
db_(NULL) {
|
||||
}
|
||||
|
||||
bool ChangeOptions() {
|
||||
option_config_++;
|
||||
if (option_config_ >= kEnd) {
|
||||
return false;
|
||||
} else {
|
||||
if (option_config_ == kMultiLevels) {
|
||||
base_env_.reset(new MockEnv(Env::Default()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the current option configuration.
|
||||
Options CurrentOptions() {
|
||||
sync_use_wal_ = false;
|
||||
sync_use_compact_ = true;
|
||||
Options options;
|
||||
switch (option_config_) {
|
||||
case kWalDir:
|
||||
options.wal_dir = test::TmpDir(env_) + "/fault_test_wal";
|
||||
break;
|
||||
case kDifferentDataDir:
|
||||
options.db_paths.emplace_back(test::TmpDir(env_) + "/fault_test_data",
|
||||
1000000U);
|
||||
break;
|
||||
case kSyncWal:
|
||||
sync_use_wal_ = true;
|
||||
sync_use_compact_ = false;
|
||||
break;
|
||||
case kWalDirSyncWal:
|
||||
options.wal_dir = test::TmpDir(env_) + "/fault_test_wal";
|
||||
sync_use_wal_ = true;
|
||||
sync_use_compact_ = false;
|
||||
break;
|
||||
case kMultiLevels:
|
||||
options.write_buffer_size = 64 * 1024;
|
||||
options.target_file_size_base = 64 * 1024;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
options.level0_stop_writes_trigger = 4;
|
||||
options.max_bytes_for_level_base = 128 * 1024;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.max_background_compactions = 8;
|
||||
options.max_background_flushes = 8;
|
||||
sync_use_wal_ = true;
|
||||
sync_use_compact_ = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
Status NewDB() {
|
||||
assert(db_ == NULL);
|
||||
assert(tiny_cache_ == nullptr);
|
||||
assert(env_ == NULL);
|
||||
|
||||
env_ =
|
||||
new FaultInjectionTestEnv(base_env_ ? base_env_.get() : Env::Default());
|
||||
|
||||
options_ = CurrentOptions();
|
||||
options_.env = env_;
|
||||
options_.paranoid_checks = true;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
tiny_cache_ = NewLRUCache(100);
|
||||
table_options.block_cache = tiny_cache_;
|
||||
options_.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
dbname_ = test::TmpDir() + "/fault_test";
|
||||
|
||||
EXPECT_OK(DestroyDB(dbname_, options_));
|
||||
|
||||
options_.create_if_missing = true;
|
||||
Status s = OpenDB();
|
||||
options_.create_if_missing = false;
|
||||
return s;
|
||||
}
|
||||
|
||||
void SetUp() override { ASSERT_OK(NewDB()); }
|
||||
|
||||
void TearDown() override {
|
||||
CloseDB();
|
||||
|
||||
Status s = DestroyDB(dbname_, options_);
|
||||
|
||||
delete env_;
|
||||
env_ = NULL;
|
||||
|
||||
tiny_cache_.reset();
|
||||
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
void Build(const WriteOptions& write_options, int start_idx, int num_vals) {
|
||||
std::string key_space, value_space;
|
||||
WriteBatch batch;
|
||||
for (int i = start_idx; i < start_idx + num_vals; i++) {
|
||||
Slice key = Key(i, &key_space);
|
||||
batch.Clear();
|
||||
batch.Put(key, Value(i, &value_space));
|
||||
ASSERT_OK(db_->Write(write_options, &batch));
|
||||
}
|
||||
}
|
||||
|
||||
Status ReadValue(int i, std::string* val) const {
|
||||
std::string key_space, value_space;
|
||||
Slice key = Key(i, &key_space);
|
||||
Value(i, &value_space);
|
||||
ReadOptions options;
|
||||
return db_->Get(options, key, val);
|
||||
}
|
||||
|
||||
Status Verify(int start_idx, int num_vals,
|
||||
ExpectedVerifResult expected) const {
|
||||
std::string val;
|
||||
std::string value_space;
|
||||
Status s;
|
||||
for (int i = start_idx; i < start_idx + num_vals && s.ok(); i++) {
|
||||
Value(i, &value_space);
|
||||
s = ReadValue(i, &val);
|
||||
if (s.ok()) {
|
||||
EXPECT_EQ(value_space, val);
|
||||
}
|
||||
if (expected == kValExpectFound) {
|
||||
if (!s.ok()) {
|
||||
fprintf(stderr, "Error when read %dth record (expect found): %s\n", i,
|
||||
s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
} else if (!s.ok() && !s.IsNotFound()) {
|
||||
fprintf(stderr, "Error when read %dth record: %s\n", i,
|
||||
s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Return the ith key
|
||||
Slice Key(int i, std::string* storage) const {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "%016d", i);
|
||||
storage->assign(buf, strlen(buf));
|
||||
return Slice(*storage);
|
||||
}
|
||||
|
||||
// Return the value to associate with the specified key
|
||||
Slice Value(int k, std::string* storage) const {
|
||||
Random r(k);
|
||||
return test::RandomString(&r, kValueSize, storage);
|
||||
}
|
||||
|
||||
Status OpenDB() {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
env_->ResetState();
|
||||
return DB::Open(options_, dbname_, &db_);
|
||||
}
|
||||
|
||||
void CloseDB() {
|
||||
delete db_;
|
||||
db_ = NULL;
|
||||
}
|
||||
|
||||
void DeleteAllData() {
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
WriteOptions options;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), iter->key()));
|
||||
}
|
||||
|
||||
delete iter;
|
||||
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = true;
|
||||
db_->Flush(flush_options);
|
||||
}
|
||||
|
||||
// rnd cannot be null for kResetDropRandomUnsyncedData
|
||||
void ResetDBState(ResetMethod reset_method, Random* rnd = nullptr) {
|
||||
env_->AssertNoOpenFile();
|
||||
switch (reset_method) {
|
||||
case kResetDropUnsyncedData:
|
||||
ASSERT_OK(env_->DropUnsyncedFileData());
|
||||
break;
|
||||
case kResetDropRandomUnsyncedData:
|
||||
ASSERT_OK(env_->DropRandomUnsyncedFileData(rnd));
|
||||
break;
|
||||
case kResetDeleteUnsyncedFiles:
|
||||
ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync());
|
||||
break;
|
||||
case kResetDropAndDeleteUnsynced:
|
||||
ASSERT_OK(env_->DropUnsyncedFileData());
|
||||
ASSERT_OK(env_->DeleteFilesCreatedAfterLastDirSync());
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void PartialCompactTestPreFault(int num_pre_sync, int num_post_sync) {
|
||||
DeleteAllData();
|
||||
|
||||
WriteOptions write_options;
|
||||
write_options.sync = sync_use_wal_;
|
||||
|
||||
Build(write_options, 0, num_pre_sync);
|
||||
if (sync_use_compact_) {
|
||||
db_->CompactRange(nullptr, nullptr);
|
||||
}
|
||||
write_options.sync = false;
|
||||
Build(write_options, num_pre_sync, num_post_sync);
|
||||
}
|
||||
|
||||
void PartialCompactTestReopenWithFault(ResetMethod reset_method,
|
||||
int num_pre_sync, int num_post_sync,
|
||||
Random* rnd = nullptr) {
|
||||
env_->SetFilesystemActive(false);
|
||||
CloseDB();
|
||||
ResetDBState(reset_method, rnd);
|
||||
ASSERT_OK(OpenDB());
|
||||
ASSERT_OK(Verify(0, num_pre_sync, FaultInjectionTest::kValExpectFound));
|
||||
ASSERT_OK(Verify(num_pre_sync, num_post_sync,
|
||||
FaultInjectionTest::kValExpectNoError));
|
||||
}
|
||||
|
||||
void NoWriteTestPreFault() {
|
||||
}
|
||||
|
||||
void NoWriteTestReopenWithFault(ResetMethod reset_method) {
|
||||
CloseDB();
|
||||
ResetDBState(reset_method);
|
||||
ASSERT_OK(OpenDB());
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(FaultInjectionTest, FaultTest) {
|
||||
do {
|
||||
Random rnd(301);
|
||||
|
||||
for (size_t idx = 0; idx < kNumIterations; idx++) {
|
||||
int num_pre_sync = rnd.Uniform(kMaxNumValues);
|
||||
int num_post_sync = rnd.Uniform(kMaxNumValues);
|
||||
|
||||
PartialCompactTestPreFault(num_pre_sync, num_post_sync);
|
||||
PartialCompactTestReopenWithFault(kResetDropUnsyncedData, num_pre_sync,
|
||||
num_post_sync);
|
||||
NoWriteTestPreFault();
|
||||
NoWriteTestReopenWithFault(kResetDropUnsyncedData);
|
||||
|
||||
PartialCompactTestPreFault(num_pre_sync, num_post_sync);
|
||||
PartialCompactTestReopenWithFault(kResetDropRandomUnsyncedData,
|
||||
num_pre_sync, num_post_sync, &rnd);
|
||||
NoWriteTestPreFault();
|
||||
NoWriteTestReopenWithFault(kResetDropUnsyncedData);
|
||||
|
||||
// Setting a separate data path won't pass the test as we don't sync
|
||||
// it after creating new files,
|
||||
PartialCompactTestPreFault(num_pre_sync, num_post_sync);
|
||||
PartialCompactTestReopenWithFault(kResetDropAndDeleteUnsynced,
|
||||
num_pre_sync, num_post_sync);
|
||||
NoWriteTestPreFault();
|
||||
NoWriteTestReopenWithFault(kResetDropAndDeleteUnsynced);
|
||||
|
||||
PartialCompactTestPreFault(num_pre_sync, num_post_sync);
|
||||
// No new files created so we expect all values since no files will be
|
||||
// dropped.
|
||||
PartialCompactTestReopenWithFault(kResetDeleteUnsyncedFiles, num_pre_sync,
|
||||
num_post_sync);
|
||||
NoWriteTestPreFault();
|
||||
NoWriteTestReopenWithFault(kResetDeleteUnsyncedFiles);
|
||||
}
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -20,6 +20,9 @@ FileIndexer::FileIndexer(const Comparator* ucmp)
|
||||
size_t FileIndexer::NumLevelIndex() const { return next_level_index_.size(); }
|
||||
|
||||
size_t FileIndexer::LevelIndexSize(size_t level) const {
|
||||
if (level >= next_level_index_.size()) {
|
||||
return 0;
|
||||
}
|
||||
return next_level_index_[level].num_index;
|
||||
}
|
||||
|
||||
|
||||
+15
-13
@@ -11,6 +11,7 @@
|
||||
#include "db/file_indexer.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
@@ -19,7 +20,7 @@ namespace rocksdb {
|
||||
|
||||
class IntComparator : public Comparator {
|
||||
public:
|
||||
int Compare(const Slice& a, const Slice& b) const {
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
assert(a.size() == 8);
|
||||
assert(b.size() == 8);
|
||||
int64_t diff = *reinterpret_cast<const int64_t*>(a.data()) -
|
||||
@@ -33,16 +34,15 @@ class IntComparator : public Comparator {
|
||||
}
|
||||
}
|
||||
|
||||
const char* Name() const {
|
||||
return "IntComparator";
|
||||
}
|
||||
const char* Name() const override { return "IntComparator"; }
|
||||
|
||||
void FindShortestSeparator(std::string* start, const Slice& limit) const {}
|
||||
void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override {}
|
||||
|
||||
void FindShortSuccessor(std::string* key) const {}
|
||||
void FindShortSuccessor(std::string* key) const override {}
|
||||
};
|
||||
|
||||
struct FileIndexerTest {
|
||||
class FileIndexerTest : public testing::Test {
|
||||
public:
|
||||
FileIndexerTest()
|
||||
: kNumLevels(4), files(new std::vector<FileMetaData*>[kNumLevels]) {}
|
||||
@@ -91,7 +91,7 @@ struct FileIndexerTest {
|
||||
};
|
||||
|
||||
// Case 0: Empty
|
||||
TEST(FileIndexerTest, Empty) {
|
||||
TEST_F(FileIndexerTest, Empty) {
|
||||
Arena arena;
|
||||
indexer = new FileIndexer(&ucmp);
|
||||
indexer->UpdateIndex(&arena, 0, files);
|
||||
@@ -99,7 +99,7 @@ TEST(FileIndexerTest, Empty) {
|
||||
}
|
||||
|
||||
// Case 1: no overlap, files are on the left of next level files
|
||||
TEST(FileIndexerTest, no_overlap_left) {
|
||||
TEST_F(FileIndexerTest, no_overlap_left) {
|
||||
Arena arena;
|
||||
indexer = new FileIndexer(&ucmp);
|
||||
// level 1
|
||||
@@ -139,7 +139,7 @@ TEST(FileIndexerTest, no_overlap_left) {
|
||||
}
|
||||
|
||||
// Case 2: no overlap, files are on the right of next level files
|
||||
TEST(FileIndexerTest, no_overlap_right) {
|
||||
TEST_F(FileIndexerTest, no_overlap_right) {
|
||||
Arena arena;
|
||||
indexer = new FileIndexer(&ucmp);
|
||||
// level 1
|
||||
@@ -181,7 +181,7 @@ TEST(FileIndexerTest, no_overlap_right) {
|
||||
}
|
||||
|
||||
// Case 3: empty L2
|
||||
TEST(FileIndexerTest, empty_L2) {
|
||||
TEST_F(FileIndexerTest, empty_L2) {
|
||||
Arena arena;
|
||||
indexer = new FileIndexer(&ucmp);
|
||||
for (uint32_t i = 1; i < kNumLevels; ++i) {
|
||||
@@ -221,7 +221,7 @@ TEST(FileIndexerTest, empty_L2) {
|
||||
}
|
||||
|
||||
// Case 4: mixed
|
||||
TEST(FileIndexerTest, mixed) {
|
||||
TEST_F(FileIndexerTest, mixed) {
|
||||
Arena arena;
|
||||
indexer = new FileIndexer(&ucmp);
|
||||
// level 1
|
||||
@@ -344,5 +344,7 @@ TEST(FileIndexerTest, mixed) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -329,4 +330,16 @@ Status SetIdentityFile(Env* env, const std::string& dbname) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status SyncManifest(Env* env, const DBOptions* db_options, WritableFile* file) {
|
||||
if (db_options->disableDataSync) {
|
||||
return Status::OK();
|
||||
} else if (db_options->use_fsync) {
|
||||
StopWatch sw(env, db_options->statistics.get(), MANIFEST_FILE_SYNC_MICROS);
|
||||
return file->Fsync();
|
||||
} else {
|
||||
StopWatch sw(env, db_options->statistics.get(), MANIFEST_FILE_SYNC_MICROS);
|
||||
return file->Sync();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+8
-1
@@ -14,15 +14,18 @@
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "port/port.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class Env;
|
||||
class Directory;
|
||||
class WritableFile;
|
||||
|
||||
enum FileType {
|
||||
kLogFile,
|
||||
@@ -135,4 +138,8 @@ extern Status SetCurrentFile(Env* env, const std::string& dbname,
|
||||
// Make the IDENTITY file for the db
|
||||
extern Status SetIdentityFile(Env* env, const std::string& dbname);
|
||||
|
||||
// Sync manifest file `file`.
|
||||
extern Status SyncManifest(Env* env, const DBOptions* db_options,
|
||||
WritableFile* file);
|
||||
|
||||
} // namespace rocksdb
|
||||
+6
-5
@@ -16,9 +16,9 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class FileNameTest { };
|
||||
class FileNameTest : public testing::Test {};
|
||||
|
||||
TEST(FileNameTest, Parse) {
|
||||
TEST_F(FileNameTest, Parse) {
|
||||
Slice db;
|
||||
FileType type;
|
||||
uint64_t number;
|
||||
@@ -105,7 +105,7 @@ TEST(FileNameTest, Parse) {
|
||||
};
|
||||
}
|
||||
|
||||
TEST(FileNameTest, InfoLogFileName) {
|
||||
TEST_F(FileNameTest, InfoLogFileName) {
|
||||
std::string dbname = ("/data/rocksdb");
|
||||
std::string db_absolute_path;
|
||||
Env::Default()->GetAbsolutePath(dbname, &db_absolute_path);
|
||||
@@ -121,7 +121,7 @@ TEST(FileNameTest, InfoLogFileName) {
|
||||
OldInfoLogFileName(dbname, 666u, db_absolute_path, "/data/rocksdb_log"));
|
||||
}
|
||||
|
||||
TEST(FileNameTest, Construction) {
|
||||
TEST_F(FileNameTest, Construction) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
std::string fname;
|
||||
@@ -175,5 +175,6 @@ TEST(FileNameTest, Construction) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+47
-15
@@ -40,6 +40,8 @@
|
||||
#include "table/table_builder.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/mutexlock.h"
|
||||
@@ -47,6 +49,7 @@
|
||||
#include "util/iostats_context_imp.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/thread_status_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -54,10 +57,13 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
port::Mutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
InstrumentedMutex* db_mutex,
|
||||
std::atomic<bool>* shutting_down,
|
||||
SequenceNumber newest_snapshot, JobContext* job_context,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
CompressionType output_compression, Statistics* stats)
|
||||
Directory* output_file_directory,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger)
|
||||
: dbname_(dbname),
|
||||
cfd_(cfd),
|
||||
db_options_(db_options),
|
||||
@@ -70,10 +76,24 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
job_context_(job_context),
|
||||
log_buffer_(log_buffer),
|
||||
db_directory_(db_directory),
|
||||
output_file_directory_(output_file_directory),
|
||||
output_compression_(output_compression),
|
||||
stats_(stats) {}
|
||||
stats_(stats),
|
||||
event_logger_(event_logger) {
|
||||
// Update the thread status to indicate flush.
|
||||
ThreadStatusUtil::SetColumnFamily(cfd_);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_FLUSH);
|
||||
TEST_SYNC_POINT("FlushJob::FlushJob()");
|
||||
}
|
||||
|
||||
FlushJob::~FlushJob() {
|
||||
TEST_SYNC_POINT("FlushJob::~FlushJob()");
|
||||
ThreadStatusUtil::ResetThreadStatus();
|
||||
}
|
||||
|
||||
Status FlushJob::Run(uint64_t* file_number) {
|
||||
AutoThreadOperationStageUpdater stage_run(
|
||||
ThreadStatus::STAGE_FLUSH_RUN);
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
uint64_t fn;
|
||||
autovector<MemTable*> mems;
|
||||
@@ -84,6 +104,7 @@ Status FlushJob::Run(uint64_t* file_number) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
||||
// entries mems are (implicitly) sorted in ascending order by their created
|
||||
// time. We will use the first memtable's `edit` to keep the meta info for
|
||||
// this flush.
|
||||
@@ -116,18 +137,20 @@ Status FlushJob::Run(uint64_t* file_number) {
|
||||
if (s.ok() && file_number != nullptr) {
|
||||
*file_number = fn;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
VersionEdit* edit, uint64_t* filenumber) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = db_options_.env->NowMicros();
|
||||
FileMetaData meta;
|
||||
|
||||
// path 0 for level 0 file.
|
||||
meta.fd = FileDescriptor(versions_->NewFileNumber(), 0, 0);
|
||||
*filenumber = meta.fd.GetNumber();
|
||||
// path 0 for level 0 file.
|
||||
|
||||
const SequenceNumber earliest_seqno_in_memtable =
|
||||
mems[0]->GetFirstSequenceNumber();
|
||||
@@ -145,8 +168,8 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
Arena arena;
|
||||
for (MemTable* m : mems) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Flushing memtable with next log file: %" PRIu64 "\n",
|
||||
cfd_->GetName().c_str(), m->GetNextLogNumber());
|
||||
"[%s] [JOB %d] Flushing memtable with next log file: %" PRIu64 "\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, m->GetNextLogNumber());
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
}
|
||||
{
|
||||
@@ -154,8 +177,8 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
NewMergingIterator(&cfd_->internal_comparator(), &memtables[0],
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), meta.fd.GetNumber());
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta.fd.GetNumber());
|
||||
|
||||
s = BuildTable(dbname_, db_options_.env, *cfd_->ioptions(), env_options_,
|
||||
cfd_->table_cache(), iter.get(), &meta,
|
||||
@@ -165,12 +188,15 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] Level-0 flush table #%" PRIu64 ": %" PRIu64 " bytes %s",
|
||||
cfd_->GetName().c_str(), meta.fd.GetNumber(), meta.fd.GetFileSize(),
|
||||
s.ToString().c_str());
|
||||
|
||||
if (!db_options_.disableDataSync && db_directory_ != nullptr) {
|
||||
db_directory_->Fsync();
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": %" PRIu64 " bytes %s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta.fd.GetNumber(),
|
||||
meta.fd.GetFileSize(), s.ToString().c_str());
|
||||
event_logger_->Log() << "event"
|
||||
<< "table_file_creation"
|
||||
<< "file_number" << meta.fd.GetNumber() << "file_size"
|
||||
<< meta.fd.GetFileSize();
|
||||
if (!db_options_.disableDataSync && output_file_directory_ != nullptr) {
|
||||
output_file_directory_->Fsync();
|
||||
}
|
||||
db_mutex_->Lock();
|
||||
}
|
||||
@@ -194,6 +220,12 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
level = base->storage_info()->PickLevelForMemTableOutput(
|
||||
mutable_cf_options_, min_user_key, max_user_key);
|
||||
// If level does not match path id, reset level back to 0
|
||||
uint32_t fdpath = LevelCompactionPicker::GetPathId(
|
||||
*cfd_->ioptions(), mutable_cf_options_, level);
|
||||
if (fdpath != 0) {
|
||||
level = 0;
|
||||
}
|
||||
}
|
||||
edit->AddFile(level, meta.fd.GetNumber(), meta.fd.GetPathId(),
|
||||
meta.fd.GetFileSize(), meta.smallest, meta.largest,
|
||||
|
||||
+10
-4
@@ -28,6 +28,8 @@
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "util/scoped_arena_iterator.h"
|
||||
@@ -54,11 +56,13 @@ class FlushJob {
|
||||
const DBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
port::Mutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
InstrumentedMutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
SequenceNumber newest_snapshot, JobContext* job_context,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
CompressionType output_compression, Statistics* stats);
|
||||
~FlushJob() {}
|
||||
Directory* output_file_directory, CompressionType output_compression,
|
||||
Statistics* stats, EventLogger* event_logger);
|
||||
|
||||
~FlushJob();
|
||||
|
||||
Status Run(uint64_t* file_number = nullptr);
|
||||
|
||||
@@ -71,14 +75,16 @@ class FlushJob {
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const EnvOptions& env_options_;
|
||||
VersionSet* versions_;
|
||||
port::Mutex* db_mutex_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
std::atomic<bool>* shutting_down_;
|
||||
SequenceNumber newest_snapshot_;
|
||||
JobContext* job_context_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
Directory* output_file_directory_;
|
||||
CompressionType output_compression_;
|
||||
Statistics* stats_;
|
||||
EventLogger* event_logger_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+20
-14
@@ -11,6 +11,7 @@
|
||||
#include "db/version_set.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "table/mock_table.h"
|
||||
@@ -20,19 +21,19 @@ namespace rocksdb {
|
||||
// TODO(icanadi) Mock out everything else:
|
||||
// 1. VersionSet
|
||||
// 2. Memtable
|
||||
class FlushJobTest {
|
||||
class FlushJobTest : public testing::Test {
|
||||
public:
|
||||
FlushJobTest()
|
||||
: env_(Env::Default()),
|
||||
dbname_(test::TmpDir() + "/flush_job_test"),
|
||||
table_cache_(NewLRUCache(50000, 16, 8)),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_,
|
||||
&write_controller_)),
|
||||
shutting_down_(false),
|
||||
mock_table_factory_(new mock::MockTableFactory()) {
|
||||
ASSERT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
// TODO(icanadi) Remove this once we mock out VersionSet
|
||||
@@ -41,7 +42,7 @@ class FlushJobTest {
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options_);
|
||||
|
||||
ASSERT_OK(versions_->Recover(column_families, false));
|
||||
EXPECT_OK(versions_->Recover(column_families, false));
|
||||
}
|
||||
|
||||
void NewDB() {
|
||||
@@ -75,25 +76,26 @@ class FlushJobTest {
|
||||
WriteBuffer write_buffer_;
|
||||
ColumnFamilyOptions cf_options_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
port::Mutex mutex_;
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
std::shared_ptr<mock::MockTableFactory> mock_table_factory_;
|
||||
};
|
||||
|
||||
TEST(FlushJobTest, Empty) {
|
||||
JobContext job_context;
|
||||
TEST_F(FlushJobTest, Empty) {
|
||||
JobContext job_context(0);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
SequenceNumber(), &job_context, nullptr, nullptr,
|
||||
kNoCompression, nullptr);
|
||||
SequenceNumber(), &job_context, nullptr, nullptr, nullptr,
|
||||
kNoCompression, nullptr, &event_logger);
|
||||
ASSERT_OK(flush_job.Run());
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
TEST(FlushJobTest, NonEmpty) {
|
||||
JobContext job_context;
|
||||
TEST_F(FlushJobTest, NonEmpty) {
|
||||
JobContext job_context(0);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
auto new_mem = cfd->ConstructNewMemtable(*cfd->GetLatestMutableCFOptions());
|
||||
new_mem->Ref();
|
||||
@@ -107,11 +109,12 @@ TEST(FlushJobTest, NonEmpty) {
|
||||
}
|
||||
cfd->imm()->Add(new_mem);
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
SequenceNumber(), &job_context, nullptr, nullptr,
|
||||
kNoCompression, nullptr);
|
||||
SequenceNumber(), &job_context, nullptr, nullptr, nullptr,
|
||||
kNoCompression, nullptr, &event_logger);
|
||||
mutex_.Lock();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
mutex_.Unlock();
|
||||
@@ -121,4 +124,7 @@ TEST(FlushJobTest, NonEmpty) {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -23,6 +23,7 @@ class FlushScheduler {
|
||||
|
||||
void ScheduleFlush(ColumnFamilyData* cfd);
|
||||
// Returns Ref()-ed column family. Client needs to Unref()
|
||||
// REQUIRES: db mutex is held (exception is single-threaded recovery)
|
||||
ColumnFamilyData* GetNextColumnFamily();
|
||||
|
||||
bool Empty();
|
||||
|
||||
@@ -53,7 +53,7 @@ class LevelIterator : public Iterator {
|
||||
status_ = Status::NotSupported("LevelIterator::SeekToLast()");
|
||||
valid_ = false;
|
||||
}
|
||||
void Prev() {
|
||||
void Prev() override {
|
||||
status_ = Status::NotSupported("LevelIterator::Prev()");
|
||||
valid_ = false;
|
||||
}
|
||||
@@ -158,7 +158,9 @@ void ForwardIterator::Cleanup(bool release_sv) {
|
||||
|
||||
if (release_sv) {
|
||||
if (sv_ != nullptr && sv_->Unref()) {
|
||||
JobContext job_context;
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
db_->mutex_.Lock();
|
||||
sv_->Cleanup();
|
||||
db_->FindObsoleteFiles(&job_context, false, true);
|
||||
|
||||
@@ -58,7 +58,7 @@ class ForwardIterator : public Iterator {
|
||||
status_ = Status::NotSupported("ForwardIterator::SeekToLast()");
|
||||
valid_ = false;
|
||||
}
|
||||
void Prev() {
|
||||
void Prev() override {
|
||||
status_ = Status::NotSupported("ForwardIterator::Prev");
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
+66
-55
@@ -30,26 +30,29 @@ void PrintLevelStatsHeader(char* buf, size_t len, const std::string& cf_name) {
|
||||
snprintf(
|
||||
buf, len,
|
||||
"\n** Compaction Stats [%s] **\n"
|
||||
"Level Files Size(MB) Score Read(GB) Rn(GB) Rnp1(GB) "
|
||||
"Level Files Size(MB) Score Read(GB) Rn(GB) Rnp1(GB) "
|
||||
"Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) "
|
||||
"Comp(sec) Comp(cnt) Avg(sec) "
|
||||
"Stall(sec) Stall(cnt) Avg(ms) RecordIn RecordDrop\n"
|
||||
"Stall(cnt) KeyIn KeyDrop\n"
|
||||
"--------------------------------------------------------------------"
|
||||
"--------------------------------------------------------------------"
|
||||
"----------------------------------------------------------\n",
|
||||
"-----------------------------------------------------------"
|
||||
"--------------------------------------\n",
|
||||
cf_name.c_str());
|
||||
}
|
||||
|
||||
void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
int num_files, int being_compacted, double total_file_size, double score,
|
||||
double w_amp, double stall_us, uint64_t stalls,
|
||||
double w_amp, uint64_t stalls,
|
||||
const InternalStats::CompactionStats& stats) {
|
||||
uint64_t bytes_read = stats.bytes_readn + stats.bytes_readnp1;
|
||||
uint64_t bytes_new = stats.bytes_written - stats.bytes_readnp1;
|
||||
int64_t bytes_new = stats.bytes_written - stats.bytes_readnp1;
|
||||
double elapsed = (stats.micros + 1) / 1000000.0;
|
||||
std::string num_input_records = NumberToHumanString(stats.num_input_records);
|
||||
std::string num_dropped_records =
|
||||
NumberToHumanString(stats.num_dropped_records);
|
||||
|
||||
snprintf(buf, len,
|
||||
"%4s %5d/%-3d %8.0f %5.1f " /* Level, Files, Size(MB), Score */
|
||||
"%4s %6d/%-3d %8.0f %5.1f " /* Level, Files, Size(MB), Score */
|
||||
"%8.1f " /* Read(GB) */
|
||||
"%7.1f " /* Rn(GB) */
|
||||
"%8.1f " /* Rnp1(GB) */
|
||||
@@ -59,30 +62,24 @@ void PrintLevelStats(char* buf, size_t len, const std::string& name,
|
||||
"%5.1f " /* W-Amp */
|
||||
"%8.1f " /* Rd(MB/s) */
|
||||
"%8.1f " /* Wr(MB/s) */
|
||||
"%9.0f " /* Comp(sec) */
|
||||
"%9.0f " /* Comp(sec) */
|
||||
"%9d " /* Comp(cnt) */
|
||||
"%8.3f " /* Avg(sec) */
|
||||
"%10.2f " /* Stall(sec) */
|
||||
"%10" PRIu64
|
||||
" " /* Stall(cnt) */
|
||||
"%7.2f " /* Avg(ms) */
|
||||
"%12" PRIu64
|
||||
" " /* input entries */
|
||||
"%12" PRIu64 "\n" /* number of records reduced */,
|
||||
"%7s " /* KeyIn */
|
||||
"%6s\n", /* KeyDrop */
|
||||
name.c_str(), num_files, being_compacted, total_file_size / kMB,
|
||||
score, bytes_read / kGB, stats.bytes_readn / kGB,
|
||||
stats.bytes_readnp1 / kGB, stats.bytes_written / kGB,
|
||||
bytes_new / kGB, stats.bytes_moved / kGB,
|
||||
w_amp, bytes_read / kMB / elapsed,
|
||||
stats.bytes_written / kMB / elapsed,
|
||||
stats.micros / 1000000.0, stats.count,
|
||||
stats.bytes_written / kMB / elapsed, stats.micros / 1000000.0,
|
||||
stats.count,
|
||||
stats.count == 0 ? 0 : stats.micros / 1000000.0 / stats.count,
|
||||
stall_us / 1000000.0, stalls,
|
||||
stalls == 0 ? 0 : stall_us / 1000.0 / stalls,
|
||||
stats.num_input_records, stats.num_dropped_records);
|
||||
stalls,
|
||||
num_input_records.c_str(), num_dropped_records.c_str());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
DBPropertyType GetPropertyType(const Slice& property, bool* is_int_property,
|
||||
@@ -129,6 +126,10 @@ DBPropertyType GetPropertyType(const Slice& property, bool* is_int_property,
|
||||
return kNumEntriesInMutableMemtable;
|
||||
} else if (in == "num-entries-imm-mem-tables") {
|
||||
return kNumEntriesInImmutableMemtable;
|
||||
} else if (in == "num-deletes-active-mem-table") {
|
||||
return kNumDeletesInMutableMemtable;
|
||||
} else if (in == "num-deletes-imm-mem-tables") {
|
||||
return kNumDeletesInImmutableMemtable;
|
||||
} else if (in == "estimate-num-keys") {
|
||||
return kEstimatedNumKeys;
|
||||
} else if (in == "estimate-table-readers-mem") {
|
||||
@@ -140,6 +141,10 @@ DBPropertyType GetPropertyType(const Slice& property, bool* is_int_property,
|
||||
return kNumSnapshots;
|
||||
} else if (in == "oldest-snapshot-time") {
|
||||
return kOldestSnapshotTime;
|
||||
} else if (in == "num-live-versions") {
|
||||
return kNumLiveVersions;
|
||||
} else if (in == "base-level") {
|
||||
return kBaseLevel;
|
||||
}
|
||||
return kUnknown;
|
||||
}
|
||||
@@ -224,6 +229,7 @@ bool InternalStats::GetStringProperty(DBPropertyType property_type,
|
||||
|
||||
bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
uint64_t* value, DBImpl* db) const {
|
||||
db->mutex_.AssertHeld();
|
||||
const auto* vstorage = cfd_->current()->storage_info();
|
||||
|
||||
switch (property_type) {
|
||||
@@ -254,17 +260,28 @@ bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
return true;
|
||||
case kNumEntriesInMutableMemtable:
|
||||
// Current number of entires in the active memtable
|
||||
*value = cfd_->mem()->GetNumEntries();
|
||||
*value = cfd_->mem()->num_entries();
|
||||
return true;
|
||||
case kNumEntriesInImmutableMemtable:
|
||||
// Current number of entries in the immutable memtables
|
||||
*value = cfd_->imm()->current()->GetTotalNumEntries();
|
||||
return true;
|
||||
case kNumDeletesInMutableMemtable:
|
||||
// Current number of entires in the active memtable
|
||||
*value = cfd_->mem()->num_deletes();
|
||||
return true;
|
||||
case kNumDeletesInImmutableMemtable:
|
||||
// Current number of entries in the immutable memtables
|
||||
*value = cfd_->imm()->current()->GetTotalNumDeletes();
|
||||
return true;
|
||||
case kEstimatedNumKeys:
|
||||
// Estimate number of entries in the column family:
|
||||
// Use estimated entries in tables + total entries in memtables.
|
||||
*value = cfd_->mem()->GetNumEntries() +
|
||||
cfd_->imm()->current()->GetTotalNumEntries() +
|
||||
*value = cfd_->mem()->num_entries() +
|
||||
cfd_->imm()->current()->GetTotalNumEntries() -
|
||||
(cfd_->mem()->num_deletes() +
|
||||
cfd_->imm()->current()->GetTotalNumDeletes()) *
|
||||
2 +
|
||||
vstorage->GetEstimatedActiveKeys();
|
||||
return true;
|
||||
case kNumSnapshots:
|
||||
@@ -273,11 +290,17 @@ bool InternalStats::GetIntProperty(DBPropertyType property_type,
|
||||
case kOldestSnapshotTime:
|
||||
*value = static_cast<uint64_t>(db->snapshots().GetOldestSnapshotTime());
|
||||
return true;
|
||||
case kNumLiveVersions:
|
||||
*value = cfd_->GetNumLiveVersions();
|
||||
return true;
|
||||
#ifndef ROCKSDB_LITE
|
||||
case kIsFileDeletionEnabled:
|
||||
*value = db->IsFileDeletionsEnabled();
|
||||
return true;
|
||||
#endif
|
||||
case kBaseLevel:
|
||||
*value = vstorage->base_level();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -300,6 +323,10 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
uint64_t wal_bytes = db_stats_[InternalStats::WAL_FILE_BYTES];
|
||||
uint64_t wal_synced = db_stats_[InternalStats::WAL_FILE_SYNCED];
|
||||
uint64_t write_with_wal = db_stats_[InternalStats::WRITE_WITH_WAL];
|
||||
uint64_t write_stall_micros = db_stats_[InternalStats::WRITE_STALL_MICROS];
|
||||
const int kHumanMicrosLen = 32;
|
||||
char human_micros[kHumanMicrosLen];
|
||||
|
||||
// Data
|
||||
// writes: total number of write requests.
|
||||
// keys: total number of key updates issued by all the write requests
|
||||
@@ -309,12 +336,14 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
// writes/batches is the average group commit size.
|
||||
//
|
||||
// The format is the same for interval stats.
|
||||
AppendHumanMicros(write_stall_micros, human_micros, kHumanMicrosLen);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative writes: %" PRIu64 " writes, %" PRIu64 " keys, %" PRIu64
|
||||
" batches, %.1f writes per batch, %.2f GB user ingest\n",
|
||||
" batches, %.1f writes per batch, %.2f GB user ingest, "
|
||||
"stall time: %s\n",
|
||||
write_other + write_self, num_keys_written, write_self,
|
||||
(write_other + write_self) / static_cast<double>(write_self + 1),
|
||||
user_bytes_written / kGB);
|
||||
user_bytes_written / kGB, human_micros);
|
||||
value->append(buf);
|
||||
// WAL
|
||||
snprintf(buf, sizeof(buf),
|
||||
@@ -330,14 +359,19 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
uint64_t interval_write_self = write_self - db_stats_snapshot_.write_self;
|
||||
uint64_t interval_num_keys_written =
|
||||
num_keys_written - db_stats_snapshot_.num_keys_written;
|
||||
AppendHumanMicros(
|
||||
write_stall_micros - db_stats_snapshot_.write_stall_micros,
|
||||
human_micros, kHumanMicrosLen);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval writes: %" PRIu64 " writes, %" PRIu64 " keys, %" PRIu64
|
||||
" batches, %.1f writes per batch, %.1f MB user ingest\n",
|
||||
" batches, %.1f writes per batch, %.1f MB user ingest, "
|
||||
"stall time: %s\n",
|
||||
interval_write_other + interval_write_self,
|
||||
interval_num_keys_written, interval_write_self,
|
||||
static_cast<double>(interval_write_other + interval_write_self) /
|
||||
(interval_write_self + 1),
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB);
|
||||
(user_bytes_written - db_stats_snapshot_.ingest_bytes) / kMB,
|
||||
human_micros);
|
||||
value->append(buf);
|
||||
|
||||
uint64_t interval_write_with_wal =
|
||||
@@ -363,6 +397,7 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
db_stats_snapshot_.wal_bytes = wal_bytes;
|
||||
db_stats_snapshot_.wal_synced = wal_synced;
|
||||
db_stats_snapshot_.write_with_wal = write_with_wal;
|
||||
db_stats_snapshot_.write_stall_micros = write_stall_micros;
|
||||
}
|
||||
|
||||
void InternalStats::DumpCFStats(std::string* value) {
|
||||
@@ -400,12 +435,9 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
int total_files = 0;
|
||||
int total_files_being_compacted = 0;
|
||||
double total_file_size = 0;
|
||||
uint64_t total_slowdown_soft = 0;
|
||||
uint64_t total_slowdown_count_soft = 0;
|
||||
uint64_t total_slowdown_hard = 0;
|
||||
uint64_t total_slowdown_count_hard = 0;
|
||||
uint64_t total_stall_count = 0;
|
||||
double total_stall_us = 0;
|
||||
for (int level = 0; level < number_levels_; level++) {
|
||||
int files = vstorage->NumLevelFiles(level);
|
||||
total_files += files;
|
||||
@@ -418,20 +450,10 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
: (stall_leveln_slowdown_count_soft_[level] +
|
||||
stall_leveln_slowdown_count_hard_[level]);
|
||||
|
||||
double stall_us = level == 0 ?
|
||||
(cf_stats_value_[LEVEL0_SLOWDOWN] +
|
||||
cf_stats_value_[LEVEL0_NUM_FILES] +
|
||||
cf_stats_value_[MEMTABLE_COMPACTION])
|
||||
: (stall_leveln_slowdown_soft_[level] +
|
||||
stall_leveln_slowdown_hard_[level]);
|
||||
|
||||
stats_sum.Add(comp_stats_[level]);
|
||||
total_file_size += vstorage->NumLevelBytes(level);
|
||||
total_stall_us += stall_us;
|
||||
total_stall_count += stalls;
|
||||
total_slowdown_soft += stall_leveln_slowdown_soft_[level];
|
||||
total_slowdown_count_soft += stall_leveln_slowdown_count_soft_[level];
|
||||
total_slowdown_hard += stall_leveln_slowdown_hard_[level];
|
||||
total_slowdown_count_hard += stall_leveln_slowdown_count_hard_[level];
|
||||
double w_amp = (comp_stats_[level].bytes_readn == 0) ? 0.0
|
||||
: comp_stats_[level].bytes_written /
|
||||
@@ -439,7 +461,7 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
PrintLevelStats(buf, sizeof(buf), "L" + ToString(level), files,
|
||||
files_being_compacted[level],
|
||||
vstorage->NumLevelBytes(level), compaction_score[level],
|
||||
w_amp, stall_us, stalls, comp_stats_[level]);
|
||||
w_amp, stalls, comp_stats_[level]);
|
||||
value->append(buf);
|
||||
}
|
||||
}
|
||||
@@ -449,7 +471,7 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
// Stats summary across levels
|
||||
PrintLevelStats(buf, sizeof(buf), "Sum", total_files,
|
||||
total_files_being_compacted, total_file_size, 0, w_amp,
|
||||
total_stall_us, total_stall_count, stats_sum);
|
||||
total_stall_count, stats_sum);
|
||||
value->append(buf);
|
||||
// Interval summary
|
||||
uint64_t interval_ingest =
|
||||
@@ -458,24 +480,14 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
interval_stats.Subtract(cf_stats_snapshot_.comp_stats);
|
||||
w_amp = interval_stats.bytes_written / static_cast<double>(interval_ingest);
|
||||
PrintLevelStats(buf, sizeof(buf), "Int", 0, 0, 0, 0,
|
||||
w_amp, total_stall_us - cf_stats_snapshot_.stall_us,
|
||||
total_stall_count - cf_stats_snapshot_.stall_count, interval_stats);
|
||||
w_amp, total_stall_count - cf_stats_snapshot_.stall_count,
|
||||
interval_stats);
|
||||
value->append(buf);
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Flush(GB): accumulative %.3f, interval %.3f\n",
|
||||
curr_ingest / kGB, interval_ingest / kGB);
|
||||
value->append(buf);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Stalls(secs): %.3f level0_slowdown, %.3f level0_numfiles, "
|
||||
"%.3f memtable_compaction, %.3f leveln_slowdown_soft, "
|
||||
"%.3f leveln_slowdown_hard\n",
|
||||
cf_stats_value_[LEVEL0_SLOWDOWN] / 1000000.0,
|
||||
cf_stats_value_[LEVEL0_NUM_FILES] / 1000000.0,
|
||||
cf_stats_value_[MEMTABLE_COMPACTION] / 1000000.0,
|
||||
total_slowdown_soft / 1000000.0,
|
||||
total_slowdown_hard / 1000000.0);
|
||||
value->append(buf);
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Stalls(count): %" PRIu64 " level0_slowdown, "
|
||||
@@ -490,7 +502,6 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
|
||||
cf_stats_snapshot_.ingest_bytes = curr_ingest;
|
||||
cf_stats_snapshot_.comp_stats = stats_sum;
|
||||
cf_stats_snapshot_.stall_us = total_stall_us;
|
||||
cf_stats_snapshot_.stall_count = total_stall_count;
|
||||
}
|
||||
|
||||
|
||||
+16
-13
@@ -21,6 +21,8 @@ namespace rocksdb {
|
||||
class MemTableList;
|
||||
class DBImpl;
|
||||
|
||||
// IMPORTANT: If you add a new property here, also add it to the list in
|
||||
// include/rocksdb/db.h
|
||||
enum DBPropertyType : uint32_t {
|
||||
kUnknown,
|
||||
kNumFilesAtLevel, // Number of files at a specific level
|
||||
@@ -38,16 +40,22 @@ enum DBPropertyType : uint32_t {
|
||||
kCurSizeActiveMemTable, // Return current size of the active memtable
|
||||
kCurSizeAllMemTables, // Return current size of all (active + immutable)
|
||||
// memtables
|
||||
kNumEntriesInMutableMemtable, // Return number of entries in the mutable
|
||||
kNumEntriesInMutableMemtable, // Return number of deletes in the mutable
|
||||
// memtable.
|
||||
kNumEntriesInImmutableMemtable, // Return sum of number of entries in all
|
||||
// the immutable mem tables.
|
||||
kNumDeletesInMutableMemtable, // Return number of entries in the mutable
|
||||
// memtable.
|
||||
kNumDeletesInImmutableMemtable, // Return sum of number of deletes in all
|
||||
// the immutable mem tables.
|
||||
kEstimatedNumKeys, // Estimated total number of keys in the database.
|
||||
kEstimatedUsageByTableReaders, // Estimated memory by table readers.
|
||||
kIsFileDeletionEnabled, // Equals disable_delete_obsolete_files_,
|
||||
// 0 means file deletions enabled
|
||||
kNumSnapshots, // Number of snapshots in the system
|
||||
kOldestSnapshotTime, // Unix timestamp of the first snapshot
|
||||
kNumLiveVersions,
|
||||
kBaseLevel, // The level that L0 data is compacted to
|
||||
};
|
||||
|
||||
extern DBPropertyType GetPropertyType(const Slice& property,
|
||||
@@ -75,6 +83,7 @@ class InternalStats {
|
||||
WRITE_DONE_BY_OTHER,
|
||||
WRITE_DONE_BY_SELF,
|
||||
WRITE_WITH_WAL,
|
||||
WRITE_STALL_MICROS,
|
||||
INTERNAL_DB_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
@@ -83,9 +92,7 @@ class InternalStats {
|
||||
cf_stats_value_(INTERNAL_CF_STATS_ENUM_MAX),
|
||||
cf_stats_count_(INTERNAL_CF_STATS_ENUM_MAX),
|
||||
comp_stats_(num_levels),
|
||||
stall_leveln_slowdown_hard_(num_levels),
|
||||
stall_leveln_slowdown_count_hard_(num_levels),
|
||||
stall_leveln_slowdown_soft_(num_levels),
|
||||
stall_leveln_slowdown_count_soft_(num_levels),
|
||||
bg_error_count_(0),
|
||||
number_levels_(num_levels),
|
||||
@@ -100,9 +107,7 @@ class InternalStats {
|
||||
cf_stats_count_[i] = 0;
|
||||
}
|
||||
for (int i = 0; i < num_levels; ++i) {
|
||||
stall_leveln_slowdown_hard_[i] = 0;
|
||||
stall_leveln_slowdown_count_hard_[i] = 0;
|
||||
stall_leveln_slowdown_soft_[i] = 0;
|
||||
stall_leveln_slowdown_count_soft_[i] = 0;
|
||||
}
|
||||
}
|
||||
@@ -206,12 +211,10 @@ class InternalStats {
|
||||
comp_stats_[level].bytes_moved += amount;
|
||||
}
|
||||
|
||||
void RecordLevelNSlowdown(int level, uint64_t micros, bool soft) {
|
||||
void RecordLevelNSlowdown(int level, bool soft) {
|
||||
if (soft) {
|
||||
stall_leveln_slowdown_soft_[level] += micros;
|
||||
++stall_leveln_slowdown_count_soft_[level];
|
||||
} else {
|
||||
stall_leveln_slowdown_hard_[level] += micros;
|
||||
++stall_leveln_slowdown_count_hard_[level];
|
||||
}
|
||||
}
|
||||
@@ -250,9 +253,7 @@ class InternalStats {
|
||||
// Per-ColumnFamily/level compaction stats
|
||||
std::vector<CompactionStats> comp_stats_;
|
||||
// These count the number of microseconds for which MakeRoomForWrite stalls.
|
||||
std::vector<uint64_t> stall_leveln_slowdown_hard_;
|
||||
std::vector<uint64_t> stall_leveln_slowdown_count_hard_;
|
||||
std::vector<uint64_t> stall_leveln_slowdown_soft_;
|
||||
std::vector<uint64_t> stall_leveln_slowdown_count_soft_;
|
||||
|
||||
// Used to compute per-interval statistics
|
||||
@@ -260,13 +261,11 @@ class InternalStats {
|
||||
// ColumnFamily-level stats
|
||||
CompactionStats comp_stats;
|
||||
uint64_t ingest_bytes; // Bytes written to L0
|
||||
uint64_t stall_us; // Stall time in micro-seconds
|
||||
uint64_t stall_count; // Stall count
|
||||
|
||||
CFStatsSnapshot()
|
||||
: comp_stats(0),
|
||||
ingest_bytes(0),
|
||||
stall_us(0),
|
||||
stall_count(0) {}
|
||||
} cf_stats_snapshot_;
|
||||
|
||||
@@ -285,6 +284,8 @@ class InternalStats {
|
||||
// to multiple keys. num_keys_written is total number of keys updated by all
|
||||
// those writes.
|
||||
uint64_t num_keys_written;
|
||||
// Total time writes delayed by stalls.
|
||||
uint64_t write_stall_micros;
|
||||
double seconds_up;
|
||||
|
||||
DBStatsSnapshot()
|
||||
@@ -295,6 +296,7 @@ class InternalStats {
|
||||
write_other(0),
|
||||
write_self(0),
|
||||
num_keys_written(0),
|
||||
write_stall_micros(0),
|
||||
seconds_up(0) {}
|
||||
} db_stats_snapshot_;
|
||||
|
||||
@@ -332,6 +334,7 @@ class InternalStats {
|
||||
WRITE_DONE_BY_OTHER,
|
||||
WRITE_DONE_BY_SELF,
|
||||
WRITE_WITH_WAL,
|
||||
WRITE_STALL_MICROS,
|
||||
INTERNAL_DB_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
@@ -363,7 +366,7 @@ class InternalStats {
|
||||
|
||||
void IncBytesMoved(int level, uint64_t amount) {}
|
||||
|
||||
void RecordLevelNSlowdown(int level, uint64_t micros, bool soft) {}
|
||||
void RecordLevelNSlowdown(int level, bool soft) {}
|
||||
|
||||
void AddCFStats(InternalCFStatsType type, uint64_t value) {}
|
||||
|
||||
|
||||
+8
-3
@@ -20,7 +20,7 @@ class MemTable;
|
||||
|
||||
struct JobContext {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return candidate_files.size() || sst_delete_files.size() ||
|
||||
return full_scan_candidate_files.size() || sst_delete_files.size() ||
|
||||
log_delete_files.size() || new_superversion != nullptr ||
|
||||
superversions_to_free.size() > 0 || memtables_to_free.size() > 0;
|
||||
}
|
||||
@@ -36,10 +36,14 @@ struct JobContext {
|
||||
}
|
||||
};
|
||||
|
||||
// Unique job id
|
||||
int job_id;
|
||||
|
||||
// a list of all files that we'll consider deleting
|
||||
// (every once in a while this is filled up with all files
|
||||
// in the DB directory)
|
||||
std::vector<CandidateFileInfo> candidate_files;
|
||||
// (filled only if we're doing full scan)
|
||||
std::vector<CandidateFileInfo> full_scan_candidate_files;
|
||||
|
||||
// the list of all live sst files that cannot be deleted
|
||||
std::vector<FileDescriptor> sst_live;
|
||||
@@ -66,7 +70,8 @@ struct JobContext {
|
||||
|
||||
uint64_t min_pending_output = 0;
|
||||
|
||||
explicit JobContext(bool create_superversion = false) {
|
||||
explicit JobContext(int _job_id, bool create_superversion = false) {
|
||||
job_id = _job_id;
|
||||
manifest_file_number = 0;
|
||||
pending_manifest_file_number = 0;
|
||||
log_number = 0;
|
||||
|
||||
+66
-11
@@ -2,7 +2,6 @@
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
@@ -28,6 +27,7 @@
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/statistics.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testutil.h"
|
||||
@@ -36,11 +36,11 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class EventListenerTest {
|
||||
class EventListenerTest : public testing::Test {
|
||||
public:
|
||||
EventListenerTest() {
|
||||
dbname_ = test::TmpDir() + "/listener_test";
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
EXPECT_OK(DestroyDB(dbname_, Options()));
|
||||
db_ = nullptr;
|
||||
Reopen();
|
||||
}
|
||||
@@ -52,7 +52,7 @@ class EventListenerTest {
|
||||
options.db_paths.emplace_back(dbname_ + "_2", 0);
|
||||
options.db_paths.emplace_back(dbname_ + "_3", 0);
|
||||
options.db_paths.emplace_back(dbname_ + "_4", 0);
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
EXPECT_OK(DestroyDB(dbname_, options));
|
||||
}
|
||||
|
||||
void CreateColumnFamilies(const std::vector<std::string>& cfs,
|
||||
@@ -92,7 +92,7 @@ class EventListenerTest {
|
||||
const std::vector<std::string>& cfs,
|
||||
const std::vector<const Options*>& options) {
|
||||
Close();
|
||||
ASSERT_EQ(cfs.size(), options.size());
|
||||
EXPECT_EQ(cfs.size(), options.size());
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (size_t i = 0; i < cfs.size(); ++i) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(cfs[i], *options[i]));
|
||||
@@ -144,12 +144,66 @@ class EventListenerTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DB* db_;
|
||||
std::string dbname_;
|
||||
std::vector<ColumnFamilyHandle*> handles_;
|
||||
};
|
||||
|
||||
class TestCompactionListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionCompleted(DB *db, const CompactionJobInfo& ci) override {
|
||||
compacted_dbs_.push_back(db);
|
||||
}
|
||||
|
||||
std::vector<DB*> compacted_dbs_;
|
||||
};
|
||||
|
||||
TEST_F(EventListenerTest, OnSingleDBCompactionTest) {
|
||||
const int kTestKeySize = 16;
|
||||
const int kTestValueSize = 984;
|
||||
const int kEntrySize = kTestKeySize + kTestValueSize;
|
||||
const int kEntriesPerBuffer = 100;
|
||||
const int kNumL0Files = 4;
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = kEntrySize * kEntriesPerBuffer;
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.target_file_size_base = options.write_buffer_size;
|
||||
options.max_bytes_for_level_base = options.target_file_size_base * 2;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.compression = kNoCompression;
|
||||
options.enable_thread_tracking = true;
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
|
||||
TestCompactionListener* listener = new TestCompactionListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
std::vector<std::string> cf_names = {
|
||||
"pikachu", "ilya", "muromec", "dobrynia",
|
||||
"nikitich", "alyosha", "popovich"};
|
||||
CreateAndReopenWithCF(cf_names, &options);
|
||||
ASSERT_OK(Put(1, "pikachu", std::string(90000, 'p')));
|
||||
ASSERT_OK(Put(2, "ilya", std::string(90000, 'i')));
|
||||
ASSERT_OK(Put(3, "muromec", std::string(90000, 'm')));
|
||||
ASSERT_OK(Put(4, "dobrynia", std::string(90000, 'd')));
|
||||
ASSERT_OK(Put(5, "nikitich", std::string(90000, 'n')));
|
||||
ASSERT_OK(Put(6, "alyosha", std::string(90000, 'a')));
|
||||
ASSERT_OK(Put(7, "popovich", std::string(90000, 'p')));
|
||||
for (size_t i = 1; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(static_cast<int>(i)));
|
||||
const Slice kStart = "a";
|
||||
const Slice kEnd = "z";
|
||||
ASSERT_OK(dbfull()->CompactRange(handles_[i], &kStart, &kEnd));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
ASSERT_EQ(listener->compacted_dbs_.size(), cf_names.size());
|
||||
for (size_t i = 0; i < cf_names.size(); ++i) {
|
||||
ASSERT_EQ(listener->compacted_dbs_[i], db_);
|
||||
}
|
||||
}
|
||||
|
||||
class TestFlushListener : public EventListener {
|
||||
public:
|
||||
void OnFlushCompleted(
|
||||
@@ -173,7 +227,7 @@ class TestFlushListener : public EventListener {
|
||||
int stop_count;
|
||||
};
|
||||
|
||||
TEST(EventListenerTest, OnSingleDBFlushTest) {
|
||||
TEST_F(EventListenerTest, OnSingleDBFlushTest) {
|
||||
Options options;
|
||||
options.write_buffer_size = 100000;
|
||||
TestFlushListener* listener = new TestFlushListener();
|
||||
@@ -204,7 +258,7 @@ TEST(EventListenerTest, OnSingleDBFlushTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EventListenerTest, MultiCF) {
|
||||
TEST_F(EventListenerTest, MultiCF) {
|
||||
Options options;
|
||||
options.write_buffer_size = 100000;
|
||||
TestFlushListener* listener = new TestFlushListener();
|
||||
@@ -234,7 +288,7 @@ TEST(EventListenerTest, MultiCF) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EventListenerTest, MultiDBMultiListeners) {
|
||||
TEST_F(EventListenerTest, MultiDBMultiListeners) {
|
||||
std::vector<TestFlushListener*> listeners;
|
||||
const int kNumDBs = 5;
|
||||
const int kNumListeners = 10;
|
||||
@@ -310,7 +364,7 @@ TEST(EventListenerTest, MultiDBMultiListeners) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(EventListenerTest, DisableBGCompaction) {
|
||||
TEST_F(EventListenerTest, DisableBGCompaction) {
|
||||
Options options;
|
||||
TestFlushListener* listener = new TestFlushListener();
|
||||
const int kSlowdownTrigger = 5;
|
||||
@@ -343,6 +397,7 @@ TEST(EventListenerTest, DisableBGCompaction) {
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ void BM_LogAndApply(int iters, int num_base_files) {
|
||||
WriteController wc;
|
||||
ColumnFamilyData* default_cfd;
|
||||
uint64_t fnum = 1;
|
||||
port::Mutex mu;
|
||||
MutexLock l(&mu);
|
||||
InstrumentedMutex mu;
|
||||
InstrumentedMutexLock l(&mu);
|
||||
|
||||
BENCHMARK_SUSPEND {
|
||||
std::string dbname = test::TmpDir() + "/rocksdb_test_benchmark";
|
||||
|
||||
+7
-16
@@ -39,12 +39,11 @@ Reader::~Reader() {
|
||||
}
|
||||
|
||||
bool Reader::SkipToInitialBlock() {
|
||||
size_t offset_in_block = initial_offset_ % kBlockSize;
|
||||
uint64_t block_start_location = initial_offset_ - offset_in_block;
|
||||
size_t initial_offset_in_block = initial_offset_ % kBlockSize;
|
||||
uint64_t block_start_location = initial_offset_ - initial_offset_in_block;
|
||||
|
||||
// Don't search a block if we'd be in the trailer
|
||||
if (offset_in_block > kBlockSize - 6) {
|
||||
offset_in_block = 0;
|
||||
if (initial_offset_in_block > kBlockSize - 6) {
|
||||
block_start_location += kBlockSize;
|
||||
}
|
||||
|
||||
@@ -82,16 +81,12 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
|
||||
const unsigned int record_type = ReadPhysicalRecord(&fragment);
|
||||
switch (record_type) {
|
||||
case kFullType:
|
||||
if (in_fragmented_record) {
|
||||
if (in_fragmented_record && !scratch->empty()) {
|
||||
// Handle bug in earlier versions of log::Writer where
|
||||
// it could emit an empty kFirstType record at the tail end
|
||||
// of a block followed by a kFullType or kFirstType record
|
||||
// at the beginning of the next block.
|
||||
if (scratch->empty()) {
|
||||
in_fragmented_record = false;
|
||||
} else {
|
||||
ReportCorruption(scratch->size(), "partial record without end(1)");
|
||||
}
|
||||
ReportCorruption(scratch->size(), "partial record without end(1)");
|
||||
}
|
||||
prospective_record_offset = physical_record_offset;
|
||||
scratch->clear();
|
||||
@@ -100,16 +95,12 @@ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
|
||||
return true;
|
||||
|
||||
case kFirstType:
|
||||
if (in_fragmented_record) {
|
||||
if (in_fragmented_record && !scratch->empty()) {
|
||||
// Handle bug in earlier versions of log::Writer where
|
||||
// it could emit an empty kFirstType record at the tail end
|
||||
// of a block followed by a kFullType or kFirstType record
|
||||
// at the beginning of the next block.
|
||||
if (scratch->empty()) {
|
||||
in_fragmented_record = false;
|
||||
} else {
|
||||
ReportCorruption(scratch->size(), "partial record without end(2)");
|
||||
}
|
||||
ReportCorruption(scratch->size(), "partial record without end(2)");
|
||||
}
|
||||
prospective_record_offset = physical_record_offset;
|
||||
scratch->assign(fragment.data(), fragment.size());
|
||||
|
||||
+51
-71
@@ -41,7 +41,7 @@ static std::string RandomSkewedString(int i, Random* rnd) {
|
||||
return BigString(NumberString(i), rnd->Skewed(17));
|
||||
}
|
||||
|
||||
class LogTest {
|
||||
class LogTest : public testing::Test {
|
||||
private:
|
||||
class StringDest : public WritableFile {
|
||||
public:
|
||||
@@ -55,9 +55,9 @@ class LogTest {
|
||||
reader_contents_ = Slice(contents_.data(), 0);
|
||||
};
|
||||
|
||||
virtual Status Close() { return Status::OK(); }
|
||||
virtual Status Flush() {
|
||||
ASSERT_TRUE(reader_contents_.size() <= last_flush_);
|
||||
virtual Status Close() override { return Status::OK(); }
|
||||
virtual Status Flush() override {
|
||||
EXPECT_TRUE(reader_contents_.size() <= last_flush_);
|
||||
size_t offset = last_flush_ - reader_contents_.size();
|
||||
reader_contents_ = Slice(
|
||||
contents_.data() + offset,
|
||||
@@ -66,8 +66,8 @@ class LogTest {
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status Sync() { return Status::OK(); }
|
||||
virtual Status Append(const Slice& slice) {
|
||||
virtual Status Sync() override { return Status::OK(); }
|
||||
virtual Status Append(const Slice& slice) override {
|
||||
contents_.append(slice.data(), slice.size());
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -99,8 +99,8 @@ class LogTest {
|
||||
force_eof_position_(0),
|
||||
returned_partial_(false) { }
|
||||
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) {
|
||||
ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error";
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
EXPECT_TRUE(!returned_partial_) << "must not Read() after eof/error";
|
||||
|
||||
if (force_error_) {
|
||||
if (force_error_position_ >= n) {
|
||||
@@ -138,7 +138,7 @@ class LogTest {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status Skip(uint64_t n) {
|
||||
virtual Status Skip(uint64_t n) override {
|
||||
if (n > contents_.size()) {
|
||||
contents_.clear();
|
||||
return Status::NotFound("in-memory file skipepd past end");
|
||||
@@ -156,7 +156,7 @@ class LogTest {
|
||||
std::string message_;
|
||||
|
||||
ReportCollector() : dropped_bytes_(0) { }
|
||||
virtual void Corruption(size_t bytes, const Status& status) {
|
||||
virtual void Corruption(size_t bytes, const Status& status) override {
|
||||
dropped_bytes_ += bytes;
|
||||
message_.append(status.ToString());
|
||||
}
|
||||
@@ -329,12 +329,9 @@ uint64_t LogTest::initial_offset_last_record_offsets_[] =
|
||||
2 * (kHeaderSize + 10000) +
|
||||
(2 * log::kBlockSize - 1000) + 3 * kHeaderSize};
|
||||
|
||||
TEST_F(LogTest, Empty) { ASSERT_EQ("EOF", Read()); }
|
||||
|
||||
TEST(LogTest, Empty) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST(LogTest, ReadWrite) {
|
||||
TEST_F(LogTest, ReadWrite) {
|
||||
Write("foo");
|
||||
Write("bar");
|
||||
Write("");
|
||||
@@ -347,7 +344,7 @@ TEST(LogTest, ReadWrite) {
|
||||
ASSERT_EQ("EOF", Read()); // Make sure reads at eof work
|
||||
}
|
||||
|
||||
TEST(LogTest, ManyBlocks) {
|
||||
TEST_F(LogTest, ManyBlocks) {
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
Write(NumberString(i));
|
||||
}
|
||||
@@ -357,7 +354,7 @@ TEST(LogTest, ManyBlocks) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST(LogTest, Fragmentation) {
|
||||
TEST_F(LogTest, Fragmentation) {
|
||||
Write("small");
|
||||
Write(BigString("medium", 50000));
|
||||
Write(BigString("large", 100000));
|
||||
@@ -367,7 +364,7 @@ TEST(LogTest, Fragmentation) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST(LogTest, MarginalTrailer) {
|
||||
TEST_F(LogTest, MarginalTrailer) {
|
||||
// Make a trailer that is exactly the same length as an empty record.
|
||||
const int n = kBlockSize - 2*kHeaderSize;
|
||||
Write(BigString("foo", n));
|
||||
@@ -380,7 +377,7 @@ TEST(LogTest, MarginalTrailer) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST(LogTest, MarginalTrailer2) {
|
||||
TEST_F(LogTest, MarginalTrailer2) {
|
||||
// Make a trailer that is exactly the same length as an empty record.
|
||||
const int n = kBlockSize - 2*kHeaderSize;
|
||||
Write(BigString("foo", n));
|
||||
@@ -393,7 +390,7 @@ TEST(LogTest, MarginalTrailer2) {
|
||||
ASSERT_EQ("", ReportMessage());
|
||||
}
|
||||
|
||||
TEST(LogTest, ShortTrailer) {
|
||||
TEST_F(LogTest, ShortTrailer) {
|
||||
const int n = kBlockSize - 2*kHeaderSize + 4;
|
||||
Write(BigString("foo", n));
|
||||
ASSERT_EQ((unsigned int)(kBlockSize - kHeaderSize + 4), WrittenBytes());
|
||||
@@ -405,7 +402,7 @@ TEST(LogTest, ShortTrailer) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST(LogTest, AlignedEof) {
|
||||
TEST_F(LogTest, AlignedEof) {
|
||||
const int n = kBlockSize - 2*kHeaderSize + 4;
|
||||
Write(BigString("foo", n));
|
||||
ASSERT_EQ((unsigned int)(kBlockSize - kHeaderSize + 4), WrittenBytes());
|
||||
@@ -413,7 +410,7 @@ TEST(LogTest, AlignedEof) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST(LogTest, RandomRead) {
|
||||
TEST_F(LogTest, RandomRead) {
|
||||
const int N = 500;
|
||||
Random write_rnd(301);
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -428,7 +425,7 @@ TEST(LogTest, RandomRead) {
|
||||
|
||||
// Tests of all the error paths in log_reader.cc follow:
|
||||
|
||||
TEST(LogTest, ReadError) {
|
||||
TEST_F(LogTest, ReadError) {
|
||||
Write("foo");
|
||||
ForceError();
|
||||
ASSERT_EQ("EOF", Read());
|
||||
@@ -436,7 +433,7 @@ TEST(LogTest, ReadError) {
|
||||
ASSERT_EQ("OK", MatchError("read error"));
|
||||
}
|
||||
|
||||
TEST(LogTest, BadRecordType) {
|
||||
TEST_F(LogTest, BadRecordType) {
|
||||
Write("foo");
|
||||
// Type is stored in header[6]
|
||||
IncrementByte(6, 100);
|
||||
@@ -446,7 +443,7 @@ TEST(LogTest, BadRecordType) {
|
||||
ASSERT_EQ("OK", MatchError("unknown record type"));
|
||||
}
|
||||
|
||||
TEST(LogTest, TruncatedTrailingRecordIsIgnored) {
|
||||
TEST_F(LogTest, TruncatedTrailingRecordIsIgnored) {
|
||||
Write("foo");
|
||||
ShrinkSize(4); // Drop all payload as well as a header byte
|
||||
ASSERT_EQ("EOF", Read());
|
||||
@@ -455,7 +452,7 @@ TEST(LogTest, TruncatedTrailingRecordIsIgnored) {
|
||||
ASSERT_EQ("", ReportMessage());
|
||||
}
|
||||
|
||||
TEST(LogTest, BadLength) {
|
||||
TEST_F(LogTest, BadLength) {
|
||||
const int kPayloadSize = kBlockSize - kHeaderSize;
|
||||
Write(BigString("bar", kPayloadSize));
|
||||
Write("foo");
|
||||
@@ -466,7 +463,7 @@ TEST(LogTest, BadLength) {
|
||||
ASSERT_EQ("OK", MatchError("bad record length"));
|
||||
}
|
||||
|
||||
TEST(LogTest, BadLengthAtEndIsIgnored) {
|
||||
TEST_F(LogTest, BadLengthAtEndIsIgnored) {
|
||||
Write("foo");
|
||||
ShrinkSize(1);
|
||||
ASSERT_EQ("EOF", Read());
|
||||
@@ -474,7 +471,7 @@ TEST(LogTest, BadLengthAtEndIsIgnored) {
|
||||
ASSERT_EQ("", ReportMessage());
|
||||
}
|
||||
|
||||
TEST(LogTest, ChecksumMismatch) {
|
||||
TEST_F(LogTest, ChecksumMismatch) {
|
||||
Write("foo");
|
||||
IncrementByte(0, 10);
|
||||
ASSERT_EQ("EOF", Read());
|
||||
@@ -482,7 +479,7 @@ TEST(LogTest, ChecksumMismatch) {
|
||||
ASSERT_EQ("OK", MatchError("checksum mismatch"));
|
||||
}
|
||||
|
||||
TEST(LogTest, UnexpectedMiddleType) {
|
||||
TEST_F(LogTest, UnexpectedMiddleType) {
|
||||
Write("foo");
|
||||
SetByte(6, kMiddleType);
|
||||
FixChecksum(0, 3);
|
||||
@@ -491,7 +488,7 @@ TEST(LogTest, UnexpectedMiddleType) {
|
||||
ASSERT_EQ("OK", MatchError("missing start"));
|
||||
}
|
||||
|
||||
TEST(LogTest, UnexpectedLastType) {
|
||||
TEST_F(LogTest, UnexpectedLastType) {
|
||||
Write("foo");
|
||||
SetByte(6, kLastType);
|
||||
FixChecksum(0, 3);
|
||||
@@ -500,7 +497,7 @@ TEST(LogTest, UnexpectedLastType) {
|
||||
ASSERT_EQ("OK", MatchError("missing start"));
|
||||
}
|
||||
|
||||
TEST(LogTest, UnexpectedFullType) {
|
||||
TEST_F(LogTest, UnexpectedFullType) {
|
||||
Write("foo");
|
||||
Write("bar");
|
||||
SetByte(6, kFirstType);
|
||||
@@ -511,7 +508,7 @@ TEST(LogTest, UnexpectedFullType) {
|
||||
ASSERT_EQ("OK", MatchError("partial record without end"));
|
||||
}
|
||||
|
||||
TEST(LogTest, UnexpectedFirstType) {
|
||||
TEST_F(LogTest, UnexpectedFirstType) {
|
||||
Write("foo");
|
||||
Write(BigString("bar", 100000));
|
||||
SetByte(6, kFirstType);
|
||||
@@ -522,7 +519,7 @@ TEST(LogTest, UnexpectedFirstType) {
|
||||
ASSERT_EQ("OK", MatchError("partial record without end"));
|
||||
}
|
||||
|
||||
TEST(LogTest, MissingLastIsIgnored) {
|
||||
TEST_F(LogTest, MissingLastIsIgnored) {
|
||||
Write(BigString("bar", kBlockSize));
|
||||
// Remove the LAST block, including header.
|
||||
ShrinkSize(14);
|
||||
@@ -531,7 +528,7 @@ TEST(LogTest, MissingLastIsIgnored) {
|
||||
ASSERT_EQ(0U, DroppedBytes());
|
||||
}
|
||||
|
||||
TEST(LogTest, PartialLastIsIgnored) {
|
||||
TEST_F(LogTest, PartialLastIsIgnored) {
|
||||
Write(BigString("bar", kBlockSize));
|
||||
// Cause a bad record length in the LAST block.
|
||||
ShrinkSize(1);
|
||||
@@ -540,7 +537,7 @@ TEST(LogTest, PartialLastIsIgnored) {
|
||||
ASSERT_EQ(0U, DroppedBytes());
|
||||
}
|
||||
|
||||
TEST(LogTest, ErrorJoinsRecords) {
|
||||
TEST_F(LogTest, ErrorJoinsRecords) {
|
||||
// Consider two fragmented records:
|
||||
// first(R1) last(R1) first(R2) last(R2)
|
||||
// where the middle two fragments disappear. We do not want
|
||||
@@ -563,61 +560,43 @@ TEST(LogTest, ErrorJoinsRecords) {
|
||||
ASSERT_GE(dropped, 2 * kBlockSize);
|
||||
}
|
||||
|
||||
TEST(LogTest, ReadStart) {
|
||||
CheckInitialOffsetRecord(0, 0);
|
||||
}
|
||||
TEST_F(LogTest, ReadStart) { CheckInitialOffsetRecord(0, 0); }
|
||||
|
||||
TEST(LogTest, ReadSecondOneOff) {
|
||||
CheckInitialOffsetRecord(1, 1);
|
||||
}
|
||||
TEST_F(LogTest, ReadSecondOneOff) { CheckInitialOffsetRecord(1, 1); }
|
||||
|
||||
TEST(LogTest, ReadSecondTenThousand) {
|
||||
CheckInitialOffsetRecord(10000, 1);
|
||||
}
|
||||
TEST_F(LogTest, ReadSecondTenThousand) { CheckInitialOffsetRecord(10000, 1); }
|
||||
|
||||
TEST(LogTest, ReadSecondStart) {
|
||||
CheckInitialOffsetRecord(10007, 1);
|
||||
}
|
||||
TEST_F(LogTest, ReadSecondStart) { CheckInitialOffsetRecord(10007, 1); }
|
||||
|
||||
TEST(LogTest, ReadThirdOneOff) {
|
||||
CheckInitialOffsetRecord(10008, 2);
|
||||
}
|
||||
TEST_F(LogTest, ReadThirdOneOff) { CheckInitialOffsetRecord(10008, 2); }
|
||||
|
||||
TEST(LogTest, ReadThirdStart) {
|
||||
CheckInitialOffsetRecord(20014, 2);
|
||||
}
|
||||
TEST_F(LogTest, ReadThirdStart) { CheckInitialOffsetRecord(20014, 2); }
|
||||
|
||||
TEST(LogTest, ReadFourthOneOff) {
|
||||
CheckInitialOffsetRecord(20015, 3);
|
||||
}
|
||||
TEST_F(LogTest, ReadFourthOneOff) { CheckInitialOffsetRecord(20015, 3); }
|
||||
|
||||
TEST(LogTest, ReadFourthFirstBlockTrailer) {
|
||||
TEST_F(LogTest, ReadFourthFirstBlockTrailer) {
|
||||
CheckInitialOffsetRecord(log::kBlockSize - 4, 3);
|
||||
}
|
||||
|
||||
TEST(LogTest, ReadFourthMiddleBlock) {
|
||||
TEST_F(LogTest, ReadFourthMiddleBlock) {
|
||||
CheckInitialOffsetRecord(log::kBlockSize + 1, 3);
|
||||
}
|
||||
|
||||
TEST(LogTest, ReadFourthLastBlock) {
|
||||
TEST_F(LogTest, ReadFourthLastBlock) {
|
||||
CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3);
|
||||
}
|
||||
|
||||
TEST(LogTest, ReadFourthStart) {
|
||||
TEST_F(LogTest, ReadFourthStart) {
|
||||
CheckInitialOffsetRecord(
|
||||
2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize,
|
||||
3);
|
||||
}
|
||||
|
||||
TEST(LogTest, ReadEnd) {
|
||||
CheckOffsetPastEndReturnsNoRecords(0);
|
||||
}
|
||||
TEST_F(LogTest, ReadEnd) { CheckOffsetPastEndReturnsNoRecords(0); }
|
||||
|
||||
TEST(LogTest, ReadPastEnd) {
|
||||
CheckOffsetPastEndReturnsNoRecords(5);
|
||||
}
|
||||
TEST_F(LogTest, ReadPastEnd) { CheckOffsetPastEndReturnsNoRecords(5); }
|
||||
|
||||
TEST(LogTest, ClearEofSingleBlock) {
|
||||
TEST_F(LogTest, ClearEofSingleBlock) {
|
||||
Write("foo");
|
||||
Write("bar");
|
||||
ForceEOF(3 + kHeaderSize + 2);
|
||||
@@ -632,7 +611,7 @@ TEST(LogTest, ClearEofSingleBlock) {
|
||||
ASSERT_TRUE(IsEOF());
|
||||
}
|
||||
|
||||
TEST(LogTest, ClearEofMultiBlock) {
|
||||
TEST_F(LogTest, ClearEofMultiBlock) {
|
||||
size_t num_full_blocks = 5;
|
||||
size_t n = (kBlockSize - kHeaderSize) * num_full_blocks + 25;
|
||||
Write(BigString("foo", n));
|
||||
@@ -649,7 +628,7 @@ TEST(LogTest, ClearEofMultiBlock) {
|
||||
ASSERT_TRUE(IsEOF());
|
||||
}
|
||||
|
||||
TEST(LogTest, ClearEofError) {
|
||||
TEST_F(LogTest, ClearEofError) {
|
||||
// If an error occurs during Read() in UnmarkEOF(), the records contained
|
||||
// in the buffer should be returned on subsequent calls of ReadRecord()
|
||||
// until no more full records are left, whereafter ReadRecord() should return
|
||||
@@ -667,7 +646,7 @@ TEST(LogTest, ClearEofError) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST(LogTest, ClearEofError2) {
|
||||
TEST_F(LogTest, ClearEofError2) {
|
||||
Write("foo");
|
||||
Write("bar");
|
||||
UnmarkEOF();
|
||||
@@ -685,5 +664,6 @@ TEST(LogTest, ClearEofError2) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+1
-1
@@ -52,7 +52,7 @@ Status Writer::AddRecord(const Slice& slice) {
|
||||
}
|
||||
|
||||
// Invariant: we never leave < kHeaderSize bytes in a block.
|
||||
assert(kBlockSize - block_offset_ - kHeaderSize >= 0);
|
||||
assert(kBlockSize - block_offset_ >= kHeaderSize);
|
||||
|
||||
const size_t avail = kBlockSize - block_offset_ - kHeaderSize;
|
||||
const size_t fragment_length = (left < avail) ? left : avail;
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/managed_iterator.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "table/merger.h"
|
||||
#include "util/xfunc.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
// Helper class that locks a mutex on construction and unlocks the mutex when
|
||||
// the destructor of the MutexLock object is invoked.
|
||||
//
|
||||
// Typical usage:
|
||||
//
|
||||
// void MyClass::MyMethod() {
|
||||
// MILock l(&mu_); // mu_ is an instance variable
|
||||
// ... some complex code, possibly with multiple return paths ...
|
||||
// }
|
||||
|
||||
class MILock {
|
||||
public:
|
||||
explicit MILock(std::mutex* mu, ManagedIterator* mi) : mu_(mu), mi_(mi) {
|
||||
this->mu_->lock();
|
||||
}
|
||||
~MILock() {
|
||||
this->mu_->unlock();
|
||||
XFUNC_TEST("managed_xftest_release", "managed_unlock", managed_unlock1,
|
||||
xf_manage_release, mi_);
|
||||
}
|
||||
ManagedIterator* GetManagedIterator() { return mi_; }
|
||||
|
||||
private:
|
||||
std::mutex* const mu_;
|
||||
ManagedIterator* mi_;
|
||||
// No copying allowed
|
||||
MILock(const MILock&) = delete;
|
||||
void operator=(const MILock&) = delete;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
//
|
||||
// Synchronization between modifiers, releasers, creators
|
||||
// If iterator operation, wait till (!in_use), set in_use, do op, reset in_use
|
||||
// if modifying mutable_iter, atomically exchange in_use:
|
||||
// return if in_use set / otherwise set in use,
|
||||
// atomically replace new iter with old , reset in use
|
||||
// The releaser is the new operation and it holds a lock for a very short time
|
||||
// The existing non-const iterator operations are supposed to be single
|
||||
// threaded and hold the lock for the duration of the operation
|
||||
// The existing const iterator operations use the cached key/values
|
||||
// and don't do any locking.
|
||||
ManagedIterator::ManagedIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd)
|
||||
: db_(db),
|
||||
read_options_(read_options),
|
||||
cfd_(cfd),
|
||||
svnum_(cfd->GetSuperVersionNumber()),
|
||||
mutable_iter_(nullptr),
|
||||
valid_(false),
|
||||
snapshot_created_(false),
|
||||
release_supported_(true) {
|
||||
read_options_.managed = false;
|
||||
if ((!read_options_.tailing) && (read_options_.snapshot == nullptr)) {
|
||||
assert(read_options_.snapshot = db_->GetSnapshot());
|
||||
snapshot_created_ = true;
|
||||
}
|
||||
cfh_.SetCFD(cfd);
|
||||
mutable_iter_ = unique_ptr<Iterator>(db->NewIterator(read_options_, &cfh_));
|
||||
XFUNC_TEST("managed_xftest_dropold", "managed_create", xf_managed_create1,
|
||||
xf_manage_create, this);
|
||||
}
|
||||
|
||||
ManagedIterator::~ManagedIterator() {
|
||||
Lock();
|
||||
if (snapshot_created_) {
|
||||
db_->ReleaseSnapshot(read_options_.snapshot);
|
||||
snapshot_created_ = false;
|
||||
read_options_.snapshot = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ManagedIterator::Valid() const { return valid_; }
|
||||
|
||||
void ManagedIterator::SeekToLast() {
|
||||
MILock l(&in_use_, this);
|
||||
if (NeedToRebuild()) {
|
||||
RebuildIterator();
|
||||
}
|
||||
assert(mutable_iter_ != nullptr);
|
||||
mutable_iter_->SeekToLast();
|
||||
if (mutable_iter_->status().ok()) {
|
||||
UpdateCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
void ManagedIterator::SeekToFirst() {
|
||||
MILock l(&in_use_, this);
|
||||
SeekInternal(Slice(), true);
|
||||
}
|
||||
|
||||
void ManagedIterator::Seek(const Slice& user_key) {
|
||||
MILock l(&in_use_, this);
|
||||
SeekInternal(user_key, false);
|
||||
}
|
||||
|
||||
void ManagedIterator::SeekInternal(const Slice& user_key, bool seek_to_first) {
|
||||
if (NeedToRebuild()) {
|
||||
RebuildIterator();
|
||||
}
|
||||
assert(mutable_iter_ != nullptr);
|
||||
if (seek_to_first) {
|
||||
mutable_iter_->SeekToFirst();
|
||||
} else {
|
||||
mutable_iter_->Seek(user_key);
|
||||
}
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
void ManagedIterator::Prev() {
|
||||
if (!valid_) {
|
||||
status_ = Status::InvalidArgument("Iterator value invalid");
|
||||
return;
|
||||
}
|
||||
MILock l(&in_use_, this);
|
||||
if (NeedToRebuild()) {
|
||||
std::string current_key = key().ToString();
|
||||
Slice old_key(current_key);
|
||||
RebuildIterator();
|
||||
SeekInternal(old_key, false);
|
||||
UpdateCurrent();
|
||||
if (!valid_) {
|
||||
return;
|
||||
}
|
||||
if (key().compare(old_key) != 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::Incomplete("Cannot do Prev now");
|
||||
return;
|
||||
}
|
||||
}
|
||||
mutable_iter_->Prev();
|
||||
if (mutable_iter_->status().ok()) {
|
||||
UpdateCurrent();
|
||||
status_ = Status::OK();
|
||||
} else {
|
||||
status_ = mutable_iter_->status();
|
||||
}
|
||||
}
|
||||
|
||||
void ManagedIterator::Next() {
|
||||
if (!valid_) {
|
||||
status_ = Status::InvalidArgument("Iterator value invalid");
|
||||
return;
|
||||
}
|
||||
MILock l(&in_use_, this);
|
||||
if (NeedToRebuild()) {
|
||||
std::string current_key = key().ToString();
|
||||
Slice old_key(current_key.data(), cached_key_.Size());
|
||||
RebuildIterator();
|
||||
SeekInternal(old_key, false);
|
||||
UpdateCurrent();
|
||||
if (!valid_) {
|
||||
return;
|
||||
}
|
||||
if (key().compare(old_key) != 0) {
|
||||
valid_ = false;
|
||||
status_ = Status::Incomplete("Cannot do Next now");
|
||||
return;
|
||||
}
|
||||
}
|
||||
mutable_iter_->Next();
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
Slice ManagedIterator::key() const {
|
||||
assert(valid_);
|
||||
return cached_key_.GetKey();
|
||||
}
|
||||
|
||||
Slice ManagedIterator::value() const {
|
||||
assert(valid_);
|
||||
return cached_value_.GetKey();
|
||||
}
|
||||
|
||||
Status ManagedIterator::status() const { return status_; }
|
||||
|
||||
void ManagedIterator::RebuildIterator() {
|
||||
svnum_ = cfd_->GetSuperVersionNumber();
|
||||
mutable_iter_ = unique_ptr<Iterator>(db_->NewIterator(read_options_, &cfh_));
|
||||
}
|
||||
|
||||
void ManagedIterator::UpdateCurrent() {
|
||||
assert(mutable_iter_ != nullptr);
|
||||
|
||||
if (!(valid_ = mutable_iter_->Valid())) {
|
||||
status_ = mutable_iter_->status();
|
||||
return;
|
||||
}
|
||||
|
||||
status_ = Status::OK();
|
||||
cached_key_.SetKey(mutable_iter_->key());
|
||||
cached_value_.SetKey(mutable_iter_->value());
|
||||
}
|
||||
|
||||
void ManagedIterator::ReleaseIter(bool only_old) {
|
||||
if ((mutable_iter_ == nullptr) || (!release_supported_)) {
|
||||
return;
|
||||
}
|
||||
if (svnum_ != cfd_->GetSuperVersionNumber() || !only_old) {
|
||||
if (!TryLock()) { // Don't release iter if in use
|
||||
return;
|
||||
}
|
||||
mutable_iter_ = nullptr; // in_use for a very short time
|
||||
UnLock();
|
||||
}
|
||||
}
|
||||
|
||||
bool ManagedIterator::NeedToRebuild() {
|
||||
if ((mutable_iter_ == nullptr) || (status_.IsIncomplete()) ||
|
||||
(!only_drop_old_ && (svnum_ != cfd_->GetSuperVersionNumber()))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ManagedIterator::Lock() {
|
||||
in_use_.lock();
|
||||
return;
|
||||
}
|
||||
|
||||
bool ManagedIterator::TryLock() { return in_use_.try_lock(); }
|
||||
|
||||
void ManagedIterator::UnLock() {
|
||||
in_use_.unlock();
|
||||
XFUNC_TEST("managed_xftest_release", "managed_unlock", managed_unlock1,
|
||||
xf_manage_release, this);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
#pragma once
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/arena.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBImpl;
|
||||
struct SuperVersion;
|
||||
class ColumnFamilyData;
|
||||
|
||||
/**
|
||||
* ManagedIterator is a special type of iterator that supports freeing the
|
||||
* underlying iterator and still being able to access the current key/value
|
||||
* pair. This is done by copying the key/value pair so that clients can
|
||||
* continue to access the data without getting a SIGSEGV.
|
||||
* The underlying iterator can be freed manually through the call to
|
||||
* ReleaseIter or automatically (as needed on space pressure or age.)
|
||||
* The iterator is recreated using the saved original arguments.
|
||||
*/
|
||||
class ManagedIterator : public Iterator {
|
||||
public:
|
||||
ManagedIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd);
|
||||
virtual ~ManagedIterator();
|
||||
|
||||
virtual void SeekToLast() override;
|
||||
virtual void Prev() override;
|
||||
virtual bool Valid() const override;
|
||||
void SeekToFirst() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void Next() override;
|
||||
virtual Slice key() const override;
|
||||
virtual Slice value() const override;
|
||||
virtual Status status() const override;
|
||||
void ReleaseIter(bool only_old);
|
||||
void SetDropOld(bool only_old) {
|
||||
only_drop_old_ = read_options_.tailing || only_old;
|
||||
}
|
||||
|
||||
private:
|
||||
void RebuildIterator();
|
||||
void UpdateCurrent();
|
||||
void SeekInternal(const Slice& user_key, bool seek_to_first);
|
||||
bool NeedToRebuild();
|
||||
void Lock();
|
||||
bool TryLock();
|
||||
void UnLock();
|
||||
DBImpl* const db_;
|
||||
ReadOptions read_options_;
|
||||
ColumnFamilyData* const cfd_;
|
||||
ColumnFamilyHandleInternal cfh_;
|
||||
|
||||
uint64_t svnum_;
|
||||
std::unique_ptr<Iterator> mutable_iter_;
|
||||
// internal iterator status
|
||||
Status status_;
|
||||
bool valid_;
|
||||
|
||||
IterKey cached_key_;
|
||||
IterKey cached_value_;
|
||||
|
||||
bool only_drop_old_ = true;
|
||||
bool snapshot_created_;
|
||||
bool release_supported_;
|
||||
std::mutex in_use_; // is managed iterator in use
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // !ROCKSDB_LITE
|
||||
+44
-18
@@ -65,16 +65,19 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
comparator_, &allocator_, ioptions.prefix_extractor,
|
||||
ioptions.info_log)),
|
||||
num_entries_(0),
|
||||
num_deletes_(0),
|
||||
flush_in_progress_(false),
|
||||
flush_completed_(false),
|
||||
file_number_(0),
|
||||
first_seqno_(0),
|
||||
mem_next_logfile_number_(0),
|
||||
locks_(moptions_.inplace_update_support ?
|
||||
moptions_.inplace_update_num_locks : 0),
|
||||
locks_(moptions_.inplace_update_support
|
||||
? moptions_.inplace_update_num_locks
|
||||
: 0),
|
||||
prefix_extractor_(ioptions.prefix_extractor),
|
||||
should_flush_(ShouldFlushNow()),
|
||||
flush_scheduled_(false) {
|
||||
flush_scheduled_(false),
|
||||
env_(ioptions.env) {
|
||||
// if should_flush_ == true without an entry inserted, something must have
|
||||
// gone wrong already.
|
||||
assert(!should_flush_);
|
||||
@@ -221,8 +224,10 @@ class MemTableIterator: public Iterator {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool Valid() const { return valid_; }
|
||||
virtual void Seek(const Slice& k) {
|
||||
virtual bool Valid() const override { return valid_; }
|
||||
virtual void Seek(const Slice& k) override {
|
||||
PERF_TIMER_GUARD(seek_on_memtable_time);
|
||||
PERF_COUNTER_ADD(seek_on_memtable_count, 1);
|
||||
if (bloom_ != nullptr &&
|
||||
!bloom_->MayContain(prefix_extractor_->Transform(ExtractUserKey(k)))) {
|
||||
valid_ = false;
|
||||
@@ -231,35 +236,35 @@ class MemTableIterator: public Iterator {
|
||||
iter_->Seek(k, nullptr);
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void SeekToFirst() {
|
||||
virtual void SeekToFirst() override {
|
||||
iter_->SeekToFirst();
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void SeekToLast() {
|
||||
virtual void SeekToLast() override {
|
||||
iter_->SeekToLast();
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void Next() {
|
||||
virtual void Next() override {
|
||||
assert(Valid());
|
||||
iter_->Next();
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void Prev() {
|
||||
virtual void Prev() override {
|
||||
assert(Valid());
|
||||
iter_->Prev();
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual Slice key() const {
|
||||
virtual Slice key() const override {
|
||||
assert(Valid());
|
||||
return GetLengthPrefixedSlice(iter_->key());
|
||||
}
|
||||
virtual Slice value() const {
|
||||
virtual Slice value() const override {
|
||||
assert(Valid());
|
||||
Slice key_slice = GetLengthPrefixedSlice(iter_->key());
|
||||
return GetLengthPrefixedSlice(key_slice.data() + key_slice.size());
|
||||
}
|
||||
|
||||
virtual Status status() const { return Status::OK(); }
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
DynamicBloom* bloom_;
|
||||
@@ -311,6 +316,9 @@ void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
assert((unsigned)(p + val_size - buf) == (unsigned)encoded_len);
|
||||
table_->Insert(handle);
|
||||
num_entries_++;
|
||||
if (type == kTypeDeletion) {
|
||||
num_deletes_++;
|
||||
}
|
||||
|
||||
if (prefix_bloom_) {
|
||||
assert(prefix_extractor_);
|
||||
@@ -342,6 +350,7 @@ struct Saver {
|
||||
Logger* logger;
|
||||
Statistics* statistics;
|
||||
bool inplace_update_support;
|
||||
Env* env_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -376,9 +385,17 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
*(s->status) = Status::OK();
|
||||
if (*(s->merge_in_progress)) {
|
||||
assert(merge_operator);
|
||||
if (!merge_operator->FullMerge(s->key->user_key(), &v,
|
||||
merge_context->GetOperands(), s->value,
|
||||
s->logger)) {
|
||||
bool merge_success = false;
|
||||
{
|
||||
StopWatchNano timer(s->env_, s->statistics != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
merge_success = merge_operator->FullMerge(
|
||||
s->key->user_key(), &v, merge_context->GetOperands(), s->value,
|
||||
s->logger);
|
||||
RecordTick(s->statistics, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
}
|
||||
if (!merge_success) {
|
||||
RecordTick(s->statistics, NUMBER_MERGE_FAILURES);
|
||||
*(s->status) =
|
||||
Status::Corruption("Error: Could not perform merge.");
|
||||
@@ -396,9 +413,17 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
if (*(s->merge_in_progress)) {
|
||||
assert(merge_operator);
|
||||
*(s->status) = Status::OK();
|
||||
if (!merge_operator->FullMerge(s->key->user_key(), nullptr,
|
||||
merge_context->GetOperands(), s->value,
|
||||
s->logger)) {
|
||||
bool merge_success = false;
|
||||
{
|
||||
StopWatchNano timer(s->env_, s->statistics != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
merge_success = merge_operator->FullMerge(
|
||||
s->key->user_key(), nullptr, merge_context->GetOperands(),
|
||||
s->value, s->logger);
|
||||
RecordTick(s->statistics, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
}
|
||||
if (!merge_success) {
|
||||
RecordTick(s->statistics, NUMBER_MERGE_FAILURES);
|
||||
*(s->status) =
|
||||
Status::Corruption("Error: Could not perform merge.");
|
||||
@@ -465,6 +490,7 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
saver.logger = moptions_.info_log;
|
||||
saver.inplace_update_support = moptions_.inplace_update_support;
|
||||
saver.statistics = moptions_.statistics;
|
||||
saver.env_ = env_;
|
||||
table_->Get(key, &saver, SaveValue);
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -17,6 +17,7 @@
|
||||
#include "db/skiplist.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/immutable_options.h"
|
||||
#include "db/memtable_allocator.h"
|
||||
@@ -59,7 +60,7 @@ class MemTable {
|
||||
const InternalKeyComparator comparator;
|
||||
explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { }
|
||||
virtual int operator()(const char* prefix_len_key1,
|
||||
const char* prefix_len_key2) const;
|
||||
const char* prefix_len_key2) const override;
|
||||
virtual int operator()(const char* prefix_len_key,
|
||||
const Slice& key) const override;
|
||||
};
|
||||
@@ -164,7 +165,9 @@ class MemTable {
|
||||
size_t CountSuccessiveMergeEntries(const LookupKey& key);
|
||||
|
||||
// Get total number of entries in the mem table.
|
||||
uint64_t GetNumEntries() const { return num_entries_; }
|
||||
uint64_t num_entries() const { return num_entries_; }
|
||||
|
||||
uint64_t num_deletes() const { return num_deletes_; }
|
||||
|
||||
// Returns the edits area that is needed for flushing the memtable
|
||||
VersionEdit* GetEdits() { return &edit_; }
|
||||
@@ -196,7 +199,10 @@ class MemTable {
|
||||
}
|
||||
|
||||
// return true if the current MemTableRep supports snapshots.
|
||||
bool IsSnapshotSupported() const { return table_->IsSnapshotSupported(); }
|
||||
// inplace update prevents snapshots,
|
||||
bool IsSnapshotSupported() const {
|
||||
return table_->IsSnapshotSupported() && !moptions_.inplace_update_support;
|
||||
}
|
||||
|
||||
// Get the lock associated for the key
|
||||
port::RWMutex* GetLock(const Slice& key);
|
||||
@@ -224,6 +230,7 @@ class MemTable {
|
||||
unique_ptr<MemTableRep> table_;
|
||||
|
||||
uint64_t num_entries_;
|
||||
uint64_t num_deletes_;
|
||||
|
||||
// These are used to manage memtable flushes to storage
|
||||
bool flush_in_progress_; // started the flush
|
||||
@@ -255,6 +262,7 @@ class MemTable {
|
||||
|
||||
// a flag indicating if flush has been scheduled
|
||||
bool flush_scheduled_;
|
||||
Env* env_;
|
||||
};
|
||||
|
||||
extern const char* EncodeKey(std::string* scratch, const Slice& target);
|
||||
|
||||
+17
-2
@@ -19,6 +19,7 @@
|
||||
#include "table/merger.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/log_buffer.h"
|
||||
#include "util/thread_status_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -95,7 +96,15 @@ void MemTableListVersion::AddIterators(
|
||||
uint64_t MemTableListVersion::GetTotalNumEntries() const {
|
||||
uint64_t total_num = 0;
|
||||
for (auto& m : memlist_) {
|
||||
total_num += m->GetNumEntries();
|
||||
total_num += m->num_entries();
|
||||
}
|
||||
return total_num;
|
||||
}
|
||||
|
||||
uint64_t MemTableListVersion::GetTotalNumDeletes() const {
|
||||
uint64_t total_num = 0;
|
||||
for (auto& m : memlist_) {
|
||||
total_num += m->num_deletes();
|
||||
}
|
||||
return total_num;
|
||||
}
|
||||
@@ -127,6 +136,8 @@ bool MemTableList::IsFlushPending() const {
|
||||
|
||||
// Returns the memtables that need to be flushed.
|
||||
void MemTableList::PickMemtablesToFlush(autovector<MemTable*>* ret) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_PICK_MEMTABLES_TO_FLUSH);
|
||||
const auto& memlist = current_->memlist_;
|
||||
for (auto it = memlist.rbegin(); it != memlist.rend(); ++it) {
|
||||
MemTable* m = *it;
|
||||
@@ -145,6 +156,8 @@ void MemTableList::PickMemtablesToFlush(autovector<MemTable*>* ret) {
|
||||
|
||||
void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
uint64_t file_number) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_ROLLBACK);
|
||||
assert(!mems.empty());
|
||||
|
||||
// If the flush was not successful, then just reset state.
|
||||
@@ -164,9 +177,11 @@ void MemTableList::RollbackMemtableFlush(const autovector<MemTable*>& mems,
|
||||
// Record a successful flush in the manifest file
|
||||
Status MemTableList::InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& mems, VersionSet* vset, port::Mutex* mu,
|
||||
const autovector<MemTable*>& mems, VersionSet* vset, InstrumentedMutex* mu,
|
||||
uint64_t file_number, autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory, LogBuffer* log_buffer) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
|
||||
// flush was sucessful
|
||||
|
||||
+5
-2
@@ -22,13 +22,14 @@
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/log_buffer.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class ColumnFamilyData;
|
||||
class InternalKeyComparator;
|
||||
class Mutex;
|
||||
class InstrumentedMutex;
|
||||
class MergeIteratorBuilder;
|
||||
|
||||
// keeps a list of immutable memtables in a vector. the list is immutable
|
||||
@@ -56,6 +57,8 @@ class MemTableListVersion {
|
||||
|
||||
uint64_t GetTotalNumEntries() const;
|
||||
|
||||
uint64_t GetTotalNumDeletes() const;
|
||||
|
||||
private:
|
||||
// REQUIRE: m is mutable memtable
|
||||
void Add(MemTable* m);
|
||||
@@ -113,7 +116,7 @@ class MemTableList {
|
||||
// Commit a successful flush in the manifest file
|
||||
Status InstallMemtableFlushResults(
|
||||
ColumnFamilyData* cfd, const MutableCFOptions& mutable_cf_options,
|
||||
const autovector<MemTable*>& m, VersionSet* vset, port::Mutex* mu,
|
||||
const autovector<MemTable*>& m, VersionSet* vset, InstrumentedMutex* mu,
|
||||
uint64_t file_number, autovector<MemTable*>* to_delete,
|
||||
Directory* db_directory, LogBuffer* log_buffer);
|
||||
|
||||
|
||||
@@ -0,0 +1,694 @@
|
||||
// Copyright (c) 2014, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same 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.
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
#ifndef GFLAGS
|
||||
#include <cstdio>
|
||||
int main() {
|
||||
fprintf(stderr, "Please install gflags to run rocksdb tools\n");
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
using GFLAGS::ParseCommandLineFlags;
|
||||
using GFLAGS::RegisterFlagValidator;
|
||||
using GFLAGS::SetUsageMessage;
|
||||
|
||||
DEFINE_string(benchmarks, "fillrandom",
|
||||
"Comma-separated list of benchmarks to run. Options:\n"
|
||||
"\tfillrandom -- write N random values\n"
|
||||
"\tfillseq -- write N values in sequential order\n"
|
||||
"\treadrandom -- read N values in random order\n"
|
||||
"\treadseq -- scan the DB\n"
|
||||
"\treadwrite -- 1 thread writes while N - 1 threads "
|
||||
"do random\n"
|
||||
"\t reads\n"
|
||||
"\tseqreadwrite -- 1 thread writes while N - 1 threads "
|
||||
"do scans\n");
|
||||
|
||||
DEFINE_string(memtablerep, "skiplist",
|
||||
"Which implementation of memtablerep to use. See "
|
||||
"include/memtablerep.h for\n"
|
||||
" more details. Options:\n"
|
||||
"\tskiplist -- backed by a skiplist\n"
|
||||
"\tvector -- backed by an std::vector\n"
|
||||
"\thashskiplist -- backed by a hash skip list\n"
|
||||
"\thashlinklist -- backed by a hash linked list\n"
|
||||
"\tcuckoo -- backed by a cuckoo hash table");
|
||||
|
||||
DEFINE_int64(bucket_count, 1000000,
|
||||
"bucket_count parameter to pass into NewHashSkiplistRepFactory or "
|
||||
"NewHashLinkListRepFactory");
|
||||
|
||||
DEFINE_int32(
|
||||
hashskiplist_height, 4,
|
||||
"skiplist_height parameter to pass into NewHashSkiplistRepFactory");
|
||||
|
||||
DEFINE_int32(
|
||||
hashskiplist_branching_factor, 4,
|
||||
"branching_factor parameter to pass into NewHashSkiplistRepFactory");
|
||||
|
||||
DEFINE_int32(
|
||||
huge_page_tlb_size, 0,
|
||||
"huge_page_tlb_size parameter to pass into NewHashLinkListRepFactory");
|
||||
|
||||
DEFINE_int32(bucket_entries_logging_threshold, 4096,
|
||||
"bucket_entries_logging_threshold parameter to pass into "
|
||||
"NewHashLinkListRepFactory");
|
||||
|
||||
DEFINE_bool(if_log_bucket_dist_when_flash, true,
|
||||
"if_log_bucket_dist_when_flash parameter to pass into "
|
||||
"NewHashLinkListRepFactory");
|
||||
|
||||
DEFINE_int32(
|
||||
threshold_use_skiplist, 256,
|
||||
"threshold_use_skiplist parameter to pass into NewHashLinkListRepFactory");
|
||||
|
||||
DEFINE_int64(
|
||||
write_buffer_size, 256,
|
||||
"write_buffer_size parameter to pass into NewHashCuckooRepFactory");
|
||||
|
||||
DEFINE_int64(
|
||||
average_data_size, 64,
|
||||
"average_data_size parameter to pass into NewHashCuckooRepFactory");
|
||||
|
||||
DEFINE_int64(
|
||||
hash_function_count, 4,
|
||||
"hash_function_count parameter to pass into NewHashCuckooRepFactory");
|
||||
|
||||
DEFINE_int32(
|
||||
num_threads, 1,
|
||||
"Number of concurrent threads to run. If the benchmark includes writes,\n"
|
||||
"then at most one thread will be a writer");
|
||||
|
||||
DEFINE_int32(num_operations, 1000000,
|
||||
"Number of operations to do for write and random read benchmarks");
|
||||
|
||||
DEFINE_int32(num_scans, 10,
|
||||
"Number of times for each thread to scan the memtablerep for "
|
||||
"sequential read "
|
||||
"benchmarks");
|
||||
|
||||
DEFINE_int32(item_size, 100, "Number of bytes each item should be");
|
||||
|
||||
DEFINE_int32(prefix_length, 8,
|
||||
"Prefix length to pass into NewFixedPrefixTransform");
|
||||
|
||||
/* VectorRep settings */
|
||||
DEFINE_int64(vectorrep_count, 0,
|
||||
"Number of entries to reserve on VectorRep initialization");
|
||||
|
||||
DEFINE_int64(seed, 0,
|
||||
"Seed base for random number generators. "
|
||||
"When 0 it is deterministic.");
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
struct CallbackVerifyArgs {
|
||||
bool found;
|
||||
LookupKey* key;
|
||||
MemTableRep* table;
|
||||
InternalKeyComparator* comparator;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Helper for quickly generating random data.
|
||||
class RandomGenerator {
|
||||
private:
|
||||
std::string data_;
|
||||
unsigned int pos_;
|
||||
|
||||
public:
|
||||
RandomGenerator() {
|
||||
Random rnd(301);
|
||||
auto size = (unsigned)std::max(1048576, FLAGS_item_size);
|
||||
test::RandomString(&rnd, size, &data_);
|
||||
pos_ = 0;
|
||||
}
|
||||
|
||||
Slice Generate(unsigned int len) {
|
||||
assert(len <= data_.size());
|
||||
if (pos_ + len > data_.size()) {
|
||||
pos_ = 0;
|
||||
}
|
||||
pos_ += len;
|
||||
return Slice(data_.data() + pos_ - len, len);
|
||||
}
|
||||
};
|
||||
|
||||
enum WriteMode { SEQUENTIAL, RANDOM, UNIQUE_RANDOM };
|
||||
|
||||
class KeyGenerator {
|
||||
public:
|
||||
KeyGenerator(Random64* rand, WriteMode mode, uint64_t num)
|
||||
: rand_(rand), mode_(mode), num_(num), next_(0) {
|
||||
if (mode_ == UNIQUE_RANDOM) {
|
||||
// NOTE: if memory consumption of this approach becomes a concern,
|
||||
// we can either break it into pieces and only random shuffle a section
|
||||
// each time. Alternatively, use a bit map implementation
|
||||
// (https://reviews.facebook.net/differential/diff/54627/)
|
||||
values_.resize(num_);
|
||||
for (uint64_t i = 0; i < num_; ++i) {
|
||||
values_[i] = i;
|
||||
}
|
||||
std::shuffle(
|
||||
values_.begin(), values_.end(),
|
||||
std::default_random_engine(static_cast<unsigned int>(FLAGS_seed)));
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Next() {
|
||||
switch (mode_) {
|
||||
case SEQUENTIAL:
|
||||
return next_++;
|
||||
case RANDOM:
|
||||
return rand_->Next() % num_;
|
||||
case UNIQUE_RANDOM:
|
||||
return values_[next_++];
|
||||
}
|
||||
assert(false);
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
private:
|
||||
Random64* rand_;
|
||||
WriteMode mode_;
|
||||
const uint64_t num_;
|
||||
uint64_t next_;
|
||||
std::vector<uint64_t> values_;
|
||||
};
|
||||
|
||||
class BenchmarkThread {
|
||||
public:
|
||||
explicit BenchmarkThread(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* bytes_written, uint64_t* bytes_read,
|
||||
uint64_t* sequence, uint64_t num_ops,
|
||||
uint64_t* read_hits)
|
||||
: table_(table),
|
||||
key_gen_(key_gen),
|
||||
bytes_written_(bytes_written),
|
||||
bytes_read_(bytes_read),
|
||||
sequence_(sequence),
|
||||
num_ops_(num_ops),
|
||||
read_hits_(read_hits) {}
|
||||
|
||||
virtual void operator()() = 0;
|
||||
virtual ~BenchmarkThread() {}
|
||||
|
||||
protected:
|
||||
MemTableRep* table_;
|
||||
KeyGenerator* key_gen_;
|
||||
uint64_t* bytes_written_;
|
||||
uint64_t* bytes_read_;
|
||||
uint64_t* sequence_;
|
||||
uint64_t num_ops_;
|
||||
uint64_t* read_hits_;
|
||||
RandomGenerator generator_;
|
||||
};
|
||||
|
||||
class FillBenchmarkThread : public BenchmarkThread {
|
||||
public:
|
||||
FillBenchmarkThread(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* bytes_written, uint64_t* bytes_read,
|
||||
uint64_t* sequence, uint64_t num_ops, uint64_t* read_hits)
|
||||
: BenchmarkThread(table, key_gen, bytes_written, bytes_read, sequence,
|
||||
num_ops, read_hits) {}
|
||||
|
||||
void FillOne() {
|
||||
char* buf = nullptr;
|
||||
auto internal_key_size = 16;
|
||||
auto encoded_len =
|
||||
FLAGS_item_size + VarintLength(internal_key_size) + internal_key_size;
|
||||
KeyHandle handle = table_->Allocate(encoded_len, &buf);
|
||||
assert(buf != nullptr);
|
||||
char* p = EncodeVarint32(buf, internal_key_size);
|
||||
auto key = key_gen_->Next();
|
||||
EncodeFixed64(p, key);
|
||||
p += 8;
|
||||
EncodeFixed64(p, ++(*sequence_));
|
||||
p += 8;
|
||||
Slice bytes = generator_.Generate(FLAGS_item_size);
|
||||
memcpy(p, bytes.data(), FLAGS_item_size);
|
||||
p += FLAGS_item_size;
|
||||
assert(p == buf + encoded_len);
|
||||
table_->Insert(handle);
|
||||
*bytes_written_ += encoded_len;
|
||||
}
|
||||
|
||||
void operator()() override {
|
||||
for (unsigned int i = 0; i < num_ops_; ++i) {
|
||||
FillOne();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ConcurrentFillBenchmarkThread : public FillBenchmarkThread {
|
||||
public:
|
||||
ConcurrentFillBenchmarkThread(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* bytes_written, uint64_t* bytes_read,
|
||||
uint64_t* sequence, uint64_t num_ops,
|
||||
uint64_t* read_hits,
|
||||
std::atomic_int* threads_done)
|
||||
: FillBenchmarkThread(table, key_gen, bytes_written, bytes_read, sequence,
|
||||
num_ops, read_hits) {
|
||||
threads_done_ = threads_done;
|
||||
}
|
||||
|
||||
void operator()() override {
|
||||
// # of read threads will be total threads - write threads (always 1). Loop
|
||||
// while all reads complete.
|
||||
while ((*threads_done_).load() < (FLAGS_num_threads - 1)) {
|
||||
FillOne();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic_int* threads_done_;
|
||||
};
|
||||
|
||||
class ReadBenchmarkThread : public BenchmarkThread {
|
||||
public:
|
||||
ReadBenchmarkThread(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* bytes_written, uint64_t* bytes_read,
|
||||
uint64_t* sequence, uint64_t num_ops, uint64_t* read_hits)
|
||||
: BenchmarkThread(table, key_gen, bytes_written, bytes_read, sequence,
|
||||
num_ops, read_hits) {}
|
||||
|
||||
static bool callback(void* arg, const char* entry) {
|
||||
CallbackVerifyArgs* callback_args = static_cast<CallbackVerifyArgs*>(arg);
|
||||
assert(callback_args != nullptr);
|
||||
uint32_t key_length;
|
||||
const char* key_ptr = GetVarint32Ptr(entry, entry + 5, &key_length);
|
||||
if ((callback_args->comparator)->user_comparator()->Compare(
|
||||
Slice(key_ptr, key_length - 8), callback_args->key->user_key()) ==
|
||||
0) {
|
||||
callback_args->found = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReadOne() {
|
||||
std::string user_key;
|
||||
auto key = key_gen_->Next();
|
||||
PutFixed64(&user_key, key);
|
||||
LookupKey lookup_key(user_key, *sequence_);
|
||||
InternalKeyComparator internal_key_comp(BytewiseComparator());
|
||||
CallbackVerifyArgs verify_args;
|
||||
verify_args.found = false;
|
||||
verify_args.key = &lookup_key;
|
||||
verify_args.table = table_;
|
||||
verify_args.comparator = &internal_key_comp;
|
||||
table_->Get(lookup_key, &verify_args, callback);
|
||||
if (verify_args.found) {
|
||||
*bytes_read_ += VarintLength(16) + 16 + FLAGS_item_size;
|
||||
++*read_hits_;
|
||||
}
|
||||
}
|
||||
void operator()() override {
|
||||
for (unsigned int i = 0; i < num_ops_; ++i) {
|
||||
ReadOne();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SeqReadBenchmarkThread : public BenchmarkThread {
|
||||
public:
|
||||
SeqReadBenchmarkThread(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* bytes_written, uint64_t* bytes_read,
|
||||
uint64_t* sequence, uint64_t num_ops,
|
||||
uint64_t* read_hits)
|
||||
: BenchmarkThread(table, key_gen, bytes_written, bytes_read, sequence,
|
||||
num_ops, read_hits) {}
|
||||
|
||||
void ReadOneSeq() {
|
||||
std::unique_ptr<MemTableRep::Iterator> iter(table_->GetIterator());
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
// pretend to read the value
|
||||
*bytes_read_ += VarintLength(16) + 16 + FLAGS_item_size;
|
||||
}
|
||||
++*read_hits_;
|
||||
}
|
||||
|
||||
void operator()() override {
|
||||
for (unsigned int i = 0; i < num_ops_; ++i) {
|
||||
{ ReadOneSeq(); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ConcurrentReadBenchmarkThread : public ReadBenchmarkThread {
|
||||
public:
|
||||
ConcurrentReadBenchmarkThread(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* bytes_written, uint64_t* bytes_read,
|
||||
uint64_t* sequence, uint64_t num_ops,
|
||||
uint64_t* read_hits,
|
||||
std::atomic_int* threads_done)
|
||||
: ReadBenchmarkThread(table, key_gen, bytes_written, bytes_read, sequence,
|
||||
num_ops, read_hits) {
|
||||
threads_done_ = threads_done;
|
||||
}
|
||||
|
||||
void operator()() override {
|
||||
for (unsigned int i = 0; i < num_ops_; ++i) {
|
||||
ReadOne();
|
||||
}
|
||||
++*threads_done_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic_int* threads_done_;
|
||||
};
|
||||
|
||||
class SeqConcurrentReadBenchmarkThread : public SeqReadBenchmarkThread {
|
||||
public:
|
||||
SeqConcurrentReadBenchmarkThread(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, uint64_t* sequence,
|
||||
uint64_t num_ops, uint64_t* read_hits,
|
||||
std::atomic_int* threads_done)
|
||||
: SeqReadBenchmarkThread(table, key_gen, bytes_written, bytes_read,
|
||||
sequence, num_ops, read_hits) {
|
||||
threads_done_ = threads_done;
|
||||
}
|
||||
|
||||
void operator()() override {
|
||||
for (unsigned int i = 0; i < num_ops_; ++i) {
|
||||
ReadOneSeq();
|
||||
}
|
||||
++*threads_done_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic_int* threads_done_;
|
||||
};
|
||||
|
||||
class Benchmark {
|
||||
public:
|
||||
explicit Benchmark(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* sequence, uint32_t num_threads)
|
||||
: table_(table),
|
||||
key_gen_(key_gen),
|
||||
sequence_(sequence),
|
||||
num_threads_(num_threads) {}
|
||||
|
||||
virtual ~Benchmark() {}
|
||||
virtual void Run() {
|
||||
std::cout << "Number of threads: " << num_threads_ << std::endl;
|
||||
std::vector<std::thread> threads;
|
||||
uint64_t bytes_written = 0;
|
||||
uint64_t bytes_read = 0;
|
||||
uint64_t read_hits = 0;
|
||||
StopWatchNano timer(Env::Default(), true);
|
||||
RunThreads(&threads, &bytes_written, &bytes_read, true, &read_hits);
|
||||
auto elapsed_time = static_cast<double>(timer.ElapsedNanos() / 1000);
|
||||
std::cout << "Elapsed time: " << static_cast<int>(elapsed_time) << " us"
|
||||
<< std::endl;
|
||||
|
||||
if (bytes_written > 0) {
|
||||
auto MiB_written = static_cast<double>(bytes_written) / (1 << 20);
|
||||
auto write_throughput = MiB_written / (elapsed_time / 1000000);
|
||||
std::cout << "Total bytes written: " << MiB_written << " MiB"
|
||||
<< std::endl;
|
||||
std::cout << "Write throughput: " << write_throughput << " MiB/s"
|
||||
<< std::endl;
|
||||
auto us_per_op = elapsed_time / num_write_ops_per_thread_;
|
||||
std::cout << "write us/op: " << us_per_op << std::endl;
|
||||
}
|
||||
if (bytes_read > 0) {
|
||||
auto MiB_read = static_cast<double>(bytes_read) / (1 << 20);
|
||||
auto read_throughput = MiB_read / (elapsed_time / 1000000);
|
||||
std::cout << "Total bytes read: " << MiB_read << " MiB" << std::endl;
|
||||
std::cout << "Read throughput: " << read_throughput << " MiB/s"
|
||||
<< std::endl;
|
||||
auto us_per_op = elapsed_time / num_read_ops_per_thread_;
|
||||
std::cout << "read us/op: " << us_per_op << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void RunThreads(std::vector<std::thread>* threads,
|
||||
uint64_t* bytes_written, uint64_t* bytes_read,
|
||||
bool write, uint64_t* read_hits) = 0;
|
||||
|
||||
protected:
|
||||
MemTableRep* table_;
|
||||
KeyGenerator* key_gen_;
|
||||
uint64_t* sequence_;
|
||||
uint64_t num_write_ops_per_thread_;
|
||||
uint64_t num_read_ops_per_thread_;
|
||||
const uint32_t num_threads_;
|
||||
};
|
||||
|
||||
class FillBenchmark : public Benchmark {
|
||||
public:
|
||||
explicit FillBenchmark(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* sequence)
|
||||
: Benchmark(table, key_gen, sequence, 1) {
|
||||
num_write_ops_per_thread_ = FLAGS_num_operations;
|
||||
}
|
||||
|
||||
void RunThreads(std::vector<std::thread>* threads, uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, bool write,
|
||||
uint64_t* read_hits) override {
|
||||
FillBenchmarkThread(table_, key_gen_, bytes_written, bytes_read, sequence_,
|
||||
num_write_ops_per_thread_, read_hits)();
|
||||
}
|
||||
};
|
||||
|
||||
class ReadBenchmark : public Benchmark {
|
||||
public:
|
||||
explicit ReadBenchmark(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* sequence)
|
||||
: Benchmark(table, key_gen, sequence, FLAGS_num_threads) {
|
||||
num_read_ops_per_thread_ = FLAGS_num_operations / FLAGS_num_threads;
|
||||
}
|
||||
|
||||
void RunThreads(std::vector<std::thread>* threads, uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, bool write,
|
||||
uint64_t* read_hits) override {
|
||||
for (int i = 0; i < FLAGS_num_threads; ++i) {
|
||||
threads->emplace_back(
|
||||
ReadBenchmarkThread(table_, key_gen_, bytes_written, bytes_read,
|
||||
sequence_, num_read_ops_per_thread_, read_hits));
|
||||
}
|
||||
for (auto& thread : *threads) {
|
||||
thread.join();
|
||||
}
|
||||
std::cout << "read hit%: "
|
||||
<< (static_cast<double>(*read_hits) / FLAGS_num_operations) * 100
|
||||
<< std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
class SeqReadBenchmark : public Benchmark {
|
||||
public:
|
||||
explicit SeqReadBenchmark(MemTableRep* table, uint64_t* sequence)
|
||||
: Benchmark(table, nullptr, sequence, FLAGS_num_threads) {
|
||||
num_read_ops_per_thread_ = FLAGS_num_scans;
|
||||
}
|
||||
|
||||
void RunThreads(std::vector<std::thread>* threads, uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, bool write,
|
||||
uint64_t* read_hits) override {
|
||||
for (int i = 0; i < FLAGS_num_threads; ++i) {
|
||||
threads->emplace_back(SeqReadBenchmarkThread(
|
||||
table_, key_gen_, bytes_written, bytes_read, sequence_,
|
||||
num_read_ops_per_thread_, read_hits));
|
||||
}
|
||||
for (auto& thread : *threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class ReadThreadType>
|
||||
class ReadWriteBenchmark : public Benchmark {
|
||||
public:
|
||||
explicit ReadWriteBenchmark(MemTableRep* table, KeyGenerator* key_gen,
|
||||
uint64_t* sequence)
|
||||
: Benchmark(table, key_gen, sequence, FLAGS_num_threads) {
|
||||
num_read_ops_per_thread_ =
|
||||
FLAGS_num_threads <= 1
|
||||
? 0
|
||||
: (FLAGS_num_operations / (FLAGS_num_threads - 1));
|
||||
num_write_ops_per_thread_ = FLAGS_num_operations;
|
||||
}
|
||||
|
||||
void RunThreads(std::vector<std::thread>* threads, uint64_t* bytes_written,
|
||||
uint64_t* bytes_read, bool write,
|
||||
uint64_t* read_hits) override {
|
||||
std::atomic_int threads_done;
|
||||
threads_done.store(0);
|
||||
threads->emplace_back(ConcurrentFillBenchmarkThread(
|
||||
table_, key_gen_, bytes_written, bytes_read, sequence_,
|
||||
num_write_ops_per_thread_, read_hits, &threads_done));
|
||||
for (int i = 1; i < FLAGS_num_threads; ++i) {
|
||||
threads->emplace_back(
|
||||
ReadThreadType(table_, key_gen_, bytes_written, bytes_read, sequence_,
|
||||
num_read_ops_per_thread_, read_hits, &threads_done));
|
||||
}
|
||||
for (auto& thread : *threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
void PrintWarnings() {
|
||||
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
|
||||
fprintf(stdout,
|
||||
"WARNING: Optimization is disabled: benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
#ifndef NDEBUG
|
||||
fprintf(stdout,
|
||||
"WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
SetUsageMessage(std::string("\nUSAGE:\n") + std::string(argv[0]) +
|
||||
" [OPTIONS]...");
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
PrintWarnings();
|
||||
|
||||
rocksdb::Options options;
|
||||
|
||||
std::unique_ptr<rocksdb::MemTableRepFactory> factory;
|
||||
if (FLAGS_memtablerep == "skiplist") {
|
||||
factory.reset(new rocksdb::SkipListFactory);
|
||||
} else if (FLAGS_memtablerep == "vector") {
|
||||
factory.reset(new rocksdb::VectorRepFactory);
|
||||
} else if (FLAGS_memtablerep == "hashskiplist") {
|
||||
factory.reset(rocksdb::NewHashSkipListRepFactory(
|
||||
FLAGS_bucket_count, FLAGS_hashskiplist_height,
|
||||
FLAGS_hashskiplist_branching_factor));
|
||||
options.prefix_extractor.reset(
|
||||
rocksdb::NewFixedPrefixTransform(FLAGS_prefix_length));
|
||||
} else if (FLAGS_memtablerep == "hashlinklist") {
|
||||
factory.reset(rocksdb::NewHashLinkListRepFactory(
|
||||
FLAGS_bucket_count, FLAGS_huge_page_tlb_size,
|
||||
FLAGS_bucket_entries_logging_threshold,
|
||||
FLAGS_if_log_bucket_dist_when_flash, FLAGS_threshold_use_skiplist));
|
||||
options.prefix_extractor.reset(
|
||||
rocksdb::NewFixedPrefixTransform(FLAGS_prefix_length));
|
||||
} else if (FLAGS_memtablerep == "cuckoo") {
|
||||
factory.reset(rocksdb::NewHashCuckooRepFactory(
|
||||
FLAGS_write_buffer_size, FLAGS_average_data_size,
|
||||
static_cast<uint32_t>(FLAGS_hash_function_count)));
|
||||
options.prefix_extractor.reset(
|
||||
rocksdb::NewFixedPrefixTransform(FLAGS_prefix_length));
|
||||
} else {
|
||||
fprintf(stdout, "Unknown memtablerep: %s\n", FLAGS_memtablerep.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rocksdb::InternalKeyComparator internal_key_comp(
|
||||
rocksdb::BytewiseComparator());
|
||||
rocksdb::MemTable::KeyComparator key_comp(internal_key_comp);
|
||||
rocksdb::Arena arena;
|
||||
rocksdb::WriteBuffer wb(FLAGS_write_buffer_size);
|
||||
rocksdb::MemTableAllocator memtable_allocator(&arena, &wb);
|
||||
uint64_t sequence;
|
||||
auto createMemtableRep = [&] {
|
||||
sequence = 0;
|
||||
return factory->CreateMemTableRep(key_comp, &memtable_allocator,
|
||||
options.prefix_extractor.get(),
|
||||
options.info_log.get());
|
||||
};
|
||||
std::unique_ptr<rocksdb::MemTableRep> memtablerep;
|
||||
rocksdb::Random64 rng(FLAGS_seed);
|
||||
const char* benchmarks = FLAGS_benchmarks.c_str();
|
||||
while (benchmarks != nullptr) {
|
||||
std::unique_ptr<rocksdb::KeyGenerator> key_gen;
|
||||
const char* sep = strchr(benchmarks, ',');
|
||||
rocksdb::Slice name;
|
||||
if (sep == nullptr) {
|
||||
name = benchmarks;
|
||||
benchmarks = nullptr;
|
||||
} else {
|
||||
name = rocksdb::Slice(benchmarks, sep - benchmarks);
|
||||
benchmarks = sep + 1;
|
||||
}
|
||||
std::unique_ptr<rocksdb::Benchmark> benchmark;
|
||||
if (name == rocksdb::Slice("fillseq")) {
|
||||
memtablerep.reset(createMemtableRep());
|
||||
key_gen.reset(new rocksdb::KeyGenerator(&rng, rocksdb::SEQUENTIAL,
|
||||
FLAGS_num_operations));
|
||||
benchmark.reset(new rocksdb::FillBenchmark(memtablerep.get(),
|
||||
key_gen.get(), &sequence));
|
||||
} else if (name == rocksdb::Slice("fillrandom")) {
|
||||
memtablerep.reset(createMemtableRep());
|
||||
key_gen.reset(new rocksdb::KeyGenerator(&rng, rocksdb::UNIQUE_RANDOM,
|
||||
FLAGS_num_operations));
|
||||
benchmark.reset(new rocksdb::FillBenchmark(memtablerep.get(),
|
||||
key_gen.get(), &sequence));
|
||||
} else if (name == rocksdb::Slice("readrandom")) {
|
||||
key_gen.reset(new rocksdb::KeyGenerator(&rng, rocksdb::RANDOM,
|
||||
FLAGS_num_operations));
|
||||
benchmark.reset(new rocksdb::ReadBenchmark(memtablerep.get(),
|
||||
key_gen.get(), &sequence));
|
||||
} else if (name == rocksdb::Slice("readseq")) {
|
||||
key_gen.reset(new rocksdb::KeyGenerator(&rng, rocksdb::SEQUENTIAL,
|
||||
FLAGS_num_operations));
|
||||
benchmark.reset(
|
||||
new rocksdb::SeqReadBenchmark(memtablerep.get(), &sequence));
|
||||
} else if (name == rocksdb::Slice("readwrite")) {
|
||||
memtablerep.reset(createMemtableRep());
|
||||
key_gen.reset(new rocksdb::KeyGenerator(&rng, rocksdb::RANDOM,
|
||||
FLAGS_num_operations));
|
||||
benchmark.reset(new rocksdb::ReadWriteBenchmark<
|
||||
rocksdb::ConcurrentReadBenchmarkThread>(memtablerep.get(),
|
||||
key_gen.get(), &sequence));
|
||||
} else if (name == rocksdb::Slice("seqreadwrite")) {
|
||||
memtablerep.reset(createMemtableRep());
|
||||
key_gen.reset(new rocksdb::KeyGenerator(&rng, rocksdb::RANDOM,
|
||||
FLAGS_num_operations));
|
||||
benchmark.reset(new rocksdb::ReadWriteBenchmark<
|
||||
rocksdb::SeqConcurrentReadBenchmarkThread>(memtablerep.get(),
|
||||
key_gen.get(), &sequence));
|
||||
} else {
|
||||
std::cout << "WARNING: skipping unknown benchmark '" << name.ToString()
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
std::cout << "Running " << name.ToString() << std::endl;
|
||||
benchmark->Run();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // GFLAGS
|
||||
+45
-20
@@ -11,6 +11,8 @@
|
||||
#include "util/statistics.h"
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include "util/perf_context_imp.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -21,7 +23,8 @@ namespace rocksdb {
|
||||
// operands_ stores the list of merge operands encountered while merging.
|
||||
// keys_[i] corresponds to operands_[i] for each i.
|
||||
void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
bool at_bottom, Statistics* stats, int* steps) {
|
||||
bool at_bottom, Statistics* stats, int* steps,
|
||||
Env* env_) {
|
||||
// Get a copy of the internal key, before it's invalidated by iter->Next()
|
||||
// Also maintain the list of merge operands seen.
|
||||
assert(HasOperator());
|
||||
@@ -78,10 +81,14 @@ void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
// => store result in operands_.back() (and update keys_.back())
|
||||
// => change the entry type to kTypeValue for keys_.back()
|
||||
// We are done! Return a success if the merge passes.
|
||||
success_ = user_merge_operator_->FullMerge(ikey.user_key, nullptr,
|
||||
operands_, &merge_result,
|
||||
logger_);
|
||||
|
||||
{
|
||||
StopWatchNano timer(env_, stats != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
success_ = user_merge_operator_->FullMerge(
|
||||
ikey.user_key, nullptr, operands_, &merge_result, logger_);
|
||||
RecordTick(stats, MERGE_OPERATION_TOTAL_TIME,
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0);
|
||||
}
|
||||
// We store the result in keys_.back() and operands_.back()
|
||||
// if nothing went wrong (i.e.: no operand corruption on disk)
|
||||
if (success_) {
|
||||
@@ -110,9 +117,14 @@ void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
// => change the entry type to kTypeValue for keys_.back()
|
||||
// We are done! Success!
|
||||
const Slice val = iter->value();
|
||||
success_ = user_merge_operator_->FullMerge(ikey.user_key, &val, operands_,
|
||||
&merge_result, logger_);
|
||||
|
||||
{
|
||||
StopWatchNano timer(env_, stats != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
success_ = user_merge_operator_->FullMerge(
|
||||
ikey.user_key, &val, operands_, &merge_result, logger_);
|
||||
RecordTick(stats, MERGE_OPERATION_TOTAL_TIME,
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0);
|
||||
}
|
||||
// We store the result in keys_.back() and operands_.back()
|
||||
// if nothing went wrong (i.e.: no operand corruption on disk)
|
||||
if (success_) {
|
||||
@@ -173,10 +185,14 @@ void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
assert(kTypeMerge == orig_ikey.type);
|
||||
assert(operands_.size() >= 1);
|
||||
assert(operands_.size() == keys_.size());
|
||||
success_ = user_merge_operator_->FullMerge(orig_ikey.user_key, nullptr,
|
||||
operands_, &merge_result,
|
||||
logger_);
|
||||
|
||||
{
|
||||
StopWatchNano timer(env_, stats != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
success_ = user_merge_operator_->FullMerge(
|
||||
orig_ikey.user_key, nullptr, operands_, &merge_result, logger_);
|
||||
RecordTick(stats, MERGE_OPERATION_TOTAL_TIME,
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0);
|
||||
}
|
||||
if (success_) {
|
||||
std::string& original_key = keys_.back(); // The original key encountered
|
||||
orig_ikey.type = kTypeValue;
|
||||
@@ -195,16 +211,25 @@ void MergeHelper::MergeUntil(Iterator* iter, SequenceNumber stop_before,
|
||||
// merge the stacked merge operands into a single operand.
|
||||
|
||||
if (operands_.size() >= 2 &&
|
||||
operands_.size() >= min_partial_merge_operands_ &&
|
||||
user_merge_operator_->PartialMergeMulti(
|
||||
operands_.size() >= min_partial_merge_operands_) {
|
||||
bool merge_success = false;
|
||||
{
|
||||
StopWatchNano timer(env_, stats != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
merge_success = user_merge_operator_->PartialMergeMulti(
|
||||
orig_ikey.user_key,
|
||||
std::deque<Slice>(operands_.begin(), operands_.end()),
|
||||
&merge_result, logger_)) {
|
||||
// Merging of operands (associative merge) was successful.
|
||||
// Replace operands with the merge result
|
||||
operands_.clear();
|
||||
operands_.push_front(std::move(merge_result));
|
||||
keys_.erase(keys_.begin(), keys_.end() - 1);
|
||||
&merge_result, logger_);
|
||||
RecordTick(stats, MERGE_OPERATION_TOTAL_TIME,
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0);
|
||||
}
|
||||
if (merge_success) {
|
||||
// Merging of operands (associative merge) was successful.
|
||||
// Replace operands with the merge result
|
||||
operands_.clear();
|
||||
operands_.push_front(std::move(merge_result));
|
||||
keys_.erase(keys_.begin(), keys_.end() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -48,7 +49,7 @@ class MergeHelper {
|
||||
// we could reach the start of the history of this user key.
|
||||
void MergeUntil(Iterator* iter, SequenceNumber stop_before = 0,
|
||||
bool at_bottom = false, Statistics* stats = nullptr,
|
||||
int* steps = nullptr);
|
||||
int* steps = nullptr, Env* env_ = nullptr);
|
||||
|
||||
// Query the merge result
|
||||
// These are valid until the next MergeUntil call
|
||||
|
||||
+2
-1
@@ -57,7 +57,8 @@ class CountMergeOperator : public AssociativeMergeOperator {
|
||||
|
||||
virtual bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value, Logger* logger) const {
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
++num_partial_merge_calls;
|
||||
return mergeOperator_->PartialMergeMulti(key, operand_list, new_value,
|
||||
logger);
|
||||
|
||||
+31
-11
@@ -14,6 +14,7 @@
|
||||
#include "util/histogram.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/thread_status_util.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
|
||||
@@ -54,13 +55,13 @@ std::shared_ptr<DB> OpenDb(bool read_only = false) {
|
||||
} else {
|
||||
s = DB::OpenForReadOnly(options, kDbName, &db);
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
EXPECT_OK(s);
|
||||
return std::shared_ptr<DB>(db);
|
||||
}
|
||||
|
||||
class PerfContextTest { };
|
||||
class PerfContextTest : public testing::Test {};
|
||||
|
||||
TEST(PerfContextTest, SeekIntoDeletion) {
|
||||
TEST_F(PerfContextTest, SeekIntoDeletion) {
|
||||
DestroyDB(kDbName, Options());
|
||||
auto db = OpenDb();
|
||||
WriteOptions write_options;
|
||||
@@ -143,7 +144,7 @@ TEST(PerfContextTest, SeekIntoDeletion) {
|
||||
std::cout << "Seek uesr key comparison: \n" << hist_seek.ToString();
|
||||
}
|
||||
|
||||
TEST(PerfContextTest, StopWatchNanoOverhead) {
|
||||
TEST_F(PerfContextTest, StopWatchNanoOverhead) {
|
||||
// profile the timer cost by itself!
|
||||
const int kTotalIterations = 1000000;
|
||||
std::vector<uint64_t> timings(kTotalIterations);
|
||||
@@ -161,7 +162,7 @@ TEST(PerfContextTest, StopWatchNanoOverhead) {
|
||||
std::cout << histogram.ToString();
|
||||
}
|
||||
|
||||
TEST(PerfContextTest, StopWatchOverhead) {
|
||||
TEST_F(PerfContextTest, StopWatchOverhead) {
|
||||
// profile the timer cost by itself!
|
||||
const int kTotalIterations = 1000000;
|
||||
uint64_t elapsed = 0;
|
||||
@@ -210,6 +211,8 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
HistogramImpl hist_write_wal_time;
|
||||
HistogramImpl hist_write_memtable_time;
|
||||
|
||||
uint64_t total_db_mutex_nanos = 0;
|
||||
|
||||
std::cout << "Inserting " << FLAGS_total_keys << " key/value pairs\n...\n";
|
||||
|
||||
std::vector<int> keys;
|
||||
@@ -225,13 +228,17 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
if (FLAGS_random_key) {
|
||||
std::random_shuffle(keys.begin(), keys.end());
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 1U);
|
||||
#endif
|
||||
int num_mutex_waited = 0;
|
||||
for (const int i : keys) {
|
||||
if (i == kFlushFlag) {
|
||||
FlushOptions fo;
|
||||
db->Flush(fo);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string key = "k" + ToString(i);
|
||||
std::string value = "v" + ToString(i);
|
||||
|
||||
@@ -239,11 +246,20 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
|
||||
perf_context.Reset();
|
||||
db->Put(write_options, key, value);
|
||||
if (++num_mutex_waited > 3) {
|
||||
#ifndef NDEBUG
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0U);
|
||||
#endif
|
||||
}
|
||||
hist_write_pre_post.Add(perf_context.write_pre_and_post_process_time);
|
||||
hist_write_wal_time.Add(perf_context.write_wal_time);
|
||||
hist_write_memtable_time.Add(perf_context.write_memtable_time);
|
||||
hist_put.Add(perf_context.user_key_comparison_count);
|
||||
total_db_mutex_nanos += perf_context.db_mutex_lock_nanos;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
ThreadStatusUtil::TEST_SetStateDelay(ThreadStatus::STATE_MUTEX_WAIT, 0U);
|
||||
#endif
|
||||
|
||||
for (const int i : keys) {
|
||||
std::string key = "k" + ToString(i);
|
||||
@@ -279,7 +295,8 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
<< " Writing WAL time: \n"
|
||||
<< hist_write_wal_time.ToString() << "\n"
|
||||
<< " Writing Mem Table time: \n"
|
||||
<< hist_write_memtable_time.ToString() << "\n";
|
||||
<< hist_write_memtable_time.ToString() << "\n"
|
||||
<< " Total DB mutex nanos: \n" << total_db_mutex_nanos << "\n";
|
||||
|
||||
std::cout << "Get(): Time to get snapshot: \n" << hist_get_snapshot.ToString()
|
||||
<< " Time to get value from memtables: \n"
|
||||
@@ -316,6 +333,9 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
ASSERT_GT(hist_mget_files.Average(), 0);
|
||||
ASSERT_GT(hist_mget_post_process.Average(), 0);
|
||||
ASSERT_GT(hist_mget_num_memtable_checked.Average(), 0);
|
||||
#ifndef NDEBUG
|
||||
ASSERT_GT(total_db_mutex_nanos, 2000U);
|
||||
#endif
|
||||
}
|
||||
|
||||
db.reset();
|
||||
@@ -405,7 +425,7 @@ void ProfileQueries(bool enabled_time = false) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PerfContextTest, KeyComparisonCount) {
|
||||
TEST_F(PerfContextTest, KeyComparisonCount) {
|
||||
SetPerfLevel(kEnableCount);
|
||||
ProfileQueries();
|
||||
|
||||
@@ -428,7 +448,7 @@ TEST(PerfContextTest, KeyComparisonCount) {
|
||||
// memtable. When there are two memtables, even the avg Seek Key comparison
|
||||
// starts to become linear to the input size.
|
||||
|
||||
TEST(PerfContextTest, SeekKeyComparison) {
|
||||
TEST_F(PerfContextTest, SeekKeyComparison) {
|
||||
DestroyDB(kDbName, Options());
|
||||
auto db = OpenDb();
|
||||
WriteOptions write_options;
|
||||
@@ -497,6 +517,7 @@ TEST(PerfContextTest, SeekKeyComparison) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
int n;
|
||||
@@ -524,6 +545,5 @@ int main(int argc, char** argv) {
|
||||
|
||||
std::cout << kDbName << "\n";
|
||||
|
||||
rocksdb::test::RunAllTests();
|
||||
return 0;
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+30
-29
@@ -29,6 +29,7 @@
|
||||
#include "util/hash.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
@@ -37,7 +38,7 @@ using std::unique_ptr;
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class PlainTableDBTest {
|
||||
class PlainTableDBTest : public testing::Test {
|
||||
protected:
|
||||
private:
|
||||
std::string dbname_;
|
||||
@@ -49,14 +50,14 @@ class PlainTableDBTest {
|
||||
public:
|
||||
PlainTableDBTest() : env_(Env::Default()) {
|
||||
dbname_ = test::TmpDir() + "/plain_table_db_test";
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
EXPECT_OK(DestroyDB(dbname_, Options()));
|
||||
db_ = nullptr;
|
||||
Reopen();
|
||||
}
|
||||
|
||||
~PlainTableDBTest() {
|
||||
delete db_;
|
||||
ASSERT_OK(DestroyDB(dbname_, Options()));
|
||||
EXPECT_OK(DestroyDB(dbname_, Options()));
|
||||
}
|
||||
|
||||
// Return the current option configuration.
|
||||
@@ -149,9 +150,8 @@ class PlainTableDBTest {
|
||||
|
||||
int NumTableFilesAtLevel(int level) {
|
||||
std::string property;
|
||||
ASSERT_TRUE(
|
||||
db_->GetProperty("rocksdb.num-files-at-level" + NumberToString(level),
|
||||
&property));
|
||||
EXPECT_TRUE(db_->GetProperty(
|
||||
"rocksdb.num-files-at-level" + NumberToString(level), &property));
|
||||
return atoi(property.c_str());
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ class PlainTableDBTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST(PlainTableDBTest, Empty) {
|
||||
TEST_F(PlainTableDBTest, Empty) {
|
||||
ASSERT_TRUE(dbfull() != nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("0000000000000foo"));
|
||||
}
|
||||
@@ -206,23 +206,23 @@ class TestPlainTableReader : public PlainTableReader {
|
||||
encoding_type, file_size, table_properties),
|
||||
expect_bloom_not_match_(expect_bloom_not_match) {
|
||||
Status s = MmapDataFile();
|
||||
ASSERT_TRUE(s.ok());
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
s = PopulateIndex(const_cast<TableProperties*>(table_properties),
|
||||
bloom_bits_per_key, hash_table_ratio, index_sparseness,
|
||||
2 * 1024 * 1024);
|
||||
ASSERT_TRUE(s.ok());
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
TableProperties* props = const_cast<TableProperties*>(table_properties);
|
||||
if (store_index_in_file) {
|
||||
auto bloom_version_ptr = props->user_collected_properties.find(
|
||||
PlainTablePropertyNames::kBloomVersion);
|
||||
ASSERT_TRUE(bloom_version_ptr != props->user_collected_properties.end());
|
||||
ASSERT_EQ(bloom_version_ptr->second, std::string("1"));
|
||||
EXPECT_TRUE(bloom_version_ptr != props->user_collected_properties.end());
|
||||
EXPECT_EQ(bloom_version_ptr->second, std::string("1"));
|
||||
if (ioptions.bloom_locality > 0) {
|
||||
auto num_blocks_ptr = props->user_collected_properties.find(
|
||||
PlainTablePropertyNames::kNumBloomBlocks);
|
||||
ASSERT_TRUE(num_blocks_ptr != props->user_collected_properties.end());
|
||||
EXPECT_TRUE(num_blocks_ptr != props->user_collected_properties.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,9 +233,9 @@ class TestPlainTableReader : public PlainTableReader {
|
||||
virtual bool MatchBloom(uint32_t hash) const override {
|
||||
bool ret = PlainTableReader::MatchBloom(hash);
|
||||
if (*expect_bloom_not_match_) {
|
||||
ASSERT_TRUE(!ret);
|
||||
EXPECT_TRUE(!ret);
|
||||
} else {
|
||||
ASSERT_TRUE(ret);
|
||||
EXPECT_TRUE(ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -262,20 +262,20 @@ class TestPlainTableFactory : public PlainTableFactory {
|
||||
TableProperties* props = nullptr;
|
||||
auto s = ReadTableProperties(file.get(), file_size, kPlainTableMagicNumber,
|
||||
ioptions.env, ioptions.info_log, &props);
|
||||
ASSERT_TRUE(s.ok());
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
if (store_index_in_file_) {
|
||||
BlockHandle bloom_block_handle;
|
||||
s = FindMetaBlock(file.get(), file_size, kPlainTableMagicNumber,
|
||||
ioptions.env, BloomBlockBuilder::kBloomBlock,
|
||||
&bloom_block_handle);
|
||||
ASSERT_TRUE(s.ok());
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
BlockHandle index_block_handle;
|
||||
s = FindMetaBlock(
|
||||
file.get(), file_size, kPlainTableMagicNumber, ioptions.env,
|
||||
PlainTableIndexBuilder::kPlainTableIndexBlock, &index_block_handle);
|
||||
ASSERT_TRUE(s.ok());
|
||||
EXPECT_TRUE(s.ok());
|
||||
}
|
||||
|
||||
auto& user_props = props->user_collected_properties;
|
||||
@@ -303,7 +303,7 @@ class TestPlainTableFactory : public PlainTableFactory {
|
||||
bool* expect_bloom_not_match_;
|
||||
};
|
||||
|
||||
TEST(PlainTableDBTest, Flush) {
|
||||
TEST_F(PlainTableDBTest, Flush) {
|
||||
for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024;
|
||||
huge_page_tlb_size += 2 * 1024 * 1024) {
|
||||
for (EncodingType encoding_type : {kPlain, kPrefix}) {
|
||||
@@ -390,7 +390,7 @@ TEST(PlainTableDBTest, Flush) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PlainTableDBTest, Flush2) {
|
||||
TEST_F(PlainTableDBTest, Flush2) {
|
||||
for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024;
|
||||
huge_page_tlb_size += 2 * 1024 * 1024) {
|
||||
for (EncodingType encoding_type : {kPlain, kPrefix}) {
|
||||
@@ -470,7 +470,7 @@ TEST(PlainTableDBTest, Flush2) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PlainTableDBTest, Iterator) {
|
||||
TEST_F(PlainTableDBTest, Iterator) {
|
||||
for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024;
|
||||
huge_page_tlb_size += 2 * 1024 * 1024) {
|
||||
for (EncodingType encoding_type : {kPlain, kPrefix}) {
|
||||
@@ -604,7 +604,7 @@ std::string MakeLongKey(size_t length, char c) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(PlainTableDBTest, IteratorLargeKeys) {
|
||||
TEST_F(PlainTableDBTest, IteratorLargeKeys) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
PlainTableOptions plain_table_options;
|
||||
@@ -654,7 +654,7 @@ std::string MakeLongKeyWithPrefix(size_t length, char c) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(PlainTableDBTest, IteratorLargeKeysWithPrefix) {
|
||||
TEST_F(PlainTableDBTest, IteratorLargeKeysWithPrefix) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
PlainTableOptions plain_table_options;
|
||||
@@ -696,7 +696,7 @@ TEST(PlainTableDBTest, IteratorLargeKeysWithPrefix) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST(PlainTableDBTest, IteratorReverseSuffixComparator) {
|
||||
TEST_F(PlainTableDBTest, IteratorReverseSuffixComparator) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
// Set only one bucket to force bucket conflict.
|
||||
@@ -765,7 +765,7 @@ TEST(PlainTableDBTest, IteratorReverseSuffixComparator) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST(PlainTableDBTest, HashBucketConflict) {
|
||||
TEST_F(PlainTableDBTest, HashBucketConflict) {
|
||||
for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024;
|
||||
huge_page_tlb_size += 2 * 1024 * 1024) {
|
||||
for (unsigned char i = 1; i <= 3; i++) {
|
||||
@@ -858,7 +858,7 @@ TEST(PlainTableDBTest, HashBucketConflict) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PlainTableDBTest, HashBucketConflictReverseSuffixComparator) {
|
||||
TEST_F(PlainTableDBTest, HashBucketConflictReverseSuffixComparator) {
|
||||
for (size_t huge_page_tlb_size = 0; huge_page_tlb_size <= 2 * 1024 * 1024;
|
||||
huge_page_tlb_size += 2 * 1024 * 1024) {
|
||||
for (unsigned char i = 1; i <= 3; i++) {
|
||||
@@ -951,7 +951,7 @@ TEST(PlainTableDBTest, HashBucketConflictReverseSuffixComparator) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PlainTableDBTest, NonExistingKeyToNonEmptyBucket) {
|
||||
TEST_F(PlainTableDBTest, NonExistingKeyToNonEmptyBucket) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
// Set only one bucket to force bucket conflict.
|
||||
@@ -1007,7 +1007,7 @@ static std::string RandomString(Random* rnd, int len) {
|
||||
return r;
|
||||
}
|
||||
|
||||
TEST(PlainTableDBTest, CompactionTrigger) {
|
||||
TEST_F(PlainTableDBTest, CompactionTrigger) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100 << 10; //100KB
|
||||
options.num_levels = 3;
|
||||
@@ -1041,7 +1041,7 @@ TEST(PlainTableDBTest, CompactionTrigger) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 1);
|
||||
}
|
||||
|
||||
TEST(PlainTableDBTest, AdaptiveTable) {
|
||||
TEST_F(PlainTableDBTest, AdaptiveTable) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
@@ -1087,5 +1087,6 @@ TEST(PlainTableDBTest, AdaptiveTable) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+16
-18
@@ -23,6 +23,7 @@ int main() {
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "util/histogram.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
using GFLAGS::ParseCommandLineFlags;
|
||||
@@ -70,20 +71,20 @@ class TestKeyComparator : public Comparator {
|
||||
|
||||
// Compare needs to be aware of the possibility of a and/or b is
|
||||
// prefix only
|
||||
virtual int Compare(const Slice& a, const Slice& b) const {
|
||||
virtual int Compare(const Slice& a, const Slice& b) const override {
|
||||
const TestKey* key_a = SliceToTestKey(a);
|
||||
const TestKey* key_b = SliceToTestKey(b);
|
||||
if (key_a->prefix != key_b->prefix) {
|
||||
if (key_a->prefix < key_b->prefix) return -1;
|
||||
if (key_a->prefix > key_b->prefix) return 1;
|
||||
} else {
|
||||
ASSERT_TRUE(key_a->prefix == key_b->prefix);
|
||||
EXPECT_TRUE(key_a->prefix == key_b->prefix);
|
||||
// note, both a and b could be prefix only
|
||||
if (a.size() != b.size()) {
|
||||
// one of them is prefix
|
||||
ASSERT_TRUE(
|
||||
(a.size() == sizeof(uint64_t) && b.size() == sizeof(TestKey)) ||
|
||||
(b.size() == sizeof(uint64_t) && a.size() == sizeof(TestKey)));
|
||||
EXPECT_TRUE(
|
||||
(a.size() == sizeof(uint64_t) && b.size() == sizeof(TestKey)) ||
|
||||
(b.size() == sizeof(uint64_t) && a.size() == sizeof(TestKey)));
|
||||
if (a.size() < b.size()) return -1;
|
||||
if (a.size() > b.size()) return 1;
|
||||
} else {
|
||||
@@ -93,7 +94,7 @@ class TestKeyComparator : public Comparator {
|
||||
}
|
||||
|
||||
// both a and b are whole key
|
||||
ASSERT_TRUE(a.size() == sizeof(TestKey) && b.size() == sizeof(TestKey));
|
||||
EXPECT_TRUE(a.size() == sizeof(TestKey) && b.size() == sizeof(TestKey));
|
||||
if (key_a->sorted < key_b->sorted) return -1;
|
||||
if (key_a->sorted > key_b->sorted) return 1;
|
||||
if (key_a->sorted == key_b->sorted) return 0;
|
||||
@@ -106,13 +107,10 @@ class TestKeyComparator : public Comparator {
|
||||
return "TestKeyComparator";
|
||||
}
|
||||
|
||||
virtual void FindShortestSeparator(
|
||||
std::string* start,
|
||||
const Slice& limit) const {
|
||||
}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const {}
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override {}
|
||||
|
||||
virtual void FindShortSuccessor(std::string* key) const override {}
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -147,7 +145,7 @@ std::string Get(DB* db, const ReadOptions& read_options, uint64_t prefix,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class PrefixTest {
|
||||
class PrefixTest : public testing::Test {
|
||||
public:
|
||||
std::shared_ptr<DB> OpenDb() {
|
||||
DB* db;
|
||||
@@ -164,7 +162,7 @@ class PrefixTest {
|
||||
FLAGS_memtable_prefix_bloom_huge_page_tlb_size;
|
||||
|
||||
Status s = DB::Open(options, kDbName, &db);
|
||||
ASSERT_OK(s);
|
||||
EXPECT_OK(s);
|
||||
return std::shared_ptr<DB>(db);
|
||||
}
|
||||
|
||||
@@ -220,7 +218,7 @@ class PrefixTest {
|
||||
Options options;
|
||||
};
|
||||
|
||||
TEST(PrefixTest, TestResult) {
|
||||
TEST_F(PrefixTest, TestResult) {
|
||||
for (int num_buckets = 1; num_buckets <= 2; num_buckets++) {
|
||||
FirstOption();
|
||||
while (NextOptions(num_buckets)) {
|
||||
@@ -393,7 +391,7 @@ TEST(PrefixTest, TestResult) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PrefixTest, DynamicPrefixIterator) {
|
||||
TEST_F(PrefixTest, DynamicPrefixIterator) {
|
||||
while (NextOptions(FLAGS_bucket_count)) {
|
||||
std::cout << "*** Mem table: " << options.memtable_factory->Name()
|
||||
<< std::endl;
|
||||
@@ -495,11 +493,11 @@ TEST(PrefixTest, DynamicPrefixIterator) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
std::cout << kDbName << "\n";
|
||||
|
||||
rocksdb::test::RunAllTests();
|
||||
return 0;
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
#endif // GFLAGS
|
||||
+2
-3
@@ -69,8 +69,7 @@ class Repairer {
|
||||
raw_table_cache_(
|
||||
// TableCache can be small since we expect each table to be opened
|
||||
// once.
|
||||
NewLRUCache(10, options_.table_cache_numshardbits,
|
||||
options_.table_cache_remove_scan_count_limit)),
|
||||
NewLRUCache(10, options_.table_cache_numshardbits)),
|
||||
next_file_number_(1) {
|
||||
table_cache_ =
|
||||
new TableCache(ioptions_, env_options_, raw_table_cache_.get());
|
||||
@@ -189,7 +188,7 @@ class Repairer {
|
||||
Env* env;
|
||||
std::shared_ptr<Logger> info_log;
|
||||
uint64_t lognum;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
virtual void Corruption(size_t bytes, const Status& s) override {
|
||||
// We print error messages for corruption, but continue repairing.
|
||||
Log(InfoLogLevel::ERROR_LEVEL, info_log,
|
||||
"Log #%" PRIu64 ": dropping %d bytes; %s", lognum,
|
||||
|
||||
+11
-10
@@ -31,9 +31,9 @@ struct TestComparator {
|
||||
}
|
||||
};
|
||||
|
||||
class SkipTest { };
|
||||
class SkipTest : public testing::Test {};
|
||||
|
||||
TEST(SkipTest, Empty) {
|
||||
TEST_F(SkipTest, Empty) {
|
||||
Arena arena;
|
||||
TestComparator cmp;
|
||||
SkipList<Key, TestComparator> list(cmp, &arena);
|
||||
@@ -49,7 +49,7 @@ TEST(SkipTest, Empty) {
|
||||
ASSERT_TRUE(!iter.Valid());
|
||||
}
|
||||
|
||||
TEST(SkipTest, InsertAndLookup) {
|
||||
TEST_F(SkipTest, InsertAndLookup) {
|
||||
const int N = 2000;
|
||||
const int R = 5000;
|
||||
Random rnd(1000);
|
||||
@@ -287,7 +287,7 @@ const uint32_t ConcurrentTest::K;
|
||||
|
||||
// Simple test that does single-threaded testing of the ConcurrentTest
|
||||
// scaffolding.
|
||||
TEST(SkipTest, ConcurrentWithoutThreads) {
|
||||
TEST_F(SkipTest, ConcurrentWithoutThreads) {
|
||||
ConcurrentTest test;
|
||||
Random rnd(test::RandomSeed());
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
@@ -364,14 +364,15 @@ static void RunConcurrent(int run) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SkipTest, Concurrent1) { RunConcurrent(1); }
|
||||
TEST(SkipTest, Concurrent2) { RunConcurrent(2); }
|
||||
TEST(SkipTest, Concurrent3) { RunConcurrent(3); }
|
||||
TEST(SkipTest, Concurrent4) { RunConcurrent(4); }
|
||||
TEST(SkipTest, Concurrent5) { RunConcurrent(5); }
|
||||
TEST_F(SkipTest, Concurrent1) { RunConcurrent(1); }
|
||||
TEST_F(SkipTest, Concurrent2) { RunConcurrent(2); }
|
||||
TEST_F(SkipTest, Concurrent3) { RunConcurrent(3); }
|
||||
TEST_F(SkipTest, Concurrent4) { RunConcurrent(4); }
|
||||
TEST_F(SkipTest, Concurrent5) { RunConcurrent(5); }
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2015, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
Slice::Slice(const SliceParts& parts, std::string* buf) {
|
||||
size_t length = 0;
|
||||
for (int i = 0; i < parts.num_parts; ++i) {
|
||||
length += parts.parts[i].size();
|
||||
}
|
||||
buf->reserve(length);
|
||||
|
||||
for (int i = 0; i < parts.num_parts; ++i) {
|
||||
buf->append(parts.parts[i].data(), parts.parts[i].size());
|
||||
}
|
||||
data_ = buf->data();
|
||||
size_ = buf->size();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -20,6 +20,8 @@ class SnapshotImpl : public Snapshot {
|
||||
public:
|
||||
SequenceNumber number_; // const after creation
|
||||
|
||||
virtual SequenceNumber GetSequenceNumber() const override { return number_; }
|
||||
|
||||
private:
|
||||
friend class SnapshotList;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class InternalKeyPropertiesCollector : public TablePropertiesCollector {
|
||||
class InternalKeyPropertiesCollectorFactory
|
||||
: public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() {
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() override {
|
||||
return new InternalKeyPropertiesCollector();
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class UserKeyTablePropertiesCollectorFactory
|
||||
explicit UserKeyTablePropertiesCollectorFactory(
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> user_collector_factory)
|
||||
: user_collector_factory_(user_collector_factory) {}
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() {
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() override {
|
||||
return new UserKeyTablePropertiesCollector(
|
||||
user_collector_factory_->CreateTablePropertiesCollector());
|
||||
}
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class TablePropertiesTest {
|
||||
};
|
||||
class TablePropertiesTest : public testing::Test {};
|
||||
|
||||
// TODO(kailiu) the following classes should be moved to some more general
|
||||
// places, so that other tests can also make use of them.
|
||||
@@ -35,11 +34,11 @@ class FakeWritableFile : public WritableFile {
|
||||
|
||||
const std::string& contents() const { return contents_; }
|
||||
|
||||
virtual Status Close() { return Status::OK(); }
|
||||
virtual Status Flush() { return Status::OK(); }
|
||||
virtual Status Sync() { return Status::OK(); }
|
||||
virtual Status Close() override { return Status::OK(); }
|
||||
virtual Status Flush() override { return Status::OK(); }
|
||||
virtual Status Sync() override { return Status::OK(); }
|
||||
|
||||
virtual Status Append(const Slice& data) {
|
||||
virtual Status Append(const Slice& data) override {
|
||||
contents_.append(data.data(), data.size());
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -60,7 +59,7 @@ class FakeRandomeAccessFile : public RandomAccessFile {
|
||||
uint64_t Size() const { return contents_.size(); }
|
||||
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const {
|
||||
char* scratch) const override {
|
||||
if (offset > contents_.size()) {
|
||||
return Status::InvalidArgument("invalid Read offset");
|
||||
}
|
||||
@@ -79,8 +78,9 @@ class FakeRandomeAccessFile : public RandomAccessFile {
|
||||
|
||||
class DumbLogger : public Logger {
|
||||
public:
|
||||
virtual void Logv(const char* format, va_list ap) { }
|
||||
virtual size_t GetLogFileSize() const { return 0; }
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* format, va_list ap) override {}
|
||||
virtual size_t GetLogFileSize() const override { return 0; }
|
||||
};
|
||||
|
||||
// Utilities test functions
|
||||
@@ -100,9 +100,9 @@ void MakeBuilder(const Options& options,
|
||||
// Collects keys that starts with "A" in a table.
|
||||
class RegularKeysStartWithA: public TablePropertiesCollector {
|
||||
public:
|
||||
const char* Name() const { return "RegularKeysStartWithA"; }
|
||||
const char* Name() const override { return "RegularKeysStartWithA"; }
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) {
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
std::string encoded;
|
||||
PutVarint32(&encoded, count_);
|
||||
*properties = UserCollectedProperties {
|
||||
@@ -112,7 +112,7 @@ class RegularKeysStartWithA: public TablePropertiesCollector {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Add(const Slice& user_key, const Slice& value) {
|
||||
Status Add(const Slice& user_key, const Slice& value) override {
|
||||
// simply asssume all user keys are not empty.
|
||||
if (user_key.data()[0] == 'A') {
|
||||
++count_;
|
||||
@@ -120,7 +120,7 @@ class RegularKeysStartWithA: public TablePropertiesCollector {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual UserCollectedProperties GetReadableProperties() const {
|
||||
virtual UserCollectedProperties GetReadableProperties() const override {
|
||||
return UserCollectedProperties{};
|
||||
}
|
||||
|
||||
@@ -130,10 +130,10 @@ class RegularKeysStartWithA: public TablePropertiesCollector {
|
||||
|
||||
class RegularKeysStartWithAFactory : public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() {
|
||||
virtual TablePropertiesCollector* CreateTablePropertiesCollector() override {
|
||||
return new RegularKeysStartWithA();
|
||||
}
|
||||
const char* Name() const { return "RegularKeysStartWithA"; }
|
||||
const char* Name() const override { return "RegularKeysStartWithA"; }
|
||||
};
|
||||
|
||||
extern uint64_t kBlockBasedTableMagicNumber;
|
||||
@@ -194,7 +194,7 @@ void TestCustomizedTablePropertiesCollector(
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(TablePropertiesTest, CustomizedTablePropertiesCollector) {
|
||||
TEST_F(TablePropertiesTest, CustomizedTablePropertiesCollector) {
|
||||
// Test properties collectors with internal keys or regular keys
|
||||
// for block based table
|
||||
for (bool encode_as_internal : { true, false }) {
|
||||
@@ -300,7 +300,7 @@ void TestInternalKeyPropertiesCollector(
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(TablePropertiesTest, InternalKeyPropertiesCollector) {
|
||||
TEST_F(TablePropertiesTest, InternalKeyPropertiesCollector) {
|
||||
TestInternalKeyPropertiesCollector(
|
||||
kBlockBasedTableMagicNumber,
|
||||
true /* sanitize */,
|
||||
@@ -325,5 +325,6 @@ TEST(TablePropertiesTest, InternalKeyPropertiesCollector) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+10
-10
@@ -27,20 +27,20 @@ class LogFileImpl : public LogFile {
|
||||
sizeFileBytes_(sizeBytes) {
|
||||
}
|
||||
|
||||
std::string PathName() const {
|
||||
std::string PathName() const override {
|
||||
if (type_ == kArchivedLogFile) {
|
||||
return ArchivedLogFileName("", logNumber_);
|
||||
}
|
||||
return LogFileName("", logNumber_);
|
||||
}
|
||||
|
||||
uint64_t LogNumber() const { return logNumber_; }
|
||||
uint64_t LogNumber() const override { return logNumber_; }
|
||||
|
||||
WalFileType Type() const { return type_; }
|
||||
WalFileType Type() const override { return type_; }
|
||||
|
||||
SequenceNumber StartSequence() const { return startSequence_; }
|
||||
SequenceNumber StartSequence() const override { return startSequence_; }
|
||||
|
||||
uint64_t SizeFileBytes() const { return sizeFileBytes_; }
|
||||
uint64_t SizeFileBytes() const override { return sizeFileBytes_; }
|
||||
|
||||
bool operator < (const LogFile& that) const {
|
||||
return LogNumber() < that.LogNumber();
|
||||
@@ -62,13 +62,13 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
const EnvOptions& soptions, const SequenceNumber seqNum,
|
||||
std::unique_ptr<VectorLogPtr> files, VersionSet const* const versions);
|
||||
|
||||
virtual bool Valid();
|
||||
virtual bool Valid() override;
|
||||
|
||||
virtual void Next();
|
||||
virtual void Next() override;
|
||||
|
||||
virtual Status status();
|
||||
virtual Status status() override;
|
||||
|
||||
virtual BatchResult GetBatch();
|
||||
virtual BatchResult GetBatch() override;
|
||||
|
||||
private:
|
||||
const std::string& dir_;
|
||||
@@ -88,7 +88,7 @@ class TransactionLogIteratorImpl : public TransactionLogIterator {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
virtual void Corruption(size_t bytes, const Status& s) override {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, info_log, "dropping %zu bytes; %s", bytes,
|
||||
s.ToString().c_str());
|
||||
}
|
||||
|
||||
+63
-47
@@ -16,6 +16,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
@@ -69,10 +71,10 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<FileMetaData*, FileComparator> FileSet;
|
||||
struct LevelState {
|
||||
std::set<uint64_t> deleted_files;
|
||||
FileSet* added_files;
|
||||
std::unordered_set<uint64_t> deleted_files;
|
||||
// Map from file number to file meta data.
|
||||
std::unordered_map<uint64_t, FileMetaData*> added_files;
|
||||
};
|
||||
|
||||
const EnvOptions& env_options_;
|
||||
@@ -93,40 +95,31 @@ class VersionBuilder::Rep {
|
||||
level_nonzero_cmp_.sort_method = FileComparator::kLevelNon0;
|
||||
level_nonzero_cmp_.internal_comparator =
|
||||
base_vstorage_->InternalComparator();
|
||||
|
||||
levels_[0].added_files = new FileSet(level_zero_cmp_);
|
||||
for (int level = 1; level < base_vstorage_->num_levels(); level++) {
|
||||
levels_[level].added_files = new FileSet(level_nonzero_cmp_);
|
||||
}
|
||||
}
|
||||
|
||||
~Rep() {
|
||||
for (int level = 0; level < base_vstorage_->num_levels(); level++) {
|
||||
const FileSet* added = levels_[level].added_files;
|
||||
std::vector<FileMetaData*> to_unref;
|
||||
to_unref.reserve(added->size());
|
||||
for (FileSet::const_iterator it = added->begin(); it != added->end();
|
||||
++it) {
|
||||
to_unref.push_back(*it);
|
||||
}
|
||||
delete added;
|
||||
for (uint32_t i = 0; i < to_unref.size(); i++) {
|
||||
FileMetaData* f = to_unref[i];
|
||||
f->refs--;
|
||||
if (f->refs <= 0) {
|
||||
if (f->table_reader_handle) {
|
||||
assert(table_cache_ != nullptr);
|
||||
table_cache_->ReleaseHandle(f->table_reader_handle);
|
||||
f->table_reader_handle = nullptr;
|
||||
}
|
||||
delete f;
|
||||
}
|
||||
const auto& added = levels_[level].added_files;
|
||||
for (auto& pair : added) {
|
||||
UnrefFile(pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] levels_;
|
||||
}
|
||||
|
||||
void UnrefFile(FileMetaData* f) {
|
||||
f->refs--;
|
||||
if (f->refs <= 0) {
|
||||
if (f->table_reader_handle) {
|
||||
assert(table_cache_ != nullptr);
|
||||
table_cache_->ReleaseHandle(f->table_reader_handle);
|
||||
f->table_reader_handle = nullptr;
|
||||
}
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckConsistency(VersionStorageInfo* vstorage) {
|
||||
#ifndef NDEBUG
|
||||
// make sure the files are sorted correctly
|
||||
@@ -175,27 +168,20 @@ class VersionBuilder::Rep {
|
||||
// is possibly moved from lower level to higher level in current
|
||||
// version
|
||||
for (int l = level + 1; !found && l < base_vstorage_->num_levels(); l++) {
|
||||
const FileSet* added = levels_[l].added_files;
|
||||
for (FileSet::const_iterator added_iter = added->begin();
|
||||
added_iter != added->end(); ++added_iter) {
|
||||
FileMetaData* f = *added_iter;
|
||||
if (f->fd.GetNumber() == number) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
auto& level_added = levels_[l].added_files;
|
||||
auto got = level_added.find(number);
|
||||
if (got != level_added.end()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// maybe this file was added in a previous edit that was Applied
|
||||
if (!found) {
|
||||
const FileSet* added = levels_[level].added_files;
|
||||
for (FileSet::const_iterator added_iter = added->begin();
|
||||
added_iter != added->end(); ++added_iter) {
|
||||
FileMetaData* f = *added_iter;
|
||||
if (f->fd.GetNumber() == number) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
auto& level_added = levels_[level].added_files;
|
||||
auto got = level_added.find(number);
|
||||
if (got != level_added.end()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
@@ -216,6 +202,12 @@ class VersionBuilder::Rep {
|
||||
const auto number = del_file.second;
|
||||
levels_[level].deleted_files.insert(number);
|
||||
CheckConsistencyForDeletes(edit, number, level);
|
||||
|
||||
auto exising = levels_[level].added_files.find(number);
|
||||
if (exising != levels_[level].added_files.end()) {
|
||||
UnrefFile(exising->second);
|
||||
levels_[level].added_files.erase(number);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new files
|
||||
@@ -224,8 +216,10 @@ class VersionBuilder::Rep {
|
||||
FileMetaData* f = new FileMetaData(new_file.second);
|
||||
f->refs = 1;
|
||||
|
||||
assert(levels_[level].added_files.find(f->fd.GetNumber()) ==
|
||||
levels_[level].added_files.end());
|
||||
levels_[level].deleted_files.erase(f->fd.GetNumber());
|
||||
levels_[level].added_files->insert(f);
|
||||
levels_[level].added_files[f->fd.GetNumber()] = f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,10 +235,31 @@ class VersionBuilder::Rep {
|
||||
const auto& base_files = base_vstorage_->LevelFiles(level);
|
||||
auto base_iter = base_files.begin();
|
||||
auto base_end = base_files.end();
|
||||
const auto& added_files = *levels_[level].added_files;
|
||||
vstorage->Reserve(level, base_files.size() + added_files.size());
|
||||
const auto& unordered_added_files = levels_[level].added_files;
|
||||
vstorage->Reserve(level,
|
||||
base_files.size() + unordered_added_files.size());
|
||||
|
||||
// Sort added files for the level.
|
||||
std::vector<FileMetaData*> added_files;
|
||||
added_files.reserve(unordered_added_files.size());
|
||||
for (const auto& pair : unordered_added_files) {
|
||||
added_files.push_back(pair.second);
|
||||
}
|
||||
std::sort(added_files.begin(), added_files.end(), cmp);
|
||||
|
||||
#ifndef NDEBUG
|
||||
FileMetaData* prev_file = nullptr;
|
||||
#endif
|
||||
|
||||
for (const auto& added : added_files) {
|
||||
#ifndef NDEBUG
|
||||
if (level > 0 && prev_file != nullptr) {
|
||||
assert(base_vstorage_->InternalComparator()->Compare(
|
||||
prev_file->smallest, added->smallest) <= 0);
|
||||
}
|
||||
prev_file = added;
|
||||
#endif
|
||||
|
||||
// Add all smaller files listed in base_
|
||||
for (auto bpos = std::upper_bound(base_iter, base_end, added, cmp);
|
||||
base_iter != bpos; ++base_iter) {
|
||||
@@ -266,7 +281,8 @@ class VersionBuilder::Rep {
|
||||
void LoadTableHandlers() {
|
||||
assert(table_cache_ != nullptr);
|
||||
for (int level = 0; level < base_vstorage_->num_levels(); level++) {
|
||||
for (auto& file_meta : *(levels_[level].added_files)) {
|
||||
for (auto& file_meta_pair : levels_[level].added_files) {
|
||||
auto* file_meta = file_meta_pair.second;
|
||||
assert(!file_meta->table_reader_handle);
|
||||
table_cache_->FindTable(
|
||||
env_options_, *(base_vstorage_->InternalComparator()),
|
||||
|
||||
+170
-19
@@ -7,12 +7,13 @@
|
||||
#include "db/version_edit.h"
|
||||
#include "db/version_set.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class VersionBuilderTest {
|
||||
class VersionBuilderTest : public testing::Test {
|
||||
public:
|
||||
const Comparator* ucmp_;
|
||||
InternalKeyComparator icmp_;
|
||||
@@ -53,15 +54,17 @@ class VersionBuilderTest {
|
||||
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 0, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100,
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
uint64_t num_entries = 0, uint64_t num_deletions = 0,
|
||||
bool sampled = false) {
|
||||
bool sampled = false, SequenceNumber smallest_seqno = 0,
|
||||
SequenceNumber largest_seqno = 0) {
|
||||
assert(level < vstorage_.num_levels());
|
||||
FileMetaData* f = new FileMetaData;
|
||||
f->fd = FileDescriptor(file_number, path_id, file_size);
|
||||
f->smallest = GetInternalKey(smallest, smallest_seq);
|
||||
f->largest = GetInternalKey(largest, largest_seq);
|
||||
f->smallest_seqno = smallest_seqno;
|
||||
f->largest_seqno = largest_seqno;
|
||||
f->compensated_file_size = file_size;
|
||||
f->refs = 0;
|
||||
f->num_entries = num_entries;
|
||||
@@ -74,26 +77,35 @@ class VersionBuilderTest {
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
vstorage_.ComputeCompactionScore(mutable_cf_options_, fifo_options_,
|
||||
size_being_compacted_);
|
||||
vstorage_.UpdateFilesBySize();
|
||||
vstorage_.UpdateNumNonEmptyLevels();
|
||||
vstorage_.GenerateFileIndexer();
|
||||
vstorage_.GenerateLevelFilesBrief();
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_.SetFinalized();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(VersionBuilderTest, ApplyAndSaveTo) {
|
||||
void UnrefFilesInVersion(VersionStorageInfo* new_vstorage) {
|
||||
for (int i = 0; i < new_vstorage->num_levels(); i++) {
|
||||
for (auto* f : new_vstorage->LevelFiles(i)) {
|
||||
if (--f->refs == 0) {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyAndSaveTo) {
|
||||
Add(0, 1U, "150", "200", 100U);
|
||||
// Level 1 score 1.2
|
||||
|
||||
Add(1, 66U, "150", "200", 100U);
|
||||
Add(1, 88U, "201", "300", 100U);
|
||||
// Level 2 score 1.8. File 7 is the largest. Should be picked
|
||||
|
||||
Add(2, 6U, "150", "179", 100U);
|
||||
Add(2, 7U, "180", "220", 100U);
|
||||
Add(2, 8U, "221", "300", 100U);
|
||||
// Level 3 score slightly larger than 1
|
||||
|
||||
Add(3, 26U, "150", "170", 100U);
|
||||
Add(3, 27U, "171", "179", 100U);
|
||||
Add(3, 28U, "191", "220", 100U);
|
||||
@@ -117,16 +129,152 @@ TEST(VersionBuilderTest, ApplyAndSaveTo) {
|
||||
ASSERT_EQ(400U, new_vstorage.NumLevelBytes(2));
|
||||
ASSERT_EQ(300U, new_vstorage.NumLevelBytes(3));
|
||||
|
||||
for (int i = 0; i < new_vstorage.num_levels(); i++) {
|
||||
for (auto* f : new_vstorage.LevelFiles(i)) {
|
||||
if (--f->refs == 0) {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
}
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST(VersionBuilderTest, EstimatedActiveKeys) {
|
||||
TEST_F(VersionBuilderTest, ApplyAndSaveToDynamic) {
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
|
||||
Add(0, 1U, "150", "200", 100U, 0, 200U, 200U, 0, 0, false, 200U, 200U);
|
||||
Add(0, 88U, "201", "300", 100U, 0, 100U, 100U, 0, 0, false, 100U, 100U);
|
||||
|
||||
Add(4, 6U, "150", "179", 100U);
|
||||
Add(4, 7U, "180", "220", 100U);
|
||||
Add(4, 8U, "221", "300", 100U);
|
||||
|
||||
Add(5, 26U, "150", "170", 100U);
|
||||
Add(5, 27U, "171", "179", 100U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(3, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200);
|
||||
version_edit.DeleteFile(0, 1U);
|
||||
version_edit.DeleteFile(0, 88U);
|
||||
|
||||
EnvOptions env_options;
|
||||
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr);
|
||||
version_builder.Apply(&version_edit);
|
||||
version_builder.SaveTo(&new_vstorage);
|
||||
|
||||
ASSERT_EQ(0U, new_vstorage.NumLevelBytes(0));
|
||||
ASSERT_EQ(100U, new_vstorage.NumLevelBytes(3));
|
||||
ASSERT_EQ(300U, new_vstorage.NumLevelBytes(4));
|
||||
ASSERT_EQ(200U, new_vstorage.NumLevelBytes(5));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyAndSaveToDynamic2) {
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
|
||||
Add(0, 1U, "150", "200", 100U, 0, 200U, 200U, 0, 0, false, 200U, 200U);
|
||||
Add(0, 88U, "201", "300", 100U, 0, 100U, 100U, 0, 0, false, 100U, 100U);
|
||||
|
||||
Add(4, 6U, "150", "179", 100U);
|
||||
Add(4, 7U, "180", "220", 100U);
|
||||
Add(4, 8U, "221", "300", 100U);
|
||||
|
||||
Add(5, 26U, "150", "170", 100U);
|
||||
Add(5, 27U, "171", "179", 100U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(4, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200);
|
||||
version_edit.DeleteFile(0, 1U);
|
||||
version_edit.DeleteFile(0, 88U);
|
||||
version_edit.DeleteFile(4, 6U);
|
||||
version_edit.DeleteFile(4, 7U);
|
||||
version_edit.DeleteFile(4, 8U);
|
||||
|
||||
EnvOptions env_options;
|
||||
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr);
|
||||
version_builder.Apply(&version_edit);
|
||||
version_builder.SaveTo(&new_vstorage);
|
||||
|
||||
ASSERT_EQ(0U, new_vstorage.NumLevelBytes(0));
|
||||
ASSERT_EQ(100U, new_vstorage.NumLevelBytes(4));
|
||||
ASSERT_EQ(200U, new_vstorage.NumLevelBytes(5));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyMultipleAndSaveTo) {
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(2, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200);
|
||||
version_edit.AddFile(2, 676, 0, 100U, GetInternalKey("401"),
|
||||
GetInternalKey("450"), 200, 200);
|
||||
version_edit.AddFile(2, 636, 0, 100U, GetInternalKey("601"),
|
||||
GetInternalKey("650"), 200, 200);
|
||||
version_edit.AddFile(2, 616, 0, 100U, GetInternalKey("501"),
|
||||
GetInternalKey("550"), 200, 200);
|
||||
version_edit.AddFile(2, 606, 0, 100U, GetInternalKey("701"),
|
||||
GetInternalKey("750"), 200, 200);
|
||||
|
||||
EnvOptions env_options;
|
||||
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr);
|
||||
version_builder.Apply(&version_edit);
|
||||
version_builder.SaveTo(&new_vstorage);
|
||||
|
||||
ASSERT_EQ(500U, new_vstorage.NumLevelBytes(2));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
EnvOptions env_options;
|
||||
VersionBuilder version_builder(env_options, nullptr, &vstorage_);
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr);
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.AddFile(2, 666, 0, 100U, GetInternalKey("301"),
|
||||
GetInternalKey("350"), 200, 200);
|
||||
version_edit.AddFile(2, 676, 0, 100U, GetInternalKey("401"),
|
||||
GetInternalKey("450"), 200, 200);
|
||||
version_edit.AddFile(2, 636, 0, 100U, GetInternalKey("601"),
|
||||
GetInternalKey("650"), 200, 200);
|
||||
version_edit.AddFile(2, 616, 0, 100U, GetInternalKey("501"),
|
||||
GetInternalKey("550"), 200, 200);
|
||||
version_edit.AddFile(2, 606, 0, 100U, GetInternalKey("701"),
|
||||
GetInternalKey("750"), 200, 200);
|
||||
version_builder.Apply(&version_edit);
|
||||
|
||||
VersionEdit version_edit2;
|
||||
version_edit.AddFile(2, 808, 0, 100U, GetInternalKey("901"),
|
||||
GetInternalKey("950"), 200, 200);
|
||||
version_edit2.DeleteFile(2, 616);
|
||||
version_edit2.DeleteFile(2, 636);
|
||||
version_edit.AddFile(2, 806, 0, 100U, GetInternalKey("801"),
|
||||
GetInternalKey("850"), 200, 200);
|
||||
version_builder.Apply(&version_edit2);
|
||||
|
||||
version_builder.SaveTo(&new_vstorage);
|
||||
|
||||
ASSERT_EQ(300U, new_vstorage.NumLevelBytes(2));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, EstimatedActiveKeys) {
|
||||
const uint32_t kTotalSamples = 20;
|
||||
const uint32_t kNumLevels = 5;
|
||||
const uint32_t kFilesPerLevel = 8;
|
||||
@@ -150,4 +298,7 @@ TEST(VersionBuilderTest, EstimatedActiveKeys) {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+3
-1
@@ -76,8 +76,10 @@ struct FileMetaData {
|
||||
|
||||
// File size compensated by deletion entry.
|
||||
// This is updated in Version::UpdateAccumulatedStats() first time when the
|
||||
// file is created or loaded. After it is updated, it is immutable.
|
||||
// file is created or loaded. After it is updated (!= 0), it is immutable.
|
||||
uint64_t compensated_file_size;
|
||||
// These values can mutate, but they can only be read or written from
|
||||
// single-threaded LogAndApply thread
|
||||
uint64_t num_entries; // the number of entries.
|
||||
uint64_t num_deletions; // the number of deletion entries.
|
||||
uint64_t raw_key_size; // total uncompressed key size.
|
||||
|
||||
@@ -22,9 +22,9 @@ static void TestEncodeDecode(const VersionEdit& edit) {
|
||||
ASSERT_EQ(encoded, encoded2);
|
||||
}
|
||||
|
||||
class VersionEditTest { };
|
||||
class VersionEditTest : public testing::Test {};
|
||||
|
||||
TEST(VersionEditTest, EncodeDecode) {
|
||||
TEST_F(VersionEditTest, EncodeDecode) {
|
||||
static const uint64_t kBig = 1ull << 50;
|
||||
static const uint32_t kBig32Bit = 1ull << 30;
|
||||
|
||||
@@ -45,7 +45,7 @@ TEST(VersionEditTest, EncodeDecode) {
|
||||
TestEncodeDecode(edit);
|
||||
}
|
||||
|
||||
TEST(VersionEditTest, EncodeEmptyFile) {
|
||||
TEST_F(VersionEditTest, EncodeEmptyFile) {
|
||||
VersionEdit edit;
|
||||
edit.AddFile(0, 0, 0, 0,
|
||||
InternalKey(),
|
||||
@@ -55,7 +55,7 @@ TEST(VersionEditTest, EncodeEmptyFile) {
|
||||
ASSERT_TRUE(!edit.EncodeTo(&buffer));
|
||||
}
|
||||
|
||||
TEST(VersionEditTest, ColumnFamilyTest) {
|
||||
TEST_F(VersionEditTest, ColumnFamilyTest) {
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(2);
|
||||
edit.AddColumnFamily("column_family");
|
||||
@@ -71,5 +71,6 @@ TEST(VersionEditTest, ColumnFamilyTest) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+288
-73
@@ -91,6 +91,7 @@ class FilePicker {
|
||||
const InternalKeyComparator* internal_comparator)
|
||||
: num_levels_(num_levels),
|
||||
curr_level_(-1),
|
||||
hit_file_level_(-1),
|
||||
search_left_bound_(0),
|
||||
search_right_bound_(FileIndexer::kLevelMaxIndex),
|
||||
#ifndef NDEBUG
|
||||
@@ -120,6 +121,7 @@ class FilePicker {
|
||||
while (curr_index_in_curr_level_ < curr_file_level_->num_files) {
|
||||
// Loops over all files in current level.
|
||||
FdWithKeyRange* f = &curr_file_level_->files[curr_index_in_curr_level_];
|
||||
hit_file_level_ = curr_level_;
|
||||
int cmp_largest = -1;
|
||||
|
||||
// Do key range filtering of files or/and fractional cascading if:
|
||||
@@ -199,9 +201,14 @@ class FilePicker {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// getter for current file level
|
||||
// for GET_HIT_L0, GET_HIT_L1 & GET_HIT_L2_AND_UP counts
|
||||
unsigned int GetHitFileLevel() { return hit_file_level_; }
|
||||
|
||||
private:
|
||||
unsigned int num_levels_;
|
||||
unsigned int curr_level_;
|
||||
unsigned int hit_file_level_;
|
||||
int32_t search_left_bound_;
|
||||
int32_t search_right_bound_;
|
||||
#ifndef NDEBUG
|
||||
@@ -326,7 +333,6 @@ void DoGenerateLevelFilesBrief(LevelFilesBrief* file_level,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
Arena* arena) {
|
||||
assert(file_level);
|
||||
assert(files.size() >= 0);
|
||||
assert(arena);
|
||||
|
||||
size_t num = files.size();
|
||||
@@ -419,23 +425,21 @@ class LevelFileNumIterator : public Iterator {
|
||||
index_(static_cast<uint32_t>(flevel->num_files)),
|
||||
current_value_(0, 0, 0) { // Marks as invalid
|
||||
}
|
||||
virtual bool Valid() const {
|
||||
return index_ < flevel_->num_files;
|
||||
}
|
||||
virtual void Seek(const Slice& target) {
|
||||
virtual bool Valid() const override { return index_ < flevel_->num_files; }
|
||||
virtual void Seek(const Slice& target) override {
|
||||
index_ = FindFile(icmp_, *flevel_, target);
|
||||
}
|
||||
virtual void SeekToFirst() { index_ = 0; }
|
||||
virtual void SeekToLast() {
|
||||
virtual void SeekToFirst() override { index_ = 0; }
|
||||
virtual void SeekToLast() override {
|
||||
index_ = (flevel_->num_files == 0)
|
||||
? 0
|
||||
: static_cast<uint32_t>(flevel_->num_files) - 1;
|
||||
}
|
||||
virtual void Next() {
|
||||
virtual void Next() override {
|
||||
assert(Valid());
|
||||
index_++;
|
||||
}
|
||||
virtual void Prev() {
|
||||
virtual void Prev() override {
|
||||
assert(Valid());
|
||||
if (index_ == 0) {
|
||||
index_ = static_cast<uint32_t>(flevel_->num_files); // Marks as invalid
|
||||
@@ -443,11 +447,11 @@ class LevelFileNumIterator : public Iterator {
|
||||
index_--;
|
||||
}
|
||||
}
|
||||
Slice key() const {
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return flevel_->files[index_].largest_key;
|
||||
}
|
||||
Slice value() const {
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
|
||||
auto file_meta = flevel_->files[index_];
|
||||
@@ -455,7 +459,8 @@ class LevelFileNumIterator : public Iterator {
|
||||
return Slice(reinterpret_cast<const char*>(¤t_value_),
|
||||
sizeof(FileDescriptor));
|
||||
}
|
||||
virtual Status status() const { return Status::OK(); }
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
const InternalKeyComparator icmp_;
|
||||
const LevelFilesBrief* flevel_;
|
||||
@@ -721,6 +726,7 @@ VersionStorageInfo::VersionStorageInfo(
|
||||
file_indexer_(user_comparator),
|
||||
compaction_style_(compaction_style),
|
||||
files_(new std::vector<FileMetaData*>[num_levels_]),
|
||||
base_level_(1),
|
||||
files_by_size_(num_levels_),
|
||||
next_file_to_compact_by_size_(num_levels_),
|
||||
compaction_score_(num_levels_),
|
||||
@@ -745,7 +751,8 @@ VersionStorageInfo::VersionStorageInfo(
|
||||
|
||||
Version::Version(ColumnFamilyData* column_family_data, VersionSet* vset,
|
||||
uint64_t version_number)
|
||||
: cfd_(column_family_data),
|
||||
: env_(vset->env_),
|
||||
cfd_(column_family_data),
|
||||
info_log_((cfd_ == nullptr) ? nullptr : cfd_->ioptions()->info_log),
|
||||
db_statistics_((cfd_ == nullptr) ? nullptr
|
||||
: cfd_->ioptions()->statistics),
|
||||
@@ -780,7 +787,7 @@ void Version::Get(const ReadOptions& read_options,
|
||||
GetContext get_context(
|
||||
user_comparator(), merge_operator_, info_log_, db_statistics_,
|
||||
status->ok() ? GetContext::kNotFound : GetContext::kMerge, user_key,
|
||||
value, value_found, merge_context);
|
||||
value, value_found, merge_context, this->env_);
|
||||
|
||||
FilePicker fp(
|
||||
storage_info_.files_, user_key, ikey, &storage_info_.level_files_brief_,
|
||||
@@ -800,6 +807,13 @@ void Version::Get(const ReadOptions& read_options,
|
||||
// Keep searching in other files
|
||||
break;
|
||||
case GetContext::kFound:
|
||||
if (fp.GetHitFileLevel() == 0) {
|
||||
RecordTick(db_statistics_, GET_HIT_L0);
|
||||
} else if (fp.GetHitFileLevel() == 1) {
|
||||
RecordTick(db_statistics_, GET_HIT_L1);
|
||||
} else if (fp.GetHitFileLevel() >= 2) {
|
||||
RecordTick(db_statistics_, GET_HIT_L2_AND_UP);
|
||||
}
|
||||
return;
|
||||
case GetContext::kDeleted:
|
||||
// Use empty error message for speed
|
||||
@@ -844,14 +858,11 @@ void VersionStorageInfo::GenerateLevelFilesBrief() {
|
||||
}
|
||||
}
|
||||
|
||||
void Version::PrepareApply(const MutableCFOptions& mutable_cf_options,
|
||||
std::vector<uint64_t>& size_being_compacted) {
|
||||
void Version::PrepareApply(const MutableCFOptions& mutable_cf_options) {
|
||||
UpdateAccumulatedStats();
|
||||
storage_info_.ComputeCompactionScore(
|
||||
mutable_cf_options, cfd_->ioptions()->compaction_options_fifo,
|
||||
size_being_compacted);
|
||||
storage_info_.UpdateFilesBySize();
|
||||
storage_info_.UpdateNumNonEmptyLevels();
|
||||
storage_info_.CalculateBaseBytes(*cfd_->ioptions(), mutable_cf_options);
|
||||
storage_info_.UpdateFilesBySize();
|
||||
storage_info_.GenerateFileIndexer();
|
||||
storage_info_.GenerateLevelFilesBrief();
|
||||
}
|
||||
@@ -941,7 +952,9 @@ void VersionStorageInfo::ComputeCompensatedSizes() {
|
||||
for (int level = 0; level < num_levels_; level++) {
|
||||
for (auto* file_meta : files_[level]) {
|
||||
// Here we only compute compensated_file_size for those file_meta
|
||||
// which compensated_file_size is uninitialized (== 0).
|
||||
// which compensated_file_size is uninitialized (== 0). This is true only
|
||||
// for files that have been created right now and no other thread has
|
||||
// access to them. That's why we can safely mutate compensated_file_size.
|
||||
if (file_meta->compensated_file_size == 0) {
|
||||
file_meta->compensated_file_size = file_meta->fd.GetFileSize() +
|
||||
file_meta->num_deletions * average_value_size *
|
||||
@@ -960,8 +973,7 @@ int VersionStorageInfo::MaxInputLevel() const {
|
||||
|
||||
void VersionStorageInfo::ComputeCompactionScore(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const CompactionOptionsFIFO& compaction_options_fifo,
|
||||
std::vector<uint64_t>& size_being_compacted) {
|
||||
const CompactionOptionsFIFO& compaction_options_fifo) {
|
||||
double max_score = 0;
|
||||
int max_score_level = 0;
|
||||
|
||||
@@ -1002,10 +1014,14 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
}
|
||||
} else {
|
||||
// Compute the ratio of current size to size limit.
|
||||
const uint64_t level_bytes =
|
||||
TotalCompensatedFileSize(files_[level]) - size_being_compacted[level];
|
||||
score = static_cast<double>(level_bytes) /
|
||||
mutable_cf_options.MaxBytesForLevel(level);
|
||||
uint64_t level_bytes_no_compacting = 0;
|
||||
for (auto f : files_[level]) {
|
||||
if (f && f->being_compacted == false) {
|
||||
level_bytes_no_compacting += f->compensated_file_size;
|
||||
}
|
||||
}
|
||||
score = static_cast<double>(level_bytes_no_compacting) /
|
||||
MaxBytesForLevel(level);
|
||||
if (max_score < score) {
|
||||
max_score = score;
|
||||
max_score_level = level;
|
||||
@@ -1064,6 +1080,45 @@ void VersionStorageInfo::AddFile(int level, FileMetaData* f) {
|
||||
level_files->push_back(f);
|
||||
}
|
||||
|
||||
// Version::PrepareApply() need to be called before calling the function, or
|
||||
// following functions called:
|
||||
// 1. UpdateNumNonEmptyLevels();
|
||||
// 2. CalculateBaseBytes();
|
||||
// 3. UpdateFilesBySize();
|
||||
// 4. GenerateFileIndexer();
|
||||
// 5. GenerateLevelFilesBrief();
|
||||
void VersionStorageInfo::SetFinalized() {
|
||||
finalized_ = true;
|
||||
#ifndef NDEBUG
|
||||
assert(base_level_ >= 1 && (num_levels() <= 1 || base_level_ < num_levels()));
|
||||
// Verify all levels newer than base_level are empty except L0
|
||||
for (int level = 1; level < base_level(); level++) {
|
||||
assert(NumLevelBytes(level) == 0);
|
||||
}
|
||||
uint64_t max_bytes_prev_level = 0;
|
||||
for (int level = base_level(); level < num_levels() - 1; level++) {
|
||||
if (LevelFiles(level).size() == 0) {
|
||||
continue;
|
||||
}
|
||||
assert(MaxBytesForLevel(level) >= max_bytes_prev_level);
|
||||
max_bytes_prev_level = MaxBytesForLevel(level);
|
||||
}
|
||||
int num_empty_non_l0_level = 0;
|
||||
for (int level = 0; level < num_levels(); level++) {
|
||||
assert(LevelFiles(level).size() == 0 ||
|
||||
LevelFiles(level).size() == LevelFilesBrief(level).num_files);
|
||||
if (level > 0 && NumLevelBytes(level) > 0) {
|
||||
num_empty_non_l0_level++;
|
||||
}
|
||||
if (LevelFiles(level).size() > 0) {
|
||||
assert(level < num_non_empty_levels());
|
||||
}
|
||||
}
|
||||
assert(compaction_level_.size() > 0);
|
||||
assert(compaction_level_.size() == compaction_score_.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
void VersionStorageInfo::UpdateNumNonEmptyLevels() {
|
||||
num_non_empty_levels_ = num_levels_;
|
||||
for (int i = num_levels_ - 1; i >= 0; i--) {
|
||||
@@ -1129,6 +1184,10 @@ bool Version::Unref() {
|
||||
bool VersionStorageInfo::OverlapInLevel(int level,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key) {
|
||||
if (level >= num_non_empty_levels_) {
|
||||
// empty level, no overlap
|
||||
return false;
|
||||
}
|
||||
return SomeFileOverlapsRange(*internal_comparator_, (level > 0),
|
||||
level_files_brief_[level], smallest_user_key,
|
||||
largest_user_key);
|
||||
@@ -1172,6 +1231,11 @@ int VersionStorageInfo::PickLevelForMemTableOutput(
|
||||
void VersionStorageInfo::GetOverlappingInputs(
|
||||
int level, const InternalKey* begin, const InternalKey* end,
|
||||
std::vector<FileMetaData*>* inputs, int hint_index, int* file_index) {
|
||||
if (level >= num_non_empty_levels_) {
|
||||
// this level is empty, no overlapping inputs
|
||||
return;
|
||||
}
|
||||
|
||||
inputs->clear();
|
||||
Slice user_begin, user_end;
|
||||
if (begin != nullptr) {
|
||||
@@ -1386,7 +1450,15 @@ uint64_t VersionStorageInfo::NumLevelBytes(int level) const {
|
||||
|
||||
const char* VersionStorageInfo::LevelSummary(
|
||||
LevelSummaryStorage* scratch) const {
|
||||
int len = snprintf(scratch->buffer, sizeof(scratch->buffer), "files[");
|
||||
int len = 0;
|
||||
if (num_levels() > 1) {
|
||||
assert(base_level_ < static_cast<int>(level_max_bytes_.size()));
|
||||
len = snprintf(scratch->buffer, sizeof(scratch->buffer),
|
||||
"base level %d max bytes base %" PRIu64, base_level_,
|
||||
level_max_bytes_[base_level_]);
|
||||
}
|
||||
len +=
|
||||
snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, " files[");
|
||||
for (int i = 0; i < num_levels(); i++) {
|
||||
int sz = sizeof(scratch->buffer) - len;
|
||||
int ret = snprintf(scratch->buffer + len, sz, "%d ", int(files_[i].size()));
|
||||
@@ -1439,6 +1511,114 @@ int64_t VersionStorageInfo::MaxNextLevelOverlappingBytes() {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t VersionStorageInfo::MaxBytesForLevel(int level) const {
|
||||
// Note: the result for level zero is not really used since we set
|
||||
// the level-0 compaction threshold based on number of files.
|
||||
assert(level >= 0);
|
||||
assert(level < static_cast<int>(level_max_bytes_.size()));
|
||||
return level_max_bytes_[level];
|
||||
}
|
||||
|
||||
void VersionStorageInfo::CalculateBaseBytes(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& options) {
|
||||
level_max_bytes_.resize(ioptions.num_levels);
|
||||
if (!ioptions.level_compaction_dynamic_level_bytes) {
|
||||
base_level_ = 1;
|
||||
|
||||
// Calculate for static bytes base case
|
||||
for (int i = 0; i < ioptions.num_levels; ++i) {
|
||||
if (i == 0 && ioptions.compaction_style == kCompactionStyleUniversal) {
|
||||
level_max_bytes_[i] = options.max_bytes_for_level_base;
|
||||
} else if (i > 1) {
|
||||
level_max_bytes_[i] = MultiplyCheckOverflow(
|
||||
MultiplyCheckOverflow(level_max_bytes_[i - 1],
|
||||
options.max_bytes_for_level_multiplier),
|
||||
options.max_bytes_for_level_multiplier_additional[i - 1]);
|
||||
} else {
|
||||
level_max_bytes_[i] = options.max_bytes_for_level_base;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint64_t max_level_size = 0;
|
||||
|
||||
int first_non_empty_level = -1;
|
||||
// Find size of non-L0 level of most data.
|
||||
// Cannot use the size of the last level because it can be empty or less
|
||||
// than previous levels after compaction.
|
||||
for (int i = 1; i < num_levels_; i++) {
|
||||
uint64_t total_size = 0;
|
||||
for (const auto& f : files_[i]) {
|
||||
total_size += f->fd.GetFileSize();
|
||||
}
|
||||
if (total_size > 0 && first_non_empty_level == -1) {
|
||||
first_non_empty_level = i;
|
||||
}
|
||||
if (total_size > max_level_size) {
|
||||
max_level_size = total_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefill every level's max bytes to disallow compaction from there.
|
||||
for (int i = 0; i < num_levels_; i++) {
|
||||
level_max_bytes_[i] = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
if (max_level_size == 0) {
|
||||
// No data for L1 and up. L0 compacts to last level directly.
|
||||
// No compaction from L1+ needs to be scheduled.
|
||||
base_level_ = num_levels_ - 1;
|
||||
} else {
|
||||
uint64_t base_bytes_max = options.max_bytes_for_level_base;
|
||||
uint64_t base_bytes_min =
|
||||
base_bytes_max / options.max_bytes_for_level_multiplier;
|
||||
|
||||
// Try whether we can make last level's target size to be max_level_size
|
||||
uint64_t cur_level_size = max_level_size;
|
||||
for (int i = num_levels_ - 2; i >= first_non_empty_level; i--) {
|
||||
// Round up after dividing
|
||||
cur_level_size /= options.max_bytes_for_level_multiplier;
|
||||
}
|
||||
|
||||
// Calculate base level and its size.
|
||||
uint64_t base_level_size;
|
||||
if (cur_level_size <= base_bytes_min) {
|
||||
// Case 1. If we make target size of last level to be max_level_size,
|
||||
// target size of the first non-empty level would be smaller than
|
||||
// base_bytes_min. We set it be base_bytes_min.
|
||||
base_level_size = base_bytes_min + 1U;
|
||||
base_level_ = first_non_empty_level;
|
||||
Warn(ioptions.info_log,
|
||||
"More existing levels in DB than needed. "
|
||||
"max_bytes_for_level_multiplier may not be guaranteed.");
|
||||
} else {
|
||||
// Find base level (where L0 data is compacted to).
|
||||
base_level_ = first_non_empty_level;
|
||||
while (base_level_ > 1 && cur_level_size > base_bytes_max) {
|
||||
--base_level_;
|
||||
cur_level_size =
|
||||
cur_level_size / options.max_bytes_for_level_multiplier;
|
||||
}
|
||||
if (cur_level_size > base_bytes_max) {
|
||||
// Even L1 will be too large
|
||||
assert(base_level_ == 1);
|
||||
base_level_size = base_bytes_max;
|
||||
} else {
|
||||
base_level_size = cur_level_size;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t level_size = base_level_size;
|
||||
for (int i = base_level_; i < num_levels_; i++) {
|
||||
if (i > base_level_) {
|
||||
level_size = MultiplyCheckOverflow(
|
||||
level_size, options.max_bytes_for_level_multiplier);
|
||||
}
|
||||
level_max_bytes_[i] = level_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Version::AddLiveFiles(std::vector<FileDescriptor>* live) {
|
||||
for (int level = 0; level < storage_info_.num_levels(); level++) {
|
||||
const std::vector<FileMetaData*>& files = storage_info_.files_[level];
|
||||
@@ -1480,11 +1660,11 @@ std::string Version::DebugString(bool hex) const {
|
||||
struct VersionSet::ManifestWriter {
|
||||
Status status;
|
||||
bool done;
|
||||
port::CondVar cv;
|
||||
InstrumentedCondVar cv;
|
||||
ColumnFamilyData* cfd;
|
||||
VersionEdit* edit;
|
||||
|
||||
explicit ManifestWriter(port::Mutex* mu, ColumnFamilyData* _cfd,
|
||||
explicit ManifestWriter(InstrumentedMutex* mu, ColumnFamilyData* _cfd,
|
||||
VersionEdit* e)
|
||||
: done(false), cv(mu), cfd(_cfd), edit(e) {}
|
||||
};
|
||||
@@ -1521,6 +1701,11 @@ VersionSet::~VersionSet() {
|
||||
|
||||
void VersionSet::AppendVersion(ColumnFamilyData* column_family_data,
|
||||
Version* v) {
|
||||
// compute new compaction score
|
||||
v->storage_info()->ComputeCompactionScore(
|
||||
*column_family_data->GetLatestMutableCFOptions(),
|
||||
column_family_data->ioptions()->compaction_options_fifo);
|
||||
|
||||
// Mark v finalized
|
||||
v->storage_info_.SetFinalized();
|
||||
|
||||
@@ -1544,7 +1729,7 @@ void VersionSet::AppendVersion(ColumnFamilyData* column_family_data,
|
||||
|
||||
Status VersionSet::LogAndApply(ColumnFamilyData* column_family_data,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionEdit* edit, port::Mutex* mu,
|
||||
VersionEdit* edit, InstrumentedMutex* mu,
|
||||
Directory* db_directory, bool new_descriptor_log,
|
||||
const ColumnFamilyOptions* new_cf_options) {
|
||||
mu->AssertHeld();
|
||||
@@ -1631,13 +1816,6 @@ Status VersionSet::LogAndApply(ColumnFamilyData* column_family_data,
|
||||
// Unlock during expensive operations. New writes cannot get here
|
||||
// because &w is ensuring that all new writes get queued.
|
||||
{
|
||||
std::vector<uint64_t> size_being_compacted;
|
||||
if (!edit->IsColumnFamilyManipulation()) {
|
||||
size_being_compacted.resize(v->storage_info()->num_levels() - 1);
|
||||
// calculate the amount of data being compacted at every level
|
||||
column_family_data->compaction_picker()->SizeBeingCompacted(
|
||||
size_being_compacted);
|
||||
}
|
||||
|
||||
mu->Unlock();
|
||||
|
||||
@@ -1668,7 +1846,7 @@ Status VersionSet::LogAndApply(ColumnFamilyData* column_family_data,
|
||||
|
||||
if (!edit->IsColumnFamilyManipulation()) {
|
||||
// This is cpu-heavy operations, which should be called outside mutex.
|
||||
v->PrepareApply(mutable_cf_options, size_being_compacted);
|
||||
v->PrepareApply(mutable_cf_options);
|
||||
}
|
||||
|
||||
// Write new record to MANIFEST log
|
||||
@@ -1685,16 +1863,8 @@ Status VersionSet::LogAndApply(ColumnFamilyData* column_family_data,
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (s.ok() && db_options_->disableDataSync == false) {
|
||||
if (db_options_->use_fsync) {
|
||||
StopWatch sw(env_, db_options_->statistics.get(),
|
||||
MANIFEST_FILE_SYNC_MICROS);
|
||||
s = descriptor_log_->file()->Fsync();
|
||||
} else {
|
||||
StopWatch sw(env_, db_options_->statistics.get(),
|
||||
MANIFEST_FILE_SYNC_MICROS);
|
||||
s = descriptor_log_->file()->Sync();
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = SyncManifest(env_, db_options_, descriptor_log_->file());
|
||||
}
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_->info_log,
|
||||
@@ -1827,7 +1997,7 @@ void VersionSet::LogAndApplyCFHelper(VersionEdit* edit) {
|
||||
|
||||
void VersionSet::LogAndApplyHelper(ColumnFamilyData* cfd,
|
||||
VersionBuilder* builder, Version* v,
|
||||
VersionEdit* edit, port::Mutex* mu) {
|
||||
VersionEdit* edit, InstrumentedMutex* mu) {
|
||||
mu->AssertHeld();
|
||||
assert(!edit->IsColumnFamilyManipulation());
|
||||
|
||||
@@ -2085,6 +2255,9 @@ Status VersionSet::Recover(
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
auto builders_iter = builders.find(cfd->GetID());
|
||||
assert(builders_iter != builders.end());
|
||||
auto* builder = builders_iter->second->version_builder();
|
||||
@@ -2099,10 +2272,7 @@ Status VersionSet::Recover(
|
||||
builder->SaveTo(v->storage_info());
|
||||
|
||||
// Install recovered version
|
||||
std::vector<uint64_t> size_being_compacted(
|
||||
v->storage_info()->num_levels() - 1);
|
||||
cfd->compaction_picker()->SizeBeingCompacted(size_being_compacted);
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(), size_being_compacted);
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions());
|
||||
AppendVersion(cfd, v);
|
||||
}
|
||||
|
||||
@@ -2123,6 +2293,9 @@ Status VersionSet::Recover(
|
||||
column_family_set_->GetMaxColumnFamily());
|
||||
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_->info_log,
|
||||
"Column family [%s] (ID %u), log number is %" PRIu64 "\n",
|
||||
cfd->GetName().c_str(), cfd->GetID(), cfd->GetLogNumber());
|
||||
@@ -2214,9 +2387,8 @@ Status VersionSet::ReduceNumberOfLevels(const std::string& dbname,
|
||||
}
|
||||
|
||||
ColumnFamilyOptions cf_options(*options);
|
||||
std::shared_ptr<Cache> tc(NewLRUCache(
|
||||
options->max_open_files - 10, options->table_cache_numshardbits,
|
||||
options->table_cache_remove_scan_count_limit));
|
||||
std::shared_ptr<Cache> tc(NewLRUCache(options->max_open_files - 10,
|
||||
options->table_cache_numshardbits));
|
||||
WriteController wc;
|
||||
WriteBuffer wb(options->db_write_buffer_size);
|
||||
VersionSet versions(dbname, options, env_options, tc.get(), &wb, &wc);
|
||||
@@ -2281,8 +2453,8 @@ Status VersionSet::ReduceNumberOfLevels(const std::string& dbname,
|
||||
|
||||
MutableCFOptions mutable_cf_options(*options, ImmutableCFOptions(*options));
|
||||
VersionEdit ve;
|
||||
port::Mutex dummy_mutex;
|
||||
MutexLock l(&dummy_mutex);
|
||||
InstrumentedMutex dummy_mutex;
|
||||
InstrumentedMutexLock l(&dummy_mutex);
|
||||
return versions.LogAndApply(
|
||||
versions.GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options, &ve, &dummy_mutex, nullptr, true);
|
||||
@@ -2430,16 +2602,16 @@ Status VersionSet::DumpManifest(Options& options, std::string& dscname,
|
||||
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
auto builders_iter = builders.find(cfd->GetID());
|
||||
assert(builders_iter != builders.end());
|
||||
auto builder = builders_iter->second->version_builder();
|
||||
|
||||
Version* v = new Version(cfd, this, current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
std::vector<uint64_t> size_being_compacted(
|
||||
v->storage_info()->num_levels() - 1);
|
||||
cfd->compaction_picker()->SizeBeingCompacted(size_being_compacted);
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(), size_being_compacted);
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions());
|
||||
|
||||
printf("--------------- Column family \"%s\" (ID %u) --------------\n",
|
||||
cfd->GetName().c_str(), (unsigned int)cfd->GetID());
|
||||
@@ -2492,6 +2664,9 @@ Status VersionSet::WriteSnapshot(log::Writer* log) {
|
||||
// LogAndApply. Column family manipulations can only happen within LogAndApply
|
||||
// (the same single thread), so we're safe to iterate.
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
{
|
||||
// Store column family info
|
||||
VersionEdit edit;
|
||||
@@ -2630,16 +2805,21 @@ void VersionSet::AddLiveFiles(std::vector<FileDescriptor>* live_list) {
|
||||
live_list->reserve(live_list->size() + static_cast<size_t>(total_files));
|
||||
|
||||
for (auto cfd : *column_family_set_) {
|
||||
auto* current = cfd->current();
|
||||
bool found_current = false;
|
||||
Version* dummy_versions = cfd->dummy_versions();
|
||||
for (Version* v = dummy_versions->next_; v != dummy_versions;
|
||||
v = v->next_) {
|
||||
const auto* vstorage = v->storage_info();
|
||||
for (int level = 0; level < vstorage->num_levels(); level++) {
|
||||
for (const auto& f : vstorage->LevelFiles(level)) {
|
||||
live_list->push_back(f->fd);
|
||||
}
|
||||
v->AddLiveFiles(live_list);
|
||||
if (v == current) {
|
||||
found_current = true;
|
||||
}
|
||||
}
|
||||
if (!found_current && current != nullptr) {
|
||||
// Should never happen unless it is a bug.
|
||||
assert(false);
|
||||
current->AddLiveFiles(live_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2694,9 +2874,22 @@ bool VersionSet::VerifyCompactionFileConsistency(Compaction* c) {
|
||||
Version* version = c->column_family_data()->current();
|
||||
const VersionStorageInfo* vstorage = version->storage_info();
|
||||
if (c->input_version() != version) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_->info_log,
|
||||
"[%s] VerifyCompactionFileConsistency version mismatch",
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_->info_log,
|
||||
"[%s] compaction output being applied to a different base version from"
|
||||
" input version",
|
||||
c->column_family_data()->GetName().c_str());
|
||||
if (c->start_level() == 0 && c->num_input_levels() > 2U) {
|
||||
// We are doing a L0->base_level compaction. The assumption is if
|
||||
// base level is not L1, levels from L1 to base_level - 1 is empty.
|
||||
// This is ensured by having one compaction from L0 going on at the
|
||||
// same time in level-based compaction. So that during the time, no
|
||||
// compaction/flush can put files to those levels.
|
||||
for (int l = c->start_level() + 1; l < c->output_level(); l++) {
|
||||
if (vstorage->NumLevelFiles(l) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t input = 0; input < c->num_input_levels(); ++input) {
|
||||
@@ -2742,6 +2935,9 @@ Status VersionSet::GetMetadataForFile(uint64_t number, int* filelevel,
|
||||
|
||||
void VersionSet::GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) {
|
||||
for (auto cfd : *column_family_set_) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
for (int level = 0; level < cfd->NumberLevels(); level++) {
|
||||
for (const auto& file :
|
||||
cfd->current()->storage_info()->LevelFiles(level)) {
|
||||
@@ -2767,9 +2963,17 @@ void VersionSet::GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) {
|
||||
}
|
||||
}
|
||||
|
||||
void VersionSet::GetObsoleteFiles(std::vector<FileMetaData*>* files) {
|
||||
files->insert(files->end(), obsolete_files_.begin(), obsolete_files_.end());
|
||||
obsolete_files_.clear();
|
||||
void VersionSet::GetObsoleteFiles(std::vector<FileMetaData*>* files,
|
||||
uint64_t min_pending_output) {
|
||||
std::vector<FileMetaData*> pending_files;
|
||||
for (auto f : obsolete_files_) {
|
||||
if (f->fd.GetNumber() < min_pending_output) {
|
||||
files->push_back(f);
|
||||
} else {
|
||||
pending_files.push_back(f);
|
||||
}
|
||||
}
|
||||
obsolete_files_.swap(pending_files);
|
||||
}
|
||||
|
||||
ColumnFamilyData* VersionSet::CreateColumnFamily(
|
||||
@@ -2786,6 +2990,9 @@ ColumnFamilyData* VersionSet::CreateColumnFamily(
|
||||
|
||||
Version* v = new Version(new_cfd, this, current_version_number_++);
|
||||
|
||||
// Fill level target base information.
|
||||
v->storage_info()->CalculateBaseBytes(*new_cfd->ioptions(),
|
||||
*new_cfd->GetLatestMutableCFOptions());
|
||||
AppendVersion(new_cfd, v);
|
||||
// GetLatestMutableCFOptions() is safe here without mutex since the
|
||||
// cfd is not available to client
|
||||
@@ -2794,4 +3001,12 @@ ColumnFamilyData* VersionSet::CreateColumnFamily(
|
||||
return new_cfd;
|
||||
}
|
||||
|
||||
uint64_t VersionSet::GetNumLiveVersions(Version* dummy_versions) {
|
||||
uint64_t count = 0;
|
||||
for (Version* v = dummy_versions->next_; v != dummy_versions; v = v->next_) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+46
-28
@@ -36,6 +36,8 @@
|
||||
#include "db/log_reader.h"
|
||||
#include "db/file_indexer.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -62,8 +64,7 @@ class MergeIteratorBuilder;
|
||||
// REQUIRES: "file_level.files" contains a sorted list of
|
||||
// non-overlapping files.
|
||||
extern int FindFile(const InternalKeyComparator& icmp,
|
||||
const LevelFilesBrief& file_level,
|
||||
const Slice& key);
|
||||
const LevelFilesBrief& file_level, const Slice& key);
|
||||
|
||||
// Returns true iff some file in "files" overlaps the user key range
|
||||
// [*smallest,*largest].
|
||||
@@ -71,19 +72,18 @@ extern int FindFile(const InternalKeyComparator& icmp,
|
||||
// largest==nullptr represents a key largest than all keys in the DB.
|
||||
// REQUIRES: If disjoint_sorted_files, file_level.files[]
|
||||
// contains disjoint ranges in sorted order.
|
||||
extern bool SomeFileOverlapsRange(
|
||||
const InternalKeyComparator& icmp,
|
||||
bool disjoint_sorted_files,
|
||||
const LevelFilesBrief& file_level,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key);
|
||||
extern bool SomeFileOverlapsRange(const InternalKeyComparator& icmp,
|
||||
bool disjoint_sorted_files,
|
||||
const LevelFilesBrief& file_level,
|
||||
const Slice* smallest_user_key,
|
||||
const Slice* largest_user_key);
|
||||
|
||||
// Generate LevelFilesBrief from vector<FdWithKeyRange*>
|
||||
// Would copy smallest_key and largest_key data to sequential memory
|
||||
// arena: Arena used to allocate the memory
|
||||
extern void DoGenerateLevelFilesBrief(LevelFilesBrief* file_level,
|
||||
const std::vector<FileMetaData*>& files,
|
||||
Arena* arena);
|
||||
const std::vector<FileMetaData*>& files,
|
||||
Arena* arena);
|
||||
|
||||
class VersionStorageInfo {
|
||||
public:
|
||||
@@ -97,7 +97,7 @@ class VersionStorageInfo {
|
||||
|
||||
void AddFile(int level, FileMetaData* f);
|
||||
|
||||
void SetFinalized() { finalized_ = true; }
|
||||
void SetFinalized();
|
||||
|
||||
// Update num_non_empty_levels_.
|
||||
void UpdateNumNonEmptyLevels();
|
||||
@@ -113,13 +113,11 @@ class VersionStorageInfo {
|
||||
|
||||
// Updates internal structures that keep track of compaction scores
|
||||
// We use compaction scores to figure out which compaction to do next
|
||||
// REQUIRES: If Version is not yet saved to current_, it can be called without
|
||||
// a lock. Once a version is saved to current_, call only with mutex held
|
||||
// REQUIRES: db_mutex held!!
|
||||
// TODO find a better way to pass compaction_options_fifo.
|
||||
void ComputeCompactionScore(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const CompactionOptionsFIFO& compaction_options_fifo,
|
||||
std::vector<uint64_t>& size_being_compacted);
|
||||
const CompactionOptionsFIFO& compaction_options_fifo);
|
||||
|
||||
// Generate level_files_brief_ from files_
|
||||
void GenerateLevelFilesBrief();
|
||||
@@ -149,15 +147,17 @@ class VersionStorageInfo {
|
||||
int* file_index = nullptr); // return index of overlap file
|
||||
|
||||
void GetOverlappingInputsBinarySearch(
|
||||
int level, const Slice& begin, // nullptr means before all keys
|
||||
const Slice& end, // nullptr means after all keys
|
||||
int level,
|
||||
const Slice& begin, // nullptr means before all keys
|
||||
const Slice& end, // nullptr means after all keys
|
||||
std::vector<FileMetaData*>* inputs,
|
||||
int hint_index, // index of overlap file
|
||||
int* file_index); // return index of overlap file
|
||||
|
||||
void ExtendOverlappingInputs(
|
||||
int level, const Slice& begin, // nullptr means before all keys
|
||||
const Slice& end, // nullptr means after all keys
|
||||
int level,
|
||||
const Slice& begin, // nullptr means before all keys
|
||||
const Slice& end, // nullptr means after all keys
|
||||
std::vector<FileMetaData*>* inputs,
|
||||
unsigned int index); // start extending from this index
|
||||
|
||||
@@ -214,6 +214,8 @@ class VersionStorageInfo {
|
||||
return files_by_size_[level];
|
||||
}
|
||||
|
||||
int base_level() const { return base_level_; }
|
||||
|
||||
// REQUIRES: lock is held
|
||||
// Set the index that is used to offset into files_by_size_ to find
|
||||
// the next compaction candidate file.
|
||||
@@ -282,12 +284,22 @@ class VersionStorageInfo {
|
||||
return internal_comparator_;
|
||||
}
|
||||
|
||||
// Returns maximum total bytes of data on a given level.
|
||||
uint64_t MaxBytesForLevel(int level) const;
|
||||
|
||||
// Must be called after any change to MutableCFOptions.
|
||||
void CalculateBaseBytes(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& options);
|
||||
|
||||
private:
|
||||
const InternalKeyComparator* internal_comparator_;
|
||||
const Comparator* user_comparator_;
|
||||
int num_levels_; // Number of levels
|
||||
int num_non_empty_levels_; // Number of levels. Any level larger than it
|
||||
// is guaranteed to be empty.
|
||||
// Per-level max bytes
|
||||
std::vector<uint64_t> level_max_bytes_;
|
||||
|
||||
// A short brief metadata of files per level
|
||||
autovector<rocksdb::LevelFilesBrief> level_files_brief_;
|
||||
FileIndexer file_indexer_;
|
||||
@@ -299,6 +311,10 @@ class VersionStorageInfo {
|
||||
// in increasing order of keys
|
||||
std::vector<FileMetaData*>* files_;
|
||||
|
||||
// Level that L0 data should be compacted to. All levels < base_level_ should
|
||||
// be empty.
|
||||
int base_level_;
|
||||
|
||||
// A list for the same set of files that are stored in files_,
|
||||
// but files in each level are now sorted based on file
|
||||
// size. The file with the largest size is at the front.
|
||||
@@ -365,10 +381,9 @@ class Version {
|
||||
Status* status, MergeContext* merge_context,
|
||||
bool* value_found = nullptr);
|
||||
|
||||
// Update scores, pre-calculated variables. It needs to be called before
|
||||
// applying the version to the version set.
|
||||
void PrepareApply(const MutableCFOptions& mutable_cf_options,
|
||||
std::vector<uint64_t>& size_being_compacted);
|
||||
// Loads some stats information from files. Call without mutex held. It needs
|
||||
// to be called before applying the version to the version set.
|
||||
void PrepareApply(const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Reference count management (so Versions do not disappear out from
|
||||
// under live iterators)
|
||||
@@ -409,7 +424,6 @@ class Version {
|
||||
|
||||
ColumnFamilyData* cfd() const { return cfd_; }
|
||||
|
||||
|
||||
// Return the next Version in the linked list. Used for debug only
|
||||
Version* TEST_Next() const {
|
||||
return next_;
|
||||
@@ -422,6 +436,7 @@ class Version {
|
||||
void GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta);
|
||||
|
||||
private:
|
||||
Env* env_;
|
||||
friend class VersionSet;
|
||||
|
||||
const InternalKeyComparator* internal_comparator() const {
|
||||
@@ -488,7 +503,7 @@ class VersionSet {
|
||||
Status LogAndApply(
|
||||
ColumnFamilyData* column_family_data,
|
||||
const MutableCFOptions& mutable_cf_options, VersionEdit* edit,
|
||||
port::Mutex* mu, Directory* db_directory = nullptr,
|
||||
InstrumentedMutex* mu, Directory* db_directory = nullptr,
|
||||
bool new_descriptor_log = false,
|
||||
const ColumnFamilyOptions* column_family_options = nullptr);
|
||||
|
||||
@@ -592,11 +607,14 @@ class VersionSet {
|
||||
|
||||
void GetLiveFilesMetaData(std::vector<LiveFileMetaData> *metadata);
|
||||
|
||||
void GetObsoleteFiles(std::vector<FileMetaData*>* files);
|
||||
void GetObsoleteFiles(std::vector<FileMetaData*>* files,
|
||||
uint64_t min_pending_output);
|
||||
|
||||
ColumnFamilySet* GetColumnFamilySet() { return column_family_set_.get(); }
|
||||
const EnvOptions& env_options() { return env_options_; }
|
||||
|
||||
static uint64_t GetNumLiveVersions(Version* dummy_versions);
|
||||
|
||||
private:
|
||||
struct ManifestWriter;
|
||||
|
||||
@@ -605,7 +623,7 @@ class VersionSet {
|
||||
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Status* status;
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
virtual void Corruption(size_t bytes, const Status& s) override {
|
||||
if (this->status->ok()) *this->status = s;
|
||||
}
|
||||
};
|
||||
@@ -659,7 +677,7 @@ class VersionSet {
|
||||
|
||||
void LogAndApplyCFHelper(VersionEdit* edit);
|
||||
void LogAndApplyHelper(ColumnFamilyData* cfd, VersionBuilder* b, Version* v,
|
||||
VersionEdit* edit, port::Mutex* mu);
|
||||
VersionEdit* edit, InstrumentedMutex* mu);
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+175
-13
@@ -14,7 +14,7 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class GenerateLevelFilesBriefTest {
|
||||
class GenerateLevelFilesBriefTest : public testing::Test {
|
||||
public:
|
||||
std::vector<FileMetaData*> files_;
|
||||
LevelFilesBrief file_level_;
|
||||
@@ -49,21 +49,20 @@ class GenerateLevelFilesBriefTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST(GenerateLevelFilesBriefTest, Empty) {
|
||||
TEST_F(GenerateLevelFilesBriefTest, Empty) {
|
||||
DoGenerateLevelFilesBrief(&file_level_, files_, &arena_);
|
||||
ASSERT_EQ(0u, file_level_.num_files);
|
||||
ASSERT_EQ(0, Compare());
|
||||
}
|
||||
|
||||
TEST(GenerateLevelFilesBriefTest, Single) {
|
||||
TEST_F(GenerateLevelFilesBriefTest, Single) {
|
||||
Add("p", "q");
|
||||
DoGenerateLevelFilesBrief(&file_level_, files_, &arena_);
|
||||
ASSERT_EQ(1u, file_level_.num_files);
|
||||
ASSERT_EQ(0, Compare());
|
||||
}
|
||||
|
||||
|
||||
TEST(GenerateLevelFilesBriefTest, Multiple) {
|
||||
TEST_F(GenerateLevelFilesBriefTest, Multiple) {
|
||||
Add("150", "200");
|
||||
Add("200", "250");
|
||||
Add("300", "350");
|
||||
@@ -73,7 +72,169 @@ TEST(GenerateLevelFilesBriefTest, Multiple) {
|
||||
ASSERT_EQ(0, Compare());
|
||||
}
|
||||
|
||||
class FindLevelFileTest {
|
||||
class CountingLogger : public Logger {
|
||||
public:
|
||||
CountingLogger() : log_count(0) {}
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char* format, va_list ap) override { log_count++; }
|
||||
int log_count;
|
||||
};
|
||||
|
||||
Options GetOptionsWithNumLevels(int num_levels,
|
||||
std::shared_ptr<CountingLogger> logger) {
|
||||
Options opt;
|
||||
opt.num_levels = num_levels;
|
||||
opt.info_log = logger;
|
||||
return opt;
|
||||
}
|
||||
|
||||
class VersionStorageInfoTest : public testing::Test {
|
||||
public:
|
||||
const Comparator* ucmp_;
|
||||
InternalKeyComparator icmp_;
|
||||
std::shared_ptr<CountingLogger> logger_;
|
||||
Options options_;
|
||||
ImmutableCFOptions ioptions_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
VersionStorageInfo vstorage_;
|
||||
|
||||
InternalKey GetInternalKey(const char* ukey,
|
||||
SequenceNumber smallest_seq = 100) {
|
||||
return InternalKey(ukey, smallest_seq, kTypeValue);
|
||||
}
|
||||
|
||||
VersionStorageInfoTest()
|
||||
: ucmp_(BytewiseComparator()),
|
||||
icmp_(ucmp_),
|
||||
logger_(new CountingLogger()),
|
||||
options_(GetOptionsWithNumLevels(6, logger_)),
|
||||
ioptions_(options_),
|
||||
mutable_cf_options_(options_, ioptions_),
|
||||
vstorage_(&icmp_, ucmp_, 6, kCompactionStyleLevel, nullptr) {}
|
||||
|
||||
~VersionStorageInfoTest() {
|
||||
for (int i = 0; i < vstorage_.num_levels(); i++) {
|
||||
for (auto* f : vstorage_.LevelFiles(i)) {
|
||||
if (--f->refs == 0) {
|
||||
delete f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 0) {
|
||||
assert(level < vstorage_.num_levels());
|
||||
FileMetaData* f = new FileMetaData;
|
||||
f->fd = FileDescriptor(file_number, 0, file_size);
|
||||
f->smallest = GetInternalKey(smallest, 0);
|
||||
f->largest = GetInternalKey(largest, 0);
|
||||
f->compensated_file_size = file_size;
|
||||
f->refs = 0;
|
||||
f->num_entries = 0;
|
||||
f->num_deletions = 0;
|
||||
vstorage_.AddFile(level, f);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(VersionStorageInfoTest, MaxBytesForLevelStatic) {
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 5;
|
||||
Add(4, 100U, "1", "2");
|
||||
Add(5, 101U, "1", "2");
|
||||
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 10U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 50U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 250U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1250U);
|
||||
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
}
|
||||
|
||||
TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamic) {
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 5;
|
||||
Add(5, 1U, "1", "2", 500U);
|
||||
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
ASSERT_EQ(vstorage_.base_level(), 5);
|
||||
|
||||
Add(5, 2U, "3", "4", 550U);
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 210U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 4);
|
||||
|
||||
Add(4, 3U, "3", "4", 550U);
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 210U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 4);
|
||||
|
||||
Add(3, 4U, "3", "4", 250U);
|
||||
Add(3, 5U, "5", "7", 300U);
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(1, logger_->log_count);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 1005U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 201U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 3);
|
||||
|
||||
Add(1, 6U, "3", "4", 5U);
|
||||
Add(1, 7U, "8", "9", 5U);
|
||||
logger_->log_count = 0;
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(1, logger_->log_count);
|
||||
ASSERT_GT(vstorage_.MaxBytesForLevel(4), 1005U);
|
||||
ASSERT_GT(vstorage_.MaxBytesForLevel(3), 1005U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 1005U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 201U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 1);
|
||||
}
|
||||
|
||||
TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicLotsOfData) {
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 100;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 2;
|
||||
Add(0, 1U, "1", "2", 50U);
|
||||
Add(1, 2U, "1", "2", 50U);
|
||||
Add(2, 3U, "1", "2", 500U);
|
||||
Add(3, 4U, "1", "2", 500U);
|
||||
Add(4, 5U, "1", "2", 1700U);
|
||||
Add(5, 6U, "1", "2", 500U);
|
||||
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 800U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 400U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 200U);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(1), 100U);
|
||||
ASSERT_EQ(vstorage_.base_level(), 1);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
}
|
||||
|
||||
TEST_F(VersionStorageInfoTest, MaxBytesForLevelDynamicLargeLevel) {
|
||||
uint64_t kOneGB = 1000U * 1000U * 1000U;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10U * kOneGB;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
Add(0, 1U, "1", "2", 50U);
|
||||
Add(3, 4U, "1", "2", 32U * kOneGB);
|
||||
Add(4, 5U, "1", "2", 500U * kOneGB);
|
||||
Add(5, 6U, "1", "2", 3000U * kOneGB);
|
||||
|
||||
vstorage_.CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(5), 3000U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(4), 300U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(3), 30U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.MaxBytesForLevel(2), 3U * kOneGB);
|
||||
ASSERT_EQ(vstorage_.base_level(), 2);
|
||||
ASSERT_EQ(0, logger_->log_count);
|
||||
}
|
||||
|
||||
class FindLevelFileTest : public testing::Test {
|
||||
public:
|
||||
LevelFilesBrief file_level_;
|
||||
bool disjoint_sorted_files_;
|
||||
@@ -131,7 +292,7 @@ class FindLevelFileTest {
|
||||
}
|
||||
};
|
||||
|
||||
TEST(FindLevelFileTest, LevelEmpty) {
|
||||
TEST_F(FindLevelFileTest, LevelEmpty) {
|
||||
LevelFileInit(0);
|
||||
|
||||
ASSERT_EQ(0, Find("foo"));
|
||||
@@ -141,7 +302,7 @@ TEST(FindLevelFileTest, LevelEmpty) {
|
||||
ASSERT_TRUE(! Overlaps(nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST(FindLevelFileTest, LevelSingle) {
|
||||
TEST_F(FindLevelFileTest, LevelSingle) {
|
||||
LevelFileInit(1);
|
||||
|
||||
Add("p", "q");
|
||||
@@ -173,7 +334,7 @@ TEST(FindLevelFileTest, LevelSingle) {
|
||||
ASSERT_TRUE(Overlaps(nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST(FindLevelFileTest, LevelMultiple) {
|
||||
TEST_F(FindLevelFileTest, LevelMultiple) {
|
||||
LevelFileInit(4);
|
||||
|
||||
Add("150", "200");
|
||||
@@ -213,7 +374,7 @@ TEST(FindLevelFileTest, LevelMultiple) {
|
||||
ASSERT_TRUE(Overlaps("450", "500"));
|
||||
}
|
||||
|
||||
TEST(FindLevelFileTest, LevelMultipleNullBoundaries) {
|
||||
TEST_F(FindLevelFileTest, LevelMultipleNullBoundaries) {
|
||||
LevelFileInit(4);
|
||||
|
||||
Add("150", "200");
|
||||
@@ -235,7 +396,7 @@ TEST(FindLevelFileTest, LevelMultipleNullBoundaries) {
|
||||
ASSERT_TRUE(Overlaps("450", nullptr));
|
||||
}
|
||||
|
||||
TEST(FindLevelFileTest, LevelOverlapSequenceChecks) {
|
||||
TEST_F(FindLevelFileTest, LevelOverlapSequenceChecks) {
|
||||
LevelFileInit(1);
|
||||
|
||||
Add("200", "200", 5000, 3000);
|
||||
@@ -246,7 +407,7 @@ TEST(FindLevelFileTest, LevelOverlapSequenceChecks) {
|
||||
ASSERT_TRUE(Overlaps("200", "210"));
|
||||
}
|
||||
|
||||
TEST(FindLevelFileTest, LevelOverlappingFiles) {
|
||||
TEST_F(FindLevelFileTest, LevelOverlappingFiles) {
|
||||
LevelFileInit(2);
|
||||
|
||||
Add("150", "600");
|
||||
@@ -269,5 +430,6 @@ TEST(FindLevelFileTest, LevelOverlappingFiles) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+16
-3
@@ -310,9 +310,15 @@ Status WalManager::GetSortedWalsOfType(const std::string& path,
|
||||
uint64_t size_bytes;
|
||||
s = env_->GetFileSize(LogFileName(path, number), &size_bytes);
|
||||
// re-try in case the alive log file has been moved to archive.
|
||||
std::string archived_file = ArchivedLogFileName(path, number);
|
||||
if (!s.ok() && log_type == kAliveLogFile &&
|
||||
env_->FileExists(ArchivedLogFileName(path, number))) {
|
||||
s = env_->GetFileSize(ArchivedLogFileName(path, number), &size_bytes);
|
||||
env_->FileExists(archived_file)) {
|
||||
s = env_->GetFileSize(archived_file, &size_bytes);
|
||||
if (!s.ok() && !env_->FileExists(archived_file)) {
|
||||
// oops, the file just got deleted from archived dir! move on
|
||||
s = Status::OK();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -354,6 +360,7 @@ Status WalManager::RetainProbableWalFiles(VectorLogPtr& all_logs,
|
||||
Status WalManager::ReadFirstRecord(const WalFileType type,
|
||||
const uint64_t number,
|
||||
SequenceNumber* sequence) {
|
||||
*sequence = 0;
|
||||
if (type != kAliveLogFile && type != kArchivedLogFile) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
"[WalManger] Unknown file type %s", ToString(type).c_str());
|
||||
@@ -383,6 +390,12 @@ Status WalManager::ReadFirstRecord(const WalFileType type,
|
||||
std::string archived_file =
|
||||
ArchivedLogFileName(db_options_.wal_dir, number);
|
||||
s = ReadFirstLine(archived_file, sequence);
|
||||
// maybe the file was deleted from archive dir. If that's the case, return
|
||||
// Status::OK(). The caller with identify this as empty file because
|
||||
// *sequence == 0
|
||||
if (!s.ok() && !env_->FileExists(archived_file)) {
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok() && *sequence != 0) {
|
||||
@@ -403,7 +416,7 @@ Status WalManager::ReadFirstLine(const std::string& fname,
|
||||
|
||||
Status* status;
|
||||
bool ignore_error; // true if db_options_.paranoid_checks==false
|
||||
virtual void Corruption(size_t bytes, const Status& s) {
|
||||
virtual void Corruption(size_t bytes, const Status& s) override {
|
||||
Log(InfoLogLevel::WARN_LEVEL, info_log,
|
||||
"[WalManager] %s%s: dropping %d bytes; %s",
|
||||
(this->ignore_error ? "(ignoring error) " : ""), fname,
|
||||
|
||||
+15
-11
@@ -14,6 +14,7 @@
|
||||
#include "db/column_family.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
#include "table/mock_table.h"
|
||||
@@ -23,12 +24,12 @@ namespace rocksdb {
|
||||
|
||||
// TODO(icanadi) mock out VersionSet
|
||||
// TODO(icanadi) move other WalManager-specific tests from db_test here
|
||||
class WalManagerTest {
|
||||
class WalManagerTest : public testing::Test {
|
||||
public:
|
||||
WalManagerTest()
|
||||
: env_(Env::Default()),
|
||||
dbname_(test::TmpDir() + "/wal_manager_test"),
|
||||
table_cache_(NewLRUCache(50000, 16, 8)),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_(db_options_.db_write_buffer_size),
|
||||
current_log_number_(0) {
|
||||
DestroyDB(dbname_, Options());
|
||||
@@ -86,7 +87,7 @@ class WalManagerTest {
|
||||
unique_ptr<TransactionLogIterator> iter;
|
||||
Status status = wal_manager_->GetUpdatesSince(
|
||||
seq, &iter, TransactionLogIterator::ReadOptions(), versions_.get());
|
||||
ASSERT_OK(status);
|
||||
EXPECT_OK(status);
|
||||
return std::move(iter);
|
||||
}
|
||||
|
||||
@@ -104,7 +105,7 @@ class WalManagerTest {
|
||||
uint64_t current_log_number_;
|
||||
};
|
||||
|
||||
TEST(WalManagerTest, ReadFirstRecordCache) {
|
||||
TEST_F(WalManagerTest, ReadFirstRecordCache) {
|
||||
Init();
|
||||
std::string path = dbname_ + "/000001.log";
|
||||
unique_ptr<WritableFile> file;
|
||||
@@ -182,17 +183,17 @@ int CountRecords(TransactionLogIterator* iter) {
|
||||
BatchResult res;
|
||||
while (iter->Valid()) {
|
||||
res = iter->GetBatch();
|
||||
ASSERT_TRUE(res.sequence > lastSequence);
|
||||
EXPECT_TRUE(res.sequence > lastSequence);
|
||||
++count;
|
||||
lastSequence = res.sequence;
|
||||
ASSERT_OK(iter->status());
|
||||
EXPECT_OK(iter->status());
|
||||
iter->Next();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(WalManagerTest, WALArchivalSizeLimit) {
|
||||
TEST_F(WalManagerTest, WALArchivalSizeLimit) {
|
||||
db_options_.WAL_ttl_seconds = 0;
|
||||
db_options_.WAL_size_limit_MB = 1000;
|
||||
Init();
|
||||
@@ -230,7 +231,7 @@ TEST(WalManagerTest, WALArchivalSizeLimit) {
|
||||
ASSERT_TRUE(log_files.empty());
|
||||
}
|
||||
|
||||
TEST(WalManagerTest, WALArchivalTtl) {
|
||||
TEST_F(WalManagerTest, WALArchivalTtl) {
|
||||
db_options_.WAL_ttl_seconds = 1000;
|
||||
Init();
|
||||
|
||||
@@ -256,7 +257,7 @@ TEST(WalManagerTest, WALArchivalTtl) {
|
||||
ASSERT_TRUE(log_files.empty());
|
||||
}
|
||||
|
||||
TEST(WalManagerTest, TransactionLogIteratorMoveOverZeroFiles) {
|
||||
TEST_F(WalManagerTest, TransactionLogIteratorMoveOverZeroFiles) {
|
||||
Init();
|
||||
RollTheLog(false);
|
||||
Put("key1", std::string(1024, 'a'));
|
||||
@@ -270,7 +271,7 @@ TEST(WalManagerTest, TransactionLogIteratorMoveOverZeroFiles) {
|
||||
ASSERT_EQ(2, CountRecords(iter.get()));
|
||||
}
|
||||
|
||||
TEST(WalManagerTest, TransactionLogIteratorJustEmptyFile) {
|
||||
TEST_F(WalManagerTest, TransactionLogIteratorJustEmptyFile) {
|
||||
Init();
|
||||
RollTheLog(false);
|
||||
auto iter = OpenTransactionLogIter(0);
|
||||
@@ -280,4 +281,7 @@ TEST(WalManagerTest, TransactionLogIteratorJustEmptyFile) {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+25
-5
@@ -33,6 +33,7 @@
|
||||
#include "util/coding.h"
|
||||
#include "util/statistics.h"
|
||||
#include <stdexcept>
|
||||
#include "util/perf_context_imp.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -280,6 +281,8 @@ void WriteBatch::PutLogData(const Slice& blob) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
// This class can *only* be used from a single-threaded write thread, because it
|
||||
// calls ColumnFamilyMemTablesImpl::Seek()
|
||||
class MemTableInserter : public WriteBatch::Handler {
|
||||
public:
|
||||
SequenceNumber sequence_;
|
||||
@@ -305,6 +308,8 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
bool SeekToColumnFamily(uint32_t column_family_id, Status* s) {
|
||||
// We are only allowed to call this from a single-threaded write thread
|
||||
// (or while holding DB mutex)
|
||||
bool found = cf_mems_->Seek(column_family_id);
|
||||
if (!found) {
|
||||
if (ignore_missing_column_families_) {
|
||||
@@ -328,7 +333,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return true;
|
||||
}
|
||||
virtual Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) {
|
||||
const Slice& value) override {
|
||||
Status seek_status;
|
||||
if (!SeekToColumnFamily(column_family_id, &seek_status)) {
|
||||
++sequence_;
|
||||
@@ -384,7 +389,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
virtual Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) {
|
||||
const Slice& value) override {
|
||||
Status seek_status;
|
||||
if (!SeekToColumnFamily(column_family_id, &seek_status)) {
|
||||
++sequence_;
|
||||
@@ -431,8 +436,17 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
std::deque<std::string> operands;
|
||||
operands.push_front(value.ToString());
|
||||
std::string new_value;
|
||||
if (!merge_operator->FullMerge(key, &get_value_slice, operands,
|
||||
&new_value, moptions->info_log)) {
|
||||
bool merge_success = false;
|
||||
{
|
||||
StopWatchNano timer(Env::Default(), moptions->statistics != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
merge_success = merge_operator->FullMerge(
|
||||
key, &get_value_slice, operands, &new_value, moptions->info_log);
|
||||
RecordTick(moptions->statistics, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
}
|
||||
|
||||
if (!merge_success) {
|
||||
// Failed to merge!
|
||||
RecordTick(moptions->statistics, NUMBER_MERGE_FAILURES);
|
||||
|
||||
@@ -454,7 +468,8 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual Status DeleteCF(uint32_t column_family_id, const Slice& key) {
|
||||
virtual Status DeleteCF(uint32_t column_family_id,
|
||||
const Slice& key) override {
|
||||
Status seek_status;
|
||||
if (!SeekToColumnFamily(column_family_id, &seek_status)) {
|
||||
++sequence_;
|
||||
@@ -485,6 +500,11 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// This function can only be called in these conditions:
|
||||
// 1) During Recovery()
|
||||
// 2) during Write(), in a single-threaded write thread
|
||||
// The reason is that it calles ColumnFamilyMemTablesImpl::Seek(), which needs
|
||||
// to be called from a single-threaded write thread (or while holding DB mutex)
|
||||
Status WriteBatchInternal::InsertInto(const WriteBatch* b,
|
||||
ColumnFamilyMemTables* memtables,
|
||||
bool ignore_missing_column_families,
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2015, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "rocksdb/write_batch_base.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Simple implementation of SlicePart variants of Put(). Child classes
|
||||
// can override these method with more performant solutions if they choose.
|
||||
void WriteBatchBase::Put(ColumnFamilyHandle* column_family,
|
||||
const SliceParts& key, const SliceParts& value) {
|
||||
std::string key_buf, value_buf;
|
||||
Slice key_slice(key, &key_buf);
|
||||
Slice value_slice(value, &value_buf);
|
||||
|
||||
Put(column_family, key_slice, value_slice);
|
||||
}
|
||||
|
||||
void WriteBatchBase::Put(const SliceParts& key, const SliceParts& value) {
|
||||
std::string key_buf, value_buf;
|
||||
Slice key_slice(key, &key_buf);
|
||||
Slice value_slice(value, &value_buf);
|
||||
|
||||
Put(key_slice, value_slice);
|
||||
}
|
||||
|
||||
void WriteBatchBase::Delete(ColumnFamilyHandle* column_family,
|
||||
const SliceParts& key) {
|
||||
std::string key_buf;
|
||||
Slice key_slice(key, &key_buf);
|
||||
Delete(column_family, key_slice);
|
||||
}
|
||||
|
||||
void WriteBatchBase::Delete(const SliceParts& key) {
|
||||
std::string key_buf;
|
||||
Slice key_slice(key, &key_buf);
|
||||
Delete(key_slice);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
+27
-23
@@ -18,6 +18,7 @@
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/scoped_arena_iterator.h"
|
||||
|
||||
@@ -42,7 +43,7 @@ static std::string PrintContents(WriteBatch* b) {
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ParsedInternalKey ikey;
|
||||
memset((void *)&ikey, 0, sizeof(ikey));
|
||||
ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey));
|
||||
EXPECT_TRUE(ParseInternalKey(iter->key(), &ikey));
|
||||
switch (ikey.type) {
|
||||
case kTypeValue:
|
||||
state.append("Put(");
|
||||
@@ -82,16 +83,16 @@ static std::string PrintContents(WriteBatch* b) {
|
||||
return state;
|
||||
}
|
||||
|
||||
class WriteBatchTest { };
|
||||
class WriteBatchTest : public testing::Test {};
|
||||
|
||||
TEST(WriteBatchTest, Empty) {
|
||||
TEST_F(WriteBatchTest, Empty) {
|
||||
WriteBatch batch;
|
||||
ASSERT_EQ("", PrintContents(&batch));
|
||||
ASSERT_EQ(0, WriteBatchInternal::Count(&batch));
|
||||
ASSERT_EQ(0, batch.Count());
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, Multiple) {
|
||||
TEST_F(WriteBatchTest, Multiple) {
|
||||
WriteBatch batch;
|
||||
batch.Put(Slice("foo"), Slice("bar"));
|
||||
batch.Delete(Slice("box"));
|
||||
@@ -106,7 +107,7 @@ TEST(WriteBatchTest, Multiple) {
|
||||
ASSERT_EQ(3, batch.Count());
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, Corruption) {
|
||||
TEST_F(WriteBatchTest, Corruption) {
|
||||
WriteBatch batch;
|
||||
batch.Put(Slice("foo"), Slice("bar"));
|
||||
batch.Delete(Slice("box"));
|
||||
@@ -119,7 +120,7 @@ TEST(WriteBatchTest, Corruption) {
|
||||
PrintContents(&batch));
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, Append) {
|
||||
TEST_F(WriteBatchTest, Append) {
|
||||
WriteBatch b1, b2;
|
||||
WriteBatchInternal::SetSequence(&b1, 200);
|
||||
WriteBatchInternal::SetSequence(&b2, 300);
|
||||
@@ -153,7 +154,7 @@ namespace {
|
||||
struct TestHandler : public WriteBatch::Handler {
|
||||
std::string seen;
|
||||
virtual Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) {
|
||||
const Slice& value) override {
|
||||
if (column_family_id == 0) {
|
||||
seen += "Put(" + key.ToString() + ", " + value.ToString() + ")";
|
||||
} else {
|
||||
@@ -163,7 +164,7 @@ namespace {
|
||||
return Status::OK();
|
||||
}
|
||||
virtual Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) {
|
||||
const Slice& value) override {
|
||||
if (column_family_id == 0) {
|
||||
seen += "Merge(" + key.ToString() + ", " + value.ToString() + ")";
|
||||
} else {
|
||||
@@ -172,10 +173,11 @@ namespace {
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
virtual void LogData(const Slice& blob) {
|
||||
virtual void LogData(const Slice& blob) override {
|
||||
seen += "LogData(" + blob.ToString() + ")";
|
||||
}
|
||||
virtual Status DeleteCF(uint32_t column_family_id, const Slice& key) {
|
||||
virtual Status DeleteCF(uint32_t column_family_id,
|
||||
const Slice& key) override {
|
||||
if (column_family_id == 0) {
|
||||
seen += "Delete(" + key.ToString() + ")";
|
||||
} else {
|
||||
@@ -187,7 +189,7 @@ namespace {
|
||||
};
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, MergeNotImplemented) {
|
||||
TEST_F(WriteBatchTest, MergeNotImplemented) {
|
||||
WriteBatch batch;
|
||||
batch.Merge(Slice("foo"), Slice("bar"));
|
||||
ASSERT_EQ(1, batch.Count());
|
||||
@@ -198,7 +200,7 @@ TEST(WriteBatchTest, MergeNotImplemented) {
|
||||
ASSERT_OK(batch.Iterate(&handler));
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, PutNotImplemented) {
|
||||
TEST_F(WriteBatchTest, PutNotImplemented) {
|
||||
WriteBatch batch;
|
||||
batch.Put(Slice("k1"), Slice("v1"));
|
||||
ASSERT_EQ(1, batch.Count());
|
||||
@@ -209,7 +211,7 @@ TEST(WriteBatchTest, PutNotImplemented) {
|
||||
ASSERT_OK(batch.Iterate(&handler));
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, DeleteNotImplemented) {
|
||||
TEST_F(WriteBatchTest, DeleteNotImplemented) {
|
||||
WriteBatch batch;
|
||||
batch.Delete(Slice("k2"));
|
||||
ASSERT_EQ(1, batch.Count());
|
||||
@@ -220,7 +222,7 @@ TEST(WriteBatchTest, DeleteNotImplemented) {
|
||||
ASSERT_OK(batch.Iterate(&handler));
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, Blob) {
|
||||
TEST_F(WriteBatchTest, Blob) {
|
||||
WriteBatch batch;
|
||||
batch.Put(Slice("k1"), Slice("v1"));
|
||||
batch.Put(Slice("k2"), Slice("v2"));
|
||||
@@ -250,26 +252,27 @@ TEST(WriteBatchTest, Blob) {
|
||||
handler.seen);
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, Continue) {
|
||||
TEST_F(WriteBatchTest, Continue) {
|
||||
WriteBatch batch;
|
||||
|
||||
struct Handler : public TestHandler {
|
||||
int num_seen = 0;
|
||||
virtual Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) {
|
||||
const Slice& value) override {
|
||||
++num_seen;
|
||||
return TestHandler::PutCF(column_family_id, key, value);
|
||||
}
|
||||
virtual Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) {
|
||||
const Slice& value) override {
|
||||
++num_seen;
|
||||
return TestHandler::MergeCF(column_family_id, key, value);
|
||||
}
|
||||
virtual void LogData(const Slice& blob) {
|
||||
virtual void LogData(const Slice& blob) override {
|
||||
++num_seen;
|
||||
TestHandler::LogData(blob);
|
||||
}
|
||||
virtual Status DeleteCF(uint32_t column_family_id, const Slice& key) {
|
||||
virtual Status DeleteCF(uint32_t column_family_id,
|
||||
const Slice& key) override {
|
||||
++num_seen;
|
||||
return TestHandler::DeleteCF(column_family_id, key);
|
||||
}
|
||||
@@ -291,7 +294,7 @@ TEST(WriteBatchTest, Continue) {
|
||||
handler.seen);
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, PutGatherSlices) {
|
||||
TEST_F(WriteBatchTest, PutGatherSlices) {
|
||||
WriteBatch batch;
|
||||
batch.Put(Slice("foo"), Slice("bar"));
|
||||
|
||||
@@ -334,7 +337,7 @@ class ColumnFamilyHandleImplDummy : public ColumnFamilyHandleImpl {
|
||||
};
|
||||
} // namespace anonymous
|
||||
|
||||
TEST(WriteBatchTest, ColumnFamiliesBatchTest) {
|
||||
TEST_F(WriteBatchTest, ColumnFamiliesBatchTest) {
|
||||
WriteBatch batch;
|
||||
ColumnFamilyHandleImplDummy zero(0), two(2), three(3), eight(8);
|
||||
batch.Put(&zero, Slice("foo"), Slice("bar"));
|
||||
@@ -358,7 +361,7 @@ TEST(WriteBatchTest, ColumnFamiliesBatchTest) {
|
||||
handler.seen);
|
||||
}
|
||||
|
||||
TEST(WriteBatchTest, ColumnFamiliesBatchWithIndexTest) {
|
||||
TEST_F(WriteBatchTest, ColumnFamiliesBatchWithIndexTest) {
|
||||
WriteBatchWithIndex batch;
|
||||
ColumnFamilyHandleImplDummy zero(0), two(2), three(3), eight(8);
|
||||
batch.Put(&zero, Slice("foo"), Slice("bar"));
|
||||
@@ -443,5 +446,6 @@ TEST(WriteBatchTest, ColumnFamiliesBatchWithIndexTest) {
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return rocksdb::test::RunAllTests();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class WriteControllerTest {};
|
||||
class WriteControllerTest : public testing::Test {};
|
||||
|
||||
TEST(WriteControllerTest, SanityTest) {
|
||||
TEST_F(WriteControllerTest, SanityTest) {
|
||||
WriteController controller;
|
||||
auto stop_token_1 = controller.GetStopToken();
|
||||
auto stop_token_2 = controller.GetStopToken();
|
||||
@@ -37,4 +37,7 @@ TEST(WriteControllerTest, SanityTest) {
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); }
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+3
-2
@@ -12,6 +12,7 @@
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "util/autovector.h"
|
||||
#include "port/port.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -27,9 +28,9 @@ class WriteThread {
|
||||
bool in_batch_group;
|
||||
bool done;
|
||||
uint64_t timeout_hint_us;
|
||||
port::CondVar cv;
|
||||
InstrumentedCondVar cv;
|
||||
|
||||
explicit Writer(port::Mutex* mu)
|
||||
explicit Writer(InstrumentedMutex* mu)
|
||||
: batch(nullptr),
|
||||
sync(false),
|
||||
disableWAL(false),
|
||||
|
||||
Loaded 100 of 459 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user