Compare commits
1067
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f80f4b2e24 | ||
|
|
44206187a2 | ||
|
|
34ac7488eb | ||
|
|
60cbb0f1d1 | ||
|
|
ff580de3a2 | ||
|
|
4b3e23e2fb | ||
|
|
1f0679da36 | ||
|
|
2ae3dd6b0a | ||
|
|
d591df1529 | ||
|
|
cf799eae84 | ||
|
|
5c816db669 | ||
|
|
7bbd6d73c8 | ||
|
|
0d5b349744 | ||
|
|
ccf8d42e4b | ||
|
|
93a748f86b | ||
|
|
3e774999e7 | ||
|
|
83563f6bb2 | ||
|
|
875a82bdf8 | ||
|
|
1e3fe8cab3 | ||
|
|
127becc9e0 | ||
|
|
b57b1c8ff8 | ||
|
|
bb83aef1da | ||
|
|
cbb9ac38d2 | ||
|
|
6749c2bcf0 | ||
|
|
9957c11e2d | ||
|
|
b4b30dcf9d | ||
|
|
cddd3b6896 | ||
|
|
b4528b797d | ||
|
|
e070651039 | ||
|
|
7a32ec39fd | ||
|
|
50dfee2039 | ||
|
|
9e92c038b8 | ||
|
|
c167902cbf | ||
|
|
c8bd5dbb4f | ||
|
|
2c932d7b80 | ||
|
|
8dff4f3437 | ||
|
|
eec6b5142e | ||
|
|
d2c080d923 | ||
|
|
754d5a3ba1 | ||
|
|
4aaa7b7bd7 | ||
|
|
b93938914d | ||
|
|
4b6bdcbcce | ||
|
|
31c0e38d86 | ||
|
|
8f5b94c75f | ||
|
|
d1fc7b96ed | ||
|
|
b253a7aa8d | ||
|
|
1625c74a36 | ||
|
|
3c716ee33d | ||
|
|
b03eab2a06 | ||
|
|
6a0a005ff2 | ||
|
|
51216ee433 | ||
|
|
5f7ca6a2f6 | ||
|
|
79bdcf0b3d | ||
|
|
b88f06b16f | ||
|
|
e6d7e01be6 | ||
|
|
889bcd1bf1 | ||
|
|
9637887264 | ||
|
|
a9eaf0a130 | ||
|
|
a1680fa612 | ||
|
|
d7bd4f7a1c | ||
|
|
0be40b9fd4 | ||
|
|
9e5e6c2f86 | ||
|
|
1ab19b445f | ||
|
|
ee32ed045c | ||
|
|
82ba449dd0 | ||
|
|
9b7febb538 | ||
|
|
fae6bfb80b | ||
|
|
45bb6e801f | ||
|
|
b08aa26a76 | ||
|
|
8dacbed556 | ||
|
|
b44df43fcb | ||
|
|
84af829053 | ||
|
|
f5ee64f9aa | ||
|
|
ce4891b33d | ||
|
|
ffa46b8f0b | ||
|
|
bf05c261d2 | ||
|
|
952ae9fbe3 | ||
|
|
6f2dbb0011 | ||
|
|
f77980fce8 | ||
|
|
d90c1e6eae | ||
|
|
d987f463eb | ||
|
|
89beaf2460 | ||
|
|
0c5d12b914 | ||
|
|
32ca2e8a73 | ||
|
|
d7f6bc1614 | ||
|
|
83d878f111 | ||
|
|
0fc6441c96 | ||
|
|
81cf9979ed | ||
|
|
5be1620629 | ||
|
|
685b87e2ef | ||
|
|
72429c2106 | ||
|
|
7707fe15a9 | ||
|
|
c583ffff41 | ||
|
|
7c1ab8b8e2 | ||
|
|
570fbd0149 | ||
|
|
da1f09e9b5 | ||
|
|
9e45984a97 | ||
|
|
b2fb4f4b9b | ||
|
|
51277b17a7 | ||
|
|
32f5ef864e | ||
|
|
a8f28297ec | ||
|
|
0ae13c9731 | ||
|
|
1895dbb768 | ||
|
|
a6cbbd0f31 | ||
|
|
f1f52af9cf | ||
|
|
e3c619eca4 | ||
|
|
57e59d97db | ||
|
|
d7cada0f38 | ||
|
|
fd2ac1fcfc | ||
|
|
0181b0e7a1 | ||
|
|
2b5e5cfa4c | ||
|
|
bfb0d02622 | ||
|
|
534b70643b | ||
|
|
3d0f729572 | ||
|
|
fd4ed1e425 | ||
|
|
693450393c | ||
|
|
8ee7584cff | ||
|
|
0a56a73bd9 | ||
|
|
3a1d3090f2 | ||
|
|
0727393627 | ||
|
|
d2ef059471 | ||
|
|
2e894a512b | ||
|
|
34b0209f05 | ||
|
|
046f575632 | ||
|
|
1c77104b5e | ||
|
|
386be8990a | ||
|
|
76d9c7c82a | ||
|
|
98f08034a1 | ||
|
|
308cf77437 | ||
|
|
ce64f469f4 | ||
|
|
e632c3db1b | ||
|
|
239d22ed72 | ||
|
|
fefbe96816 | ||
|
|
b1f662d639 | ||
|
|
aa5b27b040 | ||
|
|
10d254ec1b | ||
|
|
e8a44821d0 | ||
|
|
92e48f0717 | ||
|
|
9742c44db5 | ||
|
|
5d16f51950 | ||
|
|
1ed2e8b5bc | ||
|
|
b20803b766 | ||
|
|
8cf1daf823 | ||
|
|
083b54472c | ||
|
|
1b9fea0669 | ||
|
|
d3c37c7159 | ||
|
|
807f020184 | ||
|
|
b400be68b7 | ||
|
|
e39bdb58ec | ||
|
|
83c8977033 | ||
|
|
682990eeb3 | ||
|
|
7ee766aee1 | ||
|
|
848c5ad784 | ||
|
|
a41fb1b036 | ||
|
|
915117ae7b | ||
|
|
de5fa78762 | ||
|
|
b0c8efd840 | ||
|
|
1dcd5edbf9 | ||
|
|
87d7fc8e5d | ||
|
|
100a747eab | ||
|
|
105fd03ef8 | ||
|
|
9395a9c1d9 | ||
|
|
02f7efd9d4 | ||
|
|
aea86c5977 | ||
|
|
8d81592bec | ||
|
|
93ebb31b17 | ||
|
|
bfed57e3e0 | ||
|
|
a6bbbeffcf | ||
|
|
9110747194 | ||
|
|
693b209cb4 | ||
|
|
a01564158e | ||
|
|
23647390e6 | ||
|
|
96bca72bb4 | ||
|
|
5d6da23108 | ||
|
|
b44f504ced | ||
|
|
6163e7b13a | ||
|
|
66547bd28a | ||
|
|
e6ab823473 | ||
|
|
a2c0bce4cc | ||
|
|
2c8239f9cb | ||
|
|
e9551c9e8d | ||
|
|
0ac80086bc | ||
|
|
9ab36ddc36 | ||
|
|
a6bc34c09b | ||
|
|
3bb6b16c4a | ||
|
|
1cde2442aa | ||
|
|
2910cee32b | ||
|
|
7fefc7b245 | ||
|
|
68d213f4c9 | ||
|
|
7d9a2ce827 | ||
|
|
c6ef287452 | ||
|
|
e6091a2b7e | ||
|
|
69dc0a06bf | ||
|
|
dfea168499 | ||
|
|
241d1bd7e1 | ||
|
|
56addcd172 | ||
|
|
8d8e1810d4 | ||
|
|
b5ddc11c46 | ||
|
|
f107746e90 | ||
|
|
f68a26381a | ||
|
|
5a2cc90786 | ||
|
|
3881d5c1f2 | ||
|
|
958f2c00c8 | ||
|
|
165333b0fc | ||
|
|
cc827e157a | ||
|
|
7b4e25008a | ||
|
|
f66e5a9c3d | ||
|
|
9954f5b4a4 | ||
|
|
53936661c8 | ||
|
|
e2552ff8a9 | ||
|
|
6ab08cc3e1 | ||
|
|
3205373bb3 | ||
|
|
3b8d1f7076 | ||
|
|
5832c25ad1 | ||
|
|
569a3d9ff8 | ||
|
|
69bc7108bd | ||
|
|
8a557e9d71 | ||
|
|
fe0f071993 | ||
|
|
effa53339c | ||
|
|
f6fb0e4620 | ||
|
|
cf46a014c1 | ||
|
|
d72b5fa16d | ||
|
|
0151507cef | ||
|
|
0fea781f8f | ||
|
|
3c4cadc020 | ||
|
|
e2615ccf88 | ||
|
|
595deb0e13 | ||
|
|
5372d1aab2 | ||
|
|
52eca14ca6 | ||
|
|
1155f5ac63 | ||
|
|
7319953ae3 | ||
|
|
b8e106a72e | ||
|
|
f30f8eba2f | ||
|
|
7ed2296fc5 | ||
|
|
0691a46669 | ||
|
|
d7f01c1f7d | ||
|
|
4d71929c60 | ||
|
|
dadce055f8 | ||
|
|
d2867580ab | ||
|
|
8dc238efae | ||
|
|
7cd4b5e11c | ||
|
|
00043f9509 | ||
|
|
9b81f3f263 | ||
|
|
2747d1a1a9 | ||
|
|
9f7ed0b232 | ||
|
|
c176ea7366 | ||
|
|
d50e4bb819 | ||
|
|
4485ca74f6 | ||
|
|
7f72b214e5 | ||
|
|
bc8cc1adb3 | ||
|
|
d03c2de901 | ||
|
|
7d3c5f1e64 | ||
|
|
81573de393 | ||
|
|
a80dd5d001 | ||
|
|
7e0077f293 | ||
|
|
1eb035e6ad | ||
|
|
bdd14ac517 | ||
|
|
63e5d00bdf | ||
|
|
c25fb3d74f | ||
|
|
572d41325b | ||
|
|
e02120cdf7 | ||
|
|
7b1221a74d | ||
|
|
d6b4f994ad | ||
|
|
66a41dafc4 | ||
|
|
f19b9c155e | ||
|
|
5d016fdb6b | ||
|
|
cae9924fd9 | ||
|
|
089f28e8ba | ||
|
|
dcb44d3a09 | ||
|
|
f99cba0b92 | ||
|
|
e10dc0dace | ||
|
|
49a89d636b | ||
|
|
b94f91b9b2 | ||
|
|
06a17c8511 | ||
|
|
eebd70aaa6 | ||
|
|
779c775df9 | ||
|
|
1d4d2925c2 | ||
|
|
b67db7c2d5 | ||
|
|
18953b2998 | ||
|
|
527b7eb074 | ||
|
|
34a97c447f | ||
|
|
1c525e20b1 | ||
|
|
0bcce50285 | ||
|
|
96e0b2a2b1 | ||
|
|
f3794d2c42 | ||
|
|
97fd736923 | ||
|
|
ba950a3d1f | ||
|
|
dad5a902d3 | ||
|
|
3e33fbcd35 | ||
|
|
444555d3e4 | ||
|
|
6a74f524e5 | ||
|
|
9c5b59ac01 | ||
|
|
1840601f8e | ||
|
|
88962af8b2 | ||
|
|
df22cdf6e9 | ||
|
|
041446005b | ||
|
|
7c39a9b0e1 | ||
|
|
05cd92f766 | ||
|
|
d724e7226c | ||
|
|
738c782f02 | ||
|
|
33b05c859a | ||
|
|
ed369d7223 | ||
|
|
fb32bb12b1 | ||
|
|
3e996035ea | ||
|
|
ae8a94d615 | ||
|
|
69a74a567b | ||
|
|
1b46d4f09a | ||
|
|
c6f13892e0 | ||
|
|
05e0f32345 | ||
|
|
8171e6de13 | ||
|
|
b042495f37 | ||
|
|
420ccdfbae | ||
|
|
41466a4147 | ||
|
|
9798f2d711 | ||
|
|
5f3543921b | ||
|
|
a41366cc3a | ||
|
|
4c7e4b91a6 | ||
|
|
28183f4b66 | ||
|
|
a0abc1eddd | ||
|
|
676644f2db | ||
|
|
22ac56d62d | ||
|
|
143f7f634d | ||
|
|
955faef23a | ||
|
|
2e159e6ea3 | ||
|
|
02b533fec3 | ||
|
|
de3099e6d8 | ||
|
|
368f67f422 | ||
|
|
13c9a094d1 | ||
|
|
0f8b7a19ef | ||
|
|
8f7d977f90 | ||
|
|
064440ee84 | ||
|
|
50641d0569 | ||
|
|
f5ecc77e29 | ||
|
|
655bb6e21c | ||
|
|
ab4cdd2ed4 | ||
|
|
d3834c5069 | ||
|
|
7a1b2c58c3 | ||
|
|
7fc6d6c6b1 | ||
|
|
dec5db4e71 | ||
|
|
d47aeb1fc8 | ||
|
|
e47584b286 | ||
|
|
f17d178d03 | ||
|
|
d88fd1588d | ||
|
|
119797014c | ||
|
|
e8f568644b | ||
|
|
b736a95b0d | ||
|
|
d909d12cb3 | ||
|
|
201ffa9d62 | ||
|
|
5619f51528 | ||
|
|
d99d06b125 | ||
|
|
1b9ade7f65 | ||
|
|
e01312642b | ||
|
|
cd9514f4c8 | ||
|
|
f111c23415 | ||
|
|
6857c4c208 | ||
|
|
52897f8f9a | ||
|
|
8bc70e9840 | ||
|
|
5b81303f04 | ||
|
|
ede524c064 | ||
|
|
aeed09c346 | ||
|
|
70b80aa5fa | ||
|
|
e8d4d095cc | ||
|
|
c50a3568a5 | ||
|
|
17e96b91b5 | ||
|
|
c5e7a8517b | ||
|
|
c36773777d | ||
|
|
d69e0b8561 | ||
|
|
19ccc22a43 | ||
|
|
6968a0cfcb | ||
|
|
df618fbd9f | ||
|
|
94acd43340 | ||
|
|
14bdd745d1 | ||
|
|
aa3f5de16f | ||
|
|
9f7890cb1a | ||
|
|
1dc34c16b5 | ||
|
|
b0e7be7ce2 | ||
|
|
026ab4c922 | ||
|
|
07ba64c200 | ||
|
|
b4b35cf904 | ||
|
|
ba974c9810 | ||
|
|
9817db7edd | ||
|
|
515bb0e6bd | ||
|
|
bc7ea8fb20 | ||
|
|
2caf8e4c80 | ||
|
|
e1f3cd6ec3 | ||
|
|
02e821530a | ||
|
|
b6c3d80edb | ||
|
|
0042586190 | ||
|
|
c6dce838b7 | ||
|
|
5b885bb164 | ||
|
|
95f46e6077 | ||
|
|
31f58311e3 | ||
|
|
130613e166 | ||
|
|
50fd4fa7e6 | ||
|
|
e6a9a6b428 | ||
|
|
d6be93537e | ||
|
|
1ef35cf466 | ||
|
|
1eb0e3c1c6 | ||
|
|
e50313b207 | ||
|
|
ac3f8d1ac7 | ||
|
|
202b97eff7 | ||
|
|
f25aebf269 | ||
|
|
9b1e636a66 | ||
|
|
5147f0153a | ||
|
|
88f7739ae0 | ||
|
|
c0c033a0c1 | ||
|
|
73300c5700 | ||
|
|
37033179fe | ||
|
|
3e6a5ac9cf | ||
|
|
dcd297eb5b | ||
|
|
4a9742e696 | ||
|
|
8edbae2e04 | ||
|
|
63c6735ffc | ||
|
|
8a0441c144 | ||
|
|
460c5ed2b3 | ||
|
|
8f44f0937b | ||
|
|
1803b1587b | ||
|
|
a5325061f2 | ||
|
|
a10a0df09f | ||
|
|
031044c7ea | ||
|
|
ca755e4905 | ||
|
|
3063a028b3 | ||
|
|
2c659acc7d | ||
|
|
341a8caef9 | ||
|
|
366f8c2dc6 | ||
|
|
5803540422 | ||
|
|
f17c492a63 | ||
|
|
aad6a7ce15 | ||
|
|
1cb48c7bf6 | ||
|
|
f25c405ad2 | ||
|
|
c9920849dd | ||
|
|
81b240f81d | ||
|
|
6a902b29e1 | ||
|
|
9c31ac7925 | ||
|
|
f0d8d1680f | ||
|
|
6dcbcacb10 | ||
|
|
3196717cee | ||
|
|
f95578ac02 | ||
|
|
57ca6facc3 | ||
|
|
561a1f8d8a | ||
|
|
1449c8b9fe | ||
|
|
1938af27f5 | ||
|
|
123bc125cf | ||
|
|
ecbe168b54 | ||
|
|
00cef78858 | ||
|
|
d05da0fabd | ||
|
|
4db3f2a443 | ||
|
|
fd710d590a | ||
|
|
9b98c9edd3 | ||
|
|
4893eedacb | ||
|
|
fb50163b47 | ||
|
|
3b8463f9d2 | ||
|
|
42c7677690 | ||
|
|
8665f25031 | ||
|
|
1c6b573147 | ||
|
|
90162da1a6 | ||
|
|
1ca6c1dd56 | ||
|
|
2d27c8ecdb | ||
|
|
59cda9cfb9 | ||
|
|
091af64003 | ||
|
|
a12210c875 | ||
|
|
43649fc5b4 | ||
|
|
01cc25653a | ||
|
|
a010e4a4b2 | ||
|
|
817a11ff11 | ||
|
|
c52e1fc78c | ||
|
|
d74871da40 | ||
|
|
a25cf15ecd | ||
|
|
c505df8830 | ||
|
|
f214ada545 | ||
|
|
c10aa3b8b0 | ||
|
|
6311d709c3 | ||
|
|
8ab77465db | ||
|
|
201bf73ef7 | ||
|
|
49784c8122 | ||
|
|
3e03f308b5 | ||
|
|
95cecb0229 | ||
|
|
7676f5b15f | ||
|
|
d5b057534a | ||
|
|
7a15f6dfc7 | ||
|
|
712d825c23 | ||
|
|
4073758874 | ||
|
|
3543a3c763 | ||
|
|
3122912920 | ||
|
|
f11fdf6717 | ||
|
|
572bcca9c2 | ||
|
|
10fa064109 | ||
|
|
7cd779d304 | ||
|
|
628d168049 | ||
|
|
37423757b5 | ||
|
|
58752bb8fe | ||
|
|
7e7f70ff3e | ||
|
|
f77c227b2d | ||
|
|
21d842b0b0 | ||
|
|
86f9ed6248 | ||
|
|
dcd7b684bb | ||
|
|
f715d0b860 | ||
|
|
98d690b849 | ||
|
|
45535e8a63 | ||
|
|
c78895aaea | ||
|
|
d5ede50b04 | ||
|
|
fab6132cb3 | ||
|
|
e0a8709c09 | ||
|
|
17795fd5ce | ||
|
|
d6738ffa5d | ||
|
|
e72642667f | ||
|
|
3785b1efd4 | ||
|
|
7aeae62f84 | ||
|
|
e5e33f8cb7 | ||
|
|
a836d5947d | ||
|
|
7cdd5d9e41 | ||
|
|
3f5d3fec36 | ||
|
|
90094a2dca | ||
|
|
cc9924ffa6 | ||
|
|
5e6b4ab99e | ||
|
|
2d6d374162 | ||
|
|
3137d4f35c | ||
|
|
6ff9d23fd4 | ||
|
|
d4939c838b | ||
|
|
cd306f1d23 | ||
|
|
5fd07e0889 | ||
|
|
b0796ff5fd | ||
|
|
9dfa074cd6 | ||
|
|
033761c5f9 | ||
|
|
2926ac494e | ||
|
|
06143c2e8c | ||
|
|
f00d28fdab | ||
|
|
4705f29e2f | ||
|
|
b2cb10de5f | ||
|
|
1005a725d4 | ||
|
|
50768cd242 | ||
|
|
cc3ebbe519 | ||
|
|
8056e349e0 | ||
|
|
0001f59c8d | ||
|
|
2941792a85 | ||
|
|
320bc3c032 | ||
|
|
2825e51550 | ||
|
|
cbdd47d056 | ||
|
|
516c8c6e23 | ||
|
|
ef1236b600 | ||
|
|
28cbed1d13 | ||
|
|
cde199a797 | ||
|
|
5589a0a69c | ||
|
|
09e8fb17cd | ||
|
|
d71c4c2215 | ||
|
|
6f621e6480 | ||
|
|
8e33c2d301 | ||
|
|
52aca124c3 | ||
|
|
c7159d58f1 | ||
|
|
5d6e8fe3e3 | ||
|
|
99762dac23 | ||
|
|
57956b3fd2 | ||
|
|
b323645227 | ||
|
|
dd9502f373 | ||
|
|
aa8a3077a2 | ||
|
|
8e81e5f29e | ||
|
|
63dd1fb599 | ||
|
|
32561a9912 | ||
|
|
3fdc8608f3 | ||
|
|
9e2e2067c6 | ||
|
|
f112c77fcc | ||
|
|
888d69a87a | ||
|
|
bd183cdee1 | ||
|
|
95b4f899c5 | ||
|
|
2946155313 | ||
|
|
f2ca2aa1f7 | ||
|
|
b20bf10fda | ||
|
|
208c15bc16 | ||
|
|
c3d3c82f3a | ||
|
|
b22d9cc27d | ||
|
|
908cf5042e | ||
|
|
6ec1641294 | ||
|
|
7b79ec360b | ||
|
|
718aee242d | ||
|
|
56fc6b7deb | ||
|
|
528a43bd6c | ||
|
|
b357803362 | ||
|
|
448808529a | ||
|
|
a0bc208412 | ||
|
|
6b0172b9bf | ||
|
|
bc1eccb66d | ||
|
|
a9249d4a82 | ||
|
|
3c373494d6 | ||
|
|
40761e4031 | ||
|
|
5aef606d3b | ||
|
|
4671cd6002 | ||
|
|
2ec0b2cd20 | ||
|
|
07effa3925 | ||
|
|
cd76a71de3 | ||
|
|
5e63c0955d | ||
|
|
e523afa741 | ||
|
|
c2a98a60f9 | ||
|
|
6bc92d6d31 | ||
|
|
13cf8bbeb7 | ||
|
|
60c0d4104d | ||
|
|
52fd4445a4 | ||
|
|
693ba4ed4a | ||
|
|
5397f0b033 | ||
|
|
6c78ec7c41 | ||
|
|
fed4bd5269 | ||
|
|
c0882864f2 | ||
|
|
1a7e2e663e | ||
|
|
faa87ed839 | ||
|
|
83f585e30a | ||
|
|
7a244bb847 | ||
|
|
1b925119b5 | ||
|
|
3eb847e28f | ||
|
|
9dc8f30e47 | ||
|
|
3ce6441a0c | ||
|
|
6d00a1a5cf | ||
|
|
aaca32a5cd | ||
|
|
54c96d1995 | ||
|
|
e5a2d59446 | ||
|
|
d95bead383 | ||
|
|
51ea3cf30c | ||
|
|
6ad6c1d780 | ||
|
|
32f34f63fd | ||
|
|
98aefac09b | ||
|
|
5dc84850ad | ||
|
|
45b711f9d1 | ||
|
|
1ef187533c | ||
|
|
6a71de7835 | ||
|
|
18496b6285 | ||
|
|
b9db899f6c | ||
|
|
4e9d2f08b1 | ||
|
|
06ad1c15d7 | ||
|
|
30a603ce05 | ||
|
|
63ae679fe1 | ||
|
|
b92bbf4de8 | ||
|
|
4781eda722 | ||
|
|
2fd93c5789 | ||
|
|
329519d4aa | ||
|
|
52434a583a | ||
|
|
18a5f46b61 | ||
|
|
16a019dba0 | ||
|
|
1a4c3224c9 | ||
|
|
c3317686f8 | ||
|
|
4622472123 | ||
|
|
f1748f8c76 | ||
|
|
0c101dccc8 | ||
|
|
dc78bf1ccb | ||
|
|
a7cc3aac28 | ||
|
|
339631530a | ||
|
|
fb2bdf7d05 | ||
|
|
c45f1328ac | ||
|
|
050d58007f | ||
|
|
49d86746ed | ||
|
|
ef4e11ecb8 | ||
|
|
fd702e43f1 | ||
|
|
249e0fdd36 | ||
|
|
6208118b13 | ||
|
|
73020b6a93 | ||
|
|
a9667e84ad | ||
|
|
ab1524ff1a | ||
|
|
1069d0b4bf | ||
|
|
4a980f4ddb | ||
|
|
9fbb9a551e | ||
|
|
c24a7c03c2 | ||
|
|
5a8280d577 | ||
|
|
da83e84125 | ||
|
|
ef9a3cadcf | ||
|
|
c983d57f73 | ||
|
|
3204a39ada | ||
|
|
f4319c1280 | ||
|
|
762396d997 | ||
|
|
c575e7df94 | ||
|
|
aa6409f2e8 | ||
|
|
e603e57588 | ||
|
|
049b48ef79 | ||
|
|
0ba0a6ab0a | ||
|
|
7475ea1b0b | ||
|
|
d0654af74f | ||
|
|
d5f8df5997 | ||
|
|
39acb312ef | ||
|
|
b643c38204 | ||
|
|
dd70f5efd7 | ||
|
|
bb38628f0e | ||
|
|
0cf3af6857 | ||
|
|
edb993d52b | ||
|
|
aea333bb78 | ||
|
|
45817db8c3 | ||
|
|
dbef17c2e1 | ||
|
|
42a8f22c21 | ||
|
|
6e06e0dfc7 | ||
|
|
bf500ab44b | ||
|
|
4d4eca5433 | ||
|
|
4b01ad1e30 | ||
|
|
3b60929bdc | ||
|
|
a45d141597 | ||
|
|
8cbe254491 | ||
|
|
ab365ce3da | ||
|
|
aa3e70f9a0 | ||
|
|
cda8d42a76 | ||
|
|
67f32bf1b1 | ||
|
|
7a3f363f34 | ||
|
|
91eac2cdb3 | ||
|
|
98a928147f | ||
|
|
8a9045c028 | ||
|
|
6c65a11b8d | ||
|
|
7dd53182e0 | ||
|
|
53b95d830a | ||
|
|
3028a890ef | ||
|
|
39c6fbddb1 | ||
|
|
704cb4e929 | ||
|
|
8e4f323439 | ||
|
|
937702ea1d | ||
|
|
3ea56ffb38 | ||
|
|
becd3fc5a3 | ||
|
|
4c8603815c | ||
|
|
3ae75c2d2b | ||
|
|
9371c005aa | ||
|
|
16bd6b3f5d | ||
|
|
3dd78a6101 | ||
|
|
41b678814b | ||
|
|
d0ccb074d5 | ||
|
|
cb0fc7cca2 | ||
|
|
5e40ea6ee7 | ||
|
|
427c5999f1 | ||
|
|
a91f58b8fd | ||
|
|
3013799664 | ||
|
|
f8220a658e | ||
|
|
f1d2b29450 | ||
|
|
cb93752aff | ||
|
|
549cad378d | ||
|
|
631f863dd8 | ||
|
|
4396682c07 | ||
|
|
ca4ee4e3e4 | ||
|
|
2da09e0732 | ||
|
|
4986dc6da9 | ||
|
|
1a70af2ae1 | ||
|
|
5a8cb77d0d | ||
|
|
600fc241cf | ||
|
|
fa797408d9 | ||
|
|
3fac516950 | ||
|
|
2f65aa2337 | ||
|
|
f5210a8fb0 | ||
|
|
8f1295c00f | ||
|
|
18a3768e07 | ||
|
|
95e3dbc104 | ||
|
|
40355eac3e | ||
|
|
3a3b59a054 | ||
|
|
12e17cfd6e | ||
|
|
203e341bf4 | ||
|
|
6e6cd84ad7 | ||
|
|
252725d50f | ||
|
|
c15633ea9e | ||
|
|
27c9ae667e | ||
|
|
16f96efbe7 | ||
|
|
5787e7adc7 | ||
|
|
4631119179 | ||
|
|
0a0c2146c8 | ||
|
|
cedea7e400 | ||
|
|
66cef6646c | ||
|
|
64aa4b80e6 | ||
|
|
551a3cb1aa | ||
|
|
0942188b0a | ||
|
|
070c481871 | ||
|
|
6df4d29422 | ||
|
|
6e1e5bd08b | ||
|
|
ba365298ed | ||
|
|
e1c9620951 | ||
|
|
e776474f68 | ||
|
|
4589e861ab | ||
|
|
55324aa56a | ||
|
|
18d42a5c35 | ||
|
|
71d5a10621 | ||
|
|
ac595ebe37 | ||
|
|
50a6a3ca38 | ||
|
|
f83a89f82a | ||
|
|
bf905a78f0 | ||
|
|
fad9483fc9 | ||
|
|
46a78c69e7 | ||
|
|
33e0b3e1c0 | ||
|
|
4e9bfcf84b | ||
|
|
75c6b4c64a | ||
|
|
8536854266 | ||
|
|
6016dd0f8b | ||
|
|
47ef9e5cbd | ||
|
|
4858fd893c | ||
|
|
6b6e6f6c1e | ||
|
|
cbe5c04a0f | ||
|
|
b0a0f03b96 | ||
|
|
08b6a04055 | ||
|
|
f1982ca63d | ||
|
|
afe66288fe | ||
|
|
2794cfced4 | ||
|
|
d6b45a56e7 | ||
|
|
a55d980436 | ||
|
|
6037d1f326 | ||
|
|
0b43b51a2e | ||
|
|
184abac694 | ||
|
|
0ba5b2c3d6 | ||
|
|
0b3d74ea67 | ||
|
|
ea3bd614b8 | ||
|
|
d45edc493d | ||
|
|
271df3dad4 | ||
|
|
5a193872f8 | ||
|
|
b5512d77a1 | ||
|
|
42bda8eaba | ||
|
|
7e5d3f4f16 | ||
|
|
5f3920e491 | ||
|
|
3880d2616c | ||
|
|
156e596f9c | ||
|
|
fc58516cca | ||
|
|
a4efcd35af | ||
|
|
27fe1658a2 | ||
|
|
47feb2719c | ||
|
|
c21af6b351 | ||
|
|
821c12c482 | ||
|
|
4602f8c391 | ||
|
|
27a927fe00 | ||
|
|
3cdc6e8d84 | ||
|
|
89b52da387 | ||
|
|
50ad880ad5 | ||
|
|
15bed4bbcc | ||
|
|
10986ae3a8 | ||
|
|
2baa76b2ee | ||
|
|
ee460301fd | ||
|
|
8ebe2fce3d | ||
|
|
8198a697fd | ||
|
|
35acae360a | ||
|
|
8aaa8b0b11 | ||
|
|
25e18a7de9 | ||
|
|
bb090b6d20 | ||
|
|
a9c4e3970f | ||
|
|
d5ecc2347a | ||
|
|
58971a430d | ||
|
|
1512274f4c | ||
|
|
e310d33c12 | ||
|
|
ad32ba0343 | ||
|
|
4e30a5b4e5 | ||
|
|
c8516f3dc4 | ||
|
|
aae0fdc114 | ||
|
|
a19d0f67b9 | ||
|
|
971f69fe1f | ||
|
|
7468c691ad | ||
|
|
620c2ec141 | ||
|
|
3081b6f98b | ||
|
|
8630339e43 | ||
|
|
528d10091f | ||
|
|
149c3cf3dd | ||
|
|
f12c3cf9dd | ||
|
|
1f08a0efb3 | ||
|
|
6cf85d1d01 | ||
|
|
b5101a460d | ||
|
|
a5784ef09e | ||
|
|
122ec25bf0 | ||
|
|
d013ad9eba | ||
|
|
d918ca6fa5 | ||
|
|
852819180f | ||
|
|
218111eb72 | ||
|
|
1531cfb7ee | ||
|
|
16a89f21dc | ||
|
|
79e5160e37 | ||
|
|
510e966dc0 | ||
|
|
8238d3207e | ||
|
|
e37bc4ad40 | ||
|
|
0f78b8b917 | ||
|
|
19061fe04f | ||
|
|
9c901eb65f | ||
|
|
ae33ff3a6e | ||
|
|
59dbd625c1 | ||
|
|
8eb4e355b9 | ||
|
|
8b76b01bfe | ||
|
|
ec57b4b024 | ||
|
|
46d6caa47c | ||
|
|
97b65ce19c | ||
|
|
55d0ef40b0 | ||
|
|
a6c56416fc | ||
|
|
cd3c482d5e | ||
|
|
c5cbb77fc7 | ||
|
|
d7f21f02b3 | ||
|
|
87dc8ce9b3 | ||
|
|
2e22aa1a9c | ||
|
|
8c56dbf971 | ||
|
|
a78169e9e0 | ||
|
|
39a60c8120 | ||
|
|
c57d8760bd | ||
|
|
3eed2a8780 | ||
|
|
23b0268a7f | ||
|
|
3d0de946bb | ||
|
|
866b8ead91 | ||
|
|
aa274960ff | ||
|
|
54d6eb851b | ||
|
|
86d7276f24 | ||
|
|
0918651438 | ||
|
|
272d9297b6 | ||
|
|
f5bfc71899 | ||
|
|
42e1ded55f | ||
|
|
6c410af5a8 | ||
|
|
57ae676181 | ||
|
|
7896ca6e52 | ||
|
|
c4a315dd5e | ||
|
|
bf26959b72 | ||
|
|
37b2d046be | ||
|
|
fa2e407873 | ||
|
|
c61cf57db5 | ||
|
|
ef3690f2db | ||
|
|
14d01ec7bd | ||
|
|
5dc9f70386 | ||
|
|
88e593bf96 | ||
|
|
efde2e6be6 | ||
|
|
5a78a3bc46 | ||
|
|
b276d803d4 | ||
|
|
da51ac430b | ||
|
|
01eb0a3c7f | ||
|
|
dc2b3cf4f8 | ||
|
|
c4621a5ed2 | ||
|
|
1c953a4d5b | ||
|
|
c64ce7aa9c | ||
|
|
acdc4fdfec | ||
|
|
7ef9c18b72 | ||
|
|
a69b4e79db | ||
|
|
740fc6c2b8 | ||
|
|
42202c868d | ||
|
|
8c133b605c | ||
|
|
b50acd7238 | ||
|
|
54db278695 | ||
|
|
011948bcef | ||
|
|
9f95d8b38f | ||
|
|
3d394a6fa2 | ||
|
|
68d2b648e0 | ||
|
|
283998404f | ||
|
|
a7b4d0fea6 | ||
|
|
59051dd59d | ||
|
|
cd836a37e7 | ||
|
|
4326d22380 | ||
|
|
161d3a35ff | ||
|
|
20a9d21dfb | ||
|
|
e902789aa7 | ||
|
|
3745bbd95b | ||
|
|
e63b2880b0 | ||
|
|
a9b70e1ef7 | ||
|
|
e300f753c5 | ||
|
|
efaeca5887 | ||
|
|
4d67305661 | ||
|
|
853bb6ca3e | ||
|
|
b16f23ae8e | ||
|
|
91e6368de0 | ||
|
|
e4451b9628 | ||
|
|
770bb8ef5f | ||
|
|
87b958a210 | ||
|
|
314ac90ba9 | ||
|
|
9f33822083 | ||
|
|
10a4b35bc5 | ||
|
|
7b1dd0b60d | ||
|
|
7a650741f7 | ||
|
|
2ce5f1b944 | ||
|
|
063a14372d | ||
|
|
dd70e1b571 | ||
|
|
1840b893c2 | ||
|
|
8bcde757ce | ||
|
|
1dff223c71 | ||
|
|
a7aa2b0740 | ||
|
|
fb45fefbca | ||
|
|
7b914c2531 | ||
|
|
75d517a27f | ||
|
|
4c98bc690e | ||
|
|
550b3a3a99 | ||
|
|
ef7e89352d | ||
|
|
9322d604ee | ||
|
|
5d92110630 | ||
|
|
4056037cc2 | ||
|
|
677ae5487f | ||
|
|
19956b3807 | ||
|
|
ab9122f2cb | ||
|
|
e62c6b0ed0 | ||
|
|
27a420fbc2 | ||
|
|
b775ebb382 | ||
|
|
ac80a5c6a6 | ||
|
|
c74100a552 | ||
|
|
03379106d6 | ||
|
|
9ad229f663 | ||
|
|
0900edf776 | ||
|
|
ea520027b9 | ||
|
|
04679b3b3b | ||
|
|
2bf5c1324a | ||
|
|
3d93ca1bf6 | ||
|
|
c548431611 | ||
|
|
0ab29f7394 | ||
|
|
30dab7ad14 | ||
|
|
0c5fa59221 | ||
|
|
267e84f97d | ||
|
|
344cca75c6 | ||
|
|
0dd8e682f4 | ||
|
|
da7e54819a | ||
|
|
4e70215307 | ||
|
|
6aa162f6ff | ||
|
|
d380074569 | ||
|
|
c406e7ff51 | ||
|
|
31ff6cffee | ||
|
|
a9e525c81e | ||
|
|
6157a5fc1b | ||
|
|
be3cb1ad33 | ||
|
|
966f4a8144 | ||
|
|
261cf24efc | ||
|
|
a3da10f4eb | ||
|
|
6cb8063497 | ||
|
|
a45f212693 | ||
|
|
7c72cd5471 | ||
|
|
66f2032708 | ||
|
|
7546c7d8b4 | ||
|
|
703e4ad2fe | ||
|
|
0b98280a1f | ||
|
|
4609bed6a4 | ||
|
|
99ccb11d89 | ||
|
|
8a5a513662 | ||
|
|
bf0fd22309 | ||
|
|
cc98d10fc6 | ||
|
|
a6a022faa2 | ||
|
|
a864ad1c9c | ||
|
|
0bc586933f | ||
|
|
2785a8b4d4 | ||
|
|
d5d5f6ae06 | ||
|
|
be3ba1d53e | ||
|
|
09e6a4fee0 | ||
|
|
efc60dd8c5 | ||
|
|
a52cd974b9 | ||
|
|
de4217489c | ||
|
|
3c658b5012 | ||
|
|
f6814aa825 | ||
|
|
01a529cbda | ||
|
|
a1475af1e4 | ||
|
|
07594d4f95 | ||
|
|
bad73faf28 | ||
|
|
8d3704d93b | ||
|
|
8bd56fadd6 | ||
|
|
23f9b584a8 | ||
|
|
141557963e | ||
|
|
ffabf3c5b7 | ||
|
|
9b1e95809c | ||
|
|
b8776bb001 | ||
|
|
84c20e8337 | ||
|
|
bae2409cce | ||
|
|
8f7c44257b | ||
|
|
2772ce249c | ||
|
|
bcfd9327cb | ||
|
|
01ff36fc28 | ||
|
|
6466b09fdc | ||
|
|
40d7a701fe | ||
|
|
78b9d303d3 | ||
|
|
d912013d1b | ||
|
|
898e6519d1 | ||
|
|
8c074a7d01 | ||
|
|
6e69b4a795 | ||
|
|
1c36b46cb4 | ||
|
|
73abd04b95 | ||
|
|
29d6845d1f | ||
|
|
da84b26785 | ||
|
|
eb9070c833 | ||
|
|
b7b503d3ce | ||
|
|
327bbec981 | ||
|
|
7175a8e4eb | ||
|
|
9aefc183c6 | ||
|
|
3e9b867709 | ||
|
|
e8b5bbf7a1 | ||
|
|
79ef18e885 | ||
|
|
23a91df9f6 | ||
|
|
d8741595ae | ||
|
|
3c9dd6ffc2 | ||
|
|
42d28c9ff6 | ||
|
|
82372cbce8 | ||
|
|
69b07c5cc2 | ||
|
|
a6d27dc02c | ||
|
|
bbcaf48cb0 | ||
|
|
85194d99da | ||
|
|
5c86d2faa2 |
No files matched your search
@@ -1 +0,0 @@
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -1,3 +0,0 @@
|
||||
# Pull requests request review from the eligible staff team(s).
|
||||
# One approval from either an Admin or Developer satisfies branch protection.
|
||||
* @trueog/Admins @trueog/Developers
|
||||
+20
-22
@@ -8,52 +8,41 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y remove libzstd || true
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev libbz2-dev zlib1g-dev libsnappy-dev liblz4-dev libzstd-dev libgflags-dev
|
||||
make BUILD_TLS=yes -j2 KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror'
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' BUILD_TLS=yes -j2
|
||||
- name: gen-cert
|
||||
run: ./utils/gen-test-certs.sh
|
||||
- name: test-tls
|
||||
run: |
|
||||
sudo apt-get -y install tcl tcl-tls
|
||||
./runtest --clients 1 --verbose --tls --config server-threads 3
|
||||
./runtest --clients 2 --verbose --tls
|
||||
- name: cluster-test
|
||||
run: |
|
||||
./runtest-cluster --tls --config server-threads 3
|
||||
./runtest-cluster --tls
|
||||
- name: sentinel test
|
||||
run: |
|
||||
./runtest-sentinel
|
||||
- name: module tests
|
||||
run: |
|
||||
./runtest-moduleapi
|
||||
- name: rotation test
|
||||
run: |
|
||||
./runtest-rotation
|
||||
|
||||
build-ubuntu-old:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: actions/checkout@v1
|
||||
- name: make -j2
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y remove libzstd || true
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev libbz2-dev zlib1g-dev libsnappy-dev liblz4-dev libzstd-dev libgflags-dev
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make -j2
|
||||
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: make
|
||||
run: make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' -j2
|
||||
|
||||
@@ -61,11 +50,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y remove libzstd || true
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev libbz2-dev zlib1g-dev libsnappy-dev liblz4-dev libzstd-dev libgflags-dev
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' MALLOC=libc -j2
|
||||
|
||||
build-ubuntu-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install gcc-multilib g++-multilib libc6-dev-i386 lib32z1 uuid-dev:i386 libcurl4-openssl-dev:i386
|
||||
make KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror' 32bit -j2
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Endurance
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 * * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
test-endurance:
|
||||
runs-on: octocore
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make -j8
|
||||
- name: test-multithread (5X)
|
||||
run: |
|
||||
sudo apt-get install -y tcl tcl-tls
|
||||
./runtest --loopn 5 --config server-threads 3 --clients 5 --endurance
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@ src/keydb-server
|
||||
**/app/keydb-server
|
||||
*.deb
|
||||
*.rpm
|
||||
keydb-pro-server
|
||||
src/keydb-cli
|
||||
**/bin/keydb-cli
|
||||
**/app/keydb-cli
|
||||
@@ -48,7 +49,7 @@ src/nodes.conf
|
||||
deps/lua/src/lua
|
||||
deps/lua/src/luac
|
||||
deps/lua/src/liblua.a
|
||||
tests/tls*/*
|
||||
tests/tls/*
|
||||
.make-*
|
||||
.prerequisites
|
||||
*.dSYM
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
[submodule "deps/rocksdb"]
|
||||
path = deps/rocksdb
|
||||
url = https://github.com/facebook/rocksdb.git
|
||||
[submodule "deps/depot_tools"]
|
||||
path = deps/depot_tools
|
||||
url = https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
@@ -1,7 +1,6 @@
|
||||
Copyright (c) 2006-2020, Salvatore Sanfilippo
|
||||
Copyright (C) 2019-2021, John Sully
|
||||
Copyright (C) 2020-2021, EQ Alpha Technology Ltd.
|
||||
Copyright (C) 2022 Snap Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Top level makefile, this just calls into src/Makefile where the real work is done. Changes should be made there.
|
||||
# Top level makefile, the real shit is at src/Makefile
|
||||
|
||||
default: all
|
||||
|
||||
|
||||
@@ -2,20 +2,16 @@
|
||||

|
||||
[](https://stackshare.io/eq-alpha-technology-inc/eq-alpha-technology-inc)
|
||||
|
||||
##### KeyDB is now a part of Snap Inc! Check out the announcement [here](https://docs.keydb.dev/news/2022/05/12/keydb-joins-snap)
|
||||
|
||||
##### [Release v6.3.0](https://github.com/EQ-Alpha/KeyDB/releases/tag/v6.3.0) is here with major improvements as we consolidate our Open Source and Enterprise offerings into a single BSD-3 licensed project. See our [roadmap](https://docs.keydb.dev/docs/coming-soon) for details.
|
||||
|
||||
##### Want to extend KeyDB with Javascript? Try [ModJS](https://github.com/JohnSully/ModJS)
|
||||
|
||||
##### Need Help? Check out our extensive [documentation](https://docs.keydb.dev).
|
||||
|
||||
##### KeyDB is on Slack. Click [here](https://docs.keydb.dev/slack/) to learn more and join the KeyDB Community Slack workspace.
|
||||
##### NEW!!! KeyDB now has a Slack Community Workspace. Click [here](https://docs.keydb.dev/slack/) to learn more and join the KeyDB Community Slack workspace.
|
||||
|
||||
What is KeyDB?
|
||||
--------------
|
||||
|
||||
KeyDB is a high performance fork of Redis with a focus on multithreading, memory efficiency, and high throughput. In addition to performance improvements, KeyDB offers features such as Active Replication, FLASH Storage and Subkey Expires. KeyDB has a MVCC architecture that allows you to execute queries such as KEYS and SCAN without blocking the database and degrading performance.
|
||||
KeyDB is a high performance fork of Redis with a focus on multithreading, memory efficiency, and high throughput. In addition to multithreading, KeyDB also has features only available in Redis Enterprise such as [Active Replication](https://github.com/JohnSully/KeyDB/wiki/Active-Replication), [FLASH storage](https://github.com/JohnSully/KeyDB/wiki/FLASH-Storage) support, and some not available at all such as direct backup to AWS S3.
|
||||
|
||||
KeyDB maintains full compatibility with the Redis protocol, modules, and scripts. This includes the atomicity guarantees for scripts and transactions. Because KeyDB keeps in sync with Redis development KeyDB is a superset of Redis functionality, making KeyDB a drop in replacement for existing Redis deployments.
|
||||
|
||||
@@ -34,79 +30,48 @@ KeyDB has a different philosophy on how the codebase should evolve. We feel tha
|
||||
|
||||
Because of this difference of opinion features which are right for KeyDB may not be appropriate for Redis. A fork allows us to explore this new development path and implement features which may never be a part of Redis. KeyDB keeps in sync with upstream Redis changes, and where applicable we upstream bug fixes and changes. It is our hope that the two projects can continue to grow and learn from each other.
|
||||
|
||||
Project Support
|
||||
-------------------
|
||||
|
||||
The KeyDB team maintains this project as part of Snap Inc. KeyDB is used by Snap as part of its caching infrastructure and is fully open sourced. There is no separate commercial product and no paid support options available. We really value collaborating with the open source community and welcome PRs, bug reports, and open discussion. For community support or to get involved further with the project check out our community support options [here](https://docs.keydb.dev/docs/support) (slack, forum, meetup, github issues). Our team monitors these channels regularly.
|
||||
|
||||
|
||||
Additional Resources
|
||||
--------------------
|
||||
|
||||
Try the KeyDB [Docker Image](https://hub.docker.com/r/eqalpha/keydb)
|
||||
Check out KeyDB's [Docker Image](https://hub.docker.com/r/eqalpha/keydb)
|
||||
|
||||
Join us on [Slack](https://docs.keydb.dev/slack/)
|
||||
|
||||
Learn more using KeyDB's extensive [documentation](https://docs.keydb.dev)
|
||||
Post to the [Community Forum](https://community.keydb.dev)
|
||||
|
||||
See the [KeyDB Roadmap](https://docs.keydb.dev/docs/coming-soon) to see what's in store
|
||||
Learn more through KeyDB's [Documentation & Learning Center](https://docs.keydb.dev)
|
||||
|
||||
|
||||
Benchmarking KeyDB
|
||||
------------------
|
||||
|
||||
Please note keydb-benchmark and redis-benchmark are currently single threaded and too slow to properly benchmark KeyDB. We recommend using a redis cluster benchmark tool such as [memtier](https://github.com/RedisLabs/memtier_benchmark). Please ensure your machine has enough cores for both KeyDB and memtier if testing locally. KeyDB expects exclusive use of any cores assigned to it.
|
||||
Please note keydb-benchmark and redis-benchmark are currently single threaded and too slow to properly benchmark KeyDB. We recommend using a redis cluster benchmark tool such as [memtier](https://github.com/RedisLabs/memtier_benchmark). Please ensure your machine has enough cores for both KeyDB and memteir if testing locally. KeyDB expects exclusive use of any cores assigned to it.
|
||||
|
||||
For more details on how we benchmarked KeyDB along with performance numbers check out our blog post: [Redis Should Be Multithreaded](https://medium.com/@john_63123/redis-should-be-multi-threaded-e28319cab744?source=friends_link&sk=7ce8e9fe3ec8224a4d27ef075d085457)
|
||||
|
||||
New Configuration Options
|
||||
-------------------------
|
||||
|
||||
With new features comes new options. All other configuration options behave as you'd expect. Your existing configuration files should continue to work unchanged.
|
||||
With new features comes new options:
|
||||
|
||||
```
|
||||
server-threads N
|
||||
server-thread-affinity [true/false]
|
||||
```
|
||||
The number of threads used to serve requests. This should be related to the number of queues available in your network hardware, *not* the number of cores on your
|
||||
machine. Because KeyDB uses spinlocks to reduce latency; making this too high will reduce performance. We recommend using 4 here. By default this is set to two.
|
||||
|
||||
```
|
||||
min-clients-per-thread 50
|
||||
```
|
||||
The minimum number of clients on a thread before KeyDB assigns new connections to a different thread. Tuning this parameter is a tradeoff between locking overhead and distributing the workload over multiple cores
|
||||
The number of threads used to serve requests. This should be related to the number of queues available in your network hardware, *not* the number of cores on your machine. Because KeyDB uses spinlocks to reduce latency; making this too high will reduce performance. We recommend using 4 here. By default this is set to one.
|
||||
|
||||
```
|
||||
replica-weighting-factor 2
|
||||
```
|
||||
KeyDB will attempt to balance clients across threads evenly; However, replica clients are usually much more expensive than a normal client, and so KeyDB will try to assign fewer clients to threads with a replica. The weighting factor below is intended to help tune this behavior. A replica weighting factor of 2 means we treat a replica as the equivalent of two normal clients. Adjusting this value may improve performance when replication is used. The best weighting is workload specific - e.g. read heavy workloads should set this to 1. Very write heavy workloads may benefit from higher numbers.
|
||||
scratch-file-path /path
|
||||
|
||||
```
|
||||
active-client-balancing yes
|
||||
```
|
||||
Should KeyDB make active attempts at balancing clients across threads? This can impact performance accepting new clients. By default this is enabled. If disabled there is still a best effort from the kernel to distribute across threads with SO_REUSEPORT but it will not be as fair. By default this is enabled
|
||||
|
||||
```
|
||||
active-replica yes
|
||||
```
|
||||
If you are using active-active replication set `active-replica` option to “yes”. This will enable both instances to accept reads and writes while remaining synced. [Click here](https://docs.keydb.dev/docs/active-rep/) to see more on active-rep in our docs section. There are also [docker examples]( https://docs.keydb.dev/docs/docker-active-rep/) on docs.
|
||||
|
||||
```
|
||||
multi-master-no-forward no
|
||||
```
|
||||
Avoid forwarding RREPLAY messages to other masters? WARNING: This setting is dangerous! You must be certain all masters are connected to eachother in a true mesh topology or data loss will occur! This command can be used to reduce multimaster bus traffic
|
||||
|
||||
|
||||
```
|
||||
If you would like to use the [FLASH backed](https://github.com/JohnSully/KeyDB/wiki/FLASH-Storage) storage this option configures the directory for KeyDB's temporary files. This feature relies on snapshotting to work so must be used on a BTRFS filesystem. ZFS may also work but is untested. With this feature KeyDB will use RAM as a cache and page to disk as necessary. NOTE: This requires special compilation options, see Building KeyDB below.
|
||||
|
||||
db-s3-object /path/to/bucket
|
||||
```
|
||||
|
||||
If you would like KeyDB to dump and load directly to AWS S3 this option specifies the bucket. Using this option with the traditional RDB options will result in KeyDB backing up twice to both locations. If both are specified KeyDB will first attempt to load from the local dump file and if that fails load from S3. This requires the AWS CLI tools to be installed and configured which are used under the hood to transfer the data.
|
||||
|
||||
active-replica yes
|
||||
|
||||
```
|
||||
storage-provider flash /path/to/flash
|
||||
```
|
||||
If you would like to use KeyDB FLASH storage, specify the storage medium followed by the directory path on your local SSD volume. Note that this feature is still considered experimental and should be used with discretion. See [FLASH Documentation](https://docs.keydb.dev/docs/flash) for more details on configuration and setting up your FLASH volume.
|
||||
If you are using active-active replication set `active-replica` option to “yes”. This will enable both instances to accept reads and writes while remaining synced. [Click here](https://docs.keydb.dev/docs/active-rep/) to see more on active-rep in our docs section. There are also [docker examples]( https://docs.keydb.dev/docs/docker-active-rep/) on docs.
|
||||
|
||||
All other configuration options behave as you'd expect. Your existing configuration files should continue to work unchanged.
|
||||
|
||||
Building KeyDB
|
||||
--------------
|
||||
@@ -115,18 +80,19 @@ KeyDB can be compiled and is tested for use on Linux. KeyDB currently relies on
|
||||
|
||||
More on CentOS/Archlinux/Alpine/Debian/Ubuntu dependencies and builds can be found here: https://docs.keydb.dev/docs/build/
|
||||
|
||||
Init and clone submodule dependencies:
|
||||
|
||||
% git submodule init && git submodule update
|
||||
|
||||
Install dependencies:
|
||||
|
||||
% sudo apt install build-essential nasm autotools-dev autoconf libjemalloc-dev tcl tcl-dev uuid-dev libcurl4-openssl-dev libbz2-dev libzstd-dev liblz4-dev libsnappy-dev libssl-dev
|
||||
% sudo apt install build-essential nasm autotools-dev autoconf libjemalloc-dev tcl tcl-dev uuid-dev libcurl4-openssl-dev
|
||||
|
||||
Compiling is as simple as:
|
||||
|
||||
% make
|
||||
|
||||
To build with TLS support, you'll need OpenSSL development libraries (e.g.
|
||||
libssl-dev on Debian/Ubuntu) and run:
|
||||
|
||||
% make BUILD_TLS=yes
|
||||
|
||||
To build with systemd support, you'll need systemd development libraries (such
|
||||
as libsystemd-dev on Debian/Ubuntu or systemd-devel on CentOS) and run:
|
||||
|
||||
@@ -139,21 +105,18 @@ To append a suffix to KeyDB program names, use:
|
||||
***Note that the following dependencies may be needed:
|
||||
% sudo apt-get install autoconf autotools-dev libnuma-dev libtool
|
||||
|
||||
KeyDB by default is built with TLS enabled. To build without TLS support, use:
|
||||
|
||||
% make BUILD_TLS=no
|
||||
|
||||
Running the tests with TLS enabled (you will need `tcl-tls`
|
||||
If TLS is built, running the tests with TLS enabled (you will need `tcl-tls`
|
||||
installed):
|
||||
|
||||
% ./utils/gen-test-certs.sh
|
||||
% ./runtest --tls
|
||||
|
||||
To build with KeyDB FLASH support, use:
|
||||
|
||||
% make ENABLE_FLASH=yes
|
||||
If TLS is built, running the tests with TLS enabled (you will need `tcl-tls`
|
||||
installed):
|
||||
|
||||
***Note that the KeyDB FLASH feature is considered experimental (beta) and should used with discretion
|
||||
% ./utils/gen-test-certs.sh
|
||||
% ./runtest --tls
|
||||
|
||||
|
||||
Fixing build problems with dependencies or cached build options
|
||||
@@ -251,11 +214,11 @@ All the options in keydb.conf are also supported as options using the command
|
||||
line, with exactly the same name.
|
||||
|
||||
|
||||
Running KeyDB with TLS:
|
||||
Running Redis with TLS:
|
||||
------------------
|
||||
|
||||
Please consult the [TLS.md](TLS.md) file for more information on
|
||||
how to use KeyDB with TLS.
|
||||
how to use Redis with TLS.
|
||||
|
||||
|
||||
Playing with KeyDB
|
||||
@@ -315,6 +278,24 @@ KeyDB works by running the normal Redis event loop on multiple threads. Network
|
||||
|
||||
Unlike most databases the core data structure is the fastest part of the system. Most of the query time comes from parsing the REPL protocol and copying data to/from the network.
|
||||
|
||||
Future work:
|
||||
- Allow rebalancing of connections to different threads after the connection
|
||||
- Allow multiple readers access to the hashtable concurrently
|
||||
|
||||
Docker Build
|
||||
------------
|
||||
Build the latest binaries from the github unstable branch within a docker container. Note this is built for Ubuntu 18.04.
|
||||
Simply make a directory you would like to have the latest binaries dumped in, then run the following commmand with your updated path:
|
||||
```
|
||||
$ docker run -it --rm -v /path-to-dump-binaries:/keydb_bin eqalpha/keydb-build-bin
|
||||
```
|
||||
You should receive the following files: keydb-benchmark, keydb-check-aof, keydb-check-rdb, keydb-cli, keydb-sentinel, keydb-server
|
||||
|
||||
If you are looking to enable flash support with the build (make MALLOC=memkind) then use the following command:
|
||||
```
|
||||
$ docker run -it --rm -v /path-to-dump-binaries:/keydb_bin eqalpha/keydb-build-bin:flash
|
||||
```
|
||||
Please note that you will need libcurl4-openssl-dev in order to run keydb. With flash version you may need libnuma-dev and libtool installed in order to run the binaries. Keep this in mind especially when running in a container. For a copy of all our Dockerfiles, please see them on [docs]( https://docs.keydb.dev/docs/dockerfiles/).
|
||||
|
||||
Code contributions
|
||||
-----------------
|
||||
|
||||
@@ -6,7 +6,10 @@ Getting Started
|
||||
|
||||
### Building
|
||||
|
||||
TLS support is enabled in the default build. To build without TLS, run `make BUILD_TLS=no`.
|
||||
To build with TLS support you'll need OpenSSL development libraries (e.g.
|
||||
libssl-dev on Debian/Ubuntu).
|
||||
|
||||
Run `make BUILD_TLS=yes`.
|
||||
|
||||
### Tests
|
||||
|
||||
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
# Doc: https://wiki.sc-corp.net/pages/viewpage.action?pageId=121500284
|
||||
version: 1
|
||||
machamp:
|
||||
keydb-build:
|
||||
# Optional - build counter is linked to the build def
|
||||
tag_template: "0.0.%build.counter%-%sha%"
|
||||
# Optional - value in seconds before a build is terminated, default is 3600 seconds
|
||||
timeout: 3600
|
||||
# Optional - update ghe or not, default to true
|
||||
update_ghe: true
|
||||
code_coverage: false
|
||||
# Required
|
||||
steps:
|
||||
make-build:
|
||||
type: cmd
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./machamp_scripts/build.sh
|
||||
tls-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest --clients 4 --verbose --tls
|
||||
cluster-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest-cluster --tls
|
||||
sentinel-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest-sentinel
|
||||
module-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest-moduleapi
|
||||
rotation-test:
|
||||
type: cmd
|
||||
parent: make-build
|
||||
# https://github.sc-corp.net/Snapchat/img/tree/master/keydb/ubuntu-20-04
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/keydb/keydb-ubuntu-20-04@sha256:cf869a3f5d1de1e1d976bb906689c37b7031938eb68661b844a38c532f27248c
|
||||
command: ./runtest-rotation
|
||||
keydb-docker-build:
|
||||
# Optional - build counter is linked to the build def
|
||||
tag_template: "%sha%"
|
||||
# Optional - value in seconds before a build is terminated, default is 3600 seconds
|
||||
timeout: 3600
|
||||
# Optional - update ghe or not, default to true
|
||||
update_ghe: true
|
||||
code_coverage: false
|
||||
# Required
|
||||
steps:
|
||||
# to ensure a clearer docker build env
|
||||
code-checkout:
|
||||
type: cmd
|
||||
command: echo checkout
|
||||
# default machamp builder image does not work for multi arch
|
||||
builder_image: us.gcr.io/snapchat-build-artifacts/prod/snapchat/img/ubuntu/ubuntu-23-04@sha256:bd43177a80e6ce1c3583e8ea959b88a9081c0f56b765ec9c5a157c27a637c23b
|
||||
docker:
|
||||
parent: code-checkout
|
||||
type: docker # published images can be found in https://console.cloud.google.com/gcr/images/machamp-prod/global/keydb
|
||||
dockerfile: machamp_scripts/Dockerfile
|
||||
image_name: keydb # git commit sha will be deafult tag in the final image
|
||||
workspace_context: ./ # This is the workspace context that your Dockerfile will use to move files around. <Root of checkout repository>/<Workspace Context>/<Dockerfile> If the workspace context is just the root of the repository, you can just use "./".
|
||||
@@ -1,35 +0,0 @@
|
||||
# Doc: https://wiki.sc-corp.net/display/TOOL/ci.yaml+User+Guide
|
||||
version: 1
|
||||
on:
|
||||
# https://wiki.sc-corp.net/display/TOOL/Onboard+Machamp+Build+By+ci.yaml+Configuration
|
||||
# on pull_request is used for any pr build
|
||||
pull_request:
|
||||
- branches: ['!!main', '*'] # this branch pattern means any branch but not main branch will trigger this pr build
|
||||
workflows:
|
||||
# All builds that use machamp should use the defined `backend_workflow`
|
||||
- workflow_type: backend_workflow
|
||||
# references a build defined in build.yaml
|
||||
build_name: keydb-build
|
||||
arch_types: ["amd64", "arm64"]
|
||||
- workflow_type: backend_workflow
|
||||
# references a build defined in build.yaml
|
||||
build_name: keydb-docker-build
|
||||
arch_types: ["amd64", "arm64"]
|
||||
# on push is used for release branch, meaning: trigger this build when there is commit pushed to this branch
|
||||
push:
|
||||
- branches: [main]
|
||||
workflows:
|
||||
- workflow_type: backend_workflow
|
||||
build_name: keydb-build
|
||||
arch_types: ["amd64", "arm64"]
|
||||
- workflow_type: backend_workflow
|
||||
# references a build defined in build.yaml
|
||||
build_name: keydb-docker-build
|
||||
arch_types: ["amd64", "arm64"]
|
||||
|
||||
# below defines which branch is release branch / release tag
|
||||
machamp:
|
||||
releases:
|
||||
# Note: machamp will only respect the ci.yaml file from default branch for "release branch" definition (most repositories using master/main as default branch)
|
||||
# https://wiki.sc-corp.net/display/TOOL/Onboard+Machamp+Build+By+ci.yaml+Configuration
|
||||
- branch_name: ^main$
|
||||
Vendored
+1
-19
@@ -1,7 +1,6 @@
|
||||
# Redis dependency Makefile
|
||||
|
||||
uname_S:= $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
|
||||
|
||||
CCCOLOR="\033[34m"
|
||||
LINKCOLOR="\033[34;1m"
|
||||
@@ -34,17 +33,10 @@ ifneq ($(shell sh -c '[ -f .make-ldflags ] && cat .make-ldflags || echo none'),
|
||||
endif
|
||||
|
||||
distclean:
|
||||
ifneq ($(USE_SYSTEM_HIREDIS),yes)
|
||||
-(cd hiredis && $(MAKE) clean) > /dev/null || true
|
||||
endif
|
||||
-(cd linenoise && $(MAKE) clean) > /dev/null || true
|
||||
-(cd lua && $(MAKE) clean) > /dev/null || true
|
||||
ifneq ($(USE_SYSTEM_JEMALLOC),yes)
|
||||
-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
|
||||
endif
|
||||
ifneq ($(USE_SYSTEM_ROCKSDB),yes)
|
||||
-(cd rocksdb && $(MAKE) clean) > /dev/null || true
|
||||
endif
|
||||
-(cd hdr_histogram && $(MAKE) clean) > /dev/null || true
|
||||
-(rm -f .make-*)
|
||||
|
||||
@@ -101,17 +93,7 @@ JEMALLOC_LDFLAGS= $(LDFLAGS)
|
||||
|
||||
jemalloc: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd jemalloc && ./configure --with-version=5.2.1-0-g0 --with-lg-quantum=3 --disable-cxx CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
|
||||
cd jemalloc && ./configure --with-version=5.2.1-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)"
|
||||
cd jemalloc && $(MAKE) CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" lib/libjemalloc.a
|
||||
|
||||
.PHONY: jemalloc
|
||||
|
||||
rocksdb: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
ifeq ($(uname_M),x86_64)
|
||||
cd rocksdb && CFLAGS=-Wno-error PORTABLE=1 USE_SSE=1 FORCE_SSE42=1 $(MAKE) static_lib
|
||||
else
|
||||
cd rocksdb && PORTABLE=1 $(MAKE) static_lib
|
||||
endif
|
||||
|
||||
.PHONY: rocksdb
|
||||
-583
@@ -1,583 +0,0 @@
|
||||
// Provides an efficient blocking version of moodycamel::ConcurrentQueue.
|
||||
// ©2015-2020 Cameron Desrochers. Distributed under the terms of the simplified
|
||||
// BSD license, available at the top of concurrentqueue.h.
|
||||
// Also dual-licensed under the Boost Software License (see LICENSE.md)
|
||||
// Uses Jeff Preshing's semaphore implementation (under the terms of its
|
||||
// separate zlib license, see lightweightsemaphore.h).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
#include "lightweightsemaphore.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <cerrno>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
namespace moodycamel
|
||||
{
|
||||
// This is a blocking version of the queue. It has an almost identical interface to
|
||||
// the normal non-blocking version, with the addition of various wait_dequeue() methods
|
||||
// and the removal of producer-specific dequeue methods.
|
||||
template<typename T, typename Traits = ConcurrentQueueDefaultTraits>
|
||||
class BlockingConcurrentQueue
|
||||
{
|
||||
private:
|
||||
typedef ::moodycamel::ConcurrentQueue<T, Traits> ConcurrentQueue;
|
||||
typedef ::moodycamel::LightweightSemaphore LightweightSemaphore;
|
||||
|
||||
public:
|
||||
typedef typename ConcurrentQueue::producer_token_t producer_token_t;
|
||||
typedef typename ConcurrentQueue::consumer_token_t consumer_token_t;
|
||||
|
||||
typedef typename ConcurrentQueue::index_t index_t;
|
||||
typedef typename ConcurrentQueue::size_t size_t;
|
||||
typedef typename std::make_signed<size_t>::type ssize_t;
|
||||
|
||||
static const size_t BLOCK_SIZE = ConcurrentQueue::BLOCK_SIZE;
|
||||
static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = ConcurrentQueue::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD;
|
||||
static const size_t EXPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::EXPLICIT_INITIAL_INDEX_SIZE;
|
||||
static const size_t IMPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::IMPLICIT_INITIAL_INDEX_SIZE;
|
||||
static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = ConcurrentQueue::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
|
||||
static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = ConcurrentQueue::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE;
|
||||
static const size_t MAX_SUBQUEUE_SIZE = ConcurrentQueue::MAX_SUBQUEUE_SIZE;
|
||||
|
||||
public:
|
||||
// Creates a queue with at least `capacity` element slots; note that the
|
||||
// actual number of elements that can be inserted without additional memory
|
||||
// allocation depends on the number of producers and the block size (e.g. if
|
||||
// the block size is equal to `capacity`, only a single block will be allocated
|
||||
// up-front, which means only a single producer will be able to enqueue elements
|
||||
// without an extra allocation -- blocks aren't shared between producers).
|
||||
// This method is not thread safe -- it is up to the user to ensure that the
|
||||
// queue is fully constructed before it starts being used by other threads (this
|
||||
// includes making the memory effects of construction visible, possibly with a
|
||||
// memory barrier).
|
||||
explicit BlockingConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
|
||||
: inner(capacity), sema(create<LightweightSemaphore, ssize_t, int>(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
|
||||
{
|
||||
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
|
||||
if (!sema) {
|
||||
MOODYCAMEL_THROW(std::bad_alloc());
|
||||
}
|
||||
}
|
||||
|
||||
BlockingConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
|
||||
: inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create<LightweightSemaphore, ssize_t, int>(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
|
||||
{
|
||||
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
|
||||
if (!sema) {
|
||||
MOODYCAMEL_THROW(std::bad_alloc());
|
||||
}
|
||||
}
|
||||
|
||||
// Disable copying and copy assignment
|
||||
BlockingConcurrentQueue(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
|
||||
BlockingConcurrentQueue& operator=(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
|
||||
|
||||
// Moving is supported, but note that it is *not* a thread-safe operation.
|
||||
// Nobody can use the queue while it's being moved, and the memory effects
|
||||
// of that move must be propagated to other threads before they can use it.
|
||||
// Note: When a queue is moved, its tokens are still valid but can only be
|
||||
// used with the destination queue (i.e. semantically they are moved along
|
||||
// with the queue itself).
|
||||
BlockingConcurrentQueue(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
|
||||
: inner(std::move(other.inner)), sema(std::move(other.sema))
|
||||
{ }
|
||||
|
||||
inline BlockingConcurrentQueue& operator=(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
|
||||
{
|
||||
return swap_internal(other);
|
||||
}
|
||||
|
||||
// Swaps this queue's state with the other's. Not thread-safe.
|
||||
// Swapping two queues does not invalidate their tokens, however
|
||||
// the tokens that were created for one queue must be used with
|
||||
// only the swapped queue (i.e. the tokens are tied to the
|
||||
// queue's movable state, not the object itself).
|
||||
inline void swap(BlockingConcurrentQueue& other) MOODYCAMEL_NOEXCEPT
|
||||
{
|
||||
swap_internal(other);
|
||||
}
|
||||
|
||||
private:
|
||||
BlockingConcurrentQueue& swap_internal(BlockingConcurrentQueue& other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inner.swap(other.inner);
|
||||
sema.swap(other.sema);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
// Enqueues a single item (by copying it).
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or implicit
|
||||
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
|
||||
// or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Thread-safe.
|
||||
inline bool enqueue(T const& item)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue(item))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible).
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or implicit
|
||||
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
|
||||
// or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Thread-safe.
|
||||
inline bool enqueue(T&& item)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue(std::move(item)))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by copying it) using an explicit producer token.
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or
|
||||
// Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Thread-safe.
|
||||
inline bool enqueue(producer_token_t const& token, T const& item)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue(token, item))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible) using an explicit producer token.
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or
|
||||
// Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Thread-safe.
|
||||
inline bool enqueue(producer_token_t const& token, T&& item)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue(token, std::move(item)))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues several items.
|
||||
// Allocates memory if required. Only fails if memory allocation fails (or
|
||||
// implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
|
||||
// is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Note: Use std::make_move_iterator if the elements should be moved instead of copied.
|
||||
// Thread-safe.
|
||||
template<typename It>
|
||||
inline bool enqueue_bulk(It itemFirst, size_t count)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue_bulk(std::forward<It>(itemFirst), count))) {
|
||||
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues several items using an explicit producer token.
|
||||
// Allocates memory if required. Only fails if memory allocation fails
|
||||
// (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
|
||||
// Note: Use std::make_move_iterator if the elements should be moved
|
||||
// instead of copied.
|
||||
// Thread-safe.
|
||||
template<typename It>
|
||||
inline bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
|
||||
{
|
||||
if ((details::likely)(inner.enqueue_bulk(token, std::forward<It>(itemFirst), count))) {
|
||||
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by copying it).
|
||||
// Does not allocate memory. Fails if not enough room to enqueue (or implicit
|
||||
// production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
|
||||
// is 0).
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(T const& item)
|
||||
{
|
||||
if (inner.try_enqueue(item)) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible).
|
||||
// Does not allocate memory (except for one-time implicit producer).
|
||||
// Fails if not enough room to enqueue (or implicit production is
|
||||
// disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(T&& item)
|
||||
{
|
||||
if (inner.try_enqueue(std::move(item))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by copying it) using an explicit producer token.
|
||||
// Does not allocate memory. Fails if not enough room to enqueue.
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(producer_token_t const& token, T const& item)
|
||||
{
|
||||
if (inner.try_enqueue(token, item)) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues a single item (by moving it, if possible) using an explicit producer token.
|
||||
// Does not allocate memory. Fails if not enough room to enqueue.
|
||||
// Thread-safe.
|
||||
inline bool try_enqueue(producer_token_t const& token, T&& item)
|
||||
{
|
||||
if (inner.try_enqueue(token, std::move(item))) {
|
||||
sema->signal();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues several items.
|
||||
// Does not allocate memory (except for one-time implicit producer).
|
||||
// Fails if not enough room to enqueue (or implicit production is
|
||||
// disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
|
||||
// Note: Use std::make_move_iterator if the elements should be moved
|
||||
// instead of copied.
|
||||
// Thread-safe.
|
||||
template<typename It>
|
||||
inline bool try_enqueue_bulk(It itemFirst, size_t count)
|
||||
{
|
||||
if (inner.try_enqueue_bulk(std::forward<It>(itemFirst), count)) {
|
||||
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enqueues several items using an explicit producer token.
|
||||
// Does not allocate memory. Fails if not enough room to enqueue.
|
||||
// Note: Use std::make_move_iterator if the elements should be moved
|
||||
// instead of copied.
|
||||
// Thread-safe.
|
||||
template<typename It>
|
||||
inline bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
|
||||
{
|
||||
if (inner.try_enqueue_bulk(token, std::forward<It>(itemFirst), count)) {
|
||||
sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Attempts to dequeue from the queue.
|
||||
// Returns false if all producer streams appeared empty at the time they
|
||||
// were checked (so, the queue is likely but not guaranteed to be empty).
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline bool try_dequeue(U& item)
|
||||
{
|
||||
if (sema->tryWait()) {
|
||||
while (!inner.try_dequeue(item)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempts to dequeue from the queue using an explicit consumer token.
|
||||
// Returns false if all producer streams appeared empty at the time they
|
||||
// were checked (so, the queue is likely but not guaranteed to be empty).
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline bool try_dequeue(consumer_token_t& token, U& item)
|
||||
{
|
||||
if (sema->tryWait()) {
|
||||
while (!inner.try_dequeue(token, item)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue.
|
||||
// Returns the number of items actually dequeued.
|
||||
// Returns 0 if all producer streams appeared empty at the time they
|
||||
// were checked (so, the queue is likely but not guaranteed to be empty).
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t try_dequeue_bulk(It itemFirst, size_t max)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue using an explicit consumer token.
|
||||
// Returns the number of items actually dequeued.
|
||||
// Returns 0 if all producer streams appeared empty at the time they
|
||||
// were checked (so, the queue is likely but not guaranteed to be empty).
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Blocks the current thread until there's something to dequeue, then
|
||||
// dequeues it.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline void wait_dequeue(U& item)
|
||||
{
|
||||
while (!sema->wait()) {
|
||||
continue;
|
||||
}
|
||||
while (!inner.try_dequeue(item)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks the current thread until either there's something to dequeue
|
||||
// or the timeout (specified in microseconds) expires. Returns false
|
||||
// without setting `item` if the timeout expires, otherwise assigns
|
||||
// to `item` and returns true.
|
||||
// Using a negative timeout indicates an indefinite timeout,
|
||||
// and is thus functionally equivalent to calling wait_dequeue.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs)
|
||||
{
|
||||
if (!sema->wait(timeout_usecs)) {
|
||||
return false;
|
||||
}
|
||||
while (!inner.try_dequeue(item)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blocks the current thread until either there's something to dequeue
|
||||
// or the timeout expires. Returns false without setting `item` if the
|
||||
// timeout expires, otherwise assigns to `item` and returns true.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U, typename Rep, typename Period>
|
||||
inline bool wait_dequeue_timed(U& item, std::chrono::duration<Rep, Period> const& timeout)
|
||||
{
|
||||
return wait_dequeue_timed(item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
|
||||
}
|
||||
|
||||
// Blocks the current thread until there's something to dequeue, then
|
||||
// dequeues it using an explicit consumer token.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline void wait_dequeue(consumer_token_t& token, U& item)
|
||||
{
|
||||
while (!sema->wait()) {
|
||||
continue;
|
||||
}
|
||||
while (!inner.try_dequeue(token, item)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Blocks the current thread until either there's something to dequeue
|
||||
// or the timeout (specified in microseconds) expires. Returns false
|
||||
// without setting `item` if the timeout expires, otherwise assigns
|
||||
// to `item` and returns true.
|
||||
// Using a negative timeout indicates an indefinite timeout,
|
||||
// and is thus functionally equivalent to calling wait_dequeue.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U>
|
||||
inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::int64_t timeout_usecs)
|
||||
{
|
||||
if (!sema->wait(timeout_usecs)) {
|
||||
return false;
|
||||
}
|
||||
while (!inner.try_dequeue(token, item)) {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blocks the current thread until either there's something to dequeue
|
||||
// or the timeout expires. Returns false without setting `item` if the
|
||||
// timeout expires, otherwise assigns to `item` and returns true.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename U, typename Rep, typename Period>
|
||||
inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::chrono::duration<Rep, Period> const& timeout)
|
||||
{
|
||||
return wait_dequeue_timed(token, item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue.
|
||||
// Returns the number of items actually dequeued, which will
|
||||
// always be at least one (this method blocks until the queue
|
||||
// is non-empty) and at most max.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t wait_dequeue_bulk(It itemFirst, size_t max)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue.
|
||||
// Returns the number of items actually dequeued, which can
|
||||
// be 0 if the timeout expires while waiting for elements,
|
||||
// and at most max.
|
||||
// Using a negative timeout indicates an indefinite timeout,
|
||||
// and is thus functionally equivalent to calling wait_dequeue_bulk.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::int64_t timeout_usecs)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue.
|
||||
// Returns the number of items actually dequeued, which can
|
||||
// be 0 if the timeout expires while waiting for elements,
|
||||
// and at most max.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It, typename Rep, typename Period>
|
||||
inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::chrono::duration<Rep, Period> const& timeout)
|
||||
{
|
||||
return wait_dequeue_bulk_timed<It&>(itemFirst, max, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue using an explicit consumer token.
|
||||
// Returns the number of items actually dequeued, which will
|
||||
// always be at least one (this method blocks until the queue
|
||||
// is non-empty) and at most max.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t wait_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue using an explicit consumer token.
|
||||
// Returns the number of items actually dequeued, which can
|
||||
// be 0 if the timeout expires while waiting for elements,
|
||||
// and at most max.
|
||||
// Using a negative timeout indicates an indefinite timeout,
|
||||
// and is thus functionally equivalent to calling wait_dequeue_bulk.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It>
|
||||
inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::int64_t timeout_usecs)
|
||||
{
|
||||
size_t count = 0;
|
||||
max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs);
|
||||
while (count != max) {
|
||||
count += inner.template try_dequeue_bulk<It&>(token, itemFirst, max - count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// Attempts to dequeue several elements from the queue using an explicit consumer token.
|
||||
// Returns the number of items actually dequeued, which can
|
||||
// be 0 if the timeout expires while waiting for elements,
|
||||
// and at most max.
|
||||
// Never allocates. Thread-safe.
|
||||
template<typename It, typename Rep, typename Period>
|
||||
inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::chrono::duration<Rep, Period> const& timeout)
|
||||
{
|
||||
return wait_dequeue_bulk_timed<It&>(token, itemFirst, max, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
|
||||
}
|
||||
|
||||
|
||||
// Returns an estimate of the total number of elements currently in the queue. This
|
||||
// estimate is only accurate if the queue has completely stabilized before it is called
|
||||
// (i.e. all enqueue and dequeue operations have completed and their memory effects are
|
||||
// visible on the calling thread, and no further operations start while this method is
|
||||
// being called).
|
||||
// Thread-safe.
|
||||
inline size_t size_approx() const
|
||||
{
|
||||
return (size_t)sema->availableApprox();
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the underlying atomic variables used by
|
||||
// the queue are lock-free (they should be on most platforms).
|
||||
// Thread-safe.
|
||||
static bool is_lock_free()
|
||||
{
|
||||
return ConcurrentQueue::is_lock_free();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
template<typename U, typename A1, typename A2>
|
||||
static inline U* create(A1&& a1, A2&& a2)
|
||||
{
|
||||
void* p = (Traits::malloc)(sizeof(U));
|
||||
return p != nullptr ? new (p) U(std::forward<A1>(a1), std::forward<A2>(a2)) : nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
static inline void destroy(U* p)
|
||||
{
|
||||
if (p != nullptr) {
|
||||
p->~U();
|
||||
}
|
||||
(Traits::free)(p);
|
||||
}
|
||||
|
||||
private:
|
||||
ConcurrentQueue inner;
|
||||
std::unique_ptr<LightweightSemaphore, void (*)(LightweightSemaphore*)> sema;
|
||||
};
|
||||
|
||||
|
||||
template<typename T, typename Traits>
|
||||
inline void swap(BlockingConcurrentQueue<T, Traits>& a, BlockingConcurrentQueue<T, Traits>& b) MOODYCAMEL_NOEXCEPT
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // end namespace moodycamel
|
||||
|
||||
-3743
File diff suppressed because it is too large.
Load diff
-412
@@ -1,412 +0,0 @@
|
||||
// Provides an efficient implementation of a semaphore (LightweightSemaphore).
|
||||
// This is an extension of Jeff Preshing's sempahore implementation (licensed
|
||||
// under the terms of its separate zlib license) that has been adapted and
|
||||
// extended by Cameron Desrochers.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // For std::size_t
|
||||
#include <atomic>
|
||||
#include <type_traits> // For std::make_signed<T>
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Avoid including windows.h in a header; we only need a handful of
|
||||
// items, so we'll redeclare them here (this is relatively safe since
|
||||
// the API generally has to remain stable between Windows versions).
|
||||
// I know this is an ugly hack but it still beats polluting the global
|
||||
// namespace with thousands of generic names or adding a .cpp for nothing.
|
||||
extern "C" {
|
||||
struct _SECURITY_ATTRIBUTES;
|
||||
__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
|
||||
__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
|
||||
__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
|
||||
__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
|
||||
}
|
||||
#elif defined(__MACH__)
|
||||
#include <mach/mach.h>
|
||||
#elif defined(__unix__)
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
namespace moodycamel
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
// Code in the mpmc_sema namespace below is an adaptation of Jeff Preshing's
|
||||
// portable + lightweight semaphore implementations, originally from
|
||||
// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
|
||||
// LICENSE:
|
||||
// Copyright (c) 2015 Jeff Preshing
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgement in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
#if defined(_WIN32)
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
void* m_hSema;
|
||||
|
||||
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
|
||||
public:
|
||||
Semaphore(int initialCount = 0)
|
||||
{
|
||||
assert(initialCount >= 0);
|
||||
const long maxLong = 0x7fffffff;
|
||||
m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
|
||||
assert(m_hSema);
|
||||
}
|
||||
|
||||
~Semaphore()
|
||||
{
|
||||
CloseHandle(m_hSema);
|
||||
}
|
||||
|
||||
bool wait()
|
||||
{
|
||||
const unsigned long infinite = 0xffffffff;
|
||||
return WaitForSingleObject(m_hSema, infinite) == 0;
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
return WaitForSingleObject(m_hSema, 0) == 0;
|
||||
}
|
||||
|
||||
bool timed_wait(std::uint64_t usecs)
|
||||
{
|
||||
return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0;
|
||||
}
|
||||
|
||||
void signal(int count = 1)
|
||||
{
|
||||
while (!ReleaseSemaphore(m_hSema, count, nullptr));
|
||||
}
|
||||
};
|
||||
#elif defined(__MACH__)
|
||||
//---------------------------------------------------------
|
||||
// Semaphore (Apple iOS and OSX)
|
||||
// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
|
||||
//---------------------------------------------------------
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
semaphore_t m_sema;
|
||||
|
||||
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
|
||||
public:
|
||||
Semaphore(int initialCount = 0)
|
||||
{
|
||||
assert(initialCount >= 0);
|
||||
kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
|
||||
assert(rc == KERN_SUCCESS);
|
||||
(void)rc;
|
||||
}
|
||||
|
||||
~Semaphore()
|
||||
{
|
||||
semaphore_destroy(mach_task_self(), m_sema);
|
||||
}
|
||||
|
||||
bool wait()
|
||||
{
|
||||
return semaphore_wait(m_sema) == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
return timed_wait(0);
|
||||
}
|
||||
|
||||
bool timed_wait(std::uint64_t timeout_usecs)
|
||||
{
|
||||
mach_timespec_t ts;
|
||||
ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
|
||||
ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000);
|
||||
|
||||
// added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
|
||||
kern_return_t rc = semaphore_timedwait(m_sema, ts);
|
||||
return rc == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
void signal()
|
||||
{
|
||||
while (semaphore_signal(m_sema) != KERN_SUCCESS);
|
||||
}
|
||||
|
||||
void signal(int count)
|
||||
{
|
||||
while (count-- > 0)
|
||||
{
|
||||
while (semaphore_signal(m_sema) != KERN_SUCCESS);
|
||||
}
|
||||
}
|
||||
};
|
||||
#elif defined(__unix__)
|
||||
//---------------------------------------------------------
|
||||
// Semaphore (POSIX, Linux)
|
||||
//---------------------------------------------------------
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
sem_t m_sema;
|
||||
|
||||
Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION;
|
||||
|
||||
public:
|
||||
Semaphore(int initialCount = 0)
|
||||
{
|
||||
assert(initialCount >= 0);
|
||||
int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount));
|
||||
assert(rc == 0);
|
||||
(void)rc;
|
||||
}
|
||||
|
||||
~Semaphore()
|
||||
{
|
||||
sem_destroy(&m_sema);
|
||||
}
|
||||
|
||||
bool wait()
|
||||
{
|
||||
// http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
|
||||
int rc;
|
||||
do {
|
||||
rc = sem_wait(&m_sema);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
int rc;
|
||||
do {
|
||||
rc = sem_trywait(&m_sema);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool timed_wait(std::uint64_t usecs)
|
||||
{
|
||||
struct timespec ts;
|
||||
const int usecs_in_1_sec = 1000000;
|
||||
const int nsecs_in_1_sec = 1000000000;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += (time_t)(usecs / usecs_in_1_sec);
|
||||
ts.tv_nsec += (long)(usecs % usecs_in_1_sec) * 1000;
|
||||
// sem_timedwait bombs if you have more than 1e9 in tv_nsec
|
||||
// so we have to clean things up before passing it in
|
||||
if (ts.tv_nsec >= nsecs_in_1_sec) {
|
||||
ts.tv_nsec -= nsecs_in_1_sec;
|
||||
++ts.tv_sec;
|
||||
}
|
||||
|
||||
int rc;
|
||||
do {
|
||||
rc = sem_timedwait(&m_sema, &ts);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
void signal()
|
||||
{
|
||||
while (sem_post(&m_sema) == -1);
|
||||
}
|
||||
|
||||
void signal(int count)
|
||||
{
|
||||
while (count-- > 0)
|
||||
{
|
||||
while (sem_post(&m_sema) == -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
#else
|
||||
#error Unsupported platform! (No semaphore wrapper available)
|
||||
#endif
|
||||
|
||||
} // end namespace details
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// LightweightSemaphore
|
||||
//---------------------------------------------------------
|
||||
class LightweightSemaphore
|
||||
{
|
||||
public:
|
||||
typedef std::make_signed<std::size_t>::type ssize_t;
|
||||
|
||||
private:
|
||||
std::atomic<ssize_t> m_count;
|
||||
details::Semaphore m_sema;
|
||||
int m_maxSpins;
|
||||
|
||||
bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
|
||||
{
|
||||
ssize_t oldCount;
|
||||
int spin = m_maxSpins;
|
||||
while (--spin >= 0)
|
||||
{
|
||||
oldCount = m_count.load(std::memory_order_relaxed);
|
||||
if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
|
||||
return true;
|
||||
std::atomic_signal_fence(std::memory_order_acquire); // Prevent the compiler from collapsing the loop.
|
||||
}
|
||||
oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
|
||||
if (oldCount > 0)
|
||||
return true;
|
||||
if (timeout_usecs < 0)
|
||||
{
|
||||
if (m_sema.wait())
|
||||
return true;
|
||||
}
|
||||
if (timeout_usecs > 0 && m_sema.timed_wait((std::uint64_t)timeout_usecs))
|
||||
return true;
|
||||
// At this point, we've timed out waiting for the semaphore, but the
|
||||
// count is still decremented indicating we may still be waiting on
|
||||
// it. So we have to re-adjust the count, but only if the semaphore
|
||||
// wasn't signaled enough times for us too since then. If it was, we
|
||||
// need to release the semaphore too.
|
||||
while (true)
|
||||
{
|
||||
oldCount = m_count.load(std::memory_order_acquire);
|
||||
if (oldCount >= 0 && m_sema.try_wait())
|
||||
return true;
|
||||
if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t waitManyWithPartialSpinning(ssize_t max, std::int64_t timeout_usecs = -1)
|
||||
{
|
||||
assert(max > 0);
|
||||
ssize_t oldCount;
|
||||
int spin = m_maxSpins;
|
||||
while (--spin >= 0)
|
||||
{
|
||||
oldCount = m_count.load(std::memory_order_relaxed);
|
||||
if (oldCount > 0)
|
||||
{
|
||||
ssize_t newCount = oldCount > max ? oldCount - max : 0;
|
||||
if (m_count.compare_exchange_strong(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
|
||||
return oldCount - newCount;
|
||||
}
|
||||
std::atomic_signal_fence(std::memory_order_acquire);
|
||||
}
|
||||
oldCount = m_count.fetch_sub(1, std::memory_order_acquire);
|
||||
if (oldCount <= 0)
|
||||
{
|
||||
if ((timeout_usecs == 0) || (timeout_usecs < 0 && !m_sema.wait()) || (timeout_usecs > 0 && !m_sema.timed_wait((std::uint64_t)timeout_usecs)))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
oldCount = m_count.load(std::memory_order_acquire);
|
||||
if (oldCount >= 0 && m_sema.try_wait())
|
||||
break;
|
||||
if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max > 1)
|
||||
return 1 + tryWaitMany(max - 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
public:
|
||||
LightweightSemaphore(ssize_t initialCount = 0, int maxSpins = 10000) : m_count(initialCount), m_maxSpins(maxSpins)
|
||||
{
|
||||
assert(initialCount >= 0);
|
||||
assert(maxSpins >= 0);
|
||||
}
|
||||
|
||||
bool tryWait()
|
||||
{
|
||||
ssize_t oldCount = m_count.load(std::memory_order_relaxed);
|
||||
while (oldCount > 0)
|
||||
{
|
||||
if (m_count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wait()
|
||||
{
|
||||
return tryWait() || waitWithPartialSpinning();
|
||||
}
|
||||
|
||||
bool wait(std::int64_t timeout_usecs)
|
||||
{
|
||||
return tryWait() || waitWithPartialSpinning(timeout_usecs);
|
||||
}
|
||||
|
||||
// Acquires between 0 and (greedily) max, inclusive
|
||||
ssize_t tryWaitMany(ssize_t max)
|
||||
{
|
||||
assert(max >= 0);
|
||||
ssize_t oldCount = m_count.load(std::memory_order_relaxed);
|
||||
while (oldCount > 0)
|
||||
{
|
||||
ssize_t newCount = oldCount > max ? oldCount - max : 0;
|
||||
if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed))
|
||||
return oldCount - newCount;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Acquires at least one, and (greedily) at most max
|
||||
ssize_t waitMany(ssize_t max, std::int64_t timeout_usecs)
|
||||
{
|
||||
assert(max >= 0);
|
||||
ssize_t result = tryWaitMany(max);
|
||||
if (result == 0 && max > 0)
|
||||
result = waitManyWithPartialSpinning(max, timeout_usecs);
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t waitMany(ssize_t max)
|
||||
{
|
||||
ssize_t result = waitMany(max, -1);
|
||||
assert(result > 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
void signal(ssize_t count = 1)
|
||||
{
|
||||
assert(count >= 0);
|
||||
ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release);
|
||||
ssize_t toRelease = -oldCount < count ? -oldCount : count;
|
||||
if (toRelease > 0)
|
||||
{
|
||||
m_sema.signal((int)toRelease);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t availableApprox() const
|
||||
{
|
||||
ssize_t count = m_count.load(std::memory_order_relaxed);
|
||||
return count > 0 ? static_cast<std::size_t>(count) : 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace moodycamel
|
||||
|
||||
Vendored
-105
@@ -1,105 +0,0 @@
|
||||
AccessModifierOffset: -4
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
ColumnLimit: 120
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 4
|
||||
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
#AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlinesLeft: true
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
#AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
#AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
#BinPackArguments: true
|
||||
#BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
#ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
IncludeCategories:
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
#IndentCaseLabels: true
|
||||
#IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: false
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
name: Coverage
|
||||
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
coverage:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y -qq make cmake gcc g++ lcov bc
|
||||
- name: build
|
||||
shell: bash
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=.:$(cat /etc/ld.so.conf.d/* | grep -vF "#" | tr "\\n" ":" | sed -e "s/:$//g")
|
||||
cmake . -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=On
|
||||
make all -j$(nproc)
|
||||
- name: coverage
|
||||
shell: bash
|
||||
run: |
|
||||
make coverage
|
||||
lines=$(lcov --summary coverage.info | grep -F lines | awk '{print $2}' | sed -e "s/%//g")
|
||||
if (( $(echo "${lines} < ${COVERAGE_THRESHOLD}" | bc -l) )); then
|
||||
echo "Line coverage dropped below ${COVERAGE_THRESHOLD}% to ${lines}%"
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
COVERAGE_THRESHOLD: 85.0
|
||||
@@ -1,13 +0,0 @@
|
||||
name: Lint
|
||||
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: lint
|
||||
uses: DoozyX/clang-format-lint-action@v0.12
|
||||
with:
|
||||
clangFormatVersion: 12
|
||||
source: './include/cpp-statsd-client ./tests'
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Linux
|
||||
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y -qq make cmake gcc g++
|
||||
- name: build
|
||||
shell: bash
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=.:$(cat /etc/ld.so.conf.d/* | grep -vF "#" | tr "\\n" ":" | sed -e "s/:$//g")
|
||||
cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_SANITIZERS=On
|
||||
make all -j$(nproc)
|
||||
- name: test
|
||||
shell: bash
|
||||
run: |
|
||||
make test
|
||||
@@ -1,18 +0,0 @@
|
||||
name: Windows
|
||||
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
run: |
|
||||
choco install cmake
|
||||
- name: build
|
||||
run: |
|
||||
cmake -S . -B build -G "Visual Studio 16 2019" -A x64
|
||||
cmake --build build --target ALL_BUILD --config Release
|
||||
- name: test
|
||||
run: |
|
||||
cmake --build build --target RUN_TESTS --config Release
|
||||
@@ -1 +0,0 @@
|
||||
bin
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
# Basic project setup
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(cpp-statsd-client
|
||||
VERSION 1.0.2
|
||||
LANGUAGES CXX
|
||||
DESCRIPTION "A header-only StatsD client implemented in C++"
|
||||
HOMEPAGE_URL "https://github.com/vthiery/cpp-statsd-client")
|
||||
|
||||
option(CPP_STATSD_STANDALONE "Allows configuration of targets for verifying library functionality" ON)
|
||||
option(ENABLE_TESTS "Build tests" ON)
|
||||
option(ENABLE_COVERAGE "Build with coverage instrumentalisation" OFF)
|
||||
|
||||
if(NOT CPP_STATSD_STANDALONE)
|
||||
set(ENABLE_TESTS OFF)
|
||||
set(ENABLE_COVERAGE OFF)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
find_package(Threads)
|
||||
|
||||
# Optional code coverage targets
|
||||
if(ENABLE_COVERAGE)
|
||||
set(COVERAGE_EXCLUDES /usr/*)
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/CodeCoverage.cmake)
|
||||
APPEND_COVERAGE_COMPILER_FLAGS()
|
||||
SETUP_TARGET_FOR_COVERAGE_LCOV(NAME coverage
|
||||
EXECUTABLE testStatsdClient
|
||||
DEPENDENCIES ${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
|
||||
# The library target
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
target_include_directories(
|
||||
${PROJECT_NAME}
|
||||
INTERFACE $<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE Threads::Threads)
|
||||
if(WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE ws2_32)
|
||||
endif()
|
||||
|
||||
# The installation and pkg-config-like cmake config
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}_Targets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
configure_package_config_file(
|
||||
"${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in"
|
||||
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION
|
||||
${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
|
||||
install(EXPORT ${PROJECT_NAME}_Targets
|
||||
FILE ${PROJECT_NAME}Targets.cmake
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
|
||||
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
|
||||
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include DESTINATION include)
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
# The test targets
|
||||
add_executable(testStatsdClient ${CMAKE_CURRENT_SOURCE_DIR}/tests/testStatsdClient.cpp)
|
||||
if(WIN32)
|
||||
target_compile_options(testStatsdClient PRIVATE -W4 -WX /external:W0)
|
||||
else()
|
||||
target_compile_options(testStatsdClient PRIVATE -Wall -Wextra -pedantic -Werror)
|
||||
endif()
|
||||
target_include_directories(testStatsdClient PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||
target_link_libraries(testStatsdClient ${PROJECT_NAME})
|
||||
|
||||
set_property(TARGET testStatsdClient PROPERTY CXX_STANDARD 11)
|
||||
set_property(TARGET testStatsdClient PROPERTY CXX_EXTENSIONS OFF)
|
||||
|
||||
# The test suite
|
||||
enable_testing()
|
||||
add_test(ctestTestStatsdClient testStatsdClient)
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS testStatsdClient)
|
||||
endif()
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Vincent Thiery
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
# simple makefile to build, test and clean
|
||||
|
||||
BUILD_MODE ?= Release
|
||||
ENABLE_COVERAGE ?= On
|
||||
|
||||
build: clean
|
||||
@echo "Build in ${BUILD_MODE} mode"
|
||||
mkdir -p bin/${BUILD_MODE}
|
||||
@cd bin/${BUILD_MODE}; cmake ../../ -DCMAKE_BUILD_TYPE=${BUILD_MODE} -DENABLE_COVERAGE=${ENABLE_COVERAGE}
|
||||
@cd bin/${BUILD_MODE}; make
|
||||
|
||||
test: build
|
||||
@cd bin/${BUILD_MODE}; make test
|
||||
|
||||
coverage: build
|
||||
@cd bin/${BUILD_MODE}; make coverage
|
||||
|
||||
install: build
|
||||
@cd bin/${BUILD_MODE}; make install
|
||||
|
||||
clean:
|
||||
@rm -rf bin
|
||||
Vendored
-150
@@ -1,150 +0,0 @@
|
||||
# C++ StatsD Client
|
||||
|
||||

|
||||
|
||||
[](https://github.com/vthiery/cpp-statsd-client/releases/latest)
|
||||

|
||||
[](https://github.com/vthiery/cpp-statsd-client/actions/workflows/linux.yml?query=branch%3Amaster++)
|
||||
[](https://github.com/vthiery/cpp-statsd-client/actions/workflows/windows.yml?query=branch%3Amaster++)
|
||||
|
||||
A header-only StatsD client implemented in C++.
|
||||
The client allows:
|
||||
|
||||
- batching,
|
||||
- change of configuration at runtime,
|
||||
- user-defined frequency rate.
|
||||
|
||||
## Install and Test
|
||||
|
||||
### Makefile
|
||||
|
||||
In order to install the header files and/or run the tests, simply use the Makefile and execute
|
||||
|
||||
```sh
|
||||
make install
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```sh
|
||||
make test
|
||||
```
|
||||
|
||||
### Conan
|
||||
|
||||
If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add statsdclient/x.y.z@vthiery/stable to your conanfile.py's requires, where x.y.z is the release version you want to use. Please file issues here if you experience problems with the packages. You can also directly download the latest version [here](https://bintray.com/vthiery/conan-packages/statsdclient%3Avthiery/_latestVersion).
|
||||
|
||||
## Usage
|
||||
|
||||
### Example
|
||||
|
||||
A simple example of how to use the client:
|
||||
|
||||
```cpp
|
||||
#include "StatsdClient.hpp"
|
||||
using namespace Statsd;
|
||||
|
||||
int main() {
|
||||
// Define the client on localhost, with port 8080,
|
||||
// using a prefix,
|
||||
// a batching size of 20 bytes,
|
||||
// and three points of precision for floating point gauge values
|
||||
StatsdClient client{ "127.0.0.1", 8080, "myPrefix", 20, 3 };
|
||||
|
||||
// Increment the metric "coco"
|
||||
client.increment("coco");
|
||||
|
||||
// Decrement the metric "kiki"
|
||||
client.decrement("kiki");
|
||||
|
||||
// Adjusts "toto" by +3
|
||||
client.count("toto", 2, 0.1f);
|
||||
|
||||
// Record a gauge "titi" to 3
|
||||
client.gauge("titi", 3);
|
||||
|
||||
// Record a timing of 2ms for "myTiming"
|
||||
client.timing("myTiming", 2, 0.1f);
|
||||
|
||||
// Send a metric explicitly
|
||||
client.send("tutu", 4, "c", 2.0f);
|
||||
exit(0);
|
||||
}
|
||||
```
|
||||
|
||||
### Advanced Testing
|
||||
|
||||
A simple mock StatsD server can be found at `tests/StatsdServer.hpp`. This can be used to do simple validation of your application's metrics, typically in the form of unit tests. In fact this is the primary means by which this library is tested. The mock server itself is not distributed with the library so to use it you'd need to vendor this project into your project. Once you have though, you can test your application's use of the client like so:
|
||||
|
||||
```cpp
|
||||
#include "StatsdClient.hpp"
|
||||
#include "StatsdServer.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
using namespace Statsd;
|
||||
|
||||
struct MyApp {
|
||||
void doWork() const {
|
||||
m_client.count("bar", 3);
|
||||
}
|
||||
private:
|
||||
StatsdClient m_client{"localhost", 8125, "foo"};
|
||||
};
|
||||
|
||||
int main() {
|
||||
StatsdServer mockServer;
|
||||
|
||||
MyApp app;
|
||||
app.doWork();
|
||||
|
||||
assert(mockServer.receive() == "foo.bar:3|c");
|
||||
exit(0);
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
The configuration of the client must be input when one instantiates it. Nevertheless, the API allows the configuration ot change afterwards. For example, one can do the following:
|
||||
|
||||
```cpp
|
||||
#include "StatsdClient.hpp"
|
||||
using namespace Statsd;
|
||||
|
||||
int main()
|
||||
{
|
||||
// Define the client on localhost, with port 8080,
|
||||
// using a prefix,
|
||||
// a batching size of 20 bytes,
|
||||
// and three points of precision for floating point gauge values
|
||||
StatsdClient client{ "127.0.0.1", 8080, "myPrefix", 20, 3 };
|
||||
|
||||
client.increment("coco");
|
||||
|
||||
// Set a new configuration, using a different port, a different prefix, and more gauge precision
|
||||
client.setConfig("127.0.0.1", 8000, "anotherPrefix", 6);
|
||||
|
||||
client.decrement("kiki");
|
||||
}
|
||||
```
|
||||
|
||||
The batchsize is the only parameter that cannot be changed for the time being.
|
||||
|
||||
### Batching
|
||||
|
||||
The client supports batching of the metrics. The batch size parameter is the number of bytes to allow in each batch (UDP datagram payload) to be sent to the statsd process. This number is not a hard limit. If appending the current stat to the current batch (separated by the `'\n'` character) pushes the current batch over the batch size, that batch is enqueued (not sent) and a new batch is started. If batch size is 0, the default, then each stat is sent individually to the statsd process and no batches are enqueued.
|
||||
|
||||
### Sending
|
||||
|
||||
As previously mentioned, if batching is disabled (by setting the batch size to 0) then every stat is sent immediately in a blocking fashion. If batching is enabled (ie non-zero) then you may also set the send interval. The send interval controls the time, in milliseconds, to wait before flushing/sending the queued stats batches to the statsd process. When the send interval is non-zero a background thread is spawned which will do the flushing/sending at the configured send interval, in other words asynchronously. The queuing mechanism in this case is *not* lock-free. If batching is enabled but the send interval is set to zero then the queued batchs of stats will not be sent automatically by a background thread but must be sent manually via the `flush` method. The `flush` method is a blocking call.
|
||||
|
||||
|
||||
### Frequency rate
|
||||
|
||||
When sending a metric, a frequency rate can be set in order to limit the metrics' sampling. By default, the frequency rate is set to one and won't affect the sampling. If set to a value `epsilon` (0.0001 for the time being) close to one, the sampling is not affected either.
|
||||
|
||||
If the frequency rate is set and `epsilon` different from one, the sending will be rejected randomly (the higher the frequency rate, the lower the probability of rejection).
|
||||
|
||||
## License
|
||||
|
||||
This library is under MIT license.
|
||||
-708
@@ -1,708 +0,0 @@
|
||||
# Copyright (c) 2012 - 2017, Lars Bilke
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# CHANGES:
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# 2016-02-03, Lars Bilke
|
||||
# - Refactored functions to use named parameters
|
||||
#
|
||||
# 2017-06-02, Lars Bilke
|
||||
# - Merged with modified version from github.com/ufz/ogs
|
||||
#
|
||||
# 2019-05-06, Anatolii Kurotych
|
||||
# - Remove unnecessary --coverage flag
|
||||
#
|
||||
# 2019-12-13, FeRD (Frank Dana)
|
||||
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
|
||||
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
|
||||
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
|
||||
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
|
||||
# - Set lcov basedir with -b argument
|
||||
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
|
||||
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
|
||||
# - Delete output dir, .info file on 'make clean'
|
||||
# - Remove Python detection, since version mismatches will break gcovr
|
||||
# - Minor cleanup (lowercase function names, update examples...)
|
||||
#
|
||||
# 2019-12-19, FeRD (Frank Dana)
|
||||
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
|
||||
#
|
||||
# 2020-01-19, Bob Apthorpe
|
||||
# - Added gfortran support
|
||||
#
|
||||
# 2020-02-17, FeRD (Frank Dana)
|
||||
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
|
||||
# in EXCLUDEs, and remove manual escaping from gcovr targets
|
||||
#
|
||||
# 2021-01-19, Robin Mueller
|
||||
# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
|
||||
# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
|
||||
# flags to the gcovr command
|
||||
#
|
||||
# 2020-05-04, Mihchael Davis
|
||||
# - Add -fprofile-abs-path to make gcno files contain absolute paths
|
||||
# - Fix BASE_DIRECTORY not working when defined
|
||||
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
|
||||
#
|
||||
# 2021-05-10, Martin Stump
|
||||
# - Check if the generator is multi-config before warning about non-Debug builds
|
||||
#
|
||||
# USAGE:
|
||||
#
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
|
||||
# using a CMake option() to enable it just optionally):
|
||||
# include(CodeCoverage)
|
||||
#
|
||||
# 3. Append necessary compiler flags:
|
||||
# append_coverage_compiler_flags()
|
||||
#
|
||||
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
|
||||
#
|
||||
# 4. If you need to exclude additional directories from the report, specify them
|
||||
# using full paths in the COVERAGE_EXCLUDES variable before calling
|
||||
# setup_target_for_coverage_*().
|
||||
# Example:
|
||||
# set(COVERAGE_EXCLUDES
|
||||
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
|
||||
# '/path/to/my/src/dir2/*')
|
||||
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
|
||||
# Example:
|
||||
# setup_target_for_coverage_lcov(
|
||||
# NAME coverage
|
||||
# EXECUTABLE testrunner
|
||||
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
|
||||
#
|
||||
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
|
||||
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
|
||||
# Example:
|
||||
# set(COVERAGE_EXCLUDES "dir1/*")
|
||||
# setup_target_for_coverage_gcovr_html(
|
||||
# NAME coverage
|
||||
# EXECUTABLE testrunner
|
||||
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
|
||||
# EXCLUDE "dir2/*")
|
||||
#
|
||||
# 5. Use the functions described below to create a custom make target which
|
||||
# runs your test executable and produces a code coverage report.
|
||||
#
|
||||
# 6. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
|
||||
|
||||
# Check prereqs
|
||||
find_program( GCOV_PATH gcov )
|
||||
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
|
||||
find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
|
||||
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
|
||||
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
|
||||
find_program( CPPFILT_PATH NAMES c++filt )
|
||||
|
||||
if(NOT GCOV_PATH)
|
||||
message(FATAL_ERROR "gcov not found! Aborting...")
|
||||
endif() # NOT GCOV_PATH
|
||||
|
||||
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
||||
list(GET LANGUAGES 0 LANG)
|
||||
|
||||
if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
|
||||
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
|
||||
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
|
||||
endif()
|
||||
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
|
||||
if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
|
||||
# Do nothing; exit conditional without error if true
|
||||
elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
|
||||
# Do nothing; exit conditional without error if true
|
||||
else()
|
||||
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
|
||||
CACHE INTERNAL "")
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
|
||||
if(HAVE_fprofile_abs_path)
|
||||
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_Fortran_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C++ compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_C_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C compiler during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used for linking binaries during coverage builds."
|
||||
FORCE )
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
|
||||
FORCE )
|
||||
mark_as_advanced(
|
||||
CMAKE_Fortran_FLAGS_COVERAGE
|
||||
CMAKE_CXX_FLAGS_COVERAGE
|
||||
CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
|
||||
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
|
||||
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
|
||||
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
|
||||
link_libraries(gcov)
|
||||
endif()
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_lcov(
|
||||
# NAME testrunner_coverage # New target name
|
||||
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES testrunner # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# NO_DEMANGLE # Don't demangle C++ symbols
|
||||
# # even if c++filt is found
|
||||
# )
|
||||
function(setup_target_for_coverage_lcov)
|
||||
|
||||
set(options NO_DEMANGLE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT LCOV_PATH)
|
||||
message(FATAL_ERROR "lcov not found! Aborting...")
|
||||
endif() # NOT LCOV_PATH
|
||||
|
||||
if(NOT GENHTML_PATH)
|
||||
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
endif() # NOT GENHTML_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(LCOV_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
|
||||
|
||||
# Conditional arguments
|
||||
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
|
||||
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
|
||||
endif()
|
||||
|
||||
# Setting up commands which will be run to generate coverage data.
|
||||
# Cleanup lcov
|
||||
set(LCOV_CLEAN_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
|
||||
-b ${BASEDIR} --zerocounters
|
||||
)
|
||||
# Create baseline to make sure untouched files show up in the report
|
||||
set(LCOV_BASELINE_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
|
||||
${BASEDIR} -o ${Coverage_NAME}.base
|
||||
)
|
||||
# Run tests
|
||||
set(LCOV_EXEC_TESTS_CMD
|
||||
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
)
|
||||
# Capturing lcov counters and generating report
|
||||
set(LCOV_CAPTURE_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
|
||||
${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
|
||||
)
|
||||
# add baseline counters
|
||||
set(LCOV_BASELINE_COUNT_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
|
||||
-a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
|
||||
)
|
||||
# filter collected data to final coverage report
|
||||
set(LCOV_FILTER_CMD
|
||||
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
|
||||
${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
|
||||
)
|
||||
# Generate HTML output
|
||||
set(LCOV_GEN_HTML_CMD
|
||||
${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
|
||||
${Coverage_NAME} ${Coverage_NAME}.info
|
||||
)
|
||||
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Executed command report")
|
||||
message(STATUS "Command to clean up lcov: ")
|
||||
string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
|
||||
message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to create baseline: ")
|
||||
string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
|
||||
message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to run the tests: ")
|
||||
string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to capture counters and generate report: ")
|
||||
string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
|
||||
message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to add baseline counters: ")
|
||||
string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
|
||||
message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to filter collected data: ")
|
||||
string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
|
||||
message(STATUS "${LCOV_FILTER_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate lcov HTML output: ")
|
||||
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
|
||||
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
|
||||
endif()
|
||||
|
||||
# Setup target
|
||||
add_custom_target(${Coverage_NAME}
|
||||
COMMAND ${LCOV_CLEAN_CMD}
|
||||
COMMAND ${LCOV_BASELINE_CMD}
|
||||
COMMAND ${LCOV_EXEC_TESTS_CMD}
|
||||
COMMAND ${LCOV_CAPTURE_CMD}
|
||||
COMMAND ${LCOV_BASELINE_COUNT_CMD}
|
||||
COMMAND ${LCOV_FILTER_CMD}
|
||||
COMMAND ${LCOV_GEN_HTML_CMD}
|
||||
|
||||
# Set output files as GENERATED (will be removed on 'make clean')
|
||||
BYPRODUCTS
|
||||
${Coverage_NAME}.base
|
||||
${Coverage_NAME}.capture
|
||||
${Coverage_NAME}.total
|
||||
${Coverage_NAME}.info
|
||||
${Coverage_NAME}/index.html
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show where to find the lcov info report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
endfunction() # setup_target_for_coverage_lcov
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_gcovr_xml(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# )
|
||||
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
|
||||
# GCVOR command.
|
||||
function(setup_target_for_coverage_gcovr_xml)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDE_ARGS "")
|
||||
foreach(EXCLUDE ${GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
# Run tests
|
||||
set(GCOVR_XML_EXEC_TESTS_CMD
|
||||
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
)
|
||||
# Running gcovr
|
||||
set(GCOVR_XML_CMD
|
||||
${GCOVR_PATH} --xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS}
|
||||
--object-directory=${PROJECT_BINARY_DIR} -o ${Coverage_NAME}.xml
|
||||
)
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Executed command report")
|
||||
|
||||
message(STATUS "Command to run tests: ")
|
||||
string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate gcovr XML coverage data: ")
|
||||
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
|
||||
message(STATUS "${GCOVR_XML_CMD_SPACED}")
|
||||
endif()
|
||||
|
||||
add_custom_target(${Coverage_NAME}
|
||||
COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
|
||||
COMMAND ${GCOVR_XML_CMD}
|
||||
|
||||
BYPRODUCTS ${Coverage_NAME}.xml
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
|
||||
)
|
||||
endfunction() # setup_target_for_coverage_gcovr_xml
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_gcovr_html(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# )
|
||||
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
|
||||
# GCVOR command.
|
||||
function(setup_target_for_coverage_gcovr_html)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDE_ARGS "")
|
||||
foreach(EXCLUDE ${GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
# Run tests
|
||||
set(GCOVR_HTML_EXEC_TESTS_CMD
|
||||
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
|
||||
)
|
||||
# Create folder
|
||||
set(GCOVR_HTML_FOLDER_CMD
|
||||
${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME}
|
||||
)
|
||||
# Running gcovr
|
||||
set(GCOVR_HTML_CMD
|
||||
${GCOVR_PATH} --html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
|
||||
${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR}
|
||||
-o ${Coverage_NAME}/index.html
|
||||
)
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Executed command report")
|
||||
|
||||
message(STATUS "Command to run tests: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to create a folder: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate gcovr HTML coverage data: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_CMD_SPACED}")
|
||||
endif()
|
||||
|
||||
add_custom_target(${Coverage_NAME}
|
||||
COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
|
||||
COMMAND ${GCOVR_HTML_FOLDER_CMD}
|
||||
COMMAND ${GCOVR_HTML_CMD}
|
||||
|
||||
BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Running gcovr to produce HTML code coverage report."
|
||||
)
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
|
||||
endfunction() # setup_target_for_coverage_gcovr_html
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_fastcov(
|
||||
# NAME testrunner_coverage # New target name
|
||||
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES testrunner # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
|
||||
# NO_DEMANGLE # Don't demangle C++ symbols
|
||||
# # even if c++filt is found
|
||||
# SKIP_HTML # Don't create html report
|
||||
# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths
|
||||
# )
|
||||
function(setup_target_for_coverage_fastcov)
|
||||
|
||||
set(options NO_DEMANGLE SKIP_HTML)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT FASTCOV_PATH)
|
||||
message(FATAL_ERROR "fastcov not found! Aborting...")
|
||||
endif()
|
||||
|
||||
if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH)
|
||||
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
endif()
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (Patterns, not paths, for fastcov)
|
||||
set(FASTCOV_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
|
||||
list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
|
||||
|
||||
# Conditional arguments
|
||||
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
|
||||
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
|
||||
endif()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
|
||||
|
||||
set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
|
||||
--search-directory ${BASEDIR}
|
||||
--process-gcno
|
||||
--output ${Coverage_NAME}.json
|
||||
--exclude ${FASTCOV_EXCLUDES}
|
||||
--exclude ${FASTCOV_EXCLUDES}
|
||||
)
|
||||
|
||||
set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH}
|
||||
-C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info
|
||||
)
|
||||
|
||||
if(Coverage_SKIP_HTML)
|
||||
set(FASTCOV_HTML_CMD ";")
|
||||
else()
|
||||
set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
|
||||
-o ${Coverage_NAME} ${Coverage_NAME}.info
|
||||
)
|
||||
endif()
|
||||
|
||||
set(FASTCOV_POST_CMD ";")
|
||||
if(Coverage_POST_CMD)
|
||||
set(FASTCOV_POST_CMD ${Coverage_POST_CMD})
|
||||
endif()
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
|
||||
|
||||
message(" Running tests:")
|
||||
string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
|
||||
message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(" Capturing fastcov counters and generating report:")
|
||||
string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
|
||||
message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
|
||||
|
||||
message(" Converting fastcov .json to lcov .info:")
|
||||
string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}")
|
||||
message(" ${FASTCOV_CONVERT_CMD_SPACED}")
|
||||
|
||||
if(NOT Coverage_SKIP_HTML)
|
||||
message(" Generating HTML report: ")
|
||||
string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
|
||||
message(" ${FASTCOV_HTML_CMD_SPACED}")
|
||||
endif()
|
||||
if(Coverage_POST_CMD)
|
||||
message(" Running post command: ")
|
||||
string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}")
|
||||
message(" ${FASTCOV_POST_CMD_SPACED}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Setup target
|
||||
add_custom_target(${Coverage_NAME}
|
||||
|
||||
# Cleanup fastcov
|
||||
COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
|
||||
--search-directory ${BASEDIR}
|
||||
--zerocounters
|
||||
|
||||
COMMAND ${FASTCOV_EXEC_TESTS_CMD}
|
||||
COMMAND ${FASTCOV_CAPTURE_CMD}
|
||||
COMMAND ${FASTCOV_CONVERT_CMD}
|
||||
COMMAND ${FASTCOV_HTML_CMD}
|
||||
COMMAND ${FASTCOV_POST_CMD}
|
||||
|
||||
# Set output files as GENERATED (will be removed on 'make clean')
|
||||
BYPRODUCTS
|
||||
${Coverage_NAME}.info
|
||||
${Coverage_NAME}.json
|
||||
${Coverage_NAME}/index.html # report directory
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.")
|
||||
if(NOT Coverage_SKIP_HTML)
|
||||
string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
|
||||
endif()
|
||||
# Show where to find the fastcov info report
|
||||
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
|
||||
)
|
||||
|
||||
endfunction() # setup_target_for_coverage_fastcov
|
||||
|
||||
function(append_coverage_compiler_flags)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
|
||||
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
|
||||
endfunction() # append_coverage_compiler_flags
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
# Copyright (c) 2016, Ruslan Baratov
|
||||
#
|
||||
# Licensed under the MIT License (the "License"); you may not use this file except
|
||||
# in compliance with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://opensource.org/licenses/MIT
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software distributed
|
||||
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations under the License.
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
@@ -1,4 +0,0 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="515px" height="315px" viewBox="0 0 515 315" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Artboard Copy</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||
<g id="Artboard-Copy" stroke-width="3">
|
||||
<g id="Group" transform="translate(55.000000, 70.000000)">
|
||||
<polyline id="Path-4" stroke="#F3A530" points="7.5 118.5 48.5 44.5 191.5 170 355 87"></polyline>
|
||||
<polyline id="Path-5" stroke="#30499B" points="0.5 62 113 160.5 355 0"></polyline>
|
||||
<polyline id="Path-6" stroke="#4CB2D4" points="42 146.5 194 39 406 175.5"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 968 B |
@@ -1,318 +0,0 @@
|
||||
#ifndef STATSD_CLIENT_HPP
|
||||
#define STATSD_CLIENT_HPP
|
||||
|
||||
#include <cpp-statsd-client/UDPSender.hpp>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Statsd {
|
||||
|
||||
/*!
|
||||
*
|
||||
* Statsd client
|
||||
*
|
||||
* This is the Statsd client, exposing the classic methods
|
||||
* and relying on a UDP sender for the actual sending.
|
||||
*
|
||||
* The prefix for a stat is provided once at construction or
|
||||
* on reconfiguring the client. The separator character '.'
|
||||
* is automatically inserted between the prefix and the stats
|
||||
* key, therefore you should neither append one to the prefix
|
||||
* nor prepend one to the key
|
||||
*
|
||||
* The sampling frequency is specified per call and uses a
|
||||
* random number generator to determine whether or not the stat
|
||||
* will be recorded this time or not.
|
||||
*
|
||||
* The top level configuration includes 2 optional parameters
|
||||
* that determine how the stats are delivered to statsd. These
|
||||
* parameters are the batching size and the send interval.
|
||||
*
|
||||
* The batching size controls the number of bytes to send
|
||||
* in each UDP datagram to statsd. This is not a hard limit as
|
||||
* we continue appending to a batch of stats until the limit
|
||||
* has been reached or surpassed. When this occurs we add the
|
||||
* batch to a queue and create a new batch to appended to. A
|
||||
* value of 0 for the batching size will disable batching such
|
||||
* that each stat will be sent to the daemon individually.
|
||||
*
|
||||
* The send interval controls the rate at which queued batches
|
||||
* of stats will be sent to statsd. If batching is disabled,
|
||||
* this value is ignored and each individual stat is sent to
|
||||
* statsd immediately in a blocking fashion. If batching is
|
||||
* enabled (ie. non-zero) then the send interval is the number
|
||||
* of milliseconds to wait before flushing the queue of
|
||||
* batched stats messages to the daemon. This is done in a non-
|
||||
* blocking fashion via a background thread. If the send
|
||||
* interval is 0 then the stats messages are appended to a
|
||||
* queue until the caller manually flushes the queue via the
|
||||
* flush method.
|
||||
*
|
||||
*/
|
||||
class StatsdClient {
|
||||
public:
|
||||
//!@name Constructor and destructor, non-copyable
|
||||
//!@{
|
||||
|
||||
//! Constructor
|
||||
StatsdClient(const std::string& host,
|
||||
const uint16_t port,
|
||||
const std::string& prefix,
|
||||
const uint64_t batchsize = 0,
|
||||
const uint64_t sendInterval = 1000,
|
||||
const unsigned int gaugePrecision = 4) noexcept;
|
||||
|
||||
StatsdClient(const StatsdClient&) = delete;
|
||||
StatsdClient& operator=(const StatsdClient&) = delete;
|
||||
|
||||
//!@}
|
||||
|
||||
//!@name Methods
|
||||
//!@{
|
||||
|
||||
//! Sets a configuration { host, port, prefix, batchsize }
|
||||
void setConfig(const std::string& host,
|
||||
const uint16_t port,
|
||||
const std::string& prefix,
|
||||
const uint64_t batchsize = 0,
|
||||
const uint64_t sendInterval = 1000,
|
||||
const unsigned int gaugePrecision = 4) noexcept;
|
||||
|
||||
//! Returns the error message as an std::string
|
||||
const std::string& errorMessage() const noexcept;
|
||||
|
||||
//! Increments the key, at a given frequency rate
|
||||
void increment(const std::string& key,
|
||||
float frequency = 1.0f,
|
||||
const std::vector<std::string>& tags = {}) const noexcept;
|
||||
|
||||
//! Increments the key, at a given frequency rate
|
||||
void decrement(const std::string& key,
|
||||
float frequency = 1.0f,
|
||||
const std::vector<std::string>& tags = {}) const noexcept;
|
||||
|
||||
//! Adjusts the specified key by a given delta, at a given frequency rate
|
||||
void count(const std::string& key,
|
||||
const int delta,
|
||||
float frequency = 1.0f,
|
||||
const std::vector<std::string>& tags = {}) const noexcept;
|
||||
|
||||
//! Records a gauge for the key, with a given value, at a given frequency rate
|
||||
template <typename T>
|
||||
void gauge(const std::string& key,
|
||||
const T value,
|
||||
float frequency = 1.0f,
|
||||
const std::vector<std::string>& tags = {}) const noexcept;
|
||||
|
||||
//! Records a timing for a key, at a given frequency
|
||||
void timing(const std::string& key,
|
||||
const unsigned int ms,
|
||||
float frequency = 1.0f,
|
||||
const std::vector<std::string>& tags = {}) const noexcept;
|
||||
|
||||
//! Records a count of unique occurrences for a key, at a given frequency
|
||||
void set(const std::string& key,
|
||||
const unsigned int sum,
|
||||
float frequency = 1.0f,
|
||||
const std::vector<std::string>& tags = {}) const noexcept;
|
||||
|
||||
//! Seed the RNG that controls sampling
|
||||
void seed(unsigned int seed = std::random_device()()) noexcept;
|
||||
|
||||
//! Flush any queued stats to the daemon
|
||||
void flush() noexcept;
|
||||
|
||||
//!@}
|
||||
|
||||
private:
|
||||
// @name Private methods
|
||||
// @{
|
||||
|
||||
//! Send a value for a key, according to its type, at a given frequency
|
||||
template <typename T>
|
||||
void send(const std::string& key,
|
||||
const T value,
|
||||
const char* type,
|
||||
float frequency,
|
||||
const std::vector<std::string>& tags) const noexcept;
|
||||
|
||||
//!@}
|
||||
|
||||
private:
|
||||
//! The prefix to be used for metrics
|
||||
std::string m_prefix;
|
||||
|
||||
//! The UDP sender to be used for actual sending
|
||||
std::unique_ptr<UDPSender> m_sender;
|
||||
|
||||
//! The random number generator for handling sampling
|
||||
mutable std::mt19937 m_randomEngine;
|
||||
|
||||
//! The buffer string format our stats before sending them
|
||||
mutable std::string m_buffer;
|
||||
|
||||
//! Fixed floating point precision of gauges
|
||||
unsigned int m_gaugePrecision;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
inline std::string sanitizePrefix(std::string prefix) {
|
||||
// For convenience we provide the dot when generating the stat message
|
||||
if (!prefix.empty() && prefix.back() == '.') {
|
||||
prefix.pop_back();
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
// All supported metric types
|
||||
constexpr char METRIC_TYPE_COUNT[] = "c";
|
||||
constexpr char METRIC_TYPE_GAUGE[] = "g";
|
||||
constexpr char METRIC_TYPE_TIMING[] = "ms";
|
||||
constexpr char METRIC_TYPE_SET[] = "s";
|
||||
} // namespace detail
|
||||
|
||||
inline StatsdClient::StatsdClient(const std::string& host,
|
||||
const uint16_t port,
|
||||
const std::string& prefix,
|
||||
const uint64_t batchsize,
|
||||
const uint64_t sendInterval,
|
||||
const unsigned int gaugePrecision) noexcept
|
||||
: m_prefix(detail::sanitizePrefix(prefix)),
|
||||
m_sender(new UDPSender{host, port, batchsize, sendInterval}),
|
||||
m_gaugePrecision(gaugePrecision) {
|
||||
// Initialize the random generator to be used for sampling
|
||||
seed();
|
||||
// Avoid re-allocations by reserving a generous buffer
|
||||
m_buffer.reserve(256);
|
||||
}
|
||||
|
||||
inline void StatsdClient::setConfig(const std::string& host,
|
||||
const uint16_t port,
|
||||
const std::string& prefix,
|
||||
const uint64_t batchsize,
|
||||
const uint64_t sendInterval,
|
||||
const unsigned int gaugePrecision) noexcept {
|
||||
m_prefix = detail::sanitizePrefix(prefix);
|
||||
m_sender.reset(new UDPSender(host, port, batchsize, sendInterval));
|
||||
m_gaugePrecision = gaugePrecision;
|
||||
}
|
||||
|
||||
inline const std::string& StatsdClient::errorMessage() const noexcept {
|
||||
return m_sender->errorMessage();
|
||||
}
|
||||
|
||||
inline void StatsdClient::decrement(const std::string& key,
|
||||
float frequency,
|
||||
const std::vector<std::string>& tags) const noexcept {
|
||||
count(key, -1, frequency, tags);
|
||||
}
|
||||
|
||||
inline void StatsdClient::increment(const std::string& key,
|
||||
float frequency,
|
||||
const std::vector<std::string>& tags) const noexcept {
|
||||
count(key, 1, frequency, tags);
|
||||
}
|
||||
|
||||
inline void StatsdClient::count(const std::string& key,
|
||||
const int delta,
|
||||
float frequency,
|
||||
const std::vector<std::string>& tags) const noexcept {
|
||||
send(key, delta, detail::METRIC_TYPE_COUNT, frequency, tags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void StatsdClient::gauge(const std::string& key,
|
||||
const T value,
|
||||
const float frequency,
|
||||
const std::vector<std::string>& tags) const noexcept {
|
||||
send(key, value, detail::METRIC_TYPE_GAUGE, frequency, tags);
|
||||
}
|
||||
|
||||
inline void StatsdClient::timing(const std::string& key,
|
||||
const unsigned int ms,
|
||||
float frequency,
|
||||
const std::vector<std::string>& tags) const noexcept {
|
||||
send(key, ms, detail::METRIC_TYPE_TIMING, frequency, tags);
|
||||
}
|
||||
|
||||
inline void StatsdClient::set(const std::string& key,
|
||||
const unsigned int sum,
|
||||
float frequency,
|
||||
const std::vector<std::string>& tags) const noexcept {
|
||||
send(key, sum, detail::METRIC_TYPE_SET, frequency, tags);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void StatsdClient::send(const std::string& key,
|
||||
const T value,
|
||||
const char* type,
|
||||
float frequency,
|
||||
const std::vector<std::string>& tags) const noexcept {
|
||||
// Bail if we can't send anything anyway
|
||||
if (!m_sender->initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A valid frequency is: 0 <= f <= 1
|
||||
// At 0 you never emit the stat, at 1 you always emit the stat and with anything else you roll the dice
|
||||
frequency = std::max(std::min(frequency, 1.f), 0.f);
|
||||
constexpr float epsilon{0.0001f};
|
||||
const bool isFrequencyOne = std::fabs(frequency - 1.0f) < epsilon;
|
||||
const bool isFrequencyZero = std::fabs(frequency) < epsilon;
|
||||
if (isFrequencyZero ||
|
||||
(!isFrequencyOne && (frequency < std::uniform_real_distribution<float>(0.f, 1.f)(m_randomEngine)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Format the stat message
|
||||
std::stringstream valueStream;
|
||||
valueStream << std::fixed << std::setprecision(m_gaugePrecision) << value;
|
||||
|
||||
m_buffer.clear();
|
||||
|
||||
m_buffer.append(m_prefix);
|
||||
if (!m_prefix.empty() && !key.empty()) {
|
||||
m_buffer.push_back('.');
|
||||
}
|
||||
|
||||
m_buffer.append(key);
|
||||
m_buffer.push_back(':');
|
||||
m_buffer.append(valueStream.str());
|
||||
m_buffer.push_back('|');
|
||||
m_buffer.append(type);
|
||||
|
||||
if (frequency < 1.f) {
|
||||
m_buffer.append("|@0.");
|
||||
m_buffer.append(std::to_string(static_cast<int>(frequency * 100)));
|
||||
}
|
||||
|
||||
if (!tags.empty()) {
|
||||
m_buffer.append("|#");
|
||||
for (const auto& tag : tags) {
|
||||
m_buffer.append(tag);
|
||||
m_buffer.push_back(',');
|
||||
}
|
||||
m_buffer.pop_back();
|
||||
}
|
||||
|
||||
// Send the message via the UDP sender
|
||||
m_sender->send(m_buffer);
|
||||
}
|
||||
|
||||
inline void StatsdClient::seed(unsigned int seed) noexcept {
|
||||
m_randomEngine.seed(seed);
|
||||
}
|
||||
|
||||
inline void StatsdClient::flush() noexcept {
|
||||
m_sender->flush();
|
||||
}
|
||||
|
||||
} // namespace Statsd
|
||||
|
||||
#endif
|
||||
@@ -1,345 +0,0 @@
|
||||
#ifndef UDP_SENDER_HPP
|
||||
#define UDP_SENDER_HPP
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
namespace Statsd {
|
||||
|
||||
#ifdef _WIN32
|
||||
using SOCKET_TYPE = SOCKET;
|
||||
constexpr SOCKET_TYPE k_invalidSocket{INVALID_SOCKET};
|
||||
#define SOCKET_ERRNO WSAGetLastError()
|
||||
#define SOCKET_CLOSE closesocket
|
||||
#else
|
||||
using SOCKET_TYPE = int;
|
||||
constexpr SOCKET_TYPE k_invalidSocket{-1};
|
||||
#define SOCKET_ERRNO errno
|
||||
#define SOCKET_CLOSE close
|
||||
#endif
|
||||
|
||||
/*!
|
||||
*
|
||||
* UDP sender
|
||||
*
|
||||
* A simple UDP sender handling batching.
|
||||
*
|
||||
*/
|
||||
class UDPSender final {
|
||||
public:
|
||||
//!@name Constructor and destructor, non-copyable
|
||||
//!@{
|
||||
|
||||
//! Constructor
|
||||
UDPSender(const std::string& host,
|
||||
const uint16_t port,
|
||||
const uint64_t batchsize,
|
||||
const uint64_t sendInterval) noexcept;
|
||||
|
||||
//! Destructor
|
||||
~UDPSender();
|
||||
|
||||
UDPSender(const UDPSender&) = delete;
|
||||
UDPSender& operator=(const UDPSender&) = delete;
|
||||
UDPSender(UDPSender&&) = delete;
|
||||
|
||||
//!@}
|
||||
|
||||
//!@name Methods
|
||||
//!@{
|
||||
|
||||
//! Send or enqueue a message
|
||||
void send(const std::string& message) noexcept;
|
||||
|
||||
//! Returns the error message as a string
|
||||
const std::string& errorMessage() const noexcept;
|
||||
|
||||
//! Returns true if the sender is initialized
|
||||
bool initialized() const noexcept;
|
||||
|
||||
//! Flushes any queued messages
|
||||
void flush() noexcept;
|
||||
|
||||
//!@}
|
||||
|
||||
private:
|
||||
// @name Private methods
|
||||
// @{
|
||||
|
||||
//! Initialize the sender and returns true when it is initialized
|
||||
bool initialize() noexcept;
|
||||
|
||||
//! Queue a message to be sent to the daemon later
|
||||
inline void queueMessage(const std::string& message) noexcept;
|
||||
|
||||
//! Send a message to the daemon
|
||||
void sendToDaemon(const std::string& message) noexcept;
|
||||
|
||||
//!@}
|
||||
|
||||
private:
|
||||
// @name State variables
|
||||
// @{
|
||||
|
||||
//! Shall we exit?
|
||||
std::atomic<bool> m_mustExit{false};
|
||||
|
||||
//!@}
|
||||
|
||||
// @name Network info
|
||||
// @{
|
||||
|
||||
//! The hostname
|
||||
std::string m_host;
|
||||
|
||||
//! The port
|
||||
uint16_t m_port;
|
||||
|
||||
//! The structure holding the server
|
||||
struct sockaddr_in m_server;
|
||||
|
||||
//! The socket to be used
|
||||
SOCKET_TYPE m_socket = k_invalidSocket;
|
||||
|
||||
//!@}
|
||||
|
||||
// @name Batching info
|
||||
// @{
|
||||
|
||||
//! The batching size
|
||||
uint64_t m_batchsize;
|
||||
|
||||
//! The sending frequency in milliseconds
|
||||
uint64_t m_sendInterval;
|
||||
|
||||
//! The queue batching the messages
|
||||
std::deque<std::string> m_batchingMessageQueue;
|
||||
|
||||
//! The mutex used for batching
|
||||
std::mutex m_batchingMutex;
|
||||
|
||||
//! The thread dedicated to the batching
|
||||
std::thread m_batchingThread;
|
||||
|
||||
//!@}
|
||||
|
||||
//! Error message (optional string)
|
||||
std::string m_errorMessage;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline bool isValidSocket(const SOCKET_TYPE socket) {
|
||||
return socket != k_invalidSocket;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
struct WinSockSingleton {
|
||||
inline static const WinSockSingleton& getInstance() {
|
||||
static const WinSockSingleton instance;
|
||||
return instance;
|
||||
}
|
||||
inline bool ok() const {
|
||||
return m_ok;
|
||||
}
|
||||
~WinSockSingleton() {
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
private:
|
||||
WinSockSingleton() {
|
||||
WSADATA wsa;
|
||||
m_ok = WSAStartup(MAKEWORD(2, 2), &wsa) == 0;
|
||||
}
|
||||
bool m_ok;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
inline UDPSender::UDPSender(const std::string& host,
|
||||
const uint16_t port,
|
||||
const uint64_t batchsize,
|
||||
const uint64_t sendInterval) noexcept
|
||||
: m_host(host), m_port(port), m_batchsize(batchsize), m_sendInterval(sendInterval) {
|
||||
// Initialize the socket
|
||||
if (!initialize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If batching is on, use a dedicated thread to send after the wait time is reached
|
||||
if (m_batchsize != 0 && m_sendInterval > 0) {
|
||||
// Define the batching thread
|
||||
m_batchingThread = std::thread([this] {
|
||||
// TODO: this will drop unsent stats, should we send all the unsent stats before we exit?
|
||||
while (!m_mustExit.load(std::memory_order_acquire)) {
|
||||
std::deque<std::string> stagedMessageQueue;
|
||||
|
||||
std::unique_lock<std::mutex> batchingLock(m_batchingMutex);
|
||||
m_batchingMessageQueue.swap(stagedMessageQueue);
|
||||
batchingLock.unlock();
|
||||
|
||||
// Flush the queue
|
||||
while (!stagedMessageQueue.empty()) {
|
||||
sendToDaemon(stagedMessageQueue.front());
|
||||
stagedMessageQueue.pop_front();
|
||||
}
|
||||
|
||||
// Wait before sending the next batch
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(m_sendInterval));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
inline UDPSender::~UDPSender() {
|
||||
if (!initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're running a background thread tell it to stop
|
||||
if (m_batchingThread.joinable()) {
|
||||
m_mustExit.store(true, std::memory_order_release);
|
||||
m_batchingThread.join();
|
||||
}
|
||||
|
||||
// Cleanup the socket
|
||||
SOCKET_CLOSE(m_socket);
|
||||
}
|
||||
|
||||
inline void UDPSender::send(const std::string& message) noexcept {
|
||||
m_errorMessage.clear();
|
||||
|
||||
// If batching is on, accumulate messages in the queue
|
||||
if (m_batchsize > 0) {
|
||||
queueMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Or send it right now
|
||||
sendToDaemon(message);
|
||||
}
|
||||
|
||||
inline void UDPSender::queueMessage(const std::string& message) noexcept {
|
||||
// We aquire a lock but only if we actually need to (i.e. there is a thread also accessing the queue)
|
||||
auto batchingLock =
|
||||
m_batchingThread.joinable() ? std::unique_lock<std::mutex>(m_batchingMutex) : std::unique_lock<std::mutex>();
|
||||
// Either we don't have a place to batch our message or we exceeded the batch size, so make a new batch
|
||||
if (m_batchingMessageQueue.empty() || m_batchingMessageQueue.back().length() > m_batchsize) {
|
||||
m_batchingMessageQueue.emplace_back();
|
||||
m_batchingMessageQueue.back().reserve(m_batchsize + 256);
|
||||
} // When there is already a batch open we need a separator when its not empty
|
||||
else if (!m_batchingMessageQueue.back().empty()) {
|
||||
m_batchingMessageQueue.back().push_back('\n');
|
||||
}
|
||||
// Add the new message to the batch
|
||||
m_batchingMessageQueue.back().append(message);
|
||||
}
|
||||
|
||||
inline const std::string& UDPSender::errorMessage() const noexcept {
|
||||
return m_errorMessage;
|
||||
}
|
||||
|
||||
inline bool UDPSender::initialize() noexcept {
|
||||
#ifdef _WIN32
|
||||
if (!detail::WinSockSingleton::getInstance().ok()) {
|
||||
m_errorMessage = "WSAStartup failed: errno=" + std::to_string(SOCKET_ERRNO);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Connect the socket
|
||||
m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (!detail::isValidSocket(m_socket)) {
|
||||
m_errorMessage = "socket creation failed: errno=" + std::to_string(SOCKET_ERRNO);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::memset(&m_server, 0, sizeof(m_server));
|
||||
m_server.sin_family = AF_INET;
|
||||
m_server.sin_port = htons(m_port);
|
||||
|
||||
if (inet_pton(AF_INET, m_host.c_str(), &m_server.sin_addr) == 0) {
|
||||
// An error code has been returned by inet_aton
|
||||
|
||||
// Specify the criteria for selecting the socket address structure
|
||||
struct addrinfo hints;
|
||||
std::memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
// Get the address info using the hints
|
||||
struct addrinfo* results = nullptr;
|
||||
const int ret{getaddrinfo(m_host.c_str(), nullptr, &hints, &results)};
|
||||
if (ret != 0) {
|
||||
// An error code has been returned by getaddrinfo
|
||||
SOCKET_CLOSE(m_socket);
|
||||
m_socket = k_invalidSocket;
|
||||
m_errorMessage = "getaddrinfo failed: err=" + std::to_string(ret) + ", msg=" + gai_strerror(ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the results in m_server
|
||||
struct sockaddr_in* host_addr = (struct sockaddr_in*)results->ai_addr;
|
||||
std::memcpy(&m_server.sin_addr, &host_addr->sin_addr, sizeof(struct in_addr));
|
||||
|
||||
// Free the memory allocated
|
||||
freeaddrinfo(results);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void UDPSender::sendToDaemon(const std::string& message) noexcept {
|
||||
// Try sending the message
|
||||
const auto ret = sendto(m_socket,
|
||||
message.data(),
|
||||
#ifdef _WIN32
|
||||
static_cast<int>(message.size()),
|
||||
#else
|
||||
message.size(),
|
||||
#endif
|
||||
0,
|
||||
(struct sockaddr*)&m_server,
|
||||
sizeof(m_server));
|
||||
if (ret == -1) {
|
||||
m_errorMessage = "sendto server failed: host=" + m_host + ":" + std::to_string(m_port) +
|
||||
", err=" + std::to_string(SOCKET_ERRNO);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool UDPSender::initialized() const noexcept {
|
||||
return m_socket != k_invalidSocket;
|
||||
}
|
||||
|
||||
inline void UDPSender::flush() noexcept {
|
||||
// We aquire a lock but only if we actually need to (ie there is a thread also accessing the queue)
|
||||
auto batchingLock =
|
||||
m_batchingThread.joinable() ? std::unique_lock<std::mutex>(m_batchingMutex) : std::unique_lock<std::mutex>();
|
||||
// Flush the queue
|
||||
while (!m_batchingMessageQueue.empty()) {
|
||||
sendToDaemon(m_batchingMessageQueue.front());
|
||||
m_batchingMessageQueue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Statsd
|
||||
|
||||
#endif
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
#ifndef STATSD_SERVER_HPP
|
||||
#define STATSD_SERVER_HPP
|
||||
|
||||
// It might make sense to include this test class in the UDPSender header
|
||||
// it includes most of the cross platform defines etc that we need for socket io
|
||||
#include "cpp-statsd-client/UDPSender.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
namespace Statsd {
|
||||
|
||||
class StatsdServer {
|
||||
public:
|
||||
StatsdServer(unsigned short port = 8125) noexcept {
|
||||
#ifdef _WIN32
|
||||
if (!detail::WinSockSingleton::getInstance().ok()) {
|
||||
m_errorMessage = "WSAStartup failed: errno=" + std::to_string(SOCKET_ERRNO);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create the socket
|
||||
m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (!detail::isValidSocket(m_socket)) {
|
||||
m_errorMessage = "socket creation failed: errno=" + std::to_string(SOCKET_ERRNO);
|
||||
return;
|
||||
}
|
||||
|
||||
// Binding should be with ipv4 to all interfaces
|
||||
struct sockaddr_in address {};
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = htons(port);
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
// Try to bind
|
||||
if (bind(m_socket, reinterpret_cast<const struct sockaddr*>(&address), sizeof(address)) != 0) {
|
||||
SOCKET_CLOSE(m_socket);
|
||||
m_socket = k_invalidSocket;
|
||||
m_errorMessage = "bind failed: errno=" + std::to_string(SOCKET_ERRNO);
|
||||
}
|
||||
}
|
||||
|
||||
~StatsdServer() {
|
||||
if (detail::isValidSocket(m_socket)) {
|
||||
SOCKET_CLOSE(m_socket);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& errorMessage() const noexcept {
|
||||
return m_errorMessage;
|
||||
}
|
||||
|
||||
std::string receive() noexcept {
|
||||
// If uninitialized then bail
|
||||
if (!detail::isValidSocket(m_socket)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Try to receive (this is blocking)
|
||||
std::string buffer(256, '\0');
|
||||
int string_len;
|
||||
if ((string_len = recv(m_socket, &buffer[0], static_cast<int>(buffer.size()), 0)) < 1) {
|
||||
m_errorMessage = "Could not recv on the socket file descriptor";
|
||||
return "";
|
||||
}
|
||||
|
||||
// No error return the trimmed result
|
||||
m_errorMessage.clear();
|
||||
buffer.resize(std::min(static_cast<size_t>(string_len), buffer.size()));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
SOCKET_TYPE m_socket;
|
||||
std::string m_errorMessage;
|
||||
};
|
||||
|
||||
} // namespace Statsd
|
||||
|
||||
#endif
|
||||
-184
@@ -1,184 +0,0 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "StatsdServer.hpp"
|
||||
#include "cpp-statsd-client/StatsdClient.hpp"
|
||||
|
||||
using namespace Statsd;
|
||||
|
||||
// Each test suite below spawns a thread to recv the client messages over UDP as if it were a real statsd server
|
||||
// Note that we could just synchronously recv metrics and not use a thread but doing the test async has the
|
||||
// advantage that we can test the threaded batching mode in a straightforward way. The server thread basically
|
||||
// just keeps storing metrics in an vector until it hears a special one signaling the test is over and bails
|
||||
void mock(StatsdServer& server, std::vector<std::string>& messages) {
|
||||
do {
|
||||
// Grab the messages that are waiting
|
||||
auto recvd = server.receive();
|
||||
|
||||
// Split the messages on '\n'
|
||||
auto start = std::string::npos;
|
||||
do {
|
||||
// Keep this message
|
||||
auto end = recvd.find('\n', ++start);
|
||||
messages.emplace_back(recvd.substr(start, end));
|
||||
start = end;
|
||||
|
||||
// Bail if we found the special quit message
|
||||
if (messages.back().find("DONE") != std::string::npos) {
|
||||
messages.pop_back();
|
||||
return;
|
||||
}
|
||||
} while (start != std::string::npos);
|
||||
} while (server.errorMessage().empty() && !messages.back().empty());
|
||||
}
|
||||
|
||||
template <typename SocketWrapper>
|
||||
void throwOnError(const SocketWrapper& wrapped, bool expectEmpty = true, const std::string& extraMessage = "") {
|
||||
if (wrapped.errorMessage().empty() != expectEmpty) {
|
||||
std::cerr << (expectEmpty ? wrapped.errorMessage() : extraMessage) << std::endl;
|
||||
throw std::runtime_error(expectEmpty ? wrapped.errorMessage() : extraMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void throwOnWrongMessage(StatsdServer& server, const std::string& expected) {
|
||||
auto actual = server.receive();
|
||||
if (actual != expected) {
|
||||
std::cerr << "Expected: " << expected << " but got: " << actual << std::endl;
|
||||
throw std::runtime_error("Incorrect stat received");
|
||||
}
|
||||
}
|
||||
|
||||
void testErrorConditions() {
|
||||
// Resolve a rubbish ip and make sure initialization failed
|
||||
StatsdClient client{"256.256.256.256", 8125, "myPrefix", 20};
|
||||
throwOnError(client, false, "Should not be able to resolve a ridiculous ip");
|
||||
}
|
||||
|
||||
void testReconfigure() {
|
||||
StatsdServer server;
|
||||
throwOnError(server);
|
||||
|
||||
StatsdClient client("localhost", 8125, "first.");
|
||||
client.increment("foo");
|
||||
throwOnWrongMessage(server, "first.foo:1|c");
|
||||
|
||||
client.setConfig("localhost", 8125, "second");
|
||||
client.increment("bar");
|
||||
throwOnWrongMessage(server, "second.bar:1|c");
|
||||
|
||||
client.setConfig("localhost", 8125, "");
|
||||
client.increment("third.baz");
|
||||
throwOnWrongMessage(server, "third.baz:1|c");
|
||||
|
||||
client.increment("");
|
||||
throwOnWrongMessage(server, ":1|c");
|
||||
|
||||
// TODO: test what happens with the batching after resolving the question about incomplete
|
||||
// batches being dropped vs sent on reconfiguring
|
||||
}
|
||||
|
||||
void testSendRecv(uint64_t batchSize, uint64_t sendInterval) {
|
||||
StatsdServer mock_server;
|
||||
std::vector<std::string> messages, expected;
|
||||
std::thread server(mock, std::ref(mock_server), std::ref(messages));
|
||||
|
||||
// Set a new config that has the client send messages to a proper address that can be resolved
|
||||
StatsdClient client("localhost", 8125, "sendRecv.", batchSize, sendInterval, 3);
|
||||
throwOnError(client);
|
||||
|
||||
// TODO: I forget if we need to wait for the server to be ready here before sending the first stats
|
||||
// is there a race condition where the client sending before the server binds would drop that clients message
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
// Increment "coco"
|
||||
client.increment("coco");
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.coco:1|c");
|
||||
|
||||
// Decrement "kiki"
|
||||
client.decrement("kiki");
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.kiki:-1|c");
|
||||
|
||||
// Adjusts "toto" by +2
|
||||
client.seed(19); // this seed gets a hit on the first call
|
||||
client.count("toto", 2, 0.1f);
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.toto:2|c|@0.10");
|
||||
|
||||
// Gets "sampled out" by the random number generator
|
||||
client.count("popo", 9, 0.1f);
|
||||
throwOnError(client);
|
||||
|
||||
// Record a gauge "titi" to 3
|
||||
client.gauge("titi", 3);
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.titi:3|g");
|
||||
|
||||
// Record a gauge "titifloat" to -123.456789 with precision 3
|
||||
client.gauge("titifloat", -123.456789);
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.titifloat:-123.457|g");
|
||||
|
||||
// Record a timing of 2ms for "myTiming"
|
||||
client.seed(19);
|
||||
client.timing("myTiming", 2, 0.1f);
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.myTiming:2|ms|@0.10");
|
||||
|
||||
// Send a set with 1227 total uniques
|
||||
client.set("tutu", 1227, 2.0f);
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.tutu:1227|s");
|
||||
|
||||
// Gauge but with tags
|
||||
client.gauge("dr.röstigrabe", 333, 1.f, {"liegt", "im", "weste"});
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.dr.röstigrabe:333|g|#liegt,im,weste");
|
||||
|
||||
// All the things
|
||||
client.count("foo", -42, .9f, {"bar", "baz"});
|
||||
throwOnError(client);
|
||||
expected.emplace_back("sendRecv.foo:-42|c|@0.90|#bar,baz");
|
||||
}
|
||||
|
||||
// Signal the mock server we are done
|
||||
client.timing("DONE", 0);
|
||||
|
||||
// If manual flushing do it now
|
||||
if (sendInterval == 0) {
|
||||
client.flush();
|
||||
}
|
||||
|
||||
// Wait for the server to stop
|
||||
server.join();
|
||||
|
||||
// Make sure we get the exactly correct output
|
||||
if (messages != expected) {
|
||||
std::cerr << "Unexpected stats received by server, got:" << std::endl;
|
||||
for (const auto& message : messages) {
|
||||
std::cerr << message << std::endl;
|
||||
}
|
||||
std::cerr << std::endl << "But we expected:" << std::endl;
|
||||
for (const auto& message : expected) {
|
||||
std::cerr << message << std::endl;
|
||||
}
|
||||
throw std::runtime_error("Unexpected stats");
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
// If any of these tests fail they throw an exception, not catching makes for a nonzero return code
|
||||
|
||||
// general things that should be errors
|
||||
testErrorConditions();
|
||||
// reconfiguring how you are sending
|
||||
testReconfigure();
|
||||
// no batching
|
||||
testSendRecv(0, 0);
|
||||
// background batching
|
||||
testSendRecv(32, 1000);
|
||||
// manual flushing of batches
|
||||
testSendRecv(16, 0);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
-4
@@ -42,8 +42,6 @@
|
||||
#ifndef HIREDIS_SDS_COMPAT
|
||||
#define HIREDIS_SDS_COMPAT
|
||||
|
||||
#ifndef USE_SYSTEM_HIREDIS
|
||||
|
||||
#define sds hisds
|
||||
|
||||
#define sdslen hi_sdslen
|
||||
@@ -93,6 +91,4 @@
|
||||
#define sdsull2str hi_sdsull2str
|
||||
#define sdsupdatelen hi_sdsupdatelen
|
||||
|
||||
#endif /* !USE_SYSTEM_HIREDIS */
|
||||
|
||||
#endif /* HIREDIS_SDS_COMPAT */
|
||||
Vendored
BIN
Binary file not shown.
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
bool FValidKey(const char *key, size_t cch);
|
||||
Vendored
BIN
Binary file not shown.
Vendored
-1
Submodule deps/rocksdb deleted from 39d56d5ad7.
+5
-70
@@ -184,9 +184,7 @@ tcp-keepalive 300
|
||||
#
|
||||
# tls-client-key-file-pass secret
|
||||
|
||||
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange,
|
||||
# required by older versions of OpenSSL (<3.0). Newer versions do not require
|
||||
# this configuration and recommend against it.
|
||||
# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange:
|
||||
#
|
||||
# tls-dh-params-file keydb.dh
|
||||
|
||||
@@ -259,24 +257,6 @@ tcp-keepalive 300
|
||||
#
|
||||
# tls-session-cache-timeout 60
|
||||
|
||||
# Allow the server to monitor the filesystem and rotate out TLS certificates if
|
||||
# they change on disk, defaults to no.
|
||||
#
|
||||
# tls-rotation no
|
||||
|
||||
# Setup a allowlist of allowed Common Names (CNs)/Subject Alternative Names (SANs)
|
||||
# that are allowed to connect to this server. This includes both normal clients as
|
||||
# well as other servers connected for replication/clustering purposes. If nothing is
|
||||
# specified, then no allowlist is used and all certificates are accepted.
|
||||
# Supports IPv4, DNS, RFC822, and URI SAN types.
|
||||
# You can put multiple names on one line as follows:
|
||||
#
|
||||
# tls-allowlist <dns1> <dns2> <dns3> ...
|
||||
#
|
||||
#
|
||||
# This configuration also allows for wildcard characters with glob style formatting
|
||||
# i.e. "*.com" would allow all clients to connect with a CN/SAN that ends with ".com"
|
||||
|
||||
################################# GENERAL #####################################
|
||||
|
||||
# By default KeyDB does not run as a daemon. Use 'yes' if you need it.
|
||||
@@ -1145,11 +1125,6 @@ acllog-max-len 128
|
||||
#
|
||||
# active-expire-effort 1
|
||||
|
||||
# Force evictions when used system memory reaches X% of total system memory.
|
||||
# This is useful as a safeguard to prevent OOM kills (0 to disable).
|
||||
#
|
||||
# force-eviction-percent 0
|
||||
|
||||
############################# LAZY FREEING ####################################
|
||||
|
||||
# KeyDB has two primitives to delete keys. One is called DEL and is a blocking
|
||||
@@ -2060,49 +2035,9 @@ server-threads 2
|
||||
# bringing up replicas can result in data loss (the first master will win).
|
||||
# active-replica yes
|
||||
|
||||
# KeyDB will attempt to balance clients across threads evenly; However, replica clients
|
||||
# are usually much more expensive than a normal client, and so KeyDB will try to assign
|
||||
# fewer clients to threads with a replica. The weighting factor below is intented to help tune
|
||||
# this behavior. A replica weighting factor of 2 means we treat a replica as the equivalent
|
||||
# of two normal clients. Adjusting this value may improve performance when replication is
|
||||
# used. The best weighting is workload specific - e.g. read heavy workloads should set
|
||||
# this to 1. Very write heavy workloads may benefit from higher numbers.
|
||||
#
|
||||
# By default KeyDB sets this to 2.
|
||||
replica-weighting-factor 2
|
||||
# Enable Pro? KeyDB pro provides support for pro only features
|
||||
# note: you may omit the license key to demo pro features for a limited time
|
||||
# enable-pro [License Key]
|
||||
|
||||
# Should KeyDB make active attempts at balancing clients across threads? This can impact
|
||||
# performance accepting new clients. By default this is enabled. If disabled there is still
|
||||
# a best effort from the kernel to distribute across threads with SO_REUSEPORT but it will not
|
||||
# be as fair.
|
||||
#
|
||||
# By default this is enabled
|
||||
#
|
||||
active-client-balancing yes
|
||||
|
||||
# Enable FLASH support (Experimental Feature)
|
||||
# Enable FLASH support? (Pro Only)
|
||||
# storage-provider flash /path/to/flash/db
|
||||
|
||||
# Blob support is a way to store very large objects (>200MB) on disk
|
||||
# The files are automatically cleaned up when KeyDB exits and are only
|
||||
# for temporary use. This helps reduce memory pressure for very large
|
||||
# data items at the cost of some performance.
|
||||
#
|
||||
# By default this config is disable. When enabled the disk associated
|
||||
# with KeyDB's working directory will be used. If there is insufficient
|
||||
# disk space or any other I/O error KeyDB will instead use memory.
|
||||
#
|
||||
# blob-support false
|
||||
|
||||
# Begin load shedding if we use more than X% CPU relative to the number of server threads
|
||||
# E.g. if overload-protect-percent is set to 80 and there are 8 server-threads, then the
|
||||
# actual CPU protection will be 8 * 100 * 0.80 = 640% CPU usage.
|
||||
#
|
||||
# Set to 0 to disable
|
||||
# overload-protect-percent 0
|
||||
|
||||
# Inform KeyDB of the availability zone if running in a cloud environment. Currently
|
||||
# this is only exposed via the info command for clients to use, but in the future we
|
||||
# we may also use this when making decisions for replication.
|
||||
#
|
||||
# availability-zone "us-east-1a"
|
||||
@@ -1,114 +0,0 @@
|
||||
FROM ubuntu:20.04
|
||||
SHELL ["/bin/bash","-c"]
|
||||
RUN groupadd -r keydb && useradd -r -g keydb keydb
|
||||
# use gosu for easy step-down from root: https://github.com/tianon/gosu/releases
|
||||
ENV GOSU_VERSION 1.14
|
||||
RUN set -eux; \
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
apt-get -o Dpkg::Options::="--force-confnew" install -y --no-install-recommends ca-certificates dirmngr gnupg wget; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
|
||||
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
|
||||
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
|
||||
export GNUPGHOME="$(mktemp -d)"; \
|
||||
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
|
||||
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
|
||||
gpgconf --kill all; \
|
||||
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
chmod +x /usr/local/bin/gosu; \
|
||||
gosu --version; \
|
||||
gosu nobody true
|
||||
# build KeyDB
|
||||
ARG MAKE_JOBS=""
|
||||
ARG ENABLE_FLASH=""
|
||||
COPY . /tmp/keydb-internal
|
||||
RUN set -eux; \
|
||||
cd /tmp/keydb-internal; \
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confnew" install -qqy --no-install-recommends \
|
||||
dpkg-dev \
|
||||
pkg-config \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
nasm \
|
||||
autotools-dev \
|
||||
autoconf \
|
||||
libjemalloc-dev \
|
||||
tcl \
|
||||
tcl-dev \
|
||||
uuid-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libbz2-dev \
|
||||
libzstd-dev \
|
||||
liblz4-dev \
|
||||
libsnappy-dev \
|
||||
libssl-dev \
|
||||
git; \
|
||||
# disable protected mode as it relates to docker
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' ./src/config.cpp; \
|
||||
sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' ./src/config.cpp; \
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' ./src/config.cpp; \
|
||||
make distclean; \
|
||||
make -j$([ -z "$MAKE_JOBS" ] && nproc || echo "$MAKE_JOBS") BUILD_TLS=yes NO_LICENSE_CHECK=yes $([ -z "$ENABLE_FLASH" ] && echo "" || echo "ENABLE_FLASH=$ENABLE_FLASH"); \
|
||||
cd src; \
|
||||
mv modules/keydb_modstatsd/modstatsd.so /usr/local/lib/; \
|
||||
strip keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel; \
|
||||
mv keydb-server keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel /usr/local/bin/; \
|
||||
# clean up unused dependencies
|
||||
echo $savedAptMark; \
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
find /usr/local -type f -executable -exec ldd '{}' ';' \
|
||||
| awk '/=>/ { print $(NF-1) }' \
|
||||
| sed 's:.*/::' \
|
||||
| sort -u \
|
||||
| xargs -r dpkg-query --search \
|
||||
| cut -d: -f1 \
|
||||
| sort -u \
|
||||
| xargs -r apt-mark manual \
|
||||
; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
# create working directories and organize files
|
||||
RUN \
|
||||
mkdir /data && chown keydb:keydb /data; \
|
||||
mkdir /flash && chown keydb:keydb /flash; \
|
||||
mkdir -p /etc/keydb; \
|
||||
cp /tmp/keydb-internal/keydb.conf /etc/keydb/; \
|
||||
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(logfile .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/protected-mode yes/protected-mode no/g' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(bind .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
echo -e "\nloadmodule /usr/local/lib/modstatsd.so" >> /etc/keydb/keydb.conf; \
|
||||
ln -s keydb-cli redis-cli; \
|
||||
cd /etc/keydb; \
|
||||
ln -s keydb.conf redis.conf; \
|
||||
rm -rf /tmp/*
|
||||
# generate entrypoint script
|
||||
RUN set -eux; \
|
||||
echo '#!/bin/sh' > /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'set -e' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# perpend 'keydb-server' if not provided as first argument" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "${1}" != "keydb-server" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' set -- keydb-server "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# allow the container to be started with `--user`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "$1" = "keydb-server" -a "$(id -u)" = "0" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo " find . \! -user keydb -exec chown keydb '{}' +" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' exec gosu keydb "$0" "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'exec "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
# set remaining image properties
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
ENV KEYDB_PRO_DIRECTORY=/usr/local/bin/
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
EXPOSE 6379
|
||||
CMD ["keydb-server","/etc/keydb/keydb.conf"]
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# make the build
|
||||
git submodule init && git submodule update
|
||||
make BUILD_TLS=yes ENABLE_FLASH=yes -j$(nproc) KEYDB_CFLAGS='-Werror' KEYDB_CXXFLAGS='-Werror'
|
||||
|
||||
# gen-cert
|
||||
./utils/gen-test-certs.sh
|
||||
@@ -1,283 +0,0 @@
|
||||
import random
|
||||
import time
|
||||
import socket
|
||||
import asyncore
|
||||
import threading
|
||||
import argparse
|
||||
from pprint import pprint
|
||||
|
||||
# Globals
|
||||
ops = {}
|
||||
numclients = 0
|
||||
numkeys = 0
|
||||
runtime = 0
|
||||
clients = []
|
||||
|
||||
def _buildResp(*args):
|
||||
result = "*" + str(len(args)) + "\r\n"
|
||||
for v in args:
|
||||
result = result + "$" + str(len(v)) + "\r\n"
|
||||
result = result + v + "\r\n"
|
||||
return result.encode('utf-8')
|
||||
|
||||
class Client(asyncore.dispatcher):
|
||||
def __init__(self, host, port):
|
||||
asyncore.dispatcher.__init__(self)
|
||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.connect((host, port))
|
||||
self.buf = b''
|
||||
self.inbuf = b''
|
||||
self.callbacks = list()
|
||||
self.client_id = 0
|
||||
self.get_client_id()
|
||||
|
||||
def handle_connect(self):
|
||||
pass
|
||||
|
||||
def handle_read(self):
|
||||
self.inbuf += self.recv(8192)
|
||||
self.parse_response()
|
||||
|
||||
def handle_write(self):
|
||||
sent = self.send(self.buf)
|
||||
self.buf = self.buf[sent:]
|
||||
|
||||
def handle_close(self):
|
||||
self.close()
|
||||
|
||||
def writable(self):
|
||||
return len(self.buf) > 0
|
||||
|
||||
def parse_array(self, startpos):
|
||||
assert(self.inbuf[startpos] == ord('*'))
|
||||
endrange = self.inbuf[startpos+1:].find(ord('\r')) + 1 + startpos
|
||||
assert(endrange > 0)
|
||||
numargs = int(self.inbuf[startpos+1:endrange])
|
||||
if numargs == -1: # Nil array, used in some returns
|
||||
startpos = endrange + 2
|
||||
return startpos, []
|
||||
assert(numargs > 0)
|
||||
args = list()
|
||||
startpos = endrange + 2 # plus 1 gets us to the '\n' and the next gets us to the start char
|
||||
|
||||
while len(args) < numargs:
|
||||
# We're parsing entries of the form "$N\r\nnnnnnn\r\n"
|
||||
if startpos >= len(self.inbuf):
|
||||
return # Not the full response
|
||||
if self.inbuf[startpos] == ord('*'):
|
||||
startpos, arr = self.parse_array(startpos)
|
||||
args.append(arr)
|
||||
else:
|
||||
assert(self.inbuf[startpos] == ord('$'))
|
||||
startpos = startpos + 1
|
||||
endrange = self.inbuf[startpos:].find(b'\r')
|
||||
if endrange < 0:
|
||||
return
|
||||
endrange += startpos
|
||||
assert(endrange <= len(self.inbuf))
|
||||
length = int(self.inbuf[startpos:endrange])
|
||||
if length < 0:
|
||||
return
|
||||
startpos = endrange + 2
|
||||
assert((startpos + length) <= len(self.inbuf))
|
||||
assert(self.inbuf[startpos+length] == ord('\r'))
|
||||
assert(self.inbuf[startpos+length+1] == ord('\n'))
|
||||
args.append(self.inbuf[startpos:(startpos+length)])
|
||||
startpos += length + 2
|
||||
assert(len(args) == numargs)
|
||||
return startpos, args
|
||||
|
||||
def parse_response(self):
|
||||
if len(self.inbuf) == 0:
|
||||
return
|
||||
|
||||
while len(self.inbuf) > 0:
|
||||
if self.inbuf[0] == ord('+') or self.inbuf[0] == ord('-') or self.inbuf[0] == ord(':'):
|
||||
# This is a single line response
|
||||
endpos = self.inbuf.find(b'\n')
|
||||
if endpos < 0:
|
||||
return # incomplete response
|
||||
self.callbacks[0](self, self.inbuf[0:endpos-1])
|
||||
self.callbacks.pop(0)
|
||||
self.inbuf = self.inbuf[endpos+1:]
|
||||
|
||||
elif self.inbuf[0] == ord('*'):
|
||||
#RESP response
|
||||
try:
|
||||
startpos, args = self.parse_array(0)
|
||||
except:
|
||||
return # Not all data here yet
|
||||
self.callbacks[0](self, args)
|
||||
self.callbacks.pop(0)
|
||||
self.inbuf = self.inbuf[startpos:]
|
||||
else:
|
||||
print("ERROR: Unknown response:")
|
||||
pprint(self.inbuf)
|
||||
assert(False)
|
||||
|
||||
|
||||
def default_result_handler(self, result):
|
||||
pprint(result)
|
||||
|
||||
# Public Methods
|
||||
def set(self, key, val, callback = default_result_handler):
|
||||
self.buf += _buildResp("set", key, val)
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def lpush(self, key, val, callback = default_result_handler):
|
||||
self.buf += _buildResp("lpush", key, val)
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def blpop(self, *keys, timeout=0, callback=default_result_handler):
|
||||
self.buf += _buildResp("blpop", *keys, str(timeout))
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def delete(self, key, callback = default_result_handler):
|
||||
self.buf += _buildResp("del", key)
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def unblock(self, client_id, callback=default_result_handler):
|
||||
self.buf += _buildResp("client", "unblock", str(client_id))
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def scan(self, iter, match=None, count=None, callback = default_result_handler):
|
||||
args = ["scan", str(iter)]
|
||||
if match != None:
|
||||
args.append("MATCH")
|
||||
args.append(match)
|
||||
if count != None:
|
||||
args.append("COUNT")
|
||||
args.append(str(count))
|
||||
self.buf += _buildResp(*args)
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def get_client_id(self):
|
||||
self.buf += _buildResp("client", "id")
|
||||
self.callbacks.append(self.store_client_id)
|
||||
|
||||
def store_client_id(self, c, resp):
|
||||
assert(resp[0] == ord(':'))
|
||||
self.client_id = int(resp[1:])
|
||||
assert(self.client_id == c.client_id)
|
||||
|
||||
def get(self, key, callback = None):
|
||||
return
|
||||
|
||||
def getrandomkey():
|
||||
return str(random.randrange(0, numkeys))
|
||||
|
||||
def handle_lpush_response(c, resp=None):
|
||||
global ops
|
||||
if resp != None:
|
||||
ops['lpush'] += 1
|
||||
assert(resp[0] == ord(':'))
|
||||
c.lpush("list_" + getrandomkey(), 'bardsklfjkldsjfdlsjflksdfjklsdjflksd kldsjflksd jlkdsjf lksdjklds jrfklsdjfklsdjfkl', handle_lpush_response)
|
||||
|
||||
def handle_blpop_response(c, resp=None):
|
||||
global ops
|
||||
if resp != None:
|
||||
ops['blpop'] += 1
|
||||
c.blpop("list_" + getrandomkey(), callback=handle_blpop_response)
|
||||
|
||||
def handle_set_response(c, resp=None):
|
||||
global ops
|
||||
if resp != None:
|
||||
ops['set'] += 1
|
||||
assert(resp[0] == ord('+'))
|
||||
c.set("str_" + getrandomkey(), 'bardsklfjkldsjfdlsjflksdfjklsdjflksd kldsjflksd jlkdsjf lksdjklds jrfklsdjfklsdjfkl', handle_set_response)
|
||||
|
||||
def handle_del_response(c, resp=None):
|
||||
global ops
|
||||
if resp != None:
|
||||
ops['del'] += 1
|
||||
c.delete("list_" + getrandomkey(), handle_del_response)
|
||||
|
||||
def scan_callback(c, resp=None):
|
||||
global ops
|
||||
nextstart = int(resp[0])
|
||||
c.scan(nextstart, count=500, callback=scan_callback)
|
||||
ops['scan'] += 1
|
||||
|
||||
def clear_ops():
|
||||
global ops
|
||||
ops = {'lpush': 0, 'blpop': 0, 'del': 0, 'scan': 0, 'set': 0, 'get': 0}
|
||||
|
||||
def stats_thread():
|
||||
global ops
|
||||
global runtime
|
||||
i = 0
|
||||
while i < runtime or not runtime:
|
||||
time.sleep(1)
|
||||
print("Ops per second: " + str({k:v for (k,v) in ops.items() if v}))
|
||||
clear_ops()
|
||||
i += 1
|
||||
asyncore.close_all()
|
||||
|
||||
def flush_db_sync():
|
||||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server.connect(('127.0.0.1', 6379))
|
||||
server.send(_buildResp("flushdb"))
|
||||
resp = server.recv(8192)
|
||||
assert(resp[:3] == "+OK".encode('utf-8'))
|
||||
|
||||
def init_blocking():
|
||||
global clients
|
||||
if numkeys > 100 * numclients:
|
||||
print("WARNING: High ratio of keys to clients. Most lpushes will not be popped and unblocking will take a long time!")
|
||||
for i in range(numclients):
|
||||
clients.append(Client('127.0.0.1', 6379))
|
||||
if i % 2:
|
||||
handle_blpop_response(clients[-1])
|
||||
else:
|
||||
handle_lpush_response(clients[-1])
|
||||
|
||||
def init_lpush():
|
||||
global clients
|
||||
for i in range(numclients):
|
||||
clients.append(Client('127.0.0.1', 6379))
|
||||
for i in range (10):
|
||||
handle_lpush_response(clients[-1])
|
||||
#handle_set_response(clients[-1], None)
|
||||
|
||||
scan_client = Client('127.0.0.1', 6379)
|
||||
scan_client.scan(0, count=500, callback=scan_callback)
|
||||
|
||||
del_client = Client('127.0.0.1', 6379)
|
||||
handle_del_response(del_client)
|
||||
|
||||
def main(test, flush):
|
||||
clear_ops()
|
||||
|
||||
if flush:
|
||||
flush_db_sync()
|
||||
|
||||
try:
|
||||
globals()[f"init_{test}"]()
|
||||
except KeyError:
|
||||
print(f"Test \"{test}\" not found. Exiting...")
|
||||
exit()
|
||||
except ConnectionRefusedError:
|
||||
print("Could not connect to server. Is it running?")
|
||||
print("Exiting...")
|
||||
exit()
|
||||
|
||||
threading.Thread(target=stats_thread).start()
|
||||
asyncore.loop()
|
||||
print("Done.")
|
||||
|
||||
parser = argparse.ArgumentParser(description="Test use cases for KeyDB.")
|
||||
parser.add_argument('test', choices=[x[5:] for x in filter(lambda name: name.startswith("init_"), globals().keys())], help="which test to run")
|
||||
parser.add_argument('-c', '--clients', type=int, default=50, help="number of running clients to use")
|
||||
parser.add_argument('-k', '--keys', type=int, default=100000, help="number of keys to choose from for random tests")
|
||||
parser.add_argument('-t', '--runtime', type=int, default=0, help="how long to run the test for (default: 0 for infinite)")
|
||||
parser.add_argument('-f', '--flush', action="store_true", help="flush the db before running the test")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
args = parser.parse_args()
|
||||
except:
|
||||
exit()
|
||||
numclients = args.clients
|
||||
numkeys = args.keys
|
||||
runtime = args.runtime
|
||||
main(args.test, args.flush)
|
||||
+2
-20
@@ -1,21 +1,3 @@
|
||||
### KeyDB DEB Package Source Builds
|
||||
### KeyDB Packaging
|
||||
|
||||
This directory contains scripts and components needed to generate debian packages on different distributions/architectures from source
|
||||
|
||||
The 'debian' directory contains debian source code for bionic, buster and later distributions as it uses functions only available with debhelper11+. 'debian_dh9' is used for xenial, stretch and earlier distributions using debhelper9.
|
||||
|
||||
You will need to install pbuilder `sudo apt install pbuilder` along with other distribution specific dependencies
|
||||
|
||||
Generate deb packages with the following script command run from this directory:
|
||||
|
||||
```
|
||||
$ ./deb-buildsource.sh
|
||||
```
|
||||
|
||||
This generates a directory structure, .dsc file, original.tar.gz, and new changelog for the distribution and architecture installed.
|
||||
|
||||
When complete the produced debian packages will be located in deb_files_generated directory.
|
||||
|
||||
Arguments for the deb-buildsource.sh script that can be passed are either 'None' or '"your custom comments"'. 'None' assumes a permanent changelog entry has been made listing the correct build for the version. If you quote and enter your own comment string, it will be appended to the changelog for that deb package. If no argument is passed a default comment is generated saying that this deb package was generated by this script.
|
||||
|
||||
This script has been tested on xenial/bionic/focal/stretch/buster/bullseye
|
||||
The following directories contain packaging code used to create rpm and deb packages as well as docker images.
|
||||
+8
-4
@@ -1,17 +1,21 @@
|
||||
### KeyDB DEB Package Source Builds
|
||||
|
||||
This directory contains scripts and components needed to generate debian packages on different distributions/architectures using source
|
||||
This directory contains scripts and components needed to generate debian packages on different distributions/architectures from source
|
||||
|
||||
You will need to install pbuilder `sudo apt install pbuilder`
|
||||
The 'debian' directory contains debian source code for bionic, buster and later distributions as it uses functions only available with debhelper11+. 'debian_dh9' is used for xenial, stretch and earlier distributions using debhelper9.
|
||||
|
||||
You will need to install pbuilder `sudo apt install pbuilder` along with other distribution specific dependancies
|
||||
|
||||
Generate deb packages with the following script command run from this directory:
|
||||
|
||||
```
|
||||
$ ./deb-buildsource.sh
|
||||
```
|
||||
This generates a directory structure, .dsc file, original.tar.gz, .changes files and new changelog for the distribution and architecture installed.
|
||||
|
||||
This generates a directory structure, .dsc file, original.tar.gz, and new changelog for the distribution and architecture installed.
|
||||
|
||||
When complete the produced debian packages will be located in deb_files_generated directory.
|
||||
|
||||
Arguments for the deb-buildsource.sh script that can be passed are either 'None' or '"your custom comments"'. 'None' assumes a permanent changelog entry has been made listing the correct build for the version. If you quote and enter your own comment string, it will be appended to the changelog for that deb package. If no argument is passed a default comment is generated saying that this deb package was generated by this script.
|
||||
|
||||
This script has been tested on xenial/bionic/focal/stretch/buster/bullseye
|
||||
This script has been tested on xenial/bionic/stretch/buster
|
||||
+4
-40
@@ -257,24 +257,6 @@ tcp-keepalive 300
|
||||
#
|
||||
# tls-session-cache-timeout 60
|
||||
|
||||
# Allow the server to monitor the filesystem and rotate out TLS certificates if
|
||||
# they change on disk, defaults to no.
|
||||
#
|
||||
# tls-rotation no
|
||||
|
||||
# Setup a allowlist of allowed Common Names (CNs)/Subject Alternative Names (SANs)
|
||||
# that are allowed to connect to this server. This includes both normal clients as
|
||||
# well as other servers connected for replication/clustering purposes. If nothing is
|
||||
# specified, then no allowlist is used and all certificates are accepted.
|
||||
# Supports IPv4, DNS, RFC822, and URI SAN types.
|
||||
# You can put multiple names on one line as follows:
|
||||
#
|
||||
# tls-allowlist <dns1> <dns2> <dns3> ...
|
||||
#
|
||||
#
|
||||
# This configuration also allows for wildcard characters with glob style formatting
|
||||
# i.e. "*.com" would allow all clients to connect with a CN/SAN that ends with ".com"
|
||||
|
||||
################################# GENERAL #####################################
|
||||
|
||||
# By default KeyDB does not run as a daemon. Use 'yes' if you need it.
|
||||
@@ -2053,27 +2035,9 @@ server-threads 2
|
||||
# bringing up replicas can result in data loss (the first master will win).
|
||||
# active-replica yes
|
||||
|
||||
# KeyDB will attempt to balance clients across threads evenly; However, replica clients
|
||||
# are usually much more expensive than a normal client, and so KeyDB will try to assign
|
||||
# fewer clients to threads with a replica. The weighting factor below is intented to help tune
|
||||
# this behavior. A replica weighting factor of 2 means we treat a replica as the equivalent
|
||||
# of two normal clients. Adjusting this value may improve performance when replication is
|
||||
# used. The best weighting is workload specific - e.g. read heavy workloads should set
|
||||
# this to 1. Very write heavy workloads may benefit from higher numbers.
|
||||
#
|
||||
# By default KeyDB sets this to 2.
|
||||
replica-weighting-factor 2
|
||||
# Enable Pro? KeyDB pro provides support for pro only features
|
||||
# note: you may omit the license key to demo pro features for a limited time
|
||||
# enable-pro [License Key]
|
||||
|
||||
# Enable FLASH support (Experimental Feature)
|
||||
# Enable FLASH support? (Pro Only)
|
||||
# storage-provider flash /path/to/flash/db
|
||||
|
||||
# Blob support is a way to store very large objects (>200MB) on disk
|
||||
# The files are automatically cleaned up when KeyDB exits and are only
|
||||
# for temporary use. This helps reduce memory pressure for very large
|
||||
# data items at the cost of some performance.
|
||||
#
|
||||
# By default this config is disable. When enabled the disk associated
|
||||
# with KeyDB's working directory will be used. If there is insufficient
|
||||
# disk space or any other I/O error KeyDB will instead use memory.
|
||||
#
|
||||
# blob-support false
|
||||
@@ -19,11 +19,6 @@ elif [ "$distributor" == "Ubuntu" ]; then
|
||||
fi
|
||||
codename=$(lsb_release --codename --short)
|
||||
date=$(date +%a," "%d" "%b" "%Y" "%T)
|
||||
|
||||
# overwrite debian bookworm version until updated
|
||||
if [ $codename == "bookworm" ]; then
|
||||
distname=+deb12u1
|
||||
fi
|
||||
pkg_name=keydb-$majorv:$version$distname
|
||||
|
||||
# create build tree
|
||||
@@ -58,9 +53,8 @@ if [ "$codename" == "xenial" ]; then
|
||||
else
|
||||
sudo pbuilder create --distribution $codename
|
||||
fi
|
||||
|
||||
sudo pbuilder --update
|
||||
sudo pbuilder --build *.dsc --logfile /mnt/pbuilderlog.log
|
||||
sudo pbuilder --build *.dsc
|
||||
|
||||
# move new packages to deb_files_generated and clean up
|
||||
cp /var/cache/pbuilder/result/*$version*.deb ../deb_files_generated
|
||||
|
||||
@@ -8,7 +8,6 @@ Build-Depends:
|
||||
systemd,
|
||||
libsystemd-dev <!nocheck>,
|
||||
procps <!nocheck>,
|
||||
pkg-config <!nocheck>,
|
||||
build-essential <!nocheck>,
|
||||
tcl <!nocheck>,
|
||||
tcl-dev <!nocheck>,
|
||||
@@ -18,16 +17,11 @@ Build-Depends:
|
||||
autotools-dev <!nocheck>,
|
||||
autoconf <!nocheck>,
|
||||
libjemalloc-dev <!nocheck>,
|
||||
libssl-dev <!nocheck>,
|
||||
libsnappy-dev <!nocheck>,
|
||||
zlib1g-dev <!nocheck>,
|
||||
libbz2-dev <!nocheck>,
|
||||
liblz4-dev <!nocheck>,
|
||||
libzstd-dev <!nocheck>
|
||||
libssl-dev <!nocheck>
|
||||
Standards-Version: 4.2.1
|
||||
Homepage: https://docs.keydb.dev/
|
||||
Vcs-Git: https://github.com/Snapchat/KeyDB.git
|
||||
Vcs-Browser: https://github.com/Snapchat/KeyDB
|
||||
Vcs-Git: https://github.com/JohnSully/KeyDB.git
|
||||
Vcs-Browser: https://github.com/JohnSully/KeyDB
|
||||
|
||||
Package: keydb
|
||||
Architecture: all
|
||||
|
||||
@@ -6,9 +6,8 @@ Source: https://github.com/EQ-Alpha/KeyDB
|
||||
Files: *
|
||||
Copyright:
|
||||
© 2006-2014 Salvatore Sanfilippo <antirez@gmail.com>
|
||||
© 2019 John Sully
|
||||
© 2019-2021 John Sully
|
||||
© 2019-2021 EQ Alpha Technology Ltd.
|
||||
© 2021 Snap Inc.
|
||||
|
||||
License: BSD-3-clause
|
||||
|
||||
@@ -90,7 +89,6 @@ Files: pkg/deb/debian/*
|
||||
Copyright:
|
||||
© 2009 Chris Lamb <lamby@debian.org>
|
||||
© 2020-2021 EQ Alpha Technology Ltd. <support@eqalpha.com>
|
||||
© 2021 Snap Inc.
|
||||
License: BSD-3-clause
|
||||
|
||||
License: BSD-2-clause
|
||||
|
||||
@@ -4,7 +4,6 @@ include /usr/share/dpkg/buildflags.mk
|
||||
|
||||
export BUILD_TLS=yes
|
||||
export USE_SYSTEMD=yes
|
||||
export ENABLE_FLASH=yes
|
||||
export CFLAGS CPPFLAGS LDFLAGS
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
export DEB_LDFLAGS_MAINT_APPEND = -ldl -latomic $(LUA_LDFLAGS)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version=6
|
||||
opts=uversionmangle=s/-?(alpha|beta|rc)/~$1/ \
|
||||
https://github.com/EQ-Alpha/KeyDB/releases .*/archive/(.*).tar.gz
|
||||
https://github.com/JohnSully/KeyDB-Pro/releases .*/archive/(.*).tar.gz
|
||||
@@ -17,16 +17,11 @@ Build-Depends:
|
||||
autotools-dev <!nocheck>,
|
||||
autoconf <!nocheck>,
|
||||
libjemalloc-dev <!nocheck>,
|
||||
libssl-dev <!nocheck>,
|
||||
libsnappy-dev <!nocheck>,
|
||||
zlib1g-dev <!nocheck>,
|
||||
libbz2-dev <!nocheck>,
|
||||
liblz4-dev <!nocheck>,
|
||||
libzstd-dev <!nocheck>
|
||||
libssl-dev <!nocheck>
|
||||
Standards-Version: 4.2.1
|
||||
Homepage: https://docs.keydb.dev/
|
||||
Vcs-Git: https://github.com/Snapchat/KeyDB.git
|
||||
Vcs-Browser: https://github.com/Snapchat/KeyDB
|
||||
Vcs-Git: https://github.com/JohnSully/KeyDB.git
|
||||
Vcs-Browser: https://github.com/JohnSully/KeyDB
|
||||
|
||||
Package: keydb
|
||||
Architecture: all
|
||||
|
||||
@@ -6,7 +6,7 @@ Source: https://github.com/EQ-Alpha/KeyDB
|
||||
Files: *
|
||||
Copyright:
|
||||
© 2006-2014 Salvatore Sanfilippo <antirez@gmail.com>
|
||||
© 2019 John Sully
|
||||
© 2019-2021 John Sully
|
||||
© 2019-2021 EQ Alpha Technology Ltd.
|
||||
|
||||
License: BSD-3-clause
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
From: Chris Lamb <lamby@debian.org>
|
||||
Date: Wed, 3 Oct 2018 19:24:16 +0100
|
||||
Subject: Add support for a USE_SYSTEM_HIREDIS flag.
|
||||
|
||||
---
|
||||
deps/Makefile | 2 ++
|
||||
src/Makefile | 19 ++++++++++++++-----
|
||||
2 files changed, 16 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/deps/Makefile b/deps/Makefile
|
||||
index 2ed7736..d6b8e3e 100644
|
||||
--- a/deps/Makefile
|
||||
+++ b/deps/Makefile
|
||||
@@ -33,7 +33,9 @@ ifneq ($(shell sh -c '[ -f .make-ldflags ] && cat .make-ldflags || echo none'),
|
||||
endif
|
||||
|
||||
distclean:
|
||||
+ifneq ($(USE_SYSTEM_HIREDIS),yes)
|
||||
-(cd hiredis && $(MAKE) clean) > /dev/null || true
|
||||
+endif
|
||||
-(cd linenoise && $(MAKE) clean) > /dev/null || true
|
||||
ifneq ($(USE_SYSTEM_LUA),yes)
|
||||
-(cd lua && $(MAKE) clean) > /dev/null || true
|
||||
diff --git a/src/Makefile b/src/Makefile
|
||||
index 49085f2..9e7e6f1 100644
|
||||
--- a/src/Makefile
|
||||
+++ b/src/Makefile
|
||||
@@ -16,7 +16,7 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh')
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
|
||||
OPTIMIZATION?=-O2
|
||||
-DEPENDENCY_TARGETS=hiredis linenoise
|
||||
+DEPENDENCY_TARGETS=linenoise
|
||||
NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
@@ -107,7 +107,7 @@ endif
|
||||
endif
|
||||
endif
|
||||
# Include paths to dependencies
|
||||
-FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise
|
||||
+FINAL_CFLAGS+= -I../deps/linenoise
|
||||
|
||||
ifeq ($(MALLOC),tcmalloc)
|
||||
FINAL_CFLAGS+= -DUSE_TCMALLOC
|
||||
@@ -139,6 +139,15 @@ else
|
||||
FINAL_LIBS := ../deps/lua/src/liblua.a $(FINAL_LIBS)
|
||||
endif
|
||||
|
||||
+ifeq ($(USE_SYSTEM_HIREDIS),yes)
|
||||
+ FINAL_LIBS+= -lhiredis
|
||||
+ FINAL_CFLAGS+= -I/usr/include/hiredis
|
||||
+else
|
||||
+ DEPENDENCY_TARGETS += hiredis
|
||||
+ FINAL_LIBS+= ../deps/hiredis/libhiredis.a
|
||||
+ FINAL_CFLAGS+= -I../deps/hiredis
|
||||
+endif
|
||||
+
|
||||
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS) $(CPPFLAGS)
|
||||
REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
|
||||
REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)
|
||||
@@ -210,7 +219,7 @@ endif
|
||||
|
||||
# redis-server
|
||||
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)
|
||||
- $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
|
||||
+ $(REDIS_LD) -o $@ $^ $(FINAL_LIBS)
|
||||
|
||||
# redis-sentinel
|
||||
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)
|
||||
@@ -226,11 +235,11 @@ $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME)
|
||||
|
||||
# redis-cli
|
||||
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
|
||||
- $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)
|
||||
+ $(REDIS_LD) -o $@ $^ ../deps/linenoise/linenoise.o $(FINAL_LIBS)
|
||||
|
||||
# redis-benchmark
|
||||
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
|
||||
- $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
|
||||
+ $(REDIS_LD) -o $@ $^ $(FINAL_LIBS)
|
||||
|
||||
dict-benchmark: dict.c zmalloc.c sds.c siphash.c
|
||||
$(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS)
|
||||
@@ -0,0 +1,107 @@
|
||||
From: Chris Lamb <lamby@debian.org>
|
||||
Date: Sat, 25 Aug 2018 17:52:13 +0200
|
||||
Subject: Add support for USE_SYSTEM_JEMALLOC flag.
|
||||
|
||||
https://github.com/antirez/redis/pull/5279
|
||||
---
|
||||
deps/Makefile | 2 ++
|
||||
src/Makefile | 5 +++++
|
||||
src/object.c | 5 +++++
|
||||
src/zmalloc.c | 10 ++++++++++
|
||||
src/zmalloc.h | 4 ++++
|
||||
5 files changed, 26 insertions(+)
|
||||
|
||||
diff --git a/deps/Makefile b/deps/Makefile
|
||||
index eb35c1e..1342fac 100644
|
||||
--- a/deps/Makefile
|
||||
+++ b/deps/Makefile
|
||||
@@ -36,7 +36,9 @@ distclean:
|
||||
-(cd hiredis && $(MAKE) clean) > /dev/null || true
|
||||
-(cd linenoise && $(MAKE) clean) > /dev/null || true
|
||||
-(cd lua && $(MAKE) clean) > /dev/null || true
|
||||
+ifneq ($(USE_SYSTEM_JEMALLOC),yes)
|
||||
-(cd jemalloc && [ -f Makefile ] && $(MAKE) distclean) > /dev/null || true
|
||||
+endif
|
||||
-(rm -f .make-*)
|
||||
|
||||
.PHONY: distclean
|
||||
diff --git a/src/Makefile b/src/Makefile
|
||||
index 0ff6e8b..51363fe 100644
|
||||
--- a/src/Makefile
|
||||
+++ b/src/Makefile
|
||||
@@ -120,10 +120,15 @@ ifeq ($(MALLOC),tcmalloc_minimal)
|
||||
endif
|
||||
|
||||
ifeq ($(MALLOC),jemalloc)
|
||||
+ifeq ($(USE_SYSTEM_JEMALLOC),yes)
|
||||
+ FINAL_CFLAGS+= -DUSE_JEMALLOC -I/usr/include/jemalloc/include
|
||||
+ FINAL_LIBS := -ljemalloc $(FINAL_LIBS)
|
||||
+else
|
||||
DEPENDENCY_TARGETS+= jemalloc
|
||||
FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include
|
||||
FINAL_LIBS := ../deps/jemalloc/lib/libjemalloc.a $(FINAL_LIBS)
|
||||
endif
|
||||
+endif
|
||||
|
||||
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS) $(CPPFLAGS)
|
||||
REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
|
||||
diff --git a/src/object.c b/src/object.c
|
||||
index 6987e1e..e49c2c2 100644
|
||||
--- a/src/object.c
|
||||
+++ b/src/object.c
|
||||
@@ -36,6 +36,11 @@
|
||||
#define strtold(a,b) ((long double)strtod((a),(b)))
|
||||
#endif
|
||||
|
||||
+#if defined(USE_JEMALLOC) && (USE_SYSTEM_JEMALLOC == yes)
|
||||
+#define je_mallctl mallctl
|
||||
+#define je_malloc_stats_print malloc_stats_print
|
||||
+#endif
|
||||
+
|
||||
/* ===================== Creation and parsing of objects ==================== */
|
||||
|
||||
robj *createObject(int type, void *ptr) {
|
||||
diff --git a/src/zmalloc.c b/src/zmalloc.c
|
||||
index 2482f51..80e6571 100644
|
||||
--- a/src/zmalloc.c
|
||||
+++ b/src/zmalloc.c
|
||||
@@ -63,6 +63,15 @@ void zlibc_free(void *ptr) {
|
||||
#define realloc(ptr,size) tc_realloc(ptr,size)
|
||||
#define free(ptr) tc_free(ptr)
|
||||
#elif defined(USE_JEMALLOC)
|
||||
+#if USE_SYSTEM_JEMALLOC == yes
|
||||
+#define malloc(size) malloc(size)
|
||||
+#define calloc(count,size) calloc(count,size)
|
||||
+#define realloc(ptr,size) realloc(ptr,size)
|
||||
+#define free(ptr) free(ptr)
|
||||
+#define mallocx(size,flags) mallocx(size,flags)
|
||||
+#define dallocx(ptr,flags) dallocx(ptr,flags)
|
||||
+#define je_mallctl mallctl
|
||||
+#else
|
||||
#define malloc(size) je_malloc(size)
|
||||
#define calloc(count,size) je_calloc(count,size)
|
||||
#define realloc(ptr,size) je_realloc(ptr,size)
|
||||
@@ -70,6 +79,7 @@ void zlibc_free(void *ptr) {
|
||||
#define mallocx(size,flags) je_mallocx(size,flags)
|
||||
#define dallocx(ptr,flags) je_dallocx(ptr,flags)
|
||||
#endif
|
||||
+#endif
|
||||
|
||||
#define update_zmalloc_stat_alloc(__n) do { \
|
||||
size_t _n = (__n); \
|
||||
diff --git a/src/zmalloc.h b/src/zmalloc.h
|
||||
index 6fb19b0..62ccf29 100644
|
||||
--- a/src/zmalloc.h
|
||||
+++ b/src/zmalloc.h
|
||||
@@ -50,7 +50,11 @@
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
+#if USE_SYSTEM_JEMALLOC == yes
|
||||
+#define zmalloc_size(p) malloc_usable_size(p)
|
||||
+#else
|
||||
#define zmalloc_size(p) je_malloc_usable_size(p)
|
||||
+#endif
|
||||
#else
|
||||
#error "Newer version of jemalloc required"
|
||||
#endif
|
||||
@@ -2,5 +2,7 @@
|
||||
#debian-packaging/0003-dpkg-buildflags.patch
|
||||
#debian-packaging/0007-Set-Debian-configuration-defaults.patch
|
||||
#0010-Use-get_current_dir_name-over-PATHMAX-etc.patch
|
||||
#0010-Add-support-for-USE_SYSTEM_JEMALLOC-flag.patch
|
||||
#0011-Add-support-for-a-USE_SYSTEM_LUA-flag.patch
|
||||
#0007-Add-support-for-a-USE_SYSTEM_HIREDIS-flag.patch
|
||||
#test
|
||||
@@ -10,7 +10,6 @@ include /usr/share/dpkg/buildflags.mk
|
||||
|
||||
export BUILD_TLS=yes
|
||||
export USE_SYSTEMD=yes
|
||||
export ENABLE_FLASH=yes
|
||||
export CFLAGS CPPFLAGS LDFLAGS
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
export DEB_LDFLAGS_MAINT_APPEND = -ldl -latomic $(LUA_LDFLAGS)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version=4
|
||||
opts=uversionmangle=s/-?(alpha|beta|rc)/~$1/ \
|
||||
https://github.com/EQ-Alpha/KeyDB/releases .*/archive/(.*).tar.gz
|
||||
https://github.com/JohnSully/KeyDB/releases .*/archive/(.*).tar.gz
|
||||
+1
-175
@@ -1,177 +1,3 @@
|
||||
keydb (6:6.3.4-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* Add keydb_modstatsd, a module for providing keydb stats to a local statsd server. Can be found under src/modules/keydb_modstatsd
|
||||
* Fixed FreeBSD compile(Thanks to @bra-fsn)
|
||||
* Fixed a bug in s3 bucket config that blocked startup(Thanks to Alexandre Chichmanian)
|
||||
* Fixed a bug causing crash if keys command is called after a blocking command (Fixes issues #659, #605, #532)
|
||||
* Added proper error checking of replica configs, now KeyDB will throw an error if replica-of config is passed before active-replica or multimaster configs
|
||||
* Fixed double free bug in lazy free
|
||||
* Added "overload-protect-percent" config, when enabled this will load shed clients whenever CPU usage exceeds configured value
|
||||
* Added "availability-zone" config, this can be passed any string which will then be reported by info command
|
||||
* Updated eviction logic to account for total system memory availability using sysinfo
|
||||
* Fixed a bug where repl-backlog-size config was modified in keydb.conf with the runtime value during config rewrite
|
||||
* Fixed a possible deadlock when running CLIENT KILL with large number of clients
|
||||
* Fixed a bug where KeyDB would overcommit memory during fork BG save
|
||||
* Fixed a bug with disk repl_backlog causing double free
|
||||
* Fixed integer overflow issue in the temp rdb file naming (Thanks to @karthyuom)
|
||||
* Fixed compile issue with GCC 13.1.1 (Thanks to @michieldwitte)
|
||||
* Removed expireset and restored redis expire behaviour
|
||||
* Fixed a bug causing forked processes to hang, specifically affecting RDB and AOF(Fixes issues #675, #619)
|
||||
* Added "CLUSTER REPLICATE NO ONE" to turn a replica into an empty primary
|
||||
* Added RDB-less full sync, can be enabled with config 'enable-keydb-fastsync'
|
||||
* Added "flash-disable-key-cache" config to disable key cache which stores every key(but not value) in memory
|
||||
* Moved cluster slot to key map to being stored by storage provider, rather than in memory
|
||||
* Moved expires to be stored by storage provider, rather than in memory
|
||||
* Enabled expiry/eviction from storage provider, previously only data stored in memory could be expired/evicted(Fixes #645 along with the 2 previous updates)
|
||||
* Fixed a bug where swapdb result was not recovered after keydb restarts in FLASH mode (Thanks to @karthyuom)
|
||||
* Fixed double free bug when fast sync was canceled early
|
||||
* Fixed a bug where a temp rdb file with zero bytes is generated in flash mode (Thanks to @karthyuom)
|
||||
* Fixed a bug where flash CF options are being reset to default after flushall (Thanks to @karthyuom)(Fixes issue #717)
|
||||
* Updated debug reload command to work with flash
|
||||
* Added support for KEYDB_PASSWORD env variable(Thanks to @einar-pexip)
|
||||
* Compile with flash in Dockerfile(Thanks to @der-eismann)
|
||||
* Fix broken redis-cli symlink (Thanks to @pimvandenbroek)
|
||||
* Use tini for alpine docker(Thanks to @rofafor)
|
||||
|
||||
-- Malavan Sotheeswaran <msotheeswaran@snapchat.com> Sun, 29 Oct 2023 8:39:21 +0000
|
||||
|
||||
keydb (6:6.3.3-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* To help accelerate our development efforts for KeyDB, this will be the last release containing support for Centos 7, Ubuntu 16.04, Ubuntu 18.04, Debian 9 and 32-bit builds
|
||||
* This release contains fixes for 11 issues along with improvements to the KeyDB FLASH feature:
|
||||
* Fixed race condition with expireset access (Issue #597)
|
||||
* Fixed keys command with lua, and added keydb as an alternative to redis as lua variable to access db (Issue #562 and #594)
|
||||
* Fixed hang in aof child (Issue #554)
|
||||
* Fixed leaking fds from RDB save (Issue #453 and #584)
|
||||
* Added config for S3 RDB load/save (Issue #584)
|
||||
* Enabled active defrag during forkless background save to improve average memory efficiency (Issue #460)
|
||||
* Fixed crash with fork background save during replication (Issue #567)
|
||||
* RocksDB has been updated to v7.9.2
|
||||
* Fixed missing slot_to_key map in FLASH cluster mode (Issue #574)
|
||||
* Added keyspace notifications for keys loaded from FLASH at startup
|
||||
* Fixed race condition in prefetchKeysAsync with FLASH enabled (Issue #571)
|
||||
* Fixed integer overflow in rand family of commands(Issue #631, #632, #633)
|
||||
* Fixed bad value in hincrbyfloat(Issue #634)
|
||||
* Fixed OOM hang in rand family of commands(Issue #635)
|
||||
* Added config to limit count of return values in rand family of commands(Issue #636)
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Tue, 18 Apr 2023 10:00:37 +0000
|
||||
|
||||
keydb (6:6.3.2-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* This release contains Beta level support for KeyDB FLASH, new ASYNC commands, latency improvements and a number of bug fixes.
|
||||
* KeyDB FLASH is included as a Beta feature. FLASH allows KeyDB to persist data to the storage medium it is written to, avoiding the need for AOF/RDB files. KeyDB uses rocksdb as the persistent storage provider and can be enabled with config "storage-provider flash /path/to/rocksdb/output". Read more at https://docs.keydb.dev/docs/flash/
|
||||
* In addition to GET/MGET, ASYNC support has been added for the following commands: HGET, HMGET, HKEYS, HVALS, HGETALL, HSCAN and can be enable with config "enable-async-commands yes”
|
||||
* Packaging support for Ubuntu 22.04 (Jammy) and Debian 12 (Bookworm) has been included with this release
|
||||
* Added new soft shutdown feature, can be enabled with config "soft-shutdown yes".
|
||||
* If soft shutdown is enabled, instead of shutting down right away, the server will wait until all clients have disconnected, and will reject all new connection attempts.
|
||||
* Fixed memory leak with tls certificates when tls allowlist is enabled
|
||||
* Fixed bug in rdb load with flash enabled to ensure all dbs are safe to load (previously only checked db[0])
|
||||
* Fixed race conditions in rdb load and replication
|
||||
* Fixed memory access of rdb file after it should have been deleted
|
||||
* Fixed integer overflow bug in flash(Issue #486)
|
||||
* Improve TLS latency by queueing new commands before executing instead of after
|
||||
* Removed O(n) count of memory usage from info command(replaced by O(1) estimate)
|
||||
* Improved latency of clearing large number of flash DBs(Thanks to Paul Chen for this fix)(Issue #516)
|
||||
* replaced sprintf with snprintf to avoid potential security bugs
|
||||
* Fixed bug where a failed move due to key already existing in move target would result in the key being removed from move source(Thanks to Paul Chen for this fix)(Issue #497)
|
||||
* Fixed usage of deprecated OpenSSL api in OpenSSL v>3.0.1(Issue #392)
|
||||
* Imported security fixes from Redis (CVE-2023-22458 CVE-2022-35977)
|
||||
* Other fixed issues: #480 #477 #454 #452 #303 #425 #492 #541
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Fri, 20 Jan 2023 20:00:37 +0000
|
||||
|
||||
keydb (6:6.3.1-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* This point release contains fixes to bugs related to expires, active-rep, and rdb saving
|
||||
* Issues fixed: #419, #422, #428
|
||||
* PRs: #426, #429, #431, #433
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Mon, 23 May 2022 20:00:37 +0000
|
||||
|
||||
keydb (6:6.3.0-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* This release open sources KeyDB Enterprise features into the open source project along with PSYNC for active replication
|
||||
* Partial synchronization for active replication is introduced
|
||||
* MVCC introduced into codebase from KeyDB Enterprise
|
||||
* Async commands added: GET, MGET. These will see perf improvements
|
||||
* KEYS and SCAN commands will no longer be blocking calls
|
||||
* Async Rehash implemented for additional stability to perf
|
||||
* IStorage interface added
|
||||
* In-process background saving (forkless) to comply with maxmemory setting
|
||||
* See v6.3.0 tagged release notes on github for a detailed explanation of these changes
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Wed, 11 May 2022 20:00:37 +0000
|
||||
|
||||
keydb (6:6.2.2-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* Acquire lock in module.cpp to fix module test break
|
||||
* Fix usage of nanosleep() on Apple hardware
|
||||
* Fix networking deadlock (#183)
|
||||
* Remove redundant zfree() in keydb-benchmark
|
||||
* Issues resolved: #378, #384
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Sat, 15 Jan 2022 20:00:37 +0000
|
||||
|
||||
keydb (6:6.2.1-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* This release of KeyDB is at parity with Redis 6.2.6. In addition, the following changes were included:
|
||||
* Systemd support for release packages
|
||||
* Fixes to buffer overflows in the `BITOP SHIFT` and `CRON` commands
|
||||
* Server times are now computed on a seperate thread to improve performance
|
||||
* Now enforces syslog identity and facility as soon as possible
|
||||
* Removed erroneous use of LOG_... flags when using syslog
|
||||
* Fixed erroneous `#endif` leading to build errors on some platforms
|
||||
* Fixed the incorrect counting of client connections
|
||||
* Issues resolved: #355, #370
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Wed, 17 Nov 2021 20:00:37 +0000
|
||||
|
||||
keydb (6:6.2.0-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* Removed unused command line options(-a, -d, -P, -r, -q, --csv, -l, -I, -e, --precision, --cluster, --enable-tracking)
|
||||
* --ms option changed to --time, added --clients, --host, --port, and --threads options
|
||||
* Added keydb-diagnostic-tool
|
||||
* Improved test reliability
|
||||
* Fixed memory leak in keydb-benchmark
|
||||
* Fixed a number of race conditions
|
||||
* Fixed OSX build issues
|
||||
* Feature parity with Redis 6.2.3
|
||||
* Fixed Issues: #323 #325 #276
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Thu, 12 Aug 2021 20:00:37 +0000
|
||||
|
||||
keydb (6:6.0.18-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* 6.0.18 - This release contains a number of improvements to stability and performance.
|
||||
* Additional tests were added with improvements to existing test reliability
|
||||
* Merged Redis 6.0.10 into KeyDB
|
||||
* Issues resolved in this release: #214, #222, #238, #257, #273, #276, #285
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Fri, 26 Mar 2021 16:00:00 +0000
|
||||
|
||||
keydb (6:6.0.16-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* 6.0.16 - This release contains significant performance improvements as well as fixes to the following issues: #234, #236, #233, #207, #229, #231
|
||||
* Reduced memory consumption when Active Replication is not used
|
||||
* Reduced CPU consumption
|
||||
* Improved TLS performance
|
||||
* replica-quorum config for multi-master.Intended to be used with "replica-serve-stale-data no", if at least N replicas are connected we will consider our master link up and serve data.
|
||||
* Subkey expires performance improvement for big sets
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Mon, 28 Sep 2020 16:00:00 +0000
|
||||
|
||||
keydb (6:6.0.13-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* 6.0.13 New features added: KEYDB.MEXISTS, REPLICAOF REMOVE, multi-master-no-forward configuration option
|
||||
* KEYDB.MEXISTS - return exactly which keys exist in the database not just the count
|
||||
* REPLICAOF REMOVE - Remove specific masters in a multi master setup
|
||||
* multi-master-no-forward configuration option - Reduce network traffic in mesh topology multimaster setups
|
||||
* Redis 6.0.5 feature parity
|
||||
* Fixed Issues: 194 Bad directive or wrong number of arguments error on 'lazyfree-lazy-user-del no'; 209 Timeout using modules; 210 Databases are not merged on multi-master sync; 211 rpm packages now signed; 212 rpm file permissions updated
|
||||
|
||||
-- Ben Schermel <ben@eqalpha.com> Mon, 13 Jul 2020 8:00:00 +0000
|
||||
|
||||
keydb (6:6.0.9-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* 6.0.9 Addressing issues 187 - cpu lockup with subkey expire, 190 - missing sentinel binary in keydb-tools.
|
||||
@@ -182,7 +8,7 @@ keydb (6:6.0.8-1distribution_placeholder) codename_placeholder; urgency=medium
|
||||
|
||||
* This is our first release to fully support all Redis 6.0.4 features, including but not limited to: TLS Support (fully supports multithreading!), Client side caching, RESP 3 Support
|
||||
* KeyDB Has also added the following new features: Improved memory efficiency for short strings, Fastlock autotuning, KeyDB.HRENAME (rename a member of a hash)In addition we've spent a lot of time focussing on stability.
|
||||
* The following bug fixes are resolved: 150 - Deadlock in ReplicationFeedMonitors, 170 - KeyDB dying via SIGABORT, 180 - crash after setting maxclients via cmd line, 169 - Write performance of a master dropped sharply when a replica is added
|
||||
* The following bug fixes are resolved: 150 - Deadlock in ReplicationFeedMonitors, 170 - KeyDB dying via SIGABORT, 180 - crash after setting maxclients via cmd line, 169 - Write performance of a master dropped sharply when a slave is added
|
||||
* In addition untracked issues were resolved: Potential deadlock when entering futex sleep, Pub/Sub Async messages may not be sent in a timely manner when load is low, Excessive logging during failed RREPLAY, KeyDB unresponsive handling clients on different threads during RDB load
|
||||
* Updated deb package builds to build from source and phasing out init.d. dh_installsystemd from deb helper 11 used where applicable
|
||||
* Naming conventions are now updated [ keydbpackage_version-build~distribution_architecture] and master changelog included and maintained
|
||||
|
||||
+82
-108
@@ -1,117 +1,91 @@
|
||||
FROM ubuntu:20.04
|
||||
SHELL ["/bin/bash","-c"]
|
||||
FROM ubuntu:18.04
|
||||
# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
|
||||
RUN groupadd -r keydb && useradd -r -g keydb keydb
|
||||
# use gosu for easy step-down from root: https://github.com/tianon/gosu/releases
|
||||
ENV GOSU_VERSION 1.14
|
||||
|
||||
# grab gosu for easy step-down from root
|
||||
# https://github.com/tianon/gosu/releases
|
||||
ENV GOSU_VERSION 1.11
|
||||
RUN set -eux; \
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends ca-certificates dirmngr gnupg wget; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
|
||||
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
|
||||
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
|
||||
export GNUPGHOME="$(mktemp -d)"; \
|
||||
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
|
||||
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
|
||||
gpgconf --kill all; \
|
||||
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
chmod +x /usr/local/bin/gosu; \
|
||||
gosu --version; \
|
||||
gosu nobody true
|
||||
# build KeyDB
|
||||
ARG BRANCH
|
||||
RUN set -eux; \
|
||||
\
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -qqy --no-install-recommends \
|
||||
dpkg-dev \
|
||||
pkg-config \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
nasm \
|
||||
autotools-dev \
|
||||
autoconf \
|
||||
libjemalloc-dev \
|
||||
tcl \
|
||||
tcl-dev \
|
||||
uuid-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libbz2-dev \
|
||||
libzstd-dev \
|
||||
liblz4-dev \
|
||||
libsnappy-dev \
|
||||
libssl-dev \
|
||||
git; \
|
||||
cd /tmp && git clone --branch $BRANCH https://github.com/Snapchat/KeyDB.git --recursive; \
|
||||
cd /tmp/KeyDB; \
|
||||
# disable protected mode as it relates to docker
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' ./src/config.cpp; \
|
||||
sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' ./src/config.cpp; \
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' ./src/config.cpp; \
|
||||
make -j$(nproc) BUILD_TLS=yes ENABLE_FLASH=yes; \
|
||||
cd src; \
|
||||
strip keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel keydb-server; \
|
||||
mv keydb-server keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel /usr/local/bin/; \
|
||||
# clean up unused dependencies
|
||||
echo $savedAptMark; \
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
find /usr/local -type f -executable -exec ldd '{}' ';' \
|
||||
| awk '/=>/ { print $(NF-1) }' \
|
||||
| sed 's:.*/::' \
|
||||
| sort -u \
|
||||
| xargs -r dpkg-query --search \
|
||||
| cut -d: -f1 \
|
||||
| sort -u \
|
||||
| xargs -r apt-mark manual \
|
||||
; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
# create working directories and organize files
|
||||
# save list of currently installed packages for later so we can clean up
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
dirmngr \
|
||||
gnupg \
|
||||
wget \
|
||||
; \
|
||||
\
|
||||
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
|
||||
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
|
||||
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
|
||||
\
|
||||
# verify the signature
|
||||
export GNUPGHOME="$(mktemp -d)"; \
|
||||
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
|
||||
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
|
||||
gpgconf --kill all; \
|
||||
rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \
|
||||
\
|
||||
# clean up fetch dependencies
|
||||
apt-mark auto '.*' > /dev/null; \
|
||||
[ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \
|
||||
apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \
|
||||
\
|
||||
chmod +x /usr/local/bin/gosu; \
|
||||
# verify that the binary works
|
||||
gosu --version; \
|
||||
gosu nobody true
|
||||
|
||||
# Load pre-generated equivalent binaries to image (reduces deployment build times)
|
||||
RUN \
|
||||
mkdir /data && chown keydb:keydb /data; \
|
||||
mkdir /flash && chown keydb:keydb /flash; \
|
||||
mkdir -p /etc/keydb; \
|
||||
cp /tmp/KeyDB/keydb.conf /etc/keydb/; \
|
||||
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(logfile .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/protected-mode yes/protected-mode no/g' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(bind .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
cd /usr/local/bin; \
|
||||
ln -s keydb-cli redis-cli; \
|
||||
cd /etc/keydb; \
|
||||
ln -s keydb.conf redis.conf; \
|
||||
rm -rf /tmp/*
|
||||
# generate entrypoint script
|
||||
mkdir -p /etc/keydb
|
||||
ADD ./app/keydb-* /usr/local/bin/
|
||||
ADD ./app/docker-entrypoint.sh /usr/local/bin/
|
||||
ADD ./app/*.conf /etc/keydb/
|
||||
# Set up config and binaries
|
||||
RUN \
|
||||
cd /usr/local/bin && \
|
||||
sed -i 's/^\(bind .*\)$/# \1/' /etc/keydb/keydb.conf && \
|
||||
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/keydb/keydb.conf && \
|
||||
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/keydb/keydb.conf && \
|
||||
sed -i 's/^\(logfile .*\)$/# \1/' /etc/keydb/keydb.conf && \
|
||||
sed -i 's/protected-mode yes/protected-mode no/g' /etc/keydb/keydb.conf && \
|
||||
ln -s keydb-cli redis-cli && \
|
||||
cd /etc/keydb && \
|
||||
ln -s keydb.conf redis.conf
|
||||
|
||||
# Ensure deps installed for binaries
|
||||
RUN set -eux; \
|
||||
echo '#!/bin/sh' > /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'set -e' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# first arg is '-f' or '--some-option'" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# or first arg is `something.conf`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' set -- keydb-server "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo '# if KEYDB_PASSWORD is set, add it to the arguments' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ -n "$KEYDB_PASSWORD" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' set -- "$@" --requirepass "${KEYDB_PASSWORD}"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# allow the container to be started with `--user`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "$1" = "keydb-server" -a "$(id -u)" = "0" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo " find . \! -user keydb -exec chown keydb '{}' +" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' exec gosu keydb "$0" "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'exec "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
# set remaining image properties
|
||||
\
|
||||
savedAptMark="$(apt-mark showmanual)"; \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libcurl4 \
|
||||
libc6 \
|
||||
libssl1.1 \
|
||||
libuuid1 \
|
||||
libstdc++6 \
|
||||
libgcc1 \
|
||||
zlib1g \
|
||||
libbz2-1.0 \
|
||||
liblz4-1 \
|
||||
libsnappy1v5 \
|
||||
libzstd1 \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# create working directories
|
||||
RUN \
|
||||
mkdir /data && chown keydb:keydb /data && \
|
||||
mkdir /flash && chown keydb:keydb /flash
|
||||
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
ENV KEYDB_PRO_DIRECTORY=/usr/local/bin/
|
||||
|
||||
#COPY docker-entrypoint.sh /usr/local/bin/
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
# Expose container port and start KeyDB by default on running container
|
||||
EXPOSE 6379
|
||||
CMD ["keydb-server","/etc/keydb/keydb.conf"]
|
||||
CMD ["keydb-server", "/etc/keydb/keydb.conf"]
|
||||
@@ -1,86 +0,0 @@
|
||||
FROM alpine:3.18
|
||||
# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
|
||||
RUN addgroup -S -g 1000 keydb && adduser -S -G keydb -u 999 keydb
|
||||
RUN mkdir -p /etc/keydb
|
||||
ARG BRANCH
|
||||
RUN set -eux; \
|
||||
\
|
||||
apk add --no-cache su-exec tini; \
|
||||
apk add --no-cache --virtual .build-deps \
|
||||
coreutils \
|
||||
gcc \
|
||||
linux-headers \
|
||||
make \
|
||||
musl-dev \
|
||||
openssl-dev \
|
||||
git \
|
||||
util-linux-dev \
|
||||
curl-dev \
|
||||
g++ \
|
||||
libunwind-dev \
|
||||
bash \
|
||||
perl \
|
||||
git \
|
||||
bzip2-dev \
|
||||
zstd-dev \
|
||||
lz4-dev \
|
||||
snappy-dev \
|
||||
; \
|
||||
cd /tmp && git clone --branch $BRANCH https://github.com/Snapchat/KeyDB.git --recursive; \
|
||||
cd /tmp/KeyDB; \
|
||||
# disable protected mode as it relates to docker
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *1 *,.*[)],$' ./src/config.cpp; \
|
||||
sed -ri 's!^( *createBoolConfig[(]"protected-mode",.*, *)1( *,.*[)],)$!\10\2!' ./src/config.cpp; \
|
||||
grep -E '^ *createBoolConfig[(]"protected-mode",.*, *0 *,.*[)],$' ./src/config.cpp; \
|
||||
make -j$(nproc) BUILD_TLS=yes ENABLE_FLASH=yes; \
|
||||
cd src; \
|
||||
strip keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel keydb-server; \
|
||||
mv keydb-server keydb-cli keydb-benchmark keydb-check-rdb keydb-check-aof keydb-diagnostic-tool keydb-sentinel /usr/local/bin/; \
|
||||
runDeps="$( \
|
||||
scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \
|
||||
| tr ',' '\n' \
|
||||
| sort -u \
|
||||
| awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
|
||||
)"; \
|
||||
apk add --no-network --virtual .keydb-rundeps $runDeps; \
|
||||
apk del --no-network .build-deps; \
|
||||
# create working directories and organize files
|
||||
mkdir /data && chown keydb:keydb /data; \
|
||||
mkdir /flash && chown keydb:keydb /flash; \
|
||||
mkdir -p /etc/keydb; \
|
||||
cp /tmp/KeyDB/keydb.conf /etc/keydb/; \
|
||||
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(logfile .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/protected-mode yes/protected-mode no/g' /etc/keydb/keydb.conf; \
|
||||
sed -i 's/^\(bind .*\)$/# \1/' /etc/keydb/keydb.conf; \
|
||||
cd /usr/local/bin; \
|
||||
ln -s keydb-cli redis-cli; \
|
||||
cd /etc/keydb; \
|
||||
ln -s keydb.conf redis.conf; \
|
||||
rm -rf /tmp/*
|
||||
# generate entrypoint script
|
||||
RUN set -eux; \
|
||||
echo '#!/bin/sh' > /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'set -e' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# first arg is '-f' or '--some-option'" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# or first arg is `something.conf`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' set -- keydb-server "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo '# if KEYDB_PASSWORD is set, add it to the arguments' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ -n "$KEYDB_PASSWORD" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' set -- "$@" --requirepass "${KEYDB_PASSWORD}"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo "# allow the container to be started with `--user`" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'if [ "$1" = "keydb-server" -a "$(id -u)" = "0" ]; then' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo " find . \! -user keydb -exec chown keydb '{}' +" >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo ' exec su-exec keydb "$0" "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'fi' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
echo 'exec "$@"' >> /usr/local/bin/docker-entrypoint.sh; \
|
||||
chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
ENTRYPOINT ["tini", "--", "docker-entrypoint.sh"]
|
||||
EXPOSE 6379
|
||||
CMD ["keydb-server", "/etc/keydb/keydb.conf"]
|
||||
@@ -1,7 +1,5 @@
|
||||
This Dockerfile will clone the KeyDB repo, build, and generate a Docker image you can use
|
||||
|
||||
To build, use experimental mode to enable use of build args. Tag the build and specify branch name. The command below will generate your docker image:
|
||||
In order to create a docker image, generate the keydb binaries, copy them to the app directory, copy keydb.conf and sentinel.conf to the app directory as well, then run the following command:
|
||||
|
||||
```
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker build --build-arg BRANCH=<keydbBranch> -t <yourImageName>
|
||||
$ sudo docker build . -t <yourimagename>
|
||||
```
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# first arg is `-f` or `--some-option`
|
||||
# or first arg is `something.conf`
|
||||
if [ "${1#-}" != "$1" ] || [ "${1%.conf}" != "$1" ]; then
|
||||
set -- keydb-server "$@"
|
||||
fi
|
||||
|
||||
# allow the container to be started with `--user`
|
||||
if [ "$1" = 'keydb-server' -a "$(id -u)" = '0' ]; then
|
||||
find . \! -user keydb -exec chown keydb '{}' +
|
||||
exec gosu keydb "$0" "$@"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
### Generate RPM files for the generated binaries
|
||||
|
||||
After making the binaries you can run the following script
|
||||
After running make to produce keydb binaries you can run the following script to create rpm package
|
||||
|
||||
Usage:
|
||||
```
|
||||
|
||||
@@ -5,7 +5,10 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
version=$(grep KEYDB_REAL_VERSION $DIR/../../src/version.h | awk '{ printf $3 }' | tr -d \")
|
||||
release=1 # by default this will always be 1 for keydb version structure. If build release version needs to be update you can modify here
|
||||
arch=$(uname -m)
|
||||
dist=$(rpm --eval '%{?dist}')
|
||||
dist=el$(rpm -q --queryformat '%{VERSION}' centos-release | cut -d. -f1)
|
||||
if [[ "$dist" == "elpackage centos-release is not installed" ]]; then
|
||||
dist=el$(rpm -q --queryformat '%{VERSION}' centos-linux-release | cut -d. -f1)
|
||||
fi
|
||||
|
||||
if [[ "$arch" != "aarch64" ]] && [[ "$arch" != "x86_64" ]]; then
|
||||
echo "This script is only valid and tested for aarch64 and x86_64 architectures. You are trying to use: $arch"
|
||||
@@ -36,8 +39,8 @@ sed -i -E "1a\Version : $version" $DIR/keydb_build/keydb.spec
|
||||
sed -i '3d' $DIR/keydb_build/keydb.spec
|
||||
sed -i -E "2a\Release : $release%{?dist}" $DIR/keydb_build/keydb.spec
|
||||
|
||||
mkdir -p /root/rpmbuild/BUILDROOT/keydb-$version-$release$dist.$arch
|
||||
cp -r $DIR/keydb_build/keydb_rpm/* /root/rpmbuild/BUILDROOT/keydb-$version-$release$dist.$arch/
|
||||
mkdir -p /root/rpmbuild/BUILDROOT/keydb-$version-$release.$dist.$arch
|
||||
cp -r $DIR/keydb_build/keydb_rpm/* /root/rpmbuild/BUILDROOT/keydb-$version-$release.$dist.$arch/
|
||||
rpmbuild -bb $DIR/keydb_build/keydb.spec
|
||||
mv /root/rpmbuild/RPMS/$arch/* $DIR/rpm_files_generated
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
Name : keydb
|
||||
Version : 6.0.5
|
||||
Version : 5.3.3
|
||||
Release : 1%{?dist}
|
||||
Group : Unspecified
|
||||
License : BSD
|
||||
Packager : Snap Inc.
|
||||
Packager : EQ Alpha Technology
|
||||
|
||||
URL : https://keydb.dev
|
||||
Summary : A persistent key-value database
|
||||
|
||||
|
||||
Requires: /bin/awk
|
||||
Requires: logrotate
|
||||
Requires(pre): shadow-utils
|
||||
|
||||
@@ -257,24 +257,6 @@ tcp-keepalive 300
|
||||
#
|
||||
# tls-session-cache-timeout 60
|
||||
|
||||
# Allow the server to monitor the filesystem and rotate out TLS certificates if
|
||||
# they change on disk, defaults to no.
|
||||
#
|
||||
# tls-rotation no
|
||||
|
||||
# Setup a allowlist of allowed Common Names (CNs)/Subject Alternative Names (SANs)
|
||||
# that are allowed to connect to this server. This includes both normal clients as
|
||||
# well as other servers connected for replication/clustering purposes. If nothing is
|
||||
# specified, then no allowlist is used and all certificates are accepted.
|
||||
# Supports IPv4, DNS, RFC822, and URI SAN types.
|
||||
# You can put multiple names on one line as follows:
|
||||
#
|
||||
# tls-allowlist <dns1> <dns2> <dns3> ...
|
||||
#
|
||||
#
|
||||
# This configuration also allows for wildcard characters with glob style formatting
|
||||
# i.e. "*.com" would allow all clients to connect with a CN/SAN that ends with ".com"
|
||||
|
||||
################################# GENERAL #####################################
|
||||
|
||||
# By default KeyDB does not run as a daemon. Use 'yes' if you need it.
|
||||
@@ -2053,27 +2035,9 @@ server-threads 2
|
||||
# bringing up replicas can result in data loss (the first master will win).
|
||||
# active-replica yes
|
||||
|
||||
# KeyDB will attempt to balance clients across threads evenly; However, replica clients
|
||||
# are usually much more expensive than a normal client, and so KeyDB will try to assign
|
||||
# fewer clients to threads with a replica. The weighting factor below is intented to help tune
|
||||
# this behavior. A replica weighting factor of 2 means we treat a replica as the equivalent
|
||||
# of two normal clients. Adjusting this value may improve performance when replication is
|
||||
# used. The best weighting is workload specific - e.g. read heavy workloads should set
|
||||
# this to 1. Very write heavy workloads may benefit from higher numbers.
|
||||
#
|
||||
# By default KeyDB sets this to 2.
|
||||
replica-weighting-factor 2
|
||||
# Enable Pro? KeyDB pro provides support for pro only features
|
||||
# note: you may omit the license key to demo pro features for a limited time
|
||||
# enable-pro [License Key]
|
||||
|
||||
# Enable FLASH support (Experimental Feature)
|
||||
# Enable FLASH support? (Pro Only)
|
||||
# storage-provider flash /path/to/flash/db
|
||||
|
||||
# Blob support is a way to store very large objects (>200MB) on disk
|
||||
# The files are automatically cleaned up when KeyDB exits and are only
|
||||
# for temporary use. This helps reduce memory pressure for very large
|
||||
# data items at the cost of some performance.
|
||||
#
|
||||
# By default this config is disable. When enabled the disk associated
|
||||
# with KeyDB's working directory will be used. If there is insufficient
|
||||
# disk space or any other I/O error KeyDB will instead use memory.
|
||||
#
|
||||
# blob-support false
|
||||
@@ -1,7 +1,7 @@
|
||||
[Unit]
|
||||
Description=Advanced key-value store
|
||||
After=network.target
|
||||
Documentation=http://keydb.io/documentation, man:keydb-sentinel(1)
|
||||
Documentation=https://docs.keydb.dev, man:keydb-sentinel(1)
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
|
||||
Regular → Executable
+1
-4
@@ -1,8 +1,5 @@
|
||||
Copyright (c) 2006-2020, Salvatore Sanfilippo
|
||||
Copyright (c) 2006-2015, Salvatore Sanfilippo
|
||||
Copyright (C) 2019, John Sully
|
||||
Copyright (C) 2020-2021, EQ Alpha Technology Ltd.
|
||||
Copyright (C) 2021, Snap Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
Regular → Executable
File mode changed.
@@ -37,6 +37,5 @@ $TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/hash \
|
||||
--single unit/moduleapi/zset \
|
||||
--single unit/moduleapi/stream \
|
||||
--single unit/moduleapi/load \
|
||||
--config server-threads 3 \
|
||||
"${@}"
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/sh
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
TCLSH=""
|
||||
|
||||
export ASAN_OPTIONS=allocator_may_return_null=1 $ASAN_OPTIONS
|
||||
|
||||
for VERSION in $TCL_VERSIONS; do
|
||||
TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
|
||||
done
|
||||
|
||||
if [ -z $TCLSH ]
|
||||
then
|
||||
echo "You need tcl 8.5 or newer in order to run the KeyDB test"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -r tests/tls ] || [ ! -r tests/tls_1 ] || [ ! -r tests/tls_2 ];
|
||||
then
|
||||
echo "Generating neccessary certificates for TLS rotation testing."
|
||||
rm -rf tests/tls tests/tls_1 tests/tls_2
|
||||
|
||||
utils/gen-test-certs.sh
|
||||
mv tests/tls tests/tls_1
|
||||
utils/gen-test-certs.sh
|
||||
mv tests/tls tests/tls_2
|
||||
utils/gen-test-certs.sh
|
||||
fi
|
||||
$TCLSH tests/test_helper.tcl \
|
||||
--single unit/tls-rotation \
|
||||
--tls \
|
||||
--config server-threads 3 \
|
||||
"${@}"
|
||||
@@ -1,131 +0,0 @@
|
||||
#include "AsyncWorkQueue.h"
|
||||
#include "server.h"
|
||||
|
||||
AsyncWorkQueue::AsyncWorkQueue(int nthreads)
|
||||
{
|
||||
for (int i = 0; i < nthreads; ++i)
|
||||
{
|
||||
m_vecthreads.emplace_back([&]{
|
||||
WorkerThreadMain();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWorkQueue::WorkerThreadMain()
|
||||
{
|
||||
redisServerThreadVars vars;
|
||||
serverTL = &vars;
|
||||
|
||||
vars.clients_pending_asyncwrite = listCreate();
|
||||
|
||||
m_mutex.lock();
|
||||
m_vecpthreadVars.push_back(&vars);
|
||||
m_mutex.unlock();
|
||||
|
||||
while (!m_fQuitting)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (m_workqueue.empty())
|
||||
m_cvWakeup.wait(lock);
|
||||
|
||||
aeThreadOnline();
|
||||
while (!m_workqueue.empty())
|
||||
{
|
||||
WorkItem task = std::move(m_workqueue.front());
|
||||
m_workqueue.pop_front();
|
||||
|
||||
lock.unlock();
|
||||
serverTL->gcEpoch = g_pserver->garbageCollector.startEpoch();
|
||||
task.fnAsync();
|
||||
g_pserver->garbageCollector.endEpoch(serverTL->gcEpoch);
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
serverTL->gcEpoch = g_pserver->garbageCollector.startEpoch();
|
||||
if (listLength(serverTL->clients_pending_asyncwrite)) {
|
||||
aeAcquireLock();
|
||||
ProcessPendingAsyncWrites();
|
||||
aeReleaseLock();
|
||||
}
|
||||
g_pserver->garbageCollector.endEpoch(serverTL->gcEpoch);
|
||||
serverTL->gcEpoch.reset();
|
||||
aeThreadOffline();
|
||||
}
|
||||
|
||||
listRelease(vars.clients_pending_asyncwrite);
|
||||
|
||||
std::unique_lock<fastlock> lockf(serverTL->lockPendingWrite);
|
||||
serverTL->vecclientsProcess.clear();
|
||||
serverTL->clients_pending_write.clear();
|
||||
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
bool AsyncWorkQueue::removeClientAsyncWrites(client *c)
|
||||
{
|
||||
bool fFound = false;
|
||||
aeAcquireLock();
|
||||
m_mutex.lock();
|
||||
for (auto pvars : m_vecpthreadVars)
|
||||
{
|
||||
listIter li;
|
||||
listNode *ln;
|
||||
listRewind(pvars->clients_pending_asyncwrite, &li);
|
||||
while ((ln = listNext(&li)) != nullptr)
|
||||
{
|
||||
if (c == listNodeValue(ln))
|
||||
{
|
||||
listDelNode(pvars->clients_pending_asyncwrite, ln);
|
||||
fFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_mutex.unlock();
|
||||
aeReleaseLock();
|
||||
return fFound;
|
||||
}
|
||||
|
||||
void AsyncWorkQueue::shutdown()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
serverAssert(!GlobalLocksAcquired());
|
||||
m_fQuitting = true;
|
||||
m_cvWakeup.notify_all();
|
||||
lock.unlock();
|
||||
|
||||
for (auto &thread : m_vecthreads)
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void AsyncWorkQueue::abandonThreads()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_fQuitting = true;
|
||||
m_cvWakeup.notify_all();
|
||||
for (auto &thread : m_vecthreads)
|
||||
{
|
||||
thread.detach();
|
||||
}
|
||||
m_vecthreads.clear();
|
||||
}
|
||||
|
||||
AsyncWorkQueue::~AsyncWorkQueue()
|
||||
{
|
||||
serverAssert(!GlobalLocksAcquired() || m_vecthreads.empty());
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_fQuitting = true;
|
||||
m_cvWakeup.notify_all();
|
||||
lock.unlock();
|
||||
|
||||
abandonThreads();
|
||||
}
|
||||
|
||||
void AsyncWorkQueue::AddWorkFunction(std::function<void()> &&fnAsync, bool fHiPri)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (fHiPri)
|
||||
m_workqueue.emplace_front(std::move(fnAsync));
|
||||
else
|
||||
m_workqueue.emplace_back(std::move(fnAsync));
|
||||
m_cvWakeup.notify_one();
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#include "fastlock.h"
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
|
||||
class AsyncWorkQueue
|
||||
{
|
||||
struct WorkItem
|
||||
{
|
||||
WorkItem(std::function<void()> &&fnAsync)
|
||||
: fnAsync(std::move(fnAsync))
|
||||
{}
|
||||
|
||||
WorkItem(WorkItem&&) = default;
|
||||
std::function<void()> fnAsync;
|
||||
};
|
||||
std::vector<std::thread> m_vecthreads;
|
||||
std::vector<struct redisServerThreadVars*> m_vecpthreadVars;
|
||||
std::deque<WorkItem> m_workqueue;
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_cvWakeup;
|
||||
std::atomic<bool> m_fQuitting { false };
|
||||
|
||||
void WorkerThreadMain();
|
||||
public:
|
||||
AsyncWorkQueue(int nthreads);
|
||||
~AsyncWorkQueue();
|
||||
|
||||
void AddWorkFunction(std::function<void()> &&fnAsync, bool fHiPri = false);
|
||||
bool removeClientAsyncWrites(struct client *c);
|
||||
|
||||
void shutdown();
|
||||
|
||||
void abandonThreads();
|
||||
};
|
||||
@@ -1,62 +0,0 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include "sds.h"
|
||||
#include <string>
|
||||
|
||||
#define METADATA_DB_IDENTIFIER "c299fde0-6d42-4ec4-b939-34f680ffe39f"
|
||||
|
||||
class IStorageFactory
|
||||
{
|
||||
public:
|
||||
typedef void (*key_load_iterator)(const char *rgchKey, size_t cchKey, void *privdata);
|
||||
|
||||
virtual ~IStorageFactory() {}
|
||||
virtual class IStorage *create(int db, key_load_iterator itr, void *privdata) = 0;
|
||||
virtual class IStorage *createMetadataDb() = 0;
|
||||
virtual const char *name() const = 0;
|
||||
virtual size_t totalDiskspaceUsed() const = 0;
|
||||
virtual sdsstring getInfo() const = 0;
|
||||
virtual bool FSlow() const = 0;
|
||||
virtual size_t filedsRequired() const { return 0; }
|
||||
};
|
||||
|
||||
class IStorage
|
||||
{
|
||||
public:
|
||||
typedef std::function<bool(const char *, size_t, const void *, size_t)> callback;
|
||||
typedef std::function<void(const char *, size_t, const void *, size_t)> callbackSingle;
|
||||
|
||||
virtual ~IStorage();
|
||||
|
||||
virtual void insert(const char *key, size_t cchKey, void *data, size_t cb, bool fOverwire) = 0;
|
||||
virtual bool erase(const char *key, size_t cchKey) = 0;
|
||||
virtual void retrieve(const char *key, size_t cchKey, callbackSingle fn) const = 0;
|
||||
virtual size_t clear() = 0;
|
||||
virtual bool enumerate(callback fn) const = 0;
|
||||
virtual bool enumerate_hashslot(callback fn, unsigned int hashslot) const = 0;
|
||||
virtual size_t count() const = 0;
|
||||
|
||||
virtual void bulkInsert(char **rgkeys, size_t *rgcbkeys, char **rgvals, size_t *rgcbvals, size_t celem) {
|
||||
beginWriteBatch();
|
||||
for (size_t ielem = 0; ielem < celem; ++ielem) {
|
||||
insert(rgkeys[ielem], rgcbkeys[ielem], rgvals[ielem], rgcbvals[ielem], false);
|
||||
}
|
||||
endWriteBatch();
|
||||
}
|
||||
|
||||
virtual std::vector<std::string> getExpirationCandidates(unsigned int count) = 0;
|
||||
virtual std::vector<std::string> getEvictionCandidates(unsigned int count) = 0;
|
||||
virtual void setExpire(const char *key, size_t cchKey, long long expire) = 0;
|
||||
virtual void removeExpire(const char *key, size_t cchKey, long long expire) = 0;
|
||||
|
||||
virtual void beginWriteBatch() {} // NOP
|
||||
virtual void endWriteBatch() {} // NOP
|
||||
|
||||
virtual void batch_lock() {} // NOP
|
||||
virtual void batch_unlock() {} // NOP
|
||||
|
||||
virtual void flush() = 0;
|
||||
|
||||
/* This is permitted to be a shallow clone */
|
||||
virtual const IStorage *clone() const = 0;
|
||||
};
|
||||
+17
-98
@@ -12,20 +12,16 @@
|
||||
# Dependencies are stored in the Makefile.dep file. To rebuild this file
|
||||
# Just use 'make dep', but this is only needed by developers.
|
||||
|
||||
# we ship KeyDB with TLS by default
|
||||
# export it here as both this file and deps/Makefile uses it
|
||||
export BUILD_TLS ?= yes
|
||||
|
||||
release_hdr := $(shell sh -c './mkreleasehdr.sh')
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
|
||||
OPTIMIZATION?=-O2 -flto
|
||||
DEPENDENCY_TARGETS=linenoise lua hdr_histogram
|
||||
OPTIMIZATION?=-O2
|
||||
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram
|
||||
NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
STD=-pedantic -DREDIS_STATIC=''
|
||||
CXX_STD=-std=c++17 -pedantic -fno-rtti -D__STDC_FORMAT_MACROS
|
||||
CXX_STD=-std=c++14 -pedantic -fno-rtti -D__STDC_FORMAT_MACROS
|
||||
ifneq (,$(findstring clang,$(CC)))
|
||||
STD+=-Wno-c11-extensions
|
||||
else
|
||||
@@ -62,7 +58,6 @@ endif
|
||||
endif
|
||||
|
||||
USEASM?=true
|
||||
ENABLE_FLASH?=no
|
||||
|
||||
ifneq ($(strip $(SANITIZE)),)
|
||||
CFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE -fno-omit-frame-pointer
|
||||
@@ -72,26 +67,6 @@ ifneq ($(strip $(SANITIZE)),)
|
||||
USEASM=false
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_FLASH),yes)
|
||||
STORAGE_OBJ+= storage/rocksdb.o storage/rocksdbfactory.o
|
||||
ifeq ($(USE_SYSTEM_ROCKSDB),yes)
|
||||
FINAL_LIBS+= $(shell pkg-config --libs rocksdb)
|
||||
FINAL_CXXFLAGS+= $(shell pkg-config --cflags rocksdb) -DENABLE_ROCKSDB
|
||||
|
||||
else
|
||||
FINAL_LIBS+= -lz -lcrypto -lbz2 -lzstd -llz4 -lsnappy
|
||||
CXXFLAGS+= -I../deps/rocksdb/include/ -DENABLE_ROCKSDB
|
||||
FINAL_CXXFLAGS+= -I../deps/rocksdb/include/
|
||||
FINAL_LIBS+= ../deps/rocksdb/librocksdb.a
|
||||
DEPENDENCY_TARGETS+= rocksdb
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(CHECKED),true)
|
||||
CXXFLAGS+= -DCHECKED_BUILD
|
||||
endif
|
||||
|
||||
# Do we use our assembly spinlock? X64 only
|
||||
ifeq ($(uname_S),Linux)
|
||||
ifeq ($(uname_M),x86_64)
|
||||
@@ -107,6 +82,7 @@ endif
|
||||
|
||||
ifeq ($(COMPILER_NAME),clang)
|
||||
CXXFLAGS+= -stdlib=libc++
|
||||
LDFLAGS+= -latomic
|
||||
endif
|
||||
|
||||
# To get ARM stack traces if KeyDB crashes we need a special C flag.
|
||||
@@ -137,24 +113,17 @@ ifeq ($(USE_JEMALLOC),no)
|
||||
MALLOC=libc
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(NO_LICENSE_CHECK),yes)
|
||||
CXXFLAGS+=-DNO_LICENSE_CHECK=1
|
||||
endif
|
||||
|
||||
# Override default settings if possible
|
||||
-include .make-settings
|
||||
|
||||
DEBUG=-g -ggdb
|
||||
FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(KEYDB_CFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_CXXFLAGS=$(CXX_STD) $(WARN) $(OPT) $(DEBUG) $(CXXFLAGS) $(KEYDB_CFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_LDFLAGS=$(LDFLAGS) $(KEYDB_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS+=-lm -lz -lcrypto
|
||||
FINAL_LIBS+=-lm
|
||||
DEBUG=-g -ggdb
|
||||
|
||||
ifneq ($(uname_S),Darwin)
|
||||
ifneq ($(uname_S),FreeBSD)
|
||||
FINAL_LIBS+=-latomic
|
||||
endif
|
||||
endif
|
||||
# Linux ARM32 needs -latomic at linking time
|
||||
ifneq (,$(findstring armv,$(uname_M)))
|
||||
@@ -220,13 +189,7 @@ ifeq ($(uname_S),NetBSD)
|
||||
else
|
||||
ifeq ($(uname_S),FreeBSD)
|
||||
# FreeBSD
|
||||
FINAL_LIBS+= -lpthread -luuid -lexecinfo
|
||||
FINAL_CFLAGS+= -I/usr/local/include
|
||||
FINAL_CXXFLAGS+= -I/usr/local/include
|
||||
FINAL_LDFLAGS+= -L/usr/local/lib
|
||||
ifeq ($(USE_BACKTRACE),yes)
|
||||
FINAL_CFLAGS+= -DUSE_BACKTRACE
|
||||
endif
|
||||
FINAL_LIBS+= -lpthread -lexecinfo
|
||||
else
|
||||
ifeq ($(uname_S),DragonFly)
|
||||
# DragonFly
|
||||
@@ -267,21 +230,14 @@ endif
|
||||
|
||||
ifdef OPENSSL_PREFIX
|
||||
OPENSSL_CFLAGS=-I$(OPENSSL_PREFIX)/include
|
||||
OPENSSL_CXXFLAGS=-I$(OPENSSL_PREFIX)/include
|
||||
OPENSSL_LDFLAGS=-L$(OPENSSL_PREFIX)/lib
|
||||
# Also export OPENSSL_PREFIX so it ends up in deps sub-Makefiles
|
||||
export OPENSSL_PREFIX
|
||||
endif
|
||||
|
||||
# Include paths to dependencies
|
||||
FINAL_CFLAGS+= -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram
|
||||
FINAL_CXXFLAGS+= -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram
|
||||
|
||||
ifeq ($(USE_SYSTEM_CONCURRENTQUEUE),yes)
|
||||
FINAL_CXXFLAGS+= -I/usr/include/concurrentqueue/moodycamel
|
||||
else
|
||||
FINAL_CXXFLAGS+= -I../deps/concurrentqueue
|
||||
endif
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram
|
||||
FINAL_CXXFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src -I../deps/hdr_histogram
|
||||
|
||||
# Determine systemd support and/or build preference (defaulting to auto-detection)
|
||||
BUILD_WITH_SYSTEMD=no
|
||||
@@ -324,17 +280,11 @@ ifeq ($(MALLOC),tcmalloc_minimal)
|
||||
endif
|
||||
|
||||
ifeq ($(MALLOC),jemalloc)
|
||||
ifeq ($(USE_SYSTEM_JEMALLOC),yes)
|
||||
FINAL_CFLAGS+= -DUSE_JEMALLOC $(shell $(PKG_CONFIG) --cflags jemalloc)
|
||||
FINAL_CXXFLAGS+= -DUSE_JEMALLOC $(shell $(PKG_CONFIG) --cflags jemalloc)
|
||||
FINAL_LIBS := $(shell $(PKG_CONFIG) --libs jemalloc) $(FINAL_LIBS)
|
||||
else
|
||||
DEPENDENCY_TARGETS+= jemalloc
|
||||
FINAL_CFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include
|
||||
FINAL_CXXFLAGS+= -DUSE_JEMALLOC -I../deps/jemalloc/include
|
||||
FINAL_LIBS := ../deps/jemalloc/lib/libjemalloc.a $(FINAL_LIBS)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(MALLOC),memkind)
|
||||
DEPENDENCY_TARGETS+= memkind
|
||||
@@ -343,22 +293,6 @@ ifeq ($(MALLOC),memkind)
|
||||
FINAL_LIBS := ../deps/memkind/src/.libs/libmemkind.a -lnuma $(FINAL_LIBS)
|
||||
endif
|
||||
|
||||
ifeq ($(USE_SYSTEM_HIREDIS),yes)
|
||||
HIREDIS_CFLAGS := $(shell $(PKG_CONFIG) --cflags hiredis) -DUSE_SYSTEM_HIREDIS=1
|
||||
FINAL_CFLAGS+= $(HIREDIS_CFLAGS)
|
||||
FINAL_CXXFLAGS+= $(HIREDIS_CFLAGS)
|
||||
FINAL_LIBS+= $(shell $(PKG_CONFIG) --libs hiredis)
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
HIREDIS_TLS_CFLAGS := $(shell $(PKG_CONFIG) --cflags hiredis_ssl)
|
||||
FINAL_CFLAGS+= $(HIREDIS_TLS_CFLAGS)
|
||||
FINAL_CXXFLAGS+= $(HIREDIS_TLS_CFLAGS)
|
||||
FINAL_LIBS+= $(shell $(PKG_CONFIG) --libs hiredis_ssl)
|
||||
endif
|
||||
else
|
||||
DEPENDENCY_TARGETS+= hiredis
|
||||
FINAL_CFLAGS+= -I../deps/hiredis
|
||||
FINAL_CXXFLAGS+= -I../deps/hiredis
|
||||
FINAL_LIBS+=../deps/hiredis/libhiredis.a
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CFLAGS)
|
||||
FINAL_CXXFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
|
||||
@@ -377,7 +311,6 @@ else
|
||||
endif
|
||||
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef V
|
||||
define MAKE_INSTALL
|
||||
@@ -394,7 +327,6 @@ endif
|
||||
OS := $(shell cat /etc/os-release | grep ID= | head -n 1 | cut -d'=' -f2)
|
||||
ifeq ($(OS),alpine)
|
||||
FINAL_CXXFLAGS+=-DUNW_LOCAL_ONLY
|
||||
FINAL_CXXFLAGS+=-DALPINE
|
||||
FINAL_LIBS += -lunwind
|
||||
endif
|
||||
|
||||
@@ -414,17 +346,15 @@ ENDCOLOR="\033[0m"
|
||||
|
||||
ifndef V
|
||||
QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR);
|
||||
QUIET_CP = @printf ' %b %b\n' $(CCCOLOR)COPY$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR);
|
||||
QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
|
||||
QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
|
||||
endif
|
||||
|
||||
REDIS_SERVER_NAME=keydb-server$(PROG_SUFFIX)
|
||||
REDIS_SENTINEL_NAME=keydb-sentinel$(PROG_SUFFIX)
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o t_nhash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o connection.o tls.o sha256.o motd_server.o timeout.o setcpuaffinity.o AsyncWorkQueue.o snapshot.o storage/teststorageprovider.o keydbutils.o StorageCache.o monotonic.o cli_common.o mt19937-64.o meminfo.o $(ASM_OBJ) $(STORAGE_OBJ)
|
||||
KEYDB_SERVER_OBJ=SnapshotPayloadParseState.o
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o t_nhash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o connection.o tls.o sha256.o motd.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o $(ASM_OBJ)
|
||||
REDIS_CLI_NAME=keydb-cli$(PROG_SUFFIX)
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crcspeed.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o motd_client.o monotonic.o cli_common.o mt19937-64.o $(ASM_OBJ)
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crcspeed.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o new.o motd.o monotonic.o cli_common.o mt19937-64.o $(ASM_OBJ)
|
||||
REDIS_BENCHMARK_NAME=keydb-benchmark$(PROG_SUFFIX)
|
||||
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o release.o crcspeed.o crc64.o siphash.o redis-benchmark.o storage-lite.o fastlock.o new.o monotonic.o cli_common.o mt19937-64.o $(ASM_OBJ)
|
||||
REDIS_CHECK_RDB_NAME=keydb-check-rdb$(PROG_SUFFIX)
|
||||
@@ -450,12 +380,9 @@ persist-settings: distclean
|
||||
echo STD=$(STD) >> .make-settings
|
||||
echo WARN=$(WARN) >> .make-settings
|
||||
echo OPT=$(OPT) >> .make-settings
|
||||
echo USE_SYSTEM_CONCURRENTQUEUE=$(USE_SYSTEM_CONCURRENTQUEUE) >> .make-settings
|
||||
echo MALLOC=$(MALLOC) >> .make-settings
|
||||
echo USE_SYSTEM_JEMALLOC=$(USE_SYSTEM_JEMALLOC) >> .make-settings
|
||||
echo BUILD_TLS=$(BUILD_TLS) >> .make-settings
|
||||
echo USE_SYSTEMD=$(USE_SYSTEMD) >> .make-settings
|
||||
echo USE_SYSTEM_HIREDIS=$(USE_SYSTEM_HIREDIS) >> .make-settings
|
||||
echo CFLAGS=$(CFLAGS) >> .make-settings
|
||||
echo CXXFLAGS=$(CXXFLAGS) >> .make-settings
|
||||
echo LDFLAGS=$(LDFLAGS) >> .make-settings
|
||||
@@ -484,8 +411,8 @@ endif
|
||||
@touch $@
|
||||
|
||||
# keydb-server
|
||||
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ) $(KEYDB_SERVER_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/lua/src/liblua.a $(FINAL_LIBS)
|
||||
$(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS)
|
||||
|
||||
# keydb-sentinel
|
||||
$(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME)
|
||||
@@ -501,28 +428,22 @@ $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME)
|
||||
|
||||
# keydb-cli
|
||||
$(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/linenoise/linenoise.o $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS)
|
||||
|
||||
# keydb-benchmark
|
||||
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS)
|
||||
|
||||
# keydb-diagnostic-tool
|
||||
$(KEYDB_DIAGNOSTIC_NAME): $(KEYDB_DIAGNOSTIC_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ $(FINAL_LIBS)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
|
||||
|
||||
DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(KEYDB_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)
|
||||
DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)
|
||||
-include $(DEP)
|
||||
|
||||
# Because the jemalloc.h header is generated as a part of the jemalloc build,
|
||||
# building it should complete before building any other object. Instead of
|
||||
# depending on a single artifact, build all dependencies first.
|
||||
motd_client.o: motd.cpp .make-prerequisites
|
||||
$(REDIS_CXX) -MMD -o motd_client.o -c $< -DCLIENT -fno-lto
|
||||
|
||||
motd_server.o: motd.cpp .make-prerequisites
|
||||
$(REDIS_CXX) -MMD -o motd_server.o -c $< -DSERVER
|
||||
|
||||
%.o: %.c .make-prerequisites
|
||||
$(REDIS_CC) -MMD -o $@ -c $<
|
||||
|
||||
@@ -534,8 +455,6 @@ motd_server.o: motd.cpp .make-prerequisites
|
||||
|
||||
clean:
|
||||
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) $(KEYDB_DIAGNOSTIC_NAME) *.o *.gcda *.gcno *.gcov KeyDB.info lcov-html Makefile.dep
|
||||
rm -rf storage/*.o
|
||||
rm -rf keydb-server
|
||||
rm -f $(DEP)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
#include "server.h"
|
||||
#include "SnapshotPayloadParseState.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
static const size_t SLAB_SIZE = 2*1024*1024; // Note 64 MB because we try to give 64MB to a block
|
||||
class SlabAllocator
|
||||
{
|
||||
class Slab {
|
||||
char *m_pv = nullptr;
|
||||
size_t m_cb = 0;
|
||||
size_t m_cbAllocated = 0;
|
||||
|
||||
public:
|
||||
Slab(size_t cbAllocate) {
|
||||
m_pv = (char*)zmalloc(cbAllocate);
|
||||
m_cb = cbAllocate;
|
||||
}
|
||||
|
||||
~Slab() {
|
||||
zfree(m_pv);
|
||||
}
|
||||
|
||||
Slab(Slab &&other) {
|
||||
m_pv = other.m_pv;
|
||||
m_cb = other.m_cb;
|
||||
m_cbAllocated = other.m_cbAllocated;
|
||||
other.m_pv = nullptr;
|
||||
other.m_cb = 0;
|
||||
other.m_cbAllocated = 0;
|
||||
}
|
||||
|
||||
bool canAllocate(size_t cb) const {
|
||||
return (m_cbAllocated + cb) <= m_cb;
|
||||
}
|
||||
|
||||
void *allocate(size_t cb) {
|
||||
if ((m_cbAllocated + cb) > m_cb)
|
||||
return nullptr;
|
||||
void *pvret = m_pv + m_cbAllocated;
|
||||
m_cbAllocated += cb;
|
||||
return pvret;
|
||||
}
|
||||
};
|
||||
std::vector<Slab> m_vecslabs;
|
||||
public:
|
||||
|
||||
void *allocate(size_t cb) {
|
||||
if (m_vecslabs.empty() || !m_vecslabs.back().canAllocate(cb)) {
|
||||
m_vecslabs.emplace_back(std::max(cb, SLAB_SIZE));
|
||||
}
|
||||
return m_vecslabs.back().allocate(cb);
|
||||
}
|
||||
};
|
||||
|
||||
static uint64_t dictCStringHash(const void *key) {
|
||||
return dictGenHashFunction((unsigned char*)key, strlen((char*)key));
|
||||
}
|
||||
|
||||
|
||||
static void dictKeyDestructor(void *privdata, void *key)
|
||||
{
|
||||
DICT_NOTUSED(privdata);
|
||||
sdsfree((sds)key);
|
||||
}
|
||||
|
||||
static int dictCStringCompare(void *, const void *key1, const void *key2)
|
||||
{
|
||||
int l1,l2;
|
||||
|
||||
l1 = strlen((sds)key1);
|
||||
l2 = strlen((sds)key2);
|
||||
if (l1 != l2) return 0;
|
||||
return memcmp(key1, key2, l1) == 0;
|
||||
}
|
||||
|
||||
dictType metadataLongLongDictType = {
|
||||
dictCStringHash, /* hash function */
|
||||
NULL, /* key dup */
|
||||
NULL, /* val dup */
|
||||
dictCStringCompare, /* key compare */
|
||||
dictKeyDestructor, /* key destructor */
|
||||
nullptr, /* val destructor */
|
||||
nullptr, /* allow to expand */
|
||||
nullptr /* async free destructor */
|
||||
};
|
||||
|
||||
dictType metadataDictType = {
|
||||
dictSdsHash, /* hash function */
|
||||
NULL, /* key dup */
|
||||
NULL, /* val dup */
|
||||
dictSdsKeyCompare, /* key compare */
|
||||
dictSdsDestructor, /* key destructor */
|
||||
dictSdsDestructor, /* val destructor */
|
||||
nullptr, /* allow to expand */
|
||||
nullptr /* async free destructor */
|
||||
};
|
||||
|
||||
|
||||
SnapshotPayloadParseState::ParseStageName SnapshotPayloadParseState::getNextStage() {
|
||||
if (stackParse.empty())
|
||||
return ParseStageName::Global;
|
||||
|
||||
switch (stackParse.back().name)
|
||||
{
|
||||
case ParseStageName::None:
|
||||
return ParseStageName::Global;
|
||||
|
||||
case ParseStageName::Global:
|
||||
if (stackParse.back().arraycur == 0)
|
||||
return ParseStageName::MetaData;
|
||||
else if (stackParse.back().arraycur == 1)
|
||||
return ParseStageName::Databases;
|
||||
break;
|
||||
|
||||
case ParseStageName::MetaData:
|
||||
return ParseStageName::KeyValuePair;
|
||||
|
||||
case ParseStageName::Databases:
|
||||
return ParseStageName::Dataset;
|
||||
|
||||
case ParseStageName::Dataset:
|
||||
return ParseStageName::KeyValuePair;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw "Bad protocol: corrupt state";
|
||||
}
|
||||
|
||||
void SnapshotPayloadParseState::flushQueuedKeys() {
|
||||
if (vecqueuedKeys.empty())
|
||||
return;
|
||||
serverAssert(current_database >= 0);
|
||||
|
||||
// TODO: We can't finish parse until all the work functions finish
|
||||
int idb = current_database;
|
||||
serverAssert(vecqueuedKeys.size() == vecqueuedVals.size());
|
||||
auto sizePrev = vecqueuedKeys.size();
|
||||
if (current_database < cserver.dbnum) {
|
||||
if (g_pserver->m_pstorageFactory) {
|
||||
(*insertsInFlight)++;
|
||||
std::weak_ptr<std::atomic<int>> insertsInFlightTmp = insertsInFlight; // C++ GRRRRRRRRRRRRRRRR, we don't want to capute "this" because that's dangerous
|
||||
g_pserver->asyncworkqueue->AddWorkFunction([idb, vecqueuedKeys = std::move(this->vecqueuedKeys), vecqueuedKeysCb = std::move(this->vecqueuedKeysCb), vecqueuedVals = std::move(this->vecqueuedVals), vecqueuedValsCb = std::move(this->vecqueuedValsCb), insertsInFlightTmp, pallocator = m_spallocator.release()]() mutable {
|
||||
g_pserver->db[idb]->bulkDirectStorageInsert(vecqueuedKeys.data(), vecqueuedKeysCb.data(), vecqueuedVals.data(), vecqueuedValsCb.data(), vecqueuedKeys.size());
|
||||
(*(insertsInFlightTmp.lock()))--;
|
||||
delete pallocator;
|
||||
});
|
||||
} else {
|
||||
for (size_t ival = 0; ival < vecqueuedKeys.size(); ++ival) {
|
||||
size_t offset = 0;
|
||||
auto spexpire = deserializeExpire(vecqueuedVals[ival], vecqueuedValsCb[ival], &offset);
|
||||
auto o = deserializeStoredObject(vecqueuedVals[ival] + offset, vecqueuedValsCb[ival] - offset);
|
||||
sds sdsKey = sdsnewlen(vecqueuedKeys[ival], -static_cast<ssize_t>(vecqueuedKeysCb[ival]));
|
||||
if (dbMerge(g_pserver->db[idb], sdsKey, o, false)) {
|
||||
if (spexpire != nullptr)
|
||||
g_pserver->db[idb]->setExpire(sdsKey, std::move(*spexpire));
|
||||
} else {
|
||||
sdsfree(sdsKey);
|
||||
}
|
||||
}
|
||||
vecqueuedKeys.clear();
|
||||
vecqueuedKeysCb.clear();
|
||||
vecqueuedVals.clear();
|
||||
vecqueuedValsCb.clear();
|
||||
}
|
||||
} else {
|
||||
// else drop the data
|
||||
vecqueuedKeys.clear();
|
||||
vecqueuedKeysCb.clear();
|
||||
vecqueuedVals.clear();
|
||||
vecqueuedValsCb.clear();
|
||||
// Note: m_spallocator will get free'd when overwritten below
|
||||
}
|
||||
m_spallocator = std::make_unique<SlabAllocator>();
|
||||
cbQueued = 0;
|
||||
vecqueuedKeys.reserve(sizePrev);
|
||||
vecqueuedKeysCb.reserve(sizePrev);
|
||||
vecqueuedVals.reserve(sizePrev);
|
||||
vecqueuedValsCb.reserve(sizePrev);
|
||||
serverAssert(vecqueuedKeys.empty());
|
||||
serverAssert(vecqueuedVals.empty());
|
||||
}
|
||||
|
||||
|
||||
SnapshotPayloadParseState::SnapshotPayloadParseState() {
|
||||
// This is to represent the first array the client is intended to send us
|
||||
ParseStage stage;
|
||||
stage.name = ParseStageName::None;
|
||||
stage.arraylen = 1;
|
||||
stackParse.push_back(stage);
|
||||
|
||||
dictLongLongMetaData = dictCreate(&metadataLongLongDictType, nullptr);
|
||||
dictMetaData = dictCreate(&metadataDictType, nullptr);
|
||||
insertsInFlight = std::make_shared<std::atomic<int>>();
|
||||
m_spallocator = std::make_unique<SlabAllocator>();
|
||||
}
|
||||
|
||||
SnapshotPayloadParseState::~SnapshotPayloadParseState() {
|
||||
dictRelease(dictLongLongMetaData);
|
||||
dictRelease(dictMetaData);
|
||||
}
|
||||
|
||||
const char *SnapshotPayloadParseState::getStateDebugName(ParseStage stage) {
|
||||
switch (stage.name) {
|
||||
case ParseStageName::None:
|
||||
return "None";
|
||||
|
||||
case ParseStageName::Global:
|
||||
return "Global";
|
||||
|
||||
case ParseStageName::MetaData:
|
||||
return "MetaData";
|
||||
|
||||
case ParseStageName::Databases:
|
||||
return "Databases";
|
||||
|
||||
case ParseStageName::KeyValuePair:
|
||||
return "KeyValuePair";
|
||||
|
||||
case ParseStageName::Dataset:
|
||||
return "Dataset";
|
||||
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
size_t SnapshotPayloadParseState::depth() const { return stackParse.size(); }
|
||||
|
||||
void SnapshotPayloadParseState::trimState() {
|
||||
while (!stackParse.empty() && (stackParse.back().arraycur == stackParse.back().arraylen))
|
||||
stackParse.pop_back();
|
||||
|
||||
if (stackParse.empty()) {
|
||||
flushQueuedKeys();
|
||||
while (*insertsInFlight > 0) {
|
||||
// TODO: ProcessEventsWhileBlocked
|
||||
aeReleaseLock();
|
||||
aeAcquireLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotPayloadParseState::pushArray(long long size) {
|
||||
if (stackParse.empty())
|
||||
throw "Bad Protocol: unexpected trailing data";
|
||||
|
||||
if (size == 0) {
|
||||
stackParse.back().arraycur++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (stackParse.back().name == ParseStageName::Databases) {
|
||||
flushQueuedKeys();
|
||||
current_database = stackParse.back().arraycur;
|
||||
}
|
||||
|
||||
ParseStage stage;
|
||||
stage.name = getNextStage();
|
||||
stage.arraylen = size;
|
||||
|
||||
if (stage.name == ParseStageName::Dataset) {
|
||||
g_pserver->db[current_database]->expand(stage.arraylen);
|
||||
}
|
||||
|
||||
// Note: This affects getNextStage so ensure its after
|
||||
stackParse.back().arraycur++;
|
||||
stackParse.push_back(stage);
|
||||
}
|
||||
|
||||
void SnapshotPayloadParseState::pushValue(const char *rgch, long long cch) {
|
||||
if (stackParse.empty())
|
||||
throw "Bad Protocol: unexpected trailing bulk string";
|
||||
|
||||
if (stackParse.back().arraycur >= static_cast<int>(stackParse.back().arrvalues.size()))
|
||||
throw "Bad protocol: Unexpected value";
|
||||
|
||||
auto &stage = stackParse.back();
|
||||
stage.arrvalues[stackParse.back().arraycur].first = (char*)m_spallocator->allocate(cch);
|
||||
stage.arrvalues[stackParse.back().arraycur].second = cch;
|
||||
memcpy(stage.arrvalues[stackParse.back().arraycur].first, rgch, cch);
|
||||
stage.arraycur++;
|
||||
switch (stage.name) {
|
||||
case ParseStageName::KeyValuePair:
|
||||
if (stackParse.size() < 2)
|
||||
throw "Bad Protocol: unexpected bulk string";
|
||||
if (stackParse[stackParse.size()-2].name == ParseStageName::MetaData) {
|
||||
if (stage.arraycur == 2) {
|
||||
// We loaded both pairs
|
||||
if (stage.arrvalues[0].first == nullptr || stage.arrvalues[1].first == nullptr)
|
||||
throw "Bad Protocol: Got array when expecing a string"; // A baddy could make us derefence the vector when its too small
|
||||
|
||||
if (!strcasecmp(stage.arrvalues[0].first, "lua")) {
|
||||
/* Load the script back in memory. */
|
||||
robj *auxval = createStringObject(stage.arrvalues[1].first, stage.arrvalues[1].second);
|
||||
if (luaCreateFunction(NULL,g_pserver->lua,auxval) == NULL) {
|
||||
throw "Can't load Lua script";
|
||||
}
|
||||
} else {
|
||||
dictAdd(dictMetaData, sdsnewlen(stage.arrvalues[0].first, stage.arrvalues[0].second), sdsnewlen(stage.arrvalues[1].first, stage.arrvalues[1].second));
|
||||
}
|
||||
}
|
||||
} else if (stackParse[stackParse.size()-2].name == ParseStageName::Dataset) {
|
||||
if (stage.arraycur == 2) {
|
||||
// We loaded both pairs
|
||||
if (stage.arrvalues[0].first == nullptr || stage.arrvalues[1].first == nullptr)
|
||||
throw "Bad Protocol: Got array when expecing a string"; // A baddy could make us derefence the vector when its too small
|
||||
vecqueuedKeys.push_back(stage.arrvalues[0].first);
|
||||
vecqueuedKeysCb.push_back(stage.arrvalues[0].second);
|
||||
vecqueuedVals.push_back(stage.arrvalues[1].first);
|
||||
vecqueuedValsCb.push_back(stage.arrvalues[1].second);
|
||||
stage.arrvalues[0].first = nullptr;
|
||||
stage.arrvalues[1].first = nullptr;
|
||||
cbQueued += vecqueuedKeysCb.back();
|
||||
cbQueued += vecqueuedValsCb.back();
|
||||
if (cbQueued >= queuedBatchLimit)
|
||||
flushQueuedKeys();
|
||||
}
|
||||
} else {
|
||||
throw "Bad Protocol: unexpected bulk string";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Bad Protocol: unexpected bulk string out of KV pair";
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotPayloadParseState::pushValue(long long value) {
|
||||
if (stackParse.empty())
|
||||
throw "Bad Protocol: unexpected integer value";
|
||||
|
||||
stackParse.back().arraycur++;
|
||||
|
||||
if (stackParse.back().arraycur != 2 || stackParse.back().arrvalues[0].first == nullptr)
|
||||
throw "Bad Protocol: unexpected integer value";
|
||||
|
||||
dictEntry *de = dictAddRaw(dictLongLongMetaData, sdsnewlen(stackParse.back().arrvalues[0].first, stackParse.back().arrvalues[0].second), nullptr);
|
||||
if (de == nullptr)
|
||||
throw "Bad Protocol: metadata field sent twice";
|
||||
de->v.s64 = value;
|
||||
}
|
||||
|
||||
long long SnapshotPayloadParseState::getMetaDataLongLong(const char *szField) const {
|
||||
dictEntry *de = dictFind(dictLongLongMetaData, szField);
|
||||
|
||||
if (de == nullptr) {
|
||||
serverLog(LL_WARNING, "Master did not send field: %s", szField);
|
||||
throw false;
|
||||
}
|
||||
return de->v.s64;
|
||||
}
|
||||
|
||||
sds SnapshotPayloadParseState::getMetaDataStr(const char *szField) const {
|
||||
sdsstring str(szField, strlen(szField));
|
||||
|
||||
dictEntry *de = dictFind(dictMetaData, str.get());
|
||||
if (de == nullptr) {
|
||||
serverLog(LL_WARNING, "Master did not send field: %s", szField);
|
||||
throw false;
|
||||
}
|
||||
return (sds)de->v.val;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#pragma once
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
class SlabAllocator;
|
||||
|
||||
class SnapshotPayloadParseState {
|
||||
enum class ParseStageName {
|
||||
None,
|
||||
Global,
|
||||
MetaData,
|
||||
Databases,
|
||||
Dataset,
|
||||
KeyValuePair,
|
||||
};
|
||||
struct ParseStage {
|
||||
ParseStageName name;
|
||||
long long arraylen = 0;
|
||||
long long arraycur = 0;
|
||||
std::array<std::pair<char*, size_t>, 2> arrvalues;
|
||||
|
||||
ParseStage() {
|
||||
for (auto &pair : arrvalues) {
|
||||
pair.first = nullptr;
|
||||
pair.second = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<ParseStage> stackParse;
|
||||
|
||||
|
||||
std::vector<char*> vecqueuedKeys;
|
||||
std::vector<size_t> vecqueuedKeysCb;
|
||||
std::vector<char*> vecqueuedVals;
|
||||
std::vector<size_t> vecqueuedValsCb;
|
||||
|
||||
|
||||
std::shared_ptr<std::atomic<int>> insertsInFlight;
|
||||
std::unique_ptr<SlabAllocator> m_spallocator;
|
||||
dict *dictLongLongMetaData = nullptr;
|
||||
dict *dictMetaData = nullptr;
|
||||
size_t cbQueued = 0;
|
||||
static const size_t queuedBatchLimit = 64*1024*1024; // 64 MB
|
||||
int current_database = -1;
|
||||
|
||||
ParseStageName getNextStage();
|
||||
void flushQueuedKeys();
|
||||
|
||||
public:
|
||||
SnapshotPayloadParseState();
|
||||
~SnapshotPayloadParseState();
|
||||
|
||||
long long getMetaDataLongLong(const char *field) const;
|
||||
sds getMetaDataStr(const char *szField) const;
|
||||
|
||||
static const char *getStateDebugName(ParseStage stage);
|
||||
|
||||
size_t depth() const;
|
||||
|
||||
void trimState();
|
||||
void pushArray(long long size);
|
||||
void pushValue(const char *rgch, long long cch);
|
||||
void pushValue(long long value);
|
||||
bool shouldThrottle() const { return *insertsInFlight > (cserver.cthreads*4); }
|
||||
};
|
||||
@@ -1,236 +0,0 @@
|
||||
#include "server.h"
|
||||
|
||||
uint64_t hashPassthrough(const void *hash) {
|
||||
return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(hash));
|
||||
}
|
||||
|
||||
int hashCompare(void *, const void *key1, const void *key2) {
|
||||
auto diff = (reinterpret_cast<uintptr_t>(key1) - reinterpret_cast<uintptr_t>(key2));
|
||||
return !diff;
|
||||
}
|
||||
|
||||
dictType dbStorageCacheType = {
|
||||
hashPassthrough, /* hash function */
|
||||
NULL, /* key dup */
|
||||
NULL, /* val dup */
|
||||
hashCompare, /* key compare */
|
||||
NULL, /* key destructor */
|
||||
NULL /* val destructor */
|
||||
};
|
||||
|
||||
StorageCache::StorageCache(IStorage *storage, bool fCache)
|
||||
: m_spstorage(storage)
|
||||
{
|
||||
if (!g_pserver->flash_disable_key_cache && fCache)
|
||||
m_pdict = dictCreate(&dbStorageCacheType, nullptr);
|
||||
}
|
||||
|
||||
StorageCache::~StorageCache()
|
||||
{
|
||||
if (m_pdict != nullptr)
|
||||
dictRelease(m_pdict);
|
||||
}
|
||||
|
||||
void StorageCache::clear(void(callback)(void*))
|
||||
{
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
if (m_pdict != nullptr)
|
||||
dictEmpty(m_pdict, callback);
|
||||
m_spstorage->clear();
|
||||
m_collisionCount = 0;
|
||||
}
|
||||
|
||||
void StorageCache::clearAsync()
|
||||
{
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
if (count() == 0)
|
||||
return;
|
||||
if (m_pdict != nullptr) {
|
||||
dict *dSav = m_pdict;
|
||||
m_pdict = dictCreate(&dbStorageCacheType, nullptr);
|
||||
g_pserver->asyncworkqueue->AddWorkFunction([dSav]{
|
||||
dictEmpty(dSav, nullptr);
|
||||
});
|
||||
}
|
||||
m_spstorage->clear();
|
||||
m_collisionCount = 0;
|
||||
}
|
||||
|
||||
void StorageCache::cacheKey(sds key)
|
||||
{
|
||||
if (m_pdict == nullptr)
|
||||
return;
|
||||
uintptr_t hash = dictSdsHash(key);
|
||||
if (dictAdd(m_pdict, reinterpret_cast<void*>(hash), (void*)1) != DICT_OK) {
|
||||
dictEntry *de = dictFind(m_pdict, reinterpret_cast<void*>(hash));
|
||||
serverAssert(de != nullptr);
|
||||
de->v.s64++;
|
||||
m_collisionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
void StorageCache::cacheKey(const char *rgch, size_t cch)
|
||||
{
|
||||
if (m_pdict == nullptr)
|
||||
return;
|
||||
uintptr_t hash = dictGenHashFunction(rgch, (int)cch);
|
||||
if (dictAdd(m_pdict, reinterpret_cast<void*>(hash), (void*)1) != DICT_OK) {
|
||||
dictEntry *de = dictFind(m_pdict, reinterpret_cast<void*>(hash));
|
||||
serverAssert(de != nullptr);
|
||||
de->v.s64++;
|
||||
m_collisionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
bool StorageCache::erase(sds key)
|
||||
{
|
||||
unsigned long long when = 0;
|
||||
m_spstorage->retrieve(key, sdslen(key), [&when](const char *, size_t, const void * data, size_t cbdata) {
|
||||
auto e = deserializeExpire((const char *)data, cbdata, nullptr);
|
||||
if (e != nullptr)
|
||||
when = e->when();
|
||||
});
|
||||
bool result = m_spstorage->erase(key, sdslen(key));
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
if (result)
|
||||
{
|
||||
if (m_pdict != nullptr)
|
||||
{
|
||||
uint64_t hash = dictSdsHash(key);
|
||||
dictEntry *de = dictFind(m_pdict, reinterpret_cast<void*>(hash));
|
||||
serverAssert(de != nullptr);
|
||||
de->v.s64--;
|
||||
serverAssert(de->v.s64 >= 0);
|
||||
if (de->v.s64 == 0) {
|
||||
dictDelete(m_pdict, reinterpret_cast<void*>(hash));
|
||||
} else {
|
||||
m_collisionCount--;
|
||||
}
|
||||
}
|
||||
if (when != 0) {
|
||||
m_spstorage->removeExpire(key, sdslen(key), when);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void StorageCache::insert(sds key, const void *data, size_t cbdata, bool fOverwrite)
|
||||
{
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
if (!fOverwrite && m_pdict != nullptr)
|
||||
{
|
||||
cacheKey(key);
|
||||
}
|
||||
ul.unlock();
|
||||
m_spstorage->insert(key, sdslen(key), (void*)data, cbdata, fOverwrite);
|
||||
auto e = deserializeExpire((const char *)data, cbdata, nullptr);
|
||||
if (e != nullptr)
|
||||
m_spstorage->setExpire(key, sdslen(key), e->when());
|
||||
}
|
||||
|
||||
long _dictKeyIndex(dict *d, const void *key, uint64_t hash, dictEntry **existing);
|
||||
void StorageCache::bulkInsert(char **rgkeys, size_t *rgcbkeys, char **rgvals, size_t *rgcbvals, size_t celem)
|
||||
{
|
||||
std::vector<dictEntry*> vechashes;
|
||||
if (m_pdict != nullptr) {
|
||||
vechashes.reserve(celem);
|
||||
}
|
||||
|
||||
for (size_t ielem = 0; ielem < celem; ++ielem) {
|
||||
if (m_pdict != nullptr) {
|
||||
dictEntry *de = (dictEntry*)zmalloc(sizeof(dictEntry));
|
||||
de->key = (void*)dictGenHashFunction(rgkeys[ielem], (int)rgcbkeys[ielem]);
|
||||
de->v.u64 = 1;
|
||||
vechashes.push_back(de);
|
||||
}
|
||||
auto e = deserializeExpire(rgvals[ielem], rgcbvals[ielem], nullptr);
|
||||
if (e != nullptr)
|
||||
m_spstorage->setExpire(rgkeys[ielem], rgcbkeys[ielem], e->when());
|
||||
}
|
||||
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
bulkInsertsInProgress++;
|
||||
if (m_pdict != nullptr) {
|
||||
for (dictEntry *de : vechashes) {
|
||||
if (dictIsRehashing(m_pdict)) dictRehash(m_pdict,1);
|
||||
/* Get the index of the new element, or -1 if
|
||||
* the element already exists. */
|
||||
long index;
|
||||
if ((index = _dictKeyIndex(m_pdict, de->key, (uint64_t)de->key, nullptr)) == -1) {
|
||||
dictEntry *deLocal = dictFind(m_pdict, de->key);
|
||||
serverAssert(deLocal != nullptr);
|
||||
deLocal->v.s64++;
|
||||
m_collisionCount++;
|
||||
zfree(de);
|
||||
} else {
|
||||
int iht = dictIsRehashing(m_pdict) ? 1 : 0;
|
||||
de->next = m_pdict->ht[iht].table[index];
|
||||
m_pdict->ht[iht].table[index] = de;
|
||||
m_pdict->ht[iht].used++;
|
||||
}
|
||||
}
|
||||
}
|
||||
ul.unlock();
|
||||
|
||||
m_spstorage->bulkInsert(rgkeys, rgcbkeys, rgvals, rgcbvals, celem);
|
||||
|
||||
bulkInsertsInProgress--;
|
||||
}
|
||||
|
||||
const StorageCache *StorageCache::clone()
|
||||
{
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
// Clones never clone the cache
|
||||
StorageCache *cacheNew = new StorageCache(const_cast<IStorage*>(m_spstorage->clone()), false /*fCache*/);
|
||||
return cacheNew;
|
||||
}
|
||||
|
||||
void StorageCache::expand(uint64_t slots)
|
||||
{
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
if (m_pdict) {
|
||||
dictExpand(m_pdict, slots);
|
||||
}
|
||||
}
|
||||
|
||||
void StorageCache::retrieve(sds key, IStorage::callbackSingle fn) const
|
||||
{
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
if (m_pdict != nullptr)
|
||||
{
|
||||
uint64_t hash = dictSdsHash(key);
|
||||
dictEntry *de = dictFind(m_pdict, reinterpret_cast<void*>(hash));
|
||||
|
||||
if (de == nullptr)
|
||||
return; // Not found
|
||||
}
|
||||
ul.unlock();
|
||||
m_spstorage->retrieve(key, sdslen(key), fn);
|
||||
}
|
||||
|
||||
size_t StorageCache::count() const
|
||||
{
|
||||
std::unique_lock<fastlock> ul(m_lock, std::defer_lock);
|
||||
bool fLocked = ul.try_lock();
|
||||
size_t count = m_spstorage->count();
|
||||
if (m_pdict != nullptr && fLocked) {
|
||||
serverAssert(bulkInsertsInProgress.load(std::memory_order_seq_cst) || count == (dictSize(m_pdict) + m_collisionCount));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void StorageCache::beginWriteBatch() {
|
||||
serverAssert(GlobalLocksAcquired()); // Otherwise we deadlock
|
||||
m_spstorage->beginWriteBatch();
|
||||
}
|
||||
|
||||
void StorageCache::emergencyFreeCache() {
|
||||
std::unique_lock<fastlock> ul(m_lock);
|
||||
dict *d = m_pdict;
|
||||
m_pdict = nullptr;
|
||||
if (d != nullptr) {
|
||||
g_pserver->asyncworkqueue->AddWorkFunction([d]{
|
||||
dictRelease(d);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#pragma once
|
||||
#include "sds.h"
|
||||
|
||||
class StorageCache
|
||||
{
|
||||
std::shared_ptr<IStorage> m_spstorage;
|
||||
dict *m_pdict = nullptr;
|
||||
int m_collisionCount = 0;
|
||||
mutable fastlock m_lock {"StorageCache"};
|
||||
std::atomic<int> bulkInsertsInProgress;
|
||||
|
||||
StorageCache(IStorage *storage, bool fNoCache);
|
||||
|
||||
void cacheKey(sds key);
|
||||
void cacheKey(const char *rgchKey, size_t cchKey);
|
||||
|
||||
struct load_iter_data
|
||||
{
|
||||
StorageCache *cache;
|
||||
IStorageFactory::key_load_iterator itrParent;
|
||||
void *privdataParent;
|
||||
};
|
||||
static void key_load_itr(const char *rgchKey, size_t cchKey, void *privdata)
|
||||
{
|
||||
load_iter_data *data = (load_iter_data*)privdata;
|
||||
data->cache->cacheKey(rgchKey, cchKey);
|
||||
if (data->itrParent)
|
||||
data->itrParent(rgchKey, cchKey, data->privdataParent);
|
||||
}
|
||||
|
||||
public:
|
||||
~StorageCache();
|
||||
|
||||
static StorageCache *create(IStorageFactory *pfactory, int db, IStorageFactory::key_load_iterator fn, void *privdata) {
|
||||
StorageCache *cache = new StorageCache(nullptr, pfactory->FSlow() /*fCache*/);
|
||||
load_iter_data data = {cache, fn, privdata};
|
||||
cache->m_spstorage = std::shared_ptr<IStorage>(pfactory->create(db, key_load_itr, (void*)&data));
|
||||
return cache;
|
||||
}
|
||||
|
||||
void clear(void(callback)(void*));
|
||||
void clearAsync();
|
||||
void insert(sds key, const void *data, size_t cbdata, bool fOverwrite);
|
||||
void bulkInsert(char **rgkeys, size_t *rgcbkeys, char **rgvals, size_t *rgcbvals, size_t celem);
|
||||
void retrieve(sds key, IStorage::callbackSingle fn) const;
|
||||
bool erase(sds key);
|
||||
void emergencyFreeCache();
|
||||
bool keycacheIsEnabled() const { return m_pdict != nullptr; }
|
||||
void expand(uint64_t slots);
|
||||
|
||||
bool enumerate(IStorage::callback fn) const { return m_spstorage->enumerate(fn); }
|
||||
bool enumerate_hashslot(IStorage::callback fn, unsigned int hashslot) const { return m_spstorage->enumerate_hashslot(fn, hashslot); }
|
||||
|
||||
std::vector<std::string> getExpirationCandidates(unsigned int count) { return m_spstorage->getExpirationCandidates(count); }
|
||||
std::vector<std::string> getEvictionCandidates(unsigned int count) { return m_spstorage->getEvictionCandidates(count); }
|
||||
void setExpire(const char *key, size_t cchKey, long long expire) { m_spstorage->setExpire(key, cchKey, expire); }
|
||||
void removeExpire(const char *key, size_t cchKey, long long expire) { m_spstorage->removeExpire(key, cchKey, expire); }
|
||||
|
||||
void beginWriteBatch();
|
||||
void endWriteBatch() { m_spstorage->endWriteBatch(); }
|
||||
void batch_lock() { return m_spstorage->batch_lock(); }
|
||||
void batch_unlock() { return m_spstorage->batch_unlock(); }
|
||||
|
||||
void flush() { m_spstorage->flush(); }
|
||||
|
||||
size_t count() const;
|
||||
|
||||
const StorageCache *clone();
|
||||
};
|
||||
+12
-72
@@ -113,8 +113,6 @@ enum class AE_ASYNC_OP
|
||||
PostCppFunction,
|
||||
DeleteFileEvent,
|
||||
CreateFileEvent,
|
||||
PostAsynDBFunction,
|
||||
|
||||
};
|
||||
|
||||
struct aeCommand
|
||||
@@ -127,18 +125,12 @@ struct aeCommand
|
||||
aePostFunctionProc *proc;
|
||||
aeFileProc *fproc;
|
||||
std::function<void()> *pfn;
|
||||
aePostFunctionTokenProc* tproc;
|
||||
|
||||
};
|
||||
void *clientData;
|
||||
};
|
||||
#ifdef PIPE_BUF
|
||||
static_assert(sizeof(aeCommand) <= PIPE_BUF, "aeCommand must be small enough to send atomically through a pipe");
|
||||
#endif
|
||||
|
||||
void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
|
||||
{
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
|
||||
aeCommand cmd;
|
||||
for (;;)
|
||||
{
|
||||
@@ -160,7 +152,8 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
|
||||
|
||||
case AE_ASYNC_OP::PostFunction:
|
||||
{
|
||||
if (cmd.fLock && !ulock.owns_lock()) {
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
|
||||
if (cmd.fLock) {
|
||||
g_forkLock.releaseRead();
|
||||
ulock.lock();
|
||||
g_forkLock.acquireRead();
|
||||
@@ -171,7 +164,8 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
|
||||
|
||||
case AE_ASYNC_OP::PostCppFunction:
|
||||
{
|
||||
if (cmd.fLock && !ulock.owns_lock()) {
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
|
||||
if (cmd.fLock) {
|
||||
g_forkLock.releaseRead();
|
||||
ulock.lock();
|
||||
g_forkLock.acquireRead();
|
||||
@@ -179,17 +173,6 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
|
||||
(*cmd.pfn)();
|
||||
|
||||
delete cmd.pfn;
|
||||
break;
|
||||
}
|
||||
case AE_ASYNC_OP::PostAsynDBFunction:
|
||||
{ //added to support async api IStorage
|
||||
if (cmd.fLock && !ulock.owns_lock()) {
|
||||
g_forkLock.releaseRead();
|
||||
ulock.lock();
|
||||
g_forkLock.acquireRead();
|
||||
}
|
||||
((aePostFunctionTokenProc*)cmd.tproc)(eventLoop,(StorageToken*)cmd.clientData);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -271,20 +254,6 @@ int aePostFunction(aeEventLoop *eventLoop, aePostFunctionProc *proc, void *arg)
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
int aePostFunction(aeEventLoop *eventLoop, aePostFunctionTokenProc *proc, StorageToken *token)
|
||||
{
|
||||
//added to support async api IStorage
|
||||
aeCommand cmd = {};
|
||||
cmd.op = AE_ASYNC_OP::PostAsynDBFunction;
|
||||
cmd.tproc = proc;
|
||||
cmd.clientData = (void*)token;
|
||||
cmd.fLock = true;
|
||||
auto size = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
|
||||
if (size != sizeof(cmd))
|
||||
return AE_ERR;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fLock, bool fForceQueue)
|
||||
{
|
||||
if (eventLoop == g_eventLoopThisThread && !fForceQueue)
|
||||
@@ -295,7 +264,7 @@ int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fLock,
|
||||
|
||||
aeCommand cmd = {};
|
||||
cmd.op = AE_ASYNC_OP::PostCppFunction;
|
||||
cmd.pfn = new std::function<void()>(fn);
|
||||
cmd.pfn = new (MALLOC_LOCAL) std::function<void()>(fn);
|
||||
cmd.fLock = fLock;
|
||||
|
||||
auto size = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
|
||||
@@ -745,6 +714,11 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
if (eventLoop->flags & AE_DONT_WAIT) {
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
tvp = &tv;
|
||||
}
|
||||
|
||||
if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP) {
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
|
||||
if (!(eventLoop->beforesleepFlags & AE_SLEEP_THREADSAFE)) {
|
||||
@@ -755,11 +729,6 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
eventLoop->beforesleep(eventLoop);
|
||||
}
|
||||
|
||||
if (eventLoop->flags & AE_DONT_WAIT) {
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
tvp = &tv;
|
||||
}
|
||||
|
||||
/* Call the multiplexing API, will return only on timeout or when
|
||||
* some event fires. */
|
||||
numevents = aeApiPoll(eventLoop, tvp);
|
||||
@@ -840,7 +809,6 @@ void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep,
|
||||
}
|
||||
|
||||
thread_local spin_worker tl_worker = nullptr;
|
||||
thread_local bool fOwnLockOverride = false;
|
||||
void setAeLockSetThreadSpinWorker(spin_worker worker)
|
||||
{
|
||||
tl_worker = worker;
|
||||
@@ -878,42 +846,14 @@ void aeReleaseLock()
|
||||
g_lock.unlock();
|
||||
}
|
||||
|
||||
void aeSetThreadOwnsLockOverride(int fOverride)
|
||||
{
|
||||
fOwnLockOverride = fOverride;
|
||||
}
|
||||
|
||||
void aeReleaseForkLock()
|
||||
{
|
||||
g_forkLock.downgradeWrite();
|
||||
}
|
||||
|
||||
void aeForkLockInChild()
|
||||
{
|
||||
g_forkLock.setMulti(false);
|
||||
}
|
||||
|
||||
int aeThreadOwnsLock()
|
||||
{
|
||||
return fOwnLockOverride || g_lock.fOwnLock();
|
||||
}
|
||||
|
||||
int aeLockContested(int threshold)
|
||||
{
|
||||
ticket ticketT;
|
||||
__atomic_load(&g_lock.m_ticket.u, &ticketT.u, __ATOMIC_RELAXED);
|
||||
return ticketT.m_active < static_cast<uint16_t>(ticketT.m_avail - threshold);
|
||||
}
|
||||
|
||||
int aeLockContention()
|
||||
{
|
||||
ticket ticketT;
|
||||
__atomic_load(&g_lock.m_ticket.u, &ticketT.u, __ATOMIC_RELAXED);
|
||||
int32_t avail = ticketT.m_avail;
|
||||
int32_t active = ticketT.m_active;
|
||||
if (avail < active)
|
||||
avail += 0x10000;
|
||||
return avail - active;
|
||||
return g_lock.fOwnLock();
|
||||
}
|
||||
|
||||
void aeClosePipesForForkChild(aeEventLoop *el)
|
||||
@@ -922,4 +862,4 @@ void aeClosePipesForForkChild(aeEventLoop *el)
|
||||
el->fdCmdRead = -1;
|
||||
close(el->fdCmdWrite);
|
||||
el->fdCmdWrite = -1;
|
||||
}
|
||||
}
|
||||
@@ -72,15 +72,13 @@ extern "C" {
|
||||
#define AE_NOTUSED(V) ((void) V)
|
||||
|
||||
struct aeEventLoop;
|
||||
|
||||
/* Types and data structures */
|
||||
typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
|
||||
typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);
|
||||
typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);
|
||||
typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
|
||||
typedef void aePostFunctionProc(void *pvArgs);
|
||||
//added to support async api IStorage
|
||||
struct StorageToken;
|
||||
typedef void aePostFunctionTokenProc(struct aeEventLoop *el, struct StorageToken *token);
|
||||
|
||||
/* File event structure */
|
||||
typedef struct aeFileEvent {
|
||||
@@ -136,8 +134,6 @@ int aePostFunction(aeEventLoop *eventLoop, aePostFunctionProc *proc, void *arg);
|
||||
#ifdef __cplusplus
|
||||
} // EXTERN C
|
||||
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fLock = true, bool fForceQueue = false);
|
||||
//added to support async api IStorage
|
||||
int aePostFunction(aeEventLoop *eventLoop, aePostFunctionTokenProc *proc, StorageToken *token);
|
||||
extern "C" {
|
||||
#endif
|
||||
void aeDeleteEventLoop(aeEventLoop *eventLoop);
|
||||
@@ -175,11 +171,7 @@ int aeTryAcquireLock(int fWeak);
|
||||
void aeThreadOffline();
|
||||
void aeReleaseLock();
|
||||
void aeReleaseForkLock();
|
||||
void aeForkLockInChild();
|
||||
int aeThreadOwnsLock();
|
||||
void aeSetThreadOwnsLockOverride(int fOverride);
|
||||
int aeLockContested(int threshold);
|
||||
int aeLockContention(); // returns the number of instantaneous threads waiting on the lock
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
|
||||
if (c != nullptr)
|
||||
{
|
||||
serverAssert(!m_fArmed);
|
||||
serverAssert(c->lock.fOwnLock());
|
||||
|
||||
if (!aeTryAcquireLock(true /*fWeak*/)) // avoid locking the client if we can
|
||||
|
||||
+1
-1
@@ -441,7 +441,7 @@ static int _anetTcpServer(char *err, int port, const char *bindaddr, int af, int
|
||||
char _port[6]; /* strlen("65535") */
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
|
||||
snprintf(_port,sizeof(_port),"%d",port);
|
||||
snprintf(_port,6,"%d",port);
|
||||
memset(&hints,0,sizeof(hints));
|
||||
hints.ai_family = af;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
+51
-58
@@ -285,7 +285,7 @@ int startAppendOnly(void) {
|
||||
strerror(errno));
|
||||
return C_ERR;
|
||||
}
|
||||
if (hasActiveChildProcessOrBGSave() && g_pserver->child_type != CHILD_TYPE_AOF) {
|
||||
if (hasActiveChildProcess() && g_pserver->child_type != CHILD_TYPE_AOF) {
|
||||
g_pserver->aof_rewrite_scheduled = 1;
|
||||
serverLog(LL_WARNING,"AOF was enabled but there is already another background operation. An AOF background was scheduled to start when possible.");
|
||||
} else {
|
||||
@@ -438,7 +438,7 @@ void flushAppendOnlyFile(int force) {
|
||||
* useful for graphing / monitoring purposes. */
|
||||
if (sync_in_progress) {
|
||||
latencyAddSampleIfNeeded("aof-write-pending-fsync",latency);
|
||||
} else if (hasActiveChildProcessOrBGSave()) {
|
||||
} else if (hasActiveChildProcess()) {
|
||||
latencyAddSampleIfNeeded("aof-write-active-child",latency);
|
||||
} else {
|
||||
latencyAddSampleIfNeeded("aof-write-alone",latency);
|
||||
@@ -535,7 +535,7 @@ void flushAppendOnlyFile(int force) {
|
||||
try_fsync:
|
||||
/* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are
|
||||
* children doing I/O in the background. */
|
||||
if (g_pserver->aof_no_fsync_on_rewrite && hasActiveChildProcessOrBGSave())
|
||||
if (g_pserver->aof_no_fsync_on_rewrite && hasActiveChildProcess())
|
||||
return;
|
||||
|
||||
/* Perform the fsync if needed. */
|
||||
@@ -752,7 +752,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
|
||||
* accumulate the differences between the child DB and the current one
|
||||
* in a buffer, so that when the child process will do its work we
|
||||
* can append the differences to the new append only file. */
|
||||
if (hasActiveChildProcess() && g_pserver->child_type == CHILD_TYPE_AOF)
|
||||
if (g_pserver->child_type == CHILD_TYPE_AOF)
|
||||
aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));
|
||||
|
||||
sdsfree(buf);
|
||||
@@ -765,7 +765,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
|
||||
/* In Redis commands are always executed in the context of a client, so in
|
||||
* order to load the append only file we need to create a fake client. */
|
||||
struct client *createAOFClient(void) {
|
||||
struct client *c = new client();
|
||||
struct client *c =(client*) zmalloc(sizeof(*c), MALLOC_LOCAL);
|
||||
|
||||
selectDb(c,0);
|
||||
c->id = CLIENT_ID_AOF; /* So modules can identify it's the AOF client. */
|
||||
@@ -778,6 +778,7 @@ struct client *createAOFClient(void) {
|
||||
c->argv = NULL;
|
||||
c->original_argc = 0;
|
||||
c->original_argv = NULL;
|
||||
c->argv_len_sum = 0;
|
||||
c->bufpos = 0;
|
||||
c->fPendingAsyncWrite = FALSE;
|
||||
c->fPendingAsyncWriteHandler = FALSE;
|
||||
@@ -805,7 +806,6 @@ struct client *createAOFClient(void) {
|
||||
c->sockname = NULL;
|
||||
c->resp = 2;
|
||||
c->user = NULL;
|
||||
c->mvccCheckpoint = 0;
|
||||
listSetFreeMethod(c->reply,freeClientReplyValue);
|
||||
listSetDupMethod(c->reply,dupClientReplyValue);
|
||||
fastlock_init(&c->lock, "fake client");
|
||||
@@ -820,7 +820,7 @@ void freeFakeClientArgv(struct client *c) {
|
||||
for (j = 0; j < c->argc; j++)
|
||||
decrRefCount(c->argv[j]);
|
||||
zfree(c->argv);
|
||||
c->argv_len_sumActive = 0;
|
||||
c->argv_len_sum = 0;
|
||||
}
|
||||
|
||||
void freeFakeClient(struct client *c) {
|
||||
@@ -831,7 +831,7 @@ void freeFakeClient(struct client *c) {
|
||||
freeClientOriginalArgv(c);
|
||||
fastlock_unlock(&c->lock);
|
||||
fastlock_free(&c->lock);
|
||||
delete c;
|
||||
zfree(c);
|
||||
}
|
||||
|
||||
/* Replay the append log file. On success C_OK is returned. On non fatal
|
||||
@@ -870,11 +870,6 @@ int loadAppendOnlyFile(char *filename) {
|
||||
fakeClient = createAOFClient();
|
||||
startLoadingFile(fp, filename, RDBFLAGS_AOF_PREAMBLE);
|
||||
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
{
|
||||
g_pserver->db[idb]->trackChanges(true);
|
||||
}
|
||||
|
||||
/* Check if this AOF file has an RDB preamble. In that case we need to
|
||||
* load the RDB file and later continue loading the AOF tail. */
|
||||
char sig[5]; /* "REDIS" */
|
||||
@@ -1010,11 +1005,6 @@ int loadAppendOnlyFile(char *filename) {
|
||||
}
|
||||
|
||||
loaded_ok: /* DB loaded, cleanup and return C_OK to the caller. */
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
{
|
||||
if (g_pserver->db[idb]->processChanges(false))
|
||||
g_pserver->db[idb]->commitChanges();
|
||||
}
|
||||
fclose(fp);
|
||||
freeFakeClient(fakeClient);
|
||||
g_pserver->aof_state = old_aof_state;
|
||||
@@ -1548,6 +1538,8 @@ ssize_t aofReadDiffFromParent(void) {
|
||||
}
|
||||
|
||||
int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
dictIterator *di = NULL;
|
||||
dictEntry *de;
|
||||
size_t processed = 0;
|
||||
int j;
|
||||
long key_count = 0;
|
||||
@@ -1555,62 +1547,69 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
|
||||
for (j = 0; j < cserver.dbnum; j++) {
|
||||
char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";
|
||||
redisDb *db = g_pserver->db[j];
|
||||
if (db->size() == 0) continue;
|
||||
redisDb *db = g_pserver->db+j;
|
||||
dict *d = db->dict;
|
||||
if (dictSize(d) == 0) continue;
|
||||
di = dictGetSafeIterator(d);
|
||||
|
||||
/* SELECT the new DB */
|
||||
if (rioWrite(aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr;
|
||||
if (rioWriteBulkLongLong(aof,j) == 0) goto werr;
|
||||
|
||||
/* Iterate this DB writing every entry */
|
||||
bool fComplete = db->iterate([&](const char *keystr, robj *o)->bool{
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
sds keystr;
|
||||
redisObjectStack key;
|
||||
initStaticStringObject(key,(sds)keystr);
|
||||
robj *o = nullptr;
|
||||
|
||||
keystr = (sds)dictGetKey(de);
|
||||
o = (robj*)dictGetVal(de);
|
||||
initStaticStringObject(key,keystr);
|
||||
|
||||
expireEntry *pexpire = getExpire(db,&key);
|
||||
|
||||
/* Save the key and associated value */
|
||||
if (o->type == OBJ_STRING) {
|
||||
/* Emit a SET command */
|
||||
char cmd[]="*3\r\n$3\r\nSET\r\n";
|
||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) return false;
|
||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) goto werr;
|
||||
/* Key and value */
|
||||
if (rioWriteBulkObject(aof,&key) == 0) return false;
|
||||
if (rioWriteBulkObject(aof,o) == 0) return false;
|
||||
if (rioWriteBulkObject(aof,&key) == 0) goto werr;
|
||||
if (rioWriteBulkObject(aof,o) == 0) goto werr;
|
||||
} else if (o->type == OBJ_LIST) {
|
||||
if (rewriteListObject(aof,&key,o) == 0) return false;
|
||||
if (rewriteListObject(aof,&key,o) == 0) goto werr;
|
||||
} else if (o->type == OBJ_SET) {
|
||||
if (rewriteSetObject(aof,&key,o) == 0) return false;
|
||||
if (rewriteSetObject(aof,&key,o) == 0) goto werr;
|
||||
} else if (o->type == OBJ_ZSET) {
|
||||
if (rewriteSortedSetObject(aof,&key,o) == 0) return false;
|
||||
if (rewriteSortedSetObject(aof,&key,o) == 0) goto werr;
|
||||
} else if (o->type == OBJ_HASH) {
|
||||
if (rewriteHashObject(aof,&key,o) == 0) return false;
|
||||
if (rewriteHashObject(aof,&key,o) == 0) goto werr;
|
||||
} else if (o->type == OBJ_STREAM) {
|
||||
if (rewriteStreamObject(aof,&key,o) == 0) return false;
|
||||
if (rewriteStreamObject(aof,&key,o) == 0) goto werr;
|
||||
} else if (o->type == OBJ_MODULE) {
|
||||
if (rewriteModuleObject(aof,&key,o) == 0) return false;
|
||||
if (rewriteModuleObject(aof,&key,o) == 0) goto werr;
|
||||
} else {
|
||||
serverPanic("Unknown object type");
|
||||
}
|
||||
/* Save the expire time */
|
||||
if (o->FExpires()) {
|
||||
expireEntry *pexpire = &o->expire;
|
||||
if (pexpire != nullptr) {
|
||||
for (auto &subExpire : *pexpire) {
|
||||
if (subExpire.subkey() == nullptr)
|
||||
{
|
||||
char cmd[]="*3\r\n$9\r\nPEXPIREAT\r\n";
|
||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) return false;
|
||||
if (rioWriteBulkObject(aof,&key) == 0) return false;
|
||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) goto werr;
|
||||
if (rioWriteBulkObject(aof,&key) == 0) goto werr;
|
||||
}
|
||||
else
|
||||
{
|
||||
char cmd[]="*4\r\n$12\r\nPEXPIREMEMBERAT\r\n";
|
||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) return false;
|
||||
if (rioWriteBulkObject(aof,&key) == 0) return false;
|
||||
if (rioWrite(aof,subExpire.subkey(),sdslen(subExpire.subkey())) == 0) return false;
|
||||
if (rioWrite(aof,cmd,sizeof(cmd)-1) == 0) goto werr;
|
||||
if (rioWriteBulkObject(aof,&key) == 0) goto werr;
|
||||
if (rioWrite(aof,subExpire.subkey(),sdslen(subExpire.subkey())) == 0) goto werr;
|
||||
}
|
||||
if (rioWriteBulkLongLong(aof,subExpire.when()) == 0) return false; // common
|
||||
if (rioWriteBulkLongLong(aof,subExpire.when()) == 0) goto werr; // common
|
||||
}
|
||||
}
|
||||
|
||||
/* Read some diff from the parent process from time to time. */
|
||||
if (aof->processed_bytes > processed+AOF_READ_DIFF_INTERVAL_BYTES) {
|
||||
processed = aof->processed_bytes;
|
||||
@@ -1627,15 +1626,14 @@ int rewriteAppendOnlyFileRio(rio *aof) {
|
||||
updated_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
if (!fComplete)
|
||||
goto werr;
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
di = NULL;
|
||||
}
|
||||
return C_OK;
|
||||
|
||||
werr:
|
||||
if (di) dictReleaseIterator(di);
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
@@ -1657,7 +1655,7 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
{ // BEGIN GOTO SCOPED VARIABLES
|
||||
/* Note that we have to use a different temp name here compared to the
|
||||
* one used by rewriteAppendOnlyFileBackground() function. */
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-%d.aof", (int) getpid());
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid());
|
||||
fp = fopen(tmpfile,"w");
|
||||
if (!fp) {
|
||||
serverLog(LL_WARNING, "Opening the temp file for AOF rewrite in rewriteAppendOnlyFile(): %s", strerror(errno));
|
||||
@@ -1674,12 +1672,7 @@ int rewriteAppendOnlyFile(char *filename) {
|
||||
|
||||
if (g_pserver->aof_use_rdb_preamble) {
|
||||
int error;
|
||||
std::vector<const redisDbPersistentDataSnapshot*> vecpdb;
|
||||
for (int idb = 0; idb < cserver.dbnum; ++idb)
|
||||
{
|
||||
vecpdb.push_back(g_pserver->db[idb]);
|
||||
}
|
||||
if (rdbSaveRio(&aof,vecpdb.data(),&error,RDBFLAGS_AOF_PREAMBLE,NULL) == C_ERR) {
|
||||
if (rdbSaveRio(&aof,&error,RDBFLAGS_AOF_PREAMBLE,NULL) == C_ERR) {
|
||||
errno = error;
|
||||
goto werr;
|
||||
}
|
||||
@@ -1886,7 +1879,7 @@ void aofClosePipes(void) {
|
||||
int rewriteAppendOnlyFileBackground(void) {
|
||||
pid_t childpid;
|
||||
|
||||
if (hasActiveChildProcessOrBGSave()) return C_ERR;
|
||||
if (hasActiveChildProcess()) return C_ERR;
|
||||
if (aofCreatePipes() != C_OK) return C_ERR;
|
||||
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
|
||||
char tmpfile[256];
|
||||
@@ -1894,7 +1887,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
/* Child */
|
||||
redisSetProcTitle("keydb-aof-rewrite");
|
||||
redisSetCpuAffinity(g_pserver->aof_rewrite_cpulist);
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-bg-%d.aof", (int) getpid());
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
|
||||
if (rewriteAppendOnlyFile(tmpfile) == C_OK) {
|
||||
sendChildCowInfo(CHILD_INFO_TYPE_AOF_COW_SIZE, "AOF rewrite");
|
||||
exitFromChild(0);
|
||||
@@ -1929,7 +1922,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
void bgrewriteaofCommand(client *c) {
|
||||
if (g_pserver->child_type == CHILD_TYPE_AOF) {
|
||||
addReplyError(c,"Background append only file rewriting already in progress");
|
||||
} else if (hasActiveChildProcessOrBGSave()) {
|
||||
} else if (hasActiveChildProcess()) {
|
||||
g_pserver->aof_rewrite_scheduled = 1;
|
||||
addReplyStatus(c,"Background append only file rewriting scheduled");
|
||||
} else if (rewriteAppendOnlyFileBackground() == C_OK) {
|
||||
@@ -1943,10 +1936,10 @@ void bgrewriteaofCommand(client *c) {
|
||||
void aofRemoveTempFile(pid_t childpid) {
|
||||
char tmpfile[256];
|
||||
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-bg-%d.aof", (int) childpid);
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) childpid);
|
||||
bg_unlink(tmpfile);
|
||||
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-%d.aof", (int) childpid);
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) childpid);
|
||||
bg_unlink(tmpfile);
|
||||
}
|
||||
|
||||
@@ -1984,7 +1977,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
|
||||
/* Flush the differences accumulated by the parent to the
|
||||
* rewritten AOF. */
|
||||
latencyStartMonitor(latency);
|
||||
snprintf(tmpfile,sizeof(tmpfile),"temp-rewriteaof-bg-%d.aof",
|
||||
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof",
|
||||
(int)g_pserver->child_pid);
|
||||
newfd = open(tmpfile,O_WRONLY|O_APPEND);
|
||||
if (newfd == -1) {
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ const char *ascii_logo =
|
||||
" _ \n"
|
||||
" _-(+)-_ \n"
|
||||
" _-- / \\ --_ \n"
|
||||
" _-- / \\ --_ KeyDB %s (%s/%d) %s bit \n"
|
||||
" _-- / \\ --_ KeyDB %s (%s/%d) %s bit \n"
|
||||
" __-- / \\ --__ \n"
|
||||
" (+) _ / \\ _ (+) Running in %s mode\n"
|
||||
" | -- / \\ -- | Port: %d\n"
|
||||
|
||||
+1
-1
@@ -322,7 +322,7 @@ handle_wrap:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int checkSignedBitfieldOverflow(int64_t value, int64_t incr, int bits, int owtype, int64_t *limit) {
|
||||
int checkSignedBitfieldOverflow(int64_t value, int64_t incr, uint64_t bits, int owtype, int64_t *limit) {
|
||||
int64_t max = (bits == 64) ? INT64_MAX : (((int64_t)1<<(bits-1))-1);
|
||||
int64_t min = (-max)-1;
|
||||
|
||||
|
||||
+3
-11
@@ -96,8 +96,6 @@ void blockClient(client *c, int btype) {
|
||||
|
||||
c->flags |= CLIENT_BLOCKED;
|
||||
c->btype = btype;
|
||||
if (btype == BLOCKED_ASYNC)
|
||||
c->casyncOpsPending++;
|
||||
g_pserver->blocked_clients++;
|
||||
g_pserver->blocked_clients_by_type[btype]++;
|
||||
addClientToTimeoutTable(c);
|
||||
@@ -154,7 +152,7 @@ void processUnblockedClients(int iel) {
|
||||
}
|
||||
/* Then process client if it has more data in it's buffer. */
|
||||
if (c->querybuf && sdslen(c->querybuf) > 0) {
|
||||
processInputBuffer(c, true /*fParse*/, CMD_CALL_FULL);
|
||||
processInputBuffer(c, CMD_CALL_FULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,15 +199,11 @@ void unblockClient(client *c) {
|
||||
} else if (c->btype == BLOCKED_MODULE) {
|
||||
if (moduleClientIsBlockedOnKeys(c)) unblockClientWaitingData(c);
|
||||
unblockClientFromModule(c);
|
||||
} else if (c->btype == BLOCKED_ASYNC) {
|
||||
serverAssert(c->casyncOpsPending > 0);
|
||||
c->casyncOpsPending--;
|
||||
} else if (c->btype == BLOCKED_PAUSE) {
|
||||
listDelNode(g_pserver->paused_clients,c->paused_list_node);
|
||||
c->paused_list_node = NULL;
|
||||
} else {
|
||||
serverPanic("Unknown btype %d in unblockClient() for client %llu.",
|
||||
c->btype, (unsigned long long) c->id);
|
||||
serverPanic("Unknown btype in unblockClient().");
|
||||
}
|
||||
|
||||
/* Reset the client for a new query since, for blocking commands
|
||||
@@ -244,8 +238,7 @@ void replyToBlockedClientTimedOut(client *c) {
|
||||
} else if (c->btype == BLOCKED_MODULE) {
|
||||
moduleBlockedClientTimedOut(c);
|
||||
} else {
|
||||
serverPanic("Unknown btype %d in replyToBlockedClientTimedOut() for client %llu.",
|
||||
c->btype, (unsigned long long) c->id);
|
||||
serverPanic("Unknown btype in replyToBlockedClientTimedOut().");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,7 +727,6 @@ void unblockClientWaitingData(client *c) {
|
||||
c->bpop.xread_group = NULL;
|
||||
c->bpop.xread_consumer = NULL;
|
||||
}
|
||||
c->bpop.timeout = 0;
|
||||
}
|
||||
|
||||
static int getBlockedTypeByType(int type) {
|
||||
|
||||
@@ -42,7 +42,6 @@ typedef struct {
|
||||
* RDB / AOF saving process from the child to the parent (for instance
|
||||
* the amount of copy on write memory used) */
|
||||
void openChildInfoPipe(void) {
|
||||
serverAssert(g_pserver->child_info_pipe[0] == -1);
|
||||
if (pipe(g_pserver->child_info_pipe) == -1) {
|
||||
/* On error our two file descriptors should be still set to -1,
|
||||
* but we call anyway closeChildInfoPipe() since can't hurt. */
|
||||
@@ -70,7 +69,6 @@ void closeChildInfoPipe(void) {
|
||||
/* Send save data to parent. */
|
||||
void sendChildInfoGeneric(childInfoType info_type, size_t keys, double progress, const char *pname) {
|
||||
if (g_pserver->child_info_pipe[1] == -1) return;
|
||||
if (g_pserver->rdbThreadVars.fRdbThreadActive && g_pserver->rdbThreadVars.fRdbThreadCancel) return;
|
||||
|
||||
static monotime cow_updated = 0;
|
||||
static uint64_t cow_update_cost = 0;
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@
|
||||
#include "cli_common.h"
|
||||
#include <errno.h>
|
||||
#include <hiredis.h>
|
||||
#include "sdscompat.h" /* Use hiredis' sds compat header that maps sds calls to their hi_ variants */
|
||||
#include <sdscompat.h> /* Use hiredis' sds compat header that maps sds calls to their hi_ variants */
|
||||
#include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
+24
-74
@@ -771,7 +771,7 @@ unsigned long getClusterConnectionsCount(void) {
|
||||
/* We decrement the number of nodes by one, since there is the
|
||||
* "myself" node too in the list. Each node uses two file descriptors,
|
||||
* one incoming and one outgoing, thus the multiplication by 2. */
|
||||
return g_pserver->cluster_enabled && g_pserver->cluster != nullptr ?
|
||||
return g_pserver->cluster_enabled ?
|
||||
((dictSize(g_pserver->cluster->nodes)-1)*2) : 0;
|
||||
}
|
||||
|
||||
@@ -785,7 +785,7 @@ unsigned long getClusterConnectionsCount(void) {
|
||||
* However if the key contains the {...} pattern, only the part between
|
||||
* { and } is hashed. This may be useful in the future to force certain
|
||||
* keys to be in the same node (assuming no resharding is in progress). */
|
||||
unsigned int keyHashSlot(const char *key, int keylen) {
|
||||
unsigned int keyHashSlot(char *key, int keylen) {
|
||||
int s, e; /* start-end indexes of { and } */
|
||||
|
||||
for (s = 0; s < keylen; s++)
|
||||
@@ -1525,9 +1525,7 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
* it's greater than our view but is not in the future
|
||||
* (with 500 milliseconds tolerance) from the POV of our
|
||||
* clock. */
|
||||
mstime_t mstime;
|
||||
__atomic_load(&g_pserver->mstime, &mstime, __ATOMIC_RELAXED);
|
||||
if (pongtime <= (mstime+500) &&
|
||||
if (pongtime <= (g_pserver->mstime+500) &&
|
||||
pongtime > node->pong_received)
|
||||
{
|
||||
node->pong_received = pongtime;
|
||||
@@ -4129,7 +4127,7 @@ int verifyClusterConfigWithData(void) {
|
||||
|
||||
/* Make sure we only have keys in DB0. */
|
||||
for (j = 1; j < cserver.dbnum; j++) {
|
||||
if (g_pserver->db[j]->size()) return C_ERR;
|
||||
if (dictSize(g_pserver->db[j].dict)) return C_ERR;
|
||||
}
|
||||
|
||||
/* Check that all the slots we see populated memory have a corresponding
|
||||
@@ -4516,8 +4514,8 @@ void clusterCommand(client *c) {
|
||||
"NODES",
|
||||
" Return cluster configuration seen by node. Output format:",
|
||||
" <id> <ip:port> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ...",
|
||||
"REPLICATE (<node-id>|NO ONE)",
|
||||
" Configure current node as replica to <node-id> or turn it into empty primary.",
|
||||
"REPLICATE <node-id>",
|
||||
" Configure current node as replica to <node-id>.",
|
||||
"RESET [HARD|SOFT]",
|
||||
" Reset current node (default: soft).",
|
||||
"SET-CONFIG-EPOCH <epoch>",
|
||||
@@ -4579,7 +4577,7 @@ NULL
|
||||
clusterReplyMultiBulkSlots(c);
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"flushslots") && c->argc == 2) {
|
||||
/* CLUSTER FLUSHSLOTS */
|
||||
if (g_pserver->db[0]->size() != 0) {
|
||||
if (dictSize(g_pserver->db[0].dict) != 0) {
|
||||
addReplyError(c,"DB must be empty to perform CLUSTER FLUSHSLOTS.");
|
||||
return;
|
||||
}
|
||||
@@ -4890,22 +4888,14 @@ NULL
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|
|
||||
CLUSTER_TODO_SAVE_CONFIG);
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"replicate") && (c->argc == 3 || c->argc == 4)) {
|
||||
/* CLUSTER REPLICATE (<NODE ID>|NO ONE) */
|
||||
clusterNode *n;
|
||||
if (c->argc == 4) {
|
||||
if (0 != strcasecmp(szFromObj(c->argv[2]),"NO") || 0 != strcasecmp(szFromObj(c->argv[3]),"ONE")) {
|
||||
addReplySubcommandSyntaxError(c);
|
||||
return;
|
||||
}
|
||||
n = nullptr;
|
||||
} else {
|
||||
/* Lookup the specified node in our table. */
|
||||
n = clusterLookupNode(szFromObj(c->argv[2]));
|
||||
if (n == nullptr) {
|
||||
addReplyErrorFormat(c,"Unknown node %s", (char*)ptrFromObj(c->argv[2]));
|
||||
return;
|
||||
}
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"replicate") && c->argc == 3) {
|
||||
/* CLUSTER REPLICATE <NODE ID> */
|
||||
clusterNode *n = clusterLookupNode(szFromObj(c->argv[2]));
|
||||
|
||||
/* Lookup the specified node in our table. */
|
||||
if (!n) {
|
||||
addReplyErrorFormat(c,"Unknown node %s", (char*)ptrFromObj(c->argv[2]));
|
||||
return;
|
||||
}
|
||||
|
||||
/* I can't replicate myself. */
|
||||
@@ -4915,7 +4905,7 @@ NULL
|
||||
}
|
||||
|
||||
/* Can't replicate a slave. */
|
||||
if (n != nullptr && nodeIsSlave(n)) {
|
||||
if (nodeIsSlave(n)) {
|
||||
addReplyError(c,"I can only replicate a master, not a replica.");
|
||||
return;
|
||||
}
|
||||
@@ -4924,33 +4914,15 @@ NULL
|
||||
* slots nor keys to accept to replicate some other node.
|
||||
* Slaves can switch to another master without issues. */
|
||||
if (nodeIsMaster(myself) &&
|
||||
(myself->numslots != 0 || g_pserver->db[0]->size() != 0)) {
|
||||
(myself->numslots != 0 || dictSize(g_pserver->db[0].dict) != 0)) {
|
||||
addReplyError(c,
|
||||
"To set a master the node must be empty and "
|
||||
"without assigned slots.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == nullptr) {
|
||||
if (nodeIsMaster(myself)) {
|
||||
addReply(c,shared.ok);
|
||||
return;
|
||||
}
|
||||
serverLog(LL_NOTICE,"Stop replication and turning myself into empty primary.");
|
||||
clusterSetNodeAsMaster(myself);
|
||||
if (listLength(g_pserver->masters) > 0)
|
||||
{
|
||||
serverAssert(listLength(g_pserver->masters) == 1);
|
||||
replicationUnsetMaster((redisMaster*)listFirst(g_pserver->masters)->value);
|
||||
}
|
||||
int empty_db_flags = g_pserver->repl_slave_lazy_flush ? EMPTYDB_ASYNC : EMPTYDB_NO_FLAGS;
|
||||
emptyDb(-1,empty_db_flags, nullptr);
|
||||
/* Reset manual failover state. */
|
||||
resetManualFailover();
|
||||
} else {
|
||||
/* Set the master. */
|
||||
clusterSetMaster(n);
|
||||
}
|
||||
/* Set the master. */
|
||||
clusterSetMaster(n);
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);
|
||||
addReply(c,shared.ok);
|
||||
} else if ((!strcasecmp(szFromObj(c->argv[1]),"slaves") ||
|
||||
@@ -5102,7 +5074,7 @@ NULL
|
||||
|
||||
/* Slaves can be reset while containing data, but not master nodes
|
||||
* that must be empty. */
|
||||
if (nodeIsMaster(myself) && c->db->size() != 0) {
|
||||
if (nodeIsMaster(myself) && dictSize(c->db->dict) != 0) {
|
||||
addReplyError(c,"CLUSTER RESET can't be called with "
|
||||
"master nodes containing keys");
|
||||
return;
|
||||
@@ -5118,7 +5090,6 @@ NULL
|
||||
/* -----------------------------------------------------------------------------
|
||||
* DUMP, RESTORE and MIGRATE commands
|
||||
* -------------------------------------------------------------------------- */
|
||||
ssize_t rdbSaveAuxFieldStrStr(rio *rdb, const char *key, const char *val);
|
||||
|
||||
/* Generates a DUMP-format representation of the object 'o', adding it to the
|
||||
* io stream pointed by 'rio'. This function can't fail. */
|
||||
@@ -5131,10 +5102,6 @@ void createDumpPayload(rio *payload, robj_roptr o, robj *key) {
|
||||
rioInitWithBuffer(payload,sdsempty());
|
||||
serverAssert(rdbSaveObjectType(payload,o));
|
||||
serverAssert(rdbSaveObject(payload,o,key));
|
||||
char szT[32];
|
||||
uint64_t mvcc = mvccFromObj(o);
|
||||
snprintf(szT, sizeof(szT), "%" PRIu64, mvcc);
|
||||
serverAssert(rdbSaveAuxFieldStrStr(payload,"mvcc-tstamp", szT) != -1);
|
||||
|
||||
/* Write the footer, this is how it looks like:
|
||||
* ----------------+---------------------+---------------+
|
||||
@@ -5318,21 +5285,6 @@ void restoreCommand(client *c) {
|
||||
addReplyError(c,"Bad data format");
|
||||
return;
|
||||
}
|
||||
if (rdbLoadType(&payload) == RDB_OPCODE_AUX)
|
||||
{
|
||||
robj *auxkey, *auxval;
|
||||
if ((auxkey = rdbLoadStringObject(&payload)) == NULL) goto eoferr;
|
||||
if ((auxval = rdbLoadStringObject(&payload)) == NULL) {
|
||||
decrRefCount(auxkey);
|
||||
goto eoferr;
|
||||
}
|
||||
if (strcasecmp(szFromObj(auxkey), "mvcc-tstamp") == 0) {
|
||||
setMvccTstamp(obj, strtoull(szFromObj(auxval), nullptr, 10));
|
||||
}
|
||||
decrRefCount(auxkey);
|
||||
decrRefCount(auxval);
|
||||
}
|
||||
eoferr:
|
||||
|
||||
/* Remove the old key if needed. */
|
||||
int deleted = 0;
|
||||
@@ -5610,7 +5562,7 @@ try_again:
|
||||
/* Create RESTORE payload and generate the protocol to call the command. */
|
||||
for (j = 0; j < num_keys; j++) {
|
||||
long long ttl = 0;
|
||||
expireEntry *pexpire = c->db->getExpire(kv[j]);
|
||||
expireEntry *pexpire = getExpire(c->db,kv[j]);
|
||||
long long expireat = INVALID_EXPIRE;
|
||||
if (pexpire != nullptr)
|
||||
pexpire->FGetPrimaryExpire(&expireat);
|
||||
@@ -5901,7 +5853,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||
multiState *ms, _ms;
|
||||
multiCmd mc;
|
||||
int i, slot = 0, migrating_slot = 0, importing_slot = 0, missing_keys = 0;
|
||||
serverAssert((c->cmd->flags & CMD_ASYNC_OK) || GlobalLocksAcquired());
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
|
||||
/* Allow any key to be set if a module disabled cluster redirections. */
|
||||
if (g_pserver->cluster_module_flags & CLUSTER_MODULE_FLAG_NO_REDIRECTION)
|
||||
@@ -5991,9 +5943,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||
/* If it is not the first key, make sure it is exactly
|
||||
* the same key as the first we saw. */
|
||||
if (!equalStringObjects(firstkey,thiskey)) {
|
||||
clusterNode* nThisKey = g_pserver->cluster->slots[thisslot];
|
||||
|
||||
if ((slot != thisslot) && (nThisKey != n || migrating_slot || importing_slot || g_pserver->cluster->migrating_slots_to[slot] != nullptr || g_pserver->cluster->importing_slots_from[slot] != nullptr)) {
|
||||
if (slot != thisslot) {
|
||||
/* Error: multiple keys from different slots. */
|
||||
getKeysFreeResult(&result);
|
||||
if (error_code)
|
||||
@@ -6009,7 +5959,7 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
|
||||
|
||||
/* Migrating / Importing slot? Count keys we don't have. */
|
||||
if ((migrating_slot || importing_slot) &&
|
||||
lookupKeyRead(g_pserver->db[0],thiskey) == nullptr)
|
||||
lookupKeyRead(&g_pserver->db[0],thiskey) == nullptr)
|
||||
{
|
||||
missing_keys++;
|
||||
}
|
||||
|
||||
+3
-11
@@ -30,16 +30,7 @@ public:
|
||||
zfree(m_data);
|
||||
}
|
||||
|
||||
compactvector(const compactvector &src)
|
||||
{
|
||||
m_celem = src.m_celem;
|
||||
m_max = src.m_max;
|
||||
m_data = (T*)zmalloc(sizeof(T) * m_max, MALLOC_LOCAL);
|
||||
for (size_t ielem = 0; ielem < m_celem; ++ielem)
|
||||
{
|
||||
new (m_data+ielem) T(src[ielem]);
|
||||
}
|
||||
}
|
||||
compactvector(compactvector &) noexcept = delete;
|
||||
|
||||
compactvector(compactvector &&src) noexcept
|
||||
{
|
||||
@@ -51,6 +42,7 @@ public:
|
||||
src.m_max = 0;
|
||||
}
|
||||
|
||||
compactvector &operator=(const compactvector&) noexcept = delete;
|
||||
compactvector &operator=(compactvector &&src) noexcept
|
||||
{
|
||||
zfree(m_data);
|
||||
@@ -69,7 +61,7 @@ public:
|
||||
inline T* end() { return m_data + m_celem; }
|
||||
inline const T* end() const { return m_data + m_celem; }
|
||||
|
||||
T* insert(T* where, const T &val)
|
||||
T* insert(T* where, T &val)
|
||||
{
|
||||
assert(where >= m_data);
|
||||
size_t idx = where - m_data;
|
||||
|
||||
+15
-241
@@ -29,19 +29,12 @@
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
#include "storage/rocksdbfactory.h"
|
||||
#include "storage/teststorageprovider.h"
|
||||
#include "cluster.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
const char *KEYDB_SET_VERSION = KEYDB_REAL_VERSION;
|
||||
size_t g_semiOrderedSetTargetBucketSize = 0; // Its a header only class so nowhere else for this to go
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Config file name-value maps.
|
||||
@@ -107,11 +100,6 @@ configEnum repl_diskless_load_enum[] = {
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
configEnum storage_memory_model_enum[] = {
|
||||
{"writeback", STORAGE_WRITEBACK},
|
||||
{"writethrough", STORAGE_WRITETHROUGH},
|
||||
};
|
||||
|
||||
configEnum tls_auth_clients_enum[] = {
|
||||
{"no", TLS_CLIENT_AUTH_NO},
|
||||
{"yes", TLS_CLIENT_AUTH_YES},
|
||||
@@ -345,72 +333,6 @@ void queueLoadModule(sds path, sds *argv, int argc) {
|
||||
listAddNodeTail(g_pserver->loadmodule_queue,loadmod);
|
||||
}
|
||||
|
||||
sds g_sdsProvider = nullptr;
|
||||
sds g_sdsArgs = nullptr;
|
||||
|
||||
bool initializeStorageProvider(const char **err)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool fTest = false;
|
||||
if (g_sdsProvider == nullptr)
|
||||
return true;
|
||||
if (!strcasecmp(g_sdsProvider, "flash") && g_sdsArgs != nullptr)
|
||||
{
|
||||
#ifdef ENABLE_ROCKSDB
|
||||
// Create The Storage Factory (if necessary)
|
||||
serverLog(LL_NOTICE, "Initializing FLASH storage provider (this may take a long time)");
|
||||
adjustOpenFilesLimit();
|
||||
g_pserver->m_pstorageFactory = CreateRocksDBStorageFactory(g_sdsArgs, cserver.dbnum, cserver.storage_conf, cserver.storage_conf ? strlen(cserver.storage_conf) : 0);
|
||||
#else
|
||||
serverLog(LL_WARNING, "To use the flash storage provider please compile KeyDB with ENABLE_FLASH=yes");
|
||||
serverLog(LL_WARNING, "Exiting due to the use of an unsupported storage provider");
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
else if (!strcasecmp(g_sdsProvider, "test") && g_sdsArgs == nullptr)
|
||||
{
|
||||
g_pserver->m_pstorageFactory = new (MALLOC_LOCAL) TestStorageFactory();
|
||||
fTest = true;
|
||||
}
|
||||
|
||||
if (g_pserver->m_pstorageFactory != nullptr && !fTest)
|
||||
{
|
||||
// We need to set max memory to a sane default so keys are actually evicted properly
|
||||
if (g_pserver->maxmemory == 0 && g_pserver->maxmemory_policy == MAXMEMORY_NO_EVICTION)
|
||||
{
|
||||
#ifdef __linux__
|
||||
struct sysinfo sys;
|
||||
if (sysinfo(&sys) == 0)
|
||||
{
|
||||
// By default it's a little under half the memory. This gives sufficient room for background saving
|
||||
g_pserver->maxmemory = static_cast<unsigned long long>(sys.totalram / 2.2);
|
||||
g_pserver->maxmemory_policy = MAXMEMORY_ALLKEYS_LRU;
|
||||
}
|
||||
#else
|
||||
serverLog(LL_WARNING, "Unable to dynamically set maxmemory, please set maxmemory and maxmemory-policy if you are using a storage provier");
|
||||
#endif
|
||||
}
|
||||
else if (g_pserver->maxmemory_policy == MAXMEMORY_NO_EVICTION)
|
||||
{
|
||||
g_pserver->maxmemory_policy = MAXMEMORY_ALLKEYS_LRU;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*err = "Unknown storage provider";
|
||||
}
|
||||
return g_pserver->m_pstorageFactory != nullptr;
|
||||
}
|
||||
catch(std::string str)
|
||||
{
|
||||
serverLog(LL_WARNING, "ERROR: Failed to initialize %s storage provider. Details to follow below.", g_sdsProvider);
|
||||
serverLog(LL_WARNING, "\t%s", str.c_str());
|
||||
serverLog(LL_WARNING, "KeyDB cannot start. Exiting.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse an array of CONFIG_OOM_COUNT sds strings, validate and populate
|
||||
* g_pserver->oom_score_adj_values if valid.
|
||||
*/
|
||||
@@ -749,36 +671,6 @@ void loadServerConfigFromString(char *config) {
|
||||
g_pserver->fActiveReplica = CONFIG_DEFAULT_ACTIVE_REPLICA;
|
||||
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
||||
}
|
||||
if (listLength(g_pserver->masters) && g_pserver->fActiveReplica) {
|
||||
err = "must not set replica-of config before active-replica config"; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0], "multi-master") && argc == 2) {
|
||||
g_pserver->enable_multimaster = yesnotoi(argv[1]);
|
||||
if (g_pserver->enable_multimaster == -1) {
|
||||
g_pserver->enable_multimaster = CONFIG_DEFAULT_ENABLE_MULTIMASTER;
|
||||
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
||||
}
|
||||
if (listLength(g_pserver->masters) && g_pserver->enable_multimaster) {
|
||||
err = "must not set replica-of config before multi-master config"; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0], "tls-allowlist")) {
|
||||
if (argc < 2) {
|
||||
err = "must supply at least one element in the allow list"; goto loaderr;
|
||||
}
|
||||
if (!g_pserver->tls_allowlist.empty()) {
|
||||
err = "tls-allowlist may only be set once"; goto loaderr;
|
||||
}
|
||||
for (int i = 1; i < argc; i++)
|
||||
g_pserver->tls_allowlist.emplace(argv[i], strlen(argv[i]));
|
||||
} else if (!strcasecmp(argv[0], "tls-auditlog-blocklist")) {
|
||||
if (argc < 2) {
|
||||
err = "must supply at least one element in the block list"; goto loaderr;
|
||||
}
|
||||
if (!g_pserver->tls_auditlog_blocklist.empty()) {
|
||||
err = "tls-auditlog-blocklist may only be set once"; goto loaderr;
|
||||
}
|
||||
for (int i = 1; i < argc; i++)
|
||||
g_pserver->tls_auditlog_blocklist.emplace(argv[i], strlen(argv[i]));
|
||||
} else if (!strcasecmp(argv[0], "version-override") && argc == 2) {
|
||||
KEYDB_SET_VERSION = zstrdup(argv[1]);
|
||||
serverLog(LL_WARNING, "Warning version is overriden to: %s\n", KEYDB_SET_VERSION);
|
||||
@@ -786,16 +678,9 @@ void loadServerConfigFromString(char *config) {
|
||||
g_fTestMode = yesnotoi(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"rdbfuzz-mode")) {
|
||||
// NOP, handled in main
|
||||
} else if (!strcasecmp(argv[0],"storage-provider") && argc >= 2) {
|
||||
g_sdsProvider = sdsdup(argv[1]);
|
||||
if (argc > 2)
|
||||
g_sdsArgs = sdsdup(argv[2]);
|
||||
} else if (!strcasecmp(argv[0],"is-flash-enabled") && argc == 1) {
|
||||
#ifdef ENABLE_ROCKSDB
|
||||
exit(EXIT_SUCCESS);
|
||||
#else
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
} else if (!strcasecmp(argv[0],"enable-pro")) {
|
||||
cserver.fUsePro = true;
|
||||
break;
|
||||
} else {
|
||||
err = "Bad directive or wrong number of arguments"; goto loaderr;
|
||||
}
|
||||
@@ -905,18 +790,8 @@ void configSetCommand(client *c) {
|
||||
int err;
|
||||
const char *errstr = NULL;
|
||||
serverAssertWithInfo(c,c->argv[2],sdsEncodedObject(c->argv[2]));
|
||||
|
||||
if (c->argc < 4 || c->argc > 4) {
|
||||
o = nullptr;
|
||||
// Variadic set is only supported for tls-allowlist
|
||||
if (strcasecmp(szFromObj(c->argv[2]), "tls-allowlist")) {
|
||||
addReplySubcommandSyntaxError(c);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
o = c->argv[3];
|
||||
serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));
|
||||
}
|
||||
serverAssertWithInfo(c,c->argv[3],sdsEncodedObject(c->argv[3]));
|
||||
o = c->argv[3];
|
||||
|
||||
/* Iterate the configs that are standard */
|
||||
for (standardConfig *config = configs; config->name != NULL; config++) {
|
||||
@@ -1078,12 +953,6 @@ void configSetCommand(client *c) {
|
||||
enableWatchdog(ll);
|
||||
else
|
||||
disableWatchdog();
|
||||
} config_set_special_field("tls-allowlist") {
|
||||
g_pserver->tls_allowlist.clear();
|
||||
for (int i = 3; i < c->argc; ++i) {
|
||||
robj *val = c->argv[i];
|
||||
g_pserver->tls_allowlist.emplace(szFromObj(val), sdslen(szFromObj(val)));
|
||||
}
|
||||
/* Everything else is an error... */
|
||||
} config_set_else {
|
||||
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
||||
@@ -1287,14 +1156,6 @@ void configGetCommand(client *c) {
|
||||
addReplyBulkCString(c, g_pserver->fActiveReplica ? "yes" : "no");
|
||||
matches++;
|
||||
}
|
||||
if (stringmatch(pattern, "tls-allowlist", 1)) {
|
||||
addReplyBulkCString(c,"tls-allowlist");
|
||||
addReplyArrayLen(c, (long)g_pserver->tls_allowlist.size());
|
||||
for (auto &elem : g_pserver->tls_allowlist) {
|
||||
addReplyBulkCBuffer(c, elem.get(), elem.size()); // addReplyBulkSds will free which we absolutely don't want
|
||||
}
|
||||
matches++;
|
||||
}
|
||||
|
||||
setDeferredMapLen(c,replylen,matches);
|
||||
}
|
||||
@@ -1964,20 +1825,6 @@ int rewriteConfig(char *path, int force_all) {
|
||||
rewriteConfigStringOption(state, "version-override",KEYDB_SET_VERSION,KEYDB_REAL_VERSION);
|
||||
rewriteConfigOOMScoreAdjValuesOption(state);
|
||||
|
||||
if (!g_pserver->tls_allowlist.empty()) {
|
||||
sds conf = sdsnew("tls-allowlist ");
|
||||
for (auto &elem : g_pserver->tls_allowlist) {
|
||||
conf = sdscatsds(conf, (sds)elem.get());
|
||||
conf = sdscat(conf, " ");
|
||||
}
|
||||
// trim the trailing space
|
||||
sdsrange(conf, 0, -1);
|
||||
rewriteConfigRewriteLine(state,"tls-allowlist",conf,1 /*force*/);
|
||||
// note: conf is owned by rewriteConfigRewriteLine - no need to free
|
||||
} else {
|
||||
rewriteConfigMarkAsProcessed(state, "tls-allowlist"); // ensure the line is removed if it existed
|
||||
}
|
||||
|
||||
/* Rewrite Sentinel config if in Sentinel mode. */
|
||||
if (g_pserver->sentinel_mode) rewriteConfigSentinelOption(state);
|
||||
|
||||
@@ -2121,10 +1968,7 @@ static void sdsConfigGet(client *c, typeData data) {
|
||||
}
|
||||
|
||||
static void sdsConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
|
||||
sds sdsDefault = data.sds.default_value ? sdsnew(data.sds.default_value) : NULL;
|
||||
rewriteConfigSdsOption(state, name, *(data.sds.config), sdsDefault);
|
||||
if (sdsDefault)
|
||||
sdsfree(sdsDefault);
|
||||
rewriteConfigSdsOption(state, name, *(data.sds.config), data.sds.default_value ? sdsnew(data.sds.default_value) : NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -2481,32 +2325,6 @@ static int isValidAOFfilename(char *val, const char **err) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int isValidS3Bucket(char *s3bucket, const char **err) {
|
||||
int status = EXIT_FAILURE;
|
||||
pid_t pid = fork();
|
||||
if (pid < 0)
|
||||
{
|
||||
*err = "couldn't fork to call aws cli";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
execlp("aws", "aws", "s3", "ls", s3bucket, nullptr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
|
||||
if (status != EXIT_SUCCESS) {
|
||||
*err = "could not access s3 bucket";
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Validate specified string is a valid proc-title-template */
|
||||
static int isValidProcTitleTemplate(char *val, const char **err) {
|
||||
if (!validateProcTitleTemplate(val)) {
|
||||
@@ -2574,12 +2392,8 @@ static int updateJemallocBgThread(int val, int prev, const char **err) {
|
||||
static int updateReplBacklogSize(long long val, long long prev, const char **err) {
|
||||
/* resizeReplicationBacklog sets g_pserver->repl_backlog_size, and relies on
|
||||
* being able to tell when the size changes, so restore prev before calling it. */
|
||||
if (cserver.repl_backlog_disk_size) {
|
||||
*err = "Unable to dynamically resize the backlog because disk backlog is enabled";
|
||||
return 0;
|
||||
}
|
||||
UNUSED(err);
|
||||
g_pserver->repl_backlog_size = prev;
|
||||
g_pserver->repl_backlog_config_size = val;
|
||||
resizeReplicationBacklog(val);
|
||||
return 1;
|
||||
}
|
||||
@@ -2592,20 +2406,7 @@ static int updateMaxmemory(long long val, long long prev, const char **err) {
|
||||
if ((unsigned long long)val < used) {
|
||||
serverLog(LL_WARNING,"WARNING: the new maxmemory value set via CONFIG SET (%llu) is smaller than the current memory usage (%zu). This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.", g_pserver->maxmemory, used);
|
||||
}
|
||||
performEvictions(false /*fPreSnapshot*/);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int updateFlashMaxmemory(long long val, long long prev, const char **err) {
|
||||
UNUSED(prev);
|
||||
UNUSED(err);
|
||||
if (val && g_pserver->m_pstorageFactory) {
|
||||
size_t used = g_pserver->m_pstorageFactory->totalDiskspaceUsed();
|
||||
if ((unsigned long long)val < used) {
|
||||
serverLog(LL_WARNING,"WARNING: the new maxstorage value set via CONFIG SET (%llu) is smaller than the current storage usage (%zu). This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.", g_pserver->maxstorage, used);
|
||||
}
|
||||
performEvictions(false /*fPreSnapshot*/);
|
||||
performEvictions();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -2652,7 +2453,7 @@ static int updateMaxclients(long long val, long long prev, const char **err) {
|
||||
adjustOpenFilesLimit();
|
||||
if (g_pserver->maxclients != val) {
|
||||
static char msg[128];
|
||||
snprintf(msg, sizeof(msg), "The operating system is not able to handle the specified number of clients, try with %d", g_pserver->maxclients);
|
||||
sprintf(msg, "The operating system is not able to handle the specified number of clients, try with %d", g_pserver->maxclients);
|
||||
*err = msg;
|
||||
if (g_pserver->maxclients > prev) {
|
||||
g_pserver->maxclients = prev;
|
||||
@@ -2691,7 +2492,7 @@ static int updateMaxclients(long long val, long long prev, const char **err) {
|
||||
|
||||
if (res != AE_OK){
|
||||
static char msg[128];
|
||||
snprintf(msg, sizeof(msg),"Failed to post the request to change setsize for Thread %d", iel);
|
||||
sprintf(msg, "Failed to post the request to change setsize for Thread %d", iel);
|
||||
*err = msg;
|
||||
return 0;
|
||||
}
|
||||
@@ -2835,27 +2636,20 @@ standardConfig configs[] = {
|
||||
createBoolConfig("replica-serve-stale-data", "slave-serve-stale-data", MODIFIABLE_CONFIG, g_pserver->repl_serve_stale_data, 1, NULL, NULL),
|
||||
createBoolConfig("replica-read-only", "slave-read-only", MODIFIABLE_CONFIG, g_pserver->repl_slave_ro, 1, NULL, NULL),
|
||||
createBoolConfig("replica-ignore-maxmemory", "slave-ignore-maxmemory", MODIFIABLE_CONFIG, g_pserver->repl_slave_ignore_maxmemory, 1, NULL, NULL),
|
||||
createBoolConfig("multi-master", NULL, IMMUTABLE_CONFIG, g_pserver->enable_multimaster,CONFIG_DEFAULT_ENABLE_MULTIMASTER, NULL, NULL),
|
||||
createBoolConfig("jemalloc-bg-thread", NULL, MODIFIABLE_CONFIG, cserver.jemalloc_bg_thread, 1, NULL, updateJemallocBgThread),
|
||||
createBoolConfig("activedefrag", NULL, MODIFIABLE_CONFIG, cserver.active_defrag_enabled, 0, isValidActiveDefrag, NULL),
|
||||
createBoolConfig("syslog-enabled", NULL, IMMUTABLE_CONFIG, g_pserver->syslog_enabled, 0, NULL, NULL),
|
||||
createBoolConfig("cluster-enabled", NULL, IMMUTABLE_CONFIG, g_pserver->cluster_enabled, 0, NULL, NULL),
|
||||
createBoolConfig("appendonly", NULL, MODIFIABLE_CONFIG, g_pserver->aof_enabled, 0, NULL, updateAppendonly),
|
||||
createBoolConfig("cluster-allow-reads-when-down", NULL, MODIFIABLE_CONFIG, g_pserver->cluster_allow_reads_when_down, 0, NULL, NULL),
|
||||
createBoolConfig("delete-on-evict", NULL, MODIFIABLE_CONFIG, cserver.delete_on_evict, 0, NULL, NULL),
|
||||
createBoolConfig("use-fork", NULL, IMMUTABLE_CONFIG, cserver.fForkBgSave, 1, NULL, NULL),
|
||||
createBoolConfig("io-threads-do-reads", NULL, IMMUTABLE_CONFIG, fDummy, 0, NULL, NULL),
|
||||
createBoolConfig("time-thread-priority", NULL, IMMUTABLE_CONFIG, cserver.time_thread_priority, 0, NULL, NULL),
|
||||
createBoolConfig("prefetch-enabled", NULL, MODIFIABLE_CONFIG, g_pserver->prefetch_enabled, 1, NULL, NULL),
|
||||
createBoolConfig("allow-rdb-resize-op", NULL, MODIFIABLE_CONFIG, g_pserver->allowRdbResizeOp, 1, NULL, NULL),
|
||||
createBoolConfig("crash-log-enabled", NULL, MODIFIABLE_CONFIG, g_pserver->crashlog_enabled, 1, NULL, updateSighandlerEnabled),
|
||||
createBoolConfig("crash-memcheck-enabled", NULL, MODIFIABLE_CONFIG, g_pserver->memcheck_enabled, 1, NULL, NULL),
|
||||
createBoolConfig("use-exit-on-panic", NULL, MODIFIABLE_CONFIG, g_pserver->use_exit_on_panic, 0, NULL, NULL),
|
||||
createBoolConfig("disable-thp", NULL, MODIFIABLE_CONFIG, g_pserver->disable_thp, 1, NULL, NULL),
|
||||
createBoolConfig("cluster-allow-replica-migration", NULL, MODIFIABLE_CONFIG, g_pserver->cluster_allow_replica_migration, 1, NULL, NULL),
|
||||
createBoolConfig("replica-announced", NULL, MODIFIABLE_CONFIG, g_pserver->replica_announced, 1, NULL, NULL),
|
||||
createBoolConfig("enable-async-commands", NULL, MODIFIABLE_CONFIG, g_pserver->enable_async_commands, 0, NULL, NULL),
|
||||
createBoolConfig("multithread-load-enabled", NULL, MODIFIABLE_CONFIG, g_pserver->multithread_load_enabled, 0, NULL, NULL),
|
||||
createBoolConfig("active-client-balancing", NULL, MODIFIABLE_CONFIG, g_pserver->active_client_balancing, 1, NULL, NULL),
|
||||
|
||||
/* String Configs */
|
||||
createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->acl_filename, "", NULL, NULL),
|
||||
@@ -2866,13 +2660,11 @@ standardConfig configs[] = {
|
||||
createStringConfig("cluster-announce-ip", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->cluster_announce_ip, NULL, NULL, NULL),
|
||||
createStringConfig("syslog-ident", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->syslog_ident, "redis", NULL, NULL),
|
||||
createStringConfig("dbfilename", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->rdb_filename, CONFIG_DEFAULT_RDB_FILENAME, isValidDBfilename, NULL),
|
||||
createStringConfig("db-s3-object", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->rdb_s3bucketpath, NULL, isValidS3Bucket, NULL),
|
||||
createStringConfig("appendfilename", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->aof_filename, "appendonly.aof", isValidAOFfilename, NULL),
|
||||
createStringConfig("server_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->server_cpulist, NULL, NULL, NULL),
|
||||
createStringConfig("bio_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->bio_cpulist, NULL, NULL, NULL),
|
||||
createStringConfig("aof_rewrite_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->aof_rewrite_cpulist, NULL, NULL, NULL),
|
||||
createStringConfig("bgsave_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->bgsave_cpulist, NULL, NULL, NULL),
|
||||
createStringConfig("storage-provider-options", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, cserver.storage_conf, NULL, NULL, NULL),
|
||||
createStringConfig("ignore-warnings", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->ignore_warnings, "", NULL, NULL),
|
||||
createStringConfig("proc-title-template", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, cserver.proc_title_template, CONFIG_DEFAULT_PROC_TITLE_TEMPLATE, isValidProcTitleTemplate, updateProcTitleTemplate),
|
||||
|
||||
@@ -2887,7 +2679,6 @@ standardConfig configs[] = {
|
||||
createEnumConfig("loglevel", NULL, MODIFIABLE_CONFIG, loglevel_enum, cserver.verbosity, LL_NOTICE, NULL, NULL),
|
||||
createEnumConfig("maxmemory-policy", NULL, MODIFIABLE_CONFIG, maxmemory_policy_enum, g_pserver->maxmemory_policy, MAXMEMORY_NO_EVICTION, NULL, NULL),
|
||||
createEnumConfig("appendfsync", NULL, MODIFIABLE_CONFIG, aof_fsync_enum, g_pserver->aof_fsync, AOF_FSYNC_EVERYSEC, NULL, NULL),
|
||||
createEnumConfig("storage-cache-mode", NULL, IMMUTABLE_CONFIG, storage_memory_model_enum, cserver.storage_memory_model, STORAGE_WRITETHROUGH, NULL, NULL),
|
||||
createEnumConfig("oom-score-adj", NULL, MODIFIABLE_CONFIG, oom_score_adj_enum, g_pserver->oom_score_adj, OOM_SCORE_ADJ_NO, NULL, updateOOMScoreAdj),
|
||||
createEnumConfig("acl-pubsub-default", NULL, MODIFIABLE_CONFIG, acl_pubsub_default_enum, g_pserver->acl_pubsub_default, USER_FLAG_ALLCHANNELS, NULL, NULL),
|
||||
createEnumConfig("sanitize-dump-payload", NULL, MODIFIABLE_CONFIG, sanitize_dump_payload_enum, cserver.sanitize_dump_payload, SANITIZE_DUMP_NO, NULL, NULL),
|
||||
@@ -2908,7 +2699,7 @@ standardConfig configs[] = {
|
||||
createIntConfig("lfu-decay-time", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->lfu_decay_time, 1, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("replica-priority", "slave-priority", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->slave_priority, 100, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("repl-diskless-sync-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_diskless_sync_delay, 5, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("maxmemory-samples", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, g_pserver->maxmemory_samples, 16, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("maxmemory-samples", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, g_pserver->maxmemory_samples, 5, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("maxmemory-eviction-tenacity", NULL, MODIFIABLE_CONFIG, 0, 100, g_pserver->maxmemory_eviction_tenacity, 10, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, cserver.maxidletime, 0, INTEGER_CONFIG, NULL, NULL), /* Default client timeout: infinite */
|
||||
createIntConfig("replica-announce-port", "slave-announce-port", MODIFIABLE_CONFIG, 0, 65535, g_pserver->slave_announce_port, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
@@ -2921,18 +2712,15 @@ standardConfig configs[] = {
|
||||
createIntConfig("list-compress-depth", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->list_compress_depth, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("rdb-key-save-delay", NULL, MODIFIABLE_CONFIG, INT_MIN, INT_MAX, g_pserver->rdb_key_save_delay, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("key-load-delay", NULL, MODIFIABLE_CONFIG, INT_MIN, INT_MAX, g_pserver->key_load_delay, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("active-expire-effort", NULL, MODIFIABLE_CONFIG, 1, 10, g_pserver->active_expire_effort, 1, INTEGER_CONFIG, NULL, NULL), /* From 1 to 10. */
|
||||
createIntConfig("active-expire-effort", NULL, MODIFIABLE_CONFIG, 1, 10, cserver.active_expire_effort, 1, INTEGER_CONFIG, NULL, NULL), /* From 1 to 10. */
|
||||
createIntConfig("hz", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->config_hz, CONFIG_DEFAULT_HZ, INTEGER_CONFIG, NULL, updateHZ),
|
||||
createIntConfig("min-replicas-to-write", "min-slaves-to-write", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_min_slaves_to_write, 0, INTEGER_CONFIG, NULL, updateGoodSlaves),
|
||||
createIntConfig("min-replicas-max-lag", "min-slaves-max-lag", MODIFIABLE_CONFIG, 0, INT_MAX, g_pserver->repl_min_slaves_max_lag, 10, INTEGER_CONFIG, NULL, updateGoodSlaves),
|
||||
createIntConfig("min-clients-per-thread", NULL, MODIFIABLE_CONFIG, 0, 400, cserver.thread_min_client_threshold, 20, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("storage-flush-period", NULL, MODIFIABLE_CONFIG, 1, 10000, g_pserver->storage_flush_period, 500, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("replica-quorum", NULL, MODIFIABLE_CONFIG, -1, INT_MAX, g_pserver->repl_quorum, -1, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("replica-weighting-factor", NULL, MODIFIABLE_CONFIG, 1, INT_MAX, g_pserver->replicaIsolationFactor, 2, INTEGER_CONFIG, NULL, NULL),
|
||||
/* Unsigned int configs */
|
||||
createUIntConfig("maxclients", NULL, MODIFIABLE_CONFIG, 1, UINT_MAX, g_pserver->maxclients, 10000, INTEGER_CONFIG, NULL, updateMaxclients),
|
||||
createUIntConfig("loading-process-events-interval-keys", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, g_pserver->loading_process_events_interval_keys, 8192, MEMORY_CONFIG, NULL, NULL),
|
||||
createUIntConfig("maxclients-reserved", NULL, MODIFIABLE_CONFIG, 0, 100, g_pserver->maxclientsReserved, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
|
||||
/* Unsigned Long configs */
|
||||
createULongConfig("active-defrag-max-scan-fields", NULL, MODIFIABLE_CONFIG, 1, LONG_MAX, cserver.active_defrag_max_scan_fields, 1000, INTEGER_CONFIG, NULL, NULL), /* Default: keys with more than 1000 fields will be processed separately */
|
||||
@@ -2946,14 +2734,10 @@ standardConfig configs[] = {
|
||||
createLongLongConfig("latency-monitor-threshold", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->latency_monitor_threshold, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
createLongLongConfig("proto-max-bulk-len", NULL, MODIFIABLE_CONFIG, 1024*1024, LLONG_MAX, g_pserver->proto_max_bulk_len, 512ll*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Bulk request max size */
|
||||
createLongLongConfig("stream-node-max-entries", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->stream_node_max_entries, 100, INTEGER_CONFIG, NULL, NULL),
|
||||
createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, g_pserver->repl_backlog_config_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */
|
||||
createLongLongConfig("repl-backlog-disk-reserve", NULL, IMMUTABLE_CONFIG, 0, LLONG_MAX, cserver.repl_backlog_disk_size, 0, MEMORY_CONFIG, NULL, NULL),
|
||||
createLongLongConfig("max-snapshot-slip", NULL, MODIFIABLE_CONFIG, 0, 5000, g_pserver->snapshot_slip, 400, 0, NULL, NULL),
|
||||
createLongLongConfig("max-rand-count", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX/2, g_pserver->rand_total_threshold, LONG_MAX/2, 0, NULL, NULL),
|
||||
createLongLongConfig("repl-backlog-size", NULL, MODIFIABLE_CONFIG, 1, LLONG_MAX, g_pserver->repl_backlog_size, 1024*1024, MEMORY_CONFIG, NULL, updateReplBacklogSize), /* Default: 1mb */
|
||||
|
||||
/* Unsigned Long Long configs */
|
||||
createULongLongConfig("maxmemory", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),
|
||||
createULongLongConfig("maxstorage", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, g_pserver->maxstorage, 0, MEMORY_CONFIG, NULL, updateFlashMaxmemory),
|
||||
|
||||
/* Size_t configs */
|
||||
createSizeTConfig("hash-max-ziplist-entries", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, g_pserver->hash_max_ziplist_entries, 512, INTEGER_CONFIG, NULL, NULL),
|
||||
@@ -2975,15 +2759,6 @@ standardConfig configs[] = {
|
||||
createULongConfig("loading-process-events-interval-bytes", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, g_pserver->loading_process_events_interval_bytes, 2*1024*1024, MEMORY_CONFIG, NULL, NULL),
|
||||
createBoolConfig("multi-master-no-forward", NULL, MODIFIABLE_CONFIG, cserver.multimaster_no_forward, 0, validateMultiMasterNoForward, NULL),
|
||||
createBoolConfig("allow-write-during-load", NULL, MODIFIABLE_CONFIG, g_pserver->fWriteDuringActiveLoad, 0, NULL, NULL),
|
||||
createBoolConfig("force-backlog-disk-reserve", NULL, MODIFIABLE_CONFIG, cserver.force_backlog_disk, 0, NULL, NULL),
|
||||
createBoolConfig("soft-shutdown", NULL, MODIFIABLE_CONFIG, g_pserver->config_soft_shutdown, 0, NULL, NULL),
|
||||
createBoolConfig("flash-disable-key-cache", NULL, MODIFIABLE_CONFIG, g_pserver->flash_disable_key_cache, 0, NULL, NULL),
|
||||
createSizeTConfig("semi-ordered-set-bucket-size", NULL, MODIFIABLE_CONFIG, 0, 1024, g_semiOrderedSetTargetBucketSize, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
createSDSConfig("availability-zone", NULL, MODIFIABLE_CONFIG, 0, g_pserver->sdsAvailabilityZone, "", NULL, NULL),
|
||||
createIntConfig("overload-protect-percent", NULL, MODIFIABLE_CONFIG, 0, 200, g_pserver->overload_protect_threshold, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("force-eviction-percent", NULL, MODIFIABLE_CONFIG, 0, 100, g_pserver->force_eviction_percent, 0, INTEGER_CONFIG, NULL, NULL),
|
||||
createBoolConfig("enable-async-rehash", NULL, MODIFIABLE_CONFIG, g_pserver->enable_async_rehash, 1, NULL, NULL),
|
||||
createBoolConfig("enable-keydb-fastsync", NULL, MODIFIABLE_CONFIG, g_pserver->fEnableFastSync, 0, NULL, NULL),
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, g_pserver->tls_port, 0, INTEGER_CONFIG, NULL, updateTLSPort), /* TCP port. */
|
||||
@@ -2994,7 +2769,6 @@ standardConfig configs[] = {
|
||||
createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, g_pserver->tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL),
|
||||
createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, g_pserver->tls_ctx_config.prefer_server_ciphers, 0, NULL, updateTlsCfgBool),
|
||||
createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, g_pserver->tls_ctx_config.session_caching, 1, NULL, updateTlsCfgBool),
|
||||
createBoolConfig("tls-rotation", NULL, MODIFIABLE_CONFIG, g_pserver->tls_rotation, 0, NULL, updateTlsCfgBool),
|
||||
createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.cert_file, NULL, NULL, updateTlsCfg),
|
||||
createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.key_file, NULL, NULL, updateTlsCfg),
|
||||
createStringConfig("tls-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, g_pserver->tls_ctx_config.key_file_pass, NULL, NULL, updateTlsCfg),
|
||||
@@ -3038,7 +2812,7 @@ NULL
|
||||
};
|
||||
|
||||
addReplyHelp(c, help);
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"set") && c->argc >= 3) {
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"set") && c->argc == 4) {
|
||||
configSetCommand(c);
|
||||
} else if (!strcasecmp(szFromObj(c->argv[1]),"get") && c->argc == 3) {
|
||||
configGetCommand(c);
|
||||
|
||||
+6
-2
@@ -41,10 +41,14 @@
|
||||
|
||||
#define CONFIG_DEFAULT_RDB_FILENAME "dump.rdb"
|
||||
|
||||
/* Define redis_fstat to fstat */
|
||||
/* Define redis_fstat to fstat or fstat64() */
|
||||
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
|
||||
#define redis_fstat fstat64
|
||||
#define redis_stat stat64
|
||||
#else
|
||||
#define redis_fstat fstat
|
||||
#define redis_stat stat
|
||||
|
||||
#endif
|
||||
|
||||
/* Test for proc filesystem */
|
||||
#ifdef __linux__
|
||||
|
||||
+10
-33
@@ -29,7 +29,6 @@
|
||||
|
||||
#include "server.h"
|
||||
#include "connhelpers.h"
|
||||
#include "aelocker.h"
|
||||
|
||||
/* The connections module provides a lean abstraction of network connections
|
||||
* to avoid direct socket and async event management across the Redis code base.
|
||||
@@ -113,7 +112,7 @@ static int connSocketConnect(connection *conn, const char *addr, int port, const
|
||||
conn->state = CONN_STATE_CONNECTING;
|
||||
|
||||
conn->conn_handler = connect_handler;
|
||||
aeCreateFileEvent(serverTL->el, conn->fd, AE_WRITABLE|AE_WRITE_THREADSAFE,
|
||||
aeCreateFileEvent(serverTL->el, conn->fd, AE_WRITABLE,
|
||||
conn->type->ae_handler, conn);
|
||||
|
||||
return C_OK;
|
||||
@@ -161,11 +160,6 @@ static void connSocketClose(connection *conn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (conn->fprint) {
|
||||
zfree(conn->fprint);
|
||||
conn->fprint = NULL;
|
||||
}
|
||||
|
||||
zfree(conn);
|
||||
}
|
||||
|
||||
@@ -231,15 +225,14 @@ static int connSocketSetWriteHandler(connection *conn, ConnectionCallbackFunc fu
|
||||
else
|
||||
conn->flags &= ~CONN_FLAG_WRITE_BARRIER;
|
||||
|
||||
int flags = AE_WRITABLE;
|
||||
if (fThreadSafe)
|
||||
conn->flags |= CONN_FLAG_WRITE_THREADSAFE;
|
||||
else
|
||||
conn->flags &= ~CONN_FLAG_WRITE_THREADSAFE;
|
||||
flags |= AE_WRITE_THREADSAFE;
|
||||
|
||||
if (!conn->write_handler)
|
||||
aeDeleteFileEvent(serverTL->el,conn->fd,AE_WRITABLE);
|
||||
else
|
||||
if (aeCreateFileEvent(serverTL->el,conn->fd,AE_WRITABLE|AE_WRITE_THREADSAFE,
|
||||
if (aeCreateFileEvent(serverTL->el,conn->fd,flags,
|
||||
conn->type->ae_handler,conn) == AE_ERR) return C_ERR;
|
||||
return C_OK;
|
||||
}
|
||||
@@ -249,18 +242,17 @@ static int connSocketSetWriteHandler(connection *conn, ConnectionCallbackFunc fu
|
||||
*/
|
||||
static int connSocketSetReadHandler(connection *conn, ConnectionCallbackFunc func, bool fThreadSafe) {
|
||||
if (func == conn->read_handler) return C_OK;
|
||||
|
||||
|
||||
int flags = AE_READABLE;
|
||||
if (fThreadSafe)
|
||||
conn->flags |= CONN_FLAG_READ_THREADSAFE;
|
||||
else
|
||||
conn->flags &= ~CONN_FLAG_READ_THREADSAFE;
|
||||
flags |= AE_READ_THREADSAFE;
|
||||
|
||||
conn->read_handler = func;
|
||||
if (!conn->read_handler)
|
||||
aeDeleteFileEvent(serverTL->el,conn->fd,AE_READABLE);
|
||||
else
|
||||
if (aeCreateFileEvent(serverTL->el,conn->fd,
|
||||
AE_READABLE|AE_READ_THREADSAFE,conn->type->ae_handler,conn) == AE_ERR) return C_ERR;
|
||||
flags,conn->type->ae_handler,conn) == AE_ERR) return C_ERR;
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
@@ -268,7 +260,7 @@ static const char *connSocketGetLastError(connection *conn) {
|
||||
return strerror(conn->last_errno);
|
||||
}
|
||||
|
||||
void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask)
|
||||
static void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask)
|
||||
{
|
||||
UNUSED(el);
|
||||
UNUSED(fd);
|
||||
@@ -287,11 +279,7 @@ void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientData, in
|
||||
|
||||
if (!conn->write_handler) aeDeleteFileEvent(serverTL->el,conn->fd,AE_WRITABLE);
|
||||
|
||||
{
|
||||
AeLocker locker;
|
||||
locker.arm(nullptr);
|
||||
if (!callHandler(conn, conn->conn_handler)) return;
|
||||
}
|
||||
conn->conn_handler = NULL;
|
||||
}
|
||||
|
||||
@@ -313,27 +301,15 @@ void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientData, in
|
||||
|
||||
/* Handle normal I/O flows */
|
||||
if (!invert && call_read) {
|
||||
AeLocker lock;
|
||||
if (!(conn->flags & CONN_FLAG_READ_THREADSAFE))
|
||||
lock.arm(nullptr);
|
||||
|
||||
if (!callHandler(conn, conn->read_handler)) return;
|
||||
}
|
||||
/* Fire the writable event. */
|
||||
if (call_write) {
|
||||
AeLocker lock;
|
||||
if (!(conn->flags & CONN_FLAG_WRITE_THREADSAFE))
|
||||
lock.arm(nullptr);
|
||||
|
||||
if (!callHandler(conn, conn->write_handler)) return;
|
||||
}
|
||||
/* If we have to invert the call, fire the readable event now
|
||||
* after the writable one. */
|
||||
if (invert && call_read) {
|
||||
AeLocker lock;
|
||||
if (!(conn->flags & CONN_FLAG_READ_THREADSAFE))
|
||||
lock.arm(nullptr);
|
||||
|
||||
if (!callHandler(conn, conn->read_handler)) return;
|
||||
}
|
||||
}
|
||||
@@ -476,3 +452,4 @@ const char *connGetInfo(connection *conn, char *buf, size_t buf_len) {
|
||||
snprintf(buf, buf_len-1, "fd=%i", conn == NULL ? -1 : conn->fd);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ typedef enum {
|
||||
#define CONN_FLAG_WRITE_BARRIER (1<<1) /* Write barrier requested */
|
||||
#define CONN_FLAG_READ_THREADSAFE (1<<2)
|
||||
#define CONN_FLAG_WRITE_THREADSAFE (1<<3)
|
||||
#define CONN_FLAG_AUDIT_LOGGING_REQUIRED (1<<4)
|
||||
|
||||
#define CONN_TYPE_SOCKET 1
|
||||
#define CONN_TYPE_TLS 2
|
||||
@@ -87,7 +86,6 @@ struct connection {
|
||||
ConnectionCallbackFunc write_handler;
|
||||
ConnectionCallbackFunc read_handler;
|
||||
int fd;
|
||||
char* fprint;
|
||||
};
|
||||
|
||||
/* The connection module does not deal with listening and accepting sockets,
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
template <class T>
|
||||
class CowPtr
|
||||
{
|
||||
public:
|
||||
typedef std::shared_ptr<T> RefPtr;
|
||||
|
||||
private:
|
||||
mutable RefPtr m_sp;
|
||||
|
||||
void detach()
|
||||
{
|
||||
if( !( m_sp == nullptr || m_sp.unique() ) ) {
|
||||
m_sp = std::make_shared<T>(*m_sp);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CowPtr() = default;
|
||||
|
||||
CowPtr(const RefPtr& refptr)
|
||||
: m_sp(refptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(std::nullptr_t) const
|
||||
{
|
||||
return m_sp == nullptr;
|
||||
}
|
||||
|
||||
bool operator!=(std::nullptr_t) const
|
||||
{
|
||||
return m_sp != nullptr;
|
||||
}
|
||||
|
||||
const T& operator*() const
|
||||
{
|
||||
return *m_sp;
|
||||
}
|
||||
T& operator*()
|
||||
{
|
||||
detach();
|
||||
return *m_sp;
|
||||
}
|
||||
const T* operator->() const
|
||||
{
|
||||
return m_sp.operator->();
|
||||
}
|
||||
T* operator->()
|
||||
{
|
||||
detach();
|
||||
return m_sp.operator->();
|
||||
}
|
||||
};
|
||||
+6
-9
@@ -31,8 +31,7 @@ void cronCommand(client *c)
|
||||
if (getLongLongFromObjectOrReply(c, c->argv[ARG_EXPIRE], &interval, "missing expire time") != C_OK)
|
||||
return;
|
||||
|
||||
long long base;
|
||||
__atomic_load(&g_pserver->mstime, &base, __ATOMIC_ACQUIRE);
|
||||
long long base = g_pserver->mstime;
|
||||
if (getLongLongFromObject(c->argv[ARG_EXPIRE+1], &base) == C_OK) {
|
||||
arg_offset++;
|
||||
std::swap(base, interval);
|
||||
@@ -61,7 +60,7 @@ void cronCommand(client *c)
|
||||
spjob->interval = (uint64_t)interval;
|
||||
spjob->startTime = (uint64_t)base;
|
||||
spjob->fSingleShot = fSingleShot;
|
||||
spjob->dbNum = dbnumFromDb(c->db);
|
||||
spjob->dbNum = c->db - g_pserver->db;
|
||||
for (long i = 0; i < numkeys; ++i)
|
||||
spjob->veckeys.emplace_back(sdsdup(szFromObj(c->argv[ARG_KEYSTART+i])));
|
||||
for (long i = ARG_KEYSTART + numkeys; i < c->argc; ++i)
|
||||
@@ -117,25 +116,23 @@ void executeCronJobExpireHook(const char *key, robj *o)
|
||||
int dbId = job->dbNum;
|
||||
if (job->fSingleShot)
|
||||
{
|
||||
serverAssert(dbSyncDelete(cFake->db, keyobj));
|
||||
dbSyncDelete(cFake->db, keyobj);
|
||||
}
|
||||
else
|
||||
{
|
||||
job->startTime += job->interval;
|
||||
mstime_t mstime;
|
||||
__atomic_load(&g_pserver->mstime, &mstime, __ATOMIC_ACQUIRE);
|
||||
if (job->startTime < (uint64_t)mstime)
|
||||
if (job->startTime < (uint64_t)g_pserver->mstime)
|
||||
{
|
||||
// If we are more than one interval in the past then fast forward to
|
||||
// the first interval still in the future. If startTime wasn't zero align
|
||||
// this to the original startTime, if it was zero align to now
|
||||
if (job->startTime == job->interval)
|
||||
{ // startTime was 0
|
||||
job->startTime = mstime + job->interval;
|
||||
job->startTime = g_pserver->mstime + job->interval;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto delta = mstime - job->startTime;
|
||||
auto delta = g_pserver->mstime - job->startTime;
|
||||
auto multiple = (delta / job->interval)+1;
|
||||
job->startTime += job->interval * multiple;
|
||||
}
|
||||
|
||||
Loaded 100 of 223 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user