mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4be3b1c3e5 | ||
|
|
f54a9a6e7f | ||
|
|
eb84ad3151 | ||
|
|
50c552c5bc | ||
|
|
5b74443720 | ||
|
|
073cdbaa49 | ||
|
|
e52d6ad227 | ||
|
|
25ea289819 | ||
|
|
4dd267d497 | ||
|
|
32be357ef7 | ||
|
|
3eb6956728 | ||
|
|
18ba3e2edd | ||
|
|
1c91104f43 | ||
|
|
e47027d92d | ||
|
|
e5fd8bdb83 | ||
|
|
ac2a77a746 | ||
|
|
c4a37dcb44 | ||
|
|
a12818afc0 | ||
|
|
3b4ac8076b | ||
|
|
d70ce7ee0b | ||
|
|
186c7eedb0 | ||
|
|
53bb01516d | ||
|
|
69d5262c81 | ||
|
|
0a4cdde50a | ||
|
|
1aaa898cf1 | ||
|
|
9fc23c55f2 | ||
|
|
b797e42157 | ||
|
|
036d668b19 | ||
|
|
574b543f80 | ||
|
|
add8b50cc9 | ||
|
|
71d2496afc | ||
|
|
f289d9f4ac | ||
|
|
4a3e7d320c | ||
|
|
0513e21f9b | ||
|
|
8b369ae5bd | ||
|
|
ec79a7b53c | ||
|
|
b96372dead | ||
|
|
04c4ec41d1 | ||
|
|
2d75cd40d3 | ||
|
|
f25f1ec60b | ||
|
|
37d4a79e99 | ||
|
|
3b35134e4b | ||
|
|
94a0c32e73 | ||
|
|
17c1180603 | ||
|
|
07dddd5f7e | ||
|
|
5dad9d6d28 | ||
|
|
a7b13919bf | ||
|
|
616a1464ea | ||
|
|
c918c4b76a | ||
|
|
03ca2ac8a9 | ||
|
|
b0029bc7fa | ||
|
|
d438e1ec17 | ||
|
|
fba726e555 | ||
|
|
9da4d542fe | ||
|
|
f2b4939da4 | ||
|
|
973f1b78fd | ||
|
|
aebfd1703b | ||
|
|
753ff84a3d | ||
|
|
d7ea44f2f9 | ||
|
|
537da370da | ||
|
|
af6ec4d78e | ||
|
|
e29bb934f7 | ||
|
|
5ac97314e7 | ||
|
|
4e35ffdfab | ||
|
|
3c0852d1da | ||
|
|
0e8dfd6062 | ||
|
|
e840213d6e | ||
|
|
9239103cd4 | ||
|
|
da54d36a96 | ||
|
|
5cf176ca15 | ||
|
|
4a73bb0b43 | ||
|
|
c70d3c7ade | ||
|
|
602c13a964 | ||
|
|
f9d18e22d2 | ||
|
|
3ce091fd73 | ||
|
|
77b4806625 | ||
|
|
e8a096000b | ||
|
|
324a0f988e | ||
|
|
bc5d7b7029 | ||
|
|
3e6899d116 | ||
|
|
d4e07a8459 | ||
|
|
dc2584eea0 | ||
|
|
d18dd2c41f | ||
|
|
62384ebe9c | ||
|
|
9f246298e2 | ||
|
|
fe395fb63d | ||
|
|
2172b660eb | ||
|
|
3c233ca4ea | ||
|
|
7631734563 | ||
|
|
7a02ad070f | ||
|
|
60c509ff18 | ||
|
|
d0ba8ec8f9 | ||
|
|
54d94e9c2c | ||
|
|
e04480faed | ||
|
|
c081f7215b | ||
|
|
ac73d7558b | ||
|
|
b104b87814 | ||
|
|
640d724808 | ||
|
|
653ac1f9c6 | ||
|
|
85ac1a320a | ||
|
|
76711b6e77 | ||
|
|
c963460dbc | ||
|
|
33c86d677f | ||
|
|
e425ec1162 | ||
|
|
0712d541d1 | ||
|
|
d58ef52ba6 | ||
|
|
17a4b75cc3 | ||
|
|
2fb70dc798 | ||
|
|
a738af8f84 | ||
|
|
906523d98a | ||
|
|
438f22bc56 | ||
|
|
335981d473 | ||
|
|
548b628054 | ||
|
|
4e07b08eff | ||
|
|
ab48c165a9 | ||
|
|
972f96b3fb | ||
|
|
989e644ed8 | ||
|
|
1beef6569a | ||
|
|
3d692822f8 | ||
|
|
046099c9b5 | ||
|
|
6ff2c8d7f7 | ||
|
|
3cd9ed1c31 | ||
|
|
50e305de98 | ||
|
|
5d1457dbbf | ||
|
|
7bd725e962 | ||
|
|
296691847d | ||
|
|
342370f1d3 | ||
|
|
1a136c1f13 | ||
|
|
fbff4628a9 | ||
|
|
bd6cf7b51d | ||
|
|
fc0c6fd984 | ||
|
|
816c1e30ca | ||
|
|
c270735861 | ||
|
|
a8bf4d63f9 | ||
|
|
8f5d24ae68 | ||
|
|
0ab6fc167f | ||
|
|
b7239bf7e0 | ||
|
|
477b6ea578 | ||
|
|
83f9a6fd21 | ||
|
|
a79eae4b01 | ||
|
|
d71e728c7a | ||
|
|
7004a6f7b8 | ||
|
|
3cdfaeca3f | ||
|
|
e097222e64 | ||
|
|
cfc34d7c4e | ||
|
|
bfbcec2339 | ||
|
|
6653e32ac2 | ||
|
|
67adc937b6 | ||
|
|
1a146f89c7 | ||
|
|
c3e5ee7154 | ||
|
|
5334d8b44c | ||
|
|
36d42e65d0 | ||
|
|
b57dd9262a | ||
|
|
2ba59b5a1e | ||
|
|
1f6f7e3e89 | ||
|
|
243975d5da | ||
|
|
f0c509e2c8 | ||
|
|
dc64f46b1c | ||
|
|
2cabdb8f44 | ||
|
|
4a17b47bb5 | ||
|
|
8c2b921fdf | ||
|
|
20ce081fae | ||
|
|
5241e0dbfc | ||
|
|
c04f6a0b4c | ||
|
|
45c7ce1377 | ||
|
|
2c2ba68247 | ||
|
|
b821984d31 | ||
|
|
2f4fc539c6 | ||
|
|
1dce75b2e2 | ||
|
|
304b3c706b | ||
|
|
fa50fffaf5 | ||
|
|
c26a4d8e8a | ||
|
|
ed8fbdb560 | ||
|
|
2005c88a75 | ||
|
|
0b0f235724 | ||
|
|
23db48e8d8 | ||
|
|
beb36d9c1e | ||
|
|
67f37cf198 | ||
|
|
9053fe2a5c | ||
|
|
edde954e7b | ||
|
|
56281f3a97 | ||
|
|
4a21b1402c | ||
|
|
e39d080871 | ||
|
|
3f407b065c | ||
|
|
b77007df8b | ||
|
|
247d0979aa | ||
|
|
96fcefbf1d | ||
|
|
00197cff39 | ||
|
|
5b219eccb5 | ||
|
|
aad1191765 | ||
|
|
e333528991 | ||
|
|
7784980fcd | ||
|
|
3c6b49ed66 | ||
|
|
b3b875657f | ||
|
|
4f6e89b1d0 | ||
|
|
a2bf265a39 | ||
|
|
52fd1ff2c2 | ||
|
|
1886c435b9 | ||
|
|
63c30de80d | ||
|
|
13e66a8f51 | ||
|
|
01eabf7375 | ||
|
|
7ffb10fc1a | ||
|
|
236d4c67e9 | ||
|
|
cd7c4143d7 | ||
|
|
dfb6fe6755 | ||
|
|
3068870cce | ||
|
|
734e4acafb | ||
|
|
182b940e70 | ||
|
|
4118e13330 | ||
|
|
f2a8f92a15 | ||
|
|
4444256ab7 | ||
|
|
548d7fb261 | ||
|
|
fd43ee09da | ||
|
|
23a18ca5ad | ||
|
|
481856ac46 | ||
|
|
a0deec960f | ||
|
|
fe349db57b | ||
|
|
e63350e726 | ||
|
|
a13bde39ee | ||
|
|
73843aa636 | ||
|
|
9d60151b04 | ||
|
|
3f62215210 | ||
|
|
41e77b8390 | ||
|
|
c1038d2837 | ||
|
|
635a7bd1ad | ||
|
|
f39452e81f | ||
|
|
a4eb7387b2 | ||
|
|
36e4762ce0 | ||
|
|
86eb2b9ad9 | ||
|
|
0765babe15 | ||
|
|
647eafdc21 | ||
|
|
760ef68a69 | ||
|
|
327085b7b2 | ||
|
|
715591bba0 | ||
|
|
972e3ff295 | ||
|
|
420bdb42e7 | ||
|
|
1543d5d92e | ||
|
|
018bb2ebf5 | ||
|
|
dc51bd716b | ||
|
|
48e8baebc0 | ||
|
|
4b0aa3c4c8 | ||
|
|
53b693f5fe | ||
|
|
661e4c9267 | ||
|
|
489d142808 | ||
|
|
eba99c28e4 | ||
|
|
2ef92fea51 | ||
|
|
91300d01f6 | ||
|
|
ec2f64794b | ||
|
|
800e51553e | ||
|
|
b952c898b6 | ||
|
|
3b192f6186 | ||
|
|
6c57952002 | ||
|
|
425210cc48 | ||
|
|
1ea79a78c9 | ||
|
|
df5eeb85ca | ||
|
|
5ed650857d | ||
|
|
adb665e0bf | ||
|
|
307a4e80c8 | ||
|
|
361010d447 | ||
|
|
672300f47f | ||
|
|
b39b2ee12f | ||
|
|
1ca5f6d132 | ||
|
|
e095d0cbc7 | ||
|
|
14c0380e78 | ||
|
|
c90fef88b1 | ||
|
|
4e20c5da20 | ||
|
|
a9fb346e4a | ||
|
|
d133b08f68 | ||
|
|
144cdb8f16 | ||
|
|
9bd191d2f4 | ||
|
|
a9fae0a9d1 | ||
|
|
193221e0a1 | ||
|
|
e48f3f8b9e | ||
|
|
85bd8f518b | ||
|
|
a7875272d7 | ||
|
|
0f17f9279b | ||
|
|
cf19f559dd | ||
|
|
2dc019e095 | ||
|
|
f1aedda068 | ||
|
|
c54cdc3784 | ||
|
|
2bcaf8246b | ||
|
|
24bceb0963 | ||
|
|
815f54afad | ||
|
|
9e7cf3469b | ||
|
|
5c5d01ae74 | ||
|
|
f998c9790f | ||
|
|
879f366366 | ||
|
|
557034f362 | ||
|
|
437942e481 | ||
|
|
2b16d664cb | ||
|
|
16fb04434f | ||
|
|
ce22ea99a0 | ||
|
|
196af035c1 | ||
|
|
40a2e406f8 | ||
|
|
d5555d95a3 | ||
|
|
da61f348d3 | ||
|
|
f9eb56791a | ||
|
|
6a4faee5cd | ||
|
|
c90c48d3c8 | ||
|
|
1b295ac8ae | ||
|
|
25f5742f0b | ||
|
|
b50a81a2bb | ||
|
|
04751d5345 | ||
|
|
2946cadc46 | ||
|
|
0a9fd05c2a | ||
|
|
fcd1e0bf6c | ||
|
|
0aab5e55f0 | ||
|
|
9c0bb7f172 | ||
|
|
eeb27e1bbd | ||
|
|
c6168d13ab | ||
|
|
bc429de490 | ||
|
|
b9bc7a2aa4 | ||
|
|
60a2bbba94 | ||
|
|
9ee84067f6 | ||
|
|
f41df3045c | ||
|
|
2e8004e608 | ||
|
|
9de2f75216 | ||
|
|
24495186da | ||
|
|
1168cb810a | ||
|
|
99c052a34f | ||
|
|
f83cd64c0b | ||
|
|
0e926b84fd | ||
|
|
4dfaa6610a | ||
|
|
59a7c0337b | ||
|
|
869ae5d786 | ||
|
|
1d9dbef64e | ||
|
|
4edd39fda2 | ||
|
|
48fd619a47 | ||
|
|
48e4e842b7 | ||
|
|
fb2e412943 | ||
|
|
5af651db24 | ||
|
|
a0ba0aa877 | ||
|
|
b88f8e87c5 | ||
|
|
08616b4933 | ||
|
|
52c9808c3a | ||
|
|
5e0d6b4cc9 | ||
|
|
ab53998372 | ||
|
|
b4d07123c4 | ||
|
|
aa09d03381 | ||
|
|
6fbe96baf8 | ||
|
|
257de78d9b | ||
|
|
d88dff4ef2 | ||
|
|
5027dd17a7 | ||
|
|
fea6fdd676 | ||
|
|
b1031d6c12 | ||
|
|
f47054015d | ||
|
|
e29d3b67c2 | ||
|
|
21e8daced5 | ||
|
|
b9311aa65c | ||
|
|
a249a0b75b | ||
|
|
040328a30d | ||
|
|
8cbe3e10ca | ||
|
|
f26a139d89 | ||
|
|
5691a1d8a4 | ||
|
|
017de666c7 | ||
|
|
1b7af5fb1a | ||
|
|
27bfe327b2 | ||
|
|
89cc404dea | ||
|
|
9e7fda8299 | ||
|
|
2e4b5cab0e | ||
|
|
ce4963fdfc | ||
|
|
e489270980 | ||
|
|
f8d8cf53fe | ||
|
|
02b3e3985c | ||
|
|
447f17127c | ||
|
|
991b585ee0 | ||
|
|
d6ae6dec69 | ||
|
|
aea3ce4c83 | ||
|
|
2ad68b971a | ||
|
|
67501cfc9a | ||
|
|
04b02dd121 | ||
|
|
8c55bb87c8 | ||
|
|
d062328977 | ||
|
|
5cd28833a2 | ||
|
|
37737c3a6b | ||
|
|
2c1f95291d | ||
|
|
043cb62d63 | ||
|
|
4985f60fc8 | ||
|
|
5aded67ddb | ||
|
|
912aec13c3 | ||
|
|
7cbb298dbe | ||
|
|
a06ad47112 | ||
|
|
3fdd5b9712 | ||
|
|
ed4fc31db5 | ||
|
|
e4922e181e | ||
|
|
6d8cd7ede1 | ||
|
|
bd55e5a1e7 | ||
|
|
0f60358b0b | ||
|
|
b90e29c901 | ||
|
|
0d7acadaf9 | ||
|
|
01be44181b | ||
|
|
9d6c961383 | ||
|
|
ab01da5437 | ||
|
|
d346ba2468 | ||
|
|
e91b4d0cf6 | ||
|
|
be1f1092c9 | ||
|
|
6009c473c7 | ||
|
|
3c21c64c78 | ||
|
|
13f7a01f61 | ||
|
|
7260662b39 | ||
|
|
26388247aa | ||
|
|
87dfc1d23e | ||
|
|
eb44ed655a | ||
|
|
e4437610dd | ||
|
|
501f051081 | ||
|
|
dec9009f89 | ||
|
|
4ed69dd0b8 | ||
|
|
21f4bb5a89 | ||
|
|
4defe306fb | ||
|
|
f517d9dd09 | ||
|
|
eb3894cf42 | ||
|
|
5c64fb67d2 | ||
|
|
9e9f5a0b92 | ||
|
|
c2a62a4cb2 | ||
|
|
9ed928e7a9 | ||
|
|
4bc8c88e6b | ||
|
|
0a1bd9c509 | ||
|
|
da5a9a65c1 | ||
|
|
d45eb6c6d2 | ||
|
|
abc0ae462b | ||
|
|
715256338a | ||
|
|
5735b3dc2a | ||
|
|
654ed9a280 | ||
|
|
e4d3f5d9b8 | ||
|
|
7afbb7420b | ||
|
|
d78a4401b5 | ||
|
|
42ac9c5f1e | ||
|
|
b666f85445 | ||
|
|
4c3f4496b5 | ||
|
|
3edb9461b7 | ||
|
|
0a88f38b7e | ||
|
|
41a9070f84 | ||
|
|
8d9bf5c498 | ||
|
|
40cfa3e021 | ||
|
|
06b4785fec | ||
|
|
d7242ff4d5 | ||
|
|
17f76fc564 | ||
|
|
215d12826d | ||
|
|
a958c26435 | ||
|
|
0a165bd7d9 | ||
|
|
3639f32889 | ||
|
|
8e061f9740 | ||
|
|
81747f1be6 | ||
|
|
ba65c816bb | ||
|
|
1d980a8e35 | ||
|
|
2adab1dde2 | ||
|
|
a182b2981d | ||
|
|
f54de92307 | ||
|
|
9e4aa798c3 | ||
|
|
a10e8a056d | ||
|
|
eb1d4d53c8 | ||
|
|
22d88e24db | ||
|
|
b06b191362 | ||
|
|
1cca091298 | ||
|
|
de28a25533 | ||
|
|
0fcb6dbed7 | ||
|
|
52ee07b021 | ||
|
|
66a91e2607 | ||
|
|
67036c0406 | ||
|
|
0e2da497c6 | ||
|
|
6d61358a09 | ||
|
|
2d9d36ea46 | ||
|
|
9377518981 | ||
|
|
1ec75ee76b | ||
|
|
607628d349 | ||
|
|
ce1be2ce37 | ||
|
|
f7669b40ba | ||
|
|
22696b0881 | ||
|
|
c1865e0f7f | ||
|
|
a88677d2cf | ||
|
|
80c75593ed | ||
|
|
5051755e35 | ||
|
|
4fd08f4b8b | ||
|
|
e14fbaae26 | ||
|
|
3c2262400f | ||
|
|
ee0e2201e0 | ||
|
|
5a0e9a4cfa | ||
|
|
9447a85404 | ||
|
|
32149059f9 | ||
|
|
4590b53a4b | ||
|
|
85bb308258 | ||
|
|
8ce1b8440a | ||
|
|
380e651afc | ||
|
|
1613fa9490 | ||
|
|
6a14d55bd9 | ||
|
|
de47e2bd4d | ||
|
|
f099af4c76 | ||
|
|
db74b1a219 | ||
|
|
b18f9c9eac | ||
|
|
4e395e875e | ||
|
|
2482d5fb45 | ||
|
|
7541c7a79e | ||
|
|
c8513cde0c | ||
|
|
b49b92cf28 | ||
|
|
c7004840d2 | ||
|
|
4ad928e170 | ||
|
|
e9b2af87f8 | ||
|
|
23a057007c | ||
|
|
dade61ac26 | ||
|
|
cce702a6e4 | ||
|
|
3586901f8f | ||
|
|
b2ce59537c | ||
|
|
4b3438d2d6 | ||
|
|
a081f798b0 | ||
|
|
b10d65c2a4 | ||
|
|
f85f99bf69 | ||
|
|
7b81095171 | ||
|
|
7c95868378 | ||
|
|
ea9e0757ff | ||
|
|
2a9c97108e | ||
|
|
d76ddf327d | ||
|
|
cec2c6436b | ||
|
|
badbff65b7 | ||
|
|
4a16c32ece | ||
|
|
99c4af7160 | ||
|
|
f57bc1d034 | ||
|
|
5440675c3d | ||
|
|
6584cec8f2 | ||
|
|
817eeb29b4 | ||
|
|
ffdf6eee19 | ||
|
|
ecf9003860 | ||
|
|
72f8cc703c | ||
|
|
6a17b07ca8 | ||
|
|
78837f5d61 | ||
|
|
4cc37f59e5 | ||
|
|
ff17a2abf3 | ||
|
|
49d88be021 | ||
|
|
0b63f51fbc | ||
|
|
3981345be1 | ||
|
|
2a2ebb6f5e | ||
|
|
2fc2fd92a9 | ||
|
|
3771e37970 | ||
|
|
236756f2cf | ||
|
|
64a0082c69 | ||
|
|
dd7a748cff | ||
|
|
4fe12baa66 | ||
|
|
6525ce4caf | ||
|
|
a117891b4b | ||
|
|
b248e98cf9 | ||
|
|
deda159b55 | ||
|
|
e408e98c8c | ||
|
|
a297643f2e | ||
|
|
d11c09d9e2 | ||
|
|
d367555027 | ||
|
|
ebdfe34cc4 | ||
|
|
d1be594636 | ||
|
|
6056d6317d | ||
|
|
76a67cf741 | ||
|
|
67c1ae8831 | ||
|
|
b693ba68b5 | ||
|
|
db3dfb164e | ||
|
|
87c91bd876 | ||
|
|
2914de64e8 | ||
|
|
8b79422b52 | ||
|
|
4beffe001d | ||
|
|
821bcb0b39 | ||
|
|
5370f44a83 | ||
|
|
56dd034115 | ||
|
|
ccecf3f4fb | ||
|
|
1b0069ce2d | ||
|
|
638c49f24f | ||
|
|
6b8e9c68b7 | ||
|
|
c38b075e7e | ||
|
|
8f399e3fee | ||
|
|
98d0b78eac | ||
|
|
9fd68b7fb6 | ||
|
|
59ddb50591 | ||
|
|
f4d986364e | ||
|
|
7882cb9773 | ||
|
|
44f5cc57a5 | ||
|
|
7c4615cf1f | ||
|
|
5bb0a7f73d | ||
|
|
86396cc189 | ||
|
|
c1db098dcf | ||
|
|
7da2eaf0d9 | ||
|
|
34723b4c4a | ||
|
|
7cc0dbd66f | ||
|
|
9253767a6a | ||
|
|
f35b16f246 | ||
|
|
c3a4bea5dc | ||
|
|
8234faabfd | ||
|
|
4990c0d1a5 | ||
|
|
7323e4c8ab | ||
|
|
ee027fc19f | ||
|
|
e4609a749b | ||
|
|
2306167d30 | ||
|
|
343304e1d3 | ||
|
|
cdc4eb6892 | ||
|
|
712dd27e67 | ||
|
|
0155c73dee | ||
|
|
c49ea68c55 | ||
|
|
726c2f7e5b | ||
|
|
d51dc96a79 | ||
|
|
c116b47804 | ||
|
|
64046e581c | ||
|
|
9ae92f50b2 | ||
|
|
7c01d65344 | ||
|
|
8796934af7 | ||
|
|
5e2c796589 | ||
|
|
557748ff7b | ||
|
|
811ee21112 | ||
|
|
afad5bd1c5 | ||
|
|
6920cde893 | ||
|
|
e72ea485ed | ||
|
|
9c8ac144bd | ||
|
|
d4c45428af | ||
|
|
d3bfd33972 | ||
|
|
7efd9c25c4 | ||
|
|
50b8d29b9c | ||
|
|
5c858ddd20 | ||
|
|
e5b5f12b81 | ||
|
|
0ce258f9b3 | ||
|
|
ac0d93b08f | ||
|
|
1ae46094de | ||
|
|
8745f013ff | ||
|
|
e12270dfee | ||
|
|
bbd6a5a184 | ||
|
|
9498069fc9 | ||
|
|
f8061a237e | ||
|
|
16e225f70d | ||
|
|
a4955b39ac | ||
|
|
ae0ad719de | ||
|
|
b2a8016df1 | ||
|
|
c6654588bd | ||
|
|
2a6d0cde72 | ||
|
|
ee8bf2e41f | ||
|
|
f85df120f2 | ||
|
|
89e4c48820 | ||
|
|
b06ca5f860 | ||
|
|
4ea0ab3cc5 | ||
|
|
12767b3130 | ||
|
|
a9d512a76b | ||
|
|
d5a51d4de3 | ||
|
|
89f319c2df | ||
|
|
b50632920d | ||
|
|
b9a97181aa | ||
|
|
663afef884 | ||
|
|
32604e6601 | ||
|
|
9ab38c45ad | ||
|
|
08ab1d83ac | ||
|
|
f9b14be495 | ||
|
|
68f3eb746c | ||
|
|
e70020e4f6 | ||
|
|
7bedd94406 | ||
|
|
68a8e6b8fa | ||
|
|
e70ba4e40e | ||
|
|
2a282e5f54 | ||
|
|
d9cfaa2b16 | ||
|
|
9430333f84 | ||
|
|
515b11ffab | ||
|
|
876cb8bfb4 | ||
|
|
21c55bdb6e | ||
|
|
4b95253587 | ||
|
|
c6a8665b37 | ||
|
|
880ee363eb | ||
|
|
6797e6ffac | ||
|
|
dda6c72ac8 | ||
|
|
56222f57df | ||
|
|
15b7a4ab88 | ||
|
|
6ea41f8527 | ||
|
|
efd013d6d8 | ||
|
|
e6f68faf99 | ||
|
|
816ae098ea | ||
|
|
e295da1266 | ||
|
|
8e6b38d895 | ||
|
|
296545a2c7 | ||
|
|
61dbfbb6ce | ||
|
|
907f24d0e1 | ||
|
|
0f691c4b5b | ||
|
|
7c190070b4 | ||
|
|
e1b3ee8a79 | ||
|
|
cd4178a015 | ||
|
|
b954847fca | ||
|
|
a00bf1b3cf | ||
|
|
01f77cb192 | ||
|
|
eb53c05a35 | ||
|
|
32df9733d1 | ||
|
|
5aaef91d4a | ||
|
|
a45ee83181 | ||
|
|
38fae9e65d | ||
|
|
1a11c934d2 | ||
|
|
9b5adea97f | ||
|
|
95d96eeeb1 | ||
|
|
ff45d1b547 | ||
|
|
cb2476a0ca | ||
|
|
6b7167651f | ||
|
|
9eb0b53954 | ||
|
|
1fe3bf8298 | ||
|
|
43692793e3 | ||
|
|
95c192475d | ||
|
|
197b832afa | ||
|
|
bdb1d19a69 | ||
|
|
b726bf5961 | ||
|
|
faa7eb3b99 | ||
|
|
c4cef07f1b | ||
|
|
892e9d3047 | ||
|
|
4f2b0946d1 | ||
|
|
3b7ed677de | ||
|
|
56ac686292 | ||
|
|
3fc713ed92 | ||
|
|
343507afb1 | ||
|
|
aa432be4b9 | ||
|
|
8cd9f04feb | ||
|
|
8a4ee7e90b | ||
|
|
af6248d8b8 | ||
|
|
0d7b261236 | ||
|
|
6576fa05af | ||
|
|
c4e19b77e8 | ||
|
|
fa813f7478 | ||
|
|
d6b79e2fd0 | ||
|
|
a52e4d7d02 | ||
|
|
f9bd66779f | ||
|
|
88a2776db5 | ||
|
|
e87d5df1ae | ||
|
|
7b79238b65 | ||
|
|
4939fc3892 | ||
|
|
3a2bccc845 | ||
|
|
cf8adc971e | ||
|
|
30a24f2d3d | ||
|
|
d26a848074 | ||
|
|
249e796dfc | ||
|
|
162c9170dd | ||
|
|
8366e10ffc | ||
|
|
0babce57f7 | ||
|
|
812dbfb483 | ||
|
|
550bf895e9 | ||
|
|
886af59105 | ||
|
|
e3b1e3dfa6 | ||
|
|
f5177c761f | ||
|
|
97fd2a6383 | ||
|
|
7c919deccc | ||
|
|
9a33a723ba | ||
|
|
81f6b33d93 | ||
|
|
bc8af90e8c | ||
|
|
8100ec2cd1 | ||
|
|
7360db39e6 | ||
|
|
2a79af1c54 | ||
|
|
6faddd7c55 | ||
|
|
5009b5326b | ||
|
|
2d05eaeb28 | ||
|
|
bde7d1055f | ||
|
|
ca3db54788 | ||
|
|
20699df843 | ||
|
|
e9c1face6c | ||
|
|
fcc47fa5f1 | ||
|
|
56887f6cb8 |
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"project_id" : "rocksdb",
|
||||
"conduit_uri" : "https://reviews.facebook.net/",
|
||||
"conduit_uri" : "https://phabricator.fb.com/api/",
|
||||
"copyright_holder" : "Facebook",
|
||||
"load" : [
|
||||
"arcanist_util"
|
||||
|
||||
@@ -32,10 +32,12 @@ build/
|
||||
ldb
|
||||
manifest_dump
|
||||
sst_dump
|
||||
column_aware_encoding_exp
|
||||
util/build_version.cc
|
||||
build_tools/VALGRIND_LOGS/
|
||||
coverage/COVERAGE_REPORT
|
||||
.gdbhistory
|
||||
.gdb_history
|
||||
package/
|
||||
.phutil_module_cache
|
||||
unity.a
|
||||
@@ -64,3 +66,8 @@ java/javadoc
|
||||
scan_build_report/
|
||||
t
|
||||
LOG
|
||||
|
||||
db_logs/
|
||||
tp2/
|
||||
fbcode/
|
||||
fbcode
|
||||
+22
-16
@@ -1,42 +1,48 @@
|
||||
sudo: false
|
||||
dist: trusty
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
jdk:
|
||||
- oraclejdk7
|
||||
cache:
|
||||
- ccache
|
||||
- apt
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
|
||||
packages: ['clang-3.6' , 'zlib1g-dev', 'libbz2-dev', 'libsnappy-dev', 'curl']
|
||||
packages: ['clang-3.6' , 'g++-6', 'zlib1g-dev', 'libbz2-dev', 'libsnappy-dev', 'curl', 'libgflags-dev']
|
||||
env:
|
||||
# Run all tests before db_block_cache_test (db_test, db_test2)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_END=db_block_cache_test
|
||||
# Run all tests starting from db_block_cache_test (db_block_cache_test, db_iter_test, ...)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_START=db_block_cache_test
|
||||
# Run all tests starting from db_block_cache_test (db_block_cache_test, ..., plain_table_db_test)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_START=db_block_cache_test ROCKSDBTESTS_END=comparator_db_test
|
||||
# Run all tests starting from db_block_cache_test (comparator_db_test, ...)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_START=comparator_db_test
|
||||
# Run java tests
|
||||
- JOB_NAME=java_test
|
||||
# Build ROCKSDB_LITE
|
||||
- JOB_NAME=lite_build
|
||||
|
||||
install:
|
||||
# Build gflags
|
||||
# TODO(noetzli): Remove when gflags available through Travis
|
||||
- pushd /tmp/ && curl -L https://github.com/gflags/gflags/archive/v2.1.2.tar.gz -o gflags.tar.gz && tar xfz gflags.tar.gz && cd gflags-2.1.2 && cmake . && make && popd
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
before_script:
|
||||
# Add gflags to include/library paths
|
||||
# TODO(noetzli): Remove when gflags available through Travis
|
||||
- export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/tmp/gflags-2.1.2/lib"
|
||||
- export LIBRARY_PATH="$LIBRARY_PATH:/tmp/gflags-2.1.2/lib"
|
||||
- export CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:/tmp/gflags-2.1.2/include"
|
||||
- if [[ "${TRAVIS_OS_NAME}" == 'linux' ]]; then CXX=clang++-3.6; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == 'osx' ]]; then brew install gflags snappy; fi
|
||||
# Limit the maximum number of open file descriptors to 2000
|
||||
- ulimit -n 2000 || true
|
||||
- if [[ "${TRAVIS_OS_NAME}" == 'linux' && "${CXX}" == 'clang++' ]]; then CXX=clang++-3.6; fi
|
||||
# test one linux g++ build with g++-6
|
||||
- if [[ "${TRAVIS_OS_NAME}" == 'linux' && "${CXX}" == 'g++' && "${JOB_NAME}" == 'unittests' ]]; then CXX=g++-6; fi
|
||||
# Limit the maximum number of open file descriptors to 8192
|
||||
- ulimit -n 8192 || true
|
||||
|
||||
script:
|
||||
- ${CXX} --version
|
||||
- if [[ "${JOB_NAME}" == 'unittests' ]]; then OPT=-DTRAVIS V=1 make -j4 check_some; fi
|
||||
- if [[ "${JOB_NAME}" == 'java_test' ]]; then OPT=-DTRAVIS V=1 make clean jclean rocksdbjava jtest; fi
|
||||
- if [[ "${JOB_NAME}" == 'lite_build' ]]; then OPT="-DTRAVIS -DROCKSDB_LITE" V=1 make -j4 static_lib; fi
|
||||
|
||||
+360
-110
@@ -1,7 +1,8 @@
|
||||
# This cmake build is for Windows 64-bit only.
|
||||
# Prerequisites for Windows:
|
||||
# This cmake build is for Windows 64-bit only.
|
||||
#
|
||||
# Prerequisites:
|
||||
# You must have Visual Studio 2013 Update 4 installed. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
# You must have at least Visual Studio 2015 Update 3. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
# Run the build commands from within the Developer Command Prompt window to have paths to the compiler and runtime libraries set.
|
||||
# You must have git.exe in your %PATH% environment variable.
|
||||
#
|
||||
@@ -13,8 +14,7 @@
|
||||
# cd build
|
||||
# 3. Run cmake to generate project files for Windows, add more options to enable required third-party libraries.
|
||||
# See thirdparty.inc for more information.
|
||||
# sample command: cmake -G "Visual Studio 12 Win64" -DGFLAGS=1 -DSNAPPY=1 -DJEMALLOC=1 -DJNI=1 ..
|
||||
# OR for VS Studio 15 cmake -G "Visual Studio 14 Win64" -DGFLAGS=1 -DSNAPPY=1 -DJEMALLOC=1 -DJNI=1 ..
|
||||
# sample command: cmake -G "Visual Studio 14 Win64" -DGFLAGS=1 -DSNAPPY=1 -DJEMALLOC=1 -DJNI=1 ..
|
||||
# 4. Then build the project in debug mode (you may want to add /m[:<N>] flag to run msbuild in <N> parallel threads
|
||||
# or simply /m ot use all avail cores)
|
||||
# msbuild rocksdb.sln
|
||||
@@ -25,41 +25,149 @@
|
||||
# 5. And release mode (/m[:<N>] is also supported)
|
||||
# msbuild rocksdb.sln /p:Configuration=Release
|
||||
#
|
||||
# Linux:
|
||||
#
|
||||
# 1. Install a recent toolchain such as devtoolset-3 if you're on a older distro. C++11 required.
|
||||
# 2. mkdir build; cd build
|
||||
# 3. cmake ..
|
||||
# 4. make -j
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(rocksdb)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format MM_dd_yyyy" OUTPUT_VARIABLE DATE)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format HH:mm:ss" OUTPUT_VARIABLE TIME)
|
||||
string(REGEX REPLACE "(..)_(..)_..(..).*" "\\1/\\2/\\3" DATE ${DATE})
|
||||
string(REGEX REPLACE "(..):(.....).*" " \\1:\\2" TIME ${TIME})
|
||||
string(CONCAT GIT_DATE_TIME ${DATE} ${TIME})
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
||||
|
||||
if(WIN32)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
option(WITH_JEMALLOC "build with JeMalloc" OFF)
|
||||
if(WITH_JEMALLOC)
|
||||
find_package(JeMalloc REQUIRED)
|
||||
add_definitions(-DROCKSDB_JEMALLOC)
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||
# FreeBSD has jemaloc as default malloc
|
||||
add_definitions(-DROCKSDB_JEMALLOC)
|
||||
set(WITH_JEMALLOC ON)
|
||||
endif()
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
add_definitions(-DSNAPPY)
|
||||
include_directories(${SNAPPY_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${SNAPPY_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format MM_dd_yyyy" OUTPUT_VARIABLE DATE)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format HH:mm:ss" OUTPUT_VARIABLE TIME)
|
||||
string(REGEX REPLACE "(..)_(..)_..(..).*" "\\1/\\2/\\3" DATE "${DATE}")
|
||||
string(REGEX REPLACE "(..):(.....).*" " \\1:\\2" TIME "${TIME}")
|
||||
set(GIT_DATE_TIME "${DATE} ${TIME}")
|
||||
else()
|
||||
execute_process(COMMAND date "+%Y/%m/%d %H:%M:%S" OUTPUT_VARIABLE DATETIME)
|
||||
string(REGEX REPLACE "\n" "" DATETIME ${DATETIME})
|
||||
set(GIT_DATE_TIME "${DATETIME}")
|
||||
endif()
|
||||
|
||||
find_package(Git)
|
||||
|
||||
if (GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
if(WIN32)
|
||||
execute_process(COMMAND $ENV{COMSPEC} /C ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse HEAD OUTPUT_VARIABLE GIT_SHA)
|
||||
else()
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse HEAD OUTPUT_VARIABLE GIT_SHA)
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA 0)
|
||||
set(GIT_SHA 0)
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA ${GIT_SHA})
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_CURRENT_SOURCE_DIR}/util/build_version.cc)
|
||||
if (NOT WIN32)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION}" ROCKSDB_VERSION)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "major"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION_MAJOR
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
endif()
|
||||
|
||||
add_custom_command(OUTPUT ${BUILD_VERSION_CC}
|
||||
COMMAND echo "#include \"build_version.h\"" > ${BUILD_VERSION_CC}
|
||||
COMMAND echo "const char* rocksdb_build_git_sha = \"rocksdb_build_git_sha:${GIT_SHA}\";" >> ${BUILD_VERSION_CC}
|
||||
COMMAND echo "const char* rocksdb_build_git_datetime = \"rocksdb_build_git_datetime:${GIT_DATE_TIME}\";" >> ${BUILD_VERSION_CC}
|
||||
COMMAND echo const char* rocksdb_build_compile_date = __DATE__\; >> ${BUILD_VERSION_CC}
|
||||
)
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
add_library(build_version OBJECT ${BUILD_VERSION_CC})
|
||||
target_include_directories(build_version PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util)
|
||||
if(WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
|
||||
add_custom_target(GenerateBuildVersion DEPENDS ${BUILD_VERSION_CC})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W3 /wd4127 /wd4800 /wd4996 /wd4351")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-momit-leaf-frame-pointer" HAVE_OMIT_LEAF_FRAME_POINTER)
|
||||
if(HAVE_OMIT_LEAF_FRAME_POINTER)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -momit-leaf-frame-pointer")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
option(FAIL_ON_WARNINGS "Treat compile warnings as errors" ON)
|
||||
if(FAIL_ON_WARNINGS)
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX")
|
||||
else() # assume GCC
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W3 /WX /wd4127 /wd4800 /wd4996 /wd4351")
|
||||
option(WITH_ASAN "build with ASAN" OFF)
|
||||
if(WITH_ASAN)
|
||||
add_definitions(-DROCKSDB_TSAN_RUN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
if(WITH_JEMALLOC)
|
||||
message(FATAL "ASAN does not work well with JeMalloc")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_TSAN "build with TSAN" OFF)
|
||||
if(WITH_TSAN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread -pie")
|
||||
add_definitions(-DROCKSDB_TSAN_RUN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fPIC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fPIC")
|
||||
if(WITH_JEMALLOC)
|
||||
message(FATAL "TSAN does not work well with JeMalloc")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_UBSAN "build with UBSAN" OFF)
|
||||
if(WITH_UBSAN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined")
|
||||
if(WITH_JEMALLOC)
|
||||
message(FATAL "UBSAN does not work well with JeMalloc")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Used to run CI build and tests so we can run faster
|
||||
set(OPTIMIZE_DEBUG_DEFAULT 0) # Debug build is unoptimized by default use -DOPTDBG=1 to optimize
|
||||
@@ -70,33 +178,92 @@ else()
|
||||
set(OPTIMIZE_DEBUG ${OPTIMIZE_DEBUG_DEFAULT})
|
||||
endif()
|
||||
|
||||
if((${OPTIMIZE_DEBUG} EQUAL 1))
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt /MDd")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1 /Gm /MDd")
|
||||
if(WIN32)
|
||||
if((${OPTIMIZE_DEBUG} EQUAL 1))
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt /MDd")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1 /Gm /MDd")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oxt /Zp8 /Gm- /Gy /MD")
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oxt /Zp8 /Gm- /Gy /MD")
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp")
|
||||
endif()
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
|
||||
option(ROCKSDB_LITE "Build RocksDBLite version" OFF)
|
||||
if(ROCKSDB_LITE)
|
||||
add_definitions(-DROCKSDB_LITE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
endif()
|
||||
|
||||
add_definitions(-DWIN32 -DOS_WIN -D_MBCS -DWIN64 -DNOMINMAX)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Cygwin")
|
||||
add_definitions(-fno-builtin-memcmp -DCYGWIN)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
add_definitions(-DOS_MACOSX)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES arm)
|
||||
add_definitions(-DIOS_CROSS_COMPILE -DROCKSDB_LITE)
|
||||
# no debug info for IOS, that will make our library big
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_definitions(-DOS_LINUX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
|
||||
add_definitions(-DOS_SOLARIS)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
add_definitions(-DOS_FREEBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||
add_definitions(-DOS_NETBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
|
||||
add_definitions(-DOS_OPENBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "DragonFly")
|
||||
add_definitions(-DOS_DRAGONFLYBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
add_definitions(-DOS_ANDROID)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
add_definitions(-DWIN32 -DOS_WIN -D_MBCS -DWIN64 -DNOMINMAX)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
add_definitions(-DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX)
|
||||
endif()
|
||||
|
||||
option(WITH_FALLOCATE "build with fallocate" ON)
|
||||
|
||||
if(WITH_FALLOCATE)
|
||||
include(CheckCSourceCompiles)
|
||||
CHECK_C_SOURCE_COMPILES("
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
int fd = open(\"/dev/null\", 0);
|
||||
fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 0, 1024);
|
||||
}
|
||||
" HAVE_FALLOCATE)
|
||||
if(HAVE_FALLOCATE)
|
||||
add_definitions(-DROCKSDB_FALLOCATE_PRESENT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckFunctionExists)
|
||||
CHECK_FUNCTION_EXISTS(malloc_usable_size HAVE_MALLOC_USABLE_SIZE)
|
||||
if(HAVE_MALLOC_USABLE_SIZE)
|
||||
add_definitions(-DROCKSDB_MALLOC_USABLE_SIZE)
|
||||
endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/gtest-1.7.0/fused-src)
|
||||
|
||||
set(ROCKSDB_LIBS rocksdblib${ARTIFACT_SUFFIX})
|
||||
set(THIRDPARTY_LIBS ${THIRDPARTY_LIBS} gtest)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} Shlwapi.lib Rpcrt4.lib)
|
||||
|
||||
set(LIBS ${ROCKSDB_LIBS} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.7.0/fused-src)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_subdirectory(third-party/gtest-1.7.0/fused-src/gtest)
|
||||
|
||||
# Main library source code
|
||||
|
||||
set(SOURCES
|
||||
db/auto_roll_logger.cc
|
||||
db/builder.cc
|
||||
@@ -117,6 +284,7 @@ set(SOURCES
|
||||
db/db_info_dumper.cc
|
||||
db/db_iter.cc
|
||||
db/event_helpers.cc
|
||||
db/external_sst_file_ingestion_job.cc
|
||||
db/experimental.cc
|
||||
db/filename.cc
|
||||
db/file_indexer.cc
|
||||
@@ -132,8 +300,8 @@ set(SOURCES
|
||||
db/memtable_list.cc
|
||||
db/merge_helper.cc
|
||||
db/merge_operator.cc
|
||||
db/range_del_aggregator.cc
|
||||
db/repair.cc
|
||||
db/slice.cc
|
||||
db/snapshot_impl.cc
|
||||
db/table_cache.cc
|
||||
db/table_properties_collector.cc
|
||||
@@ -153,12 +321,6 @@ set(SOURCES
|
||||
memtable/skiplistrep.cc
|
||||
memtable/vectorrep.cc
|
||||
port/stack_trace.cc
|
||||
port/win/io_win.cc
|
||||
port/win/env_win.cc
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/xpress_win.cc
|
||||
table/adaptive_table_factory.cc
|
||||
table/block.cc
|
||||
table/block_based_filter_block.cc
|
||||
@@ -176,7 +338,7 @@ set(SOURCES
|
||||
table/full_filter_block.cc
|
||||
table/get_context.cc
|
||||
table/iterator.cc
|
||||
table/merger.cc
|
||||
table/merging_iterator.cc
|
||||
table/sst_file_writer.cc
|
||||
table/meta_blocks.cc
|
||||
table/plain_table_builder.cc
|
||||
@@ -192,13 +354,14 @@ set(SOURCES
|
||||
tools/dump/db_dump_tool.cc
|
||||
util/arena.cc
|
||||
util/bloom.cc
|
||||
util/build_version.cc
|
||||
util/cache.cc
|
||||
util/cf_options.cc
|
||||
util/clock_cache.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
util/concurrent_arena.cc
|
||||
util/crc32c.cc
|
||||
util/db_options.cc
|
||||
util/delete_scheduler.cc
|
||||
util/dynamic_bloom.cc
|
||||
util/env.cc
|
||||
@@ -214,13 +377,14 @@ set(SOURCES
|
||||
util/histogram_windowing.cc
|
||||
util/instrumented_mutex.cc
|
||||
util/iostats_context.cc
|
||||
|
||||
util/lru_cache.cc
|
||||
tools/ldb_cmd.cc
|
||||
tools/ldb_tool.cc
|
||||
util/logging.cc
|
||||
util/log_buffer.cc
|
||||
util/memenv.cc
|
||||
util/murmurhash.cc
|
||||
util/mutable_cf_options.cc
|
||||
util/options.cc
|
||||
util/options_helper.cc
|
||||
util/options_parser.cc
|
||||
@@ -229,16 +393,16 @@ set(SOURCES
|
||||
util/perf_level.cc
|
||||
util/random.cc
|
||||
util/rate_limiter.cc
|
||||
util/sharded_cache.cc
|
||||
util/slice.cc
|
||||
util/statistics.cc
|
||||
util/status.cc
|
||||
util/status_message.cc
|
||||
util/string_util.cc
|
||||
util/sync_point.cc
|
||||
util/testharness.cc
|
||||
util/testutil.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool.cc
|
||||
util/threadpool_imp.cc
|
||||
util/thread_status_impl.cc
|
||||
util/thread_status_updater.cc
|
||||
util/thread_status_util.cc
|
||||
@@ -247,26 +411,32 @@ set(SOURCES
|
||||
util/xfunc.cc
|
||||
util/xxhash.cc
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/blob_db/blob_db.cc
|
||||
utilities/checkpoint/checkpoint.cc
|
||||
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc
|
||||
utilities/date_tiered/date_tiered_db_impl.cc
|
||||
utilities/document/document_db.cc
|
||||
utilities/document/json_document.cc
|
||||
utilities/document/json_document_builder.cc
|
||||
utilities/env_mirror.cc
|
||||
utilities/env_registry.cc
|
||||
utilities/flashcache/flashcache.cc
|
||||
utilities/geodb/geodb_impl.cc
|
||||
utilities/leveldb_options/leveldb_options.cc
|
||||
utilities/lua/rocks_lua_compaction_filter.cc
|
||||
utilities/memory/memory_util.cc
|
||||
utilities/merge_operators/string_append/stringappend.cc
|
||||
utilities/merge_operators/string_append/stringappend2.cc
|
||||
utilities/merge_operators/put.cc
|
||||
utilities/merge_operators/max.cc
|
||||
utilities/merge_operators/uint64add.cc
|
||||
utilities/option_change_migration/option_change_migration.cc
|
||||
utilities/options/options_util.cc
|
||||
utilities/persistent_cache/block_cache_tier.cc
|
||||
utilities/persistent_cache/block_cache_tier_file.cc
|
||||
utilities/persistent_cache/block_cache_tier_metadata.cc
|
||||
utilities/persistent_cache/persistent_cache_tier.cc
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/redis/redis_lists.cc
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/spatialdb/spatial_db.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/transactions/optimistic_transaction_impl.cc
|
||||
@@ -280,55 +450,83 @@ set(SOURCES
|
||||
utilities/ttl/db_ttl_impl.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc
|
||||
)
|
||||
utilities/col_buf_encoder.cc
|
||||
utilities/col_buf_decoder.cc
|
||||
utilities/column_aware_encoding_util.cc
|
||||
$<TARGET_OBJECTS:build_version>)
|
||||
|
||||
# For test util library that is build only in DEBUG mode
|
||||
# and linked to tests. Add test only code that is not #ifdefed for Release here.
|
||||
set(TESTUTIL_SOURCE
|
||||
db/db_test_util.cc
|
||||
table/mock_table.cc
|
||||
util/mock_env.cc
|
||||
util/fault_injection_test_env.cc
|
||||
util/thread_status_updater_debug.cc
|
||||
)
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES
|
||||
port/win/io_win.cc
|
||||
port/win/env_win.cc
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/win_thread.cc
|
||||
port/win/xpress_win.cc)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
port/port_posix.cc
|
||||
util/env_posix.cc
|
||||
util/io_posix.cc)
|
||||
endif()
|
||||
|
||||
add_library(rocksdblib${ARTIFACT_SUFFIX} ${SOURCES})
|
||||
set_target_properties(rocksdblib${ARTIFACT_SUFFIX} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/rocksdblib${ARTIFACT_SUFFIX}.pdb")
|
||||
add_dependencies(rocksdblib${ARTIFACT_SUFFIX} GenerateBuildVersion)
|
||||
if(WIN32)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} Shlwapi.lib Rpcrt4.lib)
|
||||
set(ROCKSDB_STATIC_LIB rocksdblib${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_IMPORT_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(LIBS ${ROCKSDB_STATIC_LIB} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
else()
|
||||
set(SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT} rt)
|
||||
set(ROCKSDB_STATIC_LIB rocksdb${ARTIFACT_SUFFIX})
|
||||
set(ROCKSDB_SHARED_LIB rocksdb-shared)
|
||||
set(ROCKSDB_IMPORT_LIB ${ROCKSDB_SHARED_LIB})
|
||||
set(LIBS ${ROCKSDB_SHARED_LIB} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
add_library(rocksdb${ARTIFACT_SUFFIX} SHARED ${SOURCES})
|
||||
set_target_properties(rocksdb${ARTIFACT_SUFFIX} PROPERTIES COMPILE_FLAGS "-DROCKSDB_DLL -DROCKSDB_LIBRARY_EXPORTS /Fd${CMAKE_CFG_INTDIR}/rocksdb${ARTIFACT_SUFFIX}.pdb")
|
||||
add_dependencies(rocksdb${ARTIFACT_SUFFIX} GenerateBuildVersion)
|
||||
target_link_libraries(rocksdb${ARTIFACT_SUFFIX} ${LIBS})
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
set_target_properties(${ROCKSDB_SHARED_LIB} PROPERTIES
|
||||
LINKER_LANGUAGE CXX
|
||||
VERSION ${ROCKSDB_VERSION}
|
||||
SOVERSION ${ROCKSDB_VERSION_MAJOR}
|
||||
CXX_STANDARD 11
|
||||
OUTPUT_NAME "rocksdb")
|
||||
endif()
|
||||
|
||||
if (DEFINED JNI)
|
||||
if (${JNI} EQUAL 1)
|
||||
message(STATUS "JNI library is enabled")
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/java)
|
||||
else()
|
||||
message(STATUS "JNI library is disabled")
|
||||
endif()
|
||||
option(WITH_LIBRADOS "Build with librados" OFF)
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND SOURCES
|
||||
utilities/env_librados.cc)
|
||||
list(APPEND THIRDPARTY_LIBS rados)
|
||||
endif()
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(${ROCKSDB_STATIC_LIB} PROPERTIES
|
||||
COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/${ROCKSDB_STATIC_LIB}.pdb")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_library(${ROCKSDB_IMPORT_LIB} SHARED ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_IMPORT_LIB}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
set_target_properties(${ROCKSDB_IMPORT_LIB} PROPERTIES
|
||||
COMPILE_FLAGS "-DROCKSDB_DLL -DROCKSDB_LIBRARY_EXPORTS /Fd${CMAKE_CFG_INTDIR}/${ROCKSDB_IMPORT_LIB}.pdb")
|
||||
else()
|
||||
endif()
|
||||
|
||||
option(WITH_JNI "build with JNI" OFF)
|
||||
if(WITH_JNI OR JNI)
|
||||
message(STATUS "JNI library is enabled")
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/java)
|
||||
else()
|
||||
message(STATUS "JNI library is disabled")
|
||||
endif()
|
||||
|
||||
set(APPS
|
||||
tools/db_bench.cc
|
||||
db/memtablerep_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
tools/db_stress.cc
|
||||
tools/write_stress.cc
|
||||
tools/db_repl_stress.cc
|
||||
tools/ldb.cc
|
||||
tools/sst_dump.cc
|
||||
tools/dump/rocksdb_dump.cc
|
||||
tools/dump/rocksdb_undump.cc
|
||||
util/cache_bench.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc
|
||||
)
|
||||
|
||||
set(C_TESTS db/c_test.c)
|
||||
|
||||
set(TESTS
|
||||
db/auto_roll_logger_test.cc
|
||||
db/column_family_test.cc
|
||||
@@ -346,6 +544,9 @@ set(TESTS
|
||||
db/db_inplace_update_test.cc
|
||||
db/db_iter_test.cc
|
||||
db/db_log_iter_test.cc
|
||||
db/db_memtable_test.cc
|
||||
db/db_merge_operator_test.cc
|
||||
db/db_options_test.cc
|
||||
db/db_properties_test.cc
|
||||
db/db_table_properties_test.cc
|
||||
db/db_tailing_iter_test.cc
|
||||
@@ -353,8 +554,11 @@ set(TESTS
|
||||
db/db_test2.cc
|
||||
db/db_block_cache_test.cc
|
||||
db/db_bloom_filter_test.cc
|
||||
db/db_flush_test.cc
|
||||
db/db_iterator_test.cc
|
||||
db/db_range_del_test.cc
|
||||
db/db_sst_test.cc
|
||||
db/external_sst_file_test.cc
|
||||
db/db_universal_compaction_test.cc
|
||||
db/db_wal_test.cc
|
||||
db/dbformat_test.cc
|
||||
@@ -392,7 +596,6 @@ set(TESTS
|
||||
table/full_filter_block_test.cc
|
||||
table/merger_test.cc
|
||||
table/table_test.cc
|
||||
tools/db_sanity_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
tools/sst_dump_test.cc
|
||||
@@ -412,21 +615,26 @@ set(TESTS
|
||||
util/heap_test.cc
|
||||
util/histogram_test.cc
|
||||
util/iostats_context_test.cc
|
||||
util/lru_cache_test.cc
|
||||
util/mock_env_test.cc
|
||||
util/options_settable_test.cc
|
||||
util/options_test.cc
|
||||
util/rate_limiter_test.cc
|
||||
util/slice_transform_test.cc
|
||||
util/statistics_test.cc
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
utilities/blob_db/blob_db_test.cc
|
||||
utilities/checkpoint/checkpoint_test.cc
|
||||
utilities/date_tiered/date_tiered_test.cc
|
||||
utilities/document/document_db_test.cc
|
||||
utilities/document/json_document_test.cc
|
||||
utilities/env_registry_test.cc
|
||||
utilities/object_registry_test.cc
|
||||
utilities/geodb/geodb_test.cc
|
||||
utilities/memory/memory_test.cc
|
||||
utilities/merge_operators/string_append/stringappend_test.cc
|
||||
utilities/option_change_migration/option_change_migration_test.cc
|
||||
utilities/options/options_util_test.cc
|
||||
utilities/persistent_cache/hash_table_test.cc
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
@@ -437,21 +645,45 @@ set(TESTS
|
||||
utilities/transactions/transaction_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
utilities/column_aware_encoding_test.cc
|
||||
utilities/lua/rocks_lua_test.cc
|
||||
)
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND TESTS utilities/env_librados_test.cc)
|
||||
endif()
|
||||
|
||||
set(EXES ${APPS})
|
||||
|
||||
foreach(sourcefile ${EXES})
|
||||
string(REPLACE ".cc" "" exename ${sourcefile})
|
||||
string(REGEX REPLACE "^((.+)/)+" "" exename ${exename})
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile})
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${LIBS})
|
||||
endforeach(sourcefile ${EXES})
|
||||
set(BENCHMARKS
|
||||
tools/db_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
util/cache_bench.cc
|
||||
db/memtablerep_bench.cc
|
||||
utilities/column_aware_encoding_exp.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
add_library(testharness OBJECT util/testharness.cc)
|
||||
foreach(sourcefile ${BENCHMARKS})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
$<TARGET_OBJECTS:testharness>)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} gtest ${LIBS})
|
||||
endforeach(sourcefile ${BENCHMARKS})
|
||||
|
||||
# For test util library that is build only in DEBUG mode
|
||||
# and linked to tests. Add test only code that is not #ifdefed for Release here.
|
||||
set(TESTUTIL_SOURCE
|
||||
db/db_test_util.cc
|
||||
table/mock_table.cc
|
||||
util/mock_env.cc
|
||||
util/fault_injection_test_env.cc
|
||||
util/thread_status_updater_debug.cc
|
||||
)
|
||||
# test utilities are only build in debug
|
||||
enable_testing()
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
|
||||
set(TESTUTILLIB testutillib${ARTIFACT_SUFFIX})
|
||||
add_library(${TESTUTILLIB} STATIC ${TESTUTIL_SOURCE})
|
||||
set_target_properties(${TESTUTILLIB} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/testutillib${ARTIFACT_SUFFIX}.pdb")
|
||||
if(WIN32)
|
||||
set_target_properties(${TESTUTILLIB} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/testutillib${ARTIFACT_SUFFIX}.pdb")
|
||||
endif()
|
||||
set_target_properties(${TESTUTILLIB}
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
@@ -462,18 +694,23 @@ set_target_properties(${TESTUTILLIB}
|
||||
set(TEST_EXES ${TESTS})
|
||||
|
||||
foreach(sourcefile ${TEST_EXES})
|
||||
string(REPLACE ".cc" "" exename ${sourcefile})
|
||||
string(REGEX REPLACE "^((.+)/)+" "" exename ${exename})
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
$<TARGET_OBJECTS:testharness>)
|
||||
set_target_properties(${exename}${ARTIFACT_SUFFIX}
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${LIBS} testutillib${ARTIFACT_SUFFIX})
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} gtest ${LIBS})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
endforeach(sourcefile ${TEST_EXES})
|
||||
|
||||
# C executables must link to a shared object
|
||||
set(C_TESTS db/c_test.c)
|
||||
set(C_TEST_EXES ${C_TESTS})
|
||||
|
||||
foreach(sourcefile ${C_TEST_EXES})
|
||||
@@ -485,5 +722,18 @@ foreach(sourcefile ${C_TEST_EXES})
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} rocksdb${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX})
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${ROCKSDB_IMPORT_LIB} testutillib${ARTIFACT_SUFFIX})
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endforeach(sourcefile ${C_TEST_EXES})
|
||||
add_subdirectory(tools)
|
||||
|
||||
# Installation and packaging for Linux
|
||||
if (NOT WIN32)
|
||||
install(TARGETS ${ROCKSDB_STATIC_LIB} COMPONENT devel ARCHIVE DESTINATION lib64)
|
||||
install(TARGETS ${ROCKSDB_SHARED_LIB} COMPONENT runtime DESTINATION lib64)
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/rocksdb/"
|
||||
COMPONENT devel
|
||||
DESTINATION include/rocksdb)
|
||||
set(CMAKE_INSTALL_PREFIX /usr)
|
||||
endif()
|
||||
@@ -1,4 +1,11 @@
|
||||
# RocksDB default options change log
|
||||
## Unreleased
|
||||
* Change the default of delayed slowdown value to 16MB/s and further increase the L0 stop condition to 36 files.
|
||||
|
||||
## 5.0 (11/17/2016)
|
||||
* Options::allow_concurrent_memtable_write and Options::enable_write_thread_adaptive_yield are now true by default
|
||||
* Options.level0_stop_writes_trigger default value changes from 24 to 32.
|
||||
|
||||
## 4.8.0 (5/2/2016)
|
||||
* options.max_open_files changes from 5000 to -1. It improves performance, but users need to set file descriptor limit to be large enough and watch memory usage for index and bloom filters.
|
||||
* options.base_background_compactions changes from max_background_compactions to 1. When users set higher max_background_compactions but the write throughput is not high, the writes are less spiky to disks.
|
||||
|
||||
+93
-8
@@ -1,6 +1,91 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
|
||||
## 5.2.0 (02/08/2017)
|
||||
### Public API Change
|
||||
* NewLRUCache() will determine number of shard bits automatically based on capacity, if the user doesn't pass one. This also impacts the default block cache when the user doesn't explict provide one.
|
||||
* Change the default of delayed slowdown value to 16MB/s and further increase the L0 stop condition to 36 files.
|
||||
* (Experimental) Two-level indexing that partition the index and creates a 2nd level index on the partitions. The feature can be enabled by setting kTwoLevelIndexSearch as IndexType and configuring index_per_partition.
|
||||
|
||||
### New Features
|
||||
* Added new overloaded function GetApproximateSizes that allows to specify if memtable stats should be computed only without computing SST files' stats approximations.
|
||||
* Added new function GetApproximateMemTableStats that approximates both number of records and size of memtables.
|
||||
|
||||
### Bug Fixes
|
||||
* RangeSync() should work if ROCKSDB_FALLOCATE_PRESENT is not set
|
||||
* Fix wrong results in a data race case in Get()
|
||||
* Some fixes related to 2PC.
|
||||
* Fix several bugs in Direct I/O supports.
|
||||
|
||||
## 5.1.0 (01/13/2017)
|
||||
* Support dynamically change `delete_obsolete_files_period_micros` option via SetDBOptions().
|
||||
* Added EventListener::OnExternalFileIngested which will be called when IngestExternalFile() add a file successfully.
|
||||
* BackupEngine::Open and BackupEngineReadOnly::Open now always return error statuses matching those of the backup Env.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix the bug that if 2PC is enabled, checkpoints may loss some recent transactions.
|
||||
* When file copying is needed when creating checkpoints or bulk loading files, fsync the file after the file copying.
|
||||
|
||||
## 5.0.0 (11/17/2016)
|
||||
### Public API Change
|
||||
* Options::max_bytes_for_level_multiplier is now a double along with all getters and setters.
|
||||
* Support dynamically change `delayed_write_rate` and `max_total_wal_size` options via SetDBOptions().
|
||||
* Introduce DB::DeleteRange for optimized deletion of large ranges of contiguous keys.
|
||||
* Support dynamically change `delayed_write_rate` option via SetDBOptions().
|
||||
* Options::allow_concurrent_memtable_write and Options::enable_write_thread_adaptive_yield are now true by default.
|
||||
* Remove Tickers::SEQUENCE_NUMBER to avoid confusion if statistics object is shared among RocksDB instance. Alternatively DB::GetLatestSequenceNumber() can be used to get the same value.
|
||||
* Options.level0_stop_writes_trigger default value changes from 24 to 32.
|
||||
* New compaction filter API: CompactionFilter::FilterV2(). Allows to drop ranges of keys.
|
||||
* Removed flashcache support.
|
||||
* DB::AddFile() is deprecated and is replaced with DB::IngestExternalFile(). DB::IngestExternalFile() remove all the restrictions that existed for DB::AddFile.
|
||||
|
||||
### New Features
|
||||
* Add avoid_flush_during_shutdown option, which speeds up DB shutdown by not flushing unpersisted data (i.e. with disableWAL = true). Unpersisted data will be lost. The options is dynamically changeable via SetDBOptions().
|
||||
* Add memtable_insert_with_hint_prefix_extractor option. The option is mean to reduce CPU usage for inserting keys into memtable, if keys can be group by prefix and insert for each prefix are sequential or almost sequential. See include/rocksdb/options.h for more details.
|
||||
* Add LuaCompactionFilter in utilities. This allows developers to write compaction filters in Lua. To use this feature, LUA_PATH needs to be set to the root directory of Lua.
|
||||
* No longer populate "LATEST_BACKUP" file in backup directory, which formerly contained the number of the latest backup. The latest backup can be determined by finding the highest numbered file in the "meta/" subdirectory.
|
||||
|
||||
## 4.13.0 (10/18/2016)
|
||||
### Public API Change
|
||||
* DB::GetOptions() reflect dynamic changed options (i.e. through DB::SetOptions()) and return copy of options instead of reference.
|
||||
* Added Statistics::getAndResetTickerCount().
|
||||
|
||||
### New Features
|
||||
* Add DB::SetDBOptions() to dynamic change base_background_compactions and max_background_compactions.
|
||||
* Added Iterator::SeekForPrev(). This new API will seek to the last key that less than or equal to the target key.
|
||||
|
||||
## 4.12.0 (9/12/2016)
|
||||
### Public API Change
|
||||
* CancelAllBackgroundWork() flushes all memtables for databases containing writes that have bypassed the WAL (writes issued with WriteOptions::disableWAL=true) before shutting down background threads.
|
||||
* Merge options source_compaction_factor, max_grandparent_overlap_bytes and expanded_compaction_factor into max_compaction_bytes.
|
||||
* Remove ImmutableCFOptions.
|
||||
* Add a compression type ZSTD, which can work with ZSTD 0.8.0 or up. Still keep ZSTDNotFinal for compatibility reasons.
|
||||
|
||||
### New Features
|
||||
* Introduce NewClockCache, which is based on CLOCK algorithm with better concurrent performance in some cases. It can be used to replace the default LRU-based block cache and table cache. To use it, RocksDB need to be linked with TBB lib.
|
||||
* Change ticker/histogram statistics implementations to accumulate data in thread-local storage, which improves CPU performance by reducing cache coherency costs. Callers of CreateDBStatistics do not need to change anything to use this feature.
|
||||
* Block cache mid-point insertion, where index and filter block are inserted into LRU block cache with higher priority. The feature can be enabled by setting BlockBasedTableOptions::cache_index_and_filter_blocks_with_high_priority to true and high_pri_pool_ratio > 0 when creating NewLRUCache.
|
||||
|
||||
## 4.11.0 (8/1/2016)
|
||||
### Public API Change
|
||||
* options.memtable_prefix_bloom_huge_page_tlb_size => memtable_huge_page_size. When it is set, RocksDB will try to allocate memory from huge page for memtable too, rather than just memtable bloom filter.
|
||||
|
||||
### New Features
|
||||
* A tool to migrate DB after options change. See include/rocksdb/utilities/option_change_migration.h.
|
||||
* Add ReadOptions.background_purge_on_iterator_cleanup. If true, we avoid file deletion when destorying iterators.
|
||||
|
||||
## 4.10.0 (7/5/2016)
|
||||
### Public API Change
|
||||
* options.memtable_prefix_bloom_bits changes to options.memtable_prefix_bloom_bits_ratio and deprecate options.memtable_prefix_bloom_probes
|
||||
* enum type CompressionType and PerfLevel changes from char to unsigned char. Value of all PerfLevel shift by one.
|
||||
* Deprecate options.filter_deletes.
|
||||
|
||||
### New Features
|
||||
* Add avoid_flush_during_recovery option.
|
||||
* Add a read option background_purge_on_iterator_cleanup to avoid deleting files in foreground when destroying iterators. Instead, a job is scheduled in high priority queue and would be executed in a separate background thread.
|
||||
* RepairDB support for column families. RepairDB now associates data with non-default column families using information embedded in the SST/WAL files (4.7 or later). For data written by 4.6 or earlier, RepairDB associates it with the default column family.
|
||||
* Add options.write_buffer_manager which allows users to control total memtable sizes across multiple DB instances.
|
||||
|
||||
## 4.9.0 (6/9/2016)
|
||||
### Public API changes
|
||||
* Add bottommost_compression option, This option can be used to set a specific compression algorithm for the bottommost level (Last level containing files in the DB).
|
||||
@@ -29,7 +114,7 @@
|
||||
## 4.6.0 (3/10/2016)
|
||||
### Public API Changes
|
||||
* Change default of BlockBasedTableOptions.format_version to 2. It means default DB created by 4.6 or up cannot be opened by RocksDB version 3.9 or earlier.
|
||||
* Added strict_capacity_limit option to NewLRUCache. If the flag is set to true, insert to cache will fail if no enough capacity can be free. Signiture of Cache::Insert() is updated accordingly.
|
||||
* Added strict_capacity_limit option to NewLRUCache. If the flag is set to true, insert to cache will fail if no enough capacity can be free. Signature of Cache::Insert() is updated accordingly.
|
||||
* Tickers [NUMBER_DB_NEXT, NUMBER_DB_PREV, NUMBER_DB_NEXT_FOUND, NUMBER_DB_PREV_FOUND, ITER_BYTES_READ] are not updated immediately. The are updated when the Iterator is deleted.
|
||||
* Add monotonically increasing counter (DB property "rocksdb.current-super-version-number") that increments upon any change to the LSM tree.
|
||||
|
||||
@@ -145,8 +230,8 @@
|
||||
* options.hard_rate_limit is deprecated.
|
||||
* When options.soft_rate_limit or options.level0_slowdown_writes_trigger is triggered, the way to slow down writes is changed to: write rate to DB is limited to to options.delayed_write_rate.
|
||||
* DB::GetApproximateSizes() adds a parameter to allow the estimation to include data in mem table, with default to be not to include. It is now only supported in skip list mem table.
|
||||
* DB::CompactRange() now accept CompactRangeOptions instead of multiple paramters. CompactRangeOptions is defined in include/rocksdb/options.h.
|
||||
* CompactRange() will now skip bottommost level compaction for level based compaction if there is no compaction filter, bottommost_level_compaction is introduced in CompactRangeOptions to control when it's possbile to skip bottommost level compaction. This mean that if you want the compaction to produce a single file you need to set bottommost_level_compaction to BottommostLevelCompaction::kForce.
|
||||
* DB::CompactRange() now accept CompactRangeOptions instead of multiple parameters. CompactRangeOptions is defined in include/rocksdb/options.h.
|
||||
* CompactRange() will now skip bottommost level compaction for level based compaction if there is no compaction filter, bottommost_level_compaction is introduced in CompactRangeOptions to control when it's possible to skip bottommost level compaction. This mean that if you want the compaction to produce a single file you need to set bottommost_level_compaction to BottommostLevelCompaction::kForce.
|
||||
* Add Cache.GetPinnedUsage() to get the size of memory occupied by entries that are in use by the system.
|
||||
* DB:Open() will fail if the compression specified in Options is not linked with the binary. If you see this failure, recompile RocksDB with compression libraries present on your system. Also, previously our default compression was snappy. This behavior is now changed. Now, the default compression is snappy only if it's available on the system. If it isn't we change the default to kNoCompression.
|
||||
* We changed how we account for memory used in block cache. Previously, we only counted the sum of block sizes currently present in block cache. Now, we count the actual memory usage of the blocks. For example, a block of size 4.5KB will use 8KB memory with jemalloc. This might decrease your memory usage and possibly decrease performance. Increase block cache size if you see this happening after an upgrade.
|
||||
@@ -178,7 +263,7 @@
|
||||
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 BlockBasedTableOptions.format_version option, which allows user to specify which version of block based table he wants. As a general guideline, 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.
|
||||
@@ -254,7 +339,7 @@
|
||||
|
||||
## 3.5.0 (9/3/2014)
|
||||
### New Features
|
||||
* Add include/utilities/write_batch_with_index.h, providing a utilitiy class to query data out of WriteBatch when building it.
|
||||
* Add include/utilities/write_batch_with_index.h, providing a utility class to query data out of WriteBatch when building it.
|
||||
* Move BlockBasedTable related options to BlockBasedTableOptions from Options. Change corresponding JNI interface. Options affected include:
|
||||
no_block_cache, block_cache, block_cache_compressed, block_size, block_size_deviation, block_restart_interval, filter_policy, whole_key_filtering. filter_policy is changed to shared_ptr from a raw pointer.
|
||||
* Remove deprecated options: disable_seek_compaction and db_stats_log_interval
|
||||
@@ -283,7 +368,7 @@
|
||||
### New Features
|
||||
* Added JSON API prototype.
|
||||
* HashLinklist reduces performance outlier caused by skewed bucket by switching data in the bucket from linked list to skip list. Add parameter threshold_use_skiplist in NewHashLinkListRepFactory().
|
||||
* RocksDB is now able to reclaim storage space more effectively during the compaction process. This is done by compensating the size of each deletion entry by the 2X average value size, which makes compaction to be triggerred by deletion entries more easily.
|
||||
* RocksDB is now able to reclaim storage space more effectively during the compaction process. This is done by compensating the size of each deletion entry by the 2X average value size, which makes compaction to be triggered by deletion entries more easily.
|
||||
* Add TimeOut API to write. Now WriteOptions have a variable called timeout_hint_us. With timeout_hint_us set to non-zero, any write associated with this timeout_hint_us may be aborted when it runs longer than the specified timeout_hint_us, and it is guaranteed that any write completes earlier than the specified time-out will not be aborted due to the time-out condition.
|
||||
* Add a rate_limiter option, which controls total throughput of flush and compaction. The throughput is specified in bytes/sec. Flush always has precedence over compaction when available bandwidth is constrained.
|
||||
|
||||
@@ -298,11 +383,11 @@
|
||||
2) It added some complexity to the important code-paths,
|
||||
3) None of our internal customers were really using it.
|
||||
Because of that, Options::disable_seek_compaction is now obsolete. It is still a parameter in Options, so it does not break the build, but it does not have any effect. We plan to completely remove it at some point, so we ask users to please remove this option from your code base.
|
||||
* Add two paramters to NewHashLinkListRepFactory() for logging on too many entries in a hash bucket when flushing.
|
||||
* Add two parameters to NewHashLinkListRepFactory() for logging on too many entries in a hash bucket when flushing.
|
||||
* Added new option BlockBasedTableOptions::hash_index_allow_collision. When enabled, prefix hash index for block-based table will not store prefix and allow hash collision, reducing memory consumption.
|
||||
|
||||
### New Features
|
||||
* PlainTable now supports a new key encoding: for keys of the same prefix, the prefix is only written once. It can be enabled through encoding_type paramter of NewPlainTableFactory()
|
||||
* PlainTable now supports a new key encoding: for keys of the same prefix, the prefix is only written once. It can be enabled through encoding_type parameter of NewPlainTableFactory()
|
||||
* Add AdaptiveTableFactory, which is used to convert from a DB of PlainTable to BlockBasedTabe, or vise versa. It can be created using NewAdaptiveTableFactory()
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
+4
-4
@@ -40,7 +40,7 @@ your make commands, like this: `PORTABLE=1 make static_lib`
|
||||
## Supported platforms
|
||||
|
||||
* **Linux - Ubuntu**
|
||||
* Upgrade your gcc to version at least 4.7 to get C++11 support.
|
||||
* Upgrade your gcc to version at least 4.8 to get C++11 support.
|
||||
* Install gflags. First, try: `sudo apt-get install libgflags-dev`
|
||||
If this doesn't work and you're using Ubuntu, here's a nice tutorial:
|
||||
(http://askubuntu.com/questions/312173/installing-gflags-12-04)
|
||||
@@ -49,8 +49,8 @@ your make commands, like this: `PORTABLE=1 make static_lib`
|
||||
* Install zlib. Try: `sudo apt-get install zlib1g-dev`.
|
||||
* Install bzip2: `sudo apt-get install libbz2-dev`.
|
||||
* **Linux - CentOS**
|
||||
* Upgrade your gcc to version at least 4.7 to get C++11 support:
|
||||
`yum install gcc47-c++`
|
||||
* Upgrade your gcc to version at least 4.8 to get C++11 support:
|
||||
`yum install gcc48-c++`
|
||||
* Install gflags:
|
||||
|
||||
wget https://gflags.googlecode.com/files/gflags-2.0-no-svn-files.tar.gz
|
||||
@@ -80,7 +80,7 @@ your make commands, like this: `PORTABLE=1 make static_lib`
|
||||
* Update XCode: run `xcode-select --install` (or install it from XCode App's settting).
|
||||
* 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/versions; brew install gcc47 --use-llvm` to install gcc 4.7 (or higher).
|
||||
* run `brew tap homebrew/versions; brew install gcc48 --use-llvm` to install gcc 4.8 (or higher).
|
||||
* run `brew install rocksdb`
|
||||
|
||||
* **iOS**:
|
||||
|
||||
@@ -9,3 +9,4 @@ This is the list of all known third-party language bindings for RocksDB. If some
|
||||
* Haskell - https://hackage.haskell.org/package/rocksdb-haskell
|
||||
* PHP - https://github.com/Photonios/rocksdb-php
|
||||
* C# - https://github.com/warrenfalk/rocksdb-sharp
|
||||
* Rust - https://github.com/spacejam/rust-rocksdb
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#-----------------------------------------------
|
||||
|
||||
BASH_EXISTS := $(shell which bash)
|
||||
SHELL := $(shell which bash)
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
CXXFLAGS += ${EXTRA_CXXFLAGS}
|
||||
@@ -130,14 +133,27 @@ 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)
|
||||
ifdef ROCKSDB_USE_LIBRADOS
|
||||
LIB_SOURCES += utilities/env_librados.cc
|
||||
LDFLAGS += -lrados
|
||||
endif
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
# detect what platform we're building on
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; "$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; export PORTABLE="$(PORTABLE)"; "$(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 make_config.mk
|
||||
CLEAN_FILES += make_config.mk
|
||||
|
||||
missing_make_config_paths := $(shell \
|
||||
grep "\/\S*" -o $(CURDIR)/make_config.mk | \
|
||||
while read path; \
|
||||
do [ -e $$path ] || echo $$path; \
|
||||
done | sort | uniq)
|
||||
|
||||
$(foreach path, $(missing_make_config_paths), \
|
||||
$(warning Warning: $(path) dont exist))
|
||||
|
||||
ifneq ($(PLATFORM), IOS)
|
||||
CFLAGS += -g
|
||||
CXXFLAGS += -g
|
||||
@@ -153,6 +169,8 @@ ifneq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
# found
|
||||
CFLAGS += -fno-exceptions
|
||||
CXXFLAGS += -fno-exceptions
|
||||
# LUA is not supported under ROCKSDB_LITE
|
||||
LUA_PATH =
|
||||
endif
|
||||
|
||||
# ASAN doesn't work well with jemalloc. If we're compiling with ASAN, we should use regular malloc.
|
||||
@@ -172,6 +190,8 @@ ifdef COMPILE_WITH_TSAN
|
||||
# Turn off -pg when enabling TSAN testing, because that induces
|
||||
# a link failure. TODO: find the root cause
|
||||
PROFILING_FLAGS =
|
||||
# LUA is not supported under TSAN
|
||||
LUA_PATH =
|
||||
endif
|
||||
|
||||
# USAN doesn't work well with jemalloc. If we're compiling with USAN, we should use regular malloc.
|
||||
@@ -203,10 +223,33 @@ default: all
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
|
||||
ifndef DISABLE_WARNING_AS_ERROR
|
||||
WARNING_FLAGS += -Werror
|
||||
ifdef LUA_PATH
|
||||
|
||||
ifndef LUA_INCLUDE
|
||||
LUA_INCLUDE=$(LUA_PATH)/include
|
||||
endif
|
||||
|
||||
LUA_INCLUDE_FILE=$(LUA_INCLUDE)/lualib.h
|
||||
|
||||
ifeq ("$(wildcard $(LUA_INCLUDE_FILE))", "")
|
||||
# LUA_INCLUDE_FILE does not exist
|
||||
$(error Cannot find lualib.h under $(LUA_INCLUDE). Try to specify both LUA_PATH and LUA_INCLUDE manually)
|
||||
endif
|
||||
LUA_FLAGS = -I$(LUA_INCLUDE) -DLUA -DLUA_COMPAT_ALL
|
||||
CFLAGS += $(LUA_FLAGS)
|
||||
CXXFLAGS += $(LUA_FLAGS)
|
||||
|
||||
ifndef LUA_LIB
|
||||
LUA_LIB = $(LUA_PATH)/lib/liblua.a
|
||||
endif
|
||||
ifeq ("$(wildcard $(LUA_LIB))", "") # LUA_LIB does not exist
|
||||
$(error $(LUA_LIB) does not exist. Try to specify both LUA_PATH and LUA_LIB manually)
|
||||
endif
|
||||
LDFLAGS += $(LUA_LIB)
|
||||
|
||||
endif
|
||||
|
||||
|
||||
CFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers
|
||||
|
||||
@@ -218,14 +261,7 @@ ifdef FORCE_GIT_SHA
|
||||
else
|
||||
git_sha := $(shell git rev-parse HEAD 2>/dev/null)
|
||||
endif
|
||||
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__;'
|
||||
gen_build_version = sed -e s/@@GIT_SHA@@/$(git_sha)/ -e s/@@GIT_DATE_TIME@@/$(date)/ util/build_version.cc.in
|
||||
|
||||
# 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
|
||||
@@ -242,8 +278,8 @@ util/build_version.cc: FORCE
|
||||
else mv -f $@-t $@; fi
|
||||
|
||||
LIBOBJECTS = $(LIB_SOURCES:.cc=.o)
|
||||
LIBOBJECTS += $(TOOL_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_SOURCES:.cc=.o)
|
||||
LIBOBJECTS += $(TOOL_LIB_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_LIB_SOURCES:.cc=.o)
|
||||
|
||||
GTEST = $(GTEST_DIR)/gtest/gtest-all.o
|
||||
TESTUTIL = ./util/testutil.o
|
||||
@@ -253,7 +289,9 @@ VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
|
||||
|
||||
VALGRIND_OPTS = --error-exitcode=$(VALGRIND_ERROR) --leak-check=full
|
||||
|
||||
BENCHTOOLOBJECTS = $(BENCH_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
BENCHTOOLOBJECTS = $(BENCH_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
EXPOBJECTS = $(EXP_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
TESTS = \
|
||||
db_test \
|
||||
@@ -265,9 +303,15 @@ TESTS = \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_dynamic_level_test \
|
||||
db_flush_test \
|
||||
db_inplace_update_test \
|
||||
db_iterator_test \
|
||||
db_memtable_test \
|
||||
db_merge_operator_test \
|
||||
db_options_test \
|
||||
db_range_del_test \
|
||||
db_sst_test \
|
||||
external_sst_file_test \
|
||||
db_tailing_iter_test \
|
||||
db_universal_compaction_test \
|
||||
db_wal_test \
|
||||
@@ -275,6 +319,7 @@ TESTS = \
|
||||
db_properties_test \
|
||||
db_table_properties_test \
|
||||
autovector_test \
|
||||
cleanable_test \
|
||||
column_family_test \
|
||||
table_properties_collector_test \
|
||||
arena_test \
|
||||
@@ -319,7 +364,9 @@ TESTS = \
|
||||
skiplist_test \
|
||||
stringappend_test \
|
||||
ttl_test \
|
||||
date_tiered_test \
|
||||
backupable_db_test \
|
||||
blob_db_test \
|
||||
document_db_test \
|
||||
json_document_test \
|
||||
sim_cache_test \
|
||||
@@ -352,6 +399,7 @@ TESTS = \
|
||||
compaction_job_test \
|
||||
thread_list_test \
|
||||
sst_dump_test \
|
||||
column_aware_encoding_test \
|
||||
compact_files_test \
|
||||
perf_context_test \
|
||||
optimistic_transaction_test \
|
||||
@@ -359,22 +407,32 @@ TESTS = \
|
||||
heap_test \
|
||||
compact_on_deletion_collector_test \
|
||||
compaction_job_stats_test \
|
||||
option_change_migration_test \
|
||||
transaction_test \
|
||||
ldb_cmd_test \
|
||||
iostats_context_test \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
lua_test \
|
||||
range_del_aggregator_test \
|
||||
lru_cache_test \
|
||||
object_registry_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
compact_on_deletion_collector_test \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_sst_test \
|
||||
db_test \
|
||||
db_universal_compaction_test \
|
||||
db_wal_test \
|
||||
external_sst_file_test \
|
||||
fault_injection_test \
|
||||
inlineskiplist_test \
|
||||
manual_compaction_test \
|
||||
table_test
|
||||
persistent_cache_test \
|
||||
table_test \
|
||||
transaction_test
|
||||
|
||||
SUBSET := $(TESTS)
|
||||
ifdef ROCKSDBTESTS_START
|
||||
@@ -399,7 +457,7 @@ TEST_LIBS = \
|
||||
librocksdb_env_basic_test.a
|
||||
|
||||
# TODO: add back forward_iterator_bench, after making it build in all environemnts.
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench column_aware_encoding_exp persistent_cache_bench
|
||||
|
||||
# if user didn't config LIBNAME, set the default
|
||||
ifeq ($(LIBNAME),)
|
||||
@@ -455,7 +513,7 @@ $(SHARED3): $(SHARED4)
|
||||
endif
|
||||
|
||||
$(SHARED4):
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(LIB_SOURCES) $(TOOL_SOURCES) \
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(LIB_SOURCES) $(TOOL_LIB_SOURCES) \
|
||||
$(LDFLAGS) -o $@
|
||||
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
@@ -547,7 +605,7 @@ $(parallel_tests): $(PARALLEL_TEST)
|
||||
'#!/bin/sh' \
|
||||
"d=\$(TMPD)$$TEST_SCRIPT" \
|
||||
'mkdir -p $$d' \
|
||||
"TEST_TMPDIR=\$$d ./$$TEST_BINARY --gtest_filter=$$TEST_NAME" \
|
||||
"TEST_TMPDIR=\$$d $(DRIVER) ./$$TEST_BINARY --gtest_filter=$$TEST_NAME" \
|
||||
> $$TEST_SCRIPT; \
|
||||
chmod a=rx $$TEST_SCRIPT; \
|
||||
done
|
||||
@@ -574,7 +632,7 @@ gen_parallel_tests:
|
||||
# 107.816 PASS t/DBTest.EncodeDecompressedBlockSizeTest
|
||||
#
|
||||
slow_test_regexp = \
|
||||
^t/run-table_test-HarnessTest.Randomized$$|^t/run-db_test-.*(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$
|
||||
^t/run-table_test-HarnessTest.Randomized$$|^t/run-db_test-.*(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$|^.*RecoverFromCorruptedWALWithoutFlush$$
|
||||
prioritize_long_running_tests = \
|
||||
perl -pe 's,($(slow_test_regexp)),100 $$1,' \
|
||||
| sort -k1,1gr \
|
||||
@@ -604,7 +662,7 @@ check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| parallel -j$(J) --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0:
|
||||
@@ -619,9 +677,9 @@ valgrind_check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| parallel -j$(J) --joblog=LOG $$eta --gnu \
|
||||
'if [[ "{}" == "./"* ]] ; then $(DRIVER) {} >& t/valgrind_log-{/}; ' \
|
||||
'else {} >& t/valgrind_log-{/}; fi'
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) ' \
|
||||
'>& t/valgrind_log-{/}'
|
||||
|
||||
CLEAN_FILES += t LOG $(TMPD)
|
||||
|
||||
@@ -640,7 +698,7 @@ watch-log:
|
||||
check: all
|
||||
$(MAKE) gen_parallel_tests
|
||||
$(AM_V_GEN)if test "$(J)" != 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) T="$$t" TMPD=$(TMPD) check_0; \
|
||||
@@ -654,7 +712,8 @@ ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
sh tools/rocksdb_dump_test.sh
|
||||
endif
|
||||
|
||||
check_some: $(SUBSET) ldb_tests
|
||||
# TODO add ldb_tests
|
||||
check_some: $(SUBSET)
|
||||
for t in $(SUBSET); do echo "===== Running $$t"; ./$$t || exit 1; done
|
||||
|
||||
.PHONY: ldb_tests
|
||||
@@ -667,9 +726,15 @@ blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple blackbox
|
||||
python -u tools/db_crashtest.py blackbox
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple whitebox
|
||||
python -u tools/db_crashtest.py whitebox
|
||||
python -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD)
|
||||
python -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD)
|
||||
|
||||
asan_check:
|
||||
$(MAKE) clean
|
||||
@@ -691,10 +756,13 @@ ubsan_crash_test:
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test
|
||||
$(MAKE) clean
|
||||
|
||||
valgrind_test:
|
||||
DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
valgrind_check: $(TESTS)
|
||||
$(MAKE) gen_parallel_tests
|
||||
$(MAKE) DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" gen_parallel_tests
|
||||
$(AM_V_GEN)if test "$(J)" != 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) TMPD=$(TMPD) \
|
||||
@@ -716,10 +784,10 @@ parloop:
|
||||
for t in $(PAR_TEST); do \
|
||||
echo "===== Running $$t in parallel $(NUM_PAR)";\
|
||||
if [ $(db_test) -eq 1 ]; then \
|
||||
seq $(J) | v="$$t" parallel --gnu 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
seq $(J) | v="$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
'timeout 2m ./db_test --gtest_filter=$$v >> $$s/log-{} 2>1'; \
|
||||
else\
|
||||
seq $(J) | v="./$$t" parallel --gnu 's=$(TMPD)/rdb-{};' \
|
||||
seq $(J) | v="./$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{};' \
|
||||
'export TEST_TMPDIR=$$s; timeout 10m $$v >> $$s/log-{} 2>1'; \
|
||||
fi; \
|
||||
ret_code=$$?; \
|
||||
@@ -740,7 +808,7 @@ test_names = \
|
||||
|
||||
parallel_check: $(TESTS)
|
||||
$(AM_V_GEN)if test "$(J)" > 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
echo Running in parallel $(J); \
|
||||
@@ -750,7 +818,7 @@ parallel_check: $(TESTS)
|
||||
ret_bad=0; \
|
||||
echo $(J);\
|
||||
echo Test Dir: $(TMPD); \
|
||||
seq $(J) | parallel --gnu 's=$(TMPD)/rdb-{}; rm -rf $$s; mkdir $$s'; \
|
||||
seq $(J) | build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; rm -rf $$s; mkdir $$s'; \
|
||||
$(MAKE) PAR_TEST="$(shell $(test_names))" TMPD=$(TMPD) \
|
||||
J=$(J) db_test=1 parloop; \
|
||||
$(MAKE) PAR_TEST="$(filter-out db_test, $(TESTS))" \
|
||||
@@ -789,6 +857,7 @@ clean:
|
||||
find . -name "*.[oda]" -exec rm -f {} \;
|
||||
find . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm {} \;
|
||||
rm -rf bzip2* snappy* zlib* lz4*
|
||||
cd java; $(MAKE) clean
|
||||
|
||||
tags:
|
||||
ctags * -R
|
||||
@@ -807,7 +876,7 @@ $(LIBRARY): $(LIBOBJECTS)
|
||||
$(AM_V_AR)rm -f $@
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $(LIBOBJECTS)
|
||||
|
||||
$(TOOLS_LIBRARY): $(BENCH_SOURCES:.cc=.o) $(TOOL_SOURCES:.cc=.o) $(LIB_SOURCES:.cc=.o) $(TESTUTIL)
|
||||
$(TOOLS_LIBRARY): $(BENCH_LIB_SOURCES:.cc=.o) $(TOOL_LIB_SOURCES:.cc=.o) $(LIB_SOURCES:.cc=.o) $(TESTUTIL)
|
||||
$(AM_V_AR)rm -f $@
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
@@ -821,6 +890,9 @@ db_bench: tools/db_bench.o $(BENCHTOOLOBJECTS)
|
||||
cache_bench: util/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
persistent_cache_bench: utilities/persistent_cache/persistent_cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
memtablerep_bench: db/memtablerep_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -842,7 +914,7 @@ arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_family_test: db/column_family_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
column_family_test: db/column_family_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
table_properties_collector_test: db/table_properties_collector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -863,6 +935,9 @@ cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
option_change_migration_test: utilities/option_change_migration/option_change_migration_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
stringappend_test: utilities/merge_operators/string_append/stringappend_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -911,15 +986,33 @@ db_compaction_test: db/db_compaction_test.o db/db_test_util.o $(LIBOBJECTS) $(TE
|
||||
db_dynamic_level_test: db/db_dynamic_level_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_flush_test: db/db_flush_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_inplace_update_test: db/db_inplace_update_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_iterator_test: db/db_iterator_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_memtable_test: db/db_memtable_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_merge_operator_test: db/db_merge_operator_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_options_test: db/db_options_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_range_del_test: db/db_range_del_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_sst_test: db/db_sst_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
external_sst_file_test: db/external_sst_file_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_tailing_iter_test: db/db_tailing_iter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -962,6 +1055,9 @@ prefix_test: db/prefix_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_db_test: utilities/blob_db/blob_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
checkpoint_test: utilities/checkpoint/checkpoint_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -980,12 +1076,20 @@ spatial_db_test: utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNE
|
||||
env_mirror_test: utilities/env_mirror_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
env_registry_test: utilities/env_registry_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
ifdef ROCKSDB_USE_LIBRADOS
|
||||
env_librados_test: utilities/env_librados_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
endif
|
||||
|
||||
object_registry_test: utilities/object_registry_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
date_tiered_test: utilities/date_tiered/date_tiered_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
write_batch_with_index_test: utilities/write_batch_with_index/write_batch_with_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1040,6 +1144,9 @@ full_filter_block_test: table/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNE
|
||||
log_test: db/log_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
cleanable_test: table/cleanable_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
table_test: table/table_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1142,6 +1249,9 @@ event_logger_test: util/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
sst_dump_test: tools/sst_dump_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_aware_encoding_test: utilities/column_aware_encoding_test.o $(TESTHARNESS) $(EXPOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
optimistic_transaction_test: utilities/transactions/optimistic_transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1172,6 +1282,9 @@ transaction_test: utilities/transactions/transaction_test.o $(LIBOBJECTS) $(TEST
|
||||
sst_dump: tools/sst_dump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_aware_encoding_exp: utilities/column_aware_encoding_exp.o $(EXPOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
repair_test: db/repair_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1187,6 +1300,18 @@ iostats_context_test: util/iostats_context_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
persistent_cache_test: utilities/persistent_cache/persistent_cache_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
statistics_test: util/statistics_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_cache_test: util/lru_cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lua_test: utilities/lua/rocks_lua_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
range_del_aggregator_test: db/range_del_aggregator_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
@@ -1249,6 +1374,11 @@ else
|
||||
JAVA_INCLUDE = -I/System/Library/Frameworks/JavaVM.framework/Headers/
|
||||
endif
|
||||
endif
|
||||
ifeq ($(PLATFORM), OS_FREEBSD)
|
||||
JAVA_INCLUDE += -I$(JAVA_HOME)/include/freebsd
|
||||
ROCKSDBJNILIB = librocksdbjni-freebsd$(ARCH).so
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-freebsd$(ARCH).jar
|
||||
endif
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ROCKSDBJNILIB = librocksdbjni-solaris$(ARCH).so
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-solaris$(ARCH).jar
|
||||
@@ -1256,30 +1386,30 @@ ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
endif
|
||||
|
||||
libz.a:
|
||||
-rm -rf zlib-1.2.8
|
||||
curl -O http://zlib.net/zlib-1.2.8.tar.gz
|
||||
tar xvzf zlib-1.2.8.tar.gz
|
||||
cd zlib-1.2.8 && CFLAGS='-fPIC' ./configure --static && make
|
||||
cp zlib-1.2.8/libz.a .
|
||||
-rm -rf zlib-1.2.10
|
||||
curl -O -L http://zlib.net/zlib-1.2.10.tar.gz
|
||||
tar xvzf zlib-1.2.10.tar.gz
|
||||
cd zlib-1.2.10 && CFLAGS='-fPIC' ./configure --static && make
|
||||
cp zlib-1.2.10/libz.a .
|
||||
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-1.0.6
|
||||
curl -O http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
|
||||
curl -O -L http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
|
||||
tar xvzf bzip2-1.0.6.tar.gz
|
||||
cd bzip2-1.0.6 && make CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64'
|
||||
cp bzip2-1.0.6/libbz2.a .
|
||||
|
||||
libsnappy.a:
|
||||
-rm -rf snappy-1.1.1
|
||||
curl -O https://snappy.googlecode.com/files/snappy-1.1.1.tar.gz
|
||||
tar xvzf snappy-1.1.1.tar.gz
|
||||
cd snappy-1.1.1 && ./configure --with-pic --enable-static
|
||||
cd snappy-1.1.1 && make
|
||||
cp snappy-1.1.1/.libs/libsnappy.a .
|
||||
-rm -rf snappy-1.1.3
|
||||
curl -O -L https://github.com/google/snappy/releases/download/1.1.3/snappy-1.1.3.tar.gz
|
||||
tar xvzf snappy-1.1.3.tar.gz
|
||||
cd snappy-1.1.3 && ./configure --with-pic --enable-static
|
||||
cd snappy-1.1.3 && make
|
||||
cp snappy-1.1.3/.libs/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
-rm -rf lz4-r127
|
||||
curl -O https://codeload.github.com/Cyan4973/lz4/tar.gz/r127
|
||||
curl -O -L 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
|
||||
@@ -1290,7 +1420,7 @@ java_static_libobjects = $(patsubst %,jls/%,$(LIBOBJECTS))
|
||||
CLEAN_FILES += jls
|
||||
|
||||
JAVA_STATIC_FLAGS = -DZLIB -DBZIP2 -DSNAPPY -DLZ4
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-1.2.8 -I./bzip2-1.0.6 -I./snappy-1.1.1 -I./lz4-r127/lib
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-1.2.10 -I./bzip2-1.0.6 -I./snappy-1.1.3 -I./lz4-r127/lib
|
||||
|
||||
$(java_static_libobjects): jls/%.o: %.cc libz.a libbz2.a libsnappy.a liblz4.a
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES) -fPIC -c $< -o $@ $(COVERAGEFLAGS)
|
||||
@@ -1315,12 +1445,15 @@ rocksdbjavastaticrelease: rocksdbjavastatic
|
||||
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
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
rocksdbjavastaticpublishcentral:
|
||||
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)-win64.jar -Dclassifier=win64
|
||||
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
|
||||
|
||||
# A version of each $(LIBOBJECTS) compiled with -fPIC
|
||||
@@ -1341,6 +1474,12 @@ rocksdbjava: $(java_libobjects)
|
||||
jclean:
|
||||
cd java;$(MAKE) clean;
|
||||
|
||||
jtest_compile: rocksdbjava
|
||||
cd java;$(MAKE) java_test
|
||||
|
||||
jtest_run:
|
||||
cd java;$(MAKE) run_test
|
||||
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample;$(MAKE) test;
|
||||
|
||||
@@ -1349,7 +1488,7 @@ jdb_bench:
|
||||
|
||||
commit_prereq: build_tools/rocksdb-lego-determinator \
|
||||
build_tools/precommit_checker.py
|
||||
J=$(J) build_tools/precommit_checker.py unit unit_481 clang_unit tsan asan ubsan lite
|
||||
J=$(J) build_tools/precommit_checker.py unit unit_481 clang_unit release release_481 clang_release tsan asan ubsan lite unit_non_shm
|
||||
$(MAKE) clean && $(MAKE) jclean && $(MAKE) rocksdbjava;
|
||||
|
||||
xfunc:
|
||||
@@ -1397,7 +1536,7 @@ endif
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
all_sources = $(LIB_SOURCES) $(TEST_BENCH_SOURCES) $(MOCK_SOURCES)
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(EXP_LIB_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.d)
|
||||
|
||||
# Add proper dependency support so changing a .h file forces a .cc file to
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
It is built on earlier work on LevelDB by Sanjay Ghemawat (sanjay@google.com)
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
This document lists users of RocksDB and their use cases. If you are using RocksDB, please open a pull request and add yourself to the list.
|
||||
|
||||
## Facebook
|
||||
At Facebook, we use RocksDB as a backend for many different stateful services. We're also experimenting with running RocksDB as a storage engine for two databases:
|
||||
At Facebook, we use RocksDB as storage engines in multiple data management services and a backend for many different stateful services, including:
|
||||
|
||||
1. MyRocks -- https://github.com/MySQLOnRocksDB/mysql-5.6
|
||||
2. MongoRocks -- https://github.com/mongodb-partners/mongo-rocks
|
||||
3. ZippyDB -- Facebook's distributed key-value store with Paxos-style replication, built on top of RocksDB.[*] https://www.youtube.com/watch?v=DfiN7pG0D0khtt
|
||||
4. Laser -- Laser is a high query throughput, low (millisecond) latency, key-value storage service built on top of RocksDB.[*]
|
||||
4. Dragon -- a distributed graph query engine. https://code.facebook.com/posts/1737605303120405/dragon-a-distributed-graph-query-engine/
|
||||
5. Stylus -- a low-level stream processing framework writtenin C++.[*]
|
||||
|
||||
[*] https://research.facebook.com/publications/realtime-data-processing-at-facebook/
|
||||
|
||||
## LinkedIn
|
||||
Two different use cases at Linkedin are using RocksDB as a storage engine:
|
||||
@@ -58,4 +64,19 @@ Pinterest's Object Retrieval System uses RocksDB for storage: https://www.youtub
|
||||
quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
|
||||
## Netflix
|
||||
[Netflix](http://techblog.netflix.com/2016/05/application-data-caching-using-ssds.html) uses RocksDB on spinning disks to cache application data.
|
||||
[Netflix](http://techblog.netflix.com/2016/05/application-data-caching-using-ssds.html) Netflix uses RocksDB on AWS EC2 instances with local SSD drives to cache application data.
|
||||
|
||||
## TiKV
|
||||
[TiKV](https://github.com/pingcap/tikv) is a GEO-replicated, high-performance, distributed, transactional key-value database. TiKV is powered by Rust and Raft. TiKV uses RocksDB as its persistence layer.
|
||||
|
||||
## Apache Flink
|
||||
[Apache Flink](https://flink.apache.org/news/2016/03/08/release-1.0.0.html) uses RocksDB to store state locally on a machine.
|
||||
|
||||
## Dgraph
|
||||
[Dgraph](https://github.com/dgraph-io/dgraph) is an open-source, scalable, distributed, low latency, high throughput Graph database .They use RocksDB to store state locally on a machine.
|
||||
|
||||
## Uber
|
||||
[Uber](http://eng.uber.com/cherami/) uses RocksDB as a durable and and scalable task queue.
|
||||
|
||||
## 360 Pika
|
||||
[360](http://www.360.cn/) [Pika](https://github.com/Qihoo360/pika) is a nosql compatible with redis. With the huge amount of data stored, redis may suffer for a capacity bottleneck, and pika was born for solving it. It has widely been widely used in many company
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
# Vagrant file
|
||||
Vagrant.configure("2") do |config|
|
||||
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
|
||||
+5
-4
@@ -1,8 +1,9 @@
|
||||
version: 1.0.{build}
|
||||
image: Visual Studio 2015
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 12 Win64" -DOPTDBG=1 -DXPRESS=1 ..
|
||||
- cmake -G "Visual Studio 14 2015 Win64" -DOPTDBG=1 -DXPRESS=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
@@ -10,7 +11,7 @@ build:
|
||||
verbosity: minimal
|
||||
test:
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -EnableRerun -Run db_test2 -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -EnableRerun -Run db_test -Exclude DBTest.GroupCommitTest -Concurrency 10
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run env_test -Concurrency 1
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run db_test2 -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run db_test -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -Run env_test,env_basic_test -Concurrency 2
|
||||
|
||||
@@ -6,33 +6,66 @@
|
||||
* @phutil-library-version 2
|
||||
*/
|
||||
|
||||
phutil_register_library_map(array(
|
||||
'__library_version__' => 2,
|
||||
'class' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'cpp_linter/ArcanistCpplintLinter.php',
|
||||
'BaseDirectoryScopedFormatLinter' => 'cpp_linter/BaseDirectoryScopedFormatLinter.php',
|
||||
'FacebookArcanistConfiguration' => 'config/FacebookArcanistConfiguration.php',
|
||||
'FacebookFbcodeLintEngine' => 'lint_engine/FacebookFbcodeLintEngine.php',
|
||||
'FacebookFbcodeUnitTestEngine' => 'unit_engine/FacebookFbcodeUnitTestEngine.php',
|
||||
'FacebookHowtoevenLintEngine' => 'lint_engine/FacebookHowtoevenLintEngine.php',
|
||||
'FacebookHowtoevenLinter' => 'cpp_linter/FacebookHowtoevenLinter.php',
|
||||
'FbcodeClangFormatLinter' => 'cpp_linter/FbcodeClangFormatLinter.php',
|
||||
'FbcodeCppLinter' => 'cpp_linter/FbcodeCppLinter.php',
|
||||
),
|
||||
'function' =>
|
||||
array(
|
||||
),
|
||||
'xmap' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'ArcanistLinter',
|
||||
'BaseDirectoryScopedFormatLinter' => 'ArcanistLinter',
|
||||
'FacebookArcanistConfiguration' => 'ArcanistConfiguration',
|
||||
'FacebookFbcodeLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookFbcodeUnitTestEngine' => 'ArcanistBaseUnitTestEngine',
|
||||
'FacebookHowtoevenLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookHowtoevenLinter' => 'ArcanistLinter',
|
||||
'FbcodeClangFormatLinter' => 'BaseDirectoryScopedFormatLinter',
|
||||
'FbcodeCppLinter' => 'ArcanistLinter',
|
||||
),
|
||||
));
|
||||
if (class_exists('ArcanistWorkflow')) {
|
||||
phutil_register_library_map(array(
|
||||
'__library_version__' => 2,
|
||||
'class' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'cpp_linter/ArcanistCpplintLinter.php',
|
||||
'BaseDirectoryScopedFormatLinter' => 'cpp_linter/BaseDirectoryScopedFormatLinter.php',
|
||||
'FacebookArcanistConfiguration' => 'config/FacebookArcanistConfiguration.php',
|
||||
'FacebookFbcodeLintEngine' => 'lint_engine/FacebookFbcodeLintEngine.php',
|
||||
'FacebookFbcodeUnitTestEngine' => 'unit_engine/FacebookFbcodeUnitTestEngine.php',
|
||||
'FacebookHowtoevenLintEngine' => 'lint_engine/FacebookHowtoevenLintEngine.php',
|
||||
'FacebookHowtoevenLinter' => 'cpp_linter/FacebookHowtoevenLinter.php',
|
||||
'FbcodeClangFormatLinter' => 'cpp_linter/FbcodeClangFormatLinter.php',
|
||||
'FbcodeCppLinter' => 'cpp_linter/FbcodeCppLinter.php',
|
||||
),
|
||||
'function' =>
|
||||
array(
|
||||
),
|
||||
'xmap' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'ArcanistLinter',
|
||||
'BaseDirectoryScopedFormatLinter' => 'ArcanistLinter',
|
||||
'FacebookArcanistConfiguration' => 'ArcanistConfiguration',
|
||||
'FacebookFbcodeLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookFbcodeUnitTestEngine' => 'ArcanistBaseUnitTestEngine',
|
||||
'FacebookHowtoevenLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookHowtoevenLinter' => 'ArcanistLinter',
|
||||
'FbcodeClangFormatLinter' => 'BaseDirectoryScopedFormatLinter',
|
||||
'FbcodeCppLinter' => 'ArcanistLinter',
|
||||
),
|
||||
));
|
||||
} else {
|
||||
phutil_register_library_map(array(
|
||||
'__library_version__' => 2,
|
||||
'class' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'cpp_linter/ArcanistCpplintLinter.php',
|
||||
'BaseDirectoryScopedFormatLinter' => 'cpp_linter/BaseDirectoryScopedFormatLinter.php',
|
||||
'FacebookArcanistConfiguration' => 'config/FacebookOldArcanistConfiguration.php',
|
||||
'FacebookFbcodeLintEngine' => 'lint_engine/FacebookFbcodeLintEngine.php',
|
||||
'FacebookFbcodeUnitTestEngine' => 'unit_engine/FacebookOldFbcodeUnitTestEngine.php',
|
||||
'FacebookHowtoevenLintEngine' => 'lint_engine/FacebookHowtoevenLintEngine.php',
|
||||
'FacebookHowtoevenLinter' => 'cpp_linter/FacebookHowtoevenLinter.php',
|
||||
'FbcodeClangFormatLinter' => 'cpp_linter/FbcodeClangFormatLinter.php',
|
||||
'FbcodeCppLinter' => 'cpp_linter/FbcodeCppLinter.php',
|
||||
),
|
||||
'function' =>
|
||||
array(
|
||||
),
|
||||
'xmap' =>
|
||||
array(
|
||||
'ArcanistCpplintLinter' => 'ArcanistLinter',
|
||||
'BaseDirectoryScopedFormatLinter' => 'ArcanistLinter',
|
||||
'FacebookArcanistConfiguration' => 'ArcanistConfiguration',
|
||||
'FacebookFbcodeLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookFbcodeUnitTestEngine' => 'ArcanistBaseUnitTestEngine',
|
||||
'FacebookHowtoevenLintEngine' => 'ArcanistLintEngine',
|
||||
'FacebookHowtoevenLinter' => 'ArcanistLinter',
|
||||
'FbcodeClangFormatLinter' => 'BaseDirectoryScopedFormatLinter',
|
||||
'FbcodeCppLinter' => 'ArcanistLinter',
|
||||
),
|
||||
));
|
||||
}
|
||||
@@ -4,203 +4,40 @@
|
||||
// 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.
|
||||
|
||||
require('RocksDBCommonHelper.php');
|
||||
|
||||
define("DIFF_COMMAND", "diff");
|
||||
|
||||
class FacebookArcanistConfiguration extends ArcanistConfiguration {
|
||||
public function getCustomArgumentsForCommand($command) {
|
||||
if ($command == "land") {
|
||||
return array(
|
||||
'async' => array('help' => 'Just to make tools happy'));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function didRunWorkflow($command,
|
||||
ArcanistBaseWorkflow $workflow,
|
||||
ArcanistWorkflow $workflow,
|
||||
$error_code) {
|
||||
if ($command == 'diff' && !$workflow->isRawDiffSource()) {
|
||||
$this->startTestsInJenkins($workflow);
|
||||
$this->startTestsInSandcastle($workflow);
|
||||
// Default options don't terminate on failure, but that's what we want. In
|
||||
// the current case we use assertions intentionally as "terminate on failure
|
||||
// invariants".
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
|
||||
assert($workflow);
|
||||
assert(strlen($command) > 0);
|
||||
|
||||
if ($command == DIFF_COMMAND && !$workflow->isRawDiffSource()) {
|
||||
$diffID = $workflow->getDiffId();
|
||||
|
||||
// When submitting a diff this code path gets executed multiple times in
|
||||
// a row. We only care about the case when ID for the diff is provided
|
||||
// because that's what we need to apply the diff and trigger the tests.
|
||||
if (strlen($diffID) > 0) {
|
||||
assert(is_numeric($diffID));
|
||||
startTestsInSandcastle(true /* $applyDiff */, $workflow, $diffID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* Run tests in sandcastle */
|
||||
function postURL($diffID, $url) {
|
||||
$cmd = 'echo \'{"diff_id": "' . $diffID . '", '
|
||||
. '"name":"click here for sandcastle tests for D' . $diffID . '", '
|
||||
. '"link":"' . $url . '"}\' | '
|
||||
. 'http_proxy=fwdproxy.any.facebook.com:8080 '
|
||||
. 'https_proxy=fwdproxy.any.facebook.com:8080 arc call-conduit '
|
||||
. 'differential.updateunitresults';
|
||||
shell_exec($cmd);
|
||||
}
|
||||
|
||||
function updateTestCommand($diffID, $test, $status) {
|
||||
$cmd = 'echo \'{"diff_id": "' . $diffID . '", '
|
||||
. '"name":"' . $test . '", '
|
||||
. '"result":"' . $status . '"}\' | '
|
||||
. 'http_proxy=fwdproxy.any.facebook.com:8080 '
|
||||
. 'https_proxy=fwdproxy.any.facebook.com:8080 arc call-conduit '
|
||||
. 'differential.updateunitresults';
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
function updateTest($diffID, $test) {
|
||||
shell_exec($this->updateTestCommand($diffID, $test, "waiting"));
|
||||
}
|
||||
|
||||
function getSteps($diffID, $username, $test) {
|
||||
$arcrc_content = exec("cat ~/.arcrc | gzip -f | base64 -w0");
|
||||
|
||||
// Sandcastle machines don't have arc setup. We copy the user certificate
|
||||
// and authenticate using that in sandcastle
|
||||
$setup = array(
|
||||
"name" => "Setup arcrc",
|
||||
"shell" => "echo " . $arcrc_content . " | base64 --decode"
|
||||
. " | gzip -d > ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// arc demands certain permission on its config
|
||||
$fix_permission = array(
|
||||
"name" => "Fix environment",
|
||||
"shell" => "chmod 600 ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// fbcode is a sub-repo. We cannot patch until we add it to ignore otherwise
|
||||
// git thinks it is uncommited change
|
||||
$fix_git_ignore = array(
|
||||
"name" => "Fix git ignore",
|
||||
"shell" => "echo fbcode >> .git/info/exclude",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Patch the code (keep your fingures crossed)
|
||||
$patch = array(
|
||||
"name" => "Patch " . $diffID,
|
||||
"shell" => "HTTPS_PROXY=fwdproxy:8080 arc --arcrc-file ~/.arcrc "
|
||||
. "patch --diff " . $diffID,
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Clean up the user arc config we are using
|
||||
$cleanup = array(
|
||||
"name" => "Arc cleanup",
|
||||
"shell" => "rm -f ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Construct the steps in the order of execution
|
||||
$steps[] = $setup;
|
||||
$steps[] = $fix_permission;
|
||||
$steps[] = $fix_git_ignore;
|
||||
$steps[] = $patch;
|
||||
|
||||
// Run the actual command
|
||||
$this->updateTest($diffID, $test);
|
||||
$cmd = $this->updateTestCommand($diffID, $test, "running") . ";"
|
||||
. "./build_tools/precommit_checker.py " . $test
|
||||
. "; exit_code=$?; ([[ \$exit_code -eq 0 ]] &&"
|
||||
. $this->updateTestCommand($diffID, $test, "pass") . ")"
|
||||
. "||" . $this->updateTestCommand($diffID, $test, "fail")
|
||||
. "; cat /tmp/precommit-check.log"
|
||||
. "; for f in `ls t/log-*`; do echo \$f; cat \$f; done;"
|
||||
. "[[ \$exit_code -eq 0 ]]";
|
||||
|
||||
$run_test = array(
|
||||
"name" => "Run " . $test,
|
||||
"shell" => $cmd,
|
||||
"user" => "root",
|
||||
);
|
||||
|
||||
$steps[] = $run_test;
|
||||
$steps[] = $cleanup;
|
||||
|
||||
return $steps;
|
||||
}
|
||||
|
||||
function startTestsInSandcastle($workflow) {
|
||||
// extract information we need from workflow or CLI
|
||||
$diffID = $workflow->getDiffId();
|
||||
$username = exec("whoami");
|
||||
|
||||
if ($diffID == null || $username == null) {
|
||||
// there is no diff and we can't extract username
|
||||
// we cannot schedule sandcasstle job
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(getenv("ROCKSDB_CHECK_ALL"), 1) == 0) {
|
||||
// extract all tests from the CI definition
|
||||
$output = file_get_contents("build_tools/rocksdb-lego-determinator");
|
||||
preg_match_all('/[ ]{2}([a-zA-Z0-9_]+)[\)]{1}/', $output, $matches);
|
||||
$tests = $matches[1];
|
||||
} else {
|
||||
// manually list of tests we want to run in sandcastle
|
||||
$tests = array(
|
||||
"unit", "unit_481", "clang_unit", "tsan", "asan", "lite_test", "valgrind"
|
||||
);
|
||||
}
|
||||
|
||||
// construct a job definition for each test and add it to the master plan
|
||||
foreach ($tests as $test) {
|
||||
$arg[] = array(
|
||||
"name" => "RocksDB diff " . $diffID . " test " . $test,
|
||||
"steps" => $this->getSteps($diffID, $username, $test)
|
||||
);
|
||||
}
|
||||
|
||||
// we cannot submit the parallel execution master plan to sandcastle
|
||||
// we need supply the job plan as a determinator
|
||||
// so we construct a small job that will spit out the master job plan
|
||||
// which sandcastle will parse and execute
|
||||
// Why compress ? Otherwise we run over the max string size.
|
||||
$cmd = "echo " . base64_encode(json_encode($arg))
|
||||
. " | gzip -f | base64 -w0";
|
||||
$arg_encoded = shell_exec($cmd);
|
||||
|
||||
$command = array(
|
||||
"name" => "Run diff " . $diffID . "for user " . $username,
|
||||
"steps" => array()
|
||||
);
|
||||
|
||||
$command["steps"][] = array(
|
||||
"name" => "Generate determinator",
|
||||
"shell" => "echo " . $arg_encoded . " | base64 --decode | gzip -d"
|
||||
. " | base64 --decode",
|
||||
"determinator" => true,
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// submit to sandcastle
|
||||
$url = 'https://interngraph.intern.facebook.com/sandcastle/generate?'
|
||||
.'command=SandcastleUniversalCommand'
|
||||
.'&vcs=rocksdb-git&revision=origin%2Fmaster&type=lego'
|
||||
.'&user=krad&alias=rocksdb-precommit'
|
||||
.'&command-args=' . urlencode(json_encode($command));
|
||||
|
||||
$cmd = 'https_proxy= HTTPS_PROXY= curl -s -k -F app=659387027470559 '
|
||||
. '-F token=AeO_3f2Ya3TujjnxGD4 "' . $url . '"';
|
||||
|
||||
$output = shell_exec($cmd);
|
||||
|
||||
// extract sandcastle URL from the response
|
||||
preg_match('/url": "(.+)"/', $output, $sandcastle_url);
|
||||
|
||||
echo "\nSandcastle URL: " . $sandcastle_url[1] . "\n";
|
||||
|
||||
// Ask phabricator to display it on the diff UI
|
||||
$this->postURL($diffID, $sandcastle_url[1]);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* Send off builds to jenkins */
|
||||
function startTestsInJenkins($workflow) {
|
||||
$diffID = $workflow->getDiffID();
|
||||
if ($diffID === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$results = $workflow->getTestResults();
|
||||
if (!$results) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = "https://ci-builds.fb.com/view/rocksdb/job/rocksdb_diff_check/"
|
||||
."buildWithParameters?token=AUTH&DIFF_ID=$diffID";
|
||||
system("curl --noproxy '*' \"$url\" > /dev/null 2>&1");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. 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.
|
||||
|
||||
require('RocksDBCommonHelper.php');
|
||||
|
||||
define("DIFF_COMMAND", "diff");
|
||||
|
||||
class FacebookArcanistConfiguration extends ArcanistConfiguration {
|
||||
public function getCustomArgumentsForCommand($command) {
|
||||
if ($command == "land") {
|
||||
return array(
|
||||
'async' => array('help' => 'Just to make tools happy'));
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function didRunWorkflow($command,
|
||||
ArcanistBaseWorkflow $workflow,
|
||||
$error_code) {
|
||||
// Default options don't terminate on failure, but that's what we want. In
|
||||
// the current case we use assertions intentionally as "terminate on failure
|
||||
// invariants".
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
|
||||
assert($workflow);
|
||||
assert(strlen($command) > 0);
|
||||
|
||||
if ($command == DIFF_COMMAND && !$workflow->isRawDiffSource()) {
|
||||
$diffID = $workflow->getDiffId();
|
||||
|
||||
// When submitting a diff this code path gets executed multiple times in
|
||||
// a row. We only care about the case when ID for the diff is provided
|
||||
// because that's what we need to apply the diff and trigger the tests.
|
||||
if (strlen($diffID) > 0) {
|
||||
assert(is_numeric($diffID));
|
||||
startTestsInSandcastle(true /* $applyDiff */, $workflow, $diffID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. 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.
|
||||
|
||||
// Name of the environment variables which need to be set by the entity which
|
||||
// triggers continuous runs so that code at the end of the file gets executed
|
||||
// and Sandcastle run starts.
|
||||
define("ENV_POST_RECEIVE_HOOK", "POST_RECEIVE_HOOK");
|
||||
define("ENV_HTTPS_APP_VALUE", "HTTPS_APP_VALUE");
|
||||
define("ENV_HTTPS_TOKEN_VALUE", "HTTPS_TOKEN_VALUE");
|
||||
|
||||
define("PRIMARY_TOKEN_FILE", '/home/krad/.sandcastle');
|
||||
define("CONT_RUN_ALIAS", "leveldb");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
/* Run tests in sandcastle */
|
||||
function postURL($diffID, $url) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($url) > 0);
|
||||
|
||||
$cmd = 'echo \'{"diff_id": ' . $diffID . ', '
|
||||
. '"name":"click here for sandcastle tests for D' . $diffID . '", '
|
||||
. '"link":"' . $url . '"}\' | '
|
||||
. 'arc call-conduit '
|
||||
. 'differential.updateunitresults';
|
||||
shell_exec($cmd);
|
||||
}
|
||||
|
||||
function buildUpdateTestStatusCmd($diffID, $test, $status) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($test) > 0);
|
||||
assert(strlen($status) > 0);
|
||||
|
||||
$cmd = 'echo \'{"diff_id": ' . $diffID . ', '
|
||||
. '"name":"' . $test . '", '
|
||||
. '"result":"' . $status . '"}\' | '
|
||||
. 'arc call-conduit '
|
||||
. 'differential.updateunitresults';
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
function updateTestStatus($diffID, $test) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
assert(strlen($test) > 0);
|
||||
|
||||
shell_exec(buildUpdateTestStatusCmd($diffID, $test, "waiting"));
|
||||
}
|
||||
|
||||
function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
assert(strlen($username) > 0);
|
||||
assert(strlen($test) > 0);
|
||||
|
||||
if ($applyDiff) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
|
||||
$arcrc_content = (PHP_OS == "Darwin" ?
|
||||
exec("cat ~/.arcrc | gzip -f | base64") :
|
||||
exec("cat ~/.arcrc | gzip -f | base64 -w0"));
|
||||
assert(strlen($arcrc_content) > 0);
|
||||
|
||||
// Sandcastle machines don't have arc setup. We copy the user certificate
|
||||
// and authenticate using that in Sandcastle.
|
||||
$setup = array(
|
||||
"name" => "Setup arcrc",
|
||||
"shell" => "echo " . $arcrc_content . " | base64 --decode"
|
||||
. " | gzip -d > ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// arc demands certain permission on its config.
|
||||
// also fix the sticky bit issue in sandcastle
|
||||
$fix_permission = array(
|
||||
"name" => "Fix environment",
|
||||
"shell" => "chmod 600 ~/.arcrc && chmod +t /dev/shm",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Construct the steps in the order of execution.
|
||||
$steps[] = $setup;
|
||||
$steps[] = $fix_permission;
|
||||
}
|
||||
|
||||
// fbcode is a sub-repo. We cannot patch until we add it to ignore otherwise
|
||||
// Git thinks it is an uncommited change.
|
||||
$fix_git_ignore = array(
|
||||
"name" => "Fix git ignore",
|
||||
"shell" => "echo fbcode >> .git/info/exclude",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $fix_git_ignore;
|
||||
|
||||
// This will be the command used to execute particular type of tests.
|
||||
$cmd = "";
|
||||
|
||||
if ($applyDiff) {
|
||||
// Patch the code (keep your fingures crossed).
|
||||
$patch = array(
|
||||
"name" => "Patch " . $diffID,
|
||||
"shell" => "arc --arcrc-file ~/.arcrc "
|
||||
. "patch --nocommit --diff " . $diffID,
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $patch;
|
||||
|
||||
updateTestStatus($diffID, $test);
|
||||
$cmd = buildUpdateTestStatusCmd($diffID, $test, "running") . "; ";
|
||||
}
|
||||
|
||||
// Run the actual command.
|
||||
$cmd = $cmd . "J=$(nproc) ./build_tools/precommit_checker.py " . $test
|
||||
. "; exit_code=$?; ";
|
||||
|
||||
if ($applyDiff) {
|
||||
$cmd = $cmd . "([[ \$exit_code -eq 0 ]] &&"
|
||||
. buildUpdateTestStatusCmd($diffID, $test, "pass") . ")"
|
||||
. "||" . buildUpdateTestStatusCmd($diffID, $test, "fail")
|
||||
. "; ";
|
||||
}
|
||||
|
||||
// shell command to sort the tests based on exit code and print
|
||||
// the output of the log files.
|
||||
$cat_sorted_logs = "
|
||||
while read code log_file;
|
||||
do echo \"################ cat \$log_file [exit_code : \$code] ################\";
|
||||
cat \$log_file;
|
||||
done < <(tail -n +2 LOG | sort -k7,7n -k4,4gr | awk '{print \$7,\$NF}')";
|
||||
|
||||
// Shell command to cat all log files
|
||||
$cat_all_logs = "for f in `ls t/!(run-*)`; do echo \$f;cat \$f; done";
|
||||
|
||||
// If LOG file exist use it to cat log files sorted by exit code, otherwise
|
||||
// cat everything
|
||||
$logs_cmd = "if [ -f LOG ]; then {$cat_sorted_logs}; else {$cat_all_logs}; fi";
|
||||
|
||||
$cmd = $cmd . " cat /tmp/precommit-check.log"
|
||||
. "; shopt -s extglob; {$logs_cmd}"
|
||||
. "; shopt -u extglob; [[ \$exit_code -eq 0 ]]";
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$run_test = array(
|
||||
"name" => "Run " . $test,
|
||||
"shell" => $cmd,
|
||||
"user" => "root",
|
||||
"parser" => "python build_tools/error_filter.py " . $test,
|
||||
);
|
||||
|
||||
$steps[] = $run_test;
|
||||
|
||||
if ($applyDiff) {
|
||||
// Clean up the user arc config we are using.
|
||||
$cleanup = array(
|
||||
"name" => "Arc cleanup",
|
||||
"shell" => "rm -f ~/.arcrc",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
$steps[] = $cleanup;
|
||||
}
|
||||
|
||||
assert(count($steps) > 0);
|
||||
return $steps;
|
||||
}
|
||||
|
||||
function getSandcastleConfig() {
|
||||
$sandcastle_config = array();
|
||||
|
||||
$cwd = getcwd();
|
||||
$cwd_token_file = "{$cwd}/.sandcastle";
|
||||
// This is a case when we're executed from a continuous run. Fetch the values
|
||||
// from the environment.
|
||||
if (getenv(ENV_POST_RECEIVE_HOOK)) {
|
||||
$sandcastle_config[0] = getenv(ENV_HTTPS_APP_VALUE);
|
||||
$sandcastle_config[1] = getenv(ENV_HTTPS_TOKEN_VALUE);
|
||||
} else {
|
||||
// This is a typical `[p]arc diff` case. Fetch the values from the specific
|
||||
// configuration files.
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
if (file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists($cwd_token_file)) {
|
||||
break;
|
||||
}
|
||||
// If we failed to fetch the tokens, sleep for 0.2 second and try again
|
||||
usleep(200000);
|
||||
}
|
||||
assert(file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists($cwd_token_file));
|
||||
|
||||
// Try the primary location first, followed by a secondary.
|
||||
if (file_exists(PRIMARY_TOKEN_FILE)) {
|
||||
$cmd = 'cat ' . PRIMARY_TOKEN_FILE;
|
||||
} else {
|
||||
$cmd = 'cat ' . $cwd_token_file;
|
||||
}
|
||||
|
||||
assert(strlen($cmd) > 0);
|
||||
$sandcastle_config = explode(':', rtrim(shell_exec($cmd)));
|
||||
}
|
||||
|
||||
// In this case be very explicit about the implications.
|
||||
if (count($sandcastle_config) != 2) {
|
||||
echo "Sandcastle configuration files don't contain valid information " .
|
||||
"or the necessary environment variables aren't defined. Unable " .
|
||||
"to validate the code changes.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
assert(strlen($sandcastle_config[0]) > 0);
|
||||
assert(strlen($sandcastle_config[1]) > 0);
|
||||
assert(count($sandcastle_config) > 0);
|
||||
|
||||
return $sandcastle_config;
|
||||
}
|
||||
|
||||
// This function can be called either from `[p]arc diff` command or during
|
||||
// the Git post-receive hook.
|
||||
function startTestsInSandcastle($applyDiff, $workflow, $diffID) {
|
||||
// Default options don't terminate on failure, but that's what we want. In
|
||||
// the current case we use assertions intentionally as "terminate on failure
|
||||
// invariants".
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
|
||||
// In case of a diff we'll send notificatios to the author. Else it'll go to
|
||||
// the entire team because failures indicate that build quality has regressed.
|
||||
$username = $applyDiff ? exec("whoami") : CONT_RUN_ALIAS;
|
||||
assert(strlen($username) > 0);
|
||||
|
||||
if ($applyDiff) {
|
||||
assert($workflow);
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
}
|
||||
|
||||
// List of tests we want to run in Sandcastle.
|
||||
$tests = array("unit", "unit_non_shm", "unit_481", "clang_unit", "tsan",
|
||||
"asan", "lite_test", "valgrind", "release", "release_481",
|
||||
"clang_release", "punit", "clang_analyze", "code_cov",
|
||||
"java_build", "no_compression", "unity", "ubsan");
|
||||
|
||||
$send_email_template = array(
|
||||
'type' => 'email',
|
||||
'triggers' => array('fail'),
|
||||
'emails' => array($username . '@fb.com'),
|
||||
);
|
||||
|
||||
// Construct a job definition for each test and add it to the master plan.
|
||||
foreach ($tests as $test) {
|
||||
$stepName = "RocksDB diff " . $diffID . " test " . $test;
|
||||
|
||||
if (!$applyDiff) {
|
||||
$stepName = "RocksDB continuous integration test " . $test;
|
||||
}
|
||||
|
||||
$arg[] = array(
|
||||
"name" => $stepName,
|
||||
"report" => array($send_email_template),
|
||||
"steps" => getSteps($applyDiff, $diffID, $username, $test)
|
||||
);
|
||||
}
|
||||
|
||||
// We cannot submit the parallel execution master plan to Sandcastle and
|
||||
// need supply the job plan as a determinator. So we construct a small job
|
||||
// that will spit out the master job plan which Sandcastle will parse and
|
||||
// execute. Why compress the job definitions? Otherwise we run over the max
|
||||
// string size.
|
||||
$cmd = "echo " . base64_encode(json_encode($arg))
|
||||
. (PHP_OS == "Darwin" ?
|
||||
" | gzip -f | base64" :
|
||||
" | gzip -f | base64 -w0");
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$arg_encoded = shell_exec($cmd);
|
||||
assert(strlen($arg_encoded) > 0);
|
||||
|
||||
$runName = "Run diff " . $diffID . "for user " . $username;
|
||||
|
||||
if (!$applyDiff) {
|
||||
$runName = "RocksDB continuous integration build and test run";
|
||||
}
|
||||
|
||||
$command = array(
|
||||
"name" => $runName,
|
||||
"steps" => array()
|
||||
);
|
||||
|
||||
$command["steps"][] = array(
|
||||
"name" => "Generate determinator",
|
||||
"shell" => "echo " . $arg_encoded . " | base64 --decode | gzip -d"
|
||||
. " | base64 --decode",
|
||||
"determinator" => true,
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
// Submit to Sandcastle.
|
||||
$url = 'https://interngraph.intern.facebook.com/sandcastle/create';
|
||||
|
||||
$job = array(
|
||||
'command' => 'SandcastleUniversalCommand',
|
||||
'args' => $command,
|
||||
'capabilities' => array(
|
||||
'vcs' => 'rocksdb-int-git',
|
||||
'type' => 'lego',
|
||||
),
|
||||
'hash' => 'origin/master',
|
||||
'user' => $username,
|
||||
'alias' => 'rocksdb-precommit',
|
||||
'tags' => array('rocksdb'),
|
||||
'description' => 'Rocksdb precommit job',
|
||||
);
|
||||
|
||||
// Fetch the configuration necessary to submit a successful HTTPS request.
|
||||
$sandcastle_config = getSandcastleConfig();
|
||||
|
||||
$app = $sandcastle_config[0];
|
||||
$token = $sandcastle_config[1];
|
||||
|
||||
$cmd = 'curl -s -k -F app=' . $app . ' '
|
||||
. '-F token=' . $token . ' -F job=\'' . json_encode($job)
|
||||
.'\' "' . $url . '"';
|
||||
|
||||
$output = shell_exec($cmd);
|
||||
assert(strlen($output) > 0);
|
||||
|
||||
// Extract Sandcastle URL from the response.
|
||||
preg_match('/url": "(.+)"/', $output, $sandcastle_url);
|
||||
|
||||
assert(count($sandcastle_url) > 0, "Unable to submit Sandcastle request.");
|
||||
assert(strlen($sandcastle_url[1]) > 0, "Unable to extract Sandcastle URL.");
|
||||
|
||||
if ($applyDiff) {
|
||||
echo "\nSandcastle URL: " . $sandcastle_url[1] . "\n";
|
||||
// Ask Phabricator to display it on the diff UI.
|
||||
postURL($diffID, $sandcastle_url[1]);
|
||||
} else {
|
||||
echo "Continuous integration started Sandcastle tests. You can look at ";
|
||||
echo "the progress at:\n" . $sandcastle_url[1] . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Continuous run cript will set the environment variable and based on that
|
||||
// we'll trigger the execution of tests in Sandcastle. In that case we don't
|
||||
// need to apply any diffs and there's no associated workflow either.
|
||||
if (getenv(ENV_POST_RECEIVE_HOOK)) {
|
||||
startTestsInSandcastle(
|
||||
false /* $applyDiff */,
|
||||
NULL /* $workflow */,
|
||||
NULL /* $diffID */);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ abstract class BaseDirectoryScopedFormatLinter extends ArcanistLinter {
|
||||
$futures[$path] = $this->getFormatFuture($path, $changed);
|
||||
}
|
||||
|
||||
foreach (Futures($futures)->limit(8) as $p => $f) {
|
||||
foreach (id(new FutureIterator($futures))->limit(8) as $p => $f) {
|
||||
$this->rawLintOutput[$p] = $f->resolvex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,12 @@ class FacebookFbcodeLintEngine extends ArcanistLintEngine {
|
||||
$python_linter = new ArcanistPEP8Linter();
|
||||
$linters[] = $python_linter;
|
||||
|
||||
if (!$this->getCommitHookMode()) {
|
||||
$cpp_linters = array();
|
||||
$cpp_linters[] = $linters[] = new ArcanistCpplintLinter();
|
||||
$cpp_linters[] = $linters[] = new FbcodeCppLinter();
|
||||
$cpp_linters = array();
|
||||
$cpp_linters[] = $linters[] = new ArcanistCpplintLinter();
|
||||
$cpp_linters[] = $linters[] = new FbcodeCppLinter();
|
||||
|
||||
$clang_format_linter = new FbcodeClangFormatLinter();
|
||||
$linters[] = $clang_format_linter;
|
||||
}
|
||||
$clang_format_linter = new FbcodeClangFormatLinter();
|
||||
$linters[] = $clang_format_linter;
|
||||
|
||||
$spelling_linter = new ArcanistSpellingLinter();
|
||||
$linters[] = $spelling_linter;
|
||||
|
||||
@@ -4,18 +4,14 @@
|
||||
// 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.
|
||||
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistBaseUnitTestEngine {
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistUnitTestEngine {
|
||||
|
||||
public function run() {
|
||||
// Here we create a new unit test "jenkins_async_test" and promise we'll
|
||||
// update the results later.
|
||||
// Jenkins updates the results using `arc call-conduit
|
||||
// differential.updateunitresults` call. If you change the name here, also
|
||||
// make sure to change the name in Jenkins script that updates the test
|
||||
// result -- they have to be the same.
|
||||
$result = new ArcanistUnitTestResult();
|
||||
$result->setName("jenkins_async_test");
|
||||
$result->setResult(ArcanistUnitTestResult::RESULT_POSTPONED);
|
||||
return array($result);
|
||||
// For a call to `arc call-conduit differential.updateunitresults` to
|
||||
// succeed we need at least one entry here.
|
||||
$result = new ArcanistUnitTestResult();
|
||||
$result->setName("dummy_placeholder_entry");
|
||||
$result->setResult(ArcanistUnitTestResult::RESULT_PASS);
|
||||
return array($result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. 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.
|
||||
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistBaseUnitTestEngine {
|
||||
|
||||
public function run() {
|
||||
// For a call to `arc call-conduit differential.updateunitresults` to
|
||||
// succeed we need at least one entry here.
|
||||
$result = new ArcanistUnitTestResult();
|
||||
$result->setName("dummy_placeholder_entry");
|
||||
$result->setResult(ArcanistUnitTestResult::RESULT_PASS);
|
||||
return array($result);
|
||||
}
|
||||
}
|
||||
@@ -19,22 +19,21 @@
|
||||
#
|
||||
# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following:
|
||||
#
|
||||
# -DLEVELDB_PLATFORM_POSIX if cstdatomic is present
|
||||
# -DLEVELDB_PLATFORM_NOATOMIC if it is not
|
||||
# -DROCKSDB_PLATFORM_POSIX if posix-platform based
|
||||
# -DSNAPPY if the Snappy library is present
|
||||
# -DLZ4 if the LZ4 library is present
|
||||
# -DZSTD if the ZSTD library is present
|
||||
# -DNUMA if the NUMA library is present
|
||||
# -DTBB if the TBB library is present
|
||||
#
|
||||
# Using gflags in rocksdb:
|
||||
# Our project depends on gflags, which requires users to take some extra steps
|
||||
# before they can compile the whole repository:
|
||||
# 1. Install gflags. You may download it from here:
|
||||
# https://code.google.com/p/gflags/
|
||||
# 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 having brew installed and running brew install gflags
|
||||
# https://gflags.github.io/gflags/ (Mac users can `brew install gflags`)
|
||||
# 2. Once installed, add the include path for gflags to your CPATH env var and
|
||||
# the lib path to LIBRARY_PATH. If installed with default settings, the lib
|
||||
# will be /usr/local/lib and the include path will be /usr/local/include
|
||||
|
||||
OUTPUT=$1
|
||||
if test -z "$OUTPUT"; then
|
||||
@@ -52,12 +51,7 @@ 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
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
fi
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
@@ -170,7 +164,7 @@ case "$TARGET_OS" in
|
||||
;;
|
||||
OS_ANDROID_CROSSCOMPILE)
|
||||
PLATFORM=OS_ANDROID
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_ANDROID -DROCKSDB_PLATFORM_POSIX"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS " # All pthread features are in the Android C library
|
||||
# PORT_FILES=port/android/android.cc
|
||||
CROSS_COMPILE=true
|
||||
@@ -288,7 +282,7 @@ EOF
|
||||
# Test whether numa is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null <<EOF
|
||||
#include <numa.h>
|
||||
#inlcude <numaif.h>
|
||||
#include <numaif.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -297,6 +291,17 @@ EOF
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lnuma"
|
||||
fi
|
||||
|
||||
# Test whether tbb is available
|
||||
$CXX $CFLAGS $LDFLAGS -x c++ - -o /dev/null -ltbb 2>/dev/null <<EOF
|
||||
#include <tbb/tbb.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DTBB"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltbb"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltbb"
|
||||
fi
|
||||
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null -ljemalloc \
|
||||
2>/dev/null; then
|
||||
@@ -456,3 +461,4 @@ echo "PROFILING_FLAGS=$PROFILING_FLAGS" >> "$OUTPUT"
|
||||
if test -n "$JEMALLOC"; then
|
||||
echo "JEMALLOC=1" >> "$OUTPUT"
|
||||
fi
|
||||
echo "LUA_PATH=$LUA_PATH" >> "$OUTPUT"
|
||||
Executable
+135
@@ -0,0 +1,135 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2016, Facebook. All rights reserved.
|
||||
#
|
||||
# Overall wrapper script for RocksDB continuous builds. The implementation is a
|
||||
# trivial pulling scheme. We loop infinitely, check if any new changes have been
|
||||
# committed, if yes then trigger a Sandcastle run, and finally go to sleep again
|
||||
# for a certain interval.
|
||||
#
|
||||
|
||||
SRC_GIT_REPO=/data/git/rocksdb-public
|
||||
error=0
|
||||
|
||||
function log {
|
||||
DATE=`date +%Y-%m-%d:%H:%M:%S`
|
||||
echo $DATE $@
|
||||
}
|
||||
|
||||
function log_err {
|
||||
log "ERROR: $@ Error code: $error."
|
||||
}
|
||||
|
||||
function update_repo_status {
|
||||
# Update the parent first.
|
||||
pushd $SRC_GIT_REPO
|
||||
|
||||
# This is a fatal error. Something in the environment isn't right and we will
|
||||
# terminate the execution.
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "Where is $SRC_GIT_REPO?"
|
||||
exit $error
|
||||
fi
|
||||
|
||||
HTTPS_PROXY=fwdproxy:8080 git fetch -f
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git fetch -f failed."
|
||||
popd
|
||||
return $error
|
||||
fi
|
||||
|
||||
git update-ref refs/heads/master refs/remotes/origin/master
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git update-ref failed."
|
||||
popd
|
||||
return $error
|
||||
fi
|
||||
|
||||
popd
|
||||
|
||||
# We're back in an instance-specific directory. Get the latest changes.
|
||||
git pull --rebase
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "git pull --rebase failed."
|
||||
return $error
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Execution starts here.
|
||||
#
|
||||
|
||||
# Path to the determinator from the root of the RocksDB repo.
|
||||
CONTRUN_DETERMINATOR=./arcanist_util/config/RocksDBCommonHelper.php
|
||||
|
||||
# Value of the previous commit.
|
||||
PREV_COMMIT=
|
||||
|
||||
log "Starting to monitor for new RocksDB changes ..."
|
||||
log "Running under `pwd` as `whoami`."
|
||||
|
||||
# Paranoia. Make sure that we're using the right branch.
|
||||
git checkout master
|
||||
|
||||
error=$?
|
||||
if [ ! $error -eq 0 ]; then
|
||||
log_err "This is not good. Can't checkout master. Bye-bye!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# We'll run forever and let the execution environment terminate us if we'll
|
||||
# exceed whatever timeout is set for the job.
|
||||
while true;
|
||||
do
|
||||
# Get the latest changes committed.
|
||||
update_repo_status
|
||||
|
||||
error=$?
|
||||
if [ $error -eq 0 ]; then
|
||||
LAST_COMMIT=`git log -1 | head -1 | grep commit | awk '{ print $2; }'`
|
||||
|
||||
log "Last commit is '$LAST_COMMIT', previous commit is '$PREV_COMMIT'."
|
||||
|
||||
if [ "$PREV_COMMIT" == "$LAST_COMMIT" ]; then
|
||||
log "There were no changes since the last time I checked. Going to sleep."
|
||||
else
|
||||
if [ ! -z "$LAST_COMMIT" ]; then
|
||||
log "New code has been committed or previous commit not known. " \
|
||||
"Will trigger the tests."
|
||||
|
||||
PREV_COMMIT=$LAST_COMMIT
|
||||
log "Updated previous commit to '$PREV_COMMIT'."
|
||||
|
||||
#
|
||||
# This is where we'll trigger the Sandcastle run. The values for
|
||||
# HTTPS_APP_VALUE and HTTPS_APP_VALUE will be set in the container we're
|
||||
# running in.
|
||||
#
|
||||
POST_RECEIVE_HOOK=1 php $CONTRUN_DETERMINATOR
|
||||
|
||||
error=$?
|
||||
if [ $error -eq 0 ]; then
|
||||
log "Sandcastle run successfully triggered."
|
||||
else
|
||||
log_err "Failed to trigger Sandcastle run."
|
||||
fi
|
||||
else
|
||||
log_err "Previous commit not updated. Don't know what the last one is."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_err "Getting latest changes failed. Will skip running tests for now."
|
||||
fi
|
||||
|
||||
# Always sleep, even if errors happens while trying to determine the latest
|
||||
# commit. This will prevent us terminating in case of transient errors.
|
||||
log "Will go to sleep for 5 minutes."
|
||||
sleep 5m
|
||||
done
|
||||
+19
-16
@@ -1,16 +1,19 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/ebc96bc2fb751b5a0300b8d91a95bdf24ac1d88b/4.9.x/centos6-native/108cf83
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/b91de48a4974ec839946d824402b098d43454cef/stable/centos6-native/7aaccbe
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/53e0eac8911888a105aa98b9a35fe61cf1d8b278/4.9.x/gcc-4.9-glibc-2.20/024dbc3
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/ee36ac9a72dfac4a995f1b215bb4c0fc8a0f6f91/2.20/gcc-4.9-glibc-2.20/500e281
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.9-glibc-2.20/e9936bf
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2b24f1e99454f9ca7b0301720f94836dae1bf71b/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/af7c14c9b652cdd5ec34eadd25c3f38a9b306c5d/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/1408484d03b15492aa54b10356104e9dc22e1cc5/0.6.1/gcc-5-glibc-2.23/9bc6787
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/5a5c7a6608cb32f1e1e7f814023d5bdfbd136370/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/a4c2adecffcaa68d5585d06be2252e3efa52555b/master/gcc-5-glibc-2.23/1c32b4b
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/1abc0d3c01743b854676423cf2d3629912f34930/2.0.8/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-5-glibc-2.23/b1847cb
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/f24be37d170e04be6e469af487644d4d62e1c6c1/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/9d65c666b9adf8f2a989fd4b98a9a5e7d3afa233/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/9cee5a3628dc9d4b93897972c58eba865e25b270/3.10.0/gcc-4.9-glibc-2.20/e9936bf
|
||||
# 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,16 +1,18 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/ebc96bc2fb751b5a0300b8d91a95bdf24ac1d88b/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/b91de48a4974ec839946d824402b098d43454cef/stable/centos6-native/7aaccbe
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/53e0eac8911888a105aa98b9a35fe61cf1d8b278/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/ee36ac9a72dfac4a995f1b215bb4c0fc8a0f6f91/2.17/gcc-4.8.1-glibc-2.17/99df8fc
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/d282e6e8f3d20f4e40a516834847bdc038e07973/2.17/gcc-4.8.1-glibc-2.17/99df8fc
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2b24f1e99454f9ca7b0301720f94836dae1bf71b/1.2.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/af7c14c9b652cdd5ec34eadd25c3f38a9b306c5d/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0882df3713c7a84f15abe368dc004581f20b39d7/1.2.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/740325875f6729f42d28deaa2147b0854f3a347e/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/1408484d03b15492aa54b10356104e9dc22e1cc5/0.6.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/5a5c7a6608cb32f1e1e7f814023d5bdfbd136370/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/a4c2adecffcaa68d5585d06be2252e3efa52555b/master/gcc-4.8.1-glibc-2.17/8d31e51
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/1abc0d3c01743b854676423cf2d3629912f34930/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/9455f75ff7f4831dc9fda02a6a0f8c68922fad8f/1.0.0/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f001a51b2854957676d07306ef3abf67186b5c8b/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/fc8a13ca1fffa4d0765c716c5a0b49f0c107518f/master/gcc-4.8.1-glibc-2.17/8d31e51
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/17c514c4d102a25ca15f4558be564eeed76f4b6a/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-4.8.1-glibc-2.17/675d945
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/f24be37d170e04be6e469af487644d4d62e1c6c1/4.0.9-36_fbk5_2933_gd092e3f/gcc-4.8.1-glibc-2.17/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/9d65c666b9adf8f2a989fd4b98a9a5e7d3afa233/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/9cee5a3628dc9d4b93897972c58eba865e25b270/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/7c111ff27e0c466235163f00f280a9d617c3d2ec/4.0.9-36_fbk5_2933_gd092e3f/gcc-4.8.1-glibc-2.17/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b7fd454c4b10c6a81015d4524ed06cdeab558490/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/61e4abf5813bbc39bc4f548757ccfcadde175a48/5.2.3/centos6-native/730f94e
|
||||
@@ -117,8 +117,11 @@ class TsanErrorParser(MatchErrorParser):
|
||||
_TEST_NAME_TO_PARSERS = {
|
||||
'punit': [CompilerErrorParser, GTestErrorParser],
|
||||
'unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'release': [CompilerErrorParser, GTestErrorParser],
|
||||
'unit_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'release_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_release': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_analyze': [CompilerErrorParser, ScanBuildErrorParser],
|
||||
'code_cov': [CompilerErrorParser, GTestErrorParser],
|
||||
'unity': [CompilerErrorParser, GTestErrorParser],
|
||||
|
||||
@@ -13,8 +13,8 @@ source "$BASEDIR/dependencies.sh"
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/libs"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
@@ -43,12 +43,16 @@ if test -z $PIC_BUILD; then
|
||||
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
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
@@ -56,7 +60,7 @@ if test -z $PIC_BUILD; then
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=google"
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
@@ -72,13 +76,22 @@ if test -z $PIC_BUILD; then
|
||||
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=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -87,7 +100,7 @@ CLANG_LIB="$CLANG_BASE/lib"
|
||||
CLANG_SRC="$CLANG_BASE/../../src"
|
||||
|
||||
CLANG_ANALYZER="$CLANG_BIN/clang++"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/clang/tools/scan-build/scan-build"
|
||||
CLANG_SCAN_BUILD="$CLANG_SRC/llvm/tools/clang/tools/scan-build/bin/scan-build"
|
||||
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
# gcc
|
||||
@@ -95,8 +108,8 @@ if [ -z "$USE_CLANG" ]; then
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
@@ -107,8 +120,8 @@ else
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.9.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.9.x/x86_64-facebook-linux "
|
||||
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"
|
||||
@@ -119,18 +132,21 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
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 $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.9-glibc-2.20/lib/ld.so"
|
||||
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/gcc-4.9-glibc-2.20/lib"
|
||||
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 $ZSTD_LIBS $GFLAGS_LIBS"
|
||||
EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $TBB_LIBS"
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
@@ -9,7 +9,7 @@ source "$BASEDIR/dependencies_4.8.1.sh"
|
||||
|
||||
# location of libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/libs"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# location of glibc
|
||||
GLIBC_INCLUDE="$GLIBC_BASE/include"
|
||||
@@ -48,13 +48,17 @@ NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
|
||||
# location of tbb
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP2_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP2_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -90,13 +94,15 @@ fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DZSTD -DNUMA"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DZSTD -DNUMA -DTBB"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
@@ -104,4 +110,6 @@ EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $G
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE
|
||||
LUA_PATH="$LUA_BASE"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE LUA_PATH
|
||||
@@ -11,7 +11,8 @@ if ! which $CLANG_FORMAT_DIFF &> /dev/null
|
||||
then
|
||||
echo "You didn't have clang-format-diff.py available in your computer!"
|
||||
echo "You can download it by running: "
|
||||
echo " curl http://goo.gl/iUW1u2"
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "and move ${CLANG_FORMAT_DIFF} to some directory within PATH=${PATH}"
|
||||
exit 128
|
||||
fi
|
||||
|
||||
@@ -50,14 +51,15 @@ fi
|
||||
set -e
|
||||
|
||||
uncommitted_code=`git diff HEAD`
|
||||
LAST_MASTER=`git merge-base master HEAD`
|
||||
|
||||
# If there's no uncommitted changes, we assume user are doing post-commit
|
||||
# format check, in which case we'll check the modified lines from latest commit.
|
||||
# Otherwise, we'll check format of the uncommitted code only.
|
||||
# format check, in which case we'll check the modified lines since last commit
|
||||
# from master. Otherwise, we'll check format of the uncommitted code only.
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
# Check the format of last commit
|
||||
diffs=$(git diff -U0 HEAD^ | $CLANG_FORMAT_DIFF -p 1)
|
||||
diffs=$(git diff -U0 $LAST_MASTER^ | $CLANG_FORMAT_DIFF -p 1)
|
||||
else
|
||||
# Check the format of uncommitted lines,
|
||||
diffs=$(git diff -U0 HEAD | $CLANG_FORMAT_DIFF -p 1)
|
||||
@@ -97,7 +99,12 @@ then
|
||||
fi
|
||||
|
||||
# Do in-place format adjustment.
|
||||
git diff -U0 HEAD^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
git diff -U0 $LAST_MASTER^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
else
|
||||
git diff -U0 HEAD^ | $CLANG_FORMAT_DIFF -i -p 1
|
||||
fi
|
||||
echo "Files reformatted!"
|
||||
|
||||
# Amend to last commit if user do the post-commit format check
|
||||
|
||||
Executable
+7936
File diff suppressed because it is too large.
Load diff
@@ -65,8 +65,8 @@ function main() {
|
||||
|
||||
if [[ -d /vagrant ]]; then
|
||||
if [[ $OS = "ubuntu" ]]; then
|
||||
package g++-4.7
|
||||
export CXX=g++-4.7
|
||||
package g++-4.8
|
||||
export CXX=g++-4.8
|
||||
|
||||
# the deb would depend on libgflags2, but the static lib is the only thing
|
||||
# installed by make install
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/local/fbcode/gcc-4.8.1-glibc-2.17-fb/bin/python2.7
|
||||
#!/usr/local/fbcode/gcc-4.9-glibc-2.20-fb/bin/python2.7
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
@@ -12,17 +12,15 @@ import re
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
#
|
||||
# Simple logger
|
||||
#
|
||||
|
||||
|
||||
class Log:
|
||||
|
||||
LOG_FILE = "/tmp/precommit-check.log"
|
||||
|
||||
def __init__(self):
|
||||
self.filename = Log.LOG_FILE
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.f = open(self.filename, 'w+', 0)
|
||||
|
||||
def caption(self, str):
|
||||
@@ -52,9 +50,9 @@ class Log:
|
||||
|
||||
class Env(object):
|
||||
|
||||
def __init__(self, tests):
|
||||
def __init__(self, logfile, tests):
|
||||
self.tests = tests
|
||||
self.log = Log()
|
||||
self.log = Log(logfile)
|
||||
|
||||
def shell(self, cmd, path=os.getcwd()):
|
||||
if path:
|
||||
@@ -87,15 +85,16 @@ class Env(object):
|
||||
|
||||
class PreCommitChecker(Env):
|
||||
|
||||
def __init__(self, tests):
|
||||
Env.__init__(self, tests)
|
||||
def __init__(self, args):
|
||||
Env.__init__(self, args.logfile, args.tests)
|
||||
self.ignore_failure = args.ignore_failure
|
||||
|
||||
#
|
||||
# Get commands for a given job from the determinator file
|
||||
#
|
||||
def get_commands(self, test):
|
||||
status, out = self.GetOutput(
|
||||
"build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
"RATIO=1 build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
return status, out
|
||||
|
||||
#
|
||||
@@ -144,6 +143,7 @@ class PreCommitChecker(Env):
|
||||
self.print_row("TEST", "RESULT")
|
||||
self.print_separator()
|
||||
|
||||
result = True
|
||||
for test in self.tests:
|
||||
start_time = time.time()
|
||||
self.print_test(test)
|
||||
@@ -152,11 +152,14 @@ class PreCommitChecker(Env):
|
||||
if not result:
|
||||
self.log.error("Error running test %s" % test)
|
||||
self.print_result("FAIL (%dm)" % elapsed_min)
|
||||
return False
|
||||
self.print_result("PASS (%dm)" % elapsed_min)
|
||||
if not self.ignore_failure:
|
||||
return False
|
||||
result = False
|
||||
else:
|
||||
self.print_result("PASS (%dm)" % elapsed_min)
|
||||
|
||||
self.print_separator()
|
||||
return True
|
||||
return result
|
||||
|
||||
#
|
||||
# Print a line
|
||||
@@ -182,17 +185,24 @@ class PreCommitChecker(Env):
|
||||
#
|
||||
parser = argparse.ArgumentParser(description='RocksDB pre-commit checker.')
|
||||
|
||||
# --log <logfile>
|
||||
parser.add_argument('--logfile', default='/tmp/precommit-check.log',
|
||||
help='Log file. Default is /tmp/precommit-check.log')
|
||||
# --ignore_failure
|
||||
parser.add_argument('--ignore_failure', action='store_true', default=False,
|
||||
help='Stop when an error occurs')
|
||||
# <test ....>
|
||||
parser.add_argument('test', nargs='+',
|
||||
parser.add_argument('tests', nargs='+',
|
||||
help='CI test(s) to run. e.g: unit punit asan tsan ubsan')
|
||||
|
||||
print("Please follow log %s" % Log.LOG_FILE)
|
||||
|
||||
args = parser.parse_args()
|
||||
checker = PreCommitChecker(args.test)
|
||||
checker = PreCommitChecker(args)
|
||||
|
||||
print("Please follow log %s" % checker.log.filename)
|
||||
|
||||
if not checker.run_tests():
|
||||
print("Error running tests. Please check log file %s" % Log.LOG_FILE)
|
||||
print("Error running tests. Please check log file %s"
|
||||
% checker.log.filename)
|
||||
sys.exit(1)
|
||||
|
||||
sys.exit(0)
|
||||
@@ -8,7 +8,7 @@
|
||||
# Input Value
|
||||
# -------------------------------------------------------------------------
|
||||
# EMAIL Email address to report on trigger conditions
|
||||
# ONCAL Email address to raise a task on failure
|
||||
# ONCALL Email address to raise a task on failure
|
||||
# TRIGGER Trigger conditions for email. Valid values are fail, warn, all
|
||||
# SUBSCRIBER Email addresss to add as subscriber for task
|
||||
#
|
||||
@@ -42,6 +42,9 @@ if [ ! -z $ONCALL ]; then
|
||||
},"
|
||||
fi
|
||||
|
||||
# For now, create the tasks using only the dedicated task creation tool.
|
||||
CREATE_TASK=
|
||||
|
||||
REPORT=
|
||||
if [[ ! -z $REPORT_EMAIL || ! -z $CREATE_TASK ]]; then
|
||||
REPORT="'report': [
|
||||
@@ -56,10 +59,26 @@ fi
|
||||
CLEANUP_ENV="
|
||||
{
|
||||
'name':'Cleanup environment',
|
||||
'shell':'rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && make clean',
|
||||
'shell':'rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && (chmod +t /dev/shm || true) && make clean',
|
||||
'user':'root'
|
||||
}"
|
||||
|
||||
# We will eventually set the RATIO to 1, but we want do this
|
||||
# in steps. RATIO=$(nproc) will make it work as J=1
|
||||
if [ -z $RATIO ]; then
|
||||
RATIO=$(nproc)
|
||||
fi
|
||||
|
||||
if [ -z $PARALLEL_J ]; then
|
||||
PARALLEL_J="J=$(expr $(nproc) / ${RATIO})"
|
||||
fi
|
||||
|
||||
if [ -z $PARALLEL_j ]; then
|
||||
PARALLEL_j="-j$(expr $(nproc) / ${RATIO})"
|
||||
fi
|
||||
|
||||
PARALLELISM="$PARALLEL_J $PARALLEL_j"
|
||||
|
||||
DEBUG="OPT=-g"
|
||||
SHM="TEST_TMPDIR=/dev/shm/rocksdb"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
@@ -73,6 +92,19 @@ HTTP_PROXY="https_proxy=http://fwdproxy.29.prn1:8080 http_proxy=http://fwdproxy.
|
||||
SETUP_JAVA_ENV="export $HTTP_PROXY; export JAVA_HOME=/usr/local/jdk-7u10-64/; export PATH=\$PATH:\$JAVA_HOME/bin"
|
||||
PARSER="'parser':'python build_tools/error_filter.py $1'"
|
||||
|
||||
CONTRUN_NAME="ROCKSDB_CONTRUN_NAME"
|
||||
|
||||
# This code is getting called under various scenarios. What we care about is to
|
||||
# understand when it's called from nightly contruns because in that case we'll
|
||||
# create tasks for any failures. To follow the existing pattern, we'll check
|
||||
# the value of $ONCALL. If it's a diff then just call `false` to make sure
|
||||
# that errors will be properly propagated to the caller.
|
||||
if [ ! -z $ONCALL ]; then
|
||||
TASK_CREATION_TOOL="/usr/local/bin/mysql_mtr_filter --rocksdb --oncall $ONCALL"
|
||||
else
|
||||
TASK_CREATION_TOOL="false"
|
||||
fi
|
||||
|
||||
ARTIFACTS=" 'artifacts': [
|
||||
{
|
||||
'name':'database',
|
||||
@@ -110,14 +142,14 @@ PARALLEL_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || cat t/log-*',
|
||||
'shell':'$DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || $CONTRUN_NAME=punit_check $TASK_CREATION_TOOL || cat t/log-*',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version under gcc-4.8.1',
|
||||
'shell':'$GCC_481 $DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || cat t/log-*',
|
||||
'shell':'$GCC_481 $DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || $CONTRUN_NAME=punit_check_gcc481 $TASK_CREATION_TOOL || cat t/log-*',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -137,7 +169,49 @@ UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $DEBUG make J=1 check',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test not under /dev/shm
|
||||
#
|
||||
UNIT_TEST_NON_SHM_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'timeout': 86400,
|
||||
'shell':'$DEBUG make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build and unit tests
|
||||
#
|
||||
RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'make $PARALLEL_j release || $CONTRUN_NAME=release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -157,7 +231,27 @@ UNIT_TEST_COMMANDS_481="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $GCC_481 $DEBUG make J=1 check',
|
||||
'shell':'$SHM $GCC_481 $DEBUG make $PARALLELISM check || $CONTRUN_NAME=unit_gcc_481_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build and unit tests
|
||||
#
|
||||
RELEASE_BUILD_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Release on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release on GCC 4.8.1',
|
||||
'shell':'$GCC_481 make $PARALLEL_j release || $CONTRUN_NAME=release_gcc481 $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -177,7 +271,27 @@ CLANG_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug',
|
||||
'shell':'$CLANG $SHM $DEBUG make J=1 check',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=clang_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build with CLANG
|
||||
#
|
||||
CLANG_RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb CLANG Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'$CLANG make $PARALLEL_j release|| $CONTRUN_NAME=clang_release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -197,7 +311,7 @@ CLANG_ANALYZE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'RocksDB build and analyze',
|
||||
'shell':'$CLANG $SHM $DEBUG make J=1 analyze',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLEL_j analyze || $CONTRUN_NAME=clang_analyze $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -217,7 +331,7 @@ CODE_COV_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test and collect code coverage info',
|
||||
'shell':'$SHM $DEBUG make J=1 coverage',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM coverage || $CONTRUN_NAME=coverage $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -237,7 +351,7 @@ UNITY_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test unity test',
|
||||
'shell':'$SHM $DEBUG V=1 make J=1 unity_test',
|
||||
'shell':'$SHM $DEBUG V=1 make J=1 unity_test || $CONTRUN_NAME=unity_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -257,7 +371,7 @@ LITE_BUILD_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'$LITE make J=1 static_lib',
|
||||
'shell':'$LITE make J=1 static_lib || $CONTRUN_NAME=lite_static_lib $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -277,7 +391,7 @@ LITE_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'$SHM $LITE make J=1 check',
|
||||
'shell':'$SHM $LITE make J=1 check || $CONTRUN_NAME=lite_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -298,14 +412,14 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG make J=1 db_stress',
|
||||
'shell':'$SHM $DEBUG make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 crash_test || $CONTRUN_NAME=crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -326,7 +440,7 @@ WRITE_STRESS_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB write stress tests',
|
||||
'shell':'make write_stress && python tools/write_stress_runner.py --runtime_sec=3600 --db=/tmp/rocksdb_write_stress',
|
||||
'shell':'make write_stress && python tools/write_stress_runner.py --runtime_sec=3600 --db=/tmp/rocksdb_write_stress || $CONTRUN_NAME=write_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -348,7 +462,7 @@ ASAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under ASAN',
|
||||
'shell':'set -o pipefail && $SHM $ASAN $DEBUG make J=1 asan_check |& /usr/facebook/ops/scripts/asan_symbolize.py -d',
|
||||
'shell':'set -o pipefail && ($SHM $ASAN $DEBUG make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL) |& /usr/facebook/ops/scripts/asan_symbolize.py -d',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -370,7 +484,7 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 asan_crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -390,7 +504,7 @@ UBSAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under UBSAN',
|
||||
'shell':'set -o pipefail && $SHM $UBSAN $DEBUG make J=1 ubsan_check',
|
||||
'shell':'set -o pipefail && $SHM $UBSAN $DEBUG make $PARALLELISM ubsan_check || $CONTRUN_NAME=ubsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -412,7 +526,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 ubsan_crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -428,11 +542,13 @@ VALGRIND_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under valgrind',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit tests',
|
||||
'shell':'$DISABLE_JEMALLOC $SHM $DEBUG make valgrind_check',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM valgrind_test || $CONTRUN_NAME=valgrind_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -448,11 +564,13 @@ TSAN_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make J=1 check',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make $PARALLELISM check || $CONTRUN_NAME=tsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -474,7 +592,7 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make J=1 crash_test',
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN CRASH_TEST_KILL_ODD=1887 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -483,8 +601,6 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
TSAN_CRASH_TEST_COMMANDS=$DISABLE_COMMANDS
|
||||
|
||||
#
|
||||
# RocksDB format compatible
|
||||
#
|
||||
@@ -538,7 +654,7 @@ FORMAT_COMPATIBLE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_format_compatible',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_format_compatible || $CONTRUN_NAME=run_format_compatible $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -571,7 +687,7 @@ NO_COMPRESSION_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_no_compression',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_no_compression || $CONTRUN_NAME=run_no_compression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -628,7 +744,7 @@ REGRESSION_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Make and run script',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_regression',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_regression || $CONTRUN_NAME=run_regression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -648,7 +764,7 @@ JAVA_BUILD_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB for Java',
|
||||
'shell':'$SETUP_JAVA_ENV; $SHM make rocksdbjava',
|
||||
'shell':'$SETUP_JAVA_ENV; $SHM make rocksdbjava || $CONTRUN_NAME=rocksdbjava $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -665,12 +781,24 @@ case $1 in
|
||||
unit)
|
||||
echo $UNIT_TEST_COMMANDS
|
||||
;;
|
||||
unit_non_shm)
|
||||
echo $UNIT_TEST_NON_SHM_COMMANDS
|
||||
;;
|
||||
release)
|
||||
echo $RELEASE_BUILD_COMMANDS
|
||||
;;
|
||||
unit_481)
|
||||
echo $UNIT_TEST_COMMANDS_481
|
||||
;;
|
||||
release_481)
|
||||
echo $RELEASE_BUILD_COMMANDS_481
|
||||
;;
|
||||
clang_unit)
|
||||
echo $CLANG_UNIT_TEST_COMMANDS
|
||||
;;
|
||||
clang_release)
|
||||
echo $CLANG_RELEASE_BUILD_COMMANDS
|
||||
;;
|
||||
clang_analyze)
|
||||
echo $CLANG_ANALYZE_COMMANDS
|
||||
;;
|
||||
|
||||
@@ -82,10 +82,12 @@ get_lib_base gflags LATEST
|
||||
get_lib_base jemalloc LATEST
|
||||
get_lib_base numa LATEST
|
||||
get_lib_base libunwind LATEST
|
||||
get_lib_base tbb 4.0_update2 gcc-4.9-glibc-2.20
|
||||
|
||||
get_lib_base kernel-headers LATEST
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
get_lib_base valgrind 3.10.0 gcc-4.9-glibc-2.20
|
||||
get_lib_base lua 5.2.3 gcc-4.9-glibc-2.20
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
@@ -119,9 +121,11 @@ get_lib_base gflags LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base jemalloc LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base numa LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base libunwind LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base tbb 4.0_update2 gcc-4.8.1-glibc-2.17
|
||||
|
||||
get_lib_base kernel-headers LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
get_lib_base valgrind 3.8.1 gcc-4.8.1-glibc-2.17
|
||||
get_lib_base lua 5.2.3 centos6-native
|
||||
|
||||
git diff $OUTPUT
|
||||
@@ -1,8 +1,9 @@
|
||||
#!/bin/sh
|
||||
if [ "$#" = "0" ]; then
|
||||
echo "Usage: $0 major|minor|patch"
|
||||
echo "Usage: $0 major|minor|patch|full"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$1" = "major" ]; then
|
||||
cat include/rocksdb/version.h | grep MAJOR | head -n1 | awk '{print $3}'
|
||||
fi
|
||||
@@ -12,3 +13,10 @@ fi
|
||||
if [ "$1" = "patch" ]; then
|
||||
cat include/rocksdb/version.h | grep PATCH | head -n1 | awk '{print $3}'
|
||||
fi
|
||||
if [ "$1" = "full" ]; then
|
||||
awk '/#define ROCKSDB/ { env[$2] = $3 }
|
||||
END { printf "%s.%s.%s\n", env["ROCKSDB_MAJOR"],
|
||||
env["ROCKSDB_MINOR"],
|
||||
env["ROCKSDB_PATCH"] }' \
|
||||
include/rocksdb/version.h
|
||||
fi
|
||||
@@ -0,0 +1,22 @@
|
||||
# - Find JeMalloc library
|
||||
# Find the native JeMalloc includes and library
|
||||
# This module defines
|
||||
# JEMALLOC_INCLUDE_DIRS, where to find jemalloc.h, Set when
|
||||
# JEMALLOC_INCLUDE_DIR is found.
|
||||
# JEMALLOC_LIBRARIES, libraries to link against to use JeMalloc.
|
||||
# JEMALLOC_FOUND, If false, do not try to use JeMalloc.
|
||||
#
|
||||
find_path(JEMALLOC_INCLUDE_DIR
|
||||
jemalloc/jemalloc.h)
|
||||
|
||||
find_library(JEMALLOC_LIBRARIES
|
||||
jemalloc)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(JeMalloc DEFAULT_MSG
|
||||
JEMALLOC_LIBRARIES JEMALLOC_INCLUDE_DIR)
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
JEMALLOC_INCLUDE_DIR
|
||||
JEMALLOC_LIBRARIES
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# SNAPPY_INCLUDE_DIR - where to find snappy.h, etc.
|
||||
# SNAPPY_LIBRARIES - List of libraries when using snappy.
|
||||
# SNAPPY_FOUND - True if snappy found.
|
||||
|
||||
find_path(SNAPPY_INCLUDE_DIR
|
||||
NAMES snappy.h
|
||||
HINTS ${SNAPPY_ROOT_DIR}/include)
|
||||
|
||||
find_library(SNAPPY_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${SNAPPY_ROOT_DIR}/lib)
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set UUID_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(snappy DEFAULT_MSG SNAPPY_LIBRARIES SNAPPY_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
SNAPPY_LIBRARIES
|
||||
SNAPPY_INCLUDE_DIR)
|
||||
+20
-31
@@ -271,40 +271,29 @@ TEST_F(AutoRollLoggerTest, LogFlushWhileRolling) {
|
||||
AutoRollLogger* auto_roll_logger =
|
||||
dynamic_cast<AutoRollLogger*>(logger.get());
|
||||
ASSERT_TRUE(auto_roll_logger);
|
||||
std::thread flush_thread;
|
||||
rocksdb::port::Thread flush_thread;
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
// Need to pin the old logger before beginning the roll, as rolling grabs
|
||||
// the mutex, which would prevent us from accessing the old logger.
|
||||
{"AutoRollLogger::Flush:PinnedLogger",
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:PreRollAndPostThreadInit"},
|
||||
// Need to finish the flush thread init before this callback because the
|
||||
// callback accesses flush_thread.get_id() in order to apply certain sync
|
||||
// points only to the flush thread.
|
||||
{"AutoRollLoggerTest::LogFlushWhileRolling:PreRollAndPostThreadInit",
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:FlushCallbackBegin"},
|
||||
// Need to reset logger at this point in Flush() to exercise a race
|
||||
// condition case, which is executing the flush with the pinned (old)
|
||||
// logger after the roll has cut over to a new logger.
|
||||
{"AutoRollLoggerTest::LogFlushWhileRolling:FlushCallback1",
|
||||
"AutoRollLogger::ResetLogger:BeforeNewLogger"},
|
||||
{"AutoRollLogger::ResetLogger:AfterNewLogger",
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:FlushCallback2"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"PosixLogger::Flush:BeginCallback", [&](void* arg) {
|
||||
TEST_SYNC_POINT(
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:FlushCallbackBegin");
|
||||
if (std::this_thread::get_id() == flush_thread.get_id()) {
|
||||
TEST_SYNC_POINT(
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:FlushCallback1");
|
||||
TEST_SYNC_POINT(
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:FlushCallback2");
|
||||
}
|
||||
});
|
||||
// Notes:
|
||||
// (1) Need to pin the old logger before beginning the roll, as rolling grabs
|
||||
// the mutex, which would prevent us from accessing the old logger. This
|
||||
// also marks flush_thread with AutoRollLogger::Flush:PinnedLogger.
|
||||
// (2) Need to reset logger during PosixLogger::Flush() to exercise a race
|
||||
// condition case, which is executing the flush with the pinned (old)
|
||||
// logger after auto-roll logger has cut over to a new logger.
|
||||
// (3) PosixLogger::Flush() happens in both threads but its SyncPoints only
|
||||
// are enabled in flush_thread (the one pinning the old logger).
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependencyAndMarkers(
|
||||
{{"AutoRollLogger::Flush:PinnedLogger",
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:PreRollAndPostThreadInit"},
|
||||
{"PosixLogger::Flush:Begin1",
|
||||
"AutoRollLogger::ResetLogger:BeforeNewLogger"},
|
||||
{"AutoRollLogger::ResetLogger:AfterNewLogger",
|
||||
"PosixLogger::Flush:Begin2"}},
|
||||
{{"AutoRollLogger::Flush:PinnedLogger", "PosixLogger::Flush:Begin1"},
|
||||
{"AutoRollLogger::Flush:PinnedLogger", "PosixLogger::Flush:Begin2"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
flush_thread = std::thread([&]() { auto_roll_logger->Flush(); });
|
||||
flush_thread = port::Thread ([&]() { auto_roll_logger->Flush(); });
|
||||
TEST_SYNC_POINT(
|
||||
"AutoRollLoggerTest::LogFlushWhileRolling:PreRollAndPostThreadInit");
|
||||
RollLogFileBySizeTest(auto_roll_logger, options.max_log_file_size,
|
||||
|
||||
+22
-9
@@ -45,6 +45,7 @@ TableBuilder* NewTableBuilder(
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
int level,
|
||||
const std::string* compression_dict, const bool skip_filters) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
@@ -53,14 +54,15 @@ TableBuilder* NewTableBuilder(
|
||||
TableBuilderOptions(ioptions, internal_comparator,
|
||||
int_tbl_prop_collector_factories, compression_type,
|
||||
compression_opts, compression_dict, skip_filters,
|
||||
column_family_name),
|
||||
column_family_name, level),
|
||||
column_family_id, file);
|
||||
}
|
||||
|
||||
Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
TableCache* table_cache, InternalIterator* iter, FileMetaData* meta,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::unique_ptr<InternalIterator> range_del_iter, FileMetaData* meta,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
@@ -80,6 +82,13 @@ Status BuildTable(
|
||||
Status s;
|
||||
meta->fd.file_size = 0;
|
||||
iter->SeekToFirst();
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg(
|
||||
new RangeDelAggregator(internal_comparator, snapshots));
|
||||
s = range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
if (!s.ok()) {
|
||||
// may be non-ok if a range tombstone key is unparsable
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string fname = TableFileName(ioptions.db_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
@@ -89,7 +98,7 @@ Status BuildTable(
|
||||
#endif // !ROCKSDB_LITE
|
||||
TableProperties tp;
|
||||
|
||||
if (iter->Valid()) {
|
||||
if (iter->Valid() || range_del_agg->ShouldAddTombstones()) {
|
||||
TableBuilder* builder;
|
||||
unique_ptr<WritableFileWriter> file_writer;
|
||||
{
|
||||
@@ -108,7 +117,7 @@ Status BuildTable(
|
||||
builder = NewTableBuilder(
|
||||
ioptions, internal_comparator, int_tbl_prop_collector_factories,
|
||||
column_family_id, column_family_name, file_writer.get(), compression,
|
||||
compression_opts);
|
||||
compression_opts, level);
|
||||
}
|
||||
|
||||
MergeHelper merge(env, internal_comparator.user_comparator(),
|
||||
@@ -117,10 +126,10 @@ Status BuildTable(
|
||||
true /* internal key corruption is not ok */,
|
||||
snapshots.empty() ? 0 : snapshots.back());
|
||||
|
||||
CompactionIterator c_iter(iter, internal_comparator.user_comparator(),
|
||||
&merge, kMaxSequenceNumber, &snapshots,
|
||||
earliest_write_conflict_snapshot, env,
|
||||
true /* internal key corruption is not ok */);
|
||||
CompactionIterator c_iter(
|
||||
iter, internal_comparator.user_comparator(), &merge, kMaxSequenceNumber,
|
||||
&snapshots, earliest_write_conflict_snapshot, env,
|
||||
true /* internal key corruption is not ok */, range_del_agg.get());
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
const Slice& key = c_iter.key();
|
||||
@@ -135,6 +144,9 @@ Status BuildTable(
|
||||
ThreadStatus::FLUSH_BYTES_WRITTEN, IOSTATS(bytes_written));
|
||||
}
|
||||
}
|
||||
// nullptr for table_{min,max} so all range tombstones will be flushed
|
||||
range_del_agg->AddToBuilder(builder, nullptr /* lower_bound */,
|
||||
nullptr /* upper_bound */, meta);
|
||||
|
||||
// Finish and check for builder errors
|
||||
bool empty = builder->NumEntries() == 0;
|
||||
@@ -169,7 +181,8 @@ Status BuildTable(
|
||||
if (s.ok() && !empty) {
|
||||
// Verify that the table is usable
|
||||
std::unique_ptr<InternalIterator> it(table_cache->NewIterator(
|
||||
ReadOptions(), env_options, internal_comparator, meta->fd, nullptr,
|
||||
ReadOptions(), env_options, internal_comparator, meta->fd,
|
||||
nullptr /* range_del_agg */, nullptr,
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
false /* for_compaction */, nullptr /* arena */,
|
||||
|
||||
+5
-3
@@ -12,14 +12,14 @@
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/immutable_options.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/cf_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -49,6 +49,7 @@ TableBuilder* NewTableBuilder(
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
int level,
|
||||
const std::string* compression_dict = nullptr,
|
||||
const bool skip_filters = false);
|
||||
|
||||
@@ -63,7 +64,8 @@ TableBuilder* NewTableBuilder(
|
||||
extern Status BuildTable(
|
||||
const std::string& dbname, Env* env, const ImmutableCFOptions& options,
|
||||
const MutableCFOptions& mutable_cf_options, const EnvOptions& env_options,
|
||||
TableCache* table_cache, InternalIterator* iter, FileMetaData* meta,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::unique_ptr<InternalIterator> range_del_iter, FileMetaData* meta,
|
||||
const InternalKeyComparator& internal_comparator,
|
||||
const std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories,
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/utilities/backupable_db.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
@@ -43,13 +44,16 @@ using rocksdb::CompactionFilterContext;
|
||||
using rocksdb::CompactionOptionsFIFO;
|
||||
using rocksdb::Comparator;
|
||||
using rocksdb::CompressionType;
|
||||
using rocksdb::WALRecoveryMode;
|
||||
using rocksdb::DB;
|
||||
using rocksdb::DBOptions;
|
||||
using rocksdb::Env;
|
||||
using rocksdb::EnvOptions;
|
||||
using rocksdb::InfoLogLevel;
|
||||
using rocksdb::FileLock;
|
||||
using rocksdb::FilterPolicy;
|
||||
using rocksdb::FlushOptions;
|
||||
using rocksdb::IngestExternalFileOptions;
|
||||
using rocksdb::Iterator;
|
||||
using rocksdb::Logger;
|
||||
using rocksdb::MergeOperator;
|
||||
@@ -67,6 +71,7 @@ using rocksdb::Slice;
|
||||
using rocksdb::SliceParts;
|
||||
using rocksdb::SliceTransform;
|
||||
using rocksdb::Snapshot;
|
||||
using rocksdb::SstFileWriter;
|
||||
using rocksdb::Status;
|
||||
using rocksdb::WritableFile;
|
||||
using rocksdb::WriteBatch;
|
||||
@@ -77,6 +82,8 @@ using rocksdb::BackupableDBOptions;
|
||||
using rocksdb::BackupInfo;
|
||||
using rocksdb::RestoreOptions;
|
||||
using rocksdb::CompactRangeOptions;
|
||||
using rocksdb::RateLimiter;
|
||||
using rocksdb::NewGenericRateLimiter;
|
||||
|
||||
using std::shared_ptr;
|
||||
|
||||
@@ -97,6 +104,9 @@ struct rocksdb_readoptions_t {
|
||||
};
|
||||
struct rocksdb_writeoptions_t { WriteOptions rep; };
|
||||
struct rocksdb_options_t { Options rep; };
|
||||
struct rocksdb_compactoptions_t {
|
||||
CompactRangeOptions rep;
|
||||
};
|
||||
struct rocksdb_block_based_table_options_t { BlockBasedTableOptions rep; };
|
||||
struct rocksdb_cuckoo_table_options_t { CuckooTableOptions rep; };
|
||||
struct rocksdb_seqfile_t { SequentialFile* rep; };
|
||||
@@ -107,6 +117,10 @@ struct rocksdb_logger_t { shared_ptr<Logger> rep; };
|
||||
struct rocksdb_cache_t { shared_ptr<Cache> rep; };
|
||||
struct rocksdb_livefiles_t { std::vector<LiveFileMetaData> rep; };
|
||||
struct rocksdb_column_family_handle_t { ColumnFamilyHandle* rep; };
|
||||
struct rocksdb_envoptions_t { EnvOptions rep; };
|
||||
struct rocksdb_ingestexternalfileoptions_t { IngestExternalFileOptions rep; };
|
||||
struct rocksdb_sstfilewriter_t { SstFileWriter* rep; };
|
||||
struct rocksdb_ratelimiter_t { RateLimiter* rep; };
|
||||
|
||||
struct rocksdb_compactionfiltercontext_t {
|
||||
CompactionFilter::Context rep;
|
||||
@@ -123,6 +137,7 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
char** new_value, size_t *new_value_length,
|
||||
unsigned char* value_changed);
|
||||
const char* (*name_)(void*);
|
||||
unsigned char ignore_snapshots_;
|
||||
|
||||
virtual ~rocksdb_compactionfilter_t() {
|
||||
(*destructor_)(state_);
|
||||
@@ -148,6 +163,8 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return ignore_snapshots_; }
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
@@ -269,33 +286,31 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
|
||||
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();
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
size_t n = merge_in.operand_list.size();
|
||||
std::vector<const char*> operand_pointers(n);
|
||||
std::vector<size_t> operand_sizes(n);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
Slice operand(operand_list[i]);
|
||||
Slice operand(merge_in.operand_list[i]);
|
||||
operand_pointers[i] = operand.data();
|
||||
operand_sizes[i] = operand.size();
|
||||
}
|
||||
|
||||
const char* existing_value_data = nullptr;
|
||||
size_t existing_value_len = 0;
|
||||
if (existing_value != nullptr) {
|
||||
existing_value_data = existing_value->data();
|
||||
existing_value_len = existing_value->size();
|
||||
if (merge_in.existing_value != nullptr) {
|
||||
existing_value_data = merge_in.existing_value->data();
|
||||
existing_value_len = merge_in.existing_value->size();
|
||||
}
|
||||
|
||||
unsigned char success;
|
||||
size_t new_value_len;
|
||||
char* tmp_new_value = (*full_merge_)(
|
||||
state_, key.data(), key.size(), existing_value_data, existing_value_len,
|
||||
&operand_pointers[0], &operand_sizes[0], static_cast<int>(n), &success,
|
||||
&new_value_len);
|
||||
new_value->assign(tmp_new_value, new_value_len);
|
||||
state_, merge_in.key.data(), merge_in.key.size(), existing_value_data,
|
||||
existing_value_len, &operand_pointers[0], &operand_sizes[0],
|
||||
static_cast<int>(n), &success, &new_value_len);
|
||||
merge_out->new_value.assign(tmp_new_value, new_value_len);
|
||||
|
||||
if (delete_value_ != nullptr) {
|
||||
(*delete_value_)(state_, tmp_new_value, new_value_len);
|
||||
@@ -433,7 +448,11 @@ 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))) {
|
||||
BackupableDBOptions(path,
|
||||
nullptr,
|
||||
true,
|
||||
options->rep.info_log.get()),
|
||||
&be))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_backup_engine_t* result = new rocksdb_backup_engine_t;
|
||||
@@ -882,6 +901,17 @@ char* rocksdb_property_value(
|
||||
}
|
||||
}
|
||||
|
||||
int rocksdb_property_int(
|
||||
rocksdb_t* db,
|
||||
const char* propname,
|
||||
uint64_t *out_val) {
|
||||
if (db->rep->GetIntProperty(Slice(propname), out_val)) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
char* rocksdb_property_value_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -964,6 +994,30 @@ void rocksdb_compact_range_cf(
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr));
|
||||
}
|
||||
|
||||
void rocksdb_compact_range_opt(rocksdb_t* db, rocksdb_compactoptions_t* opt,
|
||||
const char* start_key, size_t start_key_len,
|
||||
const char* limit_key, size_t limit_key_len) {
|
||||
Slice a, b;
|
||||
db->rep->CompactRange(
|
||||
opt->rep,
|
||||
// Pass nullptr Slice if corresponding "const char*" is nullptr
|
||||
(start_key ? (a = Slice(start_key, start_key_len), &a) : nullptr),
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr));
|
||||
}
|
||||
|
||||
void rocksdb_compact_range_cf_opt(rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
rocksdb_compactoptions_t* opt,
|
||||
const char* start_key, size_t start_key_len,
|
||||
const char* limit_key, size_t limit_key_len) {
|
||||
Slice a, b;
|
||||
db->rep->CompactRange(
|
||||
opt->rep, column_family->rep,
|
||||
// Pass nullptr Slice if corresponding "const char*" is nullptr
|
||||
(start_key ? (a = Slice(start_key, start_key_len), &a) : nullptr),
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr));
|
||||
}
|
||||
|
||||
void rocksdb_flush(
|
||||
rocksdb_t* db,
|
||||
const rocksdb_flushoptions_t* options,
|
||||
@@ -1019,6 +1073,11 @@ void rocksdb_iter_seek(rocksdb_iterator_t* iter, const char* k, size_t klen) {
|
||||
iter->rep->Seek(Slice(k, klen));
|
||||
}
|
||||
|
||||
void rocksdb_iter_seek_for_prev(rocksdb_iterator_t* iter, const char* k,
|
||||
size_t klen) {
|
||||
iter->rep->SeekForPrev(Slice(k, klen));
|
||||
}
|
||||
|
||||
void rocksdb_iter_next(rocksdb_iterator_t* iter) {
|
||||
iter->rep->Next();
|
||||
}
|
||||
@@ -1206,6 +1265,53 @@ void rocksdb_writebatch_deletev_cf(
|
||||
b->rep.Delete(column_family->rep, SliceParts(key_slices.data(), num_keys));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete_range(rocksdb_writebatch_t* b,
|
||||
const char* start_key,
|
||||
size_t start_key_len, const char* end_key,
|
||||
size_t end_key_len) {
|
||||
b->rep.DeleteRange(Slice(start_key, start_key_len),
|
||||
Slice(end_key, end_key_len));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete_range_cf(
|
||||
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* start_key, size_t start_key_len, const char* end_key,
|
||||
size_t end_key_len) {
|
||||
b->rep.DeleteRange(column_family->rep, Slice(start_key, start_key_len),
|
||||
Slice(end_key, end_key_len));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete_rangev(rocksdb_writebatch_t* b, int num_keys,
|
||||
const char* const* start_keys_list,
|
||||
const size_t* start_keys_list_sizes,
|
||||
const char* const* end_keys_list,
|
||||
const size_t* end_keys_list_sizes) {
|
||||
std::vector<Slice> start_key_slices(num_keys);
|
||||
std::vector<Slice> end_key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
start_key_slices[i] = Slice(start_keys_list[i], start_keys_list_sizes[i]);
|
||||
end_key_slices[i] = Slice(end_keys_list[i], end_keys_list_sizes[i]);
|
||||
}
|
||||
b->rep.DeleteRange(SliceParts(start_key_slices.data(), num_keys),
|
||||
SliceParts(end_key_slices.data(), num_keys));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete_rangev_cf(
|
||||
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
int num_keys, const char* const* start_keys_list,
|
||||
const size_t* start_keys_list_sizes, const char* const* end_keys_list,
|
||||
const size_t* end_keys_list_sizes) {
|
||||
std::vector<Slice> start_key_slices(num_keys);
|
||||
std::vector<Slice> end_key_slices(num_keys);
|
||||
for (int i = 0; i < num_keys; i++) {
|
||||
start_key_slices[i] = Slice(start_keys_list[i], start_keys_list_sizes[i]);
|
||||
end_key_slices[i] = Slice(end_keys_list[i], end_keys_list_sizes[i]);
|
||||
}
|
||||
b->rep.DeleteRange(column_family->rep,
|
||||
SliceParts(start_key_slices.data(), num_keys),
|
||||
SliceParts(end_key_slices.data(), num_keys));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_put_log_data(
|
||||
rocksdb_writebatch_t* b,
|
||||
const char* blob, size_t len) {
|
||||
@@ -1241,6 +1347,15 @@ const char* rocksdb_writebatch_data(rocksdb_writebatch_t* b, size_t* size) {
|
||||
return b->rep.Data().c_str();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_set_save_point(rocksdb_writebatch_t* b) {
|
||||
b->rep.SetSavePoint();
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_rollback_to_save_point(rocksdb_writebatch_t* b,
|
||||
char** errptr) {
|
||||
SaveError(errptr, b->rep.RollbackToSavePoint());
|
||||
}
|
||||
|
||||
rocksdb_block_based_table_options_t*
|
||||
rocksdb_block_based_options_create() {
|
||||
return new rocksdb_block_based_table_options_t;
|
||||
@@ -1383,6 +1498,14 @@ void rocksdb_options_set_cuckoo_table_factory(
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_set_options(
|
||||
rocksdb_t* db, int count, const char* const keys[], const char* const values[], char** errptr) {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
for (int i=0; i<count; i++)
|
||||
options_map[keys[i]] = values[i];
|
||||
SaveError(errptr,
|
||||
db->rep->SetOptions(options_map));
|
||||
}
|
||||
|
||||
rocksdb_options_t* rocksdb_options_create() {
|
||||
return new rocksdb_options_t;
|
||||
@@ -1509,19 +1632,19 @@ void rocksdb_options_set_max_bytes_for_level_base(
|
||||
opt->rep.max_bytes_for_level_base = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_multiplier(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
void rocksdb_options_set_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.level_compaction_dynamic_level_bytes = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_multiplier(rocksdb_options_t* opt,
|
||||
double n) {
|
||||
opt->rep.max_bytes_for_level_multiplier = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_expanded_compaction_factor(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.expanded_compaction_factor = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_grandparent_overlap_factor(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_grandparent_overlap_factor = n;
|
||||
void rocksdb_options_set_max_compaction_bytes(rocksdb_options_t* opt,
|
||||
uint64_t n) {
|
||||
opt->rep.max_compaction_bytes = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_multiplier_additional(
|
||||
@@ -1558,6 +1681,10 @@ void rocksdb_options_set_level0_stop_writes_trigger(
|
||||
void rocksdb_options_set_max_mem_compaction_level(rocksdb_options_t* opt,
|
||||
int n) {}
|
||||
|
||||
void rocksdb_options_set_wal_recovery_mode(rocksdb_options_t* opt,int mode) {
|
||||
opt->rep.wal_recovery_mode = static_cast<WALRecoveryMode>(mode);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression(rocksdb_options_t* opt, int t) {
|
||||
opt->rep.compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
@@ -1624,9 +1751,14 @@ void rocksdb_options_set_manifest_preallocation_size(
|
||||
void rocksdb_options_set_purge_redundant_kvs_while_flush(rocksdb_options_t* opt,
|
||||
unsigned char v) {}
|
||||
|
||||
void rocksdb_options_set_allow_os_buffer(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.allow_os_buffer = v;
|
||||
void rocksdb_options_set_use_direct_reads(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.use_direct_reads = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_use_direct_writes(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.use_direct_writes = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_allow_mmap_reads(
|
||||
@@ -1687,16 +1819,21 @@ void rocksdb_options_set_bytes_per_sync(
|
||||
opt->rep.bytes_per_sync = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_allow_concurrent_memtable_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.allow_concurrent_memtable_write = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_enable_write_thread_adaptive_yield(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.enable_write_thread_adaptive_yield = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_verify_checksums_in_compaction(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.verify_checksums_in_compaction = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_filter_deletes(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.filter_deletes = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_sequential_skip_in_iterations(
|
||||
rocksdb_options_t* opt, uint64_t v) {
|
||||
opt->rep.max_sequential_skip_in_iterations = v;
|
||||
@@ -1719,6 +1856,11 @@ void rocksdb_options_set_max_background_compactions(rocksdb_options_t* opt, int
|
||||
opt->rep.max_background_compactions = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_base_background_compactions(rocksdb_options_t* opt,
|
||||
int n) {
|
||||
opt->rep.base_background_compactions = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_background_flushes(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_background_flushes = n;
|
||||
}
|
||||
@@ -1748,6 +1890,14 @@ void rocksdb_options_set_hard_rate_limit(rocksdb_options_t* opt, double v) {
|
||||
opt->rep.hard_rate_limit = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_soft_pending_compaction_bytes_limit(rocksdb_options_t* opt, size_t v) {
|
||||
opt->rep.soft_pending_compaction_bytes_limit = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_hard_pending_compaction_bytes_limit(rocksdb_options_t* opt, size_t v) {
|
||||
opt->rep.hard_pending_compaction_bytes_limit = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_rate_limit_delay_max_milliseconds(
|
||||
rocksdb_options_t* opt, unsigned int v) {
|
||||
opt->rep.rate_limit_delay_max_milliseconds = v;
|
||||
@@ -1777,76 +1927,56 @@ void rocksdb_options_set_disable_auto_compactions(rocksdb_options_t* opt, int di
|
||||
opt->rep.disable_auto_compactions = disable;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_optimize_filters_for_hits(rocksdb_options_t* opt, int v) {
|
||||
opt->rep.optimize_filters_for_hits = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_delete_obsolete_files_period_micros(
|
||||
rocksdb_options_t* opt, uint64_t v) {
|
||||
opt->rep.delete_obsolete_files_period_micros = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_source_compaction_factor(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.expanded_compaction_factor = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_prepare_for_bulk_load(rocksdb_options_t* opt) {
|
||||
opt->rep.PrepareForBulkLoad();
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_vector_rep(rocksdb_options_t *opt) {
|
||||
static rocksdb::VectorRepFactory* factory = 0;
|
||||
if (!factory) {
|
||||
factory = new rocksdb::VectorRepFactory;
|
||||
}
|
||||
opt->rep.memtable_factory.reset(factory);
|
||||
opt->rep.memtable_factory.reset(new rocksdb::VectorRepFactory);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_prefix_bloom_bits(
|
||||
rocksdb_options_t* opt, uint32_t v) {
|
||||
opt->rep.memtable_prefix_bloom_bits = v;
|
||||
void rocksdb_options_set_memtable_prefix_bloom_size_ratio(
|
||||
rocksdb_options_t* opt, double v) {
|
||||
opt->rep.memtable_prefix_bloom_size_ratio = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_prefix_bloom_probes(
|
||||
rocksdb_options_t* opt, uint32_t v) {
|
||||
opt->rep.memtable_prefix_bloom_probes = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_prefix_bloom_huge_page_tlb_size(
|
||||
rocksdb_options_t* opt, size_t v) {
|
||||
opt->rep.memtable_prefix_bloom_huge_page_tlb_size = v;
|
||||
void rocksdb_options_set_memtable_huge_page_size(rocksdb_options_t* opt,
|
||||
size_t v) {
|
||||
opt->rep.memtable_huge_page_size = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_hash_skip_list_rep(
|
||||
rocksdb_options_t *opt, size_t bucket_count,
|
||||
int32_t skiplist_height, int32_t skiplist_branching_factor) {
|
||||
static rocksdb::MemTableRepFactory* factory = 0;
|
||||
if (!factory) {
|
||||
factory = rocksdb::NewHashSkipListRepFactory(
|
||||
bucket_count, skiplist_height, skiplist_branching_factor);
|
||||
}
|
||||
rocksdb::MemTableRepFactory* factory = rocksdb::NewHashSkipListRepFactory(
|
||||
bucket_count, skiplist_height, skiplist_branching_factor);
|
||||
opt->rep.memtable_factory.reset(factory);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_hash_link_list_rep(
|
||||
rocksdb_options_t *opt, size_t bucket_count) {
|
||||
static rocksdb::MemTableRepFactory* factory = 0;
|
||||
if (!factory) {
|
||||
factory = rocksdb::NewHashLinkListRepFactory(bucket_count);
|
||||
}
|
||||
opt->rep.memtable_factory.reset(factory);
|
||||
opt->rep.memtable_factory.reset(rocksdb::NewHashLinkListRepFactory(bucket_count));
|
||||
}
|
||||
|
||||
void rocksdb_options_set_plain_table_factory(
|
||||
rocksdb_options_t *opt, uint32_t user_key_len, int bloom_bits_per_key,
|
||||
double hash_table_ratio, size_t index_sparseness) {
|
||||
static rocksdb::TableFactory* factory = 0;
|
||||
if (!factory) {
|
||||
rocksdb::PlainTableOptions options;
|
||||
options.user_key_len = user_key_len;
|
||||
options.bloom_bits_per_key = bloom_bits_per_key;
|
||||
options.hash_table_ratio = hash_table_ratio;
|
||||
options.index_sparseness = index_sparseness;
|
||||
rocksdb::PlainTableOptions options;
|
||||
options.user_key_len = user_key_len;
|
||||
options.bloom_bits_per_key = bloom_bits_per_key;
|
||||
options.hash_table_ratio = hash_table_ratio;
|
||||
options.index_sparseness = index_sparseness;
|
||||
|
||||
factory = rocksdb::NewPlainTableFactory(options);
|
||||
}
|
||||
rocksdb::TableFactory* factory = rocksdb::NewPlainTableFactory(options);
|
||||
opt->rep.table_factory.reset(factory);
|
||||
}
|
||||
|
||||
@@ -1902,6 +2032,28 @@ char *rocksdb_options_statistics_get_string(rocksdb_options_t *opt) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_ratelimiter(rocksdb_options_t *opt, rocksdb_ratelimiter_t *limiter) {
|
||||
opt->rep.rate_limiter.reset(limiter->rep);
|
||||
limiter->rep = nullptr;
|
||||
}
|
||||
|
||||
rocksdb_ratelimiter_t* rocksdb_ratelimiter_create(
|
||||
int64_t rate_bytes_per_sec,
|
||||
int64_t refill_period_us,
|
||||
int32_t fairness) {
|
||||
rocksdb_ratelimiter_t* rate_limiter = new rocksdb_ratelimiter_t;
|
||||
rate_limiter->rep = NewGenericRateLimiter(rate_bytes_per_sec,
|
||||
refill_period_us, fairness);
|
||||
return rate_limiter;
|
||||
}
|
||||
|
||||
void rocksdb_ratelimiter_destroy(rocksdb_ratelimiter_t *limiter) {
|
||||
if (limiter->rep) {
|
||||
delete limiter->rep;
|
||||
}
|
||||
delete limiter;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO:
|
||||
DB::OpenForReadOnly
|
||||
@@ -1931,10 +2083,17 @@ rocksdb_compactionfilter_t* rocksdb_compactionfilter_create(
|
||||
result->state_ = state;
|
||||
result->destructor_ = destructor;
|
||||
result->filter_ = filter;
|
||||
result->ignore_snapshots_ = false;
|
||||
result->name_ = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_compactionfilter_set_ignore_snapshots(
|
||||
rocksdb_compactionfilter_t* filter,
|
||||
unsigned char whether_ignore) {
|
||||
filter->ignore_snapshots_ = whether_ignore;
|
||||
}
|
||||
|
||||
void rocksdb_compactionfilter_destroy(rocksdb_compactionfilter_t* filter) {
|
||||
delete filter;
|
||||
}
|
||||
@@ -2132,6 +2291,16 @@ void rocksdb_readoptions_set_readahead_size(
|
||||
opt->rep.readahead_size = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_pin_data(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.pin_data = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_total_order_seek(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.total_order_seek = v;
|
||||
}
|
||||
|
||||
rocksdb_writeoptions_t* rocksdb_writeoptions_create() {
|
||||
return new rocksdb_writeoptions_t;
|
||||
}
|
||||
@@ -2149,6 +2318,28 @@ void rocksdb_writeoptions_disable_WAL(rocksdb_writeoptions_t* opt, int disable)
|
||||
opt->rep.disableWAL = disable;
|
||||
}
|
||||
|
||||
rocksdb_compactoptions_t* rocksdb_compactoptions_create() {
|
||||
return new rocksdb_compactoptions_t;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_destroy(rocksdb_compactoptions_t* opt) {
|
||||
delete opt;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_exclusive_manual_compaction(
|
||||
rocksdb_compactoptions_t* opt, unsigned char v) {
|
||||
opt->rep.exclusive_manual_compaction = v;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_change_level(rocksdb_compactoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.change_level = v;
|
||||
}
|
||||
|
||||
void rocksdb_compactoptions_set_target_level(rocksdb_compactoptions_t* opt,
|
||||
int n) {
|
||||
opt->rep.target_level = n;
|
||||
}
|
||||
|
||||
rocksdb_flushoptions_t* rocksdb_flushoptions_create() {
|
||||
return new rocksdb_flushoptions_t;
|
||||
@@ -2177,6 +2368,14 @@ void rocksdb_cache_set_capacity(rocksdb_cache_t* cache, size_t capacity) {
|
||||
cache->rep->SetCapacity(capacity);
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_usage(rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetUsage();
|
||||
}
|
||||
|
||||
size_t rocksdb_cache_get_pinned_usage(rocksdb_cache_t* cache) {
|
||||
return cache->rep->GetPinnedUsage();
|
||||
}
|
||||
|
||||
rocksdb_env_t* rocksdb_create_default_env() {
|
||||
rocksdb_env_t* result = new rocksdb_env_t;
|
||||
result->rep = Env::Default();
|
||||
@@ -2208,6 +2407,107 @@ void rocksdb_env_destroy(rocksdb_env_t* env) {
|
||||
delete env;
|
||||
}
|
||||
|
||||
rocksdb_envoptions_t* rocksdb_envoptions_create() {
|
||||
rocksdb_envoptions_t* opt = new rocksdb_envoptions_t;
|
||||
return opt;
|
||||
}
|
||||
|
||||
void rocksdb_envoptions_destroy(rocksdb_envoptions_t* opt) { delete opt; }
|
||||
|
||||
rocksdb_sstfilewriter_t* rocksdb_sstfilewriter_create(
|
||||
const rocksdb_envoptions_t* env, const rocksdb_options_t* io_options) {
|
||||
rocksdb_sstfilewriter_t* writer = new rocksdb_sstfilewriter_t;
|
||||
writer->rep =
|
||||
new SstFileWriter(env->rep, io_options->rep, io_options->rep.comparator);
|
||||
return writer;
|
||||
}
|
||||
|
||||
rocksdb_sstfilewriter_t* rocksdb_sstfilewriter_create_with_comparator(
|
||||
const rocksdb_envoptions_t* env, const rocksdb_options_t* io_options,
|
||||
const rocksdb_comparator_t* comparator) {
|
||||
rocksdb_sstfilewriter_t* writer = new rocksdb_sstfilewriter_t;
|
||||
writer->rep =
|
||||
new SstFileWriter(env->rep, io_options->rep, io_options->rep.comparator);
|
||||
return writer;
|
||||
}
|
||||
|
||||
void rocksdb_sstfilewriter_open(rocksdb_sstfilewriter_t* writer,
|
||||
const char* name, char** errptr) {
|
||||
SaveError(errptr, writer->rep->Open(std::string(name)));
|
||||
}
|
||||
|
||||
void rocksdb_sstfilewriter_add(rocksdb_sstfilewriter_t* writer, const char* key,
|
||||
size_t keylen, const char* val, size_t vallen,
|
||||
char** errptr) {
|
||||
SaveError(errptr, writer->rep->Add(Slice(key, keylen), Slice(val, vallen)));
|
||||
}
|
||||
|
||||
void rocksdb_sstfilewriter_finish(rocksdb_sstfilewriter_t* writer,
|
||||
char** errptr) {
|
||||
SaveError(errptr, writer->rep->Finish(NULL));
|
||||
}
|
||||
|
||||
void rocksdb_sstfilewriter_destroy(rocksdb_sstfilewriter_t* writer) {
|
||||
delete writer->rep;
|
||||
delete writer;
|
||||
}
|
||||
|
||||
rocksdb_ingestexternalfileoptions_t*
|
||||
rocksdb_ingestexternalfileoptions_create() {
|
||||
rocksdb_ingestexternalfileoptions_t* opt =
|
||||
new rocksdb_ingestexternalfileoptions_t;
|
||||
return opt;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_move_files(
|
||||
rocksdb_ingestexternalfileoptions_t* opt, unsigned char move_files) {
|
||||
opt->rep.move_files = move_files;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_snapshot_consistency(
|
||||
rocksdb_ingestexternalfileoptions_t* opt,
|
||||
unsigned char snapshot_consistency) {
|
||||
opt->rep.snapshot_consistency = snapshot_consistency;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_allow_global_seqno(
|
||||
rocksdb_ingestexternalfileoptions_t* opt,
|
||||
unsigned char allow_global_seqno) {
|
||||
opt->rep.allow_global_seqno = allow_global_seqno;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_allow_blocking_flush(
|
||||
rocksdb_ingestexternalfileoptions_t* opt,
|
||||
unsigned char allow_blocking_flush) {
|
||||
opt->rep.allow_blocking_flush = allow_blocking_flush;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_destroy(
|
||||
rocksdb_ingestexternalfileoptions_t* opt) {
|
||||
delete opt;
|
||||
}
|
||||
|
||||
void rocksdb_ingest_external_file(
|
||||
rocksdb_t* db, const char* const* file_list, const size_t list_len,
|
||||
const rocksdb_ingestexternalfileoptions_t* opt, char** errptr) {
|
||||
std::vector<std::string> files(list_len);
|
||||
for (size_t i = 0; i < list_len; ++i) {
|
||||
files[i] = std::string(file_list[i]);
|
||||
}
|
||||
SaveError(errptr, db->rep->IngestExternalFile(files, opt->rep));
|
||||
}
|
||||
|
||||
void rocksdb_ingest_external_file_cf(
|
||||
rocksdb_t* db, rocksdb_column_family_handle_t* handle,
|
||||
const char* const* file_list, const size_t list_len,
|
||||
const rocksdb_ingestexternalfileoptions_t* opt, char** errptr) {
|
||||
std::vector<std::string> files(list_len);
|
||||
for (size_t i = 0; i < list_len; ++i) {
|
||||
files[i] = std::string(file_list[i]);
|
||||
}
|
||||
SaveError(errptr, db->rep->IngestExternalFile(handle->rep, files, opt->rep));
|
||||
}
|
||||
|
||||
rocksdb_slicetransform_t* rocksdb_slicetransform_create(
|
||||
void* state,
|
||||
void (*destructor)(void*),
|
||||
|
||||
+187
-8
@@ -2,17 +2,18 @@
|
||||
Use of this source code is governed by a BSD-style license that can be
|
||||
found in the LICENSE file. See the AUTHORS file for names of contributors. */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef ROCKSDB_LITE // Lite does not support C API
|
||||
|
||||
#include "rocksdb/c.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#ifndef OS_WIN
|
||||
# include <unistd.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
|
||||
@@ -21,11 +22,10 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
# define snprintf _snprintf
|
||||
#define snprintf _snprintf
|
||||
|
||||
// Ok for uniqueness
|
||||
int geteuid() {
|
||||
|
||||
int result = 0;
|
||||
|
||||
result = ((int)GetCurrentProcessId() << 16);
|
||||
@@ -38,13 +38,13 @@ int geteuid() {
|
||||
|
||||
const char* phase = "";
|
||||
static char dbname[200];
|
||||
static char sstfilename[200];
|
||||
static char dbbackupname[200];
|
||||
|
||||
static void StartPhase(const char* name) {
|
||||
fprintf(stderr, "=== Test %s\n", name);
|
||||
phase = name;
|
||||
}
|
||||
|
||||
static const char* GetTempDir(void) {
|
||||
const char* ret = getenv("TEST_TMPDIR");
|
||||
if (ret == NULL || ret[0] == '\0')
|
||||
@@ -288,9 +288,11 @@ int main(int argc, char** argv) {
|
||||
rocksdb_cache_t* cache;
|
||||
rocksdb_env_t* env;
|
||||
rocksdb_options_t* options;
|
||||
rocksdb_compactoptions_t* coptions;
|
||||
rocksdb_block_based_table_options_t* table_options;
|
||||
rocksdb_readoptions_t* roptions;
|
||||
rocksdb_writeoptions_t* woptions;
|
||||
rocksdb_ratelimiter_t* rate_limiter;
|
||||
char* err = NULL;
|
||||
int run = -1;
|
||||
|
||||
@@ -304,6 +306,11 @@ int main(int argc, char** argv) {
|
||||
GetTempDir(),
|
||||
((int) geteuid()));
|
||||
|
||||
snprintf(sstfilename, sizeof(sstfilename),
|
||||
"%s/rocksdb_c_test-%d-sst",
|
||||
GetTempDir(),
|
||||
((int)geteuid()));
|
||||
|
||||
StartPhase("create_objects");
|
||||
cmp = rocksdb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
|
||||
env = rocksdb_create_default_env();
|
||||
@@ -317,6 +324,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_write_buffer_size(options, 100000);
|
||||
rocksdb_options_set_paranoid_checks(options, 1);
|
||||
rocksdb_options_set_max_open_files(options, 10);
|
||||
rocksdb_options_set_base_background_compactions(options, 1);
|
||||
table_options = rocksdb_block_based_options_create();
|
||||
rocksdb_block_based_options_set_block_cache(table_options, cache);
|
||||
rocksdb_options_set_block_based_table_factory(options, table_options);
|
||||
@@ -326,14 +334,20 @@ int main(int argc, char** argv) {
|
||||
int compression_levels[] = {rocksdb_no_compression, rocksdb_no_compression,
|
||||
rocksdb_no_compression, rocksdb_no_compression};
|
||||
rocksdb_options_set_compression_per_level(options, compression_levels, 4);
|
||||
rate_limiter = rocksdb_ratelimiter_create(1000 * 1024 * 1024, 100 * 1000, 10);
|
||||
rocksdb_options_set_ratelimiter(options, rate_limiter);
|
||||
rocksdb_ratelimiter_destroy(rate_limiter);
|
||||
|
||||
roptions = rocksdb_readoptions_create();
|
||||
rocksdb_readoptions_set_verify_checksums(roptions, 1);
|
||||
rocksdb_readoptions_set_fill_cache(roptions, 0);
|
||||
rocksdb_readoptions_set_fill_cache(roptions, 1);
|
||||
|
||||
woptions = rocksdb_writeoptions_create();
|
||||
rocksdb_writeoptions_set_sync(woptions, 1);
|
||||
|
||||
coptions = rocksdb_compactoptions_create();
|
||||
rocksdb_compactoptions_set_exclusive_manual_compaction(coptions, 1);
|
||||
|
||||
StartPhase("destroy");
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
Free(&err);
|
||||
@@ -415,6 +429,95 @@ int main(int argc, char** argv) {
|
||||
rocksdb_compact_range(db, "a", 1, "z", 1);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
StartPhase("compactallopt");
|
||||
rocksdb_compact_range_opt(db, coptions, NULL, 0, NULL, 0);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
StartPhase("compactrangeopt");
|
||||
rocksdb_compact_range_opt(db, coptions, "a", 1, "z", 1);
|
||||
CheckGet(db, roptions, "foo", "hello");
|
||||
|
||||
// Simple check cache usage
|
||||
StartPhase("cache_usage");
|
||||
{
|
||||
rocksdb_readoptions_set_pin_data(roptions, 1);
|
||||
rocksdb_iterator_t* iter = rocksdb_create_iterator(db, roptions);
|
||||
rocksdb_iter_seek(iter, "foo", 3);
|
||||
|
||||
size_t usage = rocksdb_cache_get_usage(cache);
|
||||
CheckCondition(usage > 0);
|
||||
|
||||
size_t pin_usage = rocksdb_cache_get_pinned_usage(cache);
|
||||
CheckCondition(pin_usage > 0);
|
||||
|
||||
rocksdb_iter_next(iter);
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_readoptions_set_pin_data(roptions, 0);
|
||||
}
|
||||
|
||||
StartPhase("addfile");
|
||||
{
|
||||
rocksdb_envoptions_t* env_opt = rocksdb_envoptions_create();
|
||||
rocksdb_options_t* io_options = rocksdb_options_create();
|
||||
rocksdb_sstfilewriter_t* writer =
|
||||
rocksdb_sstfilewriter_create(env_opt, io_options);
|
||||
|
||||
unlink(sstfilename);
|
||||
rocksdb_sstfilewriter_open(writer, sstfilename, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_sstfilewriter_add(writer, "sstk1", 5, "v1", 2, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_sstfilewriter_add(writer, "sstk2", 5, "v2", 2, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_sstfilewriter_add(writer, "sstk3", 5, "v3", 2, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_sstfilewriter_finish(writer, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_ingestexternalfileoptions_t* ing_opt =
|
||||
rocksdb_ingestexternalfileoptions_create();
|
||||
const char* file_list[1] = {sstfilename};
|
||||
rocksdb_ingest_external_file(db, file_list, 1, ing_opt, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "sstk1", "v1");
|
||||
CheckGet(db, roptions, "sstk2", "v2");
|
||||
CheckGet(db, roptions, "sstk3", "v3");
|
||||
|
||||
unlink(sstfilename);
|
||||
rocksdb_sstfilewriter_open(writer, sstfilename, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_sstfilewriter_add(writer, "sstk2", 5, "v4", 2, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_sstfilewriter_add(writer, "sstk22", 6, "v5", 2, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_sstfilewriter_add(writer, "sstk3", 5, "v6", 2, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_sstfilewriter_finish(writer, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_ingest_external_file(db, file_list, 1, ing_opt, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "sstk1", "v1");
|
||||
CheckGet(db, roptions, "sstk2", "v4");
|
||||
CheckGet(db, roptions, "sstk22", "v5");
|
||||
CheckGet(db, roptions, "sstk3", "v6");
|
||||
|
||||
rocksdb_ingestexternalfileoptions_destroy(ing_opt);
|
||||
rocksdb_sstfilewriter_destroy(writer);
|
||||
rocksdb_options_destroy(io_options);
|
||||
rocksdb_envoptions_destroy(env_opt);
|
||||
|
||||
// Delete all keys we just ingested
|
||||
rocksdb_delete(db, woptions, "sstk1", 5, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_delete(db, woptions, "sstk2", 5, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_delete(db, woptions, "sstk22", 6, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_delete(db, woptions, "sstk3", 5, &err);
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
StartPhase("writebatch");
|
||||
{
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
@@ -431,6 +534,24 @@ int main(int argc, char** argv) {
|
||||
int pos = 0;
|
||||
rocksdb_writebatch_iterate(wb, &pos, CheckPut, CheckDel);
|
||||
CheckCondition(pos == 3);
|
||||
rocksdb_writebatch_clear(wb);
|
||||
rocksdb_writebatch_put(wb, "bar", 3, "b", 1);
|
||||
rocksdb_writebatch_put(wb, "bay", 3, "d", 1);
|
||||
rocksdb_writebatch_delete_range(wb, "bar", 3, "bay", 3);
|
||||
rocksdb_write(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "bar", NULL);
|
||||
CheckGet(db, roptions, "bay", "d");
|
||||
rocksdb_writebatch_clear(wb);
|
||||
const char* start_list[1] = {"bay"};
|
||||
const size_t start_sizes[1] = {3};
|
||||
const char* end_list[1] = {"baz"};
|
||||
const size_t end_sizes[1] = {3};
|
||||
rocksdb_writebatch_delete_rangev(wb, 1, start_list, start_sizes, end_list,
|
||||
end_sizes);
|
||||
rocksdb_write(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "bay", NULL);
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
}
|
||||
|
||||
@@ -452,6 +573,23 @@ int main(int argc, char** argv) {
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
}
|
||||
|
||||
StartPhase("writebatch_savepoint");
|
||||
{
|
||||
rocksdb_writebatch_t* wb = rocksdb_writebatch_create();
|
||||
rocksdb_writebatch_set_save_point(wb);
|
||||
const char* k_list[2] = {"z", "ap"};
|
||||
const size_t k_sizes[2] = {1, 2};
|
||||
const char* v_list[3] = {"x", "y", "z"};
|
||||
const size_t v_sizes[3] = {1, 1, 1};
|
||||
rocksdb_writebatch_putv(wb, 2, k_list, k_sizes, 3, v_list, v_sizes);
|
||||
rocksdb_writebatch_rollback_to_save_point(wb, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_write(db, woptions, wb, &err);
|
||||
CheckNoError(err);
|
||||
CheckGet(db, roptions, "zap", NULL);
|
||||
rocksdb_writebatch_destroy(wb);
|
||||
}
|
||||
|
||||
StartPhase("writebatch_rep");
|
||||
{
|
||||
rocksdb_writebatch_t* wb1 = rocksdb_writebatch_create();
|
||||
@@ -487,6 +625,10 @@ int main(int argc, char** argv) {
|
||||
CheckIter(iter, "foo", "hello");
|
||||
rocksdb_iter_seek(iter, "b", 1);
|
||||
CheckIter(iter, "box", "c");
|
||||
rocksdb_iter_seek_for_prev(iter, "g", 1);
|
||||
CheckIter(iter, "foo", "hello");
|
||||
rocksdb_iter_seek_for_prev(iter, "box", 3);
|
||||
CheckIter(iter, "box", "c");
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
@@ -575,6 +717,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_set_create_if_missing(options, 0);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
rocksdb_options_set_wal_recovery_mode(options, 2);
|
||||
rocksdb_repair_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
@@ -697,7 +840,7 @@ int main(int argc, char** argv) {
|
||||
{
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err)
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_t* db_options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(db_options, 1);
|
||||
@@ -828,6 +971,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_prefix_extractor(options, rocksdb_slicetransform_create_fixed_prefix(3));
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
|
||||
rocksdb_options_set_plain_table_factory(options, 4, 10, 0.75, 16);
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(options, 0);
|
||||
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
@@ -862,6 +1006,19 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
|
||||
rocksdb_readoptions_set_total_order_seek(roptions, 1);
|
||||
iter = rocksdb_create_iterator(db, roptions);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
|
||||
rocksdb_iter_seek(iter, "ba", 2);
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "bar1", "bar");
|
||||
|
||||
rocksdb_iter_destroy(iter);
|
||||
rocksdb_readoptions_set_total_order_seek(roptions, 0);
|
||||
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
}
|
||||
@@ -943,12 +1100,35 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
// Simple sanity check that setting memtable rep works.
|
||||
StartPhase("memtable_reps");
|
||||
{
|
||||
// Create database with vector memtable.
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_memtable_vector_rep(options);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// Create database with hash skiplist memtable.
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
StartPhase("cleanup");
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_destroy(options);
|
||||
rocksdb_block_based_options_destroy(table_options);
|
||||
rocksdb_readoptions_destroy(roptions);
|
||||
rocksdb_writeoptions_destroy(woptions);
|
||||
rocksdb_compactoptions_destroy(coptions);
|
||||
rocksdb_cache_destroy(cache);
|
||||
rocksdb_comparator_destroy(cmp);
|
||||
rocksdb_env_destroy(env);
|
||||
@@ -958,7 +1138,6 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
#else
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
fprintf(stderr, "SKIPPED\n");
|
||||
|
||||
+202
-91
@@ -26,8 +26,8 @@
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "memtable/hash_skiplist_rep.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/options_helper.h"
|
||||
@@ -46,6 +46,11 @@ ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(
|
||||
|
||||
ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
if (cfd_ != nullptr) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
for (auto& listener : cfd_->ioptions()->listeners) {
|
||||
listener->OnColumnFamilyHandleDeletionStarted(this);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
@@ -72,26 +77,23 @@ Status ColumnFamilyHandleImpl::GetDescriptor(ColumnFamilyDescriptor* desc) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// accessing mutable cf-options requires db mutex.
|
||||
InstrumentedMutexLock l(mutex_);
|
||||
*desc = ColumnFamilyDescriptor(
|
||||
cfd()->GetName(),
|
||||
BuildColumnFamilyOptions(*cfd()->options(),
|
||||
*cfd()->GetLatestMutableCFOptions()));
|
||||
*desc = ColumnFamilyDescriptor(cfd()->GetName(), cfd()->GetLatestCFOptions());
|
||||
return Status::OK();
|
||||
#else
|
||||
return Status::NotSupported();
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
const Comparator* ColumnFamilyHandleImpl::user_comparator() const {
|
||||
const Comparator* ColumnFamilyHandleImpl::GetComparator() const {
|
||||
return cfd()->user_comparator();
|
||||
}
|
||||
|
||||
void GetIntTblPropCollectorFactory(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories) {
|
||||
auto& collector_factories = cf_options.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < cf_options.table_properties_collector_factories.size();
|
||||
auto& collector_factories = ioptions.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < ioptions.table_properties_collector_factories.size();
|
||||
++i) {
|
||||
assert(collector_factories[i]);
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
@@ -130,11 +132,6 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options) {
|
||||
"In-place memtable updates (inplace_update_support) is not compatible "
|
||||
"with concurrent writes (allow_concurrent_memtable_write)");
|
||||
}
|
||||
if (cf_options.filter_deletes) {
|
||||
return Status::InvalidArgument(
|
||||
"Delete filtering (filter_deletes) is not compatible with concurrent "
|
||||
"memtable writes (allow_concurrent_memtable_writes)");
|
||||
}
|
||||
if (!cf_options.memtable_factory->IsInsertConcurrentlySupported()) {
|
||||
return Status::InvalidArgument(
|
||||
"Memtable doesn't concurrent writes (allow_concurrent_memtable_write)");
|
||||
@@ -142,11 +139,9 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
const InternalKeyComparator* icmp,
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions result = src;
|
||||
result.comparator = icmp;
|
||||
size_t clamp_max = std::conditional<
|
||||
sizeof(size_t) == 4, std::integral_constant<size_t, 0xffffffff>,
|
||||
std::integral_constant<uint64_t, 64ull << 30>>::type::value;
|
||||
@@ -164,6 +159,10 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
result.min_write_buffer_number_to_merge =
|
||||
std::min(result.min_write_buffer_number_to_merge,
|
||||
result.max_write_buffer_number - 1);
|
||||
if (result.min_write_buffer_number_to_merge < 1) {
|
||||
result.min_write_buffer_number_to_merge = 1;
|
||||
}
|
||||
|
||||
if (result.num_levels < 1) {
|
||||
result.num_levels = 1;
|
||||
}
|
||||
@@ -177,6 +176,12 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
if (result.max_write_buffer_number_to_maintain < 0) {
|
||||
result.max_write_buffer_number_to_maintain = result.max_write_buffer_number;
|
||||
}
|
||||
// bloom filter size shouldn't exceed 1/4 of memtable size.
|
||||
if (result.memtable_prefix_bloom_size_ratio > 0.25) {
|
||||
result.memtable_prefix_bloom_size_ratio = 0.25;
|
||||
} else if (result.memtable_prefix_bloom_size_ratio < 0) {
|
||||
result.memtable_prefix_bloom_size_ratio = 0;
|
||||
}
|
||||
XFUNC_TEST("memtablelist_history", "transaction_xftest_SanitizeOptions",
|
||||
xf_transaction_set_memtable_history1,
|
||||
xf_transaction_set_memtable_history,
|
||||
@@ -204,6 +209,10 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
result.level0_stop_writes_trigger = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
if (result.max_bytes_for_level_multiplier <= 0) {
|
||||
result.max_bytes_for_level_multiplier = 1;
|
||||
}
|
||||
|
||||
if (result.level0_file_num_compaction_trigger == 0) {
|
||||
Warn(db_options.info_log.get(),
|
||||
"level0_file_num_compaction_trigger cannot be 0");
|
||||
@@ -262,6 +271,10 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
}
|
||||
}
|
||||
|
||||
if (result.max_compaction_bytes == 0) {
|
||||
result.max_compaction_bytes = result.target_file_size_base * 25;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -329,8 +342,8 @@ void SuperVersionUnrefHandle(void* ptr) {
|
||||
|
||||
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,
|
||||
Cache* _table_cache, WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, ColumnFamilySet* column_family_set)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
@@ -339,14 +352,15 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
refs_(0),
|
||||
dropped_(false),
|
||||
internal_comparator_(cf_options.comparator),
|
||||
options_(*db_options,
|
||||
SanitizeOptions(*db_options, &internal_comparator_, cf_options)),
|
||||
ioptions_(options_),
|
||||
mutable_cf_options_(options_, ioptions_),
|
||||
write_buffer_(write_buffer),
|
||||
initial_cf_options_(SanitizeOptions(db_options, cf_options)),
|
||||
ioptions_(db_options, initial_cf_options_),
|
||||
mutable_cf_options_(initial_cf_options_),
|
||||
is_delete_range_supported_(strcmp(cf_options.table_factory->Name(),
|
||||
BlockBasedTableFactory().Name()) == 0),
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
mem_(nullptr),
|
||||
imm_(options_.min_write_buffer_number_to_merge,
|
||||
options_.max_write_buffer_number_to_maintain),
|
||||
imm_(ioptions_.min_write_buffer_number_to_merge,
|
||||
ioptions_.max_write_buffer_number_to_maintain),
|
||||
super_version_(nullptr),
|
||||
super_version_number_(0),
|
||||
local_sv_(new ThreadLocalPtr(&SuperVersionUnrefHandle)),
|
||||
@@ -356,16 +370,17 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
column_family_set_(column_family_set),
|
||||
pending_flush_(false),
|
||||
pending_compaction_(false),
|
||||
prev_compaction_needed_bytes_(0) {
|
||||
prev_compaction_needed_bytes_(0),
|
||||
allow_2pc_(db_options.allow_2pc) {
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
GetIntTblPropCollectorFactory(options_, &int_tbl_prop_collector_factories_);
|
||||
GetIntTblPropCollectorFactory(ioptions_, &int_tbl_prop_collector_factories_);
|
||||
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
internal_stats_.reset(
|
||||
new InternalStats(ioptions_.num_levels, db_options->env, this));
|
||||
new InternalStats(ioptions_.num_levels, db_options.env, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, env_options, _table_cache));
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
@@ -397,7 +412,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
if (column_family_set_->NumberOfColumnFamilies() < 10) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"--------------- Options for column family [%s]:\n", name.c_str());
|
||||
options_.DumpCFOptions(ioptions_.info_log);
|
||||
initial_cf_options_.Dump(ioptions_.info_log);
|
||||
} else {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"\t(skipping printing options)\n");
|
||||
@@ -474,15 +489,43 @@ void ColumnFamilyData::SetDropped() {
|
||||
column_family_set_->RemoveColumnFamily(this);
|
||||
}
|
||||
|
||||
const double kSlowdownRatio = 1.2;
|
||||
ColumnFamilyOptions ColumnFamilyData::GetLatestCFOptions() const {
|
||||
return BuildColumnFamilyOptions(initial_cf_options_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
uint64_t ColumnFamilyData::OldestLogToKeep() {
|
||||
auto current_log = GetLogNumber();
|
||||
|
||||
if (allow_2pc_) {
|
||||
auto imm_prep_log = imm()->GetMinLogContainingPrepSection();
|
||||
auto mem_prep_log = mem()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (imm_prep_log > 0 && imm_prep_log < current_log) {
|
||||
current_log = imm_prep_log;
|
||||
}
|
||||
|
||||
if (mem_prep_log > 0 && mem_prep_log < current_log) {
|
||||
current_log = mem_prep_log;
|
||||
}
|
||||
}
|
||||
|
||||
return current_log;
|
||||
}
|
||||
|
||||
const double kIncSlowdownRatio = 0.8;
|
||||
const double kDecSlowdownRatio = 1 / kIncSlowdownRatio;
|
||||
const double kNearStopSlowdownRatio = 0.6;
|
||||
const double kDelayRecoverSlowdownRatio = 1.4;
|
||||
|
||||
namespace {
|
||||
// If penalize_stop is true, we further reduce slowdown rate.
|
||||
std::unique_ptr<WriteControllerToken> SetupDelay(
|
||||
uint64_t max_write_rate, WriteController* write_controller,
|
||||
uint64_t compaction_needed_bytes, uint64_t prev_compaction_neeed_bytes,
|
||||
WriteController* write_controller, uint64_t compaction_needed_bytes,
|
||||
uint64_t prev_compaction_need_bytes, bool penalize_stop,
|
||||
bool auto_comapctions_disabled) {
|
||||
const uint64_t kMinWriteRate = 1024u; // Minimum write rate 1KB/s.
|
||||
const uint64_t kMinWriteRate = 16 * 1024u; // Minimum write rate 16KB/s.
|
||||
|
||||
uint64_t max_write_rate = write_controller->max_delayed_write_rate();
|
||||
uint64_t write_rate = write_controller->delayed_write_rate();
|
||||
|
||||
if (auto_comapctions_disabled) {
|
||||
@@ -505,19 +548,32 @@ std::unique_ptr<WriteControllerToken> SetupDelay(
|
||||
// insert to mem tables, so we need to actively slow down before we get
|
||||
// feedback signal from compaction and flushes to avoid the full stop
|
||||
// because of hitting the max write buffer number.
|
||||
if (prev_compaction_neeed_bytes > 0 &&
|
||||
prev_compaction_neeed_bytes <= compaction_needed_bytes) {
|
||||
write_rate = static_cast<uint64_t>(static_cast<double>(write_rate) /
|
||||
kSlowdownRatio);
|
||||
//
|
||||
// If DB just falled into the stop condition, we need to further reduce
|
||||
// the write rate to avoid the stop condition.
|
||||
if (penalize_stop) {
|
||||
// Penalize the near stop or stop condition by more agressive slowdown.
|
||||
// This is to provide the long term slowdown increase signal.
|
||||
// The penalty is more than the reward of recovering to the normal
|
||||
// condition.
|
||||
write_rate = static_cast<uint64_t>(static_cast<double>(write_rate) *
|
||||
kNearStopSlowdownRatio);
|
||||
if (write_rate < kMinWriteRate) {
|
||||
write_rate = kMinWriteRate;
|
||||
}
|
||||
} else if (prev_compaction_neeed_bytes > compaction_needed_bytes) {
|
||||
} else if (prev_compaction_need_bytes > 0 &&
|
||||
prev_compaction_need_bytes <= compaction_needed_bytes) {
|
||||
write_rate = static_cast<uint64_t>(static_cast<double>(write_rate) *
|
||||
kIncSlowdownRatio);
|
||||
if (write_rate < kMinWriteRate) {
|
||||
write_rate = kMinWriteRate;
|
||||
}
|
||||
} else if (prev_compaction_need_bytes > compaction_needed_bytes) {
|
||||
// We are speeding up by ratio of kSlowdownRatio when we have paid
|
||||
// compaction debt. But we'll never speed up to faster than the write rate
|
||||
// given by users.
|
||||
write_rate = static_cast<uint64_t>(static_cast<double>(write_rate) *
|
||||
kSlowdownRatio);
|
||||
kDecSlowdownRatio);
|
||||
if (write_rate > max_write_rate) {
|
||||
write_rate = max_write_rate;
|
||||
}
|
||||
@@ -531,14 +587,31 @@ int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
|
||||
// SanitizeOptions() ensures it.
|
||||
assert(level0_file_num_compaction_trigger <= level0_slowdown_writes_trigger);
|
||||
|
||||
if (level0_file_num_compaction_trigger < 0) {
|
||||
return std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
const int64_t twice_level0_trigger =
|
||||
static_cast<int64_t>(level0_file_num_compaction_trigger) * 2;
|
||||
|
||||
const int64_t one_fourth_trigger_slowdown =
|
||||
static_cast<int64_t>(level0_file_num_compaction_trigger) +
|
||||
((level0_slowdown_writes_trigger - level0_file_num_compaction_trigger) /
|
||||
4);
|
||||
|
||||
assert(twice_level0_trigger >= 0);
|
||||
assert(one_fourth_trigger_slowdown >= 0);
|
||||
|
||||
// 1/4 of the way between L0 compaction trigger threshold and slowdown
|
||||
// condition.
|
||||
// Or twice as compaction trigger, if it is smaller.
|
||||
return std::min(level0_file_num_compaction_trigger * 2,
|
||||
level0_file_num_compaction_trigger +
|
||||
(level0_slowdown_writes_trigger -
|
||||
level0_file_num_compaction_trigger) /
|
||||
4);
|
||||
int64_t res = std::min(twice_level0_trigger, one_fourth_trigger_slowdown);
|
||||
if (res >= port::kMaxInt32) {
|
||||
return port::kMaxInt32;
|
||||
} else {
|
||||
// res fits in int
|
||||
return static_cast<int>(res);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -550,6 +623,9 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
uint64_t compaction_needed_bytes =
|
||||
vstorage->estimated_compaction_needed_bytes();
|
||||
|
||||
bool was_stopped = write_controller->IsStopped();
|
||||
bool needed_delay = write_controller->NeedsDelay();
|
||||
|
||||
if (imm()->NumNotFlushed() >= mutable_cf_options.max_write_buffer_number) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_COMPACTION, 1);
|
||||
@@ -558,8 +634,9 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"(waiting for flush), max_write_buffer_number is set to %d",
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number);
|
||||
} else if (vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::LEVEL0_NUM_FILES_TOTAL, 1);
|
||||
if (compaction_picker_->IsLevel0CompactionInProgress()) {
|
||||
@@ -569,7 +646,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
compaction_needed_bytes >=
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
@@ -583,8 +661,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
imm()->NumNotFlushed() >=
|
||||
mutable_cf_options.max_write_buffer_number - 1) {
|
||||
write_controller_token_ =
|
||||
SetupDelay(ioptions_.delayed_write_rate, write_controller,
|
||||
compaction_needed_bytes, prev_compaction_needed_bytes_,
|
||||
SetupDelay(write_controller, compaction_needed_bytes,
|
||||
prev_compaction_needed_bytes_, was_stopped,
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_SLOWDOWN, 1);
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
@@ -594,12 +672,16 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number,
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
// L0 is the last two files from stopping.
|
||||
bool near_stop = vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger - 2;
|
||||
write_controller_token_ =
|
||||
SetupDelay(ioptions_.delayed_write_rate, write_controller,
|
||||
compaction_needed_bytes, prev_compaction_needed_bytes_,
|
||||
SetupDelay(write_controller, compaction_needed_bytes,
|
||||
prev_compaction_needed_bytes_, was_stopped || near_stop,
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(InternalStats::LEVEL0_SLOWDOWN_TOTAL, 1);
|
||||
if (compaction_picker_->IsLevel0CompactionInProgress()) {
|
||||
@@ -611,12 +693,24 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"rate %" PRIu64,
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count(),
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
vstorage->estimated_compaction_needed_bytes() >=
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) {
|
||||
// If the distance to hard limit is less than 1/4 of the gap between soft
|
||||
// and
|
||||
// hard bytes limit, we think it is near stop and speed up the slowdown.
|
||||
bool near_stop =
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
(compaction_needed_bytes -
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) >
|
||||
3 * (mutable_cf_options.hard_pending_compaction_bytes_limit -
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) /
|
||||
4;
|
||||
|
||||
write_controller_token_ =
|
||||
SetupDelay(ioptions_.delayed_write_rate, write_controller,
|
||||
compaction_needed_bytes, prev_compaction_needed_bytes_,
|
||||
SetupDelay(write_controller, compaction_needed_bytes,
|
||||
prev_compaction_needed_bytes_, was_stopped || near_stop,
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::SOFT_PENDING_COMPACTION_BYTES_LIMIT, 1);
|
||||
@@ -625,31 +719,43 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"bytes %" PRIu64 " rate %" PRIu64,
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes(),
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (vstorage->l0_delay_trigger_count() >=
|
||||
GetL0ThresholdSpeedupCompaction(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger,
|
||||
mutable_cf_options.level0_slowdown_writes_trigger)) {
|
||||
write_controller_token_ = write_controller->GetCompactionPressureToken();
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Increasing compaction threads because we have %d level-0 "
|
||||
"files ",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (vstorage->estimated_compaction_needed_bytes() >=
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit / 4) {
|
||||
// Increase compaction threads if bytes needed for compaction exceeds
|
||||
// 1/4 of threshold for slowing down.
|
||||
// If soft pending compaction byte limit is not set, always speed up
|
||||
// compaction.
|
||||
write_controller_token_ = write_controller->GetCompactionPressureToken();
|
||||
if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Increasing compaction threads because of estimated pending "
|
||||
"compaction "
|
||||
"bytes %" PRIu64,
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
|
||||
}
|
||||
} else {
|
||||
write_controller_token_.reset();
|
||||
if (vstorage->l0_delay_trigger_count() >=
|
||||
GetL0ThresholdSpeedupCompaction(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger,
|
||||
mutable_cf_options.level0_slowdown_writes_trigger)) {
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Increasing compaction threads because we have %d level-0 "
|
||||
"files ",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (vstorage->estimated_compaction_needed_bytes() >=
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit / 4) {
|
||||
// Increase compaction threads if bytes needed for compaction exceeds
|
||||
// 1/4 of threshold for slowing down.
|
||||
// If soft pending compaction byte limit is not set, always speed up
|
||||
// compaction.
|
||||
write_controller_token_ =
|
||||
write_controller->GetCompactionPressureToken();
|
||||
if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Increasing compaction threads because of estimated pending "
|
||||
"compaction "
|
||||
"bytes %" PRIu64,
|
||||
name_.c_str(), vstorage->estimated_compaction_needed_bytes());
|
||||
}
|
||||
} else {
|
||||
write_controller_token_.reset();
|
||||
}
|
||||
// If the DB recovers from delay conditions, we reward with reducing
|
||||
// double the slowdown ratio. This is to balance the long term slowdown
|
||||
// increase signal.
|
||||
if (needed_delay) {
|
||||
uint64_t write_rate = write_controller->delayed_write_rate();
|
||||
write_controller->set_delayed_write_rate(static_cast<uint64_t>(
|
||||
static_cast<double>(write_rate) * kDelayRecoverSlowdownRatio));
|
||||
}
|
||||
}
|
||||
prev_compaction_needed_bytes_ = compaction_needed_bytes;
|
||||
}
|
||||
@@ -675,7 +781,7 @@ MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
assert(current_ != nullptr);
|
||||
return new MemTable(internal_comparator_, ioptions_, mutable_cf_options,
|
||||
write_buffer_, earliest_seq);
|
||||
write_buffer_manager_, earliest_seq);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::CreateNewMemtable(
|
||||
@@ -701,6 +807,13 @@ Compaction* ColumnFamilyData::PickCompaction(
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::RangeOverlapWithCompaction(
|
||||
const Slice& smallest_user_key, const Slice& largest_user_key,
|
||||
int level) const {
|
||||
return compaction_picker_->RangeOverlapWithCompaction(
|
||||
smallest_user_key, largest_user_key, level);
|
||||
}
|
||||
|
||||
const int ColumnFamilyData::kCompactAllLevels = -1;
|
||||
const int ColumnFamilyData::kCompactToBaseLevel = -2;
|
||||
|
||||
@@ -851,21 +964,21 @@ Status ColumnFamilyData::SetOptions(
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
const DBOptions* db_options,
|
||||
const ImmutableDBOptions* db_options,
|
||||
const EnvOptions& env_options,
|
||||
Cache* table_cache,
|
||||
WriteBuffer* write_buffer,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller)
|
||||
: max_column_family_(0),
|
||||
dummy_cfd_(new ColumnFamilyData(0, "", nullptr, nullptr, nullptr,
|
||||
ColumnFamilyOptions(), db_options,
|
||||
ColumnFamilyOptions(), *db_options,
|
||||
env_options, nullptr)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
table_cache_(table_cache),
|
||||
write_buffer_(write_buffer),
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
write_controller_(write_controller) {
|
||||
// initialize linked list
|
||||
dummy_cfd_->prev_ = dummy_cfd_;
|
||||
@@ -928,10 +1041,9 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
const std::string& name, uint32_t id, Version* dummy_versions,
|
||||
const ColumnFamilyOptions& options) {
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd =
|
||||
new ColumnFamilyData(id, name, dummy_versions, table_cache_,
|
||||
write_buffer_, options, db_options_,
|
||||
env_options_, this);
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
*db_options_, env_options_, this);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
@@ -1008,8 +1120,7 @@ uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family) {
|
||||
const Comparator* GetColumnFamilyUserComparator(
|
||||
ColumnFamilyHandle* column_family) {
|
||||
if (column_family != nullptr) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
return cfh->user_comparator();
|
||||
return column_family->GetComparator();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+41
-19
@@ -15,15 +15,15 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "rocksdb/compaction_job_stats.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
#include "util/cf_options.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -42,7 +42,7 @@ class LogBuffer;
|
||||
class InstrumentedMutex;
|
||||
class InstrumentedMutexLock;
|
||||
|
||||
extern const double kSlowdownRatio;
|
||||
extern const double kIncSlowdownRatio;
|
||||
|
||||
// ColumnFamilyHandleImpl is the class that clients use to access different
|
||||
// column families. It has non-trivial destructor, which gets called when client
|
||||
@@ -55,11 +55,11 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
// destroy without mutex
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
virtual const Comparator* user_comparator() const;
|
||||
|
||||
virtual uint32_t GetID() const override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
virtual const Comparator* GetComparator() const override;
|
||||
|
||||
private:
|
||||
ColumnFamilyData* cfd_;
|
||||
@@ -136,14 +136,13 @@ extern Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckConcurrentWritesSupported(
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
extern ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
const InternalKeyComparator* icmp,
|
||||
extern ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const ColumnFamilyOptions& src);
|
||||
// Wrap user defined table proproties collector factories `from cf_options`
|
||||
// into internal ones in int_tbl_prop_collector_factories. Add a system internal
|
||||
// one too.
|
||||
extern void GetIntTblPropCollectorFactory(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories);
|
||||
|
||||
@@ -201,9 +200,6 @@ class ColumnFamilyData {
|
||||
void SetLogNumber(uint64_t log_number) { log_number_ = log_number; }
|
||||
uint64_t GetLogNumber() const { return log_number_; }
|
||||
|
||||
// !!! To be deprecated! Please don't not use this function anymore!
|
||||
const Options* options() const { return &options_; }
|
||||
|
||||
// thread-safe
|
||||
const EnvOptions* soptions() const;
|
||||
const ImmutableCFOptions* ioptions() const { return &ioptions_; }
|
||||
@@ -218,6 +214,14 @@ class ColumnFamilyData {
|
||||
const MutableCFOptions* GetLatestMutableCFOptions() const {
|
||||
return &mutable_cf_options_;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
// Build ColumnFamiliesOptions with immutable options and latest mutable
|
||||
// options.
|
||||
ColumnFamilyOptions GetLatestCFOptions() const;
|
||||
|
||||
bool is_delete_range_supported() { return is_delete_range_supported_; }
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// REQUIRES: DB mutex held
|
||||
Status SetOptions(
|
||||
@@ -235,6 +239,9 @@ class ColumnFamilyData {
|
||||
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
void SetMemtable(MemTable* new_mem) { mem_ = new_mem; }
|
||||
|
||||
// calculate the oldest log needed for the durability of this column family
|
||||
uint64_t OldestLogToKeep();
|
||||
|
||||
// See Memtable constructor for explanation of earliest_seq param.
|
||||
MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq);
|
||||
@@ -249,6 +256,13 @@ class ColumnFamilyData {
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* PickCompaction(const MutableCFOptions& mutable_options,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Check if the passed range overlap with any running compactions.
|
||||
// REQUIRES: DB mutex held
|
||||
bool RangeOverlapWithCompaction(const Slice& smallest_user_key,
|
||||
const Slice& largest_user_key,
|
||||
int level) const;
|
||||
|
||||
// A flag to tell a manual compaction is to compact all levels together
|
||||
// instad of for specific level.
|
||||
static const int kCompactAllLevels;
|
||||
@@ -323,9 +337,10 @@ class ColumnFamilyData {
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBuffer* write_buffer,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& options,
|
||||
const DBOptions* db_options, const EnvOptions& env_options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options,
|
||||
ColumnFamilySet* column_family_set);
|
||||
|
||||
uint32_t id_;
|
||||
@@ -340,15 +355,17 @@ class ColumnFamilyData {
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
|
||||
int_tbl_prop_collector_factories_;
|
||||
|
||||
const Options options_;
|
||||
const ColumnFamilyOptions initial_cf_options_;
|
||||
const ImmutableCFOptions ioptions_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
|
||||
const bool is_delete_range_supported_;
|
||||
|
||||
std::unique_ptr<TableCache> table_cache_;
|
||||
|
||||
std::unique_ptr<InternalStats> internal_stats_;
|
||||
|
||||
WriteBuffer* write_buffer_;
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
|
||||
MemTable* mem_;
|
||||
MemTableList imm_;
|
||||
@@ -390,6 +407,9 @@ class ColumnFamilyData {
|
||||
bool pending_compaction_;
|
||||
|
||||
uint64_t prev_compaction_needed_bytes_;
|
||||
|
||||
// if the database was opened with 2pc enabled
|
||||
bool allow_2pc_;
|
||||
};
|
||||
|
||||
// ColumnFamilySet has interesting thread-safety requirements
|
||||
@@ -436,9 +456,11 @@ class ColumnFamilySet {
|
||||
ColumnFamilyData* current_;
|
||||
};
|
||||
|
||||
ColumnFamilySet(const std::string& dbname, const DBOptions* db_options,
|
||||
ColumnFamilySet(const std::string& dbname,
|
||||
const ImmutableDBOptions* db_options,
|
||||
const EnvOptions& env_options, Cache* table_cache,
|
||||
WriteBuffer* write_buffer, WriteController* write_controller);
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller);
|
||||
~ColumnFamilySet();
|
||||
|
||||
ColumnFamilyData* GetDefault() const;
|
||||
@@ -492,10 +514,10 @@ class ColumnFamilySet {
|
||||
ColumnFamilyData* default_cfd_cache_;
|
||||
|
||||
const std::string db_name_;
|
||||
const DBOptions* const db_options_;
|
||||
const ImmutableDBOptions* const db_options_;
|
||||
const EnvOptions env_options_;
|
||||
Cache* table_cache_;
|
||||
WriteBuffer* write_buffer_;
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
WriteController* write_controller_;
|
||||
};
|
||||
|
||||
|
||||
+515
-92
File diff suppressed because it is too large.
Load diff
+106
-5
@@ -10,6 +10,8 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -49,6 +51,10 @@ class FlushedFileCollector : public EventListener {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void ClearFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> flushed_files_;
|
||||
@@ -115,13 +121,12 @@ TEST_F(CompactFilesTest, L0ConflictsFiles) {
|
||||
TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
Options options;
|
||||
// to trigger compaction more easily
|
||||
const int kWriteBufferSize = 10000;
|
||||
const int kWriteBufferSize = 65536;
|
||||
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.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.write_buffer_size = kWriteBufferSize;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.compression = kNoCompression;
|
||||
@@ -144,6 +149,7 @@ TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files, 1));
|
||||
reinterpret_cast<DBImpl*>(db)->TEST_WaitForCompact();
|
||||
|
||||
// verify all compaction input files are deleted
|
||||
for (auto fname : l0_files) {
|
||||
@@ -152,6 +158,46 @@ TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, NotCutOutputOnLevel0) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Disable RocksDB background compaction.
|
||||
options.compaction_style = kCompactionStyleNone;
|
||||
options.level0_slowdown_writes_trigger = 1000;
|
||||
options.level0_stop_writes_trigger = 1000;
|
||||
options.write_buffer_size = 65536;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.compression = kNoCompression;
|
||||
options.max_compaction_bytes = 5000;
|
||||
|
||||
// Add listener
|
||||
FlushedFileCollector* collector = new FlushedFileCollector();
|
||||
options.listeners.emplace_back(collector);
|
||||
|
||||
DB* db = nullptr;
|
||||
DestroyDB(db_name_, options);
|
||||
Status s = DB::Open(options, db_name_, &db);
|
||||
assert(s.ok());
|
||||
assert(db);
|
||||
|
||||
// create couple files
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
db->Put(WriteOptions(), ToString(i), std::string(1000, 'a' + (i % 26)));
|
||||
}
|
||||
reinterpret_cast<DBImpl*>(db)->TEST_WaitForFlushMemTable();
|
||||
auto l0_files_1 = collector->GetFlushedFiles();
|
||||
collector->ClearFlushedFiles();
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
db->Put(WriteOptions(), ToString(i), std::string(1000, 'a' + (i % 26)));
|
||||
}
|
||||
reinterpret_cast<DBImpl*>(db)->TEST_WaitForFlushMemTable();
|
||||
auto l0_files_2 = collector->GetFlushedFiles();
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files_1, 0));
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files_2, 0));
|
||||
// no assertion failure
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -186,7 +232,7 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Start compacting files.
|
||||
std::thread compaction_thread(
|
||||
rocksdb::port::Thread compaction_thread(
|
||||
[&] { EXPECT_OK(db->CompactFiles(CompactionOptions(), l0_files, 1)); });
|
||||
|
||||
// In the meantime flush another file.
|
||||
@@ -208,6 +254,61 @@ TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, CompactionFilterWithGetSv) {
|
||||
class FilterWithGet : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
std::string* new_value,
|
||||
bool* value_changed) const override {
|
||||
if (db_ == nullptr) {
|
||||
return true;
|
||||
}
|
||||
std::string res;
|
||||
db_->Get(ReadOptions(), "", &res);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetDB(DB* db) {
|
||||
db_ = db;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "FilterWithGet"; }
|
||||
|
||||
private:
|
||||
DB* db_;
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<FilterWithGet> cf(new FilterWithGet());
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_filter = cf.get();
|
||||
|
||||
DB* db = nullptr;
|
||||
DestroyDB(db_name_, options);
|
||||
Status s = DB::Open(options, db_name_, &db);
|
||||
ASSERT_OK(s);
|
||||
|
||||
cf->SetDB(db);
|
||||
|
||||
// Write one L0 file
|
||||
db->Put(WriteOptions(), "K1", "V1");
|
||||
db->Flush(FlushOptions());
|
||||
|
||||
// Compact all L0 files using CompactFiles
|
||||
rocksdb::ColumnFamilyMetaData meta;
|
||||
db->GetColumnFamilyMetaData(&meta);
|
||||
for (auto& file : meta.levels[0].files) {
|
||||
std::string fname = file.db_path + "/" + file.name;
|
||||
ASSERT_OK(
|
||||
db->CompactFiles(rocksdb::CompactionOptions(), {fname}, 0));
|
||||
}
|
||||
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -46,7 +46,7 @@ Status CompactedDBImpl::Get(const ReadOptions& options,
|
||||
ColumnFamilyHandle*, const Slice& key, std::string* value) {
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, key, value, nullptr, nullptr,
|
||||
nullptr);
|
||||
nullptr, nullptr);
|
||||
LookupKey lkey(key, kMaxSequenceNumber);
|
||||
files_.files[FindFile(key)].fd.table_reader->Get(
|
||||
options, lkey.internal_key(), &get_context);
|
||||
@@ -77,7 +77,7 @@ std::vector<Status> CompactedDBImpl::MultiGet(const ReadOptions& options,
|
||||
if (r != nullptr) {
|
||||
GetContext get_context(user_comparator_, nullptr, nullptr, nullptr,
|
||||
GetContext::kNotFound, keys[idx], &(*values)[idx],
|
||||
nullptr, nullptr, nullptr);
|
||||
nullptr, nullptr, nullptr, nullptr);
|
||||
LookupKey lkey(keys[idx], kMaxSequenceNumber);
|
||||
r->Get(options, lkey.internal_key(), &get_context);
|
||||
if (get_context.State() == GetContext::kFound) {
|
||||
@@ -151,9 +151,9 @@ Status CompactedDBImpl::Open(const Options& options,
|
||||
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
||||
Status s = db->Init(options);
|
||||
if (s.ok()) {
|
||||
Log(INFO_LEVEL, db->db_options_.info_log,
|
||||
Log(INFO_LEVEL, db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->db_options_.info_log);
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
*dbptr = db.release();
|
||||
}
|
||||
return s;
|
||||
|
||||
@@ -76,6 +76,13 @@ class CompactedDBImpl : public DBImpl {
|
||||
ColumnFamilyHandle* column_family) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override {
|
||||
return Status::NotSupported("Not supported in compacted db mode.");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
+41
-23
@@ -140,19 +140,22 @@ bool Compaction::IsFullCompaction(
|
||||
}
|
||||
|
||||
Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
const ImmutableCFOptions& _immutable_cf_options,
|
||||
const MutableCFOptions& _mutable_cf_options,
|
||||
std::vector<CompactionInputFiles> _inputs,
|
||||
int _output_level, uint64_t _target_file_size,
|
||||
uint64_t _max_grandparent_overlap_bytes,
|
||||
uint32_t _output_path_id, CompressionType _compression,
|
||||
uint64_t _max_compaction_bytes, uint32_t _output_path_id,
|
||||
CompressionType _compression,
|
||||
std::vector<FileMetaData*> _grandparents,
|
||||
bool _manual_compaction, double _score,
|
||||
bool _deletion_compaction,
|
||||
CompactionReason _compaction_reason)
|
||||
: start_level_(_inputs[0].level),
|
||||
: input_vstorage_(vstorage),
|
||||
start_level_(_inputs[0].level),
|
||||
output_level_(_output_level),
|
||||
max_output_file_size_(_target_file_size),
|
||||
max_grandparent_overlap_bytes_(_max_grandparent_overlap_bytes),
|
||||
max_compaction_bytes_(_max_compaction_bytes),
|
||||
immutable_cf_options_(_immutable_cf_options),
|
||||
mutable_cf_options_(_mutable_cf_options),
|
||||
input_version_(nullptr),
|
||||
number_levels_(vstorage->num_levels()),
|
||||
@@ -187,8 +190,7 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
}
|
||||
}
|
||||
|
||||
Slice smallest_user_key;
|
||||
GetBoundaryKeys(vstorage, inputs_, &smallest_user_key, &largest_user_key_);
|
||||
GetBoundaryKeys(vstorage, inputs_, &smallest_user_key_, &largest_user_key_);
|
||||
}
|
||||
|
||||
Compaction::~Compaction() {
|
||||
@@ -203,11 +205,10 @@ Compaction::~Compaction() {
|
||||
}
|
||||
|
||||
bool Compaction::InputCompressionMatchesOutput() const {
|
||||
VersionStorageInfo* vstorage = input_version_->storage_info();
|
||||
int base_level = vstorage->base_level();
|
||||
bool matches =
|
||||
(GetCompressionType(*cfd_->ioptions(), vstorage, mutable_cf_options_,
|
||||
start_level_, base_level) == output_compression_);
|
||||
int base_level = input_vstorage_->base_level();
|
||||
bool matches = (GetCompressionType(immutable_cf_options_, input_vstorage_,
|
||||
mutable_cf_options_, start_level_,
|
||||
base_level) == output_compression_);
|
||||
if (matches) {
|
||||
TEST_SYNC_POINT("Compaction::InputCompressionMatchesOutput:Matches");
|
||||
return true;
|
||||
@@ -224,15 +225,14 @@ bool Compaction::IsTrivialMove() const {
|
||||
// filter to be applied to that level, and thus cannot be a trivial move.
|
||||
|
||||
// Check if start level have files with overlapping ranges
|
||||
if (start_level_ == 0 &&
|
||||
input_version_->storage_info()->level0_non_overlapping() == false) {
|
||||
if (start_level_ == 0 && input_vstorage_->level0_non_overlapping() == false) {
|
||||
// We cannot move files from L0 to L1 if the files are overlapping
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_manual_compaction_ &&
|
||||
(cfd_->ioptions()->compaction_filter != nullptr ||
|
||||
cfd_->ioptions()->compaction_filter_factory != nullptr)) {
|
||||
(immutable_cf_options_.compaction_filter != nullptr ||
|
||||
immutable_cf_options_.compaction_filter_factory != nullptr)) {
|
||||
// This is a manual compaction and we have a compaction filter that should
|
||||
// be executed, we cannot do a trivial move
|
||||
return false;
|
||||
@@ -240,15 +240,34 @@ bool Compaction::IsTrivialMove() const {
|
||||
|
||||
// Used in universal compaction, where trivial move can be done if the
|
||||
// input files are non overlapping
|
||||
if ((cfd_->ioptions()->compaction_options_universal.allow_trivial_move) &&
|
||||
if ((immutable_cf_options_.compaction_options_universal.allow_trivial_move) &&
|
||||
(output_level_ != 0)) {
|
||||
return is_trivial_move_;
|
||||
}
|
||||
|
||||
return (start_level_ != output_level_ && num_input_levels() == 1 &&
|
||||
if (!(start_level_ != output_level_ && num_input_levels() == 1 &&
|
||||
input(0, 0)->fd.GetPathId() == output_path_id() &&
|
||||
InputCompressionMatchesOutput() &&
|
||||
TotalFileSize(grandparents_) <= max_grandparent_overlap_bytes_);
|
||||
InputCompressionMatchesOutput())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// assert inputs_.size() == 1
|
||||
|
||||
for (const auto& file : inputs_.front().files) {
|
||||
std::vector<FileMetaData*> file_grand_parents;
|
||||
if (output_level_ + 1 >= number_levels_) {
|
||||
continue;
|
||||
}
|
||||
input_vstorage_->GetOverlappingInputs(output_level_ + 1, &file->smallest,
|
||||
&file->largest, &file_grand_parents);
|
||||
const auto compaction_size =
|
||||
file->fd.GetFileSize() + TotalFileSize(file_grand_parents);
|
||||
if (compaction_size > max_compaction_bytes_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Compaction::AddInputDeletions(VersionEdit* out_edit) {
|
||||
@@ -271,8 +290,7 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
// Maybe use binary search to find right entry instead of linear search?
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
const std::vector<FileMetaData*>& files =
|
||||
input_version_->storage_info()->LevelFiles(lvl);
|
||||
const std::vector<FileMetaData*>& files = input_vstorage_->LevelFiles(lvl);
|
||||
for (; level_ptrs->at(lvl) < files.size(); level_ptrs->at(lvl)++) {
|
||||
auto* f = files[level_ptrs->at(lvl)];
|
||||
if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) {
|
||||
@@ -344,7 +362,7 @@ void Compaction::ReleaseCompactionFiles(Status status) {
|
||||
|
||||
void Compaction::ResetNextCompactionIndex() {
|
||||
assert(input_version_ != nullptr);
|
||||
input_version_->storage_info()->ResetNextCompactionIndex(start_level_);
|
||||
input_vstorage_->ResetNextCompactionIndex(start_level_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -428,7 +446,7 @@ bool Compaction::IsOutputLevelEmpty() const {
|
||||
}
|
||||
|
||||
bool Compaction::ShouldFormSubcompactions() const {
|
||||
if (mutable_cf_options_.max_subcompactions <= 1 || cfd_ == nullptr) {
|
||||
if (immutable_cf_options_.max_subcompactions <= 1 || cfd_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
|
||||
+23
-8
@@ -8,10 +8,10 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#include "db/version_set.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
#include "db/version_set.h"
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -35,9 +35,10 @@ class CompactionFilter;
|
||||
class Compaction {
|
||||
public:
|
||||
Compaction(VersionStorageInfo* input_version,
|
||||
const ImmutableCFOptions& immutable_cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
std::vector<CompactionInputFiles> inputs, int output_level,
|
||||
uint64_t target_file_size, uint64_t max_grandparent_overlap_bytes,
|
||||
uint64_t target_file_size, uint64_t max_compaction_bytes,
|
||||
uint32_t output_path_id, CompressionType compression,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
bool manual_compaction = false, double score = -1,
|
||||
@@ -103,6 +104,8 @@ class Compaction {
|
||||
return &inputs_[compaction_input_level].files;
|
||||
}
|
||||
|
||||
const std::vector<CompactionInputFiles>* inputs() { return &inputs_; }
|
||||
|
||||
// Returns the LevelFilesBrief of the specified compaction input level.
|
||||
const LevelFilesBrief* input_levels(size_t compaction_input_level) const {
|
||||
return &input_levels_[compaction_input_level];
|
||||
@@ -171,6 +174,12 @@ class Compaction {
|
||||
// How many total levels are there?
|
||||
int number_levels() const { return number_levels_; }
|
||||
|
||||
// Return the ImmutableCFOptions that should be used throughout the compaction
|
||||
// procedure
|
||||
const ImmutableCFOptions* immutable_cf_options() const {
|
||||
return &immutable_cf_options_;
|
||||
}
|
||||
|
||||
// Return the MutableCFOptions that should be used throughout the compaction
|
||||
// procedure
|
||||
const MutableCFOptions* mutable_cf_options() const {
|
||||
@@ -219,6 +228,8 @@ class Compaction {
|
||||
output_table_properties_ = std::move(tp);
|
||||
}
|
||||
|
||||
Slice GetSmallestUserKey() const { return smallest_user_key_; }
|
||||
|
||||
Slice GetLargestUserKey() const { return largest_user_key_; }
|
||||
|
||||
CompactionReason compaction_reason() { return compaction_reason_; }
|
||||
@@ -227,9 +238,7 @@ class Compaction {
|
||||
return grandparents_;
|
||||
}
|
||||
|
||||
uint64_t max_grandparent_overlap_bytes() const {
|
||||
return max_grandparent_overlap_bytes_;
|
||||
}
|
||||
uint64_t max_compaction_bytes() const { return max_compaction_bytes_; }
|
||||
|
||||
private:
|
||||
// mark (or clear) all files that are being compacted
|
||||
@@ -249,11 +258,14 @@ class Compaction {
|
||||
static bool IsFullCompaction(VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs);
|
||||
|
||||
VersionStorageInfo* input_vstorage_;
|
||||
|
||||
const int start_level_; // the lowest level to be compacted
|
||||
const int output_level_; // levels to which output files are stored
|
||||
uint64_t max_output_file_size_;
|
||||
uint64_t max_grandparent_overlap_bytes_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
uint64_t max_compaction_bytes_;
|
||||
const ImmutableCFOptions immutable_cf_options_;
|
||||
const MutableCFOptions mutable_cf_options_;
|
||||
Version* input_version_;
|
||||
VersionEdit edit_;
|
||||
const int number_levels_;
|
||||
@@ -294,6 +306,9 @@ class Compaction {
|
||||
// table properties of output files
|
||||
TablePropertiesCollection output_table_properties_;
|
||||
|
||||
// smallest user keys in compaction
|
||||
Slice smallest_user_key_;
|
||||
|
||||
// largest user keys in compaction
|
||||
Slice largest_user_key_;
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2016-present, 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
|
||||
|
||||
struct CompactionIterationStats {
|
||||
// Compaction statistics
|
||||
|
||||
// Doesn't include records skipped because of
|
||||
// CompactionFilter::Decision::kRemoveAndSkipUntil.
|
||||
int64_t num_record_drop_user = 0;
|
||||
|
||||
int64_t num_record_drop_hidden = 0;
|
||||
int64_t num_record_drop_obsolete = 0;
|
||||
int64_t num_record_drop_range_del = 0;
|
||||
int64_t num_range_del_drop_obsolete = 0;
|
||||
uint64_t total_filter_time = 0;
|
||||
|
||||
// Input statistics
|
||||
// TODO(noetzli): The stats are incomplete. They are lacking everything
|
||||
// consumed by MergeHelper.
|
||||
uint64_t num_input_records = 0;
|
||||
uint64_t num_input_deletion_records = 0;
|
||||
uint64_t num_input_corrupt_records = 0;
|
||||
uint64_t total_input_raw_key_bytes = 0;
|
||||
uint64_t total_input_raw_value_bytes = 0;
|
||||
|
||||
// Single-Delete diagnostics for exceptional situations
|
||||
uint64_t num_single_del_fallthru = 0;
|
||||
uint64_t num_single_del_mismatch = 0;
|
||||
};
|
||||
+116
-27
@@ -14,8 +14,25 @@ CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
bool expect_valid_internal_key, const Compaction* compaction,
|
||||
const CompactionFilter* compaction_filter, LogBuffer* log_buffer)
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, env, expect_valid_internal_key,
|
||||
range_del_agg,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new CompactionProxy(compaction) : nullptr),
|
||||
compaction_filter, shutting_down) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
@@ -23,9 +40,10 @@ CompactionIterator::CompactionIterator(
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
env_(env),
|
||||
expect_valid_internal_key_(expect_valid_internal_key),
|
||||
compaction_(compaction),
|
||||
range_del_agg_(range_del_agg),
|
||||
compaction_(std::move(compaction)),
|
||||
compaction_filter_(compaction_filter),
|
||||
log_buffer_(log_buffer),
|
||||
shutting_down_(shutting_down),
|
||||
merge_out_iter_(merge_helper_) {
|
||||
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
||||
bottommost_level_ =
|
||||
@@ -36,11 +54,11 @@ CompactionIterator::CompactionIterator(
|
||||
|
||||
if (snapshots_->size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = last_sequence;
|
||||
earliest_snapshot_ = visible_at_tip_;
|
||||
visible_at_tip_ = true;
|
||||
earliest_snapshot_ = last_sequence;
|
||||
latest_snapshot_ = 0;
|
||||
} else {
|
||||
visible_at_tip_ = 0;
|
||||
visible_at_tip_ = false;
|
||||
earliest_snapshot_ = snapshots_->at(0);
|
||||
latest_snapshot_ = snapshots_->back();
|
||||
}
|
||||
@@ -49,12 +67,20 @@ CompactionIterator::CompactionIterator(
|
||||
} else {
|
||||
ignore_snapshots_ = false;
|
||||
}
|
||||
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
CompactionIterator::~CompactionIterator() {
|
||||
// input_ Iteartor lifetime is longer than pinned_iters_mgr_ lifetime
|
||||
input_->SetPinnedItersMgr(nullptr);
|
||||
}
|
||||
|
||||
void CompactionIterator::ResetRecordCounts() {
|
||||
iter_stats_.num_record_drop_user = 0;
|
||||
iter_stats_.num_record_drop_hidden = 0;
|
||||
iter_stats_.num_record_drop_obsolete = 0;
|
||||
iter_stats_.num_record_drop_range_del = 0;
|
||||
iter_stats_.num_range_del_drop_obsolete = 0;
|
||||
}
|
||||
|
||||
void CompactionIterator::SeekToFirst() {
|
||||
@@ -83,6 +109,8 @@ void CompactionIterator::Next() {
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
valid_ = true;
|
||||
} else {
|
||||
// We consumed all pinned merge operands, release pinned iterators
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
// MergeHelper moves the iterator to the first record after the merged
|
||||
// records, so even though we reached the end of the merge output, we do
|
||||
// not want to advance the iterator.
|
||||
@@ -98,7 +126,7 @@ void CompactionIterator::Next() {
|
||||
}
|
||||
|
||||
if (valid_) {
|
||||
// Record that we've ouputted a record for the current key.
|
||||
// Record that we've outputted a record for the current key.
|
||||
has_outputted_key_ = true;
|
||||
}
|
||||
|
||||
@@ -109,7 +137,7 @@ void CompactionIterator::NextFromInput() {
|
||||
at_next_ = false;
|
||||
valid_ = false;
|
||||
|
||||
while (!valid_ && input_->Valid()) {
|
||||
while (!valid_ && input_->Valid() && !IsShuttingDown()) {
|
||||
key_ = input_->key();
|
||||
value_ = input_->value();
|
||||
iter_stats_.num_input_records++;
|
||||
@@ -139,12 +167,20 @@ void CompactionIterator::NextFromInput() {
|
||||
iter_stats_.total_input_raw_key_bytes += key_.size();
|
||||
iter_stats_.total_input_raw_value_bytes += value_.size();
|
||||
|
||||
// If need_skip is true, we should seek the input iterator
|
||||
// to internal key skip_until and continue from there.
|
||||
bool need_skip = false;
|
||||
// Points either into compaction_filter_skip_until_ or into
|
||||
// merge_helper_->compaction_filter_skip_until_.
|
||||
Slice skip_until;
|
||||
|
||||
// Check whether the user key changed. After this if statement current_key_
|
||||
// is a copy of the current input key (maybe converted to a delete by the
|
||||
// compaction filter). ikey_.user_key is pointing to the copy.
|
||||
if (!has_current_user_key_ ||
|
||||
!cmp_->Equal(ikey_.user_key, current_user_key_)) {
|
||||
// First occurrence of this user key
|
||||
// Copy key for output
|
||||
key_ = current_key_.SetKey(key_, &ikey_);
|
||||
current_user_key_ = ikey_.user_key;
|
||||
has_current_user_key_ = true;
|
||||
@@ -160,26 +196,42 @@ void CompactionIterator::NextFromInput() {
|
||||
// number is greater than any external snapshot, then invoke the
|
||||
// filter. If the return value of the compaction filter is true,
|
||||
// replace the entry with a deletion marker.
|
||||
bool value_changed = false;
|
||||
bool to_delete = false;
|
||||
CompactionFilter::Decision filter;
|
||||
compaction_filter_value_.clear();
|
||||
compaction_filter_skip_until_.Clear();
|
||||
{
|
||||
StopWatchNano timer(env_, true);
|
||||
to_delete = compaction_filter_->Filter(
|
||||
compaction_->level(), ikey_.user_key, value_,
|
||||
&compaction_filter_value_, &value_changed);
|
||||
filter = compaction_filter_->FilterV2(
|
||||
compaction_->level(), ikey_.user_key,
|
||||
CompactionFilter::ValueType::kValue, value_,
|
||||
&compaction_filter_value_, compaction_filter_skip_until_.rep());
|
||||
iter_stats_.total_filter_time +=
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0;
|
||||
}
|
||||
if (to_delete) {
|
||||
// convert the current key to a delete
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil &&
|
||||
cmp_->Compare(*compaction_filter_skip_until_.rep(),
|
||||
ikey_.user_key) <= 0) {
|
||||
// Can't skip to a key smaller than the current one.
|
||||
// Keep the key as per FilterV2 documentation.
|
||||
filter = CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemove) {
|
||||
// convert the current key to a delete; key_ is pointing into
|
||||
// current_key_ at this point, so updating current_key_ updates key()
|
||||
ikey_.type = kTypeDeletion;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, kTypeDeletion);
|
||||
// no value associated with delete
|
||||
value_.clear();
|
||||
iter_stats_.num_record_drop_user++;
|
||||
} else if (value_changed) {
|
||||
} else if (filter == CompactionFilter::Decision::kChangeValue) {
|
||||
value_ = compaction_filter_value_;
|
||||
} else if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil) {
|
||||
need_skip = true;
|
||||
compaction_filter_skip_until_.ConvertFromUserKey(kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
skip_until = compaction_filter_skip_until_.Encode();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -202,10 +254,13 @@ void CompactionIterator::NextFromInput() {
|
||||
SequenceNumber last_snapshot = current_user_key_snapshot_;
|
||||
SequenceNumber prev_snapshot = 0; // 0 means no previous snapshot
|
||||
current_user_key_snapshot_ =
|
||||
visible_at_tip_ ? visible_at_tip_ : findEarliestVisibleSnapshot(
|
||||
ikey_.sequence, &prev_snapshot);
|
||||
visible_at_tip_
|
||||
? earliest_snapshot_
|
||||
: findEarliestVisibleSnapshot(ikey_.sequence, &prev_snapshot);
|
||||
|
||||
if (clear_and_output_next_key_) {
|
||||
if (need_skip) {
|
||||
// This case is handled below.
|
||||
} else if (clear_and_output_next_key_) {
|
||||
// In the previous iteration we encountered a single delete that we could
|
||||
// not compact out. We will keep this Put, but can drop it's data.
|
||||
// (See Optimization 3, below.)
|
||||
@@ -246,6 +301,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// those operations for a given key is documented as being undefined. So
|
||||
// we can choose how to handle such a combinations of operations. We will
|
||||
// try to compact out as much as we can in these cases.
|
||||
// We will report counts on these anomalous cases.
|
||||
|
||||
// The easiest way to process a SingleDelete during iteration is to peek
|
||||
// ahead at the next key.
|
||||
@@ -268,6 +324,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// First SingleDelete has been skipped since we already called
|
||||
// input_->Next().
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
} else if ((ikey_.sequence <= earliest_write_conflict_snapshot_) ||
|
||||
has_outputted_key_) {
|
||||
// Found a matching value, we can drop the single delete and the
|
||||
@@ -277,7 +334,12 @@ void CompactionIterator::NextFromInput() {
|
||||
|
||||
// Note: it doesn't matter whether the second key is a Put or if it
|
||||
// is an unexpected Merge or Delete. We will compact it out
|
||||
// either way.
|
||||
// either way. We will maintain counts of how many mismatches
|
||||
// happened
|
||||
if (next_ikey.type != kTypeValue) {
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
}
|
||||
|
||||
++iter_stats_.num_record_drop_hidden;
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
// Already called input_->Next() once. Call it a second time to
|
||||
@@ -289,7 +351,6 @@ void CompactionIterator::NextFromInput() {
|
||||
// to know that a write happened in this snapshot (Rule 2 above).
|
||||
// Clear the value and output the SingleDelete. (The value will be
|
||||
// outputted on the next iteration.)
|
||||
++iter_stats_.num_record_drop_hidden;
|
||||
|
||||
// Setting valid_ to true will output the current SingleDelete
|
||||
valid_ = true;
|
||||
@@ -305,7 +366,7 @@ void CompactionIterator::NextFromInput() {
|
||||
}
|
||||
} else {
|
||||
// We are at the end of the input, could not parse the next key, or hit
|
||||
// the next key. The iterator returns the single delete if the key
|
||||
// a different key. The iterator returns the single delete if the key
|
||||
// possibly exists beyond the current output level. We set
|
||||
// has_current_user_key to false so that if the iterator is at the next
|
||||
// key, we do not compare it again against the previous key at the next
|
||||
@@ -318,6 +379,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// Key doesn't exist outside of this range.
|
||||
// Can compact out this SingleDelete.
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_fallthru;
|
||||
} else {
|
||||
// Output SingleDelete
|
||||
valid_ = true;
|
||||
@@ -362,20 +424,24 @@ void CompactionIterator::NextFromInput() {
|
||||
input_->Next();
|
||||
} else if (ikey_.type == kTypeMerge) {
|
||||
if (!merge_helper_->HasOperator()) {
|
||||
LogToBuffer(log_buffer_, "Options::merge_operator is null.");
|
||||
status_ = Status::InvalidArgument(
|
||||
"merge_operator is not properly initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
pinned_iters_mgr_.StartPinning();
|
||||
// We know the merge type entry is not hidden, otherwise we would
|
||||
// have hit (A)
|
||||
// We encapsulate the merge related state machine in a different
|
||||
// object to minimize change to the existing flow.
|
||||
merge_helper_->MergeUntil(input_, prev_snapshot, bottommost_level_);
|
||||
Status s = merge_helper_->MergeUntil(input_, range_del_agg_,
|
||||
prev_snapshot, bottommost_level_);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
|
||||
if (merge_out_iter_.Valid()) {
|
||||
if (!s.ok() && !s.IsMergeInProgress()) {
|
||||
status_ = s;
|
||||
return;
|
||||
} else if (merge_out_iter_.Valid()) {
|
||||
// NOTE: key, value, and ikey_ refer to old entries.
|
||||
// These will be correctly set below.
|
||||
key_ = merge_out_iter_.key();
|
||||
@@ -395,10 +461,33 @@ void CompactionIterator::NextFromInput() {
|
||||
// batch consumed by the merge operator should not shadow any keys
|
||||
// coming after the merges
|
||||
has_current_user_key_ = false;
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
|
||||
if (merge_helper_->FilteredUntil(&skip_until)) {
|
||||
need_skip = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valid_ = true;
|
||||
// 1. new user key -OR-
|
||||
// 2. different snapshot stripe
|
||||
bool should_delete = range_del_agg_->ShouldDelete(
|
||||
key_, RangeDelAggregator::RangePositioningMode::kForwardTraversal);
|
||||
if (should_delete) {
|
||||
++iter_stats_.num_record_drop_hidden;
|
||||
++iter_stats_.num_record_drop_range_del;
|
||||
input_->Next();
|
||||
} else {
|
||||
valid_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_skip) {
|
||||
input_->Seek(skip_until);
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid_ && IsShuttingDown()) {
|
||||
status_ = Status::ShutdownInProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+63
-24
@@ -12,39 +12,68 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/compaction_iteration_stats.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "util/log_buffer.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
struct CompactionIteratorStats {
|
||||
// Compaction statistics
|
||||
int64_t num_record_drop_user = 0;
|
||||
int64_t num_record_drop_hidden = 0;
|
||||
int64_t num_record_drop_obsolete = 0;
|
||||
uint64_t total_filter_time = 0;
|
||||
|
||||
// Input statistics
|
||||
// TODO(noetzli): The stats are incomplete. They are lacking everything
|
||||
// consumed by MergeHelper.
|
||||
uint64_t num_input_records = 0;
|
||||
uint64_t num_input_deletion_records = 0;
|
||||
uint64_t num_input_corrupt_records = 0;
|
||||
uint64_t total_input_raw_key_bytes = 0;
|
||||
uint64_t total_input_raw_value_bytes = 0;
|
||||
};
|
||||
|
||||
class CompactionIterator {
|
||||
public:
|
||||
// A wrapper around Compaction. Has a much smaller interface, only what
|
||||
// CompactionIterator uses. Tests can override it.
|
||||
class CompactionProxy {
|
||||
public:
|
||||
explicit CompactionProxy(const Compaction* compaction)
|
||||
: compaction_(compaction) {}
|
||||
|
||||
virtual ~CompactionProxy() = default;
|
||||
virtual int level(size_t compaction_input_level = 0) const {
|
||||
return compaction_->level();
|
||||
}
|
||||
virtual bool KeyNotExistsBeyondOutputLevel(
|
||||
const Slice& user_key, std::vector<size_t>* level_ptrs) const {
|
||||
return compaction_->KeyNotExistsBeyondOutputLevel(user_key, level_ptrs);
|
||||
}
|
||||
virtual bool bottommost_level() const {
|
||||
return compaction_->bottommost_level();
|
||||
}
|
||||
virtual int number_levels() const { return compaction_->number_levels(); }
|
||||
virtual Slice GetLargestUserKey() const {
|
||||
return compaction_->GetLargestUserKey();
|
||||
}
|
||||
|
||||
protected:
|
||||
CompactionProxy() = default;
|
||||
|
||||
private:
|
||||
const Compaction* compaction_;
|
||||
};
|
||||
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
LogBuffer* log_buffer = nullptr);
|
||||
const std::atomic<bool>* shutting_down = nullptr);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
void ResetRecordCounts();
|
||||
|
||||
@@ -65,7 +94,7 @@ class CompactionIterator {
|
||||
const ParsedInternalKey& ikey() const { return ikey_; }
|
||||
bool Valid() const { return valid_; }
|
||||
const Slice& user_key() const { return current_user_key_; }
|
||||
const CompactionIteratorStats& iter_stats() const { return iter_stats_; }
|
||||
const CompactionIterationStats& iter_stats() const { return iter_stats_; }
|
||||
|
||||
private:
|
||||
// Processes the input stream to find the next output
|
||||
@@ -92,12 +121,13 @@ class CompactionIterator {
|
||||
const SequenceNumber earliest_write_conflict_snapshot_;
|
||||
Env* env_;
|
||||
bool expect_valid_internal_key_;
|
||||
const Compaction* compaction_;
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
std::unique_ptr<CompactionProxy> compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
LogBuffer* log_buffer_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
SequenceNumber visible_at_tip_;
|
||||
bool visible_at_tip_;
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber latest_snapshot_;
|
||||
bool ignore_snapshots_;
|
||||
@@ -136,7 +166,11 @@ class CompactionIterator {
|
||||
bool clear_and_output_next_key_ = false;
|
||||
|
||||
MergeOutputIterator merge_out_iter_;
|
||||
// PinnedIteratorsManager used to pin input_ Iterator blocks while reading
|
||||
// merge operands and then releasing them after consuming them.
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
std::string compaction_filter_value_;
|
||||
InternalKey compaction_filter_skip_until_;
|
||||
// "level_ptrs" holds indices that remember which file of an associated
|
||||
// level we were last checking during the last call to compaction->
|
||||
// KeyNotExistsBeyondOutputLevel(). This allows future calls to the function
|
||||
@@ -144,6 +178,11 @@ class CompactionIterator {
|
||||
// increasing so a later call to the function must be looking for a key that
|
||||
// is in or beyond the last file checked during the previous call
|
||||
std::vector<size_t> level_ptrs_;
|
||||
CompactionIteratorStats iter_stats_;
|
||||
CompactionIterationStats iter_stats_;
|
||||
|
||||
bool IsShuttingDown() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
return shutting_down_ && shutting_down_->load(std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
} // namespace rocksdb
|
||||
+407
-16
@@ -4,40 +4,213 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Expects no merging attempts.
|
||||
class NoMergingMergeOp : public MergeOperator {
|
||||
public:
|
||||
bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
ADD_FAILURE();
|
||||
return false;
|
||||
}
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
const std::deque<Slice>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
ADD_FAILURE();
|
||||
return false;
|
||||
}
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest NoMergingMergeOp";
|
||||
}
|
||||
};
|
||||
|
||||
// Compaction filter that gets stuck when it sees a particular key,
|
||||
// then gets unstuck when told to.
|
||||
// Always returns Decition::kRemove.
|
||||
class StallingFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType t,
|
||||
const Slice& existing_value, std::string* new_value,
|
||||
std::string* skip_until) const override {
|
||||
int k = std::atoi(key.ToString().c_str());
|
||||
last_seen.store(k);
|
||||
while (k >= stall_at.load()) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
return Decision::kRemove;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest StallingFilter";
|
||||
}
|
||||
|
||||
// Wait until the filter sees a key >= k and stalls at that key.
|
||||
// If `exact`, asserts that the seen key is equal to k.
|
||||
void WaitForStall(int k, bool exact = true) {
|
||||
stall_at.store(k);
|
||||
while (last_seen.load() < k) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
if (exact) {
|
||||
EXPECT_EQ(k, last_seen.load());
|
||||
}
|
||||
}
|
||||
|
||||
// Filter will stall on key >= stall_at. Advance stall_at to unstall.
|
||||
mutable std::atomic<int> stall_at{0};
|
||||
// Last key the filter was called with.
|
||||
mutable std::atomic<int> last_seen{0};
|
||||
};
|
||||
|
||||
class LoggingForwardVectorIterator : public InternalIterator {
|
||||
public:
|
||||
struct Action {
|
||||
enum class Type {
|
||||
SEEK_TO_FIRST,
|
||||
SEEK,
|
||||
NEXT,
|
||||
};
|
||||
|
||||
Type type;
|
||||
std::string arg;
|
||||
|
||||
explicit Action(Type _type, std::string _arg = "")
|
||||
: type(_type), arg(_arg) {}
|
||||
|
||||
bool operator==(const Action& rhs) const {
|
||||
return std::tie(type, arg) == std::tie(rhs.type, rhs.arg);
|
||||
}
|
||||
};
|
||||
|
||||
LoggingForwardVectorIterator(const std::vector<std::string>& keys,
|
||||
const std::vector<std::string>& values)
|
||||
: keys_(keys), values_(values), current_(keys.size()) {
|
||||
assert(keys_.size() == values_.size());
|
||||
}
|
||||
|
||||
virtual bool Valid() const override { return current_ < keys_.size(); }
|
||||
|
||||
virtual void SeekToFirst() override {
|
||||
log.emplace_back(Action::Type::SEEK_TO_FIRST);
|
||||
current_ = 0;
|
||||
}
|
||||
virtual void SeekToLast() override { assert(false); }
|
||||
|
||||
virtual void Seek(const Slice& target) override {
|
||||
log.emplace_back(Action::Type::SEEK, target.ToString());
|
||||
current_ = std::lower_bound(keys_.begin(), keys_.end(), target.ToString()) -
|
||||
keys_.begin();
|
||||
}
|
||||
|
||||
virtual void SeekForPrev(const Slice& target) override { assert(false); }
|
||||
|
||||
virtual void Next() override {
|
||||
assert(Valid());
|
||||
log.emplace_back(Action::Type::NEXT);
|
||||
current_++;
|
||||
}
|
||||
virtual void Prev() override { assert(false); }
|
||||
|
||||
virtual Slice key() const override {
|
||||
assert(Valid());
|
||||
return Slice(keys_[current_]);
|
||||
}
|
||||
virtual Slice value() const override {
|
||||
assert(Valid());
|
||||
return Slice(values_[current_]);
|
||||
}
|
||||
|
||||
virtual Status status() const override { return Status::OK(); }
|
||||
|
||||
std::vector<Action> log;
|
||||
|
||||
private:
|
||||
std::vector<std::string> keys_;
|
||||
std::vector<std::string> values_;
|
||||
size_t current_;
|
||||
};
|
||||
|
||||
class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
public:
|
||||
FakeCompaction() = default;
|
||||
|
||||
virtual int level(size_t compaction_input_level) const { return 0; }
|
||||
virtual bool KeyNotExistsBeyondOutputLevel(
|
||||
const Slice& user_key, std::vector<size_t>* level_ptrs) const {
|
||||
return key_not_exists_beyond_output_level;
|
||||
}
|
||||
virtual bool bottommost_level() const { return false; }
|
||||
virtual int number_levels() const { return 1; }
|
||||
virtual Slice GetLargestUserKey() const {
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
};
|
||||
|
||||
class CompactionIteratorTest : public testing::Test {
|
||||
public:
|
||||
CompactionIteratorTest() : cmp_(BytewiseComparator()), snapshots_({}) {}
|
||||
CompactionIteratorTest()
|
||||
: cmp_(BytewiseComparator()), icmp_(cmp_), snapshots_({}) {}
|
||||
|
||||
void InitIterator(const std::vector<std::string>& ks,
|
||||
const std::vector<std::string>& vs,
|
||||
SequenceNumber last_sequence) {
|
||||
merge_helper_.reset(new MergeHelper(Env::Default(), cmp_, nullptr, nullptr,
|
||||
nullptr, 0U, false, 0));
|
||||
iter_.reset(new test::VectorIterator(ks, vs));
|
||||
void InitIterators(const std::vector<std::string>& ks,
|
||||
const std::vector<std::string>& vs,
|
||||
const std::vector<std::string>& range_del_ks,
|
||||
const std::vector<std::string>& range_del_vs,
|
||||
SequenceNumber last_sequence,
|
||||
MergeOperator* merge_op = nullptr,
|
||||
CompactionFilter* filter = nullptr) {
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
new test::VectorIterator(range_del_ks, range_del_vs));
|
||||
range_del_agg_.reset(new RangeDelAggregator(icmp_, snapshots_));
|
||||
ASSERT_OK(range_del_agg_->AddTombstones(std::move(range_del_iter)));
|
||||
|
||||
std::unique_ptr<CompactionIterator::CompactionProxy> compaction;
|
||||
if (filter) {
|
||||
compaction_proxy_ = new FakeCompaction();
|
||||
compaction.reset(compaction_proxy_);
|
||||
}
|
||||
|
||||
merge_helper_.reset(new MergeHelper(Env::Default(), cmp_, merge_op, filter,
|
||||
nullptr, 0U, false, 0, 0, nullptr,
|
||||
&shutting_down_));
|
||||
iter_.reset(new LoggingForwardVectorIterator(ks, vs));
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
kMaxSequenceNumber, Env::Default(), false));
|
||||
kMaxSequenceNumber, Env::Default(), false, range_del_agg_.get(),
|
||||
std::move(compaction), filter, &shutting_down_));
|
||||
}
|
||||
|
||||
void AddSnapshot(SequenceNumber snapshot) { snapshots_.push_back(snapshot); }
|
||||
|
||||
const Comparator* cmp_;
|
||||
const InternalKeyComparator icmp_;
|
||||
std::vector<SequenceNumber> snapshots_;
|
||||
std::unique_ptr<MergeHelper> merge_helper_;
|
||||
std::unique_ptr<test::VectorIterator> iter_;
|
||||
std::unique_ptr<LoggingForwardVectorIterator> iter_;
|
||||
std::unique_ptr<CompactionIterator> c_iter_;
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg_;
|
||||
std::atomic<bool> shutting_down_{false};
|
||||
FakeCompaction* compaction_proxy_;
|
||||
};
|
||||
|
||||
// It is possible that the output of the compaction iterator is empty even if
|
||||
// the input is not.
|
||||
TEST_F(CompactionIteratorTest, EmptyResult) {
|
||||
InitIterator({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue)},
|
||||
{"", "val"}, 5);
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue)},
|
||||
{"", "val"}, {}, {}, 5);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
@@ -45,10 +218,10 @@ TEST_F(CompactionIteratorTest, EmptyResult) {
|
||||
// If there is a corruption after a single deletion, the corrupted key should
|
||||
// be preserved.
|
||||
TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
InitIterator({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue, true),
|
||||
test::KeyStr("b", 10, kTypeValue)},
|
||||
{"", "val", "val2"}, 10);
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue, true),
|
||||
test::KeyStr("b", 10, kTypeValue)},
|
||||
{"", "val", "val2"}, {}, {}, 10);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
@@ -63,6 +236,224 @@ TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValue),
|
||||
test::KeyStr("morning", 2, kTypeValue),
|
||||
test::KeyStr("night", 3, kTypeValue)},
|
||||
{"zao", "zao", "wan"},
|
||||
{test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 5);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 3, kTypeValue), c_iter_->key().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
AddSnapshot(10);
|
||||
std::vector<std::string> ks1;
|
||||
ks1.push_back(test::KeyStr("ma", 28, kTypeRangeDeletion));
|
||||
std::vector<std::string> vs1{"mz"};
|
||||
std::vector<std::string> ks2{test::KeyStr("morning", 15, kTypeValue),
|
||||
test::KeyStr("morning", 5, kTypeValue),
|
||||
test::KeyStr("night", 40, kTypeValue),
|
||||
test::KeyStr("night", 20, kTypeValue)};
|
||||
std::vector<std::string> vs2{"zao 15", "zao 5", "wan 40", "wan 20"};
|
||||
InitIterators(ks2, vs2, ks1, vs1, 40);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 40, kTypeValue), c_iter_->key().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, CompactionFilterSkipUntil) {
|
||||
class Filter : public CompactionFilter {
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType t,
|
||||
const Slice& existing_value,
|
||||
std::string* new_value,
|
||||
std::string* skip_until) const override {
|
||||
std::string k = key.ToString();
|
||||
std::string v = existing_value.ToString();
|
||||
// See InitIterators() call below for the sequence of keys and their
|
||||
// filtering decisions. Here we closely assert that compaction filter is
|
||||
// called with the expected keys and only them, and with the right values.
|
||||
if (k == "a") {
|
||||
EXPECT_EQ(ValueType::kValue, t);
|
||||
EXPECT_EQ("av50", v);
|
||||
return Decision::kKeep;
|
||||
}
|
||||
if (k == "b") {
|
||||
EXPECT_EQ(ValueType::kValue, t);
|
||||
EXPECT_EQ("bv60", v);
|
||||
*skip_until = "d+";
|
||||
return Decision::kRemoveAndSkipUntil;
|
||||
}
|
||||
if (k == "e") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
EXPECT_EQ("em71", v);
|
||||
return Decision::kKeep;
|
||||
}
|
||||
if (k == "f") {
|
||||
if (v == "fm65") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
*skip_until = "f";
|
||||
} else {
|
||||
EXPECT_EQ("fm30", v);
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
*skip_until = "g+";
|
||||
}
|
||||
return Decision::kRemoveAndSkipUntil;
|
||||
}
|
||||
if (k == "h") {
|
||||
EXPECT_EQ(ValueType::kValue, t);
|
||||
EXPECT_EQ("hv91", v);
|
||||
return Decision::kKeep;
|
||||
}
|
||||
if (k == "i") {
|
||||
EXPECT_EQ(ValueType::kMergeOperand, t);
|
||||
EXPECT_EQ("im95", v);
|
||||
*skip_until = "z";
|
||||
return Decision::kRemoveAndSkipUntil;
|
||||
}
|
||||
ADD_FAILURE();
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactionIteratorTest.CompactionFilterSkipUntil::Filter";
|
||||
}
|
||||
};
|
||||
|
||||
NoMergingMergeOp merge_op;
|
||||
Filter filter;
|
||||
InitIterators(
|
||||
{test::KeyStr("a", 50, kTypeValue), // keep
|
||||
test::KeyStr("a", 45, kTypeMerge),
|
||||
test::KeyStr("b", 60, kTypeValue), // skip to "d+"
|
||||
test::KeyStr("b", 40, kTypeValue), test::KeyStr("c", 35, kTypeValue),
|
||||
test::KeyStr("d", 70, kTypeMerge),
|
||||
test::KeyStr("e", 71, kTypeMerge), // keep
|
||||
test::KeyStr("f", 65, kTypeMerge), // skip to "f", aka keep
|
||||
test::KeyStr("f", 30, kTypeMerge), // skip to "g+"
|
||||
test::KeyStr("f", 25, kTypeValue), test::KeyStr("g", 90, kTypeValue),
|
||||
test::KeyStr("h", 91, kTypeValue), // keep
|
||||
test::KeyStr("i", 95, kTypeMerge), // skip to "z"
|
||||
test::KeyStr("j", 99, kTypeValue)},
|
||||
{"av50", "am45", "bv60", "bv40", "cv35", "dm70", "em71", "fm65", "fm30",
|
||||
"fv25", "gv90", "hv91", "im95", "jv99"},
|
||||
{}, {}, kMaxSequenceNumber, &merge_op, &filter);
|
||||
|
||||
// Compaction should output just "a", "e" and "h" keys.
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("a", 50, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_EQ("av50", c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("e", 71, kTypeMerge), c_iter_->key().ToString());
|
||||
ASSERT_EQ("em71", c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("h", 91, kTypeValue), c_iter_->key().ToString());
|
||||
ASSERT_EQ("hv91", c_iter_->value().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
|
||||
// Check that the compaction iterator did the correct sequence of calls on
|
||||
// the underlying iterator.
|
||||
using A = LoggingForwardVectorIterator::Action;
|
||||
using T = A::Type;
|
||||
std::vector<A> expected_actions = {
|
||||
A(T::SEEK_TO_FIRST),
|
||||
A(T::NEXT),
|
||||
A(T::NEXT),
|
||||
A(T::SEEK, test::KeyStr("d+", kMaxSequenceNumber, kValueTypeForSeek)),
|
||||
A(T::NEXT),
|
||||
A(T::NEXT),
|
||||
A(T::SEEK, test::KeyStr("g+", kMaxSequenceNumber, kValueTypeForSeek)),
|
||||
A(T::NEXT),
|
||||
A(T::SEEK, test::KeyStr("z", kMaxSequenceNumber, kValueTypeForSeek))};
|
||||
ASSERT_EQ(expected_actions, iter_->log);
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, ShuttingDownInFilter) {
|
||||
NoMergingMergeOp merge_op;
|
||||
StallingFilter filter;
|
||||
InitIterators(
|
||||
{test::KeyStr("1", 1, kTypeValue), test::KeyStr("2", 2, kTypeValue),
|
||||
test::KeyStr("3", 3, kTypeValue), test::KeyStr("4", 4, kTypeValue)},
|
||||
{"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, &merge_op, &filter);
|
||||
// Don't leave tombstones (kTypeDeletion) for filtered keys.
|
||||
compaction_proxy_->key_not_exists_beyond_output_level = true;
|
||||
|
||||
std::atomic<bool> seek_done{false};
|
||||
rocksdb::port::Thread compaction_thread([&] {
|
||||
c_iter_->SeekToFirst();
|
||||
EXPECT_FALSE(c_iter_->Valid());
|
||||
EXPECT_TRUE(c_iter_->status().IsShutdownInProgress());
|
||||
seek_done.store(true);
|
||||
});
|
||||
|
||||
// Let key 1 through.
|
||||
filter.WaitForStall(1);
|
||||
|
||||
// Shutdown during compaction filter call for key 2.
|
||||
filter.WaitForStall(2);
|
||||
shutting_down_.store(true);
|
||||
EXPECT_FALSE(seek_done.load());
|
||||
|
||||
// Unstall filter and wait for SeekToFirst() to return.
|
||||
filter.stall_at.store(3);
|
||||
compaction_thread.join();
|
||||
assert(seek_done.load());
|
||||
|
||||
// Check that filter was never called again.
|
||||
EXPECT_EQ(2, filter.last_seen.load());
|
||||
}
|
||||
|
||||
// Same as ShuttingDownInFilter, but shutdown happens during filter call for
|
||||
// a merge operand, not for a value.
|
||||
TEST_F(CompactionIteratorTest, ShuttingDownInMerge) {
|
||||
NoMergingMergeOp merge_op;
|
||||
StallingFilter filter;
|
||||
InitIterators(
|
||||
{test::KeyStr("1", 1, kTypeValue), test::KeyStr("2", 2, kTypeMerge),
|
||||
test::KeyStr("3", 3, kTypeMerge), test::KeyStr("4", 4, kTypeValue)},
|
||||
{"v1", "v2", "v3", "v4"}, {}, {}, kMaxSequenceNumber, &merge_op, &filter);
|
||||
compaction_proxy_->key_not_exists_beyond_output_level = true;
|
||||
|
||||
std::atomic<bool> seek_done{false};
|
||||
rocksdb::port::Thread compaction_thread([&] {
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
ASSERT_TRUE(c_iter_->status().IsShutdownInProgress());
|
||||
seek_done.store(true);
|
||||
});
|
||||
|
||||
// Let key 1 through.
|
||||
filter.WaitForStall(1);
|
||||
|
||||
// Shutdown during compaction filter call for key 2.
|
||||
filter.WaitForStall(2);
|
||||
shutting_down_.store(true);
|
||||
EXPECT_FALSE(seek_done.load());
|
||||
|
||||
// Unstall filter and wait for SeekToFirst() to return.
|
||||
filter.stall_at.store(3);
|
||||
compaction_thread.join();
|
||||
assert(seek_done.load());
|
||||
|
||||
// Check that filter was never called again.
|
||||
EXPECT_EQ(2, filter.last_seen.load());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+154
-47
@@ -45,7 +45,7 @@
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
@@ -99,6 +99,8 @@ struct CompactionJob::SubcompactionState {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t current_output_file_size;
|
||||
|
||||
// State during the subcompaction
|
||||
uint64_t total_bytes;
|
||||
uint64_t num_input_records;
|
||||
@@ -121,6 +123,7 @@ struct CompactionJob::SubcompactionState {
|
||||
end(_end),
|
||||
outfile(nullptr),
|
||||
builder(nullptr),
|
||||
current_output_file_size(0),
|
||||
total_bytes(0),
|
||||
num_input_records(0),
|
||||
num_output_records(0),
|
||||
@@ -142,6 +145,7 @@ struct CompactionJob::SubcompactionState {
|
||||
outputs = std::move(o.outputs);
|
||||
outfile = std::move(o.outfile);
|
||||
builder = std::move(o.builder);
|
||||
current_output_file_size = std::move(o.current_output_file_size);
|
||||
total_bytes = std::move(o.total_bytes);
|
||||
num_input_records = std::move(o.num_input_records);
|
||||
num_output_records = std::move(o.num_output_records);
|
||||
@@ -161,7 +165,7 @@ struct CompactionJob::SubcompactionState {
|
||||
|
||||
// Returns true iff we should stop building the current output
|
||||
// before processing "internal_key".
|
||||
bool ShouldStopBefore(const Slice& internal_key) {
|
||||
bool ShouldStopBefore(const Slice& internal_key, uint64_t curr_file_size) {
|
||||
const InternalKeyComparator* icmp =
|
||||
&compaction->column_family_data()->internal_comparator();
|
||||
const std::vector<FileMetaData*>& grandparents = compaction->grandparents();
|
||||
@@ -182,7 +186,8 @@ struct CompactionJob::SubcompactionState {
|
||||
}
|
||||
seen_key = true;
|
||||
|
||||
if (overlapped_bytes > compaction->max_grandparent_overlap_bytes()) {
|
||||
if (overlapped_bytes + curr_file_size >
|
||||
compaction->max_compaction_bytes()) {
|
||||
// Too much overlap for current output; start new output
|
||||
overlapped_bytes = 0;
|
||||
return true;
|
||||
@@ -257,9 +262,9 @@ void CompactionJob::AggregateStatistics() {
|
||||
}
|
||||
|
||||
CompactionJob::CompactionJob(
|
||||
int job_id, Compaction* compaction, const DBOptions& db_options,
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, Status* db_bg_error,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
@@ -292,7 +297,7 @@ CompactionJob::CompactionJob(
|
||||
assert(log_buffer_ != nullptr);
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
cfd->options()->enable_thread_tracking);
|
||||
db_options_.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
|
||||
ReportStartedCompaction(compaction);
|
||||
}
|
||||
@@ -306,7 +311,7 @@ void CompactionJob::ReportStartedCompaction(
|
||||
Compaction* compaction) {
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
cfd->options()->enable_thread_tracking);
|
||||
db_options_.enable_thread_tracking);
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_JOB_ID,
|
||||
@@ -479,9 +484,8 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
static_cast<uint64_t>(db_options_.max_subcompactions),
|
||||
max_output_files});
|
||||
|
||||
double mean = sum * 1.0 / subcompactions;
|
||||
|
||||
if (subcompactions > 1) {
|
||||
double mean = sum * 1.0 / subcompactions;
|
||||
// Greedily add ranges to the subcompaction until the sum of the ranges'
|
||||
// sizes becomes >= the expected mean size of a subcompaction
|
||||
sum = 0;
|
||||
@@ -518,7 +522,7 @@ Status CompactionJob::Run() {
|
||||
const uint64_t start_micros = env_->NowMicros();
|
||||
|
||||
// Launch a thread for each of subcompactions 1...num_threads-1
|
||||
std::vector<std::thread> thread_pool;
|
||||
std::vector<port::Thread> thread_pool;
|
||||
thread_pool.reserve(num_threads - 1);
|
||||
for (size_t i = 1; i < compact_->sub_compact_states.size(); i++) {
|
||||
thread_pool.emplace_back(&CompactionJob::ProcessKeyValueCompaction, this,
|
||||
@@ -534,7 +538,7 @@ Status CompactionJob::Run() {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
if (output_directory_ && !db_options_.disableDataSync) {
|
||||
if (output_directory_ && !db_options_.disable_data_sync) {
|
||||
output_directory_->Fsync();
|
||||
}
|
||||
|
||||
@@ -586,6 +590,16 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
auto vstorage = cfd->current()->storage_info();
|
||||
const auto& stats = compaction_stats_;
|
||||
|
||||
double read_write_amp = 0.0;
|
||||
double write_amp = 0.0;
|
||||
if (stats.bytes_read_non_output_levels > 0) {
|
||||
read_write_amp = (stats.bytes_written + stats.bytes_read_output_level +
|
||||
stats.bytes_read_non_output_levels) /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels);
|
||||
write_amp = stats.bytes_written /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels);
|
||||
}
|
||||
LogToBuffer(
|
||||
log_buffer_,
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
@@ -598,16 +612,10 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
stats.bytes_written / static_cast<double>(stats.micros),
|
||||
compact_->compaction->output_level(),
|
||||
stats.num_input_files_in_non_output_levels,
|
||||
stats.num_input_files_in_output_level,
|
||||
stats.num_output_files,
|
||||
stats.num_input_files_in_output_level, stats.num_output_files,
|
||||
stats.bytes_read_non_output_levels / 1048576.0,
|
||||
stats.bytes_read_output_level / 1048576.0,
|
||||
stats.bytes_written / 1048576.0,
|
||||
(stats.bytes_written + stats.bytes_read_output_level +
|
||||
stats.bytes_read_non_output_levels) /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels),
|
||||
stats.bytes_written /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels),
|
||||
stats.bytes_written / 1048576.0, read_write_amp, write_amp,
|
||||
status.ToString().c_str(), stats.num_input_records,
|
||||
stats.num_dropped_records);
|
||||
|
||||
@@ -624,6 +632,13 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
<< "num_output_records" << compact_->num_output_records
|
||||
<< "num_subcompactions" << compact_->sub_compact_states.size();
|
||||
|
||||
if (compaction_job_stats_ != nullptr) {
|
||||
stream << "num_single_delete_mismatches"
|
||||
<< compaction_job_stats_->num_single_del_mismatch;
|
||||
stream << "num_single_delete_fallthrough"
|
||||
<< compaction_job_stats_->num_single_del_fallthru;
|
||||
}
|
||||
|
||||
if (measure_io_stats_ && compaction_job_stats_ != nullptr) {
|
||||
stream << "file_write_nanos" << compaction_job_stats_->file_write_nanos;
|
||||
stream << "file_range_sync_nanos"
|
||||
@@ -646,8 +661,11 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
|
||||
void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact != nullptr);
|
||||
std::unique_ptr<InternalIterator> input(
|
||||
versions_->MakeInputIterator(sub_compact->compaction));
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg(
|
||||
new RangeDelAggregator(cfd->internal_comparator(), existing_snapshots_));
|
||||
std::unique_ptr<InternalIterator> input(versions_->MakeInputIterator(
|
||||
sub_compact->compaction, range_del_agg.get()));
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -668,7 +686,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
prev_prepare_write_nanos = IOSTATS(prepare_write_nanos);
|
||||
}
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
const MutableCFOptions* mutable_cf_options =
|
||||
sub_compact->compaction->mutable_cf_options();
|
||||
|
||||
@@ -707,7 +724,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
mutable_cf_options->min_partial_merge_operands,
|
||||
false /* internal key corruption is expected */,
|
||||
existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
|
||||
compact_->compaction->level(), db_options_.statistics.get());
|
||||
compact_->compaction->level(), db_options_.statistics.get(),
|
||||
shutting_down_);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
|
||||
@@ -725,7 +743,8 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
sub_compact->c_iter.reset(new CompactionIterator(
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_, env_, false,
|
||||
sub_compact->compaction, compaction_filter));
|
||||
range_del_agg.get(), sub_compact->compaction, compaction_filter,
|
||||
shutting_down_));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
const auto& c_iter_stats = c_iter->iter_stats();
|
||||
@@ -736,10 +755,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
std::string compression_dict;
|
||||
compression_dict.reserve(cfd->ioptions()->compression_opts.max_dict_bytes);
|
||||
|
||||
// TODO(noetzli): check whether we could check !shutting_down_->... only
|
||||
// only occasionally (see diff D42687)
|
||||
while (status.ok() && !shutting_down_->load(std::memory_order_acquire) &&
|
||||
!cfd->IsDropped() && c_iter->Valid()) {
|
||||
while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
|
||||
// Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
|
||||
// returns true.
|
||||
const Slice& key = c_iter->key();
|
||||
@@ -750,9 +766,16 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
if (end != nullptr &&
|
||||
cfd->user_comparator()->Compare(c_iter->user_key(), *end) >= 0) {
|
||||
break;
|
||||
} else if (sub_compact->ShouldStopBefore(key) &&
|
||||
} else if (sub_compact->compaction->output_level() != 0 &&
|
||||
sub_compact->ShouldStopBefore(
|
||||
key, sub_compact->current_output_file_size) &&
|
||||
sub_compact->builder != nullptr) {
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact);
|
||||
CompactionIterationStats range_del_out_stats;
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact,
|
||||
range_del_agg.get(),
|
||||
&range_del_out_stats, &key);
|
||||
RecordDroppedKeys(range_del_out_stats,
|
||||
&sub_compact->compaction_job_stats);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -775,6 +798,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact->builder != nullptr);
|
||||
assert(sub_compact->current_output() != nullptr);
|
||||
sub_compact->builder->Add(key, value);
|
||||
sub_compact->current_output_file_size = sub_compact->builder->FileSize();
|
||||
sub_compact->current_output()->meta.UpdateBoundaries(
|
||||
key, c_iter->ikey().sequence);
|
||||
sub_compact->num_output_records++;
|
||||
@@ -833,16 +857,30 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
// during subcompactions (i.e. if output size, estimated by input size, is
|
||||
// going to be 1.2MB and max_output_file_size = 1MB, prefer to have 0.6MB
|
||||
// and 0.6MB instead of 1MB and 0.2MB)
|
||||
if (sub_compact->builder->FileSize() >=
|
||||
sub_compact->compaction->max_output_file_size()) {
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact);
|
||||
if (sub_compact->compaction->output_level() != 0 &&
|
||||
sub_compact->current_output_file_size >=
|
||||
sub_compact->compaction->max_output_file_size()) {
|
||||
Status input_status = input->status();
|
||||
c_iter->Next();
|
||||
|
||||
const Slice* next_key = nullptr;
|
||||
if (c_iter->Valid()) {
|
||||
next_key = &c_iter->key();
|
||||
}
|
||||
CompactionIterationStats range_del_out_stats;
|
||||
status = FinishCompactionOutputFile(input_status, sub_compact,
|
||||
range_del_agg.get(),
|
||||
&range_del_out_stats, next_key);
|
||||
RecordDroppedKeys(range_del_out_stats,
|
||||
&sub_compact->compaction_job_stats);
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
// Use dictionary from first output file for compression of subsequent
|
||||
// files.
|
||||
sub_compact->compression_dict = std::move(compression_dict);
|
||||
}
|
||||
} else {
|
||||
c_iter->Next();
|
||||
}
|
||||
c_iter->Next();
|
||||
}
|
||||
|
||||
sub_compact->num_input_records = c_iter_stats.num_input_records;
|
||||
@@ -850,6 +888,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
c_iter_stats.num_input_deletion_records;
|
||||
sub_compact->compaction_job_stats.num_corrupt_keys =
|
||||
c_iter_stats.num_input_corrupt_records;
|
||||
sub_compact->compaction_job_stats.num_single_del_fallthru =
|
||||
c_iter_stats.num_single_del_fallthru;
|
||||
sub_compact->compaction_job_stats.num_single_del_mismatch =
|
||||
c_iter_stats.num_single_del_mismatch;
|
||||
sub_compact->compaction_job_stats.total_input_raw_key_bytes +=
|
||||
c_iter_stats.total_input_raw_key_bytes;
|
||||
sub_compact->compaction_job_stats.total_input_raw_value_bytes +=
|
||||
@@ -860,17 +902,36 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
|
||||
RecordCompactionIOStats();
|
||||
|
||||
if (status.ok() &&
|
||||
(shutting_down_->load(std::memory_order_acquire) || cfd->IsDropped())) {
|
||||
if (status.ok() && (shutting_down_->load(std::memory_order_relaxed) ||
|
||||
cfd->IsDropped())) {
|
||||
status = Status::ShutdownInProgress(
|
||||
"Database shutdown or Column family drop during compaction");
|
||||
}
|
||||
if (status.ok() && sub_compact->builder != nullptr) {
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact);
|
||||
}
|
||||
if (status.ok()) {
|
||||
status = input->status();
|
||||
}
|
||||
if (status.ok()) {
|
||||
status = c_iter->status();
|
||||
}
|
||||
|
||||
if (status.ok() && sub_compact->builder == nullptr &&
|
||||
sub_compact->outputs.size() == 0 &&
|
||||
range_del_agg->ShouldAddTombstones(bottommost_level_)) {
|
||||
// handle subcompaction containing only range deletions
|
||||
status = OpenCompactionOutputFile(sub_compact);
|
||||
}
|
||||
|
||||
// Call FinishCompactionOutputFile() even if status is not ok: it needs to
|
||||
// close the output file.
|
||||
if (sub_compact->builder != nullptr) {
|
||||
CompactionIterationStats range_del_out_stats;
|
||||
Status s = FinishCompactionOutputFile(
|
||||
status, sub_compact, range_del_agg.get(), &range_del_out_stats);
|
||||
if (status.ok()) {
|
||||
status = s;
|
||||
}
|
||||
RecordDroppedKeys(range_del_out_stats, &sub_compact->compaction_job_stats);
|
||||
}
|
||||
|
||||
if (measure_io_stats_) {
|
||||
sub_compact->compaction_job_stats.file_write_nanos +=
|
||||
@@ -892,7 +953,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
void CompactionJob::RecordDroppedKeys(
|
||||
const CompactionIteratorStats& c_iter_stats,
|
||||
const CompactionIterationStats& c_iter_stats,
|
||||
CompactionJobStats* compaction_job_stats) {
|
||||
if (c_iter_stats.num_record_drop_user > 0) {
|
||||
RecordTick(stats_, COMPACTION_KEY_DROP_USER,
|
||||
@@ -914,10 +975,21 @@ void CompactionJob::RecordDroppedKeys(
|
||||
c_iter_stats.num_record_drop_obsolete;
|
||||
}
|
||||
}
|
||||
if (c_iter_stats.num_record_drop_range_del > 0) {
|
||||
RecordTick(stats_, COMPACTION_KEY_DROP_RANGE_DEL,
|
||||
c_iter_stats.num_record_drop_range_del);
|
||||
}
|
||||
if (c_iter_stats.num_range_del_drop_obsolete > 0) {
|
||||
RecordTick(stats_, COMPACTION_RANGE_DEL_DROP_OBSOLETE,
|
||||
c_iter_stats.num_range_del_drop_obsolete);
|
||||
}
|
||||
}
|
||||
|
||||
Status CompactionJob::FinishCompactionOutputFile(
|
||||
const Status& input_status, SubcompactionState* sub_compact) {
|
||||
const Status& input_status, SubcompactionState* sub_compact,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionIterationStats* range_del_out_stats,
|
||||
const Slice* next_table_min_key /* = nullptr */) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_SYNC_FILE);
|
||||
assert(sub_compact != nullptr);
|
||||
@@ -932,6 +1004,36 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// Check for iterator errors
|
||||
Status s = input_status;
|
||||
auto meta = &sub_compact->current_output()->meta;
|
||||
if (s.ok()) {
|
||||
Slice lower_bound_guard, upper_bound_guard;
|
||||
const Slice *lower_bound, *upper_bound;
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
// For the first output table, include range tombstones before the min key
|
||||
// but after the subcompaction boundary.
|
||||
lower_bound = sub_compact->start;
|
||||
} else if (meta->smallest.size() > 0) {
|
||||
// For subsequent output tables, only include range tombstones from min
|
||||
// key onwards since the previous file was extended to contain range
|
||||
// tombstones falling before min key.
|
||||
lower_bound_guard = meta->smallest.user_key();
|
||||
lower_bound = &lower_bound_guard;
|
||||
} else {
|
||||
lower_bound = nullptr;
|
||||
}
|
||||
if (next_table_min_key != nullptr) {
|
||||
// This isn't the last file in the subcompaction, so extend until the next
|
||||
// file starts.
|
||||
upper_bound_guard = ExtractUserKey(*next_table_min_key);
|
||||
upper_bound = &upper_bound_guard;
|
||||
} else {
|
||||
// This is the last file in the subcompaction, so extend until the
|
||||
// subcompaction ends.
|
||||
upper_bound = sub_compact->end;
|
||||
}
|
||||
range_del_agg->AddToBuilder(sub_compact->builder.get(), lower_bound,
|
||||
upper_bound, meta, range_del_out_stats,
|
||||
bottommost_level_);
|
||||
}
|
||||
const uint64_t current_entries = sub_compact->builder->NumEntries();
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
if (s.ok()) {
|
||||
@@ -945,7 +1047,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
sub_compact->total_bytes += current_bytes;
|
||||
|
||||
// Finish and check for file errors
|
||||
if (s.ok() && !db_options_.disableDataSync) {
|
||||
if (s.ok() && !db_options_.disable_data_sync) {
|
||||
StopWatch sw(env_, stats_, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
s = sub_compact->outfile->Sync(db_options_.use_fsync);
|
||||
}
|
||||
@@ -960,8 +1062,9 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// Verify that the table is usable
|
||||
InternalIterator* iter = cfd->table_cache()->NewIterator(
|
||||
ReadOptions(), env_options_, cfd->internal_comparator(), meta->fd,
|
||||
nullptr, cfd->internal_stats()->GetFileReadHist(
|
||||
compact_->compaction->output_level()),
|
||||
nullptr /* range_del_agg */, nullptr,
|
||||
cfd->internal_stats()->GetFileReadHist(
|
||||
compact_->compaction->output_level()),
|
||||
false);
|
||||
s = iter->status();
|
||||
|
||||
@@ -991,6 +1094,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(), fname,
|
||||
job_id_, meta->fd, tp, TableFileCreationReason::kCompaction, s);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Report new file to SstFileManagerImpl
|
||||
auto sfm =
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
@@ -1008,8 +1112,10 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sub_compact->builder.reset();
|
||||
sub_compact->current_output_file_size = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1117,7 +1223,9 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
*cfd->ioptions(), cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
sub_compact->outfile.get(), sub_compact->compaction->output_compression(),
|
||||
cfd->ioptions()->compression_opts, &sub_compact->compression_dict,
|
||||
cfd->ioptions()->compression_opts,
|
||||
sub_compact->compaction->output_level(),
|
||||
&sub_compact->compression_dict,
|
||||
skip_filters));
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
@@ -1165,8 +1273,7 @@ void CompactionJob::UpdateCompactionStats() {
|
||||
for (int input_level = 0;
|
||||
input_level < static_cast<int>(compaction->num_input_levels());
|
||||
++input_level) {
|
||||
if (compaction->start_level() + input_level
|
||||
!= compaction->output_level()) {
|
||||
if (compaction->level(input_level) != compaction->output_level()) {
|
||||
UpdateCompactionInputStatsHelper(
|
||||
&compaction_stats_.num_input_files_in_non_output_levels,
|
||||
&compaction_stats_.bytes_read_non_output_levels,
|
||||
|
||||
+13
-7
@@ -25,6 +25,7 @@
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
@@ -37,6 +38,7 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
@@ -52,9 +54,10 @@ class Arena;
|
||||
|
||||
class CompactionJob {
|
||||
public:
|
||||
CompactionJob(int job_id, Compaction* compaction, const DBOptions& db_options,
|
||||
CompactionJob(int job_id, Compaction* compaction,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory,
|
||||
Statistics* stats, InstrumentedMutex* db_mutex,
|
||||
Status* db_bg_error,
|
||||
@@ -93,15 +96,18 @@ class CompactionJob {
|
||||
// kv-pairs
|
||||
void ProcessKeyValueCompaction(SubcompactionState* sub_compact);
|
||||
|
||||
Status FinishCompactionOutputFile(const Status& input_status,
|
||||
SubcompactionState* sub_compact);
|
||||
Status FinishCompactionOutputFile(
|
||||
const Status& input_status, SubcompactionState* sub_compact,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
CompactionIterationStats* range_del_out_stats,
|
||||
const Slice* next_table_min_key = nullptr);
|
||||
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options);
|
||||
void RecordCompactionIOStats();
|
||||
Status OpenCompactionOutputFile(SubcompactionState* sub_compact);
|
||||
void CleanupCompaction();
|
||||
void UpdateCompactionJobStats(
|
||||
const InternalStats::CompactionStats& stats) const;
|
||||
void RecordDroppedKeys(const CompactionIteratorStats& c_iter_stats,
|
||||
void RecordDroppedKeys(const CompactionIterationStats& c_iter_stats,
|
||||
CompactionJobStats* compaction_job_stats = nullptr);
|
||||
|
||||
void UpdateCompactionStats();
|
||||
@@ -120,12 +126,12 @@ class CompactionJob {
|
||||
|
||||
// DBImpl state
|
||||
const std::string& dbname_;
|
||||
const DBOptions& db_options_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const EnvOptions& env_options_;
|
||||
|
||||
Env* env_;
|
||||
VersionSet* versions_;
|
||||
std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
Directory* output_directory_;
|
||||
|
||||
@@ -957,7 +957,7 @@ TEST_P(CompactionJobStatsTest, UniversalCompactionTest) {
|
||||
uint64_t key_base = 100000000l;
|
||||
// Note: key_base must be multiple of num_keys_per_L0_file
|
||||
int num_keys_per_table = 100;
|
||||
const uint32_t kTestScale = 8;
|
||||
const uint32_t kTestScale = 6;
|
||||
const int kKeySize = 10;
|
||||
const int kValueSize = 900;
|
||||
double compression_ratio = 1.0;
|
||||
@@ -1008,8 +1008,9 @@ TEST_P(CompactionJobStatsTest, UniversalCompactionTest) {
|
||||
num_input_units,
|
||||
num_keys_per_table * num_input_units,
|
||||
1.0, 0, false));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 4U);
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 3U);
|
||||
|
||||
for (uint64_t start_key = key_base;
|
||||
start_key <= key_base * kTestScale;
|
||||
|
||||
+12
-11
@@ -10,13 +10,13 @@
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "db/compaction_job.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction_job.h"
|
||||
#include "db/version_set.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/mock_table.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -68,11 +68,12 @@ class CompactionJobTest : public testing::Test {
|
||||
CompactionJobTest()
|
||||
: env_(Env::Default()),
|
||||
dbname_(test::TmpDir() + "/compaction_job_test"),
|
||||
mutable_cf_options_(Options(), ImmutableCFOptions(Options())),
|
||||
db_options_(),
|
||||
mutable_cf_options_(cf_options_),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_(db_options_.db_write_buffer_size),
|
||||
write_buffer_manager_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_,
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_)),
|
||||
shutting_down_(false),
|
||||
mock_table_factory_(new mock::MockTableFactory()) {
|
||||
@@ -239,10 +240,10 @@ class CompactionJobTest : public testing::Test {
|
||||
num_input_files += level_files.size();
|
||||
}
|
||||
|
||||
Compaction compaction(cfd->current()->storage_info(),
|
||||
Compaction compaction(cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
compaction_input_files, 1, 1024 * 1024, 10, 0,
|
||||
kNoCompression, {}, true);
|
||||
compaction_input_files, 1, 1024 * 1024,
|
||||
10 * 1024 * 1024, 0, kNoCompression, {}, true);
|
||||
compaction.SetInputVersion(cfd->current());
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
@@ -280,12 +281,12 @@ class CompactionJobTest : public testing::Test {
|
||||
Env* env_;
|
||||
std::string dbname_;
|
||||
EnvOptions env_options_;
|
||||
ImmutableDBOptions db_options_;
|
||||
ColumnFamilyOptions cf_options_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
WriteController write_controller_;
|
||||
DBOptions db_options_;
|
||||
ColumnFamilyOptions cf_options_;
|
||||
WriteBuffer write_buffer_;
|
||||
WriteBufferManager write_buffer_manager_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
|
||||
+202
-66
@@ -151,17 +151,15 @@ CompactionPicker::~CompactionPicker() {}
|
||||
|
||||
// Delete this compaction from the list of running compactions.
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
|
||||
if (c->start_level() == 0 ||
|
||||
ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
level0_compactions_in_progress_.erase(c);
|
||||
}
|
||||
UnregisterCompaction(c);
|
||||
if (!status.ok()) {
|
||||
c->ResetNextCompactionIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionPicker::GetRange(const CompactionInputFiles& inputs,
|
||||
InternalKey* smallest, InternalKey* largest) {
|
||||
InternalKey* smallest,
|
||||
InternalKey* largest) const {
|
||||
const int level = inputs.level;
|
||||
assert(!inputs.empty());
|
||||
smallest->Clear();
|
||||
@@ -190,7 +188,8 @@ void CompactionPicker::GetRange(const CompactionInputFiles& inputs,
|
||||
|
||||
void CompactionPicker::GetRange(const CompactionInputFiles& inputs1,
|
||||
const CompactionInputFiles& inputs2,
|
||||
InternalKey* smallest, InternalKey* largest) {
|
||||
InternalKey* smallest,
|
||||
InternalKey* largest) const {
|
||||
assert(!inputs1.empty() || !inputs2.empty());
|
||||
if (inputs1.empty()) {
|
||||
GetRange(inputs2, smallest, largest);
|
||||
@@ -206,6 +205,33 @@ void CompactionPicker::GetRange(const CompactionInputFiles& inputs1,
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionPicker::GetRange(const std::vector<CompactionInputFiles>& inputs,
|
||||
InternalKey* smallest,
|
||||
InternalKey* largest) const {
|
||||
InternalKey current_smallest;
|
||||
InternalKey current_largest;
|
||||
bool initialized = false;
|
||||
for (const auto& in : inputs) {
|
||||
if (in.empty()) {
|
||||
continue;
|
||||
}
|
||||
GetRange(in, ¤t_smallest, ¤t_largest);
|
||||
if (!initialized) {
|
||||
*smallest = current_smallest;
|
||||
*largest = current_largest;
|
||||
initialized = true;
|
||||
} else {
|
||||
if (icmp_->Compare(current_smallest, *smallest) < 0) {
|
||||
*smallest = current_smallest;
|
||||
}
|
||||
if (icmp_->Compare(current_largest, *largest) > 0) {
|
||||
*largest = current_largest;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(initialized);
|
||||
}
|
||||
|
||||
bool CompactionPicker::ExpandWhileOverlapping(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs) {
|
||||
@@ -250,6 +276,42 @@ bool CompactionPicker::ExpandWhileOverlapping(const std::string& cf_name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompactionPicker::RangeOverlapWithCompaction(
|
||||
const Slice& smallest_user_key, const Slice& largest_user_key,
|
||||
int level) const {
|
||||
const Comparator* ucmp = icmp_->user_comparator();
|
||||
for (Compaction* c : compactions_in_progress_) {
|
||||
if (c->output_level() == level &&
|
||||
ucmp->Compare(smallest_user_key, c->GetLargestUserKey()) <= 0 &&
|
||||
ucmp->Compare(largest_user_key, c->GetSmallestUserKey()) >= 0) {
|
||||
// Overlap
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Did not overlap with any running compaction in level `level`
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompactionPicker::FilesRangeOverlapWithCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs, int level) const {
|
||||
bool is_empty = true;
|
||||
for (auto& in : inputs) {
|
||||
if (!in.empty()) {
|
||||
is_empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_empty) {
|
||||
// No files in inputs
|
||||
return false;
|
||||
}
|
||||
|
||||
InternalKey smallest, largest;
|
||||
GetRange(inputs, &smallest, &largest);
|
||||
return RangeOverlapWithCompaction(smallest.user_key(), largest.user_key(),
|
||||
level);
|
||||
}
|
||||
|
||||
// Returns true if any one of specified files are being compacted
|
||||
bool CompactionPicker::FilesInCompaction(
|
||||
const std::vector<FileMetaData*>& files) {
|
||||
@@ -266,10 +328,6 @@ Compaction* CompactionPicker::FormCompaction(
|
||||
const std::vector<CompactionInputFiles>& input_files, int output_level,
|
||||
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
|
||||
uint32_t output_path_id) {
|
||||
uint64_t max_grandparent_overlap_bytes =
|
||||
output_level + 1 < vstorage->num_levels()
|
||||
? mutable_cf_options.MaxGrandParentOverlapBytes(output_level + 1)
|
||||
: std::numeric_limits<uint64_t>::max();
|
||||
assert(input_files.size());
|
||||
|
||||
// TODO(rven ): we might be able to run concurrent level 0 compaction
|
||||
@@ -278,16 +336,19 @@ Compaction* CompactionPicker::FormCompaction(
|
||||
if ((input_files[0].level == 0) && !level0_compactions_in_progress_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto c = new Compaction(
|
||||
vstorage, mutable_cf_options, input_files, output_level,
|
||||
compact_options.output_file_size_limit, max_grandparent_overlap_bytes,
|
||||
output_path_id, compact_options.compression, /* grandparents */ {}, true);
|
||||
// This compaction output could overlap with a running compaction
|
||||
if (FilesRangeOverlapWithCompaction(input_files, output_level)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto c =
|
||||
new Compaction(vstorage, ioptions_, mutable_cf_options, input_files,
|
||||
output_level, compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id,
|
||||
compact_options.compression, /* grandparents */ {}, true);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
if ((c != nullptr) && (input_files[0].level == 0)) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -390,10 +451,14 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
vstorage->GetOverlappingInputs(output_level, &smallest, &largest,
|
||||
&output_level_inputs->files, *parent_index,
|
||||
parent_index);
|
||||
|
||||
if (FilesInCompaction(output_level_inputs->files)) {
|
||||
return false;
|
||||
}
|
||||
if (!output_level_inputs->empty()) {
|
||||
if (!ExpandWhileOverlapping(cf_name, vstorage, output_level_inputs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -413,19 +478,22 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
const uint64_t inputs1_size =
|
||||
TotalCompensatedFileSize(output_level_inputs->files);
|
||||
const uint64_t expanded0_size = TotalCompensatedFileSize(expanded0.files);
|
||||
uint64_t limit =
|
||||
mutable_cf_options.ExpandedCompactionByteSizeLimit(input_level);
|
||||
uint64_t limit = mutable_cf_options.max_compaction_bytes;
|
||||
if (expanded0.size() > inputs->size() &&
|
||||
inputs1_size + expanded0_size < limit &&
|
||||
!FilesInCompaction(expanded0.files) &&
|
||||
!vstorage->HasOverlappingUserKey(&expanded0.files, input_level)) {
|
||||
InternalKey new_start, new_limit;
|
||||
GetRange(expanded0, &new_start, &new_limit);
|
||||
std::vector<FileMetaData*> expanded1;
|
||||
CompactionInputFiles expanded1;
|
||||
expanded1.level = output_level;
|
||||
vstorage->GetOverlappingInputs(output_level, &new_start, &new_limit,
|
||||
&expanded1, *parent_index, parent_index);
|
||||
if (expanded1.size() == output_level_inputs->size() &&
|
||||
!FilesInCompaction(expanded1)) {
|
||||
&expanded1.files, *parent_index,
|
||||
parent_index);
|
||||
assert(!expanded1.empty());
|
||||
if (!FilesInCompaction(expanded1.files) &&
|
||||
ExpandWhileOverlapping(cf_name, vstorage, &expanded1) &&
|
||||
expanded1.size() == output_level_inputs->size()) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"[%s] Expanding@%d %" ROCKSDB_PRIszt "+%" ROCKSDB_PRIszt "(%" PRIu64
|
||||
"+%" PRIu64 " bytes) to %" ROCKSDB_PRIszt "+%" ROCKSDB_PRIszt
|
||||
@@ -436,7 +504,7 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
smallest = new_start;
|
||||
largest = new_limit;
|
||||
inputs->files = expanded0.files;
|
||||
output_level_inputs->files = expanded1;
|
||||
output_level_inputs->files = expanded1.files;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -507,16 +575,24 @@ Compaction* CompactionPicker::CompactRange(
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 2 non-exclusive manual compactions could run at the same time producing
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(inputs, output_level)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
*manual_conflict = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, output_path_id,
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
/* max_compaction_bytes */ LLONG_MAX, output_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
/* grandparents */ {}, /* is manual */ true);
|
||||
if (start_level == 0) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -548,8 +624,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// and we must not pick one file and drop another older file if the
|
||||
// two files overlap.
|
||||
if (input_level > 0) {
|
||||
const uint64_t limit = mutable_cf_options.MaxFileSizeForLevel(input_level) *
|
||||
mutable_cf_options.source_compaction_factor;
|
||||
const uint64_t limit = mutable_cf_options.max_compaction_bytes;
|
||||
uint64_t total = 0;
|
||||
for (size_t i = 0; i + 1 < inputs.size(); ++i) {
|
||||
uint64_t s = inputs[i]->compensated_file_size;
|
||||
@@ -606,27 +681,33 @@ Compaction* CompactionPicker::CompactRange(
|
||||
}
|
||||
}
|
||||
|
||||
// 2 non-exclusive manual compactions could run at the same time producing
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(compaction_inputs, output_level)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
*manual_conflict = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<FileMetaData*> grandparents;
|
||||
GetGrandparents(vstorage, inputs, output_level_inputs, &grandparents);
|
||||
Compaction* compaction = new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(compaction_inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(input_level),
|
||||
output_path_id,
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(compaction_inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
std::move(grandparents), /* is manual compaction */ true);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::CompactRange:Return", compaction);
|
||||
if (input_level == 0) {
|
||||
level0_compactions_in_progress_.insert(compaction);
|
||||
}
|
||||
RegisterCompaction(compaction);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here
|
||||
vstorage->ComputeCompactionScore(mutable_cf_options);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
|
||||
return compaction;
|
||||
}
|
||||
@@ -856,6 +937,30 @@ Status CompactionPicker::SanitizeCompactionInputFiles(
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
void CompactionPicker::RegisterCompaction(Compaction* c) {
|
||||
if (c == nullptr) {
|
||||
return;
|
||||
}
|
||||
assert(ioptions_.compaction_style != kCompactionStyleLevel ||
|
||||
!FilesRangeOverlapWithCompaction(*c->inputs(), c->output_level()));
|
||||
if (c->start_level() == 0 ||
|
||||
ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
compactions_in_progress_.insert(c);
|
||||
}
|
||||
|
||||
void CompactionPicker::UnregisterCompaction(Compaction* c) {
|
||||
if (c == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (c->start_level() == 0 ||
|
||||
ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
level0_compactions_in_progress_.erase(c);
|
||||
}
|
||||
compactions_in_progress_.erase(c);
|
||||
}
|
||||
|
||||
bool LevelCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
if (!vstorage->FilesMarkedForCompaction().empty()) {
|
||||
@@ -939,7 +1044,8 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
output_level = (level == 0) ? vstorage->base_level() : level + 1;
|
||||
if (PickCompactionBySize(vstorage, level, output_level, &inputs,
|
||||
&parent_index, &base_index) &&
|
||||
ExpandWhileOverlapping(cf_name, vstorage, &inputs)) {
|
||||
ExpandWhileOverlapping(cf_name, vstorage, &inputs) &&
|
||||
!FilesRangeOverlapWithCompaction({inputs}, output_level)) {
|
||||
// found the compaction!
|
||||
if (level == 0) {
|
||||
// L0 score = `num L0 files` / `level0_file_num_compaction_trigger`
|
||||
@@ -1012,12 +1118,24 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
compaction_inputs.push_back(output_level_inputs);
|
||||
}
|
||||
|
||||
// In some edge cases we could pick a compaction that will be compacting
|
||||
// a key range that overlap with another running compaction, and both
|
||||
// of them have the same output leve. This could happen if
|
||||
// (1) we are running a non-exclusive manual compaction
|
||||
// (2) AddFile ingest a new file into the LSM tree
|
||||
// We need to disallow this from happening.
|
||||
if (FilesRangeOverlapWithCompaction(compaction_inputs, output_level)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<FileMetaData*> grandparents;
|
||||
GetGrandparents(vstorage, inputs, output_level_inputs, &grandparents);
|
||||
auto c = new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(compaction_inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(level),
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(compaction_inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options, output_level),
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
@@ -1026,15 +1144,13 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
|
||||
// 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);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here
|
||||
vstorage->ComputeCompactionScore(mutable_cf_options);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("LevelCompactionPicker::PickCompaction:Return", c);
|
||||
|
||||
@@ -1068,7 +1184,8 @@ uint32_t LevelCompactionPicker::GetPathId(
|
||||
return p;
|
||||
} else {
|
||||
current_path_size -= level_size;
|
||||
level_size *= mutable_cf_options.max_bytes_for_level_multiplier;
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier);
|
||||
cur_level++;
|
||||
continue;
|
||||
}
|
||||
@@ -1310,6 +1427,8 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
sorted_runs.size() <
|
||||
(unsigned int)mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
LogToBuffer(log_buffer, "[%s] Universal: nothing to do\n", cf_name.c_str());
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
@@ -1344,19 +1463,34 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
assert(sorted_runs.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger));
|
||||
unsigned int num_files =
|
||||
static_cast<unsigned int>(sorted_runs.size()) -
|
||||
mutable_cf_options.level0_file_num_compaction_trigger;
|
||||
if ((c = PickCompactionUniversalReadAmp(
|
||||
cf_name, mutable_cf_options, vstorage, score, UINT_MAX,
|
||||
num_files, sorted_runs, log_buffer)) != nullptr) {
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs.size(); i++) {
|
||||
if (sorted_runs[i].being_compacted == false) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater than
|
||||
// the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted >
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
unsigned int num_files =
|
||||
num_sr_not_compacted -
|
||||
mutable_cf_options.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionUniversalReadAmp(
|
||||
cf_name, mutable_cf_options, vstorage, score, UINT_MAX,
|
||||
num_files, sorted_runs, log_buffer)) != nullptr) {
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c == nullptr) {
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1407,8 +1541,10 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
MeasureTime(ioptions_.statistics, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
c->inputs(0)->size());
|
||||
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
RegisterCompaction(c);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -1627,7 +1763,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
compaction_reason = CompactionReason::kUniversalSizeRatio;
|
||||
}
|
||||
return new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(inputs), output_level,
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level), LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, start_level,
|
||||
1, enable_compression),
|
||||
@@ -1708,14 +1844,14 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
LogToBuffer(
|
||||
log_buffer,
|
||||
"[%s] Universal: size amp not needed. newer-files-total-size %" PRIu64
|
||||
"earliest-file-size %" PRIu64,
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name.c_str(), candidate_size, earliest_file_size);
|
||||
return nullptr;
|
||||
} else {
|
||||
LogToBuffer(
|
||||
log_buffer,
|
||||
"[%s] Universal: size amp needed. newer-files-total-size %" PRIu64
|
||||
"earliest-file-size %" PRIu64,
|
||||
" earliest-file-size %" PRIu64,
|
||||
cf_name.c_str(), candidate_size, earliest_file_size);
|
||||
}
|
||||
assert(start_index < sorted_runs.size() - 1);
|
||||
@@ -1751,7 +1887,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
}
|
||||
|
||||
return new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(inputs),
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs),
|
||||
vstorage->num_levels() - 1,
|
||||
mutable_cf_options.MaxFileSizeForLevel(vstorage->num_levels() - 1),
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, path_id,
|
||||
@@ -1816,10 +1952,10 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
}
|
||||
}
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, {}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
+33
-7
@@ -20,8 +20,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -114,21 +113,33 @@ class CompactionPicker {
|
||||
return !level0_compactions_in_progress_.empty();
|
||||
}
|
||||
|
||||
protected:
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
// Return true if the passed key range overlap with a compaction output
|
||||
// that is currently running.
|
||||
bool RangeOverlapWithCompaction(const Slice& smallest_user_key,
|
||||
const Slice& largest_user_key,
|
||||
int level) const;
|
||||
|
||||
// Stores the minimal range that covers all entries in inputs in
|
||||
// *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty
|
||||
void GetRange(const CompactionInputFiles& inputs, InternalKey* smallest,
|
||||
InternalKey* largest);
|
||||
InternalKey* largest) const;
|
||||
|
||||
// Stores the minimal range that covers all entries in inputs1 and inputs2
|
||||
// in *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty
|
||||
void GetRange(const CompactionInputFiles& inputs1,
|
||||
const CompactionInputFiles& inputs2, InternalKey* smallest,
|
||||
InternalKey* largest);
|
||||
InternalKey* largest) const;
|
||||
|
||||
// Stores the minimal range that covers all entries in inputs
|
||||
// in *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty (at least on entry have one file)
|
||||
void GetRange(const std::vector<CompactionInputFiles>& inputs,
|
||||
InternalKey* smallest, InternalKey* largest) const;
|
||||
|
||||
protected:
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
|
||||
// Add more files to the inputs on "level" to make sure that
|
||||
// no newer version of a key is compacted to "level+1" while leaving an older
|
||||
@@ -149,6 +160,11 @@ class CompactionPicker {
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level, int* index);
|
||||
|
||||
// Returns true if the key range that `inputs` files cover overlap with the
|
||||
// key range of a currently running compaction.
|
||||
bool FilesRangeOverlapWithCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs, int level) const;
|
||||
|
||||
bool SetupOtherInputs(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
@@ -171,10 +187,20 @@ class CompactionPicker {
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level) const;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Register this compaction in the set of running compactions
|
||||
void RegisterCompaction(Compaction* c);
|
||||
|
||||
// Remove this compaction from the set of running compactions
|
||||
void UnregisterCompaction(Compaction* c);
|
||||
|
||||
// Keeps track of all compactions that are running on Level0.
|
||||
// It is protected by DB mutex
|
||||
// Protected by DB mutex
|
||||
std::set<Compaction*> level0_compactions_in_progress_;
|
||||
|
||||
// Keeps track of all compactions that are running.
|
||||
// Protected by DB mutex
|
||||
std::unordered_set<Compaction*> compactions_in_progress_;
|
||||
|
||||
const InternalKeyComparator* const icmp_;
|
||||
};
|
||||
|
||||
|
||||
+201
-14
@@ -48,7 +48,7 @@ class CompactionPickerTest : public testing::Test {
|
||||
: ucmp_(BytewiseComparator()),
|
||||
icmp_(ucmp_),
|
||||
ioptions_(options_),
|
||||
mutable_cf_options_(options_, ioptions_),
|
||||
mutable_cf_options_(options_),
|
||||
level_compaction_picker(ioptions_, &icmp_),
|
||||
cf_name_("dummy"),
|
||||
log_buffer_(InfoLogLevel::INFO_LEVEL, &logger_),
|
||||
@@ -66,8 +66,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
void NewVersionStorage(int num_levels, CompactionStyle style) {
|
||||
DeleteVersionStorage();
|
||||
options_.num_levels = num_levels;
|
||||
vstorage_.reset(new VersionStorageInfo(
|
||||
&icmp_, ucmp_, options_.num_levels, style, nullptr));
|
||||
vstorage_.reset(new VersionStorageInfo(&icmp_, ucmp_, options_.num_levels,
|
||||
style, nullptr, false));
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
@@ -115,12 +115,13 @@ class CompactionPickerTest : public testing::Test {
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(ioptions_.compaction_pri);
|
||||
vstorage_->UpdateNumNonEmptyLevels();
|
||||
vstorage_->GenerateFileIndexer();
|
||||
vstorage_->GenerateLevelFilesBrief();
|
||||
vstorage_->ComputeCompactionScore(mutable_cf_options_);
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
vstorage_->GenerateLevel0NonOverlapping();
|
||||
vstorage_->ComputeFilesMarkedForCompaction();
|
||||
vstorage_->SetFinalized();
|
||||
}
|
||||
};
|
||||
@@ -350,10 +351,10 @@ TEST_F(CompactionPickerTest, Level0TriggerDynamic4) {
|
||||
TEST_F(CompactionPickerTest, LevelTriggerDynamic4) {
|
||||
int num_levels = ioptions_.num_levels;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
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;
|
||||
mutable_cf_options_.compaction_pri = kMinOverlappingRatio;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
@@ -489,10 +490,10 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.compaction_pri = kMinOverlappingRatio;
|
||||
|
||||
Add(2, 6U, "150", "179", 50000000U);
|
||||
Add(2, 7U, "180", "220", 50000000U);
|
||||
@@ -516,10 +517,10 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.compaction_pri = kMinOverlappingRatio;
|
||||
|
||||
Add(2, 6U, "150", "175",
|
||||
60000000U); // Overlaps with file 26, 27, total size 521M
|
||||
@@ -546,9 +547,9 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping3) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000000;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
mutable_cf_options_.compaction_pri = kMinOverlappingRatio;
|
||||
|
||||
// file 7 and 8 over lap with the same file, but file 8 is smaller so
|
||||
// it will be picked.
|
||||
@@ -598,7 +599,7 @@ TEST_F(CompactionPickerTest, ParentIndexResetBug) {
|
||||
// ranges (with different sequence numbers) in the input files.
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.compaction_pri = kByCompensatedSize;
|
||||
ioptions_.compaction_pri = kByCompensatedSize;
|
||||
|
||||
Add(1, 1U, "100", "150", 1U);
|
||||
// Overlapping user keys
|
||||
@@ -621,9 +622,9 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys2) {
|
||||
// Overlapping user keys on same level and output level
|
||||
Add(1, 1U, "200", "400", 1000000000U);
|
||||
Add(1, 2U, "400", "500", 1U, 0, 0);
|
||||
Add(2, 3U, "400", "600", 1U);
|
||||
// The following file is not in the compaction despite overlapping user keys
|
||||
Add(2, 4U, "600", "700", 1U, 0, 0);
|
||||
Add(2, 3U, "000", "100", 1U);
|
||||
Add(2, 4U, "100", "600", 1U, 0, 0);
|
||||
Add(2, 5U, "600", "700", 1U, 0, 0);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -631,10 +632,12 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys2) {
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(4U, compaction->input(1, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 2)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys3) {
|
||||
@@ -666,6 +669,95 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys3) {
|
||||
ASSERT_EQ(7U, compaction->input(1, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys4) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000;
|
||||
|
||||
Add(1, 1U, "100", "150", 1U);
|
||||
Add(1, 2U, "150", "199", 1U, 0, 0);
|
||||
Add(1, 3U, "200", "250", 1100000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 1U, 0, 0);
|
||||
Add(1, 5U, "300", "350", 1U, 0, 0);
|
||||
|
||||
Add(2, 6U, "100", "115", 1U);
|
||||
Add(2, 7U, "125", "325", 1U);
|
||||
Add(2, 8U, "350", "400", 1U);
|
||||
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_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(7U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys5) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// Overlapping user keys on same level and output level
|
||||
Add(1, 1U, "200", "400", 1000000000U);
|
||||
Add(1, 2U, "400", "500", 1U, 0, 0);
|
||||
Add(2, 3U, "000", "100", 1U);
|
||||
Add(2, 4U, "100", "600", 1U, 0, 0);
|
||||
Add(2, 5U, "600", "700", 1U, 0, 0);
|
||||
|
||||
vstorage_->LevelFiles(2)[2]->being_compacted = true;
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys6) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// Overlapping user keys on same level and output level
|
||||
Add(1, 1U, "200", "400", 1U, 0, 0);
|
||||
Add(1, 2U, "401", "500", 1U, 0, 0);
|
||||
Add(2, 3U, "000", "100", 1U);
|
||||
Add(2, 4U, "100", "300", 1U, 0, 0);
|
||||
Add(2, 5U, "305", "450", 1U, 0, 0);
|
||||
Add(2, 6U, "460", "600", 1U, 0, 0);
|
||||
Add(2, 7U, "600", "700", 1U, 0, 0);
|
||||
|
||||
vstorage_->LevelFiles(1)[0]->marked_for_compaction = true;
|
||||
vstorage_->LevelFiles(1)[1]->marked_for_compaction = true;
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(3U, compaction->num_input_files(1));
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys7) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.max_compaction_bytes = 100000000000u;
|
||||
// Overlapping user keys on same level and output level
|
||||
Add(1, 1U, "200", "400", 1U, 0, 0);
|
||||
Add(1, 2U, "401", "500", 1000000000U, 0, 0);
|
||||
Add(2, 3U, "100", "250", 1U);
|
||||
Add(2, 4U, "300", "600", 1U, 0, 0);
|
||||
Add(2, 5U, "600", "800", 1U, 0, 0);
|
||||
|
||||
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_levels());
|
||||
ASSERT_GE(1U, compaction->num_input_files(0));
|
||||
ASSERT_GE(2U, compaction->num_input_files(1));
|
||||
// File 5 has to be included in the compaction
|
||||
ASSERT_EQ(5U, compaction->inputs(1)->back()->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NotScheduleL1IfL0WithHigherPri1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
@@ -1034,6 +1126,101 @@ TEST_F(CompactionPickerTest, IsBottommostLevelTest) {
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesHit) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 800000u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// It cannot expand because adding file 1 and 3, the compaction size will
|
||||
// exceed mutable_cf_options_.max_bytes_for_level_base.
|
||||
Add(1, 1U, "100", "150", 300000U);
|
||||
Add(1, 2U, "151", "200", 300001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 300000U, 0, 0);
|
||||
Add(2, 5U, "160", "256", 1U);
|
||||
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_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesNotHit) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 1000000u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// and it expands to file 1 and 3 too.
|
||||
Add(1, 1U, "100", "150", 300000U);
|
||||
Add(1, 2U, "151", "200", 300001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 300000U, 0, 0);
|
||||
Add(2, 5U, "000", "233", 1U);
|
||||
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_levels());
|
||||
ASSERT_EQ(3U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(0, 2)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IsTrivialMoveOn) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 10001u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2
|
||||
Add(1, 1U, "100", "150", 3000U);
|
||||
Add(1, 2U, "151", "200", 3001U);
|
||||
Add(1, 3U, "201", "250", 3000U);
|
||||
Add(1, 4U, "251", "300", 3000U);
|
||||
|
||||
Add(3, 5U, "120", "130", 7000U);
|
||||
Add(3, 6U, "170", "180", 7000U);
|
||||
Add(3, 5U, "220", "230", 7000U);
|
||||
Add(3, 5U, "270", "280", 7000U);
|
||||
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_TRUE(compaction->IsTrivialMove());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, IsTrivialMoveOff) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 10000u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick all files from level 1
|
||||
Add(1, 1U, "100", "150", 300000U, 0, 0);
|
||||
Add(1, 2U, "150", "200", 300000U, 0, 0);
|
||||
Add(1, 3U, "200", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "250", "300", 300000U, 0, 0);
|
||||
|
||||
Add(3, 5U, "120", "130", 6000U);
|
||||
Add(3, 6U, "140", "150", 6000U);
|
||||
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_FALSE(compaction->IsTrivialMove());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -40,6 +40,10 @@ class KVIter : public Iterator {
|
||||
virtual void Seek(const Slice& k) override {
|
||||
iter_ = map_->lower_bound(k.ToString());
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
iter_ = map_->upper_bound(k.ToString());
|
||||
Prev();
|
||||
}
|
||||
virtual void Next() override { ++iter_; }
|
||||
virtual void Prev() override {
|
||||
if (iter_ == map_->begin()) {
|
||||
|
||||
@@ -41,7 +41,10 @@ class CorruptionTest : public testing::Test {
|
||||
DB* db_;
|
||||
|
||||
CorruptionTest() {
|
||||
tiny_cache_ = NewLRUCache(100);
|
||||
// If LRU cache shard bit is smaller than 2 (or -1 which will automatically
|
||||
// set it to 0), test SequenceNumberRecovery will fail, likely because of a
|
||||
// bug in recovery code. Keep it 4 for now to make the test passes.
|
||||
tiny_cache_ = NewLRUCache(100, 4);
|
||||
options_.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
options_.env = &env_;
|
||||
dbname_ = test::TmpDir() + "/corruption_test";
|
||||
|
||||
@@ -41,6 +41,7 @@ class CuckooTableDBTest : public testing::Test {
|
||||
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true));
|
||||
options.allow_mmap_reads = true;
|
||||
options.create_if_missing = true;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
return options;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstdlib>
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/lru_cache.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -39,6 +40,7 @@ class DBBlockCacheTest : public DBTestBase {
|
||||
Options GetOptions(const BlockBasedTableOptions& table_options) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
// options.compression = kNoCompression;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
@@ -170,7 +172,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
InitTable(options);
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(0, 0, false);
|
||||
std::shared_ptr<Cache> compressed_cache = NewLRUCache(0, 0, false);
|
||||
std::shared_ptr<Cache> compressed_cache = NewLRUCache(1 << 25, 0, false);
|
||||
table_options.block_cache = cache;
|
||||
table_options.block_cache_compressed = compressed_cache;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
@@ -202,9 +204,6 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
cache->SetCapacity(usage);
|
||||
cache->SetStrictCapacityLimit(true);
|
||||
ASSERT_EQ(usage, cache->GetPinnedUsage());
|
||||
// compressed_cache->SetCapacity(compressed_usage);
|
||||
compressed_cache->SetCapacity(0);
|
||||
// compressed_cache->SetStrictCapacityLimit(true);
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(kNumBlocks - 1));
|
||||
ASSERT_TRUE(iter->status().IsIncomplete());
|
||||
@@ -269,16 +268,16 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksOfNewTableAddedToCache) {
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
|
||||
// Make sure index block is in cache.
|
||||
auto index_block_hit = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
auto index_block_hit = TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT);
|
||||
value = Get(1, "key");
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(index_block_hit + 1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
|
||||
value = Get(1, "key");
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(index_block_hit + 2,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
@@ -322,6 +321,91 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
filter_bytes_insert);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A mock cache wraps LRUCache, and record how many entries have been
|
||||
// inserted for each priority.
|
||||
class MockCache : public LRUCache {
|
||||
public:
|
||||
static uint32_t high_pri_insert_count;
|
||||
static uint32_t low_pri_insert_count;
|
||||
|
||||
MockCache() : LRUCache(1 << 25, 0, false, 0.0) {}
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle, Priority priority) override {
|
||||
if (priority == Priority::LOW) {
|
||||
low_pri_insert_count++;
|
||||
} else {
|
||||
high_pri_insert_count++;
|
||||
}
|
||||
return LRUCache::Insert(key, value, charge, deleter, handle, priority);
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t MockCache::high_pri_insert_count = 0;
|
||||
uint32_t MockCache::low_pri_insert_count = 0;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, IndexAndFilterBlocksCachePriority) {
|
||||
for (auto priority : {Cache::Priority::LOW, Cache::Priority::HIGH}) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache.reset(new MockCache());
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
table_options.cache_index_and_filter_blocks_with_high_priority =
|
||||
priority == Cache::Priority::HIGH ? true : false;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
MockCache::high_pri_insert_count = 0;
|
||||
MockCache::low_pri_insert_count = 0;
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// index/filter blocks added to block cache right after table creation.
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(2, /* only index/filter were added */
|
||||
TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_MISS));
|
||||
if (priority == Cache::Priority::LOW) {
|
||||
ASSERT_EQ(0, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(2, MockCache::low_pri_insert_count);
|
||||
} else {
|
||||
ASSERT_EQ(2, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(0, MockCache::low_pri_insert_count);
|
||||
}
|
||||
|
||||
// Access data block.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(3, /*adding data block*/
|
||||
TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
// Data block should be inserted with low priority.
|
||||
if (priority == Cache::Priority::LOW) {
|
||||
ASSERT_EQ(0, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(3, MockCache::low_pri_insert_count);
|
||||
} else {
|
||||
ASSERT_EQ(2, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(1, MockCache::low_pri_insert_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
+27
-49
@@ -20,15 +20,29 @@ class DBBloomFilterTest : public DBTestBase {
|
||||
DBBloomFilterTest() : DBTestBase("/db_bloom_filter_test") {}
|
||||
};
|
||||
|
||||
class DBBloomFilterTestWithParam : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
protected:
|
||||
bool use_block_based_filter_;
|
||||
|
||||
public:
|
||||
DBBloomFilterTestWithParam() : DBTestBase("/db_bloom_filter_tests") {}
|
||||
|
||||
~DBBloomFilterTestWithParam() {}
|
||||
|
||||
void SetUp() override { use_block_based_filter_ = GetParam(); }
|
||||
};
|
||||
|
||||
// KeyMayExist can lead to a few false positives, but not false negatives.
|
||||
// To make test deterministic, use a much larger number of bits per key-20 than
|
||||
// bits in the key, so that false positives are eliminated
|
||||
TEST_F(DBBloomFilterTest, KeyMayExist) {
|
||||
TEST_P(DBBloomFilterTestWithParam, KeyMayExist) {
|
||||
do {
|
||||
ReadOptions ropts;
|
||||
std::string value;
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options_override.filter_policy.reset(
|
||||
NewBloomFilterPolicy(20, use_block_based_filter_));
|
||||
Options options = CurrentOptions(options_override);
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -87,45 +101,6 @@ TEST_F(DBBloomFilterTest, KeyMayExist) {
|
||||
ChangeOptions(kSkipPlainTable | kSkipHashIndex | kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
// A delete is skipped for key if KeyMayExist(key) returns False
|
||||
// Tests Writebatch consistency and proper delete behaviour
|
||||
TEST_F(DBBloomFilterTest, FilterDeletes) {
|
||||
do {
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
Options options = CurrentOptions(options_override);
|
||||
options.filter_deletes = true;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
WriteBatch batch;
|
||||
|
||||
batch.Delete(handles_[1], "a");
|
||||
dbfull()->Write(WriteOptions(), &batch);
|
||||
ASSERT_EQ(AllEntriesFor("a", 1), "[ ]"); // Delete skipped
|
||||
batch.Clear();
|
||||
|
||||
batch.Put(handles_[1], "a", "b");
|
||||
batch.Delete(handles_[1], "a");
|
||||
dbfull()->Write(WriteOptions(), &batch);
|
||||
ASSERT_EQ(Get(1, "a"), "NOT_FOUND");
|
||||
ASSERT_EQ(AllEntriesFor("a", 1), "[ DEL, b ]"); // Delete issued
|
||||
batch.Clear();
|
||||
|
||||
batch.Delete(handles_[1], "c");
|
||||
batch.Put(handles_[1], "c", "d");
|
||||
dbfull()->Write(WriteOptions(), &batch);
|
||||
ASSERT_EQ(Get(1, "c"), "d");
|
||||
ASSERT_EQ(AllEntriesFor("c", 1), "[ d ]"); // Delete skipped
|
||||
batch.Clear();
|
||||
|
||||
ASSERT_OK(Flush(1)); // A stray Flush
|
||||
|
||||
batch.Delete(handles_[1], "c");
|
||||
dbfull()->Write(WriteOptions(), &batch);
|
||||
ASSERT_EQ(AllEntriesFor("c", 1), "[ DEL, d ]"); // Delete issued
|
||||
batch.Clear();
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, GetFilterByPrefixBloom) {
|
||||
Options options = last_options_;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
|
||||
@@ -315,7 +290,7 @@ TEST_F(DBBloomFilterTest, WholeKeyFilterProp) {
|
||||
ASSERT_EQ(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 12);
|
||||
}
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilter) {
|
||||
TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
env_->count_random_reads_ = true;
|
||||
@@ -324,7 +299,8 @@ TEST_F(DBBloomFilterTest, BloomFilter) {
|
||||
// trigger reset of table_factory
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.filter_policy.reset(
|
||||
NewBloomFilterPolicy(10, use_block_based_filter_));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -367,6 +343,9 @@ TEST_F(DBBloomFilterTest, BloomFilter) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBloomFilterTestWithParam, DBBloomFilterTestWithParam,
|
||||
::testing::Bool());
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
while (ChangeFilterOptions()) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -615,7 +594,8 @@ class BloomStatsTestWithParam
|
||||
|
||||
options_.create_if_missing = true;
|
||||
options_.prefix_extractor.reset(rocksdb::NewFixedPrefixTransform(4));
|
||||
options_.memtable_prefix_bloom_bits = 8 * 1024;
|
||||
options_.memtable_prefix_bloom_size_ratio =
|
||||
8.0 * 1024.0 / static_cast<double>(options_.write_buffer_size);
|
||||
if (use_block_table_) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.hash_index_allow_collision = false;
|
||||
@@ -684,13 +664,10 @@ TEST_P(BloomStatsTestWithParam, BloomStatsTest) {
|
||||
ASSERT_EQ(0, perf_context.bloom_sst_miss_count);
|
||||
|
||||
// check SST bloom stats
|
||||
// NOTE: hits per get differs because of code paths differences
|
||||
// in BlockBasedTable::Get()
|
||||
int hits_per_get = use_block_table_ && !use_block_based_builder_ ? 2 : 1;
|
||||
ASSERT_EQ(value1, Get(key1));
|
||||
ASSERT_EQ(hits_per_get, perf_context.bloom_sst_hit_count);
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_hit_count);
|
||||
ASSERT_EQ(value3, Get(key3));
|
||||
ASSERT_EQ(2 * hits_per_get, perf_context.bloom_sst_hit_count);
|
||||
ASSERT_EQ(2, perf_context.bloom_sst_hit_count);
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get(key2));
|
||||
ASSERT_EQ(1, perf_context.bloom_sst_miss_count);
|
||||
@@ -833,6 +810,7 @@ TEST_F(DBBloomFilterTest, PrefixScan) {
|
||||
options.max_background_compactions = 2;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
namespace rocksdb {
|
||||
|
||||
static int cfilter_count = 0;
|
||||
static int cfilter_skips = 0;
|
||||
|
||||
// This is a static filter used for filtering
|
||||
// kvs during the compaction process.
|
||||
@@ -65,6 +66,30 @@ class DeleteISFilter : public CompactionFilter {
|
||||
virtual const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
// Skip x if floor(x/10) is even, use range skips. Requires that keys are
|
||||
// zero-padded to length 10.
|
||||
class SkipEvenFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual Decision FilterV2(int level, const Slice& key, ValueType value_type,
|
||||
const Slice& existing_value, std::string* new_value,
|
||||
std::string* skip_until) const override {
|
||||
cfilter_count++;
|
||||
int i = std::stoi(key.ToString());
|
||||
if (i / 10 % 2 == 0) {
|
||||
char key_str[100];
|
||||
snprintf(key_str, sizeof(key), "%010d", i / 10 * 10 + 10);
|
||||
*skip_until = key_str;
|
||||
++cfilter_skips;
|
||||
return Decision::kRemoveAndSkipUntil;
|
||||
}
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return true; }
|
||||
|
||||
virtual const char* Name() const override { return "DeleteFilter"; }
|
||||
};
|
||||
|
||||
class DelayFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit DelayFilter(DBTestBase* d) : db_test(d) {}
|
||||
@@ -174,6 +199,20 @@ class DeleteISFilterFactory : public CompactionFilterFactory {
|
||||
virtual const char* Name() const override { return "DeleteFilterFactory"; }
|
||||
};
|
||||
|
||||
class SkipEvenFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<CompactionFilter> CreateCompactionFilter(
|
||||
const CompactionFilter::Context& context) override {
|
||||
if (context.is_manual_compaction) {
|
||||
return std::unique_ptr<CompactionFilter>(new SkipEvenFilter());
|
||||
} else {
|
||||
return std::unique_ptr<CompactionFilter>(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "SkipEvenFilterFactory"; }
|
||||
};
|
||||
|
||||
class DelayFilterFactory : public CompactionFilterFactory {
|
||||
public:
|
||||
explicit DelayFilterFactory(DBTestBase* d) : db_test(d) {}
|
||||
@@ -261,8 +300,10 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
int total = 0;
|
||||
Arena arena;
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
ScopedArenaIterator iter(
|
||||
dbfull()->NewInternalIterator(&arena, handles_[1]));
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
@@ -349,8 +390,10 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
// level Lmax because this record is at the tip
|
||||
count = 0;
|
||||
{
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
ScopedArenaIterator iter(
|
||||
dbfull()->NewInternalIterator(&arena, handles_[1]));
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[1]));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
@@ -566,7 +609,10 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
int count = 0;
|
||||
int total = 0;
|
||||
Arena arena;
|
||||
ScopedArenaIterator iter(dbfull()->NewInternalIterator(&arena));
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
ScopedArenaIterator iter(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
@@ -712,6 +758,47 @@ TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTestCompactionFilter, SkipUntil) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_filter_factory = std::make_shared<SkipEvenFilterFactory>();
|
||||
options.disable_auto_compactions = true;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Write 100K keys, these are written to a few files in L0.
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
// Key ranges in tables are [0, 38], [106, 149], [212, 260], [318, 371].
|
||||
for (int i = table * 6; i < 39 + table * 11; ++i) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "%010d", table * 100 + i);
|
||||
Put(key, std::to_string(table * 1000 + i));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
cfilter_skips = 0;
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
// Numberof skips in tables: 2, 3, 3, 3.
|
||||
ASSERT_EQ(11, cfilter_skips);
|
||||
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = table * 6; i < 39 + table * 11; ++i) {
|
||||
int k = table * 100 + i;
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "%010d", table * 100 + i);
|
||||
auto expected = std::to_string(table * 1000 + i);
|
||||
std::string val;
|
||||
Status s = db_->Get(ReadOptions(), key, &val);
|
||||
if (k / 10 % 2 == 0) {
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(expected, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+92
-20
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/experimental.h"
|
||||
#include "rocksdb/utilities/convenience.h"
|
||||
#include "util/sync_point.h"
|
||||
@@ -212,8 +213,8 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, SkipStatsUpdateTest) {
|
||||
// This test verify UpdateAccumulatedStats is not on by observing
|
||||
// the compaction behavior when there are many of deletion entries.
|
||||
// This test verify UpdateAccumulatedStats is not on
|
||||
// if options.skip_stats_update_on_db_open = true
|
||||
// The test will need to be updated if the internal behavior changes.
|
||||
|
||||
Options options = DeletionTriggerOptions(CurrentOptions());
|
||||
@@ -230,10 +231,6 @@ TEST_F(DBCompactionTest, SkipStatsUpdateTest) {
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
for (int k = 0; k < kTestSize; ++k) {
|
||||
ASSERT_OK(Delete(Key(k)));
|
||||
}
|
||||
|
||||
// Reopen the DB with stats-update disabled
|
||||
options.skip_stats_update_on_db_open = true;
|
||||
env_->random_file_open_counter_.store(0);
|
||||
@@ -309,7 +306,8 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Preloading iterator issues one table cache lookup and creates
|
||||
// a new table reader. One file is created for flush and one for compaction.
|
||||
// Compaction inputs make no table cache look-up.
|
||||
// Compaction inputs make no table cache look-up for data/range deletion
|
||||
// iterators
|
||||
ASSERT_EQ(num_table_cache_lookup, 2);
|
||||
// Create new iterator for:
|
||||
// (1) 1 for verifying flush results
|
||||
@@ -330,7 +328,8 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
cro.target_level = 2;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
db_->CompactRange(cro, nullptr, nullptr);
|
||||
// Only verifying compaction outputs issues one table cache lookup.
|
||||
// Only verifying compaction outputs issues one table cache lookup
|
||||
// for both data block and range deletion block).
|
||||
ASSERT_EQ(num_table_cache_lookup, 1);
|
||||
// One for compaction input, one for verifying compaction results.
|
||||
ASSERT_EQ(num_new_table_reader, 2);
|
||||
@@ -653,6 +652,72 @@ TEST_F(DBCompactionTest, MinorCompactionsHappen) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, UserKeyCrossFile1) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// create first file and flush to l0
|
||||
Put("4", "A");
|
||||
Put("3", "A");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
Put("2", "A");
|
||||
Delete("3");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
// move both files down to l1
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Put("2", "B");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, UserKeyCrossFile2) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// create first file and flush to l0
|
||||
Put("4", "A");
|
||||
Put("3", "A");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
Put("2", "A");
|
||||
SingleDelete("3");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
// move both files down to l1
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Put("2", "B");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ZeroSeqIdCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
@@ -953,19 +1018,21 @@ TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
|
||||
"DBImpl::BackgroundCompaction:NonTrivial",
|
||||
[&](void* arg) { non_trivial_move++; });
|
||||
bool first = true;
|
||||
bool second = true;
|
||||
// Purpose of dependencies:
|
||||
// 4 -> 1: ensure the order of two non-trivial compactions
|
||||
// 5 -> 2 and 5 -> 3: ensure we do a check before two non-trivial compactions
|
||||
// are installed
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBCompaction::ManualPartial:4", "DBCompaction::ManualPartial:1"},
|
||||
{"DBCompaction::ManualPartial:2", "DBCompaction::ManualPartial:3"},
|
||||
{"DBCompaction::ManualPartial:5",
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun"}});
|
||||
{"DBCompaction::ManualPartial:5", "DBCompaction::ManualPartial:2"},
|
||||
{"DBCompaction::ManualPartial:5", "DBCompaction::ManualPartial:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
if (first) {
|
||||
TEST_SYNC_POINT("DBCompaction::ManualPartial:4");
|
||||
first = false;
|
||||
TEST_SYNC_POINT("DBCompaction::ManualPartial:4");
|
||||
TEST_SYNC_POINT("DBCompaction::ManualPartial:3");
|
||||
} else if (second) {
|
||||
} else { // second non-trivial compaction
|
||||
TEST_SYNC_POINT("DBCompaction::ManualPartial:2");
|
||||
}
|
||||
});
|
||||
@@ -1035,13 +1102,14 @@ TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
|
||||
ASSERT_EQ(trivial_move, 6);
|
||||
ASSERT_EQ(non_trivial_move, 0);
|
||||
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
compact_options.change_level = false;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
std::string begin_string = Key(0);
|
||||
std::string end_string = Key(199);
|
||||
Slice begin(begin_string);
|
||||
Slice end(end_string);
|
||||
// First non-trivial compaction is triggered
|
||||
ASSERT_OK(db_->CompactRange(compact_options, &begin, &end));
|
||||
});
|
||||
|
||||
@@ -1065,15 +1133,17 @@ TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
|
||||
values[i] = RandomString(&rnd, value_size);
|
||||
ASSERT_OK(Put(Key(i), values[i]));
|
||||
}
|
||||
// Second non-trivial compaction is triggered
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// 3 files in L0
|
||||
// Before two non-trivial compactions are installed, there are 3 files in L0
|
||||
ASSERT_EQ("3,0,0,0,0,1,2", FilesPerLevel(0));
|
||||
TEST_SYNC_POINT("DBCompaction::ManualPartial:5");
|
||||
|
||||
// 1 file in L6, 1 file in L1
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// After two non-trivial compactions are installed, there is 1 file in L6, and
|
||||
// 1 file in L1
|
||||
ASSERT_EQ("0,1,0,0,0,0,1", FilesPerLevel(0));
|
||||
threads.join();
|
||||
|
||||
@@ -1164,7 +1234,7 @@ TEST_F(DBCompactionTest, ManualPartialFill) {
|
||||
ASSERT_EQ(trivial_move, 2);
|
||||
ASSERT_EQ(non_trivial_move, 0);
|
||||
|
||||
std::thread threads([&] {
|
||||
rocksdb::port::Thread threads([&] {
|
||||
compact_options.change_level = false;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
std::string begin_string = Key(0);
|
||||
@@ -1191,7 +1261,8 @@ TEST_F(DBCompactionTest, ManualPartialFill) {
|
||||
uint64_t target_size = 4 * options.max_bytes_for_level_base;
|
||||
for (int32_t i = 1; i < options.num_levels; i++) {
|
||||
ASSERT_LE(SizeAtLevel(i), target_size);
|
||||
target_size *= options.max_bytes_for_level_multiplier;
|
||||
target_size = static_cast<uint64_t>(target_size *
|
||||
options.max_bytes_for_level_multiplier);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("DBCompaction::PartialFill:2");
|
||||
@@ -1268,7 +1339,8 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
uint64_t target_size = 4 * options.max_bytes_for_level_base;
|
||||
for (int32_t i = 1; i < options.num_levels; i++) {
|
||||
ASSERT_LE(SizeAtLevel(i), target_size);
|
||||
target_size *= options.max_bytes_for_level_multiplier;
|
||||
target_size = static_cast<uint64_t>(target_size *
|
||||
options.max_bytes_for_level_multiplier);
|
||||
}
|
||||
|
||||
size_t old_num_files = CountFiles();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -137,7 +138,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.max_background_compactions = 2;
|
||||
options.num_levels = 5;
|
||||
options.expanded_compaction_factor = 0; // Force not expanding in compactions
|
||||
options.max_compaction_bytes = 0; // Force not expanding in compactions
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
@@ -223,8 +224,9 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "true"},
|
||||
}));
|
||||
// Write about 600K more
|
||||
for (int i = 0; i < 1500; i++) {
|
||||
// Write about 650K more.
|
||||
// Each file is about 11KB, with 9KB of data.
|
||||
for (int i = 0; i < 1300; i++) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
|
||||
RandomString(&rnd, 380)));
|
||||
}
|
||||
@@ -250,7 +252,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::thread thread([this] {
|
||||
rocksdb::port::Thread thread([this] {
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_start");
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_finish");
|
||||
@@ -294,7 +296,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
|
||||
options.max_background_compactions = 1;
|
||||
const int kNumLevels = 5;
|
||||
options.num_levels = kNumLevels;
|
||||
options.expanded_compaction_factor = 0; // Force not expanding in compactions
|
||||
options.max_compaction_bytes = 1; // Force not expanding in compactions
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
@@ -405,7 +407,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBaseInc) {
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
}
|
||||
|
||||
TEST_F(DBTestDynamicLevel, MigrateToDynamicLevelMaxBytesBase) {
|
||||
TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
|
||||
Random rnd(301);
|
||||
const int kMaxKey = 2000;
|
||||
|
||||
@@ -461,7 +463,7 @@ TEST_F(DBTestDynamicLevel, MigrateToDynamicLevelMaxBytesBase) {
|
||||
compaction_finished = false;
|
||||
// Issue manual compaction in one thread and still verify DB state
|
||||
// in main thread.
|
||||
std::thread t([&]() {
|
||||
rocksdb::port::Thread t([&]() {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = options.num_levels - 1;
|
||||
|
||||
+11
-11
@@ -14,19 +14,19 @@
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include "db/db_impl.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/version_set.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "port/port.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/file_util.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -34,10 +34,10 @@ Status DBImpl::DisableFileDeletions() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
++disable_delete_obsolete_files_;
|
||||
if (disable_delete_obsolete_files_ == 1) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Disabled");
|
||||
} else {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
@@ -58,12 +58,12 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
if (disable_delete_obsolete_files_ == 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Enabled");
|
||||
should_purge_files = true;
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
} else {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
job_context.Clean();
|
||||
LogFlush(db_options_.info_log);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -83,7 +83,6 @@ int DBImpl::IsFileDeletionsEnabled() const {
|
||||
Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
uint64_t* manifest_file_size,
|
||||
bool flush_memtable) {
|
||||
|
||||
*manifest_file_size = 0;
|
||||
|
||||
mutex_.Lock();
|
||||
@@ -110,7 +109,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
if (!status.ok()) {
|
||||
mutex_.Unlock();
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::ERROR_LEVEL, immutable_db_options_.info_log,
|
||||
"Cannot Flush data %s\n", status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
@@ -126,7 +125,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
|
||||
ret.clear();
|
||||
ret.reserve(live.size() + 2); //*.sst + CURRENT + MANIFEST
|
||||
ret.reserve(live.size() + 3); // *.sst + CURRENT + MANIFEST + OPTIONS
|
||||
|
||||
// create names of the live files. The names are not absolute
|
||||
// paths, instead they are relative to dbname_;
|
||||
@@ -136,6 +135,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
ret.push_back(CurrentFileName(""));
|
||||
ret.push_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
ret.push_back(OptionsFileName("", versions_->options_file_number()));
|
||||
|
||||
// find length of manifest file while holding the mutex lock
|
||||
*manifest_file_size = versions_->manifest_file_size();
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2011-present, 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.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBFlushTest : public DBTestBase {
|
||||
public:
|
||||
DBFlushTest() : DBTestBase("/db_flush_test") {}
|
||||
};
|
||||
|
||||
// We had issue when two background threads trying to flush at the same time,
|
||||
// only one of them get committed. The test verifies the issue is fixed.
|
||||
TEST_F(DBFlushTest, FlushWhileWritingManifest) {
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.max_background_flushes = 2;
|
||||
Reopen(options);
|
||||
FlushOptions no_wait;
|
||||
no_wait.wait = false;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"VersionSet::LogAndApply:WriteManifest",
|
||||
"DBFlushTest::FlushWhileWritingManifest:1"},
|
||||
{"MemTableList::InstallMemtableFlushResults:InProgress",
|
||||
"VersionSet::LogAndApply:WriteManifestDone"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("foo", "v"));
|
||||
ASSERT_OK(dbfull()->Flush(no_wait));
|
||||
TEST_SYNC_POINT("DBFlushTest::FlushWhileWritingManifest:1");
|
||||
ASSERT_OK(Put("bar", "v"));
|
||||
ASSERT_OK(dbfull()->Flush(no_wait));
|
||||
// If the issue is hit we will wait here forever.
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(2, TotalTableFiles());
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, SyncFail) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = fault_injection_env.get();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBFlushTest::SyncFail:1", "DBImpl::SyncClosedLogs:Start"},
|
||||
{"DBImpl::SyncClosedLogs:Failed", "DBFlushTest::SyncFail:2"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Reopen(options);
|
||||
Put("key", "value");
|
||||
auto* cfd =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
->cfd();
|
||||
int refs_before = cfd->current()->TEST_refs();
|
||||
FlushOptions flush_options;
|
||||
flush_options.wait = false;
|
||||
ASSERT_OK(dbfull()->Flush(flush_options));
|
||||
fault_injection_env->SetFilesystemActive(false);
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:1");
|
||||
TEST_SYNC_POINT("DBFlushTest::SyncFail:2");
|
||||
fault_injection_env->SetFilesystemActive(true);
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("", FilesPerLevel()); // flush failed.
|
||||
// Flush job should release ref count to current version.
|
||||
ASSERT_EQ(refs_before, cfd->current()->TEST_refs());
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+1287
-798
File diff suppressed because it is too large.
Load diff
+157
-47
@@ -13,6 +13,7 @@
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
@@ -22,6 +23,7 @@
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction_job.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/external_sst_file_ingestion_job.h"
|
||||
#include "db/flush_job.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/internal_stats.h"
|
||||
@@ -31,15 +33,16 @@
|
||||
#include "db/wal_manager.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "db/writebuffer.h"
|
||||
#include "memtable_list.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
@@ -122,6 +125,10 @@ class DBImpl : public DB {
|
||||
using DB::GetProperty;
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) override;
|
||||
using DB::GetMapProperty;
|
||||
virtual bool GetMapProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property,
|
||||
std::map<std::string, double>* value) override;
|
||||
using DB::GetIntProperty;
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, uint64_t* value) override;
|
||||
@@ -131,7 +138,13 @@ class DBImpl : public DB {
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable = false) override;
|
||||
uint8_t include_flags
|
||||
= INCLUDE_FILES) override;
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override;
|
||||
using DB::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -155,6 +168,9 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
virtual Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
using DB::NumberLevels;
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
using DB::MaxMemCompactionLevel;
|
||||
@@ -165,10 +181,9 @@ class DBImpl : public DB {
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Env* GetEnv() const override;
|
||||
using DB::GetOptions;
|
||||
virtual const Options& GetOptions(
|
||||
ColumnFamilyHandle* column_family) const override;
|
||||
virtual Options GetOptions(ColumnFamilyHandle* column_family) const override;
|
||||
using DB::GetDBOptions;
|
||||
virtual const DBOptions& GetDBOptions() const override;
|
||||
virtual DBOptions GetDBOptions() const override;
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
@@ -252,16 +267,16 @@ class DBImpl : public DB {
|
||||
//
|
||||
// Returns OK or NotFound on success,
|
||||
// other status on unexpected error.
|
||||
// TODO(andrewkr): this API need to be aware of range deletion operations
|
||||
Status GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
bool cache_only, SequenceNumber* seq,
|
||||
bool* found_record_for_key);
|
||||
|
||||
using DB::AddFile;
|
||||
virtual Status AddFile(ColumnFamilyHandle* column_family,
|
||||
const ExternalSstFileInfo* file_info,
|
||||
bool move_file) override;
|
||||
virtual Status AddFile(ColumnFamilyHandle* column_family,
|
||||
const std::string& file_path, bool move_file) override;
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override;
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -287,7 +302,8 @@ class DBImpl : public DB {
|
||||
// The keys of this iterator are internal keys (see format.h).
|
||||
// The returned iterator should be deleted when no longer needed.
|
||||
InternalIterator* NewInternalIterator(
|
||||
Arena* arena, ColumnFamilyHandle* column_family = nullptr);
|
||||
Arena* arena, RangeDelAggregator* range_del_agg,
|
||||
ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Extra methods (for testing) that are not in the public DB interface
|
||||
@@ -298,6 +314,16 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool disallow_trivial_move = false);
|
||||
|
||||
void TEST_MaybeFlushColumnFamilies();
|
||||
|
||||
bool TEST_UnableToFlushOldestLog() {
|
||||
return unable_to_flush_oldest_log_;
|
||||
}
|
||||
|
||||
bool TEST_IsLogGettingFlushed() {
|
||||
return alive_log_files_.begin()->getting_flushed;
|
||||
}
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status TEST_FlushMemTable(bool wait = true,
|
||||
ColumnFamilyHandle* cfh = nullptr);
|
||||
@@ -341,6 +367,8 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t TEST_LogfileNumber();
|
||||
|
||||
uint64_t TEST_total_log_size() const { return total_log_size_; }
|
||||
|
||||
// Returns column family name to ImmutableCFOptions map.
|
||||
Status TEST_GetAllImmutableCFOptions(
|
||||
std::unordered_map<std::string, const ImmutableCFOptions*>* iopts_map);
|
||||
@@ -356,12 +384,20 @@ class DBImpl : public DB {
|
||||
uint64_t TEST_FindMinLogContainingOutstandingPrep();
|
||||
uint64_t TEST_FindMinPrepLogReferencedByMemTable();
|
||||
|
||||
int TEST_BGCompactionsAllowed() const;
|
||||
|
||||
#endif // NDEBUG
|
||||
|
||||
// Return maximum background compaction alowed to be scheduled based on
|
||||
// Return maximum background compaction allowed to be scheduled based on
|
||||
// compaction status.
|
||||
int BGCompactionsAllowed() const;
|
||||
|
||||
// move logs pending closing from job_context to the DB queue and
|
||||
// schedule a purge
|
||||
void ScheduleBgLogWriterClose(JobContext* job_context);
|
||||
|
||||
uint64_t MinLogNumberToKeep();
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
// of all files in the filesystem in 'candidate_files'.
|
||||
// If force == false and the last call was less than
|
||||
@@ -374,12 +410,19 @@ class DBImpl : public DB {
|
||||
// belong to live files are posibly removed. Also, removes all the
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
void PurgeObsoleteFiles(const JobContext& background_contet);
|
||||
void PurgeObsoleteFiles(const JobContext& background_contet,
|
||||
bool schedule_only = false);
|
||||
|
||||
void SchedulePurge();
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override;
|
||||
|
||||
const SnapshotList& snapshots() const { return snapshots_; }
|
||||
|
||||
const ImmutableDBOptions& immutable_db_options() const {
|
||||
return immutable_db_options_;
|
||||
}
|
||||
|
||||
void CancelAllBackgroundWork(bool wait);
|
||||
|
||||
// Find Super version and reference it. Based on options, it might return
|
||||
@@ -393,9 +436,6 @@ class DBImpl : public DB {
|
||||
// mutex is held.
|
||||
SuperVersion* GetAndRefSuperVersion(uint32_t column_family_id);
|
||||
|
||||
// Same as above, should called without mutex held and not on write thread.
|
||||
SuperVersion* GetAndRefSuperVersionUnlocked(uint32_t column_family_id);
|
||||
|
||||
// Un-reference the super version and return it to thread local cache if
|
||||
// needed. If it is the last reference of the super version. Clean it up
|
||||
// after un-referencing it.
|
||||
@@ -406,18 +446,11 @@ class DBImpl : public DB {
|
||||
// REQUIRED: this function should only be called on the write thread.
|
||||
void ReturnAndCleanupSuperVersion(uint32_t colun_family_id, SuperVersion* sv);
|
||||
|
||||
// Same as above, should called without mutex held and not on write thread.
|
||||
void ReturnAndCleanupSuperVersionUnlocked(uint32_t colun_family_id,
|
||||
SuperVersion* sv);
|
||||
|
||||
// REQUIRED: this function should only be called on the write thread or if the
|
||||
// mutex is held. Return value only valid until next call to this function or
|
||||
// mutex is released.
|
||||
ColumnFamilyHandle* GetColumnFamilyHandle(uint32_t column_family_id);
|
||||
|
||||
// Same as above, should called without mutex held and not on write thread.
|
||||
ColumnFamilyHandle* GetColumnFamilyHandleUnlocked(uint32_t column_family_id);
|
||||
|
||||
// Returns the number of currently running flushes.
|
||||
// REQUIREMENT: mutex_ must be held when calling this function.
|
||||
int num_running_flushes() {
|
||||
@@ -446,7 +479,7 @@ class DBImpl : public DB {
|
||||
~RecoveredTransaction() { delete batch_; }
|
||||
};
|
||||
|
||||
bool allow_2pc() const { return db_options_.allow_2pc; }
|
||||
bool allow_2pc() const { return immutable_db_options_.allow_2pc; }
|
||||
|
||||
std::unordered_map<std::string, RecoveredTransaction*>
|
||||
recovered_transactions() {
|
||||
@@ -487,12 +520,19 @@ class DBImpl : public DB {
|
||||
|
||||
void MarkLogAsHavingPrepSectionFlushed(uint64_t log);
|
||||
void MarkLogAsContainingPrepSection(uint64_t log);
|
||||
void AddToLogsToFreeQueue(log::Writer* log_writer) {
|
||||
logs_to_free_queue_.push_back(log_writer);
|
||||
}
|
||||
|
||||
Status NewDB();
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
unique_ptr<VersionSet> versions_;
|
||||
const DBOptions db_options_;
|
||||
const DBOptions initial_db_options_;
|
||||
const ImmutableDBOptions immutable_db_options_;
|
||||
MutableDBOptions mutable_db_options_;
|
||||
Statistics* stats_;
|
||||
std::unordered_map<std::string, RecoveredTransaction*>
|
||||
recovered_transactions_;
|
||||
@@ -500,7 +540,8 @@ class DBImpl : public DB {
|
||||
InternalIterator* NewInternalIterator(const ReadOptions&,
|
||||
ColumnFamilyData* cfd,
|
||||
SuperVersion* super_version,
|
||||
Arena* arena);
|
||||
Arena* arena,
|
||||
RangeDelAggregator* range_del_agg);
|
||||
|
||||
// Except in DB::Open(), WriteOptionsFile can only be called when:
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
@@ -521,9 +562,12 @@ class DBImpl : public DB {
|
||||
Compaction *c, const Status &st,
|
||||
const CompactionJobStats& job_stats,
|
||||
int job_id);
|
||||
void NotifyOnMemTableSealed(ColumnFamilyData* cfd,
|
||||
void NotifyOnMemTableSealed(ColumnFamilyData* cfd,
|
||||
const MemTableInfo& mem_table_info);
|
||||
|
||||
void NotifyOnExternalFileIngested(
|
||||
ColumnFamilyData* cfd, const ExternalSstFileIngestionJob& ingestion_job);
|
||||
|
||||
void NewThreadStatusCfInfo(ColumnFamilyData* cfd) const;
|
||||
|
||||
void EraseThreadStatusCfInfo(ColumnFamilyData* cfd) const;
|
||||
@@ -554,7 +598,7 @@ class DBImpl : public DB {
|
||||
|
||||
struct WriteContext;
|
||||
|
||||
Status NewDB();
|
||||
struct PurgeFileInfo;
|
||||
|
||||
// Recover the descriptor from persistent storage. May do a significant
|
||||
// amount of work to recover recently logged updates. Any changes to
|
||||
@@ -569,6 +613,10 @@ class DBImpl : public DB {
|
||||
|
||||
// Delete any unneeded files and stale in-memory entries.
|
||||
void DeleteObsoleteFiles();
|
||||
// Delete obsolete files and log status and information of file deletion
|
||||
void DeleteObsoleteFileImpl(Status file_deletion_status, int job_id,
|
||||
const std::string& fname, FileType type,
|
||||
uint64_t number, uint32_t path_id);
|
||||
|
||||
// Background process needs to call
|
||||
// auto x = CaptureCurrentFileNumberInPendingOutputs()
|
||||
@@ -589,6 +637,8 @@ class DBImpl : public DB {
|
||||
// and blocked by any other pending_outputs_ calls)
|
||||
void ReleaseFileNumberFromPendingOutputs(std::list<uint64_t>::iterator v);
|
||||
|
||||
Status SyncClosedLogs(JobContext* job_context);
|
||||
|
||||
// Flush the in-memory write buffer to storage. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful.
|
||||
Status FlushMemTableToOutputFile(ColumnFamilyData* cfd,
|
||||
@@ -598,7 +648,7 @@ class DBImpl : public DB {
|
||||
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* max_sequence, bool read_only);
|
||||
SequenceNumber* next_sequence, bool read_only);
|
||||
|
||||
// The following two methods are used to flush a memtable to
|
||||
// storage. The first one is used at database RecoveryTime (when the
|
||||
@@ -610,24 +660,35 @@ class DBImpl : public DB {
|
||||
|
||||
// num_bytes: for slowdown case, delay time is calculated based on
|
||||
// `num_bytes` going through.
|
||||
Status DelayWrite(uint64_t num_bytes);
|
||||
Status DelayWrite(uint64_t num_bytes, const WriteOptions& write_options);
|
||||
|
||||
Status ScheduleFlushes(WriteContext* context);
|
||||
|
||||
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context);
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options);
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
bool writes_stopped = false);
|
||||
|
||||
// Wait for memtable flushed
|
||||
Status WaitForFlushMemTable(ColumnFamilyData* cfd);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
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, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
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,
|
||||
JobContext* job_context, LogBuffer* log_buffer);
|
||||
|
||||
// Wait for current IngestExternalFile() calls to finish.
|
||||
// REQUIRES: mutex_ held
|
||||
void WaitForIngestFile();
|
||||
|
||||
#else
|
||||
// IngestExternalFile is not supported in ROCKSDB_LITE so this function
|
||||
// will be no-op
|
||||
void WaitForIngestFile() {}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
ColumnFamilyData* GetColumnFamilyDataByName(const std::string& cf_name);
|
||||
@@ -635,11 +696,15 @@ class DBImpl : public DB {
|
||||
void MaybeScheduleFlushOrCompaction();
|
||||
void SchedulePendingFlush(ColumnFamilyData* cfd);
|
||||
void SchedulePendingCompaction(ColumnFamilyData* cfd);
|
||||
void SchedulePendingPurge(std::string fname, FileType type, uint64_t number,
|
||||
uint32_t path_id, int job_id);
|
||||
static void BGWorkCompaction(void* arg);
|
||||
static void BGWorkFlush(void* db);
|
||||
static void BGWorkPurge(void* arg);
|
||||
static void UnscheduleCallback(void* arg);
|
||||
void BackgroundCallCompaction(void* arg);
|
||||
void BackgroundCallFlush();
|
||||
void BackgroundCallPurge();
|
||||
Status BackgroundCompaction(bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer, void* m = 0);
|
||||
Status BackgroundFlush(bool* madeProgress, JobContext* job_context,
|
||||
@@ -671,6 +736,14 @@ class DBImpl : public DB {
|
||||
|
||||
const Snapshot* GetSnapshotImpl(bool is_write_conflict_boundary);
|
||||
|
||||
// Persist RocksDB options under the single write thread
|
||||
// REQUIRES: mutex locked
|
||||
Status PersistOptions();
|
||||
|
||||
void MaybeFlushColumnFamilies();
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
@@ -682,7 +755,7 @@ class DBImpl : public DB {
|
||||
// same time.
|
||||
InstrumentedMutex options_files_mutex_;
|
||||
// State below is protected by mutex_
|
||||
InstrumentedMutex mutex_;
|
||||
mutable InstrumentedMutex mutex_;
|
||||
|
||||
std::atomic<bool> shutting_down_;
|
||||
// This condition variable is signaled on these conditions:
|
||||
@@ -690,9 +763,10 @@ class DBImpl : public DB {
|
||||
// * if AnyManualCompaction, whenever a compaction finishes, even if it hasn't
|
||||
// made any progress
|
||||
// * whenever a compaction made any progress
|
||||
// * 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
|
||||
// * whenever bg_flush_scheduled_ or bg_purge_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 purge, flush or compaction
|
||||
// * whenever num_running_ingest_file_ goes to 0.
|
||||
InstrumentedCondVar bg_cv_;
|
||||
uint64_t logfile_number_;
|
||||
std::deque<uint64_t>
|
||||
@@ -785,7 +859,7 @@ class DBImpl : public DB {
|
||||
|
||||
Directories directories_;
|
||||
|
||||
WriteBuffer write_buffer_;
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
|
||||
WriteThread write_thread_;
|
||||
|
||||
@@ -812,6 +886,19 @@ class DBImpl : public DB {
|
||||
// State is protected with db mutex.
|
||||
std::list<uint64_t> pending_outputs_;
|
||||
|
||||
// PurgeFileInfo is a structure to hold information of files to be deleted in
|
||||
// purge_queue_
|
||||
struct PurgeFileInfo {
|
||||
std::string fname;
|
||||
FileType type;
|
||||
uint64_t number;
|
||||
uint32_t path_id;
|
||||
int job_id;
|
||||
PurgeFileInfo(std::string fn, FileType t, uint64_t num, uint32_t pid,
|
||||
int jid)
|
||||
: fname(fn), type(t), number(num), path_id(pid), job_id(jid) {}
|
||||
};
|
||||
|
||||
// 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
|
||||
@@ -836,6 +923,12 @@ class DBImpl : public DB {
|
||||
// invariant(column family present in compaction_queue_ <==>
|
||||
// ColumnFamilyData::pending_compaction_ == true)
|
||||
std::deque<ColumnFamilyData*> compaction_queue_;
|
||||
|
||||
// A queue to store filenames of the files to be purged
|
||||
std::deque<PurgeFileInfo> purge_queue_;
|
||||
|
||||
// A queue to store log writers to close
|
||||
std::deque<log::Writer*> logs_to_free_queue_;
|
||||
int unscheduled_flushes_;
|
||||
int unscheduled_compactions_;
|
||||
|
||||
@@ -851,6 +944,9 @@ class DBImpl : public DB {
|
||||
// stores the number of flushes are currently running
|
||||
int num_running_flushes_;
|
||||
|
||||
// number of background obsolete file purge jobs, submitted to the HIGH pool
|
||||
int bg_purge_scheduled_;
|
||||
|
||||
// Information for a manual compaction
|
||||
struct ManualCompaction {
|
||||
ColumnFamilyData* cfd;
|
||||
@@ -888,8 +984,9 @@ class DBImpl : public DB {
|
||||
// without any synchronization
|
||||
int disable_delete_obsolete_files_;
|
||||
|
||||
// next time when we should run DeleteObsoleteFiles with full scan
|
||||
uint64_t delete_obsolete_files_next_run_;
|
||||
// last time when DeleteObsoleteFiles with full scan was executed. Originaly
|
||||
// initialized with startup time.
|
||||
uint64_t delete_obsolete_files_last_run_;
|
||||
|
||||
// last time stats were dumped to LOG
|
||||
std::atomic<uint64_t> last_stats_dump_time_microsec_;
|
||||
@@ -903,6 +1000,15 @@ class DBImpl : public DB {
|
||||
// Used when disableWAL is true.
|
||||
bool has_unpersisted_data_;
|
||||
|
||||
|
||||
// if an attempt was made to flush all column families that
|
||||
// the oldest log depends on but uncommited data in the oldest
|
||||
// log prevents the log from being released.
|
||||
// We must attempt to free the dependent memtables again
|
||||
// at a later time after the transaction in the oldest
|
||||
// log is fully commited.
|
||||
bool unable_to_flush_oldest_log_;
|
||||
|
||||
static const int KEEP_LOG_FILE_NUM = 1000;
|
||||
// MSVC version 1800 still does not have constexpr for ::max()
|
||||
static const uint64_t kNoTimeOut = port::kMaxUint64;
|
||||
@@ -912,6 +1018,10 @@ class DBImpl : public DB {
|
||||
// The options to access storage files
|
||||
const EnvOptions env_options_;
|
||||
|
||||
// Number of running IngestExternalFile() calls.
|
||||
// REQUIRES: mutex held
|
||||
int num_running_ingest_file_;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
WalManager wal_manager_;
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -1007,13 +1117,13 @@ class DBImpl : public DB {
|
||||
bool ShouldntRunManualCompaction(ManualCompaction* m);
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompaction* m, ManualCompaction* m1);
|
||||
|
||||
size_t GetWalPreallocateBlockSize(uint64_t write_buffer_size) const;
|
||||
};
|
||||
|
||||
// Sanitize db options. The caller should delete result.info_log if
|
||||
// it is not equal to src.info_log.
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
const InternalKeyComparator* icmp,
|
||||
const Options& src);
|
||||
|
||||
extern DBOptions SanitizeOptions(const std::string& db, const DBOptions& src);
|
||||
|
||||
// Fix user-supplied options to be reasonable
|
||||
|
||||
@@ -19,6 +19,11 @@ uint64_t DBImpl::TEST_GetLevel0TotalSize() {
|
||||
return default_cf_handle_->cfd()->current()->storage_info()->NumLevelBytes(0);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_MaybeFlushColumnFamilies() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
MaybeFlushColumnFamilies();
|
||||
}
|
||||
|
||||
int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
@@ -178,5 +183,10 @@ Status DBImpl::TEST_GetLatestMutableCFOptions(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
int DBImpl::TEST_BGCompactionsAllowed() const {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return BGCompactionsAllowed();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // NDEBUG
|
||||
@@ -49,7 +49,8 @@ Status DBImpl::SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
}
|
||||
// Since we have some more files to compact, we should also recompute
|
||||
// compaction score
|
||||
vstorage->ComputeCompactionScore(*cfd->GetLatestMutableCFOptions());
|
||||
vstorage->ComputeCompactionScore(*cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -60,7 +61,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
assert(column_family);
|
||||
|
||||
if (target_level < 1) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Invalid target level %d\n", target_level);
|
||||
return Status::InvalidArgument("Invalid target level");
|
||||
}
|
||||
@@ -74,7 +75,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
|
||||
if (target_level >= vstorage->num_levels()) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Target level %d does not exist\n", target_level);
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument("Target level does not exist");
|
||||
@@ -93,7 +94,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
for (size_t i = 0; i < l0_files.size(); ++i) {
|
||||
auto f = l0_files[i];
|
||||
if (f->being_compacted) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. File %" PRIu64 " being compacted\n",
|
||||
f->fd.GetNumber());
|
||||
job_context.Clean();
|
||||
@@ -103,7 +104,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
if (i == 0) continue;
|
||||
auto prev_f = l0_files[i - 1];
|
||||
if (icmp->Compare(prev_f->largest, f->smallest) >= 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Files %" PRIu64 " and %" PRIu64
|
||||
" have overlapping ranges\n",
|
||||
prev_f->fd.GetNumber(), f->fd.GetNumber());
|
||||
@@ -115,7 +116,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
// Check that all levels up to target_level are empty.
|
||||
for (int level = 1; level <= target_level; ++level) {
|
||||
if (vstorage->NumLevelFiles(level) > 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Level %d not empty\n", level);
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument(
|
||||
@@ -140,7 +141,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
cfd, &job_context, *cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
} // lock released here
|
||||
LogFlush(db_options_.info_log);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
job_context.Clean();
|
||||
|
||||
return status;
|
||||
|
||||
+16
-9
@@ -8,8 +8,9 @@
|
||||
|
||||
#include "db/compacted_db_impl.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "util/perf_context_imp.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -19,8 +20,9 @@ namespace rocksdb {
|
||||
DBImplReadOnly::DBImplReadOnly(const DBOptions& db_options,
|
||||
const std::string& dbname)
|
||||
: DBImpl(db_options, dbname) {
|
||||
Log(INFO_LEVEL, db_options_.info_log, "Opening the db in read only mode");
|
||||
LogFlush(db_options_.info_log);
|
||||
Log(INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"Opening the db in read only mode");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
DBImplReadOnly::~DBImplReadOnly() {
|
||||
@@ -36,11 +38,14 @@ Status DBImplReadOnly::Get(const ReadOptions& read_options,
|
||||
auto cfd = cfh->cfd();
|
||||
SuperVersion* super_version = cfd->GetSuperVersion();
|
||||
MergeContext merge_context;
|
||||
RangeDelAggregator range_del_agg(cfd->internal_comparator(), snapshot);
|
||||
LookupKey lkey(key, snapshot);
|
||||
if (super_version->mem->Get(lkey, value, &s, &merge_context)) {
|
||||
if (super_version->mem->Get(lkey, value, &s, &merge_context, &range_del_agg,
|
||||
read_options)) {
|
||||
} else {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->Get(read_options, lkey, value, &s, &merge_context);
|
||||
super_version->current->Get(read_options, lkey, value, &s, &merge_context,
|
||||
&range_del_agg);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -59,8 +64,9 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
: latest_snapshot),
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
read_options, cfd, super_version, db_iter->GetArena());
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator());
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
return db_iter;
|
||||
}
|
||||
@@ -87,8 +93,9 @@ Status DBImplReadOnly::NewIterators(
|
||||
: latest_snapshot),
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number);
|
||||
auto* internal_iter = NewInternalIterator(
|
||||
read_options, cfd, sv, db_iter->GetArena());
|
||||
auto* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator());
|
||||
db_iter->SetIterUnderDBIter(internal_iter);
|
||||
iterators->push_back(db_iter);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,6 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
virtual Status DisableFileDeletions() override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
@@ -92,7 +91,6 @@ class DBImplReadOnly : public DBImpl {
|
||||
bool flush_memtable = true) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
using DBImpl::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
@@ -105,6 +103,14 @@ class DBImplReadOnly : public DBImpl {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override {
|
||||
return Status::NotSupported("Not supported operation in read only mode.");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/filename.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
void DumpDBFileSummary(const DBOptions& options, const std::string& dbname) {
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
const std::string& dbname) {
|
||||
if (options.info_log == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-2
@@ -6,8 +6,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/db_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
void DumpDBFileSummary(const DBOptions& options, const std::string& dbname);
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
const std::string& dbname);
|
||||
} // namespace rocksdb
|
||||
@@ -23,6 +23,8 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdate) {
|
||||
options.inplace_update_support = true;
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Update key with values of smaller size
|
||||
@@ -45,6 +47,8 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdateLargeNewValue) {
|
||||
options.inplace_update_support = true;
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Update key with values of larger size
|
||||
@@ -70,6 +74,8 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdateCallbackSmallerSize) {
|
||||
options.write_buffer_size = 100000;
|
||||
options.inplace_callback =
|
||||
rocksdb::DBTestInPlaceUpdate::updateInPlaceSmallerSize;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Update key with values of smaller size
|
||||
@@ -97,6 +103,8 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdateCallbackSmallerVarintSize) {
|
||||
options.write_buffer_size = 100000;
|
||||
options.inplace_callback =
|
||||
rocksdb::DBTestInPlaceUpdate::updateInPlaceSmallerVarintSize;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Update key with values of smaller varint size
|
||||
@@ -124,6 +132,8 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdateCallbackLargeNewValue) {
|
||||
options.write_buffer_size = 100000;
|
||||
options.inplace_callback =
|
||||
rocksdb::DBTestInPlaceUpdate::updateInPlaceLargerSize;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Update key with values of larger size
|
||||
@@ -148,7 +158,9 @@ TEST_F(DBTestInPlaceUpdate, InPlaceUpdateCallbackNoAction) {
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
options.inplace_callback =
|
||||
rocksdb::DBTestInPlaceUpdate::updateInPlaceNoAction;
|
||||
rocksdb::DBTestInPlaceUpdate::updateInPlaceNoAction;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Callback function requests no actions from db
|
||||
|
||||
@@ -33,7 +33,7 @@ TEST_F(DBIOFailureTest, DropWrites) {
|
||||
// Force out-of-space errors
|
||||
env_->drop_writes_.store(true, std::memory_order_release);
|
||||
env_->sleep_counter_.Reset();
|
||||
env_->no_sleep_ = true;
|
||||
env_->no_slowdown_ = true;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (option_config_ != kUniversalCompactionMultiLevel &&
|
||||
option_config_ != kUniversalSubcompactions) {
|
||||
@@ -111,6 +111,7 @@ TEST_F(DBIOFailureTest, NoSpaceCompactRange) {
|
||||
Status s = dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr,
|
||||
true /* disallow trivial move */);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_TRUE(s.IsNoSpace());
|
||||
|
||||
env_->no_space_.store(false, std::memory_order_release);
|
||||
} while (ChangeCompactOptions());
|
||||
|
||||
+369
-179
@@ -16,6 +16,7 @@
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -104,12 +105,13 @@ class DBIter: public Iterator {
|
||||
InternalIterator* iter, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false)
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false)
|
||||
: arena_mode_(arena_mode),
|
||||
env_(env),
|
||||
logger_(ioptions.info_log),
|
||||
user_comparator_(cmp),
|
||||
user_merge_operator_(ioptions.merge_operator),
|
||||
merge_operator_(ioptions.merge_operator),
|
||||
iter_(iter),
|
||||
sequence_(s),
|
||||
direction_(kForward),
|
||||
@@ -119,7 +121,10 @@ class DBIter: public Iterator {
|
||||
version_number_(version_number),
|
||||
iterate_upper_bound_(iterate_upper_bound),
|
||||
prefix_same_as_start_(prefix_same_as_start),
|
||||
pin_thru_lifetime_(pin_data) {
|
||||
pin_thru_lifetime_(pin_data),
|
||||
total_order_seek_(total_order_seek),
|
||||
range_del_agg_(ioptions.internal_comparator, s,
|
||||
true /* collapse_deletions */) {
|
||||
RecordTick(statistics_, NO_ITERATORS);
|
||||
prefix_extractor_ = ioptions.prefix_extractor;
|
||||
max_skip_ = max_sequential_skip_in_iterations;
|
||||
@@ -132,7 +137,9 @@ class DBIter: public Iterator {
|
||||
}
|
||||
virtual ~DBIter() {
|
||||
// Release pinned data if any
|
||||
pinned_iters_mgr_.ReleasePinnedIterators();
|
||||
if (pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
RecordTick(statistics_, NO_ITERATORS, -1);
|
||||
local_stats_.BumpGlobalStatistics(statistics_);
|
||||
if (!arena_mode_) {
|
||||
@@ -146,6 +153,10 @@ class DBIter: public Iterator {
|
||||
iter_ = iter;
|
||||
iter_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
virtual RangeDelAggregator* GetRangeDelAggregator() {
|
||||
return &range_del_agg_;
|
||||
}
|
||||
|
||||
virtual bool Valid() const override { return valid_; }
|
||||
virtual Slice key() const override {
|
||||
assert(valid_);
|
||||
@@ -154,7 +165,9 @@ class DBIter: public Iterator {
|
||||
virtual Slice value() const override {
|
||||
assert(valid_);
|
||||
if (current_entry_is_merged_) {
|
||||
return saved_value_;
|
||||
// If pinned_value_ is set then the result of merge operator is one of
|
||||
// the merge operands and we should return it.
|
||||
return pinned_value_.data() ? pinned_value_ : saved_value_;
|
||||
} else if (direction_ == kReverse) {
|
||||
return pinned_value_;
|
||||
} else {
|
||||
@@ -194,10 +207,12 @@ class DBIter: public Iterator {
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
|
||||
private:
|
||||
void ReverseToForward();
|
||||
void ReverseToBackward();
|
||||
void PrevInternal();
|
||||
void FindParseableKey(ParsedInternalKey* ikey, Direction direction);
|
||||
@@ -220,8 +235,8 @@ class DBIter: public Iterator {
|
||||
|
||||
// Release blocks pinned by TempPinData()
|
||||
void ReleaseTempPinnedData() {
|
||||
if (!pin_thru_lifetime_) {
|
||||
pinned_iters_mgr_.ReleasePinnedIterators();
|
||||
if (!pin_thru_lifetime_ && pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +254,7 @@ class DBIter: public Iterator {
|
||||
Env* const env_;
|
||||
Logger* logger_;
|
||||
const Comparator* const user_comparator_;
|
||||
const MergeOperator* const user_merge_operator_;
|
||||
const MergeOperator* const merge_operator_;
|
||||
InternalIterator* iter_;
|
||||
SequenceNumber const sequence_;
|
||||
|
||||
@@ -250,6 +265,7 @@ class DBIter: public Iterator {
|
||||
Direction direction_;
|
||||
bool valid_;
|
||||
bool current_entry_is_merged_;
|
||||
// for prefix seek mode to support prev()
|
||||
Statistics* statistics_;
|
||||
uint64_t max_skip_;
|
||||
uint64_t version_number_;
|
||||
@@ -260,8 +276,10 @@ class DBIter: public Iterator {
|
||||
// Means that we will pin all data blocks we read as long the Iterator
|
||||
// is not deleted, will be true if ReadOptions::pin_data is true
|
||||
const bool pin_thru_lifetime_;
|
||||
const bool total_order_seek_;
|
||||
// List of operands for merge operator.
|
||||
MergeContext merge_context_;
|
||||
RangeDelAggregator range_del_agg_;
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
|
||||
@@ -285,14 +303,10 @@ inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
void DBIter::Next() {
|
||||
assert(valid_);
|
||||
|
||||
// Release temporarily pinned blocks from last operation
|
||||
ReleaseTempPinnedData();
|
||||
if (direction_ == kReverse) {
|
||||
// We only pin blocks when doing kReverse
|
||||
ReleaseTempPinnedData();
|
||||
FindNextUserKey();
|
||||
direction_ = kForward;
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToFirst();
|
||||
}
|
||||
ReverseToForward();
|
||||
} else if (iter_->Valid() && !current_entry_is_merged_) {
|
||||
// If the current value is not a merge, the iter position is the
|
||||
// current key, which is already returned. We can safely issue a
|
||||
@@ -326,7 +340,8 @@ void DBIter::Next() {
|
||||
// saved_value_ => the merged value
|
||||
//
|
||||
// NOTE: In between, saved_key_ can point to a user key that has
|
||||
// a delete marker
|
||||
// a delete marker or a sequence number higher than sequence_
|
||||
// saved_key_ MUST have a proper user_key before calling this function
|
||||
//
|
||||
// The prefix_check parameter controls whether we check the iterated
|
||||
// keys against the prefix of the seeked key. Set to false when
|
||||
@@ -343,71 +358,140 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
assert(iter_->Valid());
|
||||
assert(direction_ == kForward);
|
||||
current_entry_is_merged_ = false;
|
||||
|
||||
// How many times in a row we have skipped an entry with user key less than
|
||||
// or equal to saved_key_. We could skip these entries either because
|
||||
// sequence numbers were too high or because skipping = true.
|
||||
// What saved_key_ contains throughout this method:
|
||||
// - if skipping : saved_key_ contains the key that we need to skip,
|
||||
// and we haven't seen any keys greater than that,
|
||||
// - if num_skipped > 0 : saved_key_ contains the key that we have skipped
|
||||
// num_skipped times, and we haven't seen any keys
|
||||
// greater than that,
|
||||
// - none of the above : saved_key_ can contain anything, it doesn't matter.
|
||||
uint64_t num_skipped = 0;
|
||||
|
||||
do {
|
||||
ParsedInternalKey ikey;
|
||||
|
||||
if (ParseKey(&ikey)) {
|
||||
if (iterate_upper_bound_ != nullptr &&
|
||||
user_comparator_->Compare(ikey.user_key, *iterate_upper_bound_) >= 0) {
|
||||
break;
|
||||
}
|
||||
if (!ParseKey(&ikey)) {
|
||||
// Skip corrupted keys.
|
||||
iter_->Next();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prefix_extractor_ && prefix_check &&
|
||||
prefix_extractor_->Transform(ikey.user_key).compare(prefix_start_key_) != 0) {
|
||||
break;
|
||||
}
|
||||
if (iterate_upper_bound_ != nullptr &&
|
||||
user_comparator_->Compare(ikey.user_key, *iterate_upper_bound_) >= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ikey.sequence <= sequence_) {
|
||||
if (skipping &&
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) <= 0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
} else {
|
||||
switch (ikey.type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeSingleDeletion:
|
||||
if (prefix_extractor_ && prefix_check &&
|
||||
prefix_extractor_->Transform(ikey.user_key)
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ikey.sequence <= sequence_) {
|
||||
if (skipping &&
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) <= 0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
} else {
|
||||
num_skipped = 0;
|
||||
switch (ikey.type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeSingleDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
saved_key_.SetKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
skipping = true;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
break;
|
||||
case kTypeValue:
|
||||
saved_key_.SetKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::
|
||||
kForwardTraversal)) {
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
saved_key_.SetKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
skipping = true;
|
||||
num_skipped = 0;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
break;
|
||||
case kTypeValue:
|
||||
} else {
|
||||
valid_ = true;
|
||||
saved_key_.SetKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
return;
|
||||
case kTypeMerge:
|
||||
// By now, we are sure the current ikey is going to yield a value
|
||||
saved_key_.SetKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
}
|
||||
break;
|
||||
case kTypeMerge:
|
||||
saved_key_.SetKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::
|
||||
kForwardTraversal)) {
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
skipping = true;
|
||||
num_skipped = 0;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
} else {
|
||||
// By now, we are sure the current ikey is going to yield a
|
||||
// value
|
||||
current_entry_is_merged_ = true;
|
||||
valid_ = true;
|
||||
MergeValuesNewToOld(); // Go to a different state machine
|
||||
return;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This key was inserted after our snapshot was taken.
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
|
||||
// Here saved_key_ may contain some old key, or the default empty key, or
|
||||
// key assigned by some random other method. We don't care.
|
||||
if (user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) <= 0) {
|
||||
num_skipped++;
|
||||
} else {
|
||||
saved_key_.SetKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
skipping = false;
|
||||
num_skipped = 0;
|
||||
}
|
||||
}
|
||||
// If we have sequentially iterated via numerous keys and still not
|
||||
// found the next user-key, then it is better to seek so that we can
|
||||
// avoid too many key comparisons. We seek to the last occurrence of
|
||||
// our current key by looking for sequence number 0 and type deletion
|
||||
// (the smallest type).
|
||||
if (skipping && num_skipped > max_skip_) {
|
||||
|
||||
// If we have sequentially iterated via numerous equal keys, then it's
|
||||
// better to seek so that we can avoid too many key comparisons.
|
||||
if (num_skipped > max_skip_) {
|
||||
num_skipped = 0;
|
||||
std::string last_key;
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetKey(), 0,
|
||||
kTypeDeletion));
|
||||
if (skipping) {
|
||||
// We're looking for the next user-key but all we see are the same
|
||||
// user-key with decreasing sequence numbers. Fast forward to
|
||||
// sequence number 0 and type deletion (the smallest type).
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetKey(), 0,
|
||||
kTypeDeletion));
|
||||
// Don't set skipping = false because we may still see more user-keys
|
||||
// equal to saved_key_.
|
||||
} else {
|
||||
// We saw multiple entries with this user key and sequence numbers
|
||||
// higher than sequence_. Fast forward to sequence_.
|
||||
// Note that this only covers a case when a higher key was overwritten
|
||||
// many times since our snapshot was taken, not the case when a lot of
|
||||
// different keys were inserted after our snapshot was taken.
|
||||
AppendInternalKey(&last_key,
|
||||
ParsedInternalKey(saved_key_.GetKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
}
|
||||
iter_->Seek(last_key);
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
} else {
|
||||
@@ -424,19 +508,23 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
// POST: saved_value_ has the merged value for the user key
|
||||
// iter_ points to the next entry (or invalid)
|
||||
void DBIter::MergeValuesNewToOld() {
|
||||
if (!user_merge_operator_) {
|
||||
if (!merge_operator_) {
|
||||
Log(InfoLogLevel::ERROR_LEVEL,
|
||||
logger_, "Options::merge_operator is null.");
|
||||
status_ = Status::InvalidArgument("user_merge_operator_ must be set.");
|
||||
status_ = Status::InvalidArgument("merge_operator_ must be set.");
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Temporarily pin the blocks that hold merge operands
|
||||
TempPinData();
|
||||
merge_context_.Clear();
|
||||
// Start the merge process by pushing the first operand
|
||||
merge_context_.PushOperand(iter_->value());
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
|
||||
ParsedInternalKey ikey;
|
||||
Status s;
|
||||
for (iter_->Next(); iter_->Valid(); iter_->Next()) {
|
||||
if (!ParseKey(&ikey)) {
|
||||
// skip corrupted key
|
||||
@@ -446,7 +534,10 @@ void DBIter::MergeValuesNewToOld() {
|
||||
if (!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
// hit the next user key, stop right here
|
||||
break;
|
||||
} else if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type) {
|
||||
} else if (kTypeDeletion == ikey.type || kTypeSingleDeletion == ikey.type ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::
|
||||
kForwardTraversal)) {
|
||||
// hit a delete with the same user key, stop right here
|
||||
// iter_ is positioned after delete
|
||||
iter_->Next();
|
||||
@@ -456,14 +547,11 @@ void DBIter::MergeValuesNewToOld() {
|
||||
// final result in saved_value_. We are done!
|
||||
// ignore corruption if there is any.
|
||||
const Slice val = iter_->value();
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(ikey.user_key, &val,
|
||||
merge_context_.GetOperands(),
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, ikey.user_key, &val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_, &pinned_value_);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
}
|
||||
// iter_ is positioned after put
|
||||
iter_->Next();
|
||||
@@ -471,33 +559,32 @@ void DBIter::MergeValuesNewToOld() {
|
||||
} else if (kTypeMerge == ikey.type) {
|
||||
// hit a merge, add the value as an operand and run associative merge.
|
||||
// when complete, add result to operands and continue.
|
||||
const Slice& val = iter_->value();
|
||||
merge_context_.PushOperand(val);
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
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,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME, timer.ElapsedNanos());
|
||||
// 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.
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::Prev() {
|
||||
assert(valid_);
|
||||
ReleaseTempPinnedData();
|
||||
if (direction_ == kForward) {
|
||||
ReverseToBackward();
|
||||
}
|
||||
ReleaseTempPinnedData();
|
||||
PrevInternal();
|
||||
if (statistics_ != nullptr) {
|
||||
local_stats_.prev_count_++;
|
||||
@@ -506,24 +593,46 @@ void DBIter::Prev() {
|
||||
local_stats_.bytes_read_ += (key().size() + value().size());
|
||||
}
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_ &&
|
||||
prefix_extractor_->Transform(saved_key_.GetKey())
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
void DBIter::ReverseToForward() {
|
||||
if (prefix_extractor_ != nullptr && !total_order_seek_) {
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(ParsedInternalKey(
|
||||
saved_key_.GetKey(), kMaxSequenceNumber, kValueTypeForSeek));
|
||||
iter_->Seek(last_key.GetKey());
|
||||
}
|
||||
FindNextUserKey();
|
||||
direction_ = kForward;
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToFirst();
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::ReverseToBackward() {
|
||||
if (prefix_extractor_ != nullptr && !total_order_seek_) {
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(
|
||||
ParsedInternalKey(saved_key_.GetKey(), 0, kValueTypeForSeekForPrev));
|
||||
iter_->SeekForPrev(last_key.GetKey());
|
||||
}
|
||||
if (current_entry_is_merged_) {
|
||||
// Not placed in the same key. Need to call Prev() until finding the
|
||||
// previous key.
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToLast();
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
}
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
while (iter_->Valid() &&
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetKey()) > 0) {
|
||||
if (ikey.sequence > sequence_) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
} else {
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
}
|
||||
iter_->Prev();
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
}
|
||||
@@ -560,6 +669,11 @@ void DBIter::PrevInternal() {
|
||||
if (user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
FindPrevUserKey();
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_ &&
|
||||
prefix_extractor_->Transform(saved_key_.GetKey())
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
valid_ = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!iter_->Valid()) {
|
||||
@@ -571,6 +685,7 @@ void DBIter::PrevInternal() {
|
||||
}
|
||||
}
|
||||
// We haven't found any key - iterator is not valid
|
||||
// Or the prefix is different than start prefix
|
||||
assert(!iter_->Valid());
|
||||
valid_ = false;
|
||||
}
|
||||
@@ -590,6 +705,9 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
|
||||
// Temporarily pin blocks that hold (merge operands / the value)
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
size_t num_skipped = 0;
|
||||
while (iter_->Valid() && ikey.sequence <= sequence_ &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
@@ -601,11 +719,17 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
last_key_entry_type = ikey.type;
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeValue:
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
ikey,
|
||||
RangeDelAggregator::RangePositioningMode::kBackwardTraversal)) {
|
||||
last_key_entry_type = kTypeRangeDeletion;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
} else {
|
||||
assert(iter_->IsValuePinned());
|
||||
pinned_value_ = iter_->value();
|
||||
}
|
||||
merge_context_.Clear();
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
pinned_value_ = iter_->value();
|
||||
last_not_merge_type = kTypeValue;
|
||||
last_not_merge_type = last_key_entry_type;
|
||||
break;
|
||||
case kTypeDeletion:
|
||||
case kTypeSingleDeletion:
|
||||
@@ -614,8 +738,19 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
break;
|
||||
case kTypeMerge:
|
||||
assert(user_merge_operator_ != nullptr);
|
||||
merge_context_.PushOperandBack(iter_->value());
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
ikey,
|
||||
RangeDelAggregator::RangePositioningMode::kBackwardTraversal)) {
|
||||
merge_context_.Clear();
|
||||
last_key_entry_type = kTypeRangeDeletion;
|
||||
last_not_merge_type = last_key_entry_type;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
} else {
|
||||
assert(merge_operator_ != nullptr);
|
||||
merge_context_.PushOperandBack(
|
||||
iter_->value(), iter_->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@@ -628,32 +763,28 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
}
|
||||
|
||||
Status s;
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeSingleDeletion:
|
||||
case kTypeRangeDeletion:
|
||||
valid_ = false;
|
||||
return false;
|
||||
case kTypeMerge:
|
||||
current_entry_is_merged_ = true;
|
||||
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,
|
||||
merge_context_.GetOperands(),
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
if (last_not_merge_type == kTypeDeletion ||
|
||||
last_not_merge_type == kTypeSingleDeletion ||
|
||||
last_not_merge_type == kTypeRangeDeletion) {
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(),
|
||||
nullptr, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_);
|
||||
} else {
|
||||
assert(last_not_merge_type == kTypeValue);
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), &pinned_value_,
|
||||
merge_context_.GetOperands(),
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME,
|
||||
timer.ElapsedNanos());
|
||||
}
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetKey(), &pinned_value_,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_);
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
@@ -664,12 +795,18 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
break;
|
||||
}
|
||||
valid_ = true;
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This function is used in FindValueForCurrentKey.
|
||||
// We use Seek() function instead of Prev() to find necessary value
|
||||
bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
// FindValueForCurrentKey will enable pinning before calling
|
||||
// FindValueForCurrentKeyUsingSeek()
|
||||
assert(pinned_iters_mgr_.PinningEnabled());
|
||||
std::string last_key;
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
@@ -680,42 +817,46 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kForward);
|
||||
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeDeletion ||
|
||||
ikey.type == kTypeSingleDeletion) {
|
||||
if (ikey.type == kTypeValue) {
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
pinned_value_ = iter_->value();
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::kBackwardTraversal)) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (ikey.type == kTypeValue) {
|
||||
assert(iter_->IsValuePinned());
|
||||
pinned_value_ = iter_->value();
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// kTypeMerge. We need to collect all kTypeMerge values and save them
|
||||
// in operands
|
||||
current_entry_is_merged_ = true;
|
||||
merge_context_.Clear();
|
||||
while (iter_->Valid() &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey()) &&
|
||||
ikey.type == kTypeMerge) {
|
||||
merge_context_.PushOperand(iter_->value());
|
||||
while (
|
||||
iter_->Valid() &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey()) &&
|
||||
ikey.type == kTypeMerge &&
|
||||
!range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::kBackwardTraversal)) {
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
iter_->Next();
|
||||
FindParseableKey(&ikey, kForward);
|
||||
}
|
||||
|
||||
Status s;
|
||||
if (!iter_->Valid() ||
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey()) ||
|
||||
ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion) {
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), nullptr,
|
||||
merge_context_.GetOperands(),
|
||||
&saved_value_, logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME, timer.ElapsedNanos());
|
||||
}
|
||||
ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::kBackwardTraversal)) {
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(),
|
||||
nullptr, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_);
|
||||
// Make iter_ valid and point to saved_key_
|
||||
if (!iter_->Valid() ||
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
@@ -723,19 +864,20 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
}
|
||||
valid_ = true;
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const Slice& val = iter_->value();
|
||||
{
|
||||
StopWatchNano timer(env_, statistics_ != nullptr);
|
||||
PERF_TIMER_GUARD(merge_operator_time_nanos);
|
||||
user_merge_operator_->FullMerge(saved_key_.GetKey(), &val,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_);
|
||||
RecordTick(statistics_, MERGE_OPERATION_TOTAL_TIME, timer.ElapsedNanos());
|
||||
}
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), &val,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
valid_ = true;
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -780,6 +922,11 @@ void DBIter::FindPrevUserKey() {
|
||||
++num_skipped;
|
||||
}
|
||||
}
|
||||
if (ikey.sequence > sequence_) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
} else {
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
}
|
||||
iter_->Prev();
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
}
|
||||
@@ -800,14 +947,13 @@ void DBIter::Seek(const Slice& target) {
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
ReleaseTempPinnedData();
|
||||
saved_key_.Clear();
|
||||
// now savved_key is used to store internal key.
|
||||
saved_key_.SetInternalKey(target, sequence_);
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->Seek(saved_key_.GetKey());
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
}
|
||||
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (iter_->Valid()) {
|
||||
if (prefix_extractor_ && prefix_same_as_start_) {
|
||||
@@ -815,6 +961,9 @@ void DBIter::Seek(const Slice& target) {
|
||||
}
|
||||
direction_ = kForward;
|
||||
ClearSavedValue();
|
||||
// convert the InternalKey to UserKey in saved_key_ before
|
||||
// passed to FindNextUserEntry
|
||||
saved_key_.Reserve(saved_key_.Size() - 8);
|
||||
FindNextUserEntry(false /* not skipping */, prefix_same_as_start_);
|
||||
if (!valid_) {
|
||||
prefix_start_key_.clear();
|
||||
@@ -828,6 +977,47 @@ void DBIter::Seek(const Slice& target) {
|
||||
} else {
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_buf_.SetKey(prefix_start_key_);
|
||||
prefix_start_key_ = prefix_start_buf_.GetKey();
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::SeekForPrev(const Slice& target) {
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
ReleaseTempPinnedData();
|
||||
saved_key_.Clear();
|
||||
// now saved_key is used to store internal key.
|
||||
saved_key_.SetInternalKey(target, 0 /* sequence_number */,
|
||||
kValueTypeForSeekForPrev);
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->SeekForPrev(saved_key_.GetKey());
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
}
|
||||
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (iter_->Valid()) {
|
||||
if (prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_key_ = prefix_extractor_->Transform(target);
|
||||
}
|
||||
direction_ = kReverse;
|
||||
ClearSavedValue();
|
||||
PrevInternal();
|
||||
if (!valid_) {
|
||||
prefix_start_key_.clear();
|
||||
}
|
||||
if (statistics_ != nullptr) {
|
||||
if (valid_) {
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK_FOUND);
|
||||
RecordTick(statistics_, ITER_BYTES_READ, key().size() + value().size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valid_ = false;
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_buf_.SetKey(prefix_start_key_);
|
||||
prefix_start_key_ = prefix_start_buf_.GetKey();
|
||||
@@ -847,10 +1037,13 @@ void DBIter::SeekToFirst() {
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->SeekToFirst();
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
}
|
||||
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (iter_->Valid()) {
|
||||
saved_key_.SetKey(ExtractUserKey(iter_->key()),
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
FindNextUserEntry(false /* not skipping */, false /* no prefix check */);
|
||||
if (statistics_ != nullptr) {
|
||||
if (valid_) {
|
||||
@@ -880,30 +1073,22 @@ void DBIter::SeekToLast() {
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->SeekToLast();
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
}
|
||||
// When the iterate_upper_bound is set to a value,
|
||||
// it will seek to the last key before the
|
||||
// ReadOptions.iterate_upper_bound
|
||||
if (iter_->Valid() && iterate_upper_bound_ != nullptr) {
|
||||
saved_key_.SetKey(*iterate_upper_bound_, false /* copy */);
|
||||
std::string last_key;
|
||||
AppendInternalKey(&last_key,
|
||||
ParsedInternalKey(saved_key_.GetKey(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek));
|
||||
|
||||
iter_->Seek(last_key);
|
||||
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToLast();
|
||||
} else {
|
||||
iter_->Prev();
|
||||
if (!iter_->Valid()) {
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
SeekForPrev(*iterate_upper_bound_);
|
||||
range_del_agg_.InvalidateTombstoneMapPositions();
|
||||
if (!Valid()) {
|
||||
return;
|
||||
} else if (user_comparator_->Equal(*iterate_upper_bound_, key())) {
|
||||
Prev();
|
||||
}
|
||||
} else {
|
||||
PrevInternal();
|
||||
}
|
||||
PrevInternal();
|
||||
if (statistics_ != nullptr) {
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (valid_) {
|
||||
@@ -917,18 +1102,16 @@ void DBIter::SeekToLast() {
|
||||
}
|
||||
}
|
||||
|
||||
Iterator* NewDBIterator(Env* env, const ImmutableCFOptions& ioptions,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number,
|
||||
const Slice* iterate_upper_bound,
|
||||
bool prefix_same_as_start, bool pin_data) {
|
||||
DBIter* db_iter =
|
||||
new DBIter(env, ioptions, user_key_comparator, internal_iter, sequence,
|
||||
false, max_sequential_skip_in_iterations, version_number,
|
||||
iterate_upper_bound, prefix_same_as_start, pin_data);
|
||||
Iterator* NewDBIterator(
|
||||
Env* env, const ImmutableCFOptions& ioptions,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, const Slice* iterate_upper_bound,
|
||||
bool prefix_same_as_start, bool pin_data, bool total_order_seek) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, ioptions, user_key_comparator, internal_iter, sequence, false,
|
||||
max_sequential_skip_in_iterations, version_number, iterate_upper_bound,
|
||||
prefix_same_as_start, pin_data, total_order_seek);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
@@ -936,6 +1119,10 @@ ArenaWrappedDBIter::~ArenaWrappedDBIter() { db_iter_->~DBIter(); }
|
||||
|
||||
void ArenaWrappedDBIter::SetDBIter(DBIter* iter) { db_iter_ = iter; }
|
||||
|
||||
RangeDelAggregator* ArenaWrappedDBIter::GetRangeDelAggregator() {
|
||||
return db_iter_->GetRangeDelAggregator();
|
||||
}
|
||||
|
||||
void ArenaWrappedDBIter::SetIterUnderDBIter(InternalIterator* iter) {
|
||||
static_cast<DBIter*>(db_iter_)->SetIter(iter);
|
||||
}
|
||||
@@ -946,6 +1133,9 @@ inline void ArenaWrappedDBIter::SeekToLast() { db_iter_->SeekToLast(); }
|
||||
inline void ArenaWrappedDBIter::Seek(const Slice& target) {
|
||||
db_iter_->Seek(target);
|
||||
}
|
||||
inline void ArenaWrappedDBIter::SeekForPrev(const Slice& target) {
|
||||
db_iter_->SeekForPrev(target);
|
||||
}
|
||||
inline void ArenaWrappedDBIter::Next() { db_iter_->Next(); }
|
||||
inline void ArenaWrappedDBIter::Prev() { db_iter_->Prev(); }
|
||||
inline Slice ArenaWrappedDBIter::key() const { return db_iter_->key(); }
|
||||
@@ -964,15 +1154,15 @@ ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ImmutableCFOptions& ioptions,
|
||||
const Comparator* user_key_comparator, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound, bool prefix_same_as_start,
|
||||
bool pin_data) {
|
||||
const Slice* iterate_upper_bound, bool prefix_same_as_start, bool pin_data,
|
||||
bool total_order_seek) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
Arena* arena = iter->GetArena();
|
||||
auto mem = arena->AllocateAligned(sizeof(DBIter));
|
||||
DBIter* db_iter =
|
||||
new (mem) DBIter(env, ioptions, user_key_comparator, nullptr, sequence,
|
||||
true, max_sequential_skip_in_iterations, version_number,
|
||||
iterate_upper_bound, prefix_same_as_start, pin_data);
|
||||
DBIter* db_iter = new (mem) DBIter(
|
||||
env, ioptions, user_key_comparator, nullptr, sequence, true,
|
||||
max_sequential_skip_in_iterations, version_number, iterate_upper_bound,
|
||||
prefix_same_as_start, pin_data, total_order_seek);
|
||||
|
||||
iter->SetDBIter(db_iter);
|
||||
|
||||
|
||||
+9
-3
@@ -10,11 +10,13 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -30,7 +32,8 @@ extern Iterator* NewDBIterator(
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false);
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false);
|
||||
|
||||
// A wrapper iterator which wraps DB Iterator and the arena, with which the DB
|
||||
// iterator is supposed be allocated. This class is used as an entry point of
|
||||
@@ -44,6 +47,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
// Get the arena to be used to allocate memory for DBIter to be wrapped,
|
||||
// as well as child iterators in it.
|
||||
virtual Arena* GetArena() { return &arena_; }
|
||||
virtual RangeDelAggregator* GetRangeDelAggregator();
|
||||
|
||||
// Set the DB Iterator to be wrapped
|
||||
|
||||
@@ -56,6 +60,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
virtual Slice key() const override;
|
||||
@@ -76,6 +81,7 @@ extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
const Comparator* user_key_comparator, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false);
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false);
|
||||
|
||||
} // namespace rocksdb
|
||||
+122
-4
@@ -16,7 +16,7 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "table/iterator_wrapper.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testharness.h"
|
||||
@@ -117,6 +117,11 @@ class TestIterator : public InternalIterator {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SeekForPrev(const Slice& target) override {
|
||||
assert(initialized_);
|
||||
SeekForPrevImpl(target, &cmp);
|
||||
}
|
||||
|
||||
virtual void Next() override {
|
||||
assert(initialized_);
|
||||
if (data_.empty() || (iter_ == data_.size() - 1)) {
|
||||
@@ -150,6 +155,9 @@ class TestIterator : public InternalIterator {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual bool IsKeyPinned() const override { return true; }
|
||||
virtual bool IsValuePinned() const override { return true; }
|
||||
|
||||
private:
|
||||
bool initialized_;
|
||||
bool valid_;
|
||||
@@ -349,7 +357,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
db_iter->SeekToLast();
|
||||
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(static_cast<int>(perf_context.internal_key_skipped_count), 1);
|
||||
ASSERT_EQ(static_cast<int>(perf_context.internal_key_skipped_count), 7);
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
@@ -472,7 +480,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
db_iter->SeekToLast();
|
||||
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(static_cast<int>(perf_context.internal_delete_skipped_count), 0);
|
||||
ASSERT_EQ(static_cast<int>(perf_context.internal_delete_skipped_count), 1);
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
@@ -1121,6 +1129,26 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
}
|
||||
|
||||
{
|
||||
// put, singledelete, merge
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddPut("a", "val_a");
|
||||
internal_iter->AddSingleDeletion("a");
|
||||
internal_iter->AddMerge("a", "merge_1");
|
||||
internal_iter->AddMerge("a", "merge_2");
|
||||
internal_iter->AddPut("b", "val_b");
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ImmutableCFOptions(options), BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIterator6) {
|
||||
@@ -1723,6 +1751,15 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_1,merge_2");
|
||||
|
||||
db_iter->SeekForPrev("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_3,merge_4");
|
||||
db_iter->Next();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_5,merge_6");
|
||||
|
||||
db_iter->Seek("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
@@ -1731,6 +1768,15 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_3,merge_4");
|
||||
|
||||
db_iter->SeekForPrev("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_3,merge_4");
|
||||
db_iter->Next();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_5,merge_6");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1761,6 +1807,18 @@ TEST_F(DBIteratorTest, DBIterator10) {
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "3");
|
||||
|
||||
db_iter->SeekForPrev("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
db_iter->Next();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "4");
|
||||
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "3");
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, SeekToLastOccurrenceSeq0) {
|
||||
@@ -1839,6 +1897,66 @@ TEST_F(DBIteratorTest, DBIterator12) {
|
||||
ASSERT_FALSE(db_iter->Valid());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIterator13) {
|
||||
Options options;
|
||||
options.merge_operator = nullptr;
|
||||
|
||||
std::string key;
|
||||
key.resize(9);
|
||||
key.assign(9, static_cast<char>(0));
|
||||
key[0] = 'b';
|
||||
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddPut(key, "0");
|
||||
internal_iter->AddPut(key, "1");
|
||||
internal_iter->AddPut(key, "2");
|
||||
internal_iter->AddPut(key, "3");
|
||||
internal_iter->AddPut(key, "4");
|
||||
internal_iter->AddPut(key, "5");
|
||||
internal_iter->AddPut(key, "6");
|
||||
internal_iter->AddPut(key, "7");
|
||||
internal_iter->AddPut(key, "8");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 2, 3, 0));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), key);
|
||||
ASSERT_EQ(db_iter->value().ToString(), "2");
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIterator14) {
|
||||
Options options;
|
||||
options.merge_operator = nullptr;
|
||||
|
||||
std::string key("b");
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddPut("b", "0");
|
||||
internal_iter->AddPut("b", "1");
|
||||
internal_iter->AddPut("b", "2");
|
||||
internal_iter->AddPut("b", "3");
|
||||
internal_iter->AddPut("a", "4");
|
||||
internal_iter->AddPut("a", "5");
|
||||
internal_iter->AddPut("a", "6");
|
||||
internal_iter->AddPut("c", "7");
|
||||
internal_iter->AddPut("c", "8");
|
||||
internal_iter->AddPut("c", "9");
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(
|
||||
NewDBIterator(env_, ImmutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, 4, 1, 0));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "3");
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "4");
|
||||
}
|
||||
|
||||
class DBIterWithMergeIterTest : public testing::Test {
|
||||
public:
|
||||
DBIterWithMergeIterTest()
|
||||
@@ -1911,7 +2029,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIterator1) {
|
||||
|
||||
TEST_F(DBIterWithMergeIterTest, InnerMergeIterator2) {
|
||||
// Test Prev() when one child iterator is at its end.
|
||||
db_iter_->Seek("g");
|
||||
db_iter_->SeekForPrev("g");
|
||||
ASSERT_TRUE(db_iter_->Valid());
|
||||
ASSERT_EQ(db_iter_->key().ToString(), "g");
|
||||
ASSERT_EQ(db_iter_->value().ToString(), "3");
|
||||
|
||||
+255
-15
@@ -7,7 +7,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/iostats_context.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
@@ -203,6 +206,22 @@ TEST_F(DBIteratorTest, IterSeekBeforePrev) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterSeekForPrevBeforeNext) {
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("0", "f"));
|
||||
ASSERT_OK(Put("1", "h"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("2", "j"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
iter->SeekForPrev(Slice("0"));
|
||||
iter->Next();
|
||||
iter->SeekForPrev(Slice("1"));
|
||||
iter->Next();
|
||||
delete iter;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string MakeLongKey(size_t length, char c) {
|
||||
return std::string(length, c);
|
||||
@@ -230,6 +249,13 @@ TEST_F(DBIteratorTest, IterLongKeys) {
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(127, 3) + "->3");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(64, 4) + "->4");
|
||||
|
||||
iter->SeekForPrev(MakeLongKey(127, 3));
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(127, 3) + "->3");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(32, 2) + "->2");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(50, 1) + "->1");
|
||||
delete iter;
|
||||
|
||||
iter = db_->NewIterator(ReadOptions());
|
||||
@@ -260,6 +286,11 @@ TEST_F(DBIteratorTest, IterNextWithNewerSeq) {
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
iter->SeekForPrev(Slice("b"));
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
@@ -283,7 +314,13 @@ TEST_F(DBIteratorTest, IterPrevWithNewerSeq) {
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
|
||||
iter->Prev();
|
||||
iter->SeekForPrev(Slice("d"));
|
||||
ASSERT_EQ(IterStatus(iter), "d->e");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
iter->Prev();
|
||||
delete iter;
|
||||
}
|
||||
@@ -293,10 +330,13 @@ TEST_F(DBIteratorTest, IterPrevWithNewerSeq2) {
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
ASSERT_OK(Put("d", "e"));
|
||||
ASSERT_OK(Put("e", "f"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
auto iter2 = db_->NewIterator(ReadOptions());
|
||||
iter->Seek(Slice("c"));
|
||||
iter2->SeekForPrev(Slice("d"));
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
ASSERT_EQ(IterStatus(iter2), "c->d");
|
||||
|
||||
// Create a key that needs to be skipped for Seq too new
|
||||
for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;
|
||||
@@ -306,9 +346,12 @@ TEST_F(DBIteratorTest, IterPrevWithNewerSeq2) {
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
|
||||
iter->Prev();
|
||||
iter2->Prev();
|
||||
ASSERT_EQ(IterStatus(iter2), "a->b");
|
||||
iter2->Prev();
|
||||
delete iter;
|
||||
delete iter2;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterEmpty) {
|
||||
@@ -325,6 +368,9 @@ TEST_F(DBIteratorTest, IterEmpty) {
|
||||
iter->Seek("foo");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->SeekForPrev("foo");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
delete iter;
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
@@ -357,14 +403,24 @@ TEST_F(DBIteratorTest, IterSingle) {
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("a");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("a");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("b");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("b");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
delete iter;
|
||||
} while (ChangeCompactOptions());
|
||||
@@ -410,11 +466,21 @@ TEST_F(DBIteratorTest, IterMulti) {
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Seek("ax");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->SeekForPrev("c");
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->SeekForPrev("bx");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
|
||||
iter->Seek("b");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->Seek("z");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("b");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->SeekForPrev("");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
// Switch from reverse to forward
|
||||
iter->SeekToLast();
|
||||
@@ -602,7 +668,8 @@ TEST_F(DBIteratorTest, IterMultiWithDelete) {
|
||||
// TODO: merge operator does not support backward iteration yet
|
||||
if (kPlainTableAllBytesPrefix != option_config_ &&
|
||||
kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&
|
||||
kHashLinkList != option_config_) {
|
||||
kHashLinkList != option_config_ &&
|
||||
kHashSkipList != option_config_) { // doesn't support SeekToLast
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "ka->va");
|
||||
}
|
||||
@@ -657,6 +724,7 @@ TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
options.snapshot = snapshot;
|
||||
Iterator* iter = db_->NewIterator(options, handles_[1]);
|
||||
|
||||
ASSERT_OK(Put(1, "key0", "val0"));
|
||||
// Put more values after the snapshot
|
||||
ASSERT_OK(Put(1, "key100", "val100"));
|
||||
ASSERT_OK(Put(1, "key101", "val101"));
|
||||
@@ -667,7 +735,7 @@ TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
// TODO: merge operator does not support backward iteration yet
|
||||
if (kPlainTableAllBytesPrefix != option_config_ &&
|
||||
kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&
|
||||
kHashLinkList != option_config_) {
|
||||
kHashLinkList != option_config_ && kHashSkipList != option_config_) {
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "key4->val4");
|
||||
iter->Prev();
|
||||
@@ -681,6 +749,26 @@ TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
iter->Next();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
}
|
||||
|
||||
if (!CurrentOptions().merge_operator) {
|
||||
// TODO(gzh): merge operator does not support backward iteration yet
|
||||
if (kPlainTableAllBytesPrefix != option_config_ &&
|
||||
kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&
|
||||
kHashLinkList != option_config_ && kHashSkipList != option_config_) {
|
||||
iter->SeekForPrev("key1");
|
||||
ASSERT_EQ(IterStatus(iter), "key1->val1");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "key2->val2");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "key3->val3");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "key2->val2");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "key1->val1");
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
}
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
delete iter;
|
||||
// skip as HashCuckooRep does not support snapshot
|
||||
@@ -744,6 +832,19 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("g1")), 0);
|
||||
|
||||
iter->SeekForPrev("g1");
|
||||
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("g1")), 0);
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("foo1")), 0);
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("foo")), 0);
|
||||
}
|
||||
|
||||
// testing iterate_upper_bound and forward iterator
|
||||
@@ -876,7 +977,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
|
||||
// TODO(3.13): fix the issue of Seek() + Prev() which might not necessary
|
||||
// return the biggest key which is smaller than the seek key.
|
||||
TEST_F(DBIteratorTest, PrevAfterMerge) {
|
||||
TEST_F(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
@@ -897,6 +998,14 @@ TEST_F(DBIteratorTest, PrevAfterMerge) {
|
||||
it->Prev();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("1", it->key().ToString());
|
||||
|
||||
it->SeekForPrev("1");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("1", it->key().ToString());
|
||||
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("2", it->key().ToString());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
@@ -979,7 +1088,6 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
|
||||
{
|
||||
// Test Seek to random keys
|
||||
printf("Testing seek on %zu keys\n", random_keys.size());
|
||||
std::vector<Slice> keys_slices;
|
||||
std::vector<std::string> true_keys;
|
||||
for (auto& k : random_keys) {
|
||||
@@ -1001,9 +1109,31 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Test SeekForPrev to random keys
|
||||
std::vector<Slice> keys_slices;
|
||||
std::vector<std::string> true_keys;
|
||||
for (auto& k : random_keys) {
|
||||
iter->SeekForPrev(k);
|
||||
if (!iter->Valid()) {
|
||||
ASSERT_EQ(true_data.upper_bound(k), true_data.begin());
|
||||
continue;
|
||||
}
|
||||
std::string prop_value;
|
||||
ASSERT_OK(
|
||||
iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
|
||||
ASSERT_EQ("1", prop_value);
|
||||
keys_slices.push_back(iter->key());
|
||||
true_keys.push_back((--true_data.upper_bound(k))->first);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < keys_slices.size(); i++) {
|
||||
ASSERT_EQ(keys_slices[i].ToString(), true_keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Test iterating all data forward
|
||||
printf("Testing iterating forward on all keys\n");
|
||||
std::vector<Slice> all_keys;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
std::string prop_value;
|
||||
@@ -1024,7 +1154,6 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
|
||||
{
|
||||
// Test iterating all data backward
|
||||
printf("Testing iterating backward on all keys\n");
|
||||
std::vector<Slice> all_keys;
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
std::string prop_value;
|
||||
@@ -1229,6 +1358,60 @@ TEST_F(DBIteratorTest, PinnedDataIteratorReadAfterUpdate) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterSeekForPrevCrossingFiles) {
|
||||
Options options = CurrentOptions();
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.disable_auto_compactions = true;
|
||||
// Enable prefix bloom for SST files
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("a1", "va1"));
|
||||
ASSERT_OK(Put("a2", "va2"));
|
||||
ASSERT_OK(Put("a3", "va3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("b1", "vb1"));
|
||||
ASSERT_OK(Put("b2", "vb2"));
|
||||
ASSERT_OK(Put("b3", "vb3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("b4", "vb4"));
|
||||
ASSERT_OK(Put("d1", "vd1"));
|
||||
ASSERT_OK(Put("d2", "vd2"));
|
||||
ASSERT_OK(Put("d4", "vd4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
MoveFilesToLevel(1);
|
||||
{
|
||||
ReadOptions ro;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
|
||||
iter->SeekForPrev("a4");
|
||||
ASSERT_EQ(iter->key().ToString(), "a3");
|
||||
ASSERT_EQ(iter->value().ToString(), "va3");
|
||||
|
||||
iter->SeekForPrev("c2");
|
||||
ASSERT_EQ(iter->key().ToString(), "b3");
|
||||
iter->SeekForPrev("d3");
|
||||
ASSERT_EQ(iter->key().ToString(), "d2");
|
||||
iter->SeekForPrev("b5");
|
||||
ASSERT_EQ(iter->key().ToString(), "b4");
|
||||
delete iter;
|
||||
}
|
||||
|
||||
{
|
||||
ReadOptions ro;
|
||||
ro.prefix_same_as_start = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
iter->SeekForPrev("c2");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocks) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -1461,7 +1644,7 @@ TEST_F(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
std::atomic<uint64_t> total_prev_found(0);
|
||||
std::atomic<uint64_t> total_bytes(0);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
std::function<void()> reader_func_next = [&]() {
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
|
||||
@@ -1517,13 +1700,14 @@ TEST_F(DBIteratorTest, IteratorWithLocalStatistics) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_NEXT), total_next);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_NEXT), (uint64_t)total_next);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_NEXT_FOUND),
|
||||
total_next_found);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_PREV), total_prev);
|
||||
(uint64_t)total_next_found);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_PREV), (uint64_t)total_prev);
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_DB_PREV_FOUND),
|
||||
total_prev_found);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ITER_BYTES_READ), total_bytes);
|
||||
(uint64_t)total_prev_found);
|
||||
ASSERT_EQ(TestGetTickerCount(options, ITER_BYTES_READ), (uint64_t)total_bytes);
|
||||
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, ReadAhead) {
|
||||
@@ -1596,6 +1780,62 @@ TEST_F(DBIteratorTest, ReadAhead) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
// Insert a key, create a snapshot iterator, overwrite key lots of times,
|
||||
// seek to a smaller key. Expect DBIter to fall back to a seek instead of
|
||||
// going through all the overwrites linearly.
|
||||
TEST_F(DBIteratorTest, DBIteratorSkipRecentDuplicatesTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.max_sequential_skip_in_iterations = 3;
|
||||
options.prefix_extractor = nullptr;
|
||||
options.write_buffer_size = 1 << 27; // big enough to avoid flush
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Insert.
|
||||
ASSERT_OK(Put("b", "0"));
|
||||
|
||||
// Create iterator.
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
|
||||
// Insert a lot.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
ASSERT_OK(Put("b", std::to_string(i + 1).c_str()));
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Check that memtable wasn't flushed.
|
||||
std::string val;
|
||||
ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level0", &val));
|
||||
EXPECT_EQ("0", val);
|
||||
#endif
|
||||
|
||||
// Seek iterator to a smaller key.
|
||||
perf_context.Reset();
|
||||
iter->Seek("a");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ("b", iter->key().ToString());
|
||||
EXPECT_EQ("0", iter->value().ToString());
|
||||
|
||||
// Check that the seek didn't do too much work.
|
||||
// Checks are not tight, just make sure that everything is well below 100.
|
||||
EXPECT_LT(perf_context.internal_key_skipped_count, 4);
|
||||
EXPECT_LT(perf_context.internal_recent_skipped_count, 8);
|
||||
EXPECT_LT(perf_context.seek_on_memtable_count, 10);
|
||||
EXPECT_LT(perf_context.next_on_memtable_count, 10);
|
||||
EXPECT_LT(perf_context.prev_on_memtable_count, 10);
|
||||
|
||||
// Check that iterator did something like what we expect.
|
||||
EXPECT_EQ(perf_context.internal_delete_skipped_count, 0);
|
||||
EXPECT_EQ(perf_context.internal_merge_count, 0);
|
||||
EXPECT_GE(perf_context.internal_recent_skipped_count, 2);
|
||||
EXPECT_GE(perf_context.seek_on_memtable_count, 2);
|
||||
EXPECT_EQ(1, options.statistics->getTickerCount(
|
||||
NUMBER_OF_RESEEKS_IN_ITERATION));
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) 2011-present, 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/memtable.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBMemTableTest : public DBTestBase {
|
||||
public:
|
||||
DBMemTableTest() : DBTestBase("/db_memtable_test") {}
|
||||
};
|
||||
|
||||
class MockMemTableRep : public MemTableRep {
|
||||
public:
|
||||
explicit MockMemTableRep(MemTableAllocator* allocator, MemTableRep* rep)
|
||||
: MemTableRep(allocator), rep_(rep), num_insert_with_hint_(0) {}
|
||||
|
||||
virtual KeyHandle Allocate(const size_t len, char** buf) override {
|
||||
return rep_->Allocate(len, buf);
|
||||
}
|
||||
|
||||
virtual void Insert(KeyHandle handle) override {
|
||||
return rep_->Insert(handle);
|
||||
}
|
||||
|
||||
virtual void InsertWithHint(KeyHandle handle, void** hint) override {
|
||||
num_insert_with_hint_++;
|
||||
ASSERT_NE(nullptr, hint);
|
||||
last_hint_in_ = *hint;
|
||||
rep_->InsertWithHint(handle, hint);
|
||||
last_hint_out_ = *hint;
|
||||
}
|
||||
|
||||
virtual bool Contains(const char* key) const override {
|
||||
return rep_->Contains(key);
|
||||
}
|
||||
|
||||
virtual void Get(const LookupKey& k, void* callback_args,
|
||||
bool (*callback_func)(void* arg,
|
||||
const char* entry)) override {
|
||||
rep_->Get(k, callback_args, callback_func);
|
||||
}
|
||||
|
||||
virtual size_t ApproximateMemoryUsage() override {
|
||||
return rep_->ApproximateMemoryUsage();
|
||||
}
|
||||
|
||||
virtual Iterator* GetIterator(Arena* arena) override {
|
||||
return rep_->GetIterator(arena);
|
||||
}
|
||||
|
||||
void* last_hint_in() { return last_hint_in_; }
|
||||
void* last_hint_out() { return last_hint_out_; }
|
||||
int num_insert_with_hint() { return num_insert_with_hint_; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MemTableRep> rep_;
|
||||
void* last_hint_in_;
|
||||
void* last_hint_out_;
|
||||
int num_insert_with_hint_;
|
||||
};
|
||||
|
||||
class MockMemTableRepFactory : public MemTableRepFactory {
|
||||
public:
|
||||
virtual MemTableRep* CreateMemTableRep(const MemTableRep::KeyComparator& cmp,
|
||||
MemTableAllocator* allocator,
|
||||
const SliceTransform* transform,
|
||||
Logger* logger) override {
|
||||
SkipListFactory factory;
|
||||
MemTableRep* skiplist_rep =
|
||||
factory.CreateMemTableRep(cmp, allocator, transform, logger);
|
||||
mock_rep_ = new MockMemTableRep(allocator, skiplist_rep);
|
||||
return mock_rep_;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "MockMemTableRepFactory"; }
|
||||
|
||||
MockMemTableRep* rep() { return mock_rep_; }
|
||||
|
||||
bool IsInsertConcurrentlySupported() const override { return false; }
|
||||
|
||||
private:
|
||||
MockMemTableRep* mock_rep_;
|
||||
};
|
||||
|
||||
class TestPrefixExtractor : public SliceTransform {
|
||||
public:
|
||||
virtual const char* Name() const override { return "TestPrefixExtractor"; }
|
||||
|
||||
virtual Slice Transform(const Slice& key) const override {
|
||||
const char* p = separator(key);
|
||||
if (p == nullptr) {
|
||||
return Slice();
|
||||
}
|
||||
return Slice(key.data(), p - key.data() + 1);
|
||||
}
|
||||
|
||||
virtual bool InDomain(const Slice& key) const override {
|
||||
return separator(key) != nullptr;
|
||||
}
|
||||
|
||||
virtual bool InRange(const Slice& key) const override { return false; }
|
||||
|
||||
private:
|
||||
const char* separator(const Slice& key) const {
|
||||
return reinterpret_cast<const char*>(memchr(key.data(), '_', key.size()));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
Options options;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(new MockMemTableRepFactory());
|
||||
options.memtable_insert_with_hint_prefix_extractor.reset(
|
||||
new TestPrefixExtractor());
|
||||
Reopen(options);
|
||||
MockMemTableRep* rep =
|
||||
reinterpret_cast<MockMemTableRepFactory*>(options.memtable_factory.get())
|
||||
->rep();
|
||||
ASSERT_OK(Put("foo_k1", "foo_v1"));
|
||||
ASSERT_EQ(nullptr, rep->last_hint_in());
|
||||
void* hint_foo = rep->last_hint_out();
|
||||
ASSERT_OK(Put("foo_k2", "foo_v2"));
|
||||
ASSERT_EQ(hint_foo, rep->last_hint_in());
|
||||
ASSERT_EQ(hint_foo, rep->last_hint_out());
|
||||
ASSERT_OK(Put("foo_k3", "foo_v3"));
|
||||
ASSERT_EQ(hint_foo, rep->last_hint_in());
|
||||
ASSERT_EQ(hint_foo, rep->last_hint_out());
|
||||
ASSERT_OK(Put("bar_k1", "bar_v1"));
|
||||
ASSERT_EQ(nullptr, rep->last_hint_in());
|
||||
void* hint_bar = rep->last_hint_out();
|
||||
ASSERT_NE(hint_foo, hint_bar);
|
||||
ASSERT_OK(Put("bar_k2", "bar_v2"));
|
||||
ASSERT_EQ(hint_bar, rep->last_hint_in());
|
||||
ASSERT_EQ(hint_bar, rep->last_hint_out());
|
||||
ASSERT_EQ(5, rep->num_insert_with_hint());
|
||||
ASSERT_OK(Put("whitelisted", "vvv"));
|
||||
ASSERT_EQ(5, rep->num_insert_with_hint());
|
||||
ASSERT_EQ("foo_v1", Get("foo_k1"));
|
||||
ASSERT_EQ("foo_v2", Get("foo_k2"));
|
||||
ASSERT_EQ("foo_v3", Get("foo_k3"));
|
||||
ASSERT_EQ("bar_v1", Get("bar_k1"));
|
||||
ASSERT_EQ("bar_v2", Get("bar_k2"));
|
||||
ASSERT_EQ("vvv", Get("whitelisted"));
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2011-present, 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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/forward_iterator.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// Test merge operator functionality.
|
||||
class DBMergeOperatorTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperatorTest() : DBTestBase("/db_merge_operator_test") {}
|
||||
};
|
||||
|
||||
// A test merge operator mimics put but also fails if one of merge operands is
|
||||
// "corrupted".
|
||||
class TestPutOperator : public MergeOperator {
|
||||
public:
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
if (merge_in.existing_value != nullptr &&
|
||||
*(merge_in.existing_value) == "corrupted") {
|
||||
return false;
|
||||
}
|
||||
for (auto value : merge_in.operand_list) {
|
||||
if (value == "corrupted") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
merge_out->existing_operand = merge_in.operand_list.back();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return "TestPutOperator"; }
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnRead) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
Reopen(options);
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Merge("k1", "corrupted"));
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k1", &value).IsCorruption());
|
||||
VerifyDBInternal({{"k1", "corrupted"}, {"k1", "v1"}});
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnWrite) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
options.max_successive_merges = 3;
|
||||
Reopen(options);
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Merge("k1", "v2"));
|
||||
// Will trigger a merge when hitting max_successive_merges and the merge
|
||||
// will fail. The delta will be inserted nevertheless.
|
||||
ASSERT_OK(Merge("k1", "corrupted"));
|
||||
// Data should stay unmerged after the error.
|
||||
VerifyDBInternal({{"k1", "corrupted"}, {"k1", "v2"}, {"k1", "v1"}});
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnIteration) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator.reset(new TestPutOperator());
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Merge("k1", "corrupted"));
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
VerifyDBFromMap({{"k1", ""}, {"k2", "v2"}}, nullptr, false,
|
||||
{{"k1", Status::Corruption()}});
|
||||
VerifyDBInternal({{"k1", "corrupted"}, {"k1", "v1"}, {"k2", "v2"}});
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
ASSERT_OK(Merge("k2", "corrupted"));
|
||||
VerifyDBFromMap({{"k1", "v1"}, {"k2", ""}}, nullptr, false,
|
||||
{{"k2", Status::Corruption()}});
|
||||
VerifyDBInternal({{"k1", "v1"}, {"k2", "corrupted"}, {"k2", "v2"}});
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
// Copyright (c) 2011-present, 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.
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "util/options_helper.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBOptionsTest : public DBTestBase {
|
||||
public:
|
||||
DBOptionsTest() : DBTestBase("/db_options_test") {}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::unordered_map<std::string, std::string> GetMutableDBOptionsMap(
|
||||
const DBOptions& options) {
|
||||
std::string options_str;
|
||||
GetStringFromDBOptions(&options_str, options);
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : db_options_type_info) {
|
||||
if (opt.second.is_mutable &&
|
||||
opt.second.verification != OptionVerificationType::kDeprecated) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
}
|
||||
return mutable_map;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> GetMutableCFOptionsMap(
|
||||
const ColumnFamilyOptions& options) {
|
||||
std::string options_str;
|
||||
GetStringFromColumnFamilyOptions(&options_str, options);
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : cf_options_type_info) {
|
||||
if (opt.second.is_mutable &&
|
||||
opt.second.verification != OptionVerificationType::kDeprecated) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
}
|
||||
return mutable_map;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> GetRandomizedMutableCFOptionsMap(
|
||||
Random* rnd) {
|
||||
Options options;
|
||||
ImmutableDBOptions db_options(options);
|
||||
test::RandomInitCFOptions(&options, rnd);
|
||||
auto sanitized_options = SanitizeOptions(db_options, options);
|
||||
auto opt_map = GetMutableCFOptionsMap(sanitized_options);
|
||||
delete options.compaction_filter;
|
||||
return opt_map;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> GetRandomizedMutableDBOptionsMap(
|
||||
Random* rnd) {
|
||||
DBOptions db_options;
|
||||
test::RandomInitDBOptions(&db_options, rnd);
|
||||
auto sanitized_options = SanitizeOptions(dbname_, db_options);
|
||||
return GetMutableDBOptionsMap(sanitized_options);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
|
||||
// RocksDB lite don't support dynamic options.
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBOptionsTest, GetLatestDBOptions) {
|
||||
// GetOptions should be able to get latest option changed by SetOptions.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Random rnd(228);
|
||||
Reopen(options);
|
||||
auto new_options = GetRandomizedMutableDBOptionsMap(&rnd);
|
||||
ASSERT_OK(dbfull()->SetDBOptions(new_options));
|
||||
ASSERT_EQ(new_options, GetMutableDBOptionsMap(dbfull()->GetDBOptions()));
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, GetLatestCFOptions) {
|
||||
// GetOptions should be able to get latest option changed by SetOptions.
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
Random rnd(228);
|
||||
Reopen(options);
|
||||
CreateColumnFamilies({"foo"}, options);
|
||||
ReopenWithColumnFamilies({"default", "foo"}, options);
|
||||
auto options_default = GetRandomizedMutableCFOptionsMap(&rnd);
|
||||
auto options_foo = GetRandomizedMutableCFOptionsMap(&rnd);
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[0], options_default));
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[1], options_foo));
|
||||
ASSERT_EQ(options_default,
|
||||
GetMutableCFOptionsMap(dbfull()->GetOptions(handles_[0])));
|
||||
ASSERT_EQ(options_foo,
|
||||
GetMutableCFOptionsMap(dbfull()->GetOptions(handles_[1])));
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsAndReopen) {
|
||||
Random rnd(1044);
|
||||
auto rand_opts = GetRandomizedMutableCFOptionsMap(&rnd);
|
||||
ASSERT_OK(dbfull()->SetOptions(rand_opts));
|
||||
// Verify if DB can be reopen after setting options.
|
||||
Options options;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, EnableAutoCompactionAndTriggerStall) {
|
||||
const std::string kValue(1024, 'v');
|
||||
for (int method_type = 0; method_type < 2; method_type++) {
|
||||
for (int option_type = 0; option_type < 4; option_type++) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 1024 * 1024 * 10;
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.level0_stop_writes_trigger = std::numeric_limits<int>::max();
|
||||
options.level0_slowdown_writes_trigger = std::numeric_limits<int>::max();
|
||||
options.hard_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
options.soft_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int i = 0;
|
||||
for (; i < 1024; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
Flush();
|
||||
for (; i < 1024 * 2; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
uint64_t l0_size = SizeAtLevel(0);
|
||||
|
||||
switch (option_type) {
|
||||
case 0:
|
||||
// test with level0_stop_writes_trigger
|
||||
options.level0_stop_writes_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
break;
|
||||
case 1:
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
break;
|
||||
case 2:
|
||||
options.hard_pending_compaction_bytes_limit = l0_size;
|
||||
options.soft_pending_compaction_bytes_limit = l0_size;
|
||||
break;
|
||||
case 3:
|
||||
options.soft_pending_compaction_bytes_limit = l0_size;
|
||||
break;
|
||||
}
|
||||
Reopen(options);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBOptionsTest::EnableAutoCompactionAndTriggerStall:1",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"DBImpl::BackgroundCompaction():BeforePickCompaction",
|
||||
"DBOptionsTest::EnableAutoCompactionAndTriggerStall:2"},
|
||||
{"DBOptionsTest::EnableAutoCompactionAndTriggerStall:3",
|
||||
"DBImpl::BackgroundCompaction():AfterPickCompaction"}});
|
||||
// Block background compaction.
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
switch (method_type) {
|
||||
case 0:
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
break;
|
||||
case 1:
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction(
|
||||
{dbfull()->DefaultColumnFamily()}));
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:1");
|
||||
// Wait for stall condition recalculate.
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:2");
|
||||
|
||||
switch (option_type) {
|
||||
case 0:
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
break;
|
||||
case 1:
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
break;
|
||||
case 2:
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
break;
|
||||
case 3:
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:3");
|
||||
|
||||
// Background compaction executed.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsMayTriggerCompaction) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 1000;
|
||||
Reopen(options);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Need to insert two keys to avoid trivial move.
|
||||
ASSERT_OK(Put("foo", ToString(i)));
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
Flush();
|
||||
}
|
||||
ASSERT_EQ("3", FilesPerLevel());
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "3"}}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetBackgroundCompactionThreads) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.base_background_compactions = 1; // default value
|
||||
options.max_background_compactions = 1; // default value
|
||||
Reopen(options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"base_background_compactions", "2"},
|
||||
{"max_background_compactions", "3"}}));
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
auto stop_token = dbfull()->TEST_write_controler().GetStopToken();
|
||||
ASSERT_EQ(3, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, AvoidFlushDuringShutdown) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
WriteOptions write_without_wal;
|
||||
write_without_wal.disableWAL = true;
|
||||
|
||||
ASSERT_FALSE(options.avoid_flush_during_shutdown);
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "v1", write_without_wal));
|
||||
Reopen(options);
|
||||
ASSERT_EQ("v1", Get("foo"));
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "v2", write_without_wal));
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"avoid_flush_during_shutdown", "true"}}));
|
||||
Reopen(options);
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
ASSERT_EQ("", FilesPerLevel());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetDelayedWriteRateOption) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.delayed_write_rate = 2 * 1024U * 1024U;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(2 * 1024U * 1024U, dbfull()->TEST_write_controler().max_delayed_write_rate());
|
||||
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"delayed_write_rate", "20000"}}));
|
||||
ASSERT_EQ(20000, dbfull()->TEST_write_controler().max_delayed_write_rate());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, MaxTotalWalSizeChange) {
|
||||
Random rnd(1044);
|
||||
const auto value_size = size_t(1024);
|
||||
std::string value;
|
||||
test::RandomString(&rnd, value_size, &value);
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
CreateColumnFamilies({"1", "2", "3"}, options);
|
||||
ReopenWithColumnFamilies({"default", "1", "2", "3"}, options);
|
||||
|
||||
WriteOptions write_options;
|
||||
|
||||
const int key_count = 100;
|
||||
for (int i = 0; i < key_count; ++i) {
|
||||
for (size_t cf = 0; cf < handles_.size(); ++cf) {
|
||||
ASSERT_OK(Put(static_cast<int>(cf), Key(i), value));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"max_total_wal_size", "10"}}));
|
||||
|
||||
for (size_t cf = 0; cf < handles_.size(); ++cf) {
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[cf]);
|
||||
ASSERT_EQ("1", FilesPerLevel(static_cast<int>(cf)));
|
||||
}
|
||||
}
|
||||
|
||||
static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
dbfull->TEST_LockMutex();
|
||||
JobContext job_context(0);
|
||||
dbfull->FindObsoleteFiles(&job_context, false);
|
||||
ASSERT_EQ(empty, job_context.full_scan_candidate_files.empty());
|
||||
job_context.Clean();
|
||||
dbfull->TEST_UnlockMutex();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
SpecialEnv env(Env::Default());
|
||||
env.time_elapse_only_sleep_ = true;
|
||||
Options options;
|
||||
options.env = &env;
|
||||
options.create_if_missing = true;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
// Verify that candidate files set is empty when no full scan requested.
|
||||
assert_candidate_files_empty(dbfull(), true);
|
||||
|
||||
ASSERT_OK(
|
||||
dbfull()->SetDBOptions({{"delete_obsolete_files_period_micros", "0"}}));
|
||||
|
||||
// After delete_obsolete_files_period_micros updated to 0, the next call
|
||||
// to FindObsoleteFiles should make a full scan
|
||||
assert_candidate_files_empty(dbfull(), false);
|
||||
|
||||
ASSERT_OK(
|
||||
dbfull()->SetDBOptions({{"delete_obsolete_files_period_micros", "20"}}));
|
||||
|
||||
assert_candidate_files_empty(dbfull(), true);
|
||||
|
||||
env.addon_time_.store(20);
|
||||
assert_candidate_files_empty(dbfull(), true);
|
||||
|
||||
env.addon_time_.store(21);
|
||||
assert_candidate_files_empty(dbfull(), false);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -34,6 +34,7 @@ TEST_F(DBPropertiesTest, Empty) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000; // Small write buffer
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options = CurrentOptions(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
@@ -236,7 +237,7 @@ void GetExpectedTableProperties(TableProperties* expected_tp,
|
||||
expected_tp->data_size =
|
||||
kTableCount * (kKeysPerTable * (kKeySize + 8 + kValueSize));
|
||||
expected_tp->index_size =
|
||||
expected_tp->num_data_blocks * (kAvgSuccessorSize + 12);
|
||||
expected_tp->num_data_blocks * (kAvgSuccessorSize + 8);
|
||||
expected_tp->filter_size =
|
||||
kTableCount * (kKeysPerTable * kBloomBitsPerKey / 8);
|
||||
}
|
||||
@@ -491,6 +492,7 @@ TEST_F(DBPropertiesTest, NumImmutableMemTable) {
|
||||
|
||||
std::string big_value(1000000 * 2, 'x');
|
||||
std::string num;
|
||||
uint64_t value;
|
||||
SetPerfLevel(kEnableTime);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableTime);
|
||||
|
||||
@@ -555,11 +557,11 @@ TEST_F(DBPropertiesTest, NumImmutableMemTable) {
|
||||
ASSERT_TRUE(dbfull()->GetProperty(
|
||||
handles_[1], DB::Properties::kNumImmutableMemTableFlushed, &num));
|
||||
ASSERT_EQ(num, "3");
|
||||
ASSERT_TRUE(dbfull()->GetProperty(
|
||||
handles_[1], "rocksdb.cur-size-active-mem-table", &num));
|
||||
// "192" is the size of the metadata of an empty skiplist, this would
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty(
|
||||
handles_[1], "rocksdb.cur-size-active-mem-table", &value));
|
||||
// "192" is the size of the metadata of two empty skiplists, this would
|
||||
// break if we change the default skiplist implementation
|
||||
ASSERT_EQ(num, "192");
|
||||
ASSERT_GE(value, 192);
|
||||
|
||||
uint64_t int_num;
|
||||
uint64_t base_total_size;
|
||||
@@ -594,7 +596,8 @@ TEST_F(DBPropertiesTest, NumImmutableMemTable) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, GetProperty) {
|
||||
// TODO(techdept) : Disabled flaky test #12863555
|
||||
TEST_F(DBPropertiesTest, DISABLED_GetProperty) {
|
||||
// Set sizes to both background thread pool to be 1 and block them.
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
env_->SetBackgroundThreads(1, Env::LOW);
|
||||
|
||||
@@ -0,0 +1,818 @@
|
||||
// Copyright (c) 2016-present, 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 "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/testutil.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBRangeDelTest : public DBTestBase {
|
||||
public:
|
||||
DBRangeDelTest() : DBTestBase("/db_range_del_test") {}
|
||||
|
||||
std::string GetNumericStr(int key) {
|
||||
uint64_t uint64_key = static_cast<uint64_t>(key);
|
||||
std::string str;
|
||||
str.resize(8);
|
||||
memcpy(&str[0], static_cast<void*>(&uint64_key), 8);
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
// PlainTableFactory and NumTableFilesAtLevel() are not supported in
|
||||
// ROCKSDB_LITE
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBRangeDelTest, NonBlockBasedTableNotSupported) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_factory.reset(new PlainTableFactory());
|
||||
opts.prefix_extractor.reset(NewNoopTransform());
|
||||
opts.allow_mmap_reads = true;
|
||||
opts.max_sequential_skip_in_iterations = 999999;
|
||||
Reopen(opts);
|
||||
|
||||
ASSERT_TRUE(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "dr1", "dr1")
|
||||
.IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, FlushOutputHasOnlyRangeTombstones) {
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "dr1",
|
||||
"dr2"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, CompactionOutputHasOnlyRangeTombstone) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.statistics = CreateDBStatistics();
|
||||
Reopen(opts);
|
||||
|
||||
// snapshot protects range tombstone from dropping due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z");
|
||||
db_->Flush(FlushOptions());
|
||||
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr,
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(0, TestGetTickerCount(opts, COMPACTION_RANGE_DEL_DROP_OBSOLETE));
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, CompactionOutputFilesExactlyFilled) {
|
||||
// regression test for exactly filled compaction output files. Previously
|
||||
// another file would be generated containing all range deletions, which
|
||||
// could invalidate the non-overlapping file boundary invariant.
|
||||
const int kNumPerFile = 4, kNumFiles = 2, kFileBytes = 9 << 10;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_file_num_compaction_trigger = kNumFiles;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
options.num_levels = 2;
|
||||
options.target_file_size_base = kFileBytes;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size_deviation = 50; // each block holds two keys
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
// snapshot protects range tombstone from dropping due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), Key(0), Key(1));
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
std::vector<std::string> values;
|
||||
// Write 12K (4 values, each 3K)
|
||||
for (int j = 0; j < kNumPerFile; j++) {
|
||||
values.push_back(RandomString(&rnd, 3 << 10));
|
||||
ASSERT_OK(Put(Key(i * kNumPerFile + j), values[j]));
|
||||
if (j == 0 && i > 0) {
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
// put extra key to trigger final flush
|
||||
ASSERT_OK(Put("", ""));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr,
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(1));
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, MaxCompactionBytesCutsOutputFiles) {
|
||||
// Ensures range deletion spanning multiple compaction output files that are
|
||||
// cut by max_compaction_bytes will have non-overlapping key-ranges.
|
||||
// https://github.com/facebook/rocksdb/issues/1778
|
||||
const int kNumFiles = 2, kNumPerFile = 1 << 8, kBytesPerVal = 1 << 12;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.level0_file_num_compaction_trigger = kNumFiles;
|
||||
opts.max_compaction_bytes = kNumPerFile * kBytesPerVal;
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
// Want max_compaction_bytes to trigger the end of compaction output file, not
|
||||
// target_file_size_base, so make the latter much bigger
|
||||
opts.target_file_size_base = 100 * opts.max_compaction_bytes;
|
||||
Reopen(opts);
|
||||
|
||||
// snapshot protects range tombstone from dropping due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// It spans the whole key-range, thus will be included in all output files
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
GetNumericStr(0),
|
||||
GetNumericStr(kNumFiles * kNumPerFile - 1)));
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
std::vector<std::string> values;
|
||||
// Write 1MB (256 values, each 4K)
|
||||
for (int j = 0; j < kNumPerFile; j++) {
|
||||
values.push_back(RandomString(&rnd, kBytesPerVal));
|
||||
ASSERT_OK(Put(GetNumericStr(kNumPerFile * i + j), values[j]));
|
||||
}
|
||||
// extra entry to trigger SpecialSkipListFactory's flush
|
||||
ASSERT_OK(Put(GetNumericStr(kNumPerFile), ""));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr,
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GE(NumTableFilesAtLevel(1), 2);
|
||||
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
|
||||
for (size_t i = 0; i < files[1].size() - 1; ++i) {
|
||||
ASSERT_TRUE(InternalKeyComparator(opts.comparator)
|
||||
.Compare(files[1][i].largest, files[1][i + 1].smallest) <
|
||||
0);
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, SentinelsOmittedFromOutputFile) {
|
||||
// Regression test for bug where sentinel range deletions (i.e., ones with
|
||||
// sequence number of zero) were included in output files.
|
||||
// snapshot protects range tombstone from dropping due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// gaps between ranges creates sentinels in our internal representation
|
||||
std::vector<std::pair<std::string, std::string>> range_dels = {{"a", "b"}, {"c", "d"}, {"e", "f"}};
|
||||
for (const auto& range_del : range_dels) {
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
range_del.first, range_del.second));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
ASSERT_GT(files[0][0].smallest_seqno, 0);
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, FlushRangeDelsSameStartKey) {
|
||||
db_->Put(WriteOptions(), "b1", "val");
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "c"));
|
||||
db_->Put(WriteOptions(), "b2", "val");
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "b"));
|
||||
// first iteration verifies query correctness in memtable, second verifies
|
||||
// query correctness for a single SST file
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i > 0) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "b1", &value).IsNotFound());
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "b2", &value));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, CompactRangeDelsSameStartKey) {
|
||||
db_->Put(WriteOptions(), "unused", "val"); // prevents empty after compaction
|
||||
db_->Put(WriteOptions(), "b1", "val");
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "c"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "b"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
ASSERT_EQ(3, NumTableFilesAtLevel(0));
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i > 0) {
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr,
|
||||
true /* disallow_trivial_move */);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
}
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "b1", &value).IsNotFound());
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBRangeDelTest, FlushRemovesCoveredKeys) {
|
||||
const int kNum = 300, kRangeBegin = 50, kRangeEnd = 250;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
Reopen(opts);
|
||||
|
||||
// Write a third before snapshot, a third between snapshot and tombstone, and
|
||||
// a third after the tombstone. Keys older than snapshot or newer than the
|
||||
// tombstone should be preserved.
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
if (i == kNum / 3) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
} else if (i == 2 * kNum / 3) {
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
GetNumericStr(kRangeBegin), GetNumericStr(kRangeEnd));
|
||||
}
|
||||
db_->Put(WriteOptions(), GetNumericStr(i), "val");
|
||||
}
|
||||
db_->Flush(FlushOptions());
|
||||
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
ReadOptions read_opts;
|
||||
read_opts.ignore_range_deletions = true;
|
||||
std::string value;
|
||||
if (i < kRangeBegin || i > kRangeEnd || i < kNum / 3 || i >= 2 * kNum / 3) {
|
||||
ASSERT_OK(db_->Get(read_opts, GetNumericStr(i), &value));
|
||||
} else {
|
||||
ASSERT_TRUE(db_->Get(read_opts, GetNumericStr(i), &value).IsNotFound());
|
||||
}
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
// NumTableFilesAtLevel() is not supported in ROCKSDB_LITE
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBRangeDelTest, CompactionRemovesCoveredKeys) {
|
||||
const int kNumPerFile = 100, kNumFiles = 4;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
opts.num_levels = 2;
|
||||
opts.statistics = CreateDBStatistics();
|
||||
Reopen(opts);
|
||||
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
if (i > 0) {
|
||||
// range tombstone covers first half of the previous file
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
GetNumericStr((i - 1) * kNumPerFile),
|
||||
GetNumericStr((i - 1) * kNumPerFile + kNumPerFile / 2));
|
||||
}
|
||||
// Make sure a given key appears in each file so compaction won't be able to
|
||||
// use trivial move, which would happen if the ranges were non-overlapping.
|
||||
// Also, we need an extra element since flush is only triggered when the
|
||||
// number of keys is one greater than SpecialSkipListFactory's limit.
|
||||
// We choose a key outside the key-range used by the test to avoid conflict.
|
||||
db_->Put(WriteOptions(), GetNumericStr(kNumPerFile * kNumFiles), "val");
|
||||
|
||||
for (int j = 0; j < kNumPerFile; ++j) {
|
||||
db_->Put(WriteOptions(), GetNumericStr(i * kNumPerFile + j), "val");
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ((kNumFiles - 1) * kNumPerFile / 2,
|
||||
TestGetTickerCount(opts, COMPACTION_KEY_DROP_RANGE_DEL));
|
||||
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
for (int j = 0; j < kNumPerFile; ++j) {
|
||||
ReadOptions read_opts;
|
||||
read_opts.ignore_range_deletions = true;
|
||||
std::string value;
|
||||
if (i == kNumFiles - 1 || j >= kNumPerFile / 2) {
|
||||
ASSERT_OK(
|
||||
db_->Get(read_opts, GetNumericStr(i * kNumPerFile + j), &value));
|
||||
} else {
|
||||
ASSERT_TRUE(
|
||||
db_->Get(read_opts, GetNumericStr(i * kNumPerFile + j), &value)
|
||||
.IsNotFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, ValidLevelSubcompactionBoundaries) {
|
||||
const int kNumPerFile = 100, kNumFiles = 4, kFileBytes = 100 << 10;
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumFiles;
|
||||
options.max_bytes_for_level_base = 2 * kFileBytes;
|
||||
options.max_subcompactions = 4;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
options.num_levels = 3;
|
||||
options.target_file_size_base = kFileBytes;
|
||||
options.target_file_size_multiplier = 1;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int j = 0; j < kNumFiles; ++j) {
|
||||
if (i > 0) {
|
||||
// delete [95,105) in two files, [295,305) in next two
|
||||
int mid = (j + (1 - j % 2)) * kNumPerFile;
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(mid - 5), Key(mid + 5));
|
||||
}
|
||||
std::vector<std::string> values;
|
||||
// Write 100KB (100 values, each 1K)
|
||||
for (int k = 0; k < kNumPerFile; k++) {
|
||||
values.push_back(RandomString(&rnd, 990));
|
||||
ASSERT_OK(Put(Key(j * kNumPerFile + k), values[k]));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put("", ""));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
if (j < kNumFiles - 1) {
|
||||
// background compaction may happen early for kNumFiles'th file
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), j + 1);
|
||||
}
|
||||
if (j == options.level0_file_num_compaction_trigger - 1) {
|
||||
// When i == 1, compaction will output some files to L1, at which point
|
||||
// L1 is not bottommost so range deletions cannot be compacted away. The
|
||||
// new L1 files must be generated with non-overlapping key ranges even
|
||||
// though multiple subcompactions see the same ranges deleted, else an
|
||||
// assertion will fail.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(2), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, ValidUniversalSubcompactionBoundaries) {
|
||||
const int kNumPerFile = 100, kFilesPerLevel = 4, kNumLevels = 4;
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_options_universal.min_merge_width = kFilesPerLevel;
|
||||
options.compaction_options_universal.max_merge_width = kFilesPerLevel;
|
||||
options.compaction_options_universal.size_ratio = 10;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.level0_file_num_compaction_trigger = kFilesPerLevel;
|
||||
options.max_subcompactions = 4;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
options.num_levels = kNumLevels;
|
||||
options.target_file_size_base = kNumPerFile << 10;
|
||||
options.target_file_size_multiplier = 1;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumLevels - 1; ++i) {
|
||||
for (int j = 0; j < kFilesPerLevel; ++j) {
|
||||
if (i == kNumLevels - 2) {
|
||||
// insert range deletions [95,105) in two files, [295,305) in next two
|
||||
// to prepare L1 for later manual compaction.
|
||||
int mid = (j + (1 - j % 2)) * kNumPerFile;
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(mid - 5), Key(mid + 5));
|
||||
}
|
||||
std::vector<std::string> values;
|
||||
// Write 100KB (100 values, each 1K)
|
||||
for (int k = 0; k < kNumPerFile; k++) {
|
||||
values.push_back(RandomString(&rnd, 990));
|
||||
ASSERT_OK(Put(Key(j * kNumPerFile + k), values[k]));
|
||||
}
|
||||
// put extra key to trigger flush
|
||||
ASSERT_OK(Put("", ""));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
if (j < kFilesPerLevel - 1) {
|
||||
// background compaction may happen early for kFilesPerLevel'th file
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), j + 1);
|
||||
}
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_GT(NumTableFilesAtLevel(kNumLevels - 1 - i), kFilesPerLevel - 1);
|
||||
}
|
||||
// Now L1-L3 are full, when we compact L1->L2 we should see (1) subcompactions
|
||||
// happen since input level > 0; (2) range deletions are not dropped since
|
||||
// output level is not bottommost. If no file boundary assertion fails, that
|
||||
// probably means universal compaction + subcompaction + range deletion are
|
||||
// compatible.
|
||||
ASSERT_OK(dbfull()->RunManualCompaction(
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
->cfd(),
|
||||
1 /* input_level */, 2 /* output_level */, 0 /* output_path_id */,
|
||||
nullptr /* begin */, nullptr /* end */, true /* exclusive */,
|
||||
true /* disallow_trivial_move */));
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBRangeDelTest, CompactionRemovesCoveredMergeOperands) {
|
||||
const int kNumPerFile = 3, kNumFiles = 3;
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(2 * kNumPerFile));
|
||||
opts.merge_operator = MergeOperators::CreateUInt64AddOperator();
|
||||
opts.num_levels = 2;
|
||||
Reopen(opts);
|
||||
|
||||
// Iterates kNumFiles * kNumPerFile + 1 times since flushing the last file
|
||||
// requires an extra entry.
|
||||
for (int i = 0; i <= kNumFiles * kNumPerFile; ++i) {
|
||||
if (i % kNumPerFile == 0 && i / kNumPerFile == kNumFiles - 1) {
|
||||
// Delete merge operands from all but the last file
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "key",
|
||||
"key_");
|
||||
}
|
||||
std::string val;
|
||||
PutFixed64(&val, i);
|
||||
db_->Merge(WriteOptions(), "key", val);
|
||||
// we need to prevent trivial move using Puts so compaction will actually
|
||||
// process the merge operands.
|
||||
db_->Put(WriteOptions(), "prevent_trivial_move", "");
|
||||
if (i > 0 && i % kNumPerFile == 0) {
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
}
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.ignore_range_deletions = true;
|
||||
std::string expected, actual;
|
||||
ASSERT_OK(db_->Get(read_opts, "key", &actual));
|
||||
PutFixed64(&expected, 45); // 1+2+...+9
|
||||
ASSERT_EQ(expected, actual);
|
||||
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
expected.clear();
|
||||
ASSERT_OK(db_->Get(read_opts, "key", &actual));
|
||||
uint64_t tmp;
|
||||
Slice tmp2(actual);
|
||||
GetFixed64(&tmp2, &tmp);
|
||||
PutFixed64(&expected, 30); // 6+7+8+9 (earlier operands covered by tombstone)
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
// NumTableFilesAtLevel() is not supported in ROCKSDB_LITE
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBRangeDelTest, ObsoleteTombstoneCleanup) {
|
||||
// During compaction to bottommost level, verify range tombstones older than
|
||||
// the oldest snapshot are removed, while others are preserved.
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.num_levels = 2;
|
||||
opts.statistics = CreateDBStatistics();
|
||||
Reopen(opts);
|
||||
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "dr1",
|
||||
"dr1"); // obsolete after compaction
|
||||
db_->Put(WriteOptions(), "key", "val");
|
||||
db_->Flush(FlushOptions());
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "dr2",
|
||||
"dr2"); // protected by snapshot
|
||||
db_->Put(WriteOptions(), "key", "val");
|
||||
db_->Flush(FlushOptions());
|
||||
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(1));
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(1));
|
||||
ASSERT_EQ(1, TestGetTickerCount(opts, COMPACTION_RANGE_DEL_DROP_OBSOLETE));
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, TableEvictedDuringScan) {
|
||||
// The RangeDelAggregator holds pointers into range deletion blocks created by
|
||||
// table readers. This test ensures the aggregator can still access those
|
||||
// blocks even if it outlives the table readers that created them.
|
||||
//
|
||||
// DBIter always keeps readers open for L0 files. So, in order to test
|
||||
// aggregator outliving reader, we need to have deletions in L1 files, which
|
||||
// are opened/closed on-demand during the scan. This is accomplished by
|
||||
// setting kNumRanges > level0_stop_writes_trigger, which prevents deletions
|
||||
// from all lingering in L0 (there is at most one range deletion per L0 file).
|
||||
//
|
||||
// The first L1 file will contain a range deletion since its begin key is 0.
|
||||
// SeekToFirst() references that table's reader and adds its range tombstone
|
||||
// to the aggregator. Upon advancing beyond that table's key-range via Next(),
|
||||
// the table reader will be unreferenced by the iterator. Since we manually
|
||||
// call Evict() on all readers before the full scan, this unreference causes
|
||||
// the reader's refcount to drop to zero and thus be destroyed.
|
||||
//
|
||||
// When it is destroyed, we do not remove its range deletions from the
|
||||
// aggregator. So, subsequent calls to Next() must be able to use these
|
||||
// deletions to decide whether a key is covered. This will work as long as
|
||||
// the aggregator properly references the range deletion block.
|
||||
const int kNum = 25, kRangeBegin = 0, kRangeEnd = 7, kNumRanges = 5;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.level0_file_num_compaction_trigger = 4;
|
||||
opts.level0_stop_writes_trigger = 4;
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(1));
|
||||
opts.num_levels = 2;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.block_cache = NewLRUCache(8 << 20);
|
||||
opts.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(opts);
|
||||
|
||||
// Hold a snapshot so range deletions can't become obsolete during compaction
|
||||
// to bottommost level (i.e., L1).
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
db_->Put(WriteOptions(), GetNumericStr(i), "val");
|
||||
if (i > 0) {
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
if (i >= kNum / 2 && i < kNum / 2 + kNumRanges) {
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
GetNumericStr(kRangeBegin), GetNumericStr(kRangeEnd));
|
||||
}
|
||||
}
|
||||
// Must be > 1 so the first L1 file can be closed before scan finishes
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 1);
|
||||
std::vector<uint64_t> file_numbers = ListTableFiles(env_, dbname_);
|
||||
|
||||
ReadOptions read_opts;
|
||||
auto* iter = db_->NewIterator(read_opts);
|
||||
int expected = kRangeEnd;
|
||||
iter->SeekToFirst();
|
||||
for (auto file_number : file_numbers) {
|
||||
// This puts table caches in the state of being externally referenced only
|
||||
// so they are destroyed immediately upon iterator unreferencing.
|
||||
TableCache::Evict(dbfull()->TEST_table_cache(), file_number);
|
||||
}
|
||||
for (; iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(GetNumericStr(expected), iter->key());
|
||||
++expected;
|
||||
// Keep clearing block cache's LRU so range deletion block can be freed as
|
||||
// soon as its refcount drops to zero.
|
||||
bbto.block_cache->EraseUnRefEntries();
|
||||
}
|
||||
ASSERT_EQ(kNum, expected);
|
||||
delete iter;
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetCoveredKeyFromMutableMemtable) {
|
||||
db_->Put(WriteOptions(), "key", "val");
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(read_opts, "key", &value).IsNotFound());
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetCoveredKeyFromImmutableMemtable) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.max_write_buffer_number = 3;
|
||||
opts.min_write_buffer_number_to_merge = 2;
|
||||
// SpecialSkipListFactory lets us specify maximum number of elements the
|
||||
// memtable can hold. It switches the active memtable to immutable (flush is
|
||||
// prevented by the above options) upon inserting an element that would
|
||||
// overflow the memtable.
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(1));
|
||||
Reopen(opts);
|
||||
|
||||
db_->Put(WriteOptions(), "key", "val");
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
db_->Put(WriteOptions(), "blah", "val");
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(read_opts, "key", &value).IsNotFound());
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetCoveredKeyFromSst) {
|
||||
db_->Put(WriteOptions(), "key", "val");
|
||||
// snapshot prevents key from being deleted during flush
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(read_opts, "key", &value).IsNotFound());
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetCoveredMergeOperandFromMemtable) {
|
||||
const int kNumMergeOps = 10;
|
||||
Options opts = CurrentOptions();
|
||||
opts.merge_operator = MergeOperators::CreateUInt64AddOperator();
|
||||
Reopen(opts);
|
||||
|
||||
for (int i = 0; i < kNumMergeOps; ++i) {
|
||||
std::string val;
|
||||
PutFixed64(&val, i);
|
||||
db_->Merge(WriteOptions(), "key", val);
|
||||
if (i == kNumMergeOps / 2) {
|
||||
// deletes [0, 5]
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "key",
|
||||
"key_");
|
||||
}
|
||||
}
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string expected, actual;
|
||||
ASSERT_OK(db_->Get(read_opts, "key", &actual));
|
||||
PutFixed64(&expected, 30); // 6+7+8+9
|
||||
ASSERT_EQ(expected, actual);
|
||||
|
||||
expected.clear();
|
||||
read_opts.ignore_range_deletions = true;
|
||||
ASSERT_OK(db_->Get(read_opts, "key", &actual));
|
||||
PutFixed64(&expected, 45); // 0+1+2+...+9
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, GetIgnoresRangeDeletions) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.max_write_buffer_number = 4;
|
||||
opts.min_write_buffer_number_to_merge = 3;
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(1));
|
||||
Reopen(opts);
|
||||
|
||||
db_->Put(WriteOptions(), "sst_key", "val");
|
||||
// snapshot prevents key from being deleted during flush
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
db_->Put(WriteOptions(), "imm_key", "val");
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
db_->Put(WriteOptions(), "mem_key", "val");
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.ignore_range_deletions = true;
|
||||
for (std::string key : {"sst_key", "imm_key", "mem_key"}) {
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(read_opts, key, &value));
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, IteratorRemovesCoveredKeys) {
|
||||
const int kNum = 200, kRangeBegin = 50, kRangeEnd = 150, kNumPerFile = 25;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
Reopen(opts);
|
||||
|
||||
// Write half of the keys before the tombstone and half after the tombstone.
|
||||
// Only covered keys (i.e., within the range and older than the tombstone)
|
||||
// should be deleted.
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
if (i == kNum / 2) {
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
GetNumericStr(kRangeBegin), GetNumericStr(kRangeEnd));
|
||||
}
|
||||
db_->Put(WriteOptions(), GetNumericStr(i), "val");
|
||||
}
|
||||
ReadOptions read_opts;
|
||||
auto* iter = db_->NewIterator(read_opts);
|
||||
|
||||
int expected = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(GetNumericStr(expected), iter->key());
|
||||
if (expected == kRangeBegin - 1) {
|
||||
expected = kNum / 2;
|
||||
} else {
|
||||
++expected;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(kNum, expected);
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, IteratorOverUserSnapshot) {
|
||||
const int kNum = 200, kRangeBegin = 50, kRangeEnd = 150, kNumPerFile = 25;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
Reopen(opts);
|
||||
|
||||
const Snapshot* snapshot = nullptr;
|
||||
// Put a snapshot before the range tombstone, verify an iterator using that
|
||||
// snapshot sees all inserted keys.
|
||||
for (int i = 0; i < kNum; ++i) {
|
||||
if (i == kNum / 2) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
GetNumericStr(kRangeBegin), GetNumericStr(kRangeEnd));
|
||||
}
|
||||
db_->Put(WriteOptions(), GetNumericStr(i), "val");
|
||||
}
|
||||
ReadOptions read_opts;
|
||||
read_opts.snapshot = snapshot;
|
||||
auto* iter = db_->NewIterator(read_opts);
|
||||
|
||||
int expected = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(GetNumericStr(expected), iter->key());
|
||||
++expected;
|
||||
}
|
||||
ASSERT_EQ(kNum / 2, expected);
|
||||
delete iter;
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, IteratorIgnoresRangeDeletions) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.max_write_buffer_number = 4;
|
||||
opts.min_write_buffer_number_to_merge = 3;
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(1));
|
||||
Reopen(opts);
|
||||
|
||||
db_->Put(WriteOptions(), "sst_key", "val");
|
||||
// snapshot prevents key from being deleted during flush
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
db_->Put(WriteOptions(), "imm_key", "val");
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
db_->Put(WriteOptions(), "mem_key", "val");
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
|
||||
ReadOptions read_opts;
|
||||
read_opts.ignore_range_deletions = true;
|
||||
auto* iter = db_->NewIterator(read_opts);
|
||||
int i = 0;
|
||||
std::string expected[] = {"imm_key", "mem_key", "sst_key"};
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), ++i) {
|
||||
std::string key;
|
||||
ASSERT_EQ(expected[i], iter->key());
|
||||
}
|
||||
ASSERT_EQ(3, i);
|
||||
delete iter;
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, TailingIteratorRangeTombstoneUnsupported) {
|
||||
db_->Put(WriteOptions(), "key", "val");
|
||||
// snapshot prevents key from being deleted during flush
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "a", "z"));
|
||||
|
||||
// iterations check unsupported in memtable, l0, and then l1
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
ReadOptions read_opts;
|
||||
read_opts.tailing = true;
|
||||
auto* iter = db_->NewIterator(read_opts);
|
||||
if (i == 2) {
|
||||
// For L1+, iterators over files are created on-demand, so need seek
|
||||
iter->SeekToFirst();
|
||||
}
|
||||
ASSERT_TRUE(iter->status().IsNotSupported());
|
||||
delete iter;
|
||||
if (i == 0) {
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
} else if (i == 1) {
|
||||
MoveFilesToLevel(1);
|
||||
}
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
rocksdb::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+71
-546
@@ -8,9 +8,9 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/sst_file_manager.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "util/sst_file_manager_impl.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -52,6 +52,47 @@ TEST_F(DBSSTTest, DontDeletePendingOutputs) {
|
||||
Compact("a", "b");
|
||||
}
|
||||
|
||||
// 1 Create some SST files by inserting K-V pairs into DB
|
||||
// 2 Close DB and change suffix from ".sst" to ".ldb" for every other SST file
|
||||
// 3 Open DB and check if all key can be read
|
||||
TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 110 << 10; // 110KB
|
||||
options.num_levels = 4;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
int key_id = 0;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
GenerateNewFile(&rnd, &key_id, false);
|
||||
}
|
||||
Flush();
|
||||
Close();
|
||||
int const num_files = GetSstFileCount(dbname_);
|
||||
ASSERT_GT(num_files, 0);
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
GetSstFiles(dbname_, &filenames);
|
||||
int num_ldb_files = 0;
|
||||
for (size_t i = 0; i < filenames.size(); ++i) {
|
||||
if (i & 1) {
|
||||
continue;
|
||||
}
|
||||
std::string const rdb_name = dbname_ + "/" + filenames[i];
|
||||
std::string const ldb_name = Rocks2LevelTableFileName(rdb_name);
|
||||
ASSERT_TRUE(env_->RenameFile(rdb_name, ldb_name).ok());
|
||||
++num_ldb_files;
|
||||
}
|
||||
ASSERT_GT(num_ldb_files, 0);
|
||||
ASSERT_EQ(num_files, GetSstFileCount(dbname_));
|
||||
|
||||
Reopen(options);
|
||||
for (int k = 0; k < key_id; ++k) {
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(k)));
|
||||
}
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBSSTTest, DontDeleteMovedFile) {
|
||||
// This test triggers move compaction and verifies that the file is not
|
||||
@@ -144,12 +185,13 @@ TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) {
|
||||
}
|
||||
};
|
||||
env_->table_write_callback_ = &block_first_time;
|
||||
// Create 1MB sst file
|
||||
// Insert 2.5MB data, which should trigger a flush because we exceed
|
||||
// write_buffer_size. The flush will be blocked with block_first_time
|
||||
// pending_file is protecting all the files created after
|
||||
for (int j = 0; j < 256; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 10 * 1024)));
|
||||
}
|
||||
// this should trigger a flush, which is blocked with block_first_time
|
||||
// pending_file is protecting all the files created after
|
||||
blocking_thread.WaitUntilSleeping();
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(2, nullptr, nullptr));
|
||||
|
||||
@@ -179,8 +221,6 @@ TEST_F(DBSSTTest, DeleteObsoleteFilesPendingOutputs) {
|
||||
listener->VerifyMatchedCount(1);
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBSSTTest, DBWithSstFileManager) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(sst_file_manager.get());
|
||||
@@ -245,7 +285,6 @@ TEST_F(DBSSTTest, DBWithSstFileManager) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
Destroy(last_options_);
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
@@ -257,8 +296,31 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DeleteScheduler::BackgroundEmptyTrash:Wait",
|
||||
[&](void* arg) { penalties.push_back(*(static_cast<int*>(arg))); });
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
// Turn timed wait into a simulated sleep
|
||||
uint64_t* abs_time_us = static_cast<uint64_t*>(arg);
|
||||
int64_t cur_time = 0;
|
||||
env_->GetCurrentTime(&cur_time);
|
||||
if (*abs_time_us > static_cast<uint64_t>(cur_time)) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us -
|
||||
static_cast<uint64_t>(cur_time));
|
||||
}
|
||||
|
||||
// Randomly sleep shortly
|
||||
env_->addon_time_.fetch_add(
|
||||
static_cast<uint64_t>(Random::GetTLSInstance()->Uniform(10)));
|
||||
|
||||
// Set wait until time to before current to force not to sleep.
|
||||
int64_t real_cur_time = 0;
|
||||
Env::Default()->GetCurrentTime(&real_cur_time);
|
||||
*abs_time_us = static_cast<uint64_t>(real_cur_time);
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
env_->no_slowdown_ = true;
|
||||
env_->time_elapse_only_sleep_ = true;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
@@ -306,6 +368,7 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
ASSERT_EQ(expected_penlty, penalties[i]);
|
||||
}
|
||||
ASSERT_GT(time_spent_deleting, expected_penlty * 0.9);
|
||||
ASSERT_LT(time_spent_deleting, expected_penlty * 1.1);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
@@ -416,7 +479,6 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
|
||||
// We have deleted the 4 sst files in the delete_scheduler
|
||||
ASSERT_EQ(bg_delete_file, 4);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBSSTTest, DBWithMaxSpaceAllowed) {
|
||||
std::shared_ptr<SstFileManager> sst_file_manager(NewSstFileManager(env_));
|
||||
@@ -514,7 +576,6 @@ TEST_F(DBSSTTest, DBWithMaxSpaceAllowedRandomized) {
|
||||
ASSERT_GT(reached_max_space_on_compaction, 0);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) {
|
||||
// Open DB with infinite max open files
|
||||
// - First iteration use 1 thread to open files
|
||||
@@ -552,7 +613,7 @@ TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) {
|
||||
}
|
||||
Close();
|
||||
|
||||
// Reopening the DB will load all exisitng files
|
||||
// Reopening the DB will load all existing files
|
||||
Reopen(options);
|
||||
ASSERT_EQ("12,0,12", FilesPerLevel(0));
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
@@ -748,544 +809,8 @@ TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
ASSERT_EQ(total_sst_files_size, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, AddExternalSstFile) {
|
||||
do {
|
||||
std::string sst_files_folder = test::TmpDir(env_) + "/sst_files/";
|
||||
env_->CreateDir(sst_files_folder);
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
|
||||
// file1.sst (0 => 99)
|
||||
std::string file1 = sst_files_folder + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(0));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(99));
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
s = sst_file_writer.Add(Key(100), "bad_val");
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
// file2.sst (100 => 199)
|
||||
std::string file2 = sst_files_folder + "file2.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
for (int k = 100; k < 200; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
// Cannot add this key because it's not after last added key
|
||||
s = sst_file_writer.Add(Key(99), "bad_val");
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ExternalSstFileInfo file2_info;
|
||||
s = sst_file_writer.Finish(&file2_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file2_info.file_path, file2);
|
||||
ASSERT_EQ(file2_info.num_entries, 100);
|
||||
ASSERT_EQ(file2_info.smallest_key, Key(100));
|
||||
ASSERT_EQ(file2_info.largest_key, Key(199));
|
||||
|
||||
// file3.sst (195 => 299)
|
||||
// This file values overlap with file2 values
|
||||
std::string file3 = sst_files_folder + "file3.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file3));
|
||||
for (int k = 195; k < 300; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k) + "_val_overlap"));
|
||||
}
|
||||
ExternalSstFileInfo file3_info;
|
||||
s = sst_file_writer.Finish(&file3_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file3_info.file_path, file3);
|
||||
ASSERT_EQ(file3_info.num_entries, 105);
|
||||
ASSERT_EQ(file3_info.smallest_key, Key(195));
|
||||
ASSERT_EQ(file3_info.largest_key, Key(299));
|
||||
|
||||
// file4.sst (30 => 39)
|
||||
// This file values overlap with file1 values
|
||||
std::string file4 = sst_files_folder + "file4.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file4));
|
||||
for (int k = 30; k < 40; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k) + "_val_overlap"));
|
||||
}
|
||||
ExternalSstFileInfo file4_info;
|
||||
s = sst_file_writer.Finish(&file4_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file4_info.file_path, file4);
|
||||
ASSERT_EQ(file4_info.num_entries, 10);
|
||||
ASSERT_EQ(file4_info.smallest_key, Key(30));
|
||||
ASSERT_EQ(file4_info.largest_key, Key(39));
|
||||
|
||||
// file5.sst (400 => 499)
|
||||
std::string file5 = sst_files_folder + "file5.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file5));
|
||||
for (int k = 400; k < 500; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file5_info;
|
||||
s = sst_file_writer.Finish(&file5_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file5_info.file_path, file5);
|
||||
ASSERT_EQ(file5_info.num_entries, 100);
|
||||
ASSERT_EQ(file5_info.smallest_key, Key(400));
|
||||
ASSERT_EQ(file5_info.largest_key, Key(499));
|
||||
|
||||
// Cannot create an empty sst file
|
||||
std::string file_empty = sst_files_folder + "file_empty.sst";
|
||||
ExternalSstFileInfo file_empty_info;
|
||||
s = sst_file_writer.Finish(&file_empty_info);
|
||||
ASSERT_NOK(s);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Add file using file path
|
||||
s = db_->AddFile(file1);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
|
||||
// Add file while holding a snapshot will fail
|
||||
const Snapshot* s1 = db_->GetSnapshot();
|
||||
if (s1 != nullptr) {
|
||||
ASSERT_NOK(db_->AddFile(&file2_info));
|
||||
db_->ReleaseSnapshot(s1);
|
||||
}
|
||||
// We can add the file after releaseing the snapshot
|
||||
ASSERT_OK(db_->AddFile(&file2_info));
|
||||
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
for (int k = 0; k < 200; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
|
||||
// This file have overlapping values with the exisitng data
|
||||
s = db_->AddFile(file3);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
// This file have overlapping values with the exisitng data
|
||||
s = db_->AddFile(&file4_info);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
// Overwrite values of keys divisible by 5
|
||||
for (int k = 0; k < 200; k += 5) {
|
||||
ASSERT_OK(Put(Key(k), Key(k) + "_val_new"));
|
||||
}
|
||||
ASSERT_NE(db_->GetLatestSequenceNumber(), 0U);
|
||||
|
||||
// Key range of file5 (400 => 499) dont overlap with any keys in DB
|
||||
ASSERT_OK(db_->AddFile(file5));
|
||||
|
||||
// Make sure values are correct before and after flush/compaction
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int k = 0; k < 200; k++) {
|
||||
std::string value = Key(k) + "_val";
|
||||
if (k % 5 == 0) {
|
||||
value += "_new";
|
||||
}
|
||||
ASSERT_EQ(Get(Key(k)), value);
|
||||
}
|
||||
for (int k = 400; k < 500; k++) {
|
||||
std::string value = Key(k) + "_val";
|
||||
ASSERT_EQ(Get(Key(k)), value);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
Close();
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
|
||||
// Delete keys in range (400 => 499)
|
||||
for (int k = 400; k < 500; k++) {
|
||||
ASSERT_OK(Delete(Key(k)));
|
||||
}
|
||||
// We deleted range (400 => 499) but cannot add file5 because
|
||||
// of the range tombstones
|
||||
ASSERT_NOK(db_->AddFile(file5));
|
||||
|
||||
// Compacting the DB will remove the tombstones
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// Now we can add the file
|
||||
ASSERT_OK(db_->AddFile(file5));
|
||||
|
||||
// Verify values of file5 in DB
|
||||
for (int k = 400; k < 500; k++) {
|
||||
std::string value = Key(k) + "_val";
|
||||
ASSERT_EQ(Get(Key(k)), value);
|
||||
}
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
// This test reporduce a bug that can happen in some cases if the DB started
|
||||
// purging obsolete files when we are adding an external sst file.
|
||||
// This situation may result in deleting the file while it's being added.
|
||||
TEST_F(DBSSTTest, AddExternalSstFilePurgeObsoleteFilesBug) {
|
||||
std::string sst_files_folder = test::TmpDir(env_) + "/sst_files/";
|
||||
env_->CreateDir(sst_files_folder);
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
|
||||
// file1.sst (0 => 500)
|
||||
std::string sst_file_path = sst_files_folder + "file1.sst";
|
||||
Status s = sst_file_writer.Open(sst_file_path);
|
||||
ASSERT_OK(s);
|
||||
for (int i = 0; i < 500; i++) {
|
||||
std::string k = Key(i);
|
||||
s = sst_file_writer.Add(k, k + "_val");
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
|
||||
ExternalSstFileInfo sst_file_info;
|
||||
s = sst_file_writer.Finish(&sst_file_info);
|
||||
ASSERT_OK(s);
|
||||
|
||||
options.delete_obsolete_files_period_micros = 0;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::AddFile:FileCopied", [&](void* arg) {
|
||||
ASSERT_OK(Put("aaa", "bbb"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("aaa", "xxx"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
s = db_->AddFile(sst_file_path);
|
||||
ASSERT_OK(s);
|
||||
|
||||
for (int i = 0; i < 500; i++) {
|
||||
std::string k = Key(i);
|
||||
std::string v = k + "_val";
|
||||
ASSERT_EQ(Get(k), v);
|
||||
}
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, AddExternalSstFileNoCopy) {
|
||||
std::string sst_files_folder = test::TmpDir(env_) + "/sst_files/";
|
||||
env_->CreateDir(sst_files_folder);
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
|
||||
// file1.sst (0 => 99)
|
||||
std::string file1 = sst_files_folder + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(0));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(99));
|
||||
|
||||
// file2.sst (100 => 299)
|
||||
std::string file2 = sst_files_folder + "file2.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
for (int k = 100; k < 300; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file2_info;
|
||||
s = sst_file_writer.Finish(&file2_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file2_info.file_path, file2);
|
||||
ASSERT_EQ(file2_info.num_entries, 200);
|
||||
ASSERT_EQ(file2_info.smallest_key, Key(100));
|
||||
ASSERT_EQ(file2_info.largest_key, Key(299));
|
||||
|
||||
// file3.sst (110 => 124) .. overlap with file2.sst
|
||||
std::string file3 = sst_files_folder + "file3.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file3));
|
||||
for (int k = 110; k < 125; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k) + "_val_overlap"));
|
||||
}
|
||||
ExternalSstFileInfo file3_info;
|
||||
s = sst_file_writer.Finish(&file3_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file3_info.file_path, file3);
|
||||
ASSERT_EQ(file3_info.num_entries, 15);
|
||||
ASSERT_EQ(file3_info.smallest_key, Key(110));
|
||||
ASSERT_EQ(file3_info.largest_key, Key(124));
|
||||
|
||||
s = db_->AddFile(&file1_info, true /* move file */);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(Status::NotFound(), env_->FileExists(file1));
|
||||
|
||||
s = db_->AddFile(&file2_info, false /* copy file */);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file2));
|
||||
|
||||
// This file have overlapping values with the exisitng data
|
||||
s = db_->AddFile(&file3_info, true /* move file */);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file3));
|
||||
|
||||
for (int k = 0; k < 300; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, AddExternalSstFileMultiThreaded) {
|
||||
std::string sst_files_folder = test::TmpDir(env_) + "/sst_files/";
|
||||
// Bulk load 10 files every file contain 1000 keys
|
||||
int num_files = 10;
|
||||
int keys_per_file = 1000;
|
||||
|
||||
// Generate file names
|
||||
std::vector<std::string> file_names;
|
||||
for (int i = 0; i < num_files; i++) {
|
||||
std::string file_name = "file_" + ToString(i) + ".sst";
|
||||
file_names.push_back(sst_files_folder + file_name);
|
||||
}
|
||||
|
||||
do {
|
||||
env_->CreateDir(sst_files_folder);
|
||||
Options options = CurrentOptions();
|
||||
|
||||
std::atomic<int> thread_num(0);
|
||||
std::function<void()> write_file_func = [&]() {
|
||||
int file_idx = thread_num.fetch_add(1);
|
||||
int range_start = file_idx * keys_per_file;
|
||||
int range_end = range_start + keys_per_file;
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
|
||||
ASSERT_OK(sst_file_writer.Open(file_names[file_idx]));
|
||||
|
||||
for (int k = range_start; k < range_end; k++) {
|
||||
ASSERT_OK(sst_file_writer.Add(Key(k), Key(k)));
|
||||
}
|
||||
|
||||
Status s = sst_file_writer.Finish();
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
};
|
||||
// Write num_files files in parallel
|
||||
std::vector<std::thread> sst_writer_threads;
|
||||
for (int i = 0; i < num_files; ++i) {
|
||||
sst_writer_threads.emplace_back(write_file_func);
|
||||
}
|
||||
|
||||
for (auto& t : sst_writer_threads) {
|
||||
t.join();
|
||||
}
|
||||
|
||||
fprintf(stderr, "Wrote %d files (%d keys)\n", num_files,
|
||||
num_files * keys_per_file);
|
||||
|
||||
thread_num.store(0);
|
||||
std::atomic<int> files_added(0);
|
||||
std::function<void()> load_file_func = [&]() {
|
||||
// We intentionally add every file twice, and assert that it was added
|
||||
// only once and the other add failed
|
||||
int thread_id = thread_num.fetch_add(1);
|
||||
int file_idx = thread_id / 2;
|
||||
// sometimes we use copy, sometimes link .. the result should be the same
|
||||
bool move_file = (thread_id % 3 == 0);
|
||||
|
||||
Status s = db_->AddFile(file_names[file_idx], move_file);
|
||||
if (s.ok()) {
|
||||
files_added++;
|
||||
}
|
||||
};
|
||||
// Bulk load num_files files in parallel
|
||||
std::vector<std::thread> add_file_threads;
|
||||
DestroyAndReopen(options);
|
||||
for (int i = 0; i < num_files * 2; ++i) {
|
||||
add_file_threads.emplace_back(load_file_func);
|
||||
}
|
||||
|
||||
for (auto& t : add_file_threads) {
|
||||
t.join();
|
||||
}
|
||||
ASSERT_EQ(files_added.load(), num_files);
|
||||
fprintf(stderr, "Loaded %d files (%d keys)\n", num_files,
|
||||
num_files * keys_per_file);
|
||||
|
||||
// Overwrite values of keys divisible by 100
|
||||
for (int k = 0; k < num_files * keys_per_file; k += 100) {
|
||||
std::string key = Key(k);
|
||||
Status s = Put(key, key + "_new");
|
||||
ASSERT_TRUE(s.ok());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Make sure the values are correct before and after flush/compaction
|
||||
for (int k = 0; k < num_files * keys_per_file; ++k) {
|
||||
std::string key = Key(k);
|
||||
std::string value = (k % 100 == 0) ? (key + "_new") : key;
|
||||
ASSERT_EQ(Get(key), value);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
fprintf(stderr, "Verified %d values\n", num_files * keys_per_file);
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, AddExternalSstFileOverlappingRanges) {
|
||||
std::string sst_files_folder = test::TmpDir(env_) + "/sst_files/";
|
||||
Random rnd(301);
|
||||
do {
|
||||
env_->CreateDir(sst_files_folder);
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
|
||||
printf("Option config = %d\n", option_config_);
|
||||
std::vector<std::pair<int, int>> key_ranges;
|
||||
for (int i = 0; i < 500; i++) {
|
||||
int range_start = rnd.Uniform(20000);
|
||||
int keys_per_range = 10 + rnd.Uniform(41);
|
||||
|
||||
key_ranges.emplace_back(range_start, range_start + keys_per_range);
|
||||
}
|
||||
|
||||
int memtable_add = 0;
|
||||
int success_add_file = 0;
|
||||
int failed_add_file = 0;
|
||||
std::map<std::string, std::string> true_data;
|
||||
for (size_t i = 0; i < key_ranges.size(); i++) {
|
||||
int range_start = key_ranges[i].first;
|
||||
int range_end = key_ranges[i].second;
|
||||
|
||||
Status s;
|
||||
std::string range_val = "range_" + ToString(i);
|
||||
|
||||
// For 20% of ranges we use DB::Put, for 80% we use DB::AddFile
|
||||
if (i && i % 5 == 0) {
|
||||
// Use DB::Put to insert range (insert into memtable)
|
||||
range_val += "_put";
|
||||
for (int k = range_start; k <= range_end; k++) {
|
||||
s = Put(Key(k), range_val);
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
memtable_add++;
|
||||
} else {
|
||||
// Use DB::AddFile to insert range
|
||||
range_val += "_add_file";
|
||||
|
||||
// Generate the file containing the range
|
||||
std::string file_name = sst_files_folder + env_->GenerateUniqueId();
|
||||
ASSERT_OK(sst_file_writer.Open(file_name));
|
||||
for (int k = range_start; k <= range_end; k++) {
|
||||
s = sst_file_writer.Add(Key(k), range_val);
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ExternalSstFileInfo file_info;
|
||||
s = sst_file_writer.Finish(&file_info);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Insert the generated file
|
||||
s = db_->AddFile(&file_info);
|
||||
|
||||
auto it = true_data.lower_bound(Key(range_start));
|
||||
if (it != true_data.end() && it->first <= Key(range_end)) {
|
||||
// This range overlap with data already exist in DB
|
||||
ASSERT_NOK(s);
|
||||
failed_add_file++;
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
success_add_file++;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
// Update true_data map to include the new inserted data
|
||||
for (int k = range_start; k <= range_end; k++) {
|
||||
true_data[Key(k)] = range_val;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush / Compact the DB
|
||||
if (i && i % 50 == 0) {
|
||||
Flush();
|
||||
}
|
||||
if (i && i % 75 == 0) {
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
printf(
|
||||
"Total: %zu ranges\n"
|
||||
"AddFile()|Success: %d ranges\n"
|
||||
"AddFile()|RangeConflict: %d ranges\n"
|
||||
"Put(): %d ranges\n",
|
||||
key_ranges.size(), success_add_file, failed_add_file, memtable_add);
|
||||
|
||||
// Verify the correctness of the data
|
||||
for (const auto& kv : true_data) {
|
||||
ASSERT_EQ(Get(kv.first), kv.second);
|
||||
}
|
||||
printf("keys/values verified\n");
|
||||
} while (ChangeOptions(kSkipPlainTable | kSkipUniversalCompaction |
|
||||
kSkipFIFOCompaction));
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// 1 Create some SST files by inserting K-V pairs into DB
|
||||
// 2 Close DB and change suffix from ".sst" to ".ldb" for every other SST file
|
||||
// 3 Open DB and check if all key can be read
|
||||
TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 110 << 10; // 110KB
|
||||
options.num_levels = 4;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
int key_id = 0;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
GenerateNewFile(&rnd, &key_id, false);
|
||||
}
|
||||
Flush();
|
||||
Close();
|
||||
int const num_files = GetSstFileCount(dbname_);
|
||||
ASSERT_GT(num_files, 0);
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
GetSstFiles(dbname_, &filenames);
|
||||
int num_ldb_files = 0;
|
||||
for (size_t i = 0; i < filenames.size(); ++i) {
|
||||
if (i & 1) {
|
||||
continue;
|
||||
}
|
||||
std::string const rdb_name = dbname_ + "/" + filenames[i];
|
||||
std::string const ldb_name = Rocks2LevelTableFileName(rdb_name);
|
||||
ASSERT_TRUE(env_->RenameFile(rdb_name, ldb_name).ok());
|
||||
++num_ldb_files;
|
||||
}
|
||||
ASSERT_GT(num_ldb_files, 0);
|
||||
ASSERT_EQ(num_files, GetSstFileCount(dbname_));
|
||||
|
||||
Reopen(options);
|
||||
for (int k = 0; k < key_id; ++k) {
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(k)));
|
||||
}
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -138,14 +138,14 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 4096;
|
||||
options.max_write_buffer_number = 8;
|
||||
options.max_write_buffer_number = 3;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
options.level0_stop_writes_trigger = 4;
|
||||
options.target_file_size_base = 2048;
|
||||
options.max_bytes_for_level_base = 10240;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.soft_rate_limit = 1.1;
|
||||
options.hard_pending_compaction_bytes_limit = 16 * 1024;
|
||||
options.num_levels = 8;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
@@ -155,6 +155,12 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), RandomString(&rnd, 102)));
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
if (NumTableFilesAtLevel(0) == 0) {
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), RandomString(&rnd, 102)));
|
||||
Flush();
|
||||
}
|
||||
|
||||
db_->PauseBackgroundWork();
|
||||
|
||||
// Ensure that we have at least L0, L1 and L2
|
||||
|
||||
@@ -305,6 +305,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorPrefixSeek) {
|
||||
options.disable_auto_compactions = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
@@ -428,6 +429,59 @@ TEST_F(DBTestTailingIterator, TailingIteratorUpperBound) {
|
||||
ASSERT_EQ(0, immutable_seeks);
|
||||
}
|
||||
|
||||
TEST_F(DBTestTailingIterator, TailingIteratorGap) {
|
||||
// level 1: [20, 25] [35, 40]
|
||||
// level 2: [10 - 15] [45 - 50]
|
||||
// level 3: [20, 30, 40]
|
||||
// Previously there is a bug in tailing_iterator that if there is a gap in
|
||||
// lower level, the key will be skipped if it is within the range between
|
||||
// the largest key of index n file and the smallest key of index n+1 file
|
||||
// if both file fit in that gap. In this example, 25 < key < 35
|
||||
// https://github.com/facebook/rocksdb/issues/1372
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
|
||||
ASSERT_OK(Put(1, "20", "20"));
|
||||
ASSERT_OK(Put(1, "30", "30"));
|
||||
ASSERT_OK(Put(1, "40", "40"));
|
||||
ASSERT_OK(Flush(1));
|
||||
MoveFilesToLevel(3, 1);
|
||||
|
||||
ASSERT_OK(Put(1, "10", "10"));
|
||||
ASSERT_OK(Put(1, "15", "15"));
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "45", "45"));
|
||||
ASSERT_OK(Put(1, "50", "50"));
|
||||
ASSERT_OK(Flush(1));
|
||||
MoveFilesToLevel(2, 1);
|
||||
|
||||
ASSERT_OK(Put(1, "20", "20"));
|
||||
ASSERT_OK(Put(1, "25", "25"));
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "35", "35"));
|
||||
ASSERT_OK(Put(1, "40", "40"));
|
||||
ASSERT_OK(Flush(1));
|
||||
MoveFilesToLevel(1, 1);
|
||||
|
||||
ColumnFamilyMetaData meta;
|
||||
db_->GetColumnFamilyMetaData(handles_[1], &meta);
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
||||
it->Seek("30");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("30", it->key().ToString());
|
||||
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("35", it->key().ToString());
|
||||
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("40", it->key().ToString());
|
||||
}
|
||||
|
||||
TEST_F(DBTestTailingIterator, ManagedTailingIteratorSingle) {
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
@@ -572,6 +626,7 @@ TEST_F(DBTestTailingIterator, ManagedTailingIteratorPrefixSeek) {
|
||||
options.disable_auto_compactions = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
@@ -699,6 +754,58 @@ TEST_F(DBTestTailingIterator, ForwardIteratorVersionProperty) {
|
||||
}
|
||||
ASSERT_EQ(v3, v4);
|
||||
}
|
||||
|
||||
TEST_F(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
const Slice upper_bound("cc", 3);
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
|
||||
|
||||
// 1st L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// 2nd L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "zz", "NOT-SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
|
||||
iter->Seek("aa");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||
}
|
||||
|
||||
TEST_F(DBTestTailingIterator, SeekToFirstWithUpperBoundBug) {
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
const Slice upper_bound("cc", 3);
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
|
||||
|
||||
// 1st L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// 2nd L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "zz", "NOT-SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
|
||||
+292
-191
@@ -31,6 +31,7 @@
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -197,6 +198,19 @@ TEST_F(DBTest, MemEnvTest) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, OpenWhenOpen) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
rocksdb::DB* db2 = nullptr;
|
||||
rocksdb::Status s = DB::Open(options, dbname_, &db2);
|
||||
|
||||
ASSERT_EQ(Status::Code::kIOError, s.code());
|
||||
ASSERT_EQ(Status::SubCode::kNone, s.subcode());
|
||||
ASSERT_TRUE(strstr(s.getState(), "lock ") != nullptr);
|
||||
|
||||
delete db2;
|
||||
}
|
||||
|
||||
TEST_F(DBTest, WriteEmptyBatch) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -215,6 +229,50 @@ TEST_F(DBTest, WriteEmptyBatch) {
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest, SkipDelay) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.write_buffer_size = 100000;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
for (bool sync : {true, false}) {
|
||||
for (bool disableWAL : {true, false}) {
|
||||
// Use a small number to ensure a large delay that is still effective
|
||||
// when we do Put
|
||||
// TODO(myabandeh): this is time dependent and could potentially make
|
||||
// the test flaky
|
||||
auto token = dbfull()->TEST_write_controler().GetDelayToken(1);
|
||||
std::atomic<int> sleep_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:Sleep",
|
||||
[&](void* arg) { sleep_count.fetch_add(1); });
|
||||
std::atomic<int> wait_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DelayWrite:Wait",
|
||||
[&](void* arg) { wait_count.fetch_add(1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = sync;
|
||||
wo.disableWAL = disableWAL;
|
||||
wo.no_slowdown = true;
|
||||
dbfull()->Put(wo, "foo", "bar");
|
||||
// We need the 2nd write to trigger delay. This is because delay is
|
||||
// estimated based on the last write size which is 0 for the first write.
|
||||
ASSERT_NOK(dbfull()->Put(wo, "foo2", "bar2"));
|
||||
ASSERT_GE(sleep_count.load(), 0);
|
||||
ASSERT_GE(wait_count.load(), 0);
|
||||
token.reset();
|
||||
|
||||
token = dbfull()->TEST_write_controler().GetDelayToken(1000000000);
|
||||
wo.no_slowdown = false;
|
||||
ASSERT_OK(dbfull()->Put(wo, "foo3", "bar3"));
|
||||
ASSERT_GE(sleep_count.load(), 1);
|
||||
token.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, ReadOnlyDB) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
@@ -941,7 +999,7 @@ TEST_F(DBTest, FlushSchedule) {
|
||||
options.max_write_buffer_number = 2;
|
||||
options.write_buffer_size = 120 * 1024;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
|
||||
std::atomic<int> thread_num(0);
|
||||
// each column family will have 5 thread, each thread generating 2 memtables.
|
||||
@@ -1189,6 +1247,12 @@ bool MinLevelToCompress(CompressionType& type, Options& options, int wbits,
|
||||
} else if (LZ4_Supported()) {
|
||||
type = kLZ4Compression;
|
||||
fprintf(stderr, "using lz4\n");
|
||||
} else if (XPRESS_Supported()) {
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTD;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
return false;
|
||||
@@ -1246,7 +1310,9 @@ TEST_F(DBTest, MinLevelToCompress2) {
|
||||
MinLevelHelper(this, options);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, RepeatedWritesToSameKey) {
|
||||
// This test may fail because of a legit case that multiple L0 files
|
||||
// are trivial moved to L1.
|
||||
TEST_F(DBTest, DISABLED_RepeatedWritesToSameKey) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -1342,17 +1408,19 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
std::string start = Key(50);
|
||||
std::string end = Key(60);
|
||||
Range r(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
uint8_t include_both = DB::SizeApproximationFlags::INCLUDE_FILES |
|
||||
DB::SizeApproximationFlags::INCLUDE_MEMTABLES;
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_GT(size, 6000);
|
||||
ASSERT_LT(size, 204800);
|
||||
// Zero if not including mem table
|
||||
db_->GetApproximateSizes(&r, 1, &size, false);
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -1362,13 +1430,13 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(100);
|
||||
end = Key(1020);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_GT(size, 6000);
|
||||
|
||||
options.max_write_buffer_number = 8;
|
||||
@@ -1391,28 +1459,28 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(100);
|
||||
end = Key(300);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_GT(size, 6000);
|
||||
|
||||
start = Key(2100);
|
||||
end = Key(2300);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size, include_both);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
uint64_t size_with_mt, size_without_mt;
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, include_both);
|
||||
ASSERT_GT(size_with_mt, 6000);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt, false);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt);
|
||||
ASSERT_EQ(size_without_mt, 0);
|
||||
|
||||
Flush();
|
||||
@@ -1424,12 +1492,65 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, true);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt, false);
|
||||
db_->GetApproximateSizes(&r, 1, &size_with_mt, include_both);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt);
|
||||
ASSERT_GT(size_with_mt, size_without_mt);
|
||||
ASSERT_GT(size_without_mt, 6000);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetApproximateMemTableStats) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100000000;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int N = 128;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
|
||||
uint64_t count;
|
||||
uint64_t size;
|
||||
|
||||
std::string start = Key(50);
|
||||
std::string end = Key(60);
|
||||
Range r(start, end);
|
||||
db_->GetApproximateMemTableStats(r, &count, &size);
|
||||
ASSERT_GT(count, 0);
|
||||
ASSERT_LE(count, N);
|
||||
ASSERT_GT(size, 6000);
|
||||
ASSERT_LT(size, 204800);
|
||||
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateMemTableStats(r, &count, &size);
|
||||
ASSERT_EQ(count, 0);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
Flush();
|
||||
|
||||
start = Key(50);
|
||||
end = Key(60);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateMemTableStats(r, &count, &size);
|
||||
ASSERT_EQ(count, 0);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(Put(Key(1000 + i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
|
||||
start = Key(100);
|
||||
end = Key(1020);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateMemTableStats(r, &count, &size);
|
||||
ASSERT_GT(count, 20);
|
||||
ASSERT_GT(size, 6000);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ApproximateSizes) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
@@ -2067,8 +2188,8 @@ TEST_F(DBTest, SnapshotFiles) {
|
||||
dbfull()->DisableFileDeletions();
|
||||
dbfull()->GetLiveFiles(files, &manifest_size);
|
||||
|
||||
// CURRENT, MANIFEST, *.sst files (one for each CF)
|
||||
ASSERT_EQ(files.size(), 4U);
|
||||
// CURRENT, MANIFEST, OPTIONS, *.sst files (one for each CF)
|
||||
ASSERT_EQ(files.size(), 5U);
|
||||
|
||||
uint64_t number = 0;
|
||||
FileType type;
|
||||
@@ -2285,130 +2406,6 @@ TEST_F(DBTest, FlushOneColumnFamily) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest, SharedWriteBuffer) {
|
||||
Options options = CurrentOptions();
|
||||
options.db_write_buffer_size = 100000; // this is the real limit
|
||||
options.write_buffer_size = 500000; // this is never hit
|
||||
CreateAndReopenWithCF({"pikachu", "dobrynia", "nikitich"}, options);
|
||||
|
||||
// Trigger a flush on CF "nikitich"
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
ASSERT_OK(Put(1, Key(1), DummyString(1)));
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(90000)));
|
||||
ASSERT_OK(Put(2, Key(2), DummyString(20000)));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(1));
|
||||
}
|
||||
|
||||
// "dobrynia": 20KB
|
||||
// Flush 'dobrynia'
|
||||
ASSERT_OK(Put(3, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(Put(2, Key(2), DummyString(70000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(1));
|
||||
}
|
||||
|
||||
// "nikitich" still has data of 80KB
|
||||
// Inserting Data in "dobrynia" triggers "nikitich" flushing.
|
||||
ASSERT_OK(Put(3, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(Put(2, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
|
||||
// "dobrynia" still has 40KB
|
||||
ASSERT_OK(Put(1, Key(2), DummyString(20000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(10000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
// This should triggers no flush
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
|
||||
// "default": 10KB, "pikachu": 20KB, "dobrynia": 40KB
|
||||
ASSERT_OK(Put(1, Key(2), DummyString(40000)));
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[0]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
// This should triggers flush of "pikachu"
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(0));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(2));
|
||||
}
|
||||
|
||||
// "default": 10KB, "dobrynia": 40KB
|
||||
// Some remaining writes so 'default', 'dobrynia' and 'nikitich' flush on
|
||||
// closure.
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1)));
|
||||
ReopenWithColumnFamilies({"default", "pikachu", "dobrynia", "nikitich"},
|
||||
options);
|
||||
{
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "default"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "pikachu"),
|
||||
static_cast<uint64_t>(1));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "dobrynia"),
|
||||
static_cast<uint64_t>(2));
|
||||
ASSERT_EQ(GetNumberOfSstFilesForColumnFamily(db_, "nikitich"),
|
||||
static_cast<uint64_t>(3));
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, PurgeInfoLogs) {
|
||||
Options options = CurrentOptions();
|
||||
options.keep_log_file_num = 5;
|
||||
@@ -2585,11 +2582,13 @@ class MultiThreadedDBTest : public DBTest,
|
||||
TEST_P(MultiThreadedDBTest, MultiThreaded) {
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
Options options = CurrentOptions(options_override);
|
||||
std::vector<std::string> cfs;
|
||||
for (int i = 1; i < kColumnFamilies; ++i) {
|
||||
cfs.push_back(ToString(i));
|
||||
}
|
||||
CreateAndReopenWithCF(cfs, CurrentOptions(options_override));
|
||||
Reopen(options);
|
||||
CreateAndReopenWithCF(cfs, options);
|
||||
// Initialize state
|
||||
MTState mt;
|
||||
mt.test = this;
|
||||
@@ -2764,15 +2763,11 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::AddFile;
|
||||
virtual Status AddFile(ColumnFamilyHandle* column_family,
|
||||
const ExternalSstFileInfo* file_path,
|
||||
bool move_file) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
virtual Status AddFile(ColumnFamilyHandle* column_family,
|
||||
const std::string& file_path,
|
||||
bool move_file) override {
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& options) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
@@ -2860,6 +2855,12 @@ class ModelDB : public DB {
|
||||
const Slice& property, uint64_t* value) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetMapProperty;
|
||||
virtual bool GetMapProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property,
|
||||
std::map<std::string, double>* value) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetAggregatedIntProperty;
|
||||
virtual bool GetAggregatedIntProperty(const Slice& property,
|
||||
uint64_t* value) override {
|
||||
@@ -2868,11 +2869,20 @@ class ModelDB : public DB {
|
||||
using DB::GetApproximateSizes;
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
bool include_memtable) override {
|
||||
uint8_t include_flags
|
||||
= INCLUDE_FILES) override {
|
||||
for (int i = 0; i < n; i++) {
|
||||
sizes[i] = 0;
|
||||
}
|
||||
}
|
||||
using DB::GetApproximateMemTableStats;
|
||||
virtual void GetApproximateMemTableStats(ColumnFamilyHandle* column_family,
|
||||
const Range& range,
|
||||
uint64_t* const count,
|
||||
uint64_t* const size) override {
|
||||
*count = 0;
|
||||
*size = 0;
|
||||
}
|
||||
using DB::CompactRange;
|
||||
virtual Status CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -2880,6 +2890,12 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
virtual Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& new_options)
|
||||
override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
using DB::CompactFiles;
|
||||
virtual Status CompactFiles(const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -2924,13 +2940,12 @@ class ModelDB : public DB {
|
||||
virtual Env* GetEnv() const override { return nullptr; }
|
||||
|
||||
using DB::GetOptions;
|
||||
virtual const Options& GetOptions(
|
||||
ColumnFamilyHandle* column_family) const override {
|
||||
virtual Options GetOptions(ColumnFamilyHandle* column_family) const override {
|
||||
return options_;
|
||||
}
|
||||
|
||||
using DB::GetDBOptions;
|
||||
virtual const DBOptions& GetDBOptions() const override { return options_; }
|
||||
virtual DBOptions GetDBOptions() const override { return options_; }
|
||||
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const rocksdb::FlushOptions& options,
|
||||
@@ -3000,6 +3015,10 @@ class ModelDB : public DB {
|
||||
virtual void Seek(const Slice& k) override {
|
||||
iter_ = map_->lower_bound(k.ToString());
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
iter_ = map_->upper_bound(k.ToString());
|
||||
Prev();
|
||||
}
|
||||
virtual void Next() override { ++iter_; }
|
||||
virtual void Prev() override {
|
||||
if (iter_ == map_->begin()) {
|
||||
@@ -3455,19 +3474,21 @@ TEST_F(DBTest, TableOptionsSanitizeTest) {
|
||||
TEST_F(DBTest, MmapAndBufferOptions) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
options.allow_os_buffer = false;
|
||||
options.use_direct_reads = true;
|
||||
options.allow_mmap_reads = true;
|
||||
ASSERT_NOK(TryReopen(options));
|
||||
|
||||
// All other combinations are acceptable
|
||||
options.allow_os_buffer = true;
|
||||
options.use_direct_reads = false;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
options.allow_os_buffer = false;
|
||||
options.allow_mmap_reads = false;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
if (IsDirectIOSupported()) {
|
||||
options.use_direct_reads = true;
|
||||
options.allow_mmap_reads = false;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
|
||||
options.allow_os_buffer = true;
|
||||
options.use_direct_reads = false;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
#endif
|
||||
@@ -3536,7 +3557,7 @@ TEST_F(DBTest, SanitizeNumThreads) {
|
||||
}
|
||||
|
||||
TEST_F(DBTest, WriteSingleThreadEntry) {
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
dbfull()->TEST_LockMutex();
|
||||
auto w = dbfull()->TEST_BeginWrite();
|
||||
threads.emplace_back([&] { Put("a", "b"); });
|
||||
@@ -3722,7 +3743,7 @@ TEST_F(DBTest, DynamicMemtableOptions) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
#if ROCKSDB_USING_THREAD_STATUS
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
namespace {
|
||||
void VerifyOperationCount(Env* env, ThreadStatus::OperationType op_type,
|
||||
int expected_count) {
|
||||
@@ -3757,14 +3778,24 @@ TEST_F(DBTest, GetThreadStatus) {
|
||||
env_->SetBackgroundThreads(kHighPriCounts[test], Env::HIGH);
|
||||
env_->SetBackgroundThreads(kLowPriCounts[test], Env::LOW);
|
||||
// Wait to ensure the all threads has been registered
|
||||
env_->SleepForMicroseconds(100000);
|
||||
s = env_->GetThreadList(&thread_list);
|
||||
ASSERT_OK(s);
|
||||
unsigned int thread_type_counts[ThreadStatus::NUM_THREAD_TYPES];
|
||||
memset(thread_type_counts, 0, sizeof(thread_type_counts));
|
||||
for (auto thread : thread_list) {
|
||||
ASSERT_LT(thread.thread_type, ThreadStatus::NUM_THREAD_TYPES);
|
||||
thread_type_counts[thread.thread_type]++;
|
||||
// Try up to 60 seconds.
|
||||
for (int num_try = 0; num_try < 60000; num_try++) {
|
||||
env_->SleepForMicroseconds(1000);
|
||||
thread_list.clear();
|
||||
s = env_->GetThreadList(&thread_list);
|
||||
ASSERT_OK(s);
|
||||
memset(thread_type_counts, 0, sizeof(thread_type_counts));
|
||||
for (auto thread : thread_list) {
|
||||
ASSERT_LT(thread.thread_type, ThreadStatus::NUM_THREAD_TYPES);
|
||||
thread_type_counts[thread.thread_type]++;
|
||||
}
|
||||
if (thread_type_counts[ThreadStatus::HIGH_PRIORITY] ==
|
||||
kHighPriCounts[test] &&
|
||||
thread_type_counts[ThreadStatus::LOW_PRIORITY] ==
|
||||
kLowPriCounts[test]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Verify the total number of threades
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::HIGH_PRIORITY] +
|
||||
@@ -4182,7 +4213,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
options.level0_stop_writes_trigger = 2;
|
||||
options.target_file_size_base = 2048;
|
||||
options.target_file_size_base = 20480;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.max_bytes_for_level_base = 102400;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
@@ -4210,7 +4241,8 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(3), 0);
|
||||
ASSERT_GT(SizeAtLevel(0) + SizeAtLevel(4), 20U * 4000U);
|
||||
// Assuming each files' metadata is at least 50 bytes/
|
||||
ASSERT_GT(SizeAtLevel(0) + SizeAtLevel(4), 20U * 4000U + 50U * 4);
|
||||
|
||||
// Insert 400KB. Some data will be compressed
|
||||
for (int i = 21; i < 120; i++) {
|
||||
@@ -4220,7 +4252,8 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
||||
ASSERT_LT(SizeAtLevel(0) + SizeAtLevel(3) + SizeAtLevel(4), 120U * 4000U);
|
||||
ASSERT_LT(SizeAtLevel(0) + SizeAtLevel(3) + SizeAtLevel(4),
|
||||
120U * 4000U + 50U * 24);
|
||||
// Make sure data in files in L3 is not compacted by removing all files
|
||||
// in L4 and calculate number of rows
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
@@ -4240,7 +4273,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
num_keys++;
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_GT(SizeAtLevel(0) + SizeAtLevel(3), num_keys * 4000U);
|
||||
ASSERT_GT(SizeAtLevel(0) + SizeAtLevel(3), num_keys * 4000U + num_keys * 10U);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, DynamicLevelCompressionPerLevel2) {
|
||||
@@ -4387,10 +4420,8 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
options.level0_slowdown_writes_trigger = 4;
|
||||
options.level0_stop_writes_trigger = 8;
|
||||
options.max_grandparent_overlap_factor = 10;
|
||||
options.expanded_compaction_factor = 25;
|
||||
options.source_compaction_factor = 1;
|
||||
options.target_file_size_base = k64KB;
|
||||
options.max_compaction_bytes = options.target_file_size_base * 10;
|
||||
options.target_file_size_multiplier = 1;
|
||||
options.max_bytes_for_level_base = k128KB;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
@@ -4807,6 +4838,77 @@ TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, CompressionStatsTest) {
|
||||
CompressionType type;
|
||||
|
||||
if (Snappy_Supported()) {
|
||||
type = kSnappyCompression;
|
||||
fprintf(stderr, "using snappy\n");
|
||||
} else if (Zlib_Supported()) {
|
||||
type = kZlibCompression;
|
||||
fprintf(stderr, "using zlib\n");
|
||||
} else if (BZip2_Supported()) {
|
||||
type = kBZip2Compression;
|
||||
fprintf(stderr, "using bzip2\n");
|
||||
} else if (LZ4_Supported()) {
|
||||
type = kLZ4Compression;
|
||||
fprintf(stderr, "using lz4\n");
|
||||
} else if (XPRESS_Supported()) {
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTD;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.compression = type;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.statistics->stats_level_ = StatsLevel::kExceptTimeForMutex;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int kNumKeysWritten = 100000;
|
||||
|
||||
// Check that compressions occur and are counted when compression is turned on
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED), 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED), 0);
|
||||
|
||||
options.compression = kNoCompression;
|
||||
DestroyAndReopen(options);
|
||||
uint64_t currentCompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED);
|
||||
uint64_t currentDecompressions =
|
||||
options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED);
|
||||
|
||||
// Check that compressions do not occur when turned off
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED)
|
||||
- currentCompressions, 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
auto r = Get(Key(i));
|
||||
}
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_DECOMPRESSED)
|
||||
- currentDecompressions, 0);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MutexWaitStatsDisabledByDefault) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -4895,12 +4997,11 @@ class DelayedMergeOperator : public MergeOperator {
|
||||
|
||||
public:
|
||||
explicit DelayedMergeOperator(DBTest* d) : db_test_(d) {}
|
||||
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 {
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
db_test_->env_->addon_time_.fetch_add(1000);
|
||||
*new_value = "";
|
||||
merge_out->new_value = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4917,7 +5018,7 @@ TEST_F(DBTest, MergeTestTime) {
|
||||
SetPerfLevel(kEnableTime);
|
||||
this->env_->addon_time_.store(0);
|
||||
this->env_->time_elapse_only_sleep_ = true;
|
||||
this->env_->no_sleep_ = true;
|
||||
this->env_->no_slowdown_ = true;
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.merge_operator.reset(new DelayedMergeOperator(this));
|
||||
@@ -4949,7 +5050,7 @@ TEST_F(DBTest, MergeTestTime) {
|
||||
|
||||
ASSERT_EQ(1, count);
|
||||
ASSERT_EQ(2000000, TestGetTickerCount(options, MERGE_OPERATION_TOTAL_TIME));
|
||||
#if ROCKSDB_USING_THREAD_STATUS
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
ASSERT_GT(TestGetTickerCount(options, FLUSH_WRITE_BYTES), 0);
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
this->env_->time_elapse_only_sleep_ = false;
|
||||
@@ -5069,7 +5170,7 @@ TEST_F(DBTest, SuggestCompactRangeTest) {
|
||||
options.compression = kNoCompression;
|
||||
options.max_bytes_for_level_base = 450 << 10;
|
||||
options.target_file_size_base = 98 << 10;
|
||||
options.max_grandparent_overlap_factor = 1 << 20; // inf
|
||||
options.max_compaction_bytes = static_cast<uint64_t>(1) << 60; // inf
|
||||
|
||||
Reopen(options);
|
||||
|
||||
@@ -5334,7 +5435,7 @@ TEST_F(DBTest, FlushesInParallelWithCompactRange) {
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
threads.emplace_back([&]() { Compact("a", "z"); });
|
||||
|
||||
TEST_SYNC_POINT("DBTest::FlushesInParallelWithCompactRange:1");
|
||||
@@ -5357,12 +5458,12 @@ TEST_F(DBTest, FlushesInParallelWithCompactRange) {
|
||||
|
||||
TEST_F(DBTest, DelayedWriteRate) {
|
||||
const int kEntriesPerMemTable = 100;
|
||||
const int kTotalFlushes = 20;
|
||||
const int kTotalFlushes = 12;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
env_->SetBackgroundThreads(1, Env::LOW);
|
||||
options.env = env_;
|
||||
env_->no_sleep_ = true;
|
||||
env_->no_slowdown_ = true;
|
||||
options.write_buffer_size = 100000000;
|
||||
options.max_write_buffer_number = 256;
|
||||
options.max_background_compactions = 1;
|
||||
@@ -5407,8 +5508,8 @@ TEST_F(DBTest, DelayedWriteRate) {
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
estimated_sleep_time += size_memtable * 1000000u / cur_rate;
|
||||
// Slow down twice. One for memtable switch and one for flush finishes.
|
||||
cur_rate = static_cast<uint64_t>(static_cast<double>(cur_rate) /
|
||||
kSlowdownRatio / kSlowdownRatio);
|
||||
cur_rate = static_cast<uint64_t>(static_cast<double>(cur_rate) *
|
||||
kIncSlowdownRatio * kIncSlowdownRatio);
|
||||
}
|
||||
// Estimate the total sleep time fall into the rough range.
|
||||
ASSERT_GT(env_->addon_time_.load(),
|
||||
@@ -5416,7 +5517,7 @@ TEST_F(DBTest, DelayedWriteRate) {
|
||||
ASSERT_LT(env_->addon_time_.load(),
|
||||
static_cast<int64_t>(estimated_sleep_time * 2));
|
||||
|
||||
env_->no_sleep_ = false;
|
||||
env_->no_slowdown_ = false;
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
@@ -5740,7 +5841,7 @@ TEST_F(DBTest, PauseBackgroundWorkTest) {
|
||||
options.write_buffer_size = 100000; // Small write buffer
|
||||
Reopen(options);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
std::vector<port::Thread> threads;
|
||||
std::atomic<bool> done(false);
|
||||
db_->PauseBackgroundWork();
|
||||
threads.emplace_back([&]() {
|
||||
|
||||
+1110
-32
File diff suppressed because it is too large.
Load diff
+192
-15
@@ -8,6 +8,7 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/forward_iterator.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -19,7 +20,7 @@ SpecialEnv::SpecialEnv(Env* base)
|
||||
sleep_counter_(this),
|
||||
addon_time_(0),
|
||||
time_elapse_only_sleep_(false),
|
||||
no_sleep_(false) {
|
||||
no_slowdown_(false) {
|
||||
delay_sstable_sync_.store(false, std::memory_order_release);
|
||||
drop_writes_.store(false, std::memory_order_release);
|
||||
no_space_.store(false, std::memory_order_release);
|
||||
@@ -30,6 +31,8 @@ SpecialEnv::SpecialEnv(Env* base)
|
||||
manifest_write_error_.store(false, std::memory_order_release);
|
||||
log_write_error_.store(false, std::memory_order_release);
|
||||
random_file_open_counter_.store(0, std::memory_order_relaxed);
|
||||
delete_count_.store(0, std::memory_order_relaxed);
|
||||
num_open_wal_file_.store(0);
|
||||
log_write_slowdown_ = 0;
|
||||
bytes_written_ = 0;
|
||||
sync_counter_ = 0;
|
||||
@@ -69,7 +72,12 @@ DBTestBase::~DBTestBase() {
|
||||
options.db_paths.emplace_back(dbname_ + "_2", 0);
|
||||
options.db_paths.emplace_back(dbname_ + "_3", 0);
|
||||
options.db_paths.emplace_back(dbname_ + "_4", 0);
|
||||
EXPECT_OK(DestroyDB(dbname_, options));
|
||||
|
||||
if (getenv("KEEP_DB")) {
|
||||
printf("DB is still at %s\n", dbname_.c_str());
|
||||
} else {
|
||||
EXPECT_OK(DestroyDB(dbname_, options));
|
||||
}
|
||||
delete env_;
|
||||
}
|
||||
|
||||
@@ -91,10 +99,6 @@ bool DBTestBase::ShouldSkipOptions(int option_config, int skip_mask) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((skip_mask & kSkipDeletesFilterFirst) &&
|
||||
option_config == kDeletesFilterFirst) {
|
||||
return true;
|
||||
}
|
||||
if ((skip_mask & kSkipUniversalCompaction) &&
|
||||
(option_config == kUniversalCompaction ||
|
||||
option_config == kUniversalCompactionMultiLevel)) {
|
||||
@@ -235,6 +239,7 @@ Options DBTestBase::CurrentOptions(
|
||||
case kHashSkipList:
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.memtable_factory.reset(NewHashSkipListRepFactory(16));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
break;
|
||||
case kPlainTableFirstBytePrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
@@ -266,15 +271,18 @@ Options DBTestBase::CurrentOptions(
|
||||
break;
|
||||
case kVectorRep:
|
||||
options.memtable_factory.reset(new VectorRepFactory(100));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
break;
|
||||
case kHashLinkList:
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.memtable_factory.reset(
|
||||
NewHashLinkListRepFactory(4, 0, 3, true, 4));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
break;
|
||||
case kHashCuckoo:
|
||||
options.memtable_factory.reset(
|
||||
NewHashCuckooRepFactory(options.write_buffer_size));
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
break;
|
||||
#endif // ROCKSDB_LITE
|
||||
case kMergePut:
|
||||
@@ -305,15 +313,13 @@ Options DBTestBase::CurrentOptions(
|
||||
break;
|
||||
case kManifestFileSize:
|
||||
options.max_manifest_file_size = 50; // 50 bytes
|
||||
break;
|
||||
case kPerfOptions:
|
||||
options.soft_rate_limit = 2.0;
|
||||
options.delayed_write_rate = 8 * 1024 * 1024;
|
||||
options.report_bg_io_stats = true;
|
||||
// TODO(3.13) -- test more options
|
||||
break;
|
||||
case kDeletesFilterFirst:
|
||||
options.filter_deletes = true;
|
||||
break;
|
||||
case kUniversalCompaction:
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 1;
|
||||
@@ -347,6 +353,11 @@ Options DBTestBase::CurrentOptions(
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithPartitionedIndex: {
|
||||
table_options.index_type = BlockBasedTableOptions::kTwoLevelIndexSearch;
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
break;
|
||||
}
|
||||
case kBlockBasedTableWithIndexRestartInterval: {
|
||||
table_options.index_block_restart_interval = 8;
|
||||
break;
|
||||
@@ -449,7 +460,7 @@ void DBTestBase::Reopen(const Options& options) {
|
||||
|
||||
void DBTestBase::Close() {
|
||||
for (auto h : handles_) {
|
||||
delete h;
|
||||
db_->DestroyColumnFamilyHandle(h);
|
||||
}
|
||||
handles_.clear();
|
||||
delete db_;
|
||||
@@ -477,6 +488,22 @@ Status DBTestBase::TryReopen(const Options& options) {
|
||||
return DB::Open(options, dbname_, &db_);
|
||||
}
|
||||
|
||||
bool DBTestBase::IsDirectIOSupported() {
|
||||
EnvOptions env_options;
|
||||
env_options.use_mmap_writes = false;
|
||||
env_options.use_direct_writes = true;
|
||||
std::string tmp = TempFileName(dbname_, 999);
|
||||
Status s;
|
||||
{
|
||||
unique_ptr<WritableFile> file;
|
||||
s = env_->NewWritableFile(tmp, &file, env_options);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = env_->DeleteFile(tmp);
|
||||
}
|
||||
return s.ok();
|
||||
}
|
||||
|
||||
Status DBTestBase::Flush(int cf) {
|
||||
if (cf == 0) {
|
||||
return db_->Flush(FlushOptions());
|
||||
@@ -502,6 +529,15 @@ Status DBTestBase::Put(int cf, const Slice& k, const Slice& v,
|
||||
}
|
||||
}
|
||||
|
||||
Status DBTestBase::Merge(const Slice& k, const Slice& v, WriteOptions wo) {
|
||||
return db_->Merge(wo, k, v);
|
||||
}
|
||||
|
||||
Status DBTestBase::Merge(int cf, const Slice& k, const Slice& v,
|
||||
WriteOptions wo) {
|
||||
return db_->Merge(wo, handles_[cf], k, v);
|
||||
}
|
||||
|
||||
Status DBTestBase::Delete(const std::string& k) {
|
||||
return db_->Delete(WriteOptions(), k);
|
||||
}
|
||||
@@ -590,11 +626,15 @@ std::string DBTestBase::Contents(int cf) {
|
||||
|
||||
std::string DBTestBase::AllEntriesFor(const Slice& user_key, int cf) {
|
||||
Arena arena;
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
ScopedArenaIterator iter;
|
||||
if (cf == 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena));
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg));
|
||||
} else {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, handles_[cf]));
|
||||
iter.set(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[cf]));
|
||||
}
|
||||
InternalKey target(user_key, kMaxSequenceNumber, kTypeValue);
|
||||
iter->Seek(target.Encode());
|
||||
@@ -737,7 +777,7 @@ double DBTestBase::CompressionRatioAtLevel(int level, int cf) {
|
||||
|
||||
int DBTestBase::TotalTableFiles(int cf, int levels) {
|
||||
if (levels == -1) {
|
||||
levels = CurrentOptions().num_levels;
|
||||
levels = (cf == 0) ? db_->NumberLevels() : db_->NumberLevels(handles_[1]);
|
||||
}
|
||||
int result = 0;
|
||||
for (int level = 0; level < levels; level++) {
|
||||
@@ -995,10 +1035,14 @@ UpdateStatus DBTestBase::updateInPlaceNoAction(char* prevValue,
|
||||
void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
ScopedArenaIterator iter;
|
||||
Arena arena;
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
if (cf != 0) {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, handles_[cf]));
|
||||
iter.set(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[cf]));
|
||||
} else {
|
||||
iter.set(dbfull()->NewInternalIterator(&arena));
|
||||
iter.set(dbfull()->NewInternalIterator(&arena, &range_del_agg));
|
||||
}
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(iter->status().ok(), true);
|
||||
@@ -1081,7 +1125,140 @@ std::vector<std::uint64_t> DBTestBase::ListTableFiles(Env* env,
|
||||
return file_numbers;
|
||||
}
|
||||
|
||||
void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
|
||||
size_t* total_reads_res, bool tailing_iter,
|
||||
std::map<std::string, Status> status) {
|
||||
size_t total_reads = 0;
|
||||
|
||||
for (auto& kv : true_data) {
|
||||
Status s = status[kv.first];
|
||||
if (s.ok()) {
|
||||
ASSERT_EQ(Get(kv.first), kv.second);
|
||||
} else {
|
||||
std::string value;
|
||||
ASSERT_EQ(s, db_->Get(ReadOptions(), kv.first, &value));
|
||||
}
|
||||
total_reads++;
|
||||
}
|
||||
|
||||
// Normal Iterator
|
||||
{
|
||||
int iter_cnt = 0;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
// Verify Iterator::Next()
|
||||
iter_cnt = 0;
|
||||
auto data_iter = true_data.begin();
|
||||
Status s;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), data_iter++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_iter->first);
|
||||
Status current_status = status[data_iter->first];
|
||||
if (!current_status.ok()) {
|
||||
s = current_status;
|
||||
}
|
||||
ASSERT_EQ(iter->status(), s);
|
||||
if (current_status.ok()) {
|
||||
ASSERT_EQ(iter->value().ToString(), data_iter->second);
|
||||
}
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
ASSERT_EQ(data_iter, true_data.end()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
delete iter;
|
||||
|
||||
// Verify Iterator::Prev()
|
||||
// Use a new iterator to make sure its status is clean.
|
||||
iter = db_->NewIterator(ro);
|
||||
iter_cnt = 0;
|
||||
s = Status::OK();
|
||||
auto data_rev = true_data.rbegin();
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev(), data_rev++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_rev->first);
|
||||
Status current_status = status[data_rev->first];
|
||||
if (!current_status.ok()) {
|
||||
s = current_status;
|
||||
}
|
||||
ASSERT_EQ(iter->status(), s);
|
||||
if (current_status.ok()) {
|
||||
ASSERT_EQ(iter->value().ToString(), data_rev->second);
|
||||
}
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
ASSERT_EQ(data_rev, true_data.rend()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
|
||||
// Verify Iterator::Seek()
|
||||
for (auto kv : true_data) {
|
||||
iter->Seek(kv.first);
|
||||
ASSERT_EQ(kv.first, iter->key().ToString());
|
||||
ASSERT_EQ(kv.second, iter->value().ToString());
|
||||
total_reads++;
|
||||
}
|
||||
delete iter;
|
||||
}
|
||||
|
||||
if (tailing_iter) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Tailing iterator
|
||||
int iter_cnt = 0;
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
ro.total_order_seek = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
|
||||
// Verify ForwardIterator::Next()
|
||||
iter_cnt = 0;
|
||||
auto data_iter = true_data.begin();
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), data_iter++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_iter->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_iter->second);
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
ASSERT_EQ(data_iter, true_data.end()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
|
||||
// Verify ForwardIterator::Seek()
|
||||
for (auto kv : true_data) {
|
||||
iter->Seek(kv.first);
|
||||
ASSERT_EQ(kv.first, iter->key().ToString());
|
||||
ASSERT_EQ(kv.second, iter->value().ToString());
|
||||
total_reads++;
|
||||
}
|
||||
|
||||
delete iter;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
if (total_reads_res) {
|
||||
*total_reads_res = total_reads;
|
||||
}
|
||||
}
|
||||
|
||||
void DBTestBase::VerifyDBInternal(
|
||||
std::vector<std::pair<std::string, std::string>> true_data) {
|
||||
Arena arena;
|
||||
InternalKeyComparator icmp(last_options_.comparator);
|
||||
RangeDelAggregator range_del_agg(icmp, {});
|
||||
auto iter = dbfull()->NewInternalIterator(&arena, &range_del_agg);
|
||||
iter->SeekToFirst();
|
||||
for (auto p : true_data) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ParsedInternalKey ikey;
|
||||
ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey));
|
||||
ASSERT_EQ(p.first, ikey.user_key);
|
||||
ASSERT_EQ(p.second, iter->value());
|
||||
iter->Next();
|
||||
};
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
iter->~InternalIterator();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
uint64_t DBTestBase::GetNumberOfSstFilesForColumnFamily(
|
||||
DB* db, std::string column_family_name) {
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
|
||||
+89
-20
@@ -39,6 +39,7 @@
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
@@ -192,6 +193,10 @@ class SpecialSkipListFactory : public MemTableRepFactory {
|
||||
}
|
||||
virtual const char* Name() const override { return "SkipListFactory"; }
|
||||
|
||||
bool IsInsertConcurrentlySupported() const override {
|
||||
return factory_.IsInsertConcurrentlySupported();
|
||||
}
|
||||
|
||||
private:
|
||||
SkipListFactory factory_;
|
||||
int num_entries_flush_;
|
||||
@@ -220,12 +225,26 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Drop writes on the floor
|
||||
return Status::OK();
|
||||
} else if (env_->no_space_.load(std::memory_order_acquire)) {
|
||||
return Status::IOError("No space left on device");
|
||||
return Status::NoSpace("No space left on device");
|
||||
} else {
|
||||
env_->bytes_written_ += data.size();
|
||||
return base_->Append(data);
|
||||
}
|
||||
}
|
||||
Status PositionedAppend(const Slice& data, uint64_t offset) override {
|
||||
if (env_->table_write_callback_) {
|
||||
(*env_->table_write_callback_)();
|
||||
}
|
||||
if (env_->drop_writes_.load(std::memory_order_acquire)) {
|
||||
// Drop writes on the floor
|
||||
return Status::OK();
|
||||
} else if (env_->no_space_.load(std::memory_order_acquire)) {
|
||||
return Status::NoSpace("No space left on device");
|
||||
} else {
|
||||
env_->bytes_written_ += data.size();
|
||||
return base_->PositionedAppend(data, offset);
|
||||
}
|
||||
}
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status Close() override {
|
||||
// SyncPoint is not supported in Released Windows Mode.
|
||||
@@ -252,6 +271,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
Env::IOPriority GetIOPriority() override {
|
||||
return base_->GetIOPriority();
|
||||
}
|
||||
bool use_direct_io() const override {
|
||||
return base_->use_direct_io();
|
||||
}
|
||||
};
|
||||
class ManifestFile : public WritableFile {
|
||||
public:
|
||||
@@ -284,7 +306,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
class WalFile : public WritableFile {
|
||||
public:
|
||||
WalFile(SpecialEnv* env, unique_ptr<WritableFile>&& b)
|
||||
: env_(env), base_(std::move(b)) {}
|
||||
: env_(env), base_(std::move(b)) {
|
||||
env_->num_open_wal_file_.fetch_add(1);
|
||||
}
|
||||
virtual ~WalFile() { env_->num_open_wal_file_.fetch_add(-1); }
|
||||
Status Append(const Slice& data) override {
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_SYNC_POINT("SpecialEnv::WalFile::Append:1");
|
||||
@@ -306,7 +331,18 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status Close() override { return base_->Close(); }
|
||||
Status Close() override {
|
||||
// SyncPoint is not supported in Released Windows Mode.
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
// Check preallocation size
|
||||
// preallocation size is never passed to base file.
|
||||
size_t preallocation_size = preallocation_block_size();
|
||||
TEST_SYNC_POINT_CALLBACK("DBTestWalFile.GetPreallocationStatus",
|
||||
&preallocation_size);
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
|
||||
return base_->Close();
|
||||
}
|
||||
Status Flush() override { return base_->Flush(); }
|
||||
Status Sync() override {
|
||||
++env_->sync_counter_;
|
||||
@@ -339,7 +375,14 @@ class SpecialEnv : public EnvWrapper {
|
||||
return Status::IOError("simulated write error");
|
||||
}
|
||||
|
||||
Status s = target()->NewWritableFile(f, r, soptions);
|
||||
EnvOptions optimized = soptions;
|
||||
if (strstr(f.c_str(), "MANIFEST") != nullptr ||
|
||||
strstr(f.c_str(), "log") != nullptr) {
|
||||
optimized.use_mmap_writes = false;
|
||||
optimized.use_direct_writes = false;
|
||||
}
|
||||
|
||||
Status s = target()->NewWritableFile(f, r, optimized);
|
||||
if (s.ok()) {
|
||||
if (strstr(f.c_str(), ".sst") != nullptr) {
|
||||
r->reset(new SSTableFile(this, std::move(*r)));
|
||||
@@ -413,10 +456,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
virtual void SleepForMicroseconds(int micros) override {
|
||||
sleep_counter_.Increment();
|
||||
if (no_sleep_ || time_elapse_only_sleep_) {
|
||||
if (no_slowdown_ || time_elapse_only_sleep_) {
|
||||
addon_time_.fetch_add(micros);
|
||||
}
|
||||
if (!no_sleep_) {
|
||||
if (!no_slowdown_) {
|
||||
target()->SleepForMicroseconds(micros);
|
||||
}
|
||||
}
|
||||
@@ -442,6 +485,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
addon_time_.load();
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(const std::string& fname) override {
|
||||
delete_count_.fetch_add(1);
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
Random rnd_;
|
||||
port::Mutex rnd_mutex_; // Lock to pretect rnd_
|
||||
|
||||
@@ -469,6 +517,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Slow down every log write, in micro-seconds.
|
||||
std::atomic<int> log_write_slowdown_;
|
||||
|
||||
// Number of WAL files that are still open for write.
|
||||
std::atomic<int> num_open_wal_file_;
|
||||
|
||||
bool count_random_reads_;
|
||||
anon::AtomicCounter random_read_counter_;
|
||||
std::atomic<size_t> random_read_bytes_counter_;
|
||||
@@ -493,9 +544,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
std::atomic<int64_t> addon_time_;
|
||||
|
||||
std::atomic<int> delete_count_;
|
||||
|
||||
bool time_elapse_only_sleep_;
|
||||
|
||||
bool no_sleep_;
|
||||
bool no_slowdown_;
|
||||
|
||||
std::atomic<bool> is_wal_sync_thread_safe_{true};
|
||||
};
|
||||
@@ -550,22 +603,22 @@ class DBTestBase : public testing::Test {
|
||||
kWalDirAndMmapReads = 16,
|
||||
kManifestFileSize = 17,
|
||||
kPerfOptions = 18,
|
||||
kDeletesFilterFirst = 19,
|
||||
kHashSkipList = 20,
|
||||
kUniversalCompaction = 21,
|
||||
kUniversalCompactionMultiLevel = 22,
|
||||
kCompressedBlockCache = 23,
|
||||
kInfiniteMaxOpenFiles = 24,
|
||||
kxxHashChecksum = 25,
|
||||
kFIFOCompaction = 26,
|
||||
kOptimizeFiltersForHits = 27,
|
||||
kRowCache = 28,
|
||||
kRecycleLogFiles = 29,
|
||||
kConcurrentSkipList = 30,
|
||||
kEnd = 31,
|
||||
kHashSkipList = 19,
|
||||
kUniversalCompaction = 20,
|
||||
kUniversalCompactionMultiLevel = 21,
|
||||
kCompressedBlockCache = 22,
|
||||
kInfiniteMaxOpenFiles = 23,
|
||||
kxxHashChecksum = 24,
|
||||
kFIFOCompaction = 25,
|
||||
kOptimizeFiltersForHits = 26,
|
||||
kRowCache = 27,
|
||||
kRecycleLogFiles = 28,
|
||||
kConcurrentSkipList = 29,
|
||||
kEnd = 30,
|
||||
kLevelSubcompactions = 31,
|
||||
kUniversalSubcompactions = 32,
|
||||
kBlockBasedTableWithIndexRestartInterval = 33,
|
||||
kBlockBasedTableWithPartitionedIndex = 34,
|
||||
};
|
||||
int option_config_;
|
||||
|
||||
@@ -664,6 +717,8 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
Status TryReopen(const Options& options);
|
||||
|
||||
bool IsDirectIOSupported();
|
||||
|
||||
Status Flush(int cf = 0);
|
||||
|
||||
Status Put(const Slice& k, const Slice& v, WriteOptions wo = WriteOptions());
|
||||
@@ -671,6 +726,12 @@ class DBTestBase : public testing::Test {
|
||||
Status Put(int cf, const Slice& k, const Slice& v,
|
||||
WriteOptions wo = WriteOptions());
|
||||
|
||||
Status Merge(const Slice& k, const Slice& v,
|
||||
WriteOptions wo = WriteOptions());
|
||||
|
||||
Status Merge(int cf, const Slice& k, const Slice& v,
|
||||
WriteOptions wo = WriteOptions());
|
||||
|
||||
Status Delete(const std::string& k);
|
||||
|
||||
Status Delete(int cf, const std::string& k);
|
||||
@@ -797,6 +858,14 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
std::vector<std::uint64_t> ListTableFiles(Env* env, const std::string& path);
|
||||
|
||||
void VerifyDBFromMap(
|
||||
std::map<std::string, std::string> true_data,
|
||||
size_t* total_reads_res = nullptr, bool tailing_iter = false,
|
||||
std::map<std::string, Status> status = std::map<std::string, Status>());
|
||||
|
||||
void VerifyDBInternal(
|
||||
std::vector<std::pair<std::string, std::string>> true_data);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
uint64_t GetNumberOfSstFilesForColumnFamily(DB* db,
|
||||
std::string column_family_name);
|
||||
|
||||
@@ -127,7 +127,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSingleSortedRun) {
|
||||
options.level0_file_num_compaction_trigger = 0;
|
||||
options.compaction_options_universal.size_ratio = 10;
|
||||
options.compaction_options_universal.min_merge_width = 2;
|
||||
options.compaction_options_universal.max_size_amplification_percent = 1;
|
||||
options.compaction_options_universal.max_size_amplification_percent = 0;
|
||||
|
||||
options.write_buffer_size = 105 << 10; // 105KB
|
||||
options.arena_block_size = 4 << 10;
|
||||
@@ -151,6 +151,9 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSingleSortedRun) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(NumSortedRuns(0), 1);
|
||||
}
|
||||
ASSERT_OK(Put(Key(key_idx), ""));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(NumSortedRuns(0), 1);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, OptimizeFiltersForHits) {
|
||||
@@ -634,10 +637,132 @@ TEST_P(DBTestUniversalCompactionParallel, UniversalCompactionParallel) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompactionParallel, PickByFileNumberBug) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = num_levels_;
|
||||
options.write_buffer_size = 1 * 1024; // 1KB
|
||||
options.level0_file_num_compaction_trigger = 7;
|
||||
options.max_background_compactions = 2;
|
||||
options.target_file_size_base = 1024 * 1024; // 1MB
|
||||
|
||||
// Disable size amplifiction compaction
|
||||
options.compaction_options_universal.max_size_amplification_percent =
|
||||
UINT_MAX;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBTestUniversalCompactionParallel::PickByFileNumberBug:0",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"UniversalCompactionPicker::PickCompaction:Return",
|
||||
"DBTestUniversalCompactionParallel::PickByFileNumberBug:1"},
|
||||
{"DBTestUniversalCompactionParallel::PickByFileNumberBug:2",
|
||||
"CompactionJob::Run():Start"}});
|
||||
|
||||
int total_picked_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
if (arg) {
|
||||
total_picked_compactions++;
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Write 7 files to trigger compaction
|
||||
int key_idx = 1;
|
||||
for (int i = 1; i <= 70; i++) {
|
||||
std::string k = Key(key_idx++);
|
||||
ASSERT_OK(Put(k, k));
|
||||
if (i % 10 == 0) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the 1st background compaction process to start
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:0");
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:1");
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
|
||||
// Write 3 files while 1st compaction is held
|
||||
// These 3 files have different sizes to avoid compacting based on size_ratio
|
||||
int num_keys = 1000;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 1; j <= num_keys; j++) {
|
||||
std::string k = Key(key_idx++);
|
||||
ASSERT_OK(Put(k, k));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
num_keys -= 100;
|
||||
}
|
||||
|
||||
// Wait for the 2nd background compaction process to start
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:0");
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:1");
|
||||
|
||||
// Hold the 1st and 2nd compaction from finishing
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:2");
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// Although 2 compaction threads started, the second one did not compact
|
||||
// anything because the number of files not being compacted is less than
|
||||
// level0_file_num_compaction_trigger
|
||||
EXPECT_EQ(total_picked_compactions, 1);
|
||||
EXPECT_EQ(TotalTableFiles(), 4);
|
||||
|
||||
// Stop SyncPoint and destroy the DB and reopen it again
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
key_idx = 1;
|
||||
total_picked_compactions = 0;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Write 7 files to trigger compaction
|
||||
for (int i = 1; i <= 70; i++) {
|
||||
std::string k = Key(key_idx++);
|
||||
ASSERT_OK(Put(k, k));
|
||||
if (i % 10 == 0) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the 1st background compaction process to start
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:0");
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:1");
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
|
||||
// Write 8 files while 1st compaction is held
|
||||
// These 8 files have different sizes to avoid compacting based on size_ratio
|
||||
num_keys = 1000;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 1; j <= num_keys; j++) {
|
||||
std::string k = Key(key_idx++);
|
||||
ASSERT_OK(Put(k, k));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
num_keys -= 100;
|
||||
}
|
||||
|
||||
// Wait for the 2nd background compaction process to start
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:0");
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:1");
|
||||
|
||||
// Hold the 1st and 2nd compaction from finishing
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:2");
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// This time we will trigger a compaction because of size ratio and
|
||||
// another compaction because of number of files that are not compacted
|
||||
// greater than 7
|
||||
EXPECT_GE(total_picked_compactions, 2);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBTestUniversalCompactionParallel,
|
||||
DBTestUniversalCompactionParallel,
|
||||
::testing::Combine(::testing::Values(1, 10),
|
||||
::testing::Bool()));
|
||||
::testing::Values(false)));
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionOptions) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -919,7 +1044,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionTrivialMoveTest2) {
|
||||
options.num_levels = 15;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.level0_file_num_compaction_trigger = 8;
|
||||
options.max_background_compactions = 4;
|
||||
options.max_background_compactions = 1;
|
||||
options.target_file_size_base = 64 * 1024;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -993,13 +1118,13 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionFourPaths) {
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 2, 4)
|
||||
// (1, 2, 4) -> (3, 4)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[2].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 1, 2, 4) -> (8)
|
||||
// (1, 3, 4) -> (8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
|
||||
@@ -1013,22 +1138,22 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionFourPaths) {
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
|
||||
// (1, 2, 8)
|
||||
// (1, 2, 8) -> (3, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 1, 2, 8) -> (4, 8)
|
||||
// (1, 3, 8) -> (4, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[2].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
|
||||
// (1, 4, 8)
|
||||
// (1, 4, 8) -> (5, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[2].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
for (int i = 0; i < key_idx; i++) {
|
||||
auto v = Get(Key(i));
|
||||
@@ -1192,12 +1317,12 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSecondPathRatio) {
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 2, 4)
|
||||
// (1, 2, 4) -> (3, 4)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(2, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 1, 2, 4) -> (8)
|
||||
// (1, 3, 4) -> (8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
@@ -1212,20 +1337,20 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSecondPathRatio) {
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 2, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(2, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 1, 2, 8) -> (4, 8)
|
||||
// (1, 2, 8) -> (3, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 4, 8)
|
||||
// (1, 3, 8) -> (4, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 4, 8) -> (5, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
for (int i = 0; i < key_idx; i++) {
|
||||
auto v = Get(Key(i));
|
||||
|
||||
+461
-5
@@ -8,7 +8,10 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/options_helper.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -84,7 +87,7 @@ TEST_F(DBWALTest, SyncWALNotBlockWrite) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::thread thread([&]() { ASSERT_OK(db_->SyncWAL()); });
|
||||
rocksdb::port::Thread thread([&]() { ASSERT_OK(db_->SyncWAL()); });
|
||||
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotBlockWrite:1");
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
@@ -116,7 +119,7 @@ TEST_F(DBWALTest, SyncWALNotWaitWrite) {
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
std::thread thread([&]() { ASSERT_OK(Put("foo2", "bar2")); });
|
||||
rocksdb::port::Thread thread([&]() { ASSERT_OK(Put("foo2", "bar2")); });
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:1");
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
@@ -156,6 +159,7 @@ TEST_F(DBWALTest, RecoverWithTableHandle) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
@@ -289,7 +293,170 @@ TEST_F(DBWALTest, RecoveryWithEmptyLog) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_F(DBWALTest, PreallocateBlock) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1000 * 1000;
|
||||
options.max_total_wal_size = 0;
|
||||
|
||||
size_t expected_preallocation_size = static_cast<size_t>(
|
||||
options.write_buffer_size + options.write_buffer_size / 10);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::atomic<int> called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTestWalFile.GetPreallocationStatus", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
size_t preallocation_size = *(static_cast<size_t*>(arg));
|
||||
ASSERT_EQ(expected_preallocation_size, preallocation_size);
|
||||
called.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
|
||||
options.max_total_wal_size = 1000 * 1000;
|
||||
expected_preallocation_size = static_cast<size_t>(options.max_total_wal_size);
|
||||
Reopen(options);
|
||||
called.store(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTestWalFile.GetPreallocationStatus", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
size_t preallocation_size = *(static_cast<size_t*>(arg));
|
||||
ASSERT_EQ(expected_preallocation_size, preallocation_size);
|
||||
called.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
|
||||
options.db_write_buffer_size = 800 * 1000;
|
||||
expected_preallocation_size =
|
||||
static_cast<size_t>(options.db_write_buffer_size);
|
||||
Reopen(options);
|
||||
called.store(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTestWalFile.GetPreallocationStatus", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
size_t preallocation_size = *(static_cast<size_t*>(arg));
|
||||
ASSERT_EQ(expected_preallocation_size, preallocation_size);
|
||||
called.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
|
||||
expected_preallocation_size = 700 * 1000;
|
||||
std::shared_ptr<WriteBufferManager> write_buffer_manager =
|
||||
std::make_shared<WriteBufferManager>(static_cast<uint64_t>(700 * 1000));
|
||||
options.write_buffer_manager = write_buffer_manager;
|
||||
Reopen(options);
|
||||
called.store(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTestWalFile.GetPreallocationStatus", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
size_t preallocation_size = *(static_cast<size_t*>(arg));
|
||||
ASSERT_EQ(expected_preallocation_size, preallocation_size);
|
||||
called.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
}
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBWALTest, FullPurgePreservesRecycledLog) {
|
||||
// For github issue #1303
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.recycle_log_file_num = 2;
|
||||
if (i != 0) {
|
||||
options.wal_dir = alternative_wal_dir_;
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
ASSERT_GT(log_files.size(), 0);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Now the original WAL is in log_files[0] and should be marked for
|
||||
// recycling.
|
||||
// Verify full purge cannot remove this file.
|
||||
JobContext job_context(0);
|
||||
dbfull()->TEST_LockMutex();
|
||||
dbfull()->FindObsoleteFiles(&job_context, true /* force */);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
dbfull()->PurgeObsoleteFiles(job_context);
|
||||
|
||||
if (i == 0) {
|
||||
ASSERT_OK(
|
||||
env_->FileExists(LogFileName(dbname_, log_files[0]->LogNumber())));
|
||||
} else {
|
||||
ASSERT_OK(env_->FileExists(
|
||||
LogFileName(alternative_wal_dir_, log_files[0]->LogNumber())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, GetSortedWalFiles) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
ASSERT_EQ(0, log_files.size());
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "v1"));
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
ASSERT_EQ(1, log_files.size());
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoveryWithLogDataForSomeCFs) {
|
||||
// Test for regression of WAL cleanup missing files that don't contain data
|
||||
// for every column family.
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
ASSERT_OK(Put(1, "foo", "v1"));
|
||||
ASSERT_OK(Put(1, "foo", "v2"));
|
||||
uint64_t earliest_log_nums[2];
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i > 0) {
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
|
||||
}
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
if (log_files.size() > 0) {
|
||||
earliest_log_nums[i] = log_files[0]->LogNumber();
|
||||
} else {
|
||||
earliest_log_nums[i] = port::kMaxUint64;
|
||||
}
|
||||
}
|
||||
// Check at least the first WAL was cleaned up during the recovery.
|
||||
ASSERT_LT(earliest_log_nums[0], earliest_log_nums[1]);
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithLargeLog) {
|
||||
do {
|
||||
{
|
||||
@@ -382,6 +549,7 @@ TEST_F(DBWALTest, RecoverCheckFileAmount) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100000;
|
||||
options.arena_block_size = 4 * 1024;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
CreateAndReopenWithCF({"pikachu", "dobrynia", "nikitich"}, options);
|
||||
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
@@ -468,6 +636,39 @@ TEST_F(DBWALTest, SyncMultipleLogs) {
|
||||
ASSERT_OK(dbfull()->SyncWAL());
|
||||
}
|
||||
|
||||
// Github issue 1339. Prior the fix we read sequence id from the first log to
|
||||
// a local variable, then keep increase the variable as we replay logs,
|
||||
// ignoring actual sequence id of the records. This is incorrect if some writes
|
||||
// come with WAL disabled.
|
||||
TEST_F(DBWALTest, PartOfWritesWithWALDisabled) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = CurrentOptions();
|
||||
options.env = fault_env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
WriteOptions wal_on, wal_off;
|
||||
wal_on.sync = true;
|
||||
wal_on.disableWAL = false;
|
||||
wal_off.disableWAL = true;
|
||||
CreateAndReopenWithCF({"dummy"}, options);
|
||||
ASSERT_OK(Put(1, "dummy", "d1", wal_on)); // seq id 1
|
||||
ASSERT_OK(Put(1, "dummy", "d2", wal_off));
|
||||
ASSERT_OK(Put(1, "dummy", "d3", wal_off));
|
||||
ASSERT_OK(Put(0, "key", "v4", wal_on)); // seq id 4
|
||||
ASSERT_OK(Flush(0));
|
||||
ASSERT_OK(Put(0, "key", "v5", wal_on)); // seq id 5
|
||||
ASSERT_EQ("v5", Get(0, "key"));
|
||||
// Simulate a crash.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
Close();
|
||||
fault_env->ResetState();
|
||||
ReopenWithColumnFamilies({"default", "dummy"}, options);
|
||||
// Prior to the fix, we may incorrectly recover "v5" with sequence id = 3.
|
||||
ASSERT_EQ("v5", Get(0, "key"));
|
||||
// Destroy DB before destruct fault_env.
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
//
|
||||
// Test WAL recovery for the various modes available
|
||||
//
|
||||
@@ -485,20 +686,20 @@ class RecoveryTestHelper {
|
||||
// Create WAL files with values filled in
|
||||
static void FillData(DBWALTest* test, const Options& options,
|
||||
const size_t wal_count, size_t* count) {
|
||||
const DBOptions& db_options = options;
|
||||
const ImmutableDBOptions db_options(options);
|
||||
|
||||
*count = 0;
|
||||
|
||||
shared_ptr<Cache> table_cache = NewLRUCache(50000, 16);
|
||||
EnvOptions env_options;
|
||||
WriteBuffer write_buffer(db_options.db_write_buffer_size);
|
||||
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
|
||||
|
||||
unique_ptr<VersionSet> versions;
|
||||
unique_ptr<WalManager> wal_manager;
|
||||
WriteController write_controller;
|
||||
|
||||
versions.reset(new VersionSet(test->dbname_, &db_options, env_options,
|
||||
table_cache.get(), &write_buffer,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller));
|
||||
|
||||
wal_manager.reset(new WalManager(db_options, env_options));
|
||||
@@ -756,8 +957,263 @@ TEST_F(DBWALTest, kSkipAnyCorruptedRecords) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, AvoidFlushDuringRecovery) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
|
||||
// Test with flush after recovery.
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("bar", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
ASSERT_OK(Put("bar", "v4"));
|
||||
ASSERT_EQ(1, TotalTableFiles());
|
||||
// Reopen DB. Check if WAL logs flushed.
|
||||
Reopen(options);
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v4", Get("bar"));
|
||||
ASSERT_EQ(2, TotalTableFiles());
|
||||
|
||||
// Test without flush after recovery.
|
||||
options.avoid_flush_during_recovery = true;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "v5"));
|
||||
ASSERT_OK(Put("bar", "v6"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "v7"));
|
||||
ASSERT_OK(Put("bar", "v8"));
|
||||
ASSERT_EQ(1, TotalTableFiles());
|
||||
// Reopen DB. WAL logs should not be flushed this time.
|
||||
Reopen(options);
|
||||
ASSERT_EQ("v7", Get("foo"));
|
||||
ASSERT_EQ("v8", Get("bar"));
|
||||
ASSERT_EQ(1, TotalTableFiles());
|
||||
|
||||
// Force flush with allow_2pc.
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.allow_2pc = true;
|
||||
ASSERT_OK(Put("foo", "v9"));
|
||||
ASSERT_OK(Put("bar", "v10"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("foo", "v11"));
|
||||
ASSERT_OK(Put("bar", "v12"));
|
||||
Reopen(options);
|
||||
ASSERT_EQ("v11", Get("foo"));
|
||||
ASSERT_EQ("v12", Get("bar"));
|
||||
ASSERT_EQ(2, TotalTableFiles());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, WalCleanupAfterAvoidFlushDuringRecovery) {
|
||||
// Verifies WAL files that were present during recovery, but not flushed due
|
||||
// to avoid_flush_during_recovery, will be considered for deletion at a later
|
||||
// stage. We check at least one such file is deleted during Flush().
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.avoid_flush_during_recovery = true;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
Reopen(options);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i > 0) {
|
||||
// Flush() triggers deletion of obsolete tracked files
|
||||
Flush();
|
||||
}
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
if (i == 0) {
|
||||
ASSERT_GT(log_files.size(), 0);
|
||||
} else {
|
||||
ASSERT_EQ(0, log_files.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithoutFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.create_if_missing = false;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 64 * 1024 * 1024;
|
||||
|
||||
size_t count = RecoveryTestHelper::FillData(this, &options);
|
||||
auto validateData = [this, count]() {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
ASSERT_NE(Get("key" + ToString(i)), "NOT_FOUND");
|
||||
}
|
||||
};
|
||||
Reopen(options);
|
||||
validateData();
|
||||
// Insert some data without flush
|
||||
ASSERT_OK(Put("foo", "foo_v1"));
|
||||
ASSERT_OK(Put("bar", "bar_v1"));
|
||||
Reopen(options);
|
||||
validateData();
|
||||
ASSERT_EQ(Get("foo"), "foo_v1");
|
||||
ASSERT_EQ(Get("bar"), "bar_v1");
|
||||
// Insert again and reopen
|
||||
ASSERT_OK(Put("foo", "foo_v2"));
|
||||
ASSERT_OK(Put("bar", "bar_v2"));
|
||||
Reopen(options);
|
||||
validateData();
|
||||
ASSERT_EQ(Get("foo"), "foo_v2");
|
||||
ASSERT_EQ(Get("bar"), "bar_v2");
|
||||
// manual flush and insert again
|
||||
Flush();
|
||||
ASSERT_EQ(Get("foo"), "foo_v2");
|
||||
ASSERT_EQ(Get("bar"), "bar_v2");
|
||||
ASSERT_OK(Put("foo", "foo_v3"));
|
||||
ASSERT_OK(Put("bar", "bar_v3"));
|
||||
Reopen(options);
|
||||
validateData();
|
||||
ASSERT_EQ(Get("foo"), "foo_v3");
|
||||
ASSERT_EQ(Get("bar"), "bar_v3");
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithoutFlushMultipleCF) {
|
||||
const std::string kSmallValue = "v";
|
||||
const std::string kLargeValue = DummyString(1024);
|
||||
Options options = CurrentOptions();
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.create_if_missing = false;
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
auto countWalFiles = [this]() {
|
||||
VectorLogPtr log_files;
|
||||
dbfull()->GetSortedWalFiles(log_files);
|
||||
return log_files.size();
|
||||
};
|
||||
|
||||
// Create DB with multiple column families and multiple log files.
|
||||
CreateAndReopenWithCF({"one", "two"}, options);
|
||||
ASSERT_OK(Put(0, "key1", kSmallValue));
|
||||
ASSERT_OK(Put(1, "key2", kLargeValue));
|
||||
Flush(1);
|
||||
ASSERT_EQ(1, countWalFiles());
|
||||
ASSERT_OK(Put(0, "key3", kSmallValue));
|
||||
ASSERT_OK(Put(2, "key4", kLargeValue));
|
||||
Flush(2);
|
||||
ASSERT_EQ(2, countWalFiles());
|
||||
|
||||
// Reopen, insert and flush.
|
||||
options.db_write_buffer_size = 64 * 1024 * 1024;
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, options);
|
||||
ASSERT_EQ(Get(0, "key1"), kSmallValue);
|
||||
ASSERT_EQ(Get(1, "key2"), kLargeValue);
|
||||
ASSERT_EQ(Get(0, "key3"), kSmallValue);
|
||||
ASSERT_EQ(Get(2, "key4"), kLargeValue);
|
||||
// Insert more data.
|
||||
ASSERT_OK(Put(0, "key5", kLargeValue));
|
||||
ASSERT_OK(Put(1, "key6", kLargeValue));
|
||||
ASSERT_EQ(3, countWalFiles());
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(2, "key7", kLargeValue));
|
||||
ASSERT_EQ(4, countWalFiles());
|
||||
|
||||
// Reopen twice and validate.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ReopenWithColumnFamilies({"default", "one", "two"}, options);
|
||||
ASSERT_EQ(Get(0, "key1"), kSmallValue);
|
||||
ASSERT_EQ(Get(1, "key2"), kLargeValue);
|
||||
ASSERT_EQ(Get(0, "key3"), kSmallValue);
|
||||
ASSERT_EQ(Get(2, "key4"), kLargeValue);
|
||||
ASSERT_EQ(Get(0, "key5"), kLargeValue);
|
||||
ASSERT_EQ(Get(1, "key6"), kLargeValue);
|
||||
ASSERT_EQ(Get(2, "key7"), kLargeValue);
|
||||
ASSERT_EQ(4, countWalFiles());
|
||||
}
|
||||
}
|
||||
|
||||
// In this test we are trying to do the following:
|
||||
// 1. Create a DB with corrupted WAL log;
|
||||
// 2. Open with avoid_flush_during_recovery = true;
|
||||
// 3. Append more data without flushing, which creates new WAL log.
|
||||
// 4. Open again. See if it can correctly handle previous corruption.
|
||||
TEST_F(DBWALTest, RecoverFromCorruptedWALWithoutFlush) {
|
||||
const int jstart = RecoveryTestHelper::kWALFileOffset;
|
||||
const int jend = jstart + RecoveryTestHelper::kWALFilesCount;
|
||||
const int kAppendKeys = 100;
|
||||
Options options = CurrentOptions();
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.create_if_missing = false;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 64 * 1024 * 1024;
|
||||
|
||||
auto getAll = [this]() {
|
||||
std::vector<std::pair<std::string, std::string>> data;
|
||||
ReadOptions ropt;
|
||||
Iterator* iter = dbfull()->NewIterator(ropt);
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
data.push_back(
|
||||
std::make_pair(iter->key().ToString(), iter->value().ToString()));
|
||||
}
|
||||
delete iter;
|
||||
return data;
|
||||
};
|
||||
for (auto& mode : wal_recovery_mode_string_map) {
|
||||
options.wal_recovery_mode = mode.second;
|
||||
for (auto trunc : {true, false}) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = jstart; j < jend; j++) {
|
||||
// Create corrupted WAL
|
||||
RecoveryTestHelper::FillData(this, &options);
|
||||
RecoveryTestHelper::CorruptWAL(this, options, /*off=*/i * .3,
|
||||
/*len%=*/.1, /*wal=*/j, trunc);
|
||||
// Skip the test if DB won't open.
|
||||
if (!TryReopen(options).ok()) {
|
||||
ASSERT_TRUE(options.wal_recovery_mode ==
|
||||
WALRecoveryMode::kAbsoluteConsistency ||
|
||||
(!trunc &&
|
||||
options.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords));
|
||||
continue;
|
||||
}
|
||||
ASSERT_OK(TryReopen(options));
|
||||
// Append some more data.
|
||||
for (int k = 0; k < kAppendKeys; k++) {
|
||||
std::string key = "extra_key" + ToString(k);
|
||||
std::string value = DummyString(RecoveryTestHelper::kValueSize);
|
||||
ASSERT_OK(Put(key, value));
|
||||
}
|
||||
// Save data for comparison.
|
||||
auto data = getAll();
|
||||
// Reopen. Verify data.
|
||||
ASSERT_OK(TryReopen(options));
|
||||
auto actual_data = getAll();
|
||||
ASSERT_EQ(data, actual_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBWALTest, WalTermTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
wo.disableWAL = false;
|
||||
|
||||
WriteBatch batch;
|
||||
batch.Put("foo", "bar");
|
||||
batch.MarkWalTerminationPoint();
|
||||
batch.Put("foo2", "bar2");
|
||||
|
||||
ASSERT_OK(dbfull()->Write(wo, &batch));
|
||||
|
||||
// make sure we can re-open it.
|
||||
ASSERT_OK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo2"));
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+16
-2
@@ -20,9 +20,18 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// kValueTypeForSeek defines the ValueType that should be passed when
|
||||
// constructing a ParsedInternalKey object for seeking to a particular
|
||||
// sequence number (since we sort sequence numbers in decreasing order
|
||||
// and the value type is embedded as the low 8 bits in the sequence
|
||||
// number in internal keys, we need to use the highest-numbered
|
||||
// ValueType, not the lowest).
|
||||
const ValueType kValueTypeForSeek = kTypeSingleDeletion;
|
||||
const ValueType kValueTypeForSeekForPrev = kTypeDeletion;
|
||||
|
||||
uint64_t PackSequenceAndType(uint64_t seq, ValueType t) {
|
||||
assert(seq <= kMaxSequenceNumber);
|
||||
assert(IsValueType(t));
|
||||
assert(IsExtendedValueType(t));
|
||||
return (seq << 8) | t;
|
||||
}
|
||||
|
||||
@@ -31,7 +40,7 @@ void UnPackSequenceAndType(uint64_t packed, uint64_t* seq, ValueType* t) {
|
||||
*t = static_cast<ValueType>(packed & 0xff);
|
||||
|
||||
assert(*seq <= kMaxSequenceNumber);
|
||||
assert(IsValueType(*t));
|
||||
assert(IsExtendedValueType(*t));
|
||||
}
|
||||
|
||||
void AppendInternalKey(std::string* result, const ParsedInternalKey& key) {
|
||||
@@ -39,6 +48,11 @@ void AppendInternalKey(std::string* result, const ParsedInternalKey& key) {
|
||||
PutFixed64(result, PackSequenceAndType(key.sequence, key.type));
|
||||
}
|
||||
|
||||
void AppendInternalKeyFooter(std::string* result, SequenceNumber s,
|
||||
ValueType t) {
|
||||
PutFixed64(result, PackSequenceAndType(s, t));
|
||||
}
|
||||
|
||||
std::string ParsedInternalKey::DebugString(bool hex) const {
|
||||
char buf[50];
|
||||
snprintf(buf, sizeof(buf), "' @ %" PRIu64 ": %d", sequence,
|
||||
|
||||
Loaded 100 of 716 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user