Compare commits
422
Commits
2.6.8
...
2.4.6_win32
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
387330d753 | ||
|
|
ac1cdea778 | ||
|
|
734dabb6b7 | ||
|
|
e8c6f1a74f | ||
|
|
c8c65bd39a | ||
|
|
e1849b6456 | ||
|
|
d0f37e247c | ||
|
|
3b67a0f905 | ||
|
|
2c2b6159d3 | ||
|
|
d4945b253b | ||
|
|
53785789a0 | ||
|
|
e7b85b3315 | ||
|
|
a5045d552c | ||
|
|
8939ff1230 | ||
|
|
8b102e041a | ||
|
|
273f41023d | ||
|
|
5bb2565959 | ||
|
|
0692d060b3 | ||
|
|
2e689217d5 | ||
|
|
b76088845c | ||
|
|
8a625029e7 | ||
|
|
8a6b21da61 | ||
|
|
c08eb8e74d | ||
|
|
8b860b36b3 | ||
|
|
61e44f614d | ||
|
|
bc62bc5eac | ||
|
|
67d6b29404 | ||
|
|
901120f8e6 | ||
|
|
54a2934f10 | ||
|
|
a675e5b171 | ||
|
|
0f2199ec29 | ||
|
|
241e3cff83 | ||
|
|
a9c2f0f28a | ||
|
|
7558b1fe4d | ||
|
|
fcf5783632 | ||
|
|
6532df2e7e | ||
|
|
8a82ee0f3c | ||
|
|
f4e2abfcd4 | ||
|
|
3852e2a831 | ||
|
|
6a252e9498 | ||
|
|
5d5e68a486 | ||
|
|
a6e4627438 | ||
|
|
93babe4b41 | ||
|
|
713b4c5401 | ||
|
|
51baa1922b | ||
|
|
f3ac12aac2 | ||
|
|
0b1531e50b | ||
|
|
b1b98c64cc | ||
|
|
cec6b04842 | ||
|
|
beb4bacce6 | ||
|
|
3fac86ff1d | ||
|
|
18fe946f6a | ||
|
|
9e68987f53 | ||
|
|
7e11ca0ae9 | ||
|
|
d1387aa64d | ||
|
|
7d31a020ea | ||
|
|
cacab3b1c8 | ||
|
|
072a9052c7 | ||
|
|
00af23bcbf | ||
|
|
15c9497280 | ||
|
|
45bd4a1853 | ||
|
|
0327d9b8e9 | ||
|
|
1e81d971dc | ||
|
|
9a3a84bfdf | ||
|
|
1126d5aa49 | ||
|
|
1fe4cd5de9 | ||
|
|
421f6afd3d | ||
|
|
7c6da7327f | ||
|
|
b6fe152b83 | ||
|
|
1270a13667 | ||
|
|
98f5abb6e3 | ||
|
|
ab648d973d | ||
|
|
5ab72bedb7 | ||
|
|
9a83d13fb5 | ||
|
|
6662806a26 | ||
|
|
6e3f1a9e47 | ||
|
|
d12f26c231 | ||
|
|
a502f48320 | ||
|
|
0c71939c05 | ||
|
|
a4f46f211c | ||
|
|
8185c2e9f1 | ||
|
|
71bb31bf16 | ||
|
|
c7d23f3ed1 | ||
|
|
09cefcfbb3 | ||
|
|
1ca8deebef | ||
|
|
b7f667b553 | ||
|
|
067ad37f96 | ||
|
|
a6e312bf9a | ||
|
|
0e841b998a | ||
|
|
cd0458470e | ||
|
|
678dbc48fd | ||
|
|
3a91aca033 | ||
|
|
9286ecd767 | ||
|
|
902a656f7a | ||
|
|
a58e486d79 | ||
|
|
b0fcf23304 | ||
|
|
57f611d953 | ||
|
|
20c26321c2 | ||
|
|
2017d8baf7 | ||
|
|
a511ce1b49 | ||
|
|
1f164f4e1d | ||
|
|
9f775040c8 | ||
|
|
6ca793fc21 | ||
|
|
01ac844ba2 | ||
|
|
79b9ec53d1 | ||
|
|
4c2d5f0980 | ||
|
|
056c2acaea | ||
|
|
f85c383b6c | ||
|
|
3ae56d8458 | ||
|
|
0807f5b9ff | ||
|
|
2eae7cbc7d | ||
|
|
693618f1c1 | ||
|
|
c67e8d3bf6 | ||
|
|
411cb75bcd | ||
|
|
794419192e | ||
|
|
481a05cd23 | ||
|
|
b9a65682fb | ||
|
|
e3d1c8b080 | ||
|
|
682b63312e | ||
|
|
6fd1a72712 | ||
|
|
2e48ad5422 | ||
|
|
67f594f9b5 | ||
|
|
b7bf29059e | ||
|
|
889d468e51 | ||
|
|
84f48cc401 | ||
|
|
44690ab15f | ||
|
|
a72c0423b4 | ||
|
|
c8d0013f38 | ||
|
|
5ca75751ed | ||
|
|
af14408e5c | ||
|
|
ed56ad5689 | ||
|
|
c427f5d3c1 | ||
|
|
325d2eb8b5 | ||
|
|
0fb6212cf4 | ||
|
|
f602318349 | ||
|
|
2401c3eccb | ||
|
|
8d9804bf40 | ||
|
|
da30c9e1e3 | ||
|
|
3c51d3b36b | ||
|
|
00c7f312cc | ||
|
|
b10913b93d | ||
|
|
cbf01c0e01 | ||
|
|
214cf63730 | ||
|
|
4a4f4cb5ff | ||
|
|
a57bd791e9 | ||
|
|
ed9f997ce5 | ||
|
|
55eea93cae | ||
|
|
753e91b6c1 | ||
|
|
a830d941a7 | ||
|
|
9c3a89853c | ||
|
|
7ca6672338 | ||
|
|
6c68df4f32 | ||
|
|
8b58723eb2 | ||
|
|
abd4cd7827 | ||
|
|
81c74eb48f | ||
|
|
a395ce2659 | ||
|
|
3a4afb0473 | ||
|
|
63aed54a27 | ||
|
|
64cb15799c | ||
|
|
19453556d4 | ||
|
|
91654a40e7 | ||
|
|
619174117d | ||
|
|
0462d983c2 | ||
|
|
847fc09855 | ||
|
|
d229160e18 | ||
|
|
5dc2a4885d | ||
|
|
30b345ddbb | ||
|
|
06966abc66 | ||
|
|
99509ab257 | ||
|
|
c755c637c6 | ||
|
|
1ecfcb9380 | ||
|
|
616da6585a | ||
|
|
5d5fe340e0 | ||
|
|
b04e800b98 | ||
|
|
f2ac94876d | ||
|
|
13572dc21b | ||
|
|
eb48b10738 | ||
|
|
307868e149 | ||
|
|
322ad08697 | ||
|
|
b90c72c86a | ||
|
|
61751a4dc4 | ||
|
|
4dfecf64e1 | ||
|
|
7638130d6e | ||
|
|
6c194dae58 | ||
|
|
7a4c4682f2 | ||
|
|
054ed87d85 | ||
|
|
8dcec3bf25 | ||
|
|
7dc811b8cb | ||
|
|
0f3be53d7d | ||
|
|
4e5b1d3851 | ||
|
|
27c8f84c66 | ||
|
|
32463852be | ||
|
|
567d575dbe | ||
|
|
a7982fed9e | ||
|
|
b590cedff1 | ||
|
|
02d71527d8 | ||
|
|
112569d12d | ||
|
|
82c6e9c297 | ||
|
|
fcf862d22b | ||
|
|
16d227ef2d | ||
|
|
ac1eda60b0 | ||
|
|
598387b970 | ||
|
|
8789dc82aa | ||
|
|
7f90bcfaee | ||
|
|
860e3ade2d | ||
|
|
0042a2d737 | ||
|
|
0811e89c80 | ||
|
|
fe71355df9 | ||
|
|
33d00ff7d5 | ||
|
|
c60a0f588e | ||
|
|
942354f925 | ||
|
|
cd7a497164 | ||
|
|
5fca35da3f | ||
|
|
d260b9a499 | ||
|
|
45df42c9fe | ||
|
|
6e326c81dc | ||
|
|
83533b622e | ||
|
|
913090ec63 | ||
|
|
51501514e9 | ||
|
|
09843df1dd | ||
|
|
4f8cf6a23c | ||
|
|
0d119ad3a3 | ||
|
|
6a1275bcab | ||
|
|
3769dbd722 | ||
|
|
afc151d3ba | ||
|
|
6ac57f16cb | ||
|
|
61a707af39 | ||
|
|
5cb0558e8c | ||
|
|
e5dbbc20f1 | ||
|
|
1f036c6587 | ||
|
|
7dd1e95288 | ||
|
|
ecfc0583da | ||
|
|
ff8d85885e | ||
|
|
d89db584fa | ||
|
|
1f2af715cb | ||
|
|
78c05caf5e | ||
|
|
10e4b58312 | ||
|
|
de6ba77bc4 | ||
|
|
5d64f21792 | ||
|
|
86277eee4c | ||
|
|
24430cb820 | ||
|
|
604d26ffde | ||
|
|
338e44a6e7 | ||
|
|
de77addb36 | ||
|
|
9f517b920e | ||
|
|
d0f710ea09 | ||
|
|
d0d72c19d3 | ||
|
|
da49700d5a | ||
|
|
ba07e1f505 | ||
|
|
8ec9d6ef69 | ||
|
|
deff2338fa | ||
|
|
2ee7dd362c | ||
|
|
fddd64e610 | ||
|
|
d275513b04 | ||
|
|
2618476032 | ||
|
|
c924df8c83 | ||
|
|
01f36192c8 | ||
|
|
6e258c76bf | ||
|
|
355d8cb0b5 | ||
|
|
f1e072c57d | ||
|
|
a244985d1d | ||
|
|
b83a52c6f8 | ||
|
|
20dad91bdf | ||
|
|
d548f3daad | ||
|
|
992fe21706 | ||
|
|
2f170ad733 | ||
|
|
29a87f23b1 | ||
|
|
0882fe5e43 | ||
|
|
7e5cf2660c | ||
|
|
91867a5ea2 | ||
|
|
1c30cad1ab | ||
|
|
bd50301f3e | ||
|
|
760dec3a6c | ||
|
|
e8852d782d | ||
|
|
1f84b0648b | ||
|
|
73f01b0606 | ||
|
|
552a6fb39d | ||
|
|
5045ff843a | ||
|
|
c92283770d | ||
|
|
44d4b87f1f | ||
|
|
cebac46589 | ||
|
|
68495c46fe | ||
|
|
3dffb8e21e | ||
|
|
1c53928fca | ||
|
|
660a0cdf02 | ||
|
|
01fc59b787 | ||
|
|
ff8187844a | ||
|
|
1dabf116d8 | ||
|
|
5b97a708fc | ||
|
|
17f41d49b4 | ||
|
|
8514f3c035 | ||
|
|
3fa46ef069 | ||
|
|
3c2fe3f88d | ||
|
|
687054849e | ||
|
|
577b084e3c | ||
|
|
6a9764d183 | ||
|
|
c138dc7da4 | ||
|
|
1b905277bb | ||
|
|
cf7a138684 | ||
|
|
edfd6ae76b | ||
|
|
408e12dcaa | ||
|
|
dfeb899b2d | ||
|
|
2b886275e9 | ||
|
|
93db956e06 | ||
|
|
27fc6199ca | ||
|
|
0b01578d2e | ||
|
|
920c45b818 | ||
|
|
cf6c3f4b04 | ||
|
|
fb90934c47 | ||
|
|
207ca3cedd | ||
|
|
97d3b7dc8d | ||
|
|
abdbfc14c0 | ||
|
|
724376a0df | ||
|
|
476f044db6 | ||
|
|
8b2449dca0 | ||
|
|
763d6165ea | ||
|
|
2e18193f5b | ||
|
|
7f9c973467 | ||
|
|
366e872d4d | ||
|
|
4d8f13ddc3 | ||
|
|
3e518093b6 | ||
|
|
99cec798bb | ||
|
|
ccf96e197b | ||
|
|
8f0a41be98 | ||
|
|
63b29390b2 | ||
|
|
46aa0e2472 | ||
|
|
07d9e35637 | ||
|
|
2e12082b3e | ||
|
|
08532b1e99 | ||
|
|
771ea6e1a6 | ||
|
|
c5802e3f4a | ||
|
|
eab8382d18 | ||
|
|
efc77ad91c | ||
|
|
a3886e29a6 | ||
|
|
db912c5a93 | ||
|
|
2165ac5573 | ||
|
|
f8224a4f90 | ||
|
|
679de7ae2f | ||
|
|
efc3f1783d | ||
|
|
0b8c9eac68 | ||
|
|
4c36024f51 | ||
|
|
8e1db6a05f | ||
|
|
d3882605c0 | ||
|
|
3d6eade781 | ||
|
|
6b017e615f | ||
|
|
3164869d78 | ||
|
|
b38c299886 | ||
|
|
5b40d6e656 | ||
|
|
8c3b630561 | ||
|
|
4484e3b591 | ||
|
|
453a906fc9 | ||
|
|
de5d4be994 | ||
|
|
69bfdcf7d4 | ||
|
|
699465c31e | ||
|
|
45332cc47b | ||
|
|
2d2c17b075 | ||
|
|
048c0f0f37 | ||
|
|
3eb3cc9da4 | ||
|
|
74f6ee8dd5 | ||
|
|
7a55d72f28 | ||
|
|
4e4b3b943c | ||
|
|
61e2547675 | ||
|
|
595b5974f8 | ||
|
|
58418d7c3e | ||
|
|
c02bda324f | ||
|
|
1afef16d88 | ||
|
|
7377aacd84 | ||
|
|
eece2d528c | ||
|
|
eb5aeaf138 | ||
|
|
1a6281f52f | ||
|
|
c2571b314a | ||
|
|
97aeda9828 | ||
|
|
ca9d961073 | ||
|
|
2446fbb3bb | ||
|
|
970e813be2 | ||
|
|
a36d524562 | ||
|
|
bbaf76ea2f | ||
|
|
669f302e16 | ||
|
|
71791e7a8e | ||
|
|
2f91d7ada4 | ||
|
|
5ef8f9531d | ||
|
|
3ce30a6906 | ||
|
|
65e79a8272 | ||
|
|
ef8bc13d0d | ||
|
|
0ffeadd290 | ||
|
|
db0e263b42 | ||
|
|
d526d09e41 | ||
|
|
9738e587da | ||
|
|
da67b5f59a | ||
|
|
825d2904d1 | ||
|
|
3becef9ee6 | ||
|
|
2ccf6e81a6 | ||
|
|
e18b59ae7e | ||
|
|
8ff1353766 | ||
|
|
99677e89d0 | ||
|
|
f43502fb0e | ||
|
|
ebb07fb4bb | ||
|
|
c13c4080e5 | ||
|
|
360664c9c8 | ||
|
|
7ac1b364fe | ||
|
|
1b2b8cbbde | ||
|
|
f487bbbfe2 | ||
|
|
4b2499cf2f | ||
|
|
01a0b09886 | ||
|
|
90419b5681 | ||
|
|
ae7db3fe83 | ||
|
|
454f4bfd28 | ||
|
|
1408d147e4 | ||
|
|
78055bd4ca | ||
|
|
736d447380 | ||
|
|
71b6f64f2a | ||
|
|
c4e93cadfb | ||
|
|
0540df22ba | ||
|
|
9ac5be2ebe | ||
|
|
b902c1413e | ||
|
|
54e9f28922 | ||
|
|
dd889d884f | ||
|
|
1443a814d0 | ||
|
|
076ff11c06 | ||
|
|
6418b4c790 | ||
|
|
4769dc7826 | ||
|
|
039357e471 |
+20
-2
@@ -9,13 +9,31 @@ redis-check-aof
|
||||
doc-tools
|
||||
mkrelease.sh
|
||||
release
|
||||
myredis.conf
|
||||
misc/*
|
||||
src/release.h
|
||||
appendonly.aof
|
||||
SHORT_TERM_TODO
|
||||
redis.conf.*
|
||||
release.h
|
||||
src/transfer.sh
|
||||
src/configs
|
||||
src/redis-server.dSYM
|
||||
*.user
|
||||
*.exe
|
||||
*.sdf
|
||||
*.suo
|
||||
deps/pthreads-win32/
|
||||
msvs/Debug/
|
||||
msvs/Release/
|
||||
msvs/RedisBenchmark/Debug/
|
||||
msvs/RedisBenchmark/Release/
|
||||
msvs/RedisCheckAof/Debug/
|
||||
msvs/RedisCheckAof/Release/
|
||||
msvs/RedisCheckDump/Debug/
|
||||
msvs/RedisCheckDump/Release/
|
||||
msvs/RedisCli/Debug/
|
||||
msvs/RedisCli/Release/
|
||||
msvs/hiredis/Debug/
|
||||
msvs/hiredis/Release/
|
||||
msvs/ipch
|
||||
msvs/RedisServer.opensdf
|
||||
|
||||
|
||||
+165
-30
@@ -1,38 +1,173 @@
|
||||
Welcome to Redis 2.1.9 (2.2 Release Candidate 1)
|
||||
Redis 2.4 release notes
|
||||
|
||||
This is the first Release Candidate of Redis 2.2, in our experience the
|
||||
server is very stable, but in the latest weeks we rewrote part of the internals
|
||||
in order to use a lot less memory while the saving child process is performing
|
||||
a BGREWRITEAOF or a BGSAVE, so handle with care for a couple of weeks.
|
||||
Migrating from 2.2 to 2.4
|
||||
=========================
|
||||
|
||||
Oh, and I've some very good news: the majority of apps can work if you simply replace 2.2 in your old 2.0 environment. I can't think of any breakage.
|
||||
Redis 2.2 is mostly a strict subset of 2.4.
|
||||
The only thing you should be aware is that you can't use .rdb and AOF files
|
||||
generated with 2.4 into a 2.2 instance.
|
||||
|
||||
WHAT'S NEW IN REDIS 2.2.x
|
||||
2.4 slaves can be attached to 2.2 masters, but not the contrary, and only for
|
||||
the time needed to perform the version upgrade.
|
||||
|
||||
* Specially encoded data types, small lists and sets can now use up to an order of magnitude less memory.
|
||||
* VM partial rewrite for code cleaness and memory usage.
|
||||
* Change to the implementation of the top level dictionary for better memory efficienty.
|
||||
* redis-cli is hugely improved: tab completion, inline help (Thanks to TJ Holowaychuk), raw output, rewritten using the new hiredis C library.
|
||||
* Networking internals rewritten for efficiency. You can expect LRANGE and similar commands to be at least 10 times faster.
|
||||
* Most read only commands are now copy-on-write friendly, this means that Redis will use little memory when a saving child is active and the parent process is mostly stressed by read queries.
|
||||
* Non blocking replication even from the point of view of the slave, with configurable behavior about what to do when the link is disconnected. You can select if serving old data or replying with an error.
|
||||
* Check-and-set (CAS) transactions with the new WATCH command.
|
||||
* Now write operations work against keys with an EXPIRE set! Imagine the possibilities.
|
||||
* New maxmemory eviction policies. It is possible to select among LRU, farest TTL expire, and other algorithms, and if when the memory limit is reached only keys with an expire set or all the keys should be expired.
|
||||
* SETBIT / GETBIT / SETRANGE / GETRANGE / STRLEN. Now your strings are your arrays!
|
||||
* Syslog support (Thanks to Jonah H. Harris)
|
||||
* Unix domain socket support.
|
||||
* New List related functions LINSERT, LPUSHX, RPUSHX (Thanks to Robey Pointer)
|
||||
* BRPOPLPUSH (Thanks to Michel Martens and Damian Janowski)
|
||||
* Much more interesting informations in the INFO output.
|
||||
* Sorted sets are now less memory hungry.
|
||||
* Non blocking loading of .rdb / AOF file on startup, with process information in the INFO output.
|
||||
* Now Redis has a clean, powerful, supported C library: hiredis.
|
||||
* Code layout completely new, the 2.0.x huge redis.c file is now splitted in many parts.
|
||||
* Redis-benchmark rewritten to be faster and in order to use hiredis as well.
|
||||
* Endless other CPU optimizations and bugs fixed.
|
||||
From the point of view of the API Redis 2.4 only adds new commands
|
||||
(other commands now accepts a variable number of arguments) so you don't need
|
||||
to modify your program in order to use Redis 2.4.
|
||||
|
||||
Credits: Where not specified the implementation and design are done by Salvatore Sanfilippo and Pieter Noordhuis. Thanks to VMware for making all this possible. Also many thanks to all the other contributors and the amazing community we have.
|
||||
---------
|
||||
CHANGELOG
|
||||
---------
|
||||
|
||||
What's new in Redis 2.4.6
|
||||
=========================
|
||||
|
||||
* [BUGFIX] Fixed issue #141 part 1: Possible protocol desyncs when clients send
|
||||
wrong protocol is now fixed. (See issue 141 for more details)
|
||||
* [BUGFIX] Fixed issue #141 part 2: Connection of multiple slaves used to result
|
||||
from time to time into corrupted protocol send to slaves connected
|
||||
after the first one. (See issue 141 for more details)
|
||||
* [BUGFIX] Do not propagate DEBUG LOADAOF.
|
||||
* New INFO contains information such as ip/port/state for every conneced slave.
|
||||
* Show GCC version in INFO output.
|
||||
|
||||
What's new in Redis 2.4.5
|
||||
=========================
|
||||
|
||||
* [BUGFIX] Fixed a ZUNIONSTORE/ZINTERSTORE bug that can cause a NaN to be
|
||||
inserted as a sorted set element score. This happens when one of the
|
||||
elements has +inf/-inf score and the weight used is 0.
|
||||
* [BUGFIX] Fixed memory leak in CLIENT INFO.
|
||||
* [BUGFIX] Fixed a non critical SORT bug (Issue 224).
|
||||
* [BUGFIX] Fixed a replication bug: now the timeout configuration is respected
|
||||
during the connection with the master.
|
||||
* --quiet option implemented in the Redis test.
|
||||
|
||||
What's new in Redis 2.4.4
|
||||
=========================
|
||||
|
||||
* [BUGFIX] jemalloc upgraded to version 2.2.5, previous versions had a
|
||||
potentially serious issue when allocating big memory areas, something that
|
||||
Redis actually does. However we never received bug reports that appear
|
||||
to be caused by jemalloc.
|
||||
* [BUGFIX] DISCARD now clears DIRTY_CAS flag in the client. Now the next
|
||||
transaction will not fail if the previous transaction used WATCH and
|
||||
the key was touched.
|
||||
* CLIENT LIST output modified to include the last command executed by clients.
|
||||
* Better bug report on crash.
|
||||
* Protocol errors are now logged for loglevel >= verbose.
|
||||
* Two new INFO fields related to AOF, that can be useful when investigating
|
||||
Redis issues.
|
||||
|
||||
What's new in Redis 2.4.3
|
||||
=========================
|
||||
|
||||
* redis-cli now supports 'single quotes' style strings.
|
||||
* It is possible to disable password auth with CONFIG SET.
|
||||
* Some section of redis.conf better documented.
|
||||
* Default timeout for client connections now is 0 (no timeout).
|
||||
* I/O buffer length modified for better performances with big payloads.
|
||||
* Fixed crash on SPARC due to improper alighment due to bad assumptions about data types size.
|
||||
* CLIENT LIST output improved, code refactored.
|
||||
* [BUGFIX] Max log message length set to 4k to avoid truncation in INFO output written in the log file after a crash.
|
||||
* [BUGFIX] Close client connection when the query buffer reaches 1GB and log informaiton about the offending client, instead of crashing the instance when the query buffer reaches 2GB (for sds.c string overflow). This is related to issue #141 (github issues) and should fix the effect of this rare bug related to replication, but the cause is still not perfectly clear.
|
||||
|
||||
What's new in Redis 2.4.2
|
||||
=========================
|
||||
|
||||
* [BUGFIX] Unix socket creation mask config directive fixed.
|
||||
* [BUGFIX] Fixed a bug that forced Redis to continously rewrite the AOF file
|
||||
when a manual BGREWRITEAOF was issued during a BGSAVE was
|
||||
still in progress.
|
||||
* [BUGFIX] Fixed a server crash resulting from bad handling of SLAVEOF NO ONE.
|
||||
* [BUGFIX] Fixed a memory leak in redis-cli.
|
||||
* Debian/Ubuntu Redis system-wide installation script added under /utils.
|
||||
|
||||
What's new in Redis 2.4.1
|
||||
=========================
|
||||
|
||||
* [BUGFIX] FLUSHALL was not replicated nor written into the Append Only File.
|
||||
* [BUGFIX] FLUSHALL now only performs a sync SAVE if there is at least
|
||||
one save point configured.
|
||||
|
||||
What's new in Redis 2.4.0
|
||||
=========================
|
||||
|
||||
* [BUGFIX] redis-cli segfault with single numerical argument fixed.
|
||||
* [BUGFIX] OpenBSD compilation problem fixed.
|
||||
* [BUGFIX] More robust Redis test, with better random port selection.
|
||||
* [BUGFIX] Fix for bug #128 about the RENAME command.
|
||||
* [BUGFIX] Fixed Issue #131. stime/utime reported in INFO was inverted.
|
||||
* [BUGFIX] Unlink Unix socket file on shutdown.
|
||||
* [BUGFIX] AUTH now returns error if no password is set on the server.
|
||||
* [BUGFIX] Exit with Fatal error at startup on RDB loading errors.
|
||||
* redis-check-dump: RDB version 2 now supported.
|
||||
* More informative error when DEBUG RELOAD fails.
|
||||
* Added a config directive for a Unix socket mask.
|
||||
* CONFIG SET/GET for loglevel.
|
||||
|
||||
What's new in Redis 2.3.11 (2.4 Release Candidate 8)
|
||||
====================================================
|
||||
|
||||
* [BUGFIX] Fixed a rare but possible AOF race condition that could result into
|
||||
duplicated commands inside the AOF.
|
||||
* [BUGFIX] Fixed issue 620, don't segfault on corrupted (by hand) AOF.
|
||||
* [BUGFIX] Fixed compilation on Mac/PPC.
|
||||
* [BUGFIX] Don't replicate SAVE.
|
||||
* LRANGE optimization may drastically improve performances when querying the
|
||||
final part of a long list.
|
||||
* redis-cli now implements a --latency mode to monitory Redis delay.
|
||||
* Hash type settings removed from INFO (same info is available via config GET)
|
||||
* Include port number on error when can't bind.
|
||||
* AOF fsync is now performed in background when fsync policy is 'everysec'.
|
||||
* AOF performances improved moving in background a possibly slow close(2) call.
|
||||
* AOF protocol synthesis speedup.
|
||||
|
||||
What's new in Redis 2.3.10 (2.4 Release Candidate 7)
|
||||
====================================================
|
||||
|
||||
* [BUGFIX] Fixed issue 593 (BRPOPLPUSH related crash).
|
||||
* [BUGFIX] Fixed an issue with the networking layer that may prevent Redis from sending the whole reply back to client under extreme conditions.
|
||||
|
||||
What's new in Redis 2.3.9 (2.4 Release Candidate 6)
|
||||
===================================================
|
||||
|
||||
* [BUGFIX] Fixed a bug with the automatic AOF rewrite causing continuous
|
||||
rewrites for AOF files bigger than 4 GB.
|
||||
* New maxmemory tests.
|
||||
|
||||
What's new in Redis 2.3.8 (2.4 Release Candidate 5)
|
||||
===================================================
|
||||
|
||||
Compared to Redis 2.3.7 (RC4) there are the following changes:
|
||||
|
||||
* [BUGFIX] HDEL: Abort deleting fields when hash is removed.
|
||||
* [BUGFIX] Fix adding bulk reply when getcwd fails.
|
||||
|
||||
WHAT'S NEW IN REDIS 2.4 compared to the 2.2 version?
|
||||
====================================================
|
||||
|
||||
* Specially encoded sorted sets, now small sorted sets will use little memory.
|
||||
* Native persistence of specially encoded data types (ziplists, zipmaps,
|
||||
intsets). Many data sets will be saved and loaded an order of magnitude
|
||||
faster.
|
||||
* Variadic versions of commands: SADD, HDEL, SREM, ZREM, ZADD, L/RPUSH.
|
||||
* Jemalloc support (enabled by default for Linux build) in order to
|
||||
avoid fragmentation issues.
|
||||
* Reduced memory usage while saving.
|
||||
* More info fields (peak memory, fork time, ...)
|
||||
* OBJECT command for objects introspection.
|
||||
* CLIENT command for clients introspection.
|
||||
* Non blocking slave -> master connection.
|
||||
* Better redis-cli connection handling. New redis-cli features.
|
||||
* Better redis-benchmark, now able to benchmark user provided commands.
|
||||
* Colorized Make.
|
||||
* VM deprecated. Still supported but with a big warning... don't use it.
|
||||
* Many speed optimizations and bug fixes.
|
||||
|
||||
Credits: Where not specified the implementation and design are done by
|
||||
Salvatore Sanfilippo and Pieter Noordhuis. Thanks to VMware for making all
|
||||
this possible. Also many thanks to all the other contributors and the amazing
|
||||
community we have.
|
||||
|
||||
Cheers,
|
||||
Salvatore
|
||||
|
||||
@@ -1,30 +1 @@
|
||||
To compile Redis, do the following:
|
||||
|
||||
cd src; make
|
||||
|
||||
The compilation will produce a redis-server binary.
|
||||
|
||||
To install Redis, use
|
||||
|
||||
make install
|
||||
|
||||
and all the binaries will be installed on /usr/local/bin.
|
||||
|
||||
Alternatively:
|
||||
|
||||
make PREFIX=/some/other/directory
|
||||
|
||||
to have the binaries in /some/other/directory/bin.
|
||||
|
||||
Run the server using the following command line:
|
||||
|
||||
/path/to/redis-server
|
||||
|
||||
This will start a Redis server with the default configuration.
|
||||
|
||||
Otherwise if you want to provide your configuration use:
|
||||
|
||||
/path/to/redis-server /path/to/redis.conf
|
||||
|
||||
You can find an example redis.conf file in the root directory
|
||||
of this source distribution.
|
||||
Please check the README file.
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
Where to find complete Redis documentation?
|
||||
-------------------------------------------
|
||||
|
||||
This README is just a fast "quick start" document. You can find more detailed
|
||||
documentation here:
|
||||
|
||||
1) http://code.google.com/p/redis
|
||||
2) Check the 'doc' directory. doc/README.html is a good starting point :)
|
||||
|
||||
Building Redis
|
||||
--------------
|
||||
|
||||
It is as simple as:
|
||||
|
||||
% make
|
||||
|
||||
Redis is just a single binary, but if you want to install it you can use
|
||||
the "make install" target that will copy the binary in /usr/local/bin
|
||||
for default. You can also use "make PREFIX=/some/other/directory install"
|
||||
if you wish to use a different destination.
|
||||
|
||||
You can run a 32 bit Redis binary using:
|
||||
|
||||
% make 32bit
|
||||
|
||||
After you build Redis is a good idea to test it, using:
|
||||
|
||||
% make test
|
||||
|
||||
Buliding using tcmalloc
|
||||
-----------------------
|
||||
|
||||
tcmalloc is a fast and space efficient implementation (for little objects)
|
||||
of malloc(). Compiling Redis with it can improve performances and memeory
|
||||
usage. You can read more about it here:
|
||||
|
||||
http://goog-perftools.sourceforge.net/doc/tcmalloc.html
|
||||
|
||||
In order to compile Redis with tcmalloc support install tcmalloc on your system
|
||||
and then use:
|
||||
|
||||
% make USE_TCMALLOC=yes
|
||||
|
||||
Note that you can pass any other target to make, as long as you append
|
||||
USE_TCMALLOC=yes at the end.
|
||||
|
||||
Running Redis
|
||||
-------------
|
||||
|
||||
To run Redis with the default configuration just type:
|
||||
|
||||
% cd src
|
||||
% ./redis-server
|
||||
|
||||
If you want to provide your redis.conf, you have to run it using an additional
|
||||
parameter (the path of the configuration file):
|
||||
|
||||
% cd src
|
||||
% ./redis-server /path/to/redis.conf
|
||||
|
||||
Playing with Redis
|
||||
------------------
|
||||
|
||||
You can use redis-cli to play with Redis. Start a redis-server instance,
|
||||
then in another terminal try the following:
|
||||
|
||||
% cd src
|
||||
% ./redis-cli
|
||||
redis> ping
|
||||
PONG
|
||||
redis> set foo bar
|
||||
OK
|
||||
redis> get foo
|
||||
"bar"
|
||||
redis> incr mycounter
|
||||
(integer) 1
|
||||
redis> incr mycounter
|
||||
(integer) 2
|
||||
redis>
|
||||
|
||||
You can find the list of all the available commands here:
|
||||
|
||||
http://code.google.com/p/redis/wiki/CommandReference
|
||||
|
||||
Enjoy!
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
Redis on Windows prototype
|
||||
===
|
||||
## What's new in this release
|
||||
|
||||
- The project is now a fork of https://github.com/antirez/redis
|
||||
- libuv dependency was removed in favor of calling Win32 APIs directly. This was done in order to make integrations with upstream repo more manageable.
|
||||
- Improvement to the snapshotting (save on disk) algorithm. The code now serializes to buffer on a background thread then writes to disk. The main thread is blocked only on writes while the background thread is writing to buffer. This is an improvement to a full block where the snapshot was written to disk on the main thread.
|
||||
- Improved unit tests pass rate
|
||||
|
||||
===
|
||||
Special thanks to Dušan Majkic (https://github.com/dmajkic, https://github.com/dmajkic/redis/) for his project on GitHub that gave us the opportunity to quickly learn some on the intricacies of Redis code. His project also helped us to build our prototype quickly.
|
||||
|
||||
|
||||
## How to build Redis
|
||||
|
||||
|
||||
### Build pthreads library using Visual Studio
|
||||
|
||||
You can use the free Express Edition available at http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-express.
|
||||
|
||||
Redis uses pthreads. The build assumes it is linked as a static library.
|
||||
You need to get Windows pthreads sources and build as a static library.
|
||||
|
||||
1. You can download the code from ftp://sourceware.org/pub/pthreads-win32/pthreads-w32-2-8-0-release.exe.
|
||||
|
||||
2. Extract the files to a folder on your local drive.
|
||||
|
||||
3. From your local folder, copy all the files from Pre-built.2/include to your redis folder under
|
||||
deps/pthreads-win32/include (the directory needs to be created).
|
||||
|
||||
4. From Visual Studio, do Open Project, and navigate to the installed folder, and then pthreads.2.
|
||||
|
||||
5. Open the pthread project file (pthread.dsp).
|
||||
|
||||
6. VS will ask to convert to the current version format. Select Yes.
|
||||
|
||||
7. Open project properties and change the following:
|
||||
|
||||
- Change configuration type from Dynamic Library (.dll) to Static Library (.lib)
|
||||
- Change Target Extension for .dll to .lib
|
||||
- Under C-C++/Code Generation, change Runtime Library choice to Multi-threaded (/MT)
|
||||
|
||||
8. Build the project. This will create a pthread.lib under pthreads.2 directory.
|
||||
|
||||
9. Copy pthread.lib to your redis folder under deps/pthreads-win32/lib/release|debug (you have to create the directories).
|
||||
|
||||
### Build Redis using Visual Studio
|
||||
|
||||
- Open the solution file msvs\redisserver.sln in Visual Studio 10, and build.
|
||||
|
||||
This should create the following executables in the msvs\$(Configuration) folder:
|
||||
|
||||
- redis-server.exe
|
||||
- redis-benchmark.exe
|
||||
- redis-cli.exe
|
||||
- redis-check-dump.exe
|
||||
- redis-check-aof.exe
|
||||
|
||||
If there is an error in finding the pthreads.lib file, check that you have selected the same configuration as used for building the pthreads.lib file, and that you copied it to the expected location.
|
||||
|
||||
### Release Notes
|
||||
|
||||
This is a pre-release version of the software and is not fully tested.
|
||||
This is intended to be a 32bit release only. No work has been done in order to produce a 64bit version of Redis on Windows.
|
||||
To run the test suite requires some manual work:
|
||||
|
||||
- The tests assume that the binaries are in the src folder, so you need to copy the binaries from the msvs folder to src.
|
||||
- The tests make use of TCL. This must be installed separately.
|
||||
- To run the tests in a command window: `tclsh8.5.exe tests/test_helper.tcl`.
|
||||
|
||||
### Plan for the next release
|
||||
|
||||
- Update code base to Redis 2.4.7
|
||||
- We plan to improve further the snapshotting process. We are working on some ideas on how to we can simulate the Copy-On-Write feature at the application level.
|
||||
|
||||
@@ -1,66 +1,4 @@
|
||||
Redis TODO and Roadmap
|
||||
----------------------
|
||||
This is a stable release! No TODO file here.
|
||||
Please check the TODO file in the master branch on github.
|
||||
|
||||
VERSION 2.2 TODO (Optimizations and latency)
|
||||
============================================
|
||||
|
||||
* Support for syslog(3).
|
||||
* Change the implementation of ZCOUNT to use the augmented skiplist in order to be much faster.
|
||||
* Add an explicit test for MULTI/EXEC reloaded in the AOF.
|
||||
* Command table -> hash table, with support for command renaming
|
||||
|
||||
VM TODO
|
||||
=======
|
||||
|
||||
* Use multiple open FDs against the VM file, one for thread.
|
||||
* Check what happens performance-wise if instead of creating threads again and again the same threads are reused forever. Note: this requires a way to disable this clients in the child, but waiting for empty new jobs queue can be enough.
|
||||
|
||||
STRING COMMANDS
|
||||
===============
|
||||
|
||||
* Implement STRLEN, PEEK, POKE, SETBIT, GETBIT
|
||||
|
||||
OTHER IMPORTANT THINGS THAT WILL BE ADDED BUT I'M NOT SURE WHEN
|
||||
===============================================================
|
||||
|
||||
BIG ONES:
|
||||
|
||||
* BRPOPLPUSH
|
||||
* Specially encoded memory-saving integer sets.
|
||||
* A command to export a JSON dump (there should be mostly working patch needing major reworking).
|
||||
* Specially encoded sets of integers (this includes a big refactoring providing an higher level layer for Sets manipulation)
|
||||
|
||||
SMALL ONES:
|
||||
|
||||
* If sizeof(double) == sizeof(void*) we could store the double value of sorted sets directly in place of the pointer instead of allocating it in the heap.
|
||||
* Delete on writes against expire policy should only happen after argument parsing for commands doing their own arg parsing stuff.
|
||||
* Give errors when incrementing a key that does not look like an integer, when providing as a sorted set score something can't be parsed as a double, and so forth.
|
||||
* MSADD (n keys) (n values). See this thread in the Redis google group: http://groups.google.com/group/redis-db/browse_thread/thread/e766d84eb375cd41
|
||||
* Don't save empty lists / sets / zsets on disk with snapshotting.
|
||||
* Remove keys when a list / set / zset reaches length of 0.
|
||||
* An option to exec a command slave-side if the master connection is lost: even cooler: if the script returns "0" the slave elects itself as master, otherwise continue trying to reconnect.
|
||||
* PING the master from time to time to check if it's gone.
|
||||
|
||||
THE "MAYBE" TODO LIST: things that may or may not get implemented
|
||||
=================================================================
|
||||
|
||||
Most of this can be seen just as proposals, the fact they are in this list
|
||||
it's not a guarantee they'll ever get implemented ;)
|
||||
|
||||
* SORT: Don't copy the list into a vector when BY argument is constant.
|
||||
* Write the hash table size of every db in the dump, so that Redis can resize the hash table just one time when loading a big DB.
|
||||
* Byte Array type (BA prefixed commands): BASETBIT BAGETBIT BASETU8 U16 U32 U64 S8 S16 S32 S64, ability to atomically INCRBY all the base types. BARANGE to get a range of bytes as a bulk value, BASETRANGE to set a range of bytes.
|
||||
* Read-only mode.
|
||||
* Kill the delete-on-write behavior of expires, replicating DELs
|
||||
* Multiple BY in SORT.
|
||||
|
||||
KNOWN BUGS
|
||||
==========
|
||||
|
||||
* LRANGE and other commands are using 32 bit integers for ranges, and overflows are not detected. So LRANGE mylist 0 23498204823094823904823904 will have random effects.
|
||||
|
||||
REDIS CLI TODO
|
||||
==============
|
||||
|
||||
* Computer parsable output generation
|
||||
* Memoize return values so that they can be used later as arguments, like $1
|
||||
https://github.com/antirez/redis/raw/master/TODO
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
An updated list of client libraries for Redis can be found here:
|
||||
|
||||
http://code.google.com/p/redis
|
||||
|
||||
All the links are in the front page.
|
||||
|
||||
Most mainstream (and less mainstream) languages are supported.
|
||||
|
||||
If you plan to write a new client library for a missing language, please
|
||||
get in touch with the other developers writing a message to the Redis google
|
||||
group that is located here: http://groups.google.com/group/redis-db
|
||||
Vendored
+7
-4
@@ -15,8 +15,9 @@ ifeq ($(uname_S),SunOS)
|
||||
DYLIB_MAKE_CMD?=$(CC) -G -o ${DYLIBNAME} ${OBJ}
|
||||
STLIBNAME?=libhiredis.a
|
||||
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
|
||||
else ifeq ($(uname_S),Darwin)
|
||||
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
|
||||
else
|
||||
ifeq ($(uname_S),Darwin)
|
||||
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
|
||||
CCLINK?=-lm -pthread
|
||||
LDFLAGS?=-L. -Wl,-rpath,.
|
||||
OBJARCH?=-arch i386 -arch x86_64
|
||||
@@ -25,7 +26,7 @@ else ifeq ($(uname_S),Darwin)
|
||||
STLIBNAME?=libhiredis.a
|
||||
STLIB_MAKE_CMD?=libtool -static -o ${STLIBNAME} - ${OBJ}
|
||||
else
|
||||
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
|
||||
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wstrict-prototypes -Wwrite-strings $(ARCH) $(PROF)
|
||||
CCLINK?=-lm -pthread
|
||||
LDFLAGS?=-L. -Wl,-rpath,.
|
||||
DYLIBNAME?=libhiredis.so
|
||||
@@ -33,6 +34,8 @@ else
|
||||
STLIBNAME?=libhiredis.a
|
||||
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
|
||||
endif
|
||||
endif
|
||||
|
||||
CCOPT= $(CFLAGS) $(CCLINK)
|
||||
DEBUG?= -g -ggdb
|
||||
|
||||
@@ -45,7 +48,7 @@ all: ${DYLIBNAME} ${BINS}
|
||||
|
||||
# Deps (use make dep to generate this)
|
||||
net.o: net.c fmacros.h net.h
|
||||
async.o: async.c async.h hiredis.h sds.h util.h
|
||||
async.o: async.c async.h hiredis.h sds.h util.h dict.c dict.h
|
||||
example.o: example.c hiredis.h
|
||||
hiredis.o: hiredis.c hiredis.h net.h sds.h util.h
|
||||
sds.o: sds.c sds.h
|
||||
|
||||
Vendored
+2
-2
@@ -108,7 +108,7 @@ was received:
|
||||
* **`REDIS_REPLY_ARRAY`**:
|
||||
* A multi bulk reply. The number of elements in the multi bulk reply is stored in
|
||||
`reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well
|
||||
and can be accessed via `reply->elements[..index..]`.
|
||||
and can be accessed via `reply->element[..index..]`.
|
||||
Redis may reply with nested arrays but this is fully supported.
|
||||
|
||||
Replies should be freed using the `freeReplyObject()` function.
|
||||
@@ -171,7 +171,7 @@ the latter means an error occurred while reading a reply. Just as with the other
|
||||
the `err` field in the context can be used to find out what the cause of this error is.
|
||||
|
||||
The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
|
||||
a single call to `write(2)`):
|
||||
a single call to `read(2)`):
|
||||
|
||||
redisReply *reply;
|
||||
redisAppendCommand(context,"SET foo bar");
|
||||
|
||||
Vendored
+286
-55
@@ -30,14 +30,65 @@
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include "async.h"
|
||||
#include "dict.c"
|
||||
#include "sds.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
#endif
|
||||
|
||||
/* Forward declaration of function in hiredis.c */
|
||||
void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
|
||||
|
||||
/* Functions managing dictionary of callbacks for pub/sub. */
|
||||
static unsigned int callbackHash(const void *key) {
|
||||
return dictGenHashFunction((unsigned char*)key,sdslen((char*)key));
|
||||
}
|
||||
|
||||
static void *callbackValDup(void *privdata, const void *src) {
|
||||
redisCallback *dup = malloc(sizeof(*dup));
|
||||
((void) privdata);
|
||||
memcpy(dup,src,sizeof(*dup));
|
||||
return dup;
|
||||
}
|
||||
|
||||
static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) {
|
||||
int l1, l2;
|
||||
((void) privdata);
|
||||
|
||||
l1 = sdslen((sds)key1);
|
||||
l2 = sdslen((sds)key2);
|
||||
if (l1 != l2) return 0;
|
||||
return memcmp(key1,key2,l1) == 0;
|
||||
}
|
||||
|
||||
static void callbackKeyDestructor(void *privdata, void *key) {
|
||||
((void) privdata);
|
||||
sdsfree((sds)key);
|
||||
}
|
||||
|
||||
static void callbackValDestructor(void *privdata, void *val) {
|
||||
((void) privdata);
|
||||
free(val);
|
||||
}
|
||||
|
||||
static dictType callbackDict = {
|
||||
callbackHash,
|
||||
NULL,
|
||||
callbackValDup,
|
||||
callbackKeyCompare,
|
||||
callbackKeyDestructor,
|
||||
callbackValDestructor
|
||||
};
|
||||
|
||||
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
|
||||
c = &(ac->c);
|
||||
@@ -50,19 +101,23 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
ac->err = 0;
|
||||
ac->errstr = NULL;
|
||||
ac->data = NULL;
|
||||
ac->_adapter_data = NULL;
|
||||
|
||||
ac->evAddRead = NULL;
|
||||
ac->evDelRead = NULL;
|
||||
ac->evAddWrite = NULL;
|
||||
ac->evDelWrite = NULL;
|
||||
ac->evCleanup = NULL;
|
||||
ac->ev.data = NULL;
|
||||
ac->ev.addRead = NULL;
|
||||
ac->ev.delRead = NULL;
|
||||
ac->ev.addWrite = NULL;
|
||||
ac->ev.delWrite = NULL;
|
||||
ac->ev.cleanup = NULL;
|
||||
|
||||
ac->onConnect = NULL;
|
||||
ac->onDisconnect = NULL;
|
||||
|
||||
ac->replies.head = NULL;
|
||||
ac->replies.tail = NULL;
|
||||
ac->sub.invalid.head = NULL;
|
||||
ac->sub.invalid.tail = NULL;
|
||||
ac->sub.channels = dictCreate(&callbackDict,NULL);
|
||||
ac->sub.patterns = dictCreate(&callbackDict,NULL);
|
||||
return ac;
|
||||
}
|
||||
|
||||
@@ -96,6 +151,11 @@ int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFun
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
|
||||
if (ac->onConnect == NULL) {
|
||||
ac->onConnect = fn;
|
||||
|
||||
/* The common way to detect an established connection is to wait for
|
||||
* the first write event to be fired. This assumes the related event
|
||||
* library functions are already set. */
|
||||
if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
|
||||
return REDIS_OK;
|
||||
}
|
||||
return REDIS_ERR;
|
||||
@@ -114,11 +174,11 @@ static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
|
||||
redisCallback *cb;
|
||||
|
||||
/* Copy callback from stack to heap */
|
||||
cb = calloc(1,sizeof(*cb));
|
||||
cb = malloc(sizeof(*cb));
|
||||
if (!cb) redisOOM();
|
||||
if (source != NULL) {
|
||||
cb->fn = source->fn;
|
||||
cb->privdata = source->privdata;
|
||||
memcpy(cb,source,sizeof(*cb));
|
||||
cb->next = NULL;
|
||||
}
|
||||
|
||||
/* Store callback in list */
|
||||
@@ -146,51 +206,150 @@ static int __redisShiftCallback(redisCallbackList *list, redisCallback *target)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
|
||||
* from being issued, but tries to flush the output buffer and execute
|
||||
* callbacks for all remaining replies.
|
||||
*
|
||||
* This functions is generally called from within a callback, so the
|
||||
* processCallbacks function will pick up the flag when there are no
|
||||
* more replies. */
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) {
|
||||
redisContext *c = &(ac->c);
|
||||
c->flags |= REDIS_DISCONNECTING;
|
||||
if (cb->fn != NULL) {
|
||||
c->flags |= REDIS_IN_CALLBACK;
|
||||
cb->fn(ac,reply,cb->privdata);
|
||||
c->flags &= ~REDIS_IN_CALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to free the context. */
|
||||
static void __redisAsyncFree(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
dictIterator *it;
|
||||
dictEntry *de;
|
||||
|
||||
/* Execute pending callbacks with NULL reply. */
|
||||
while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK)
|
||||
__redisRunCallback(ac,&cb,NULL);
|
||||
|
||||
/* Execute callbacks for invalid commands */
|
||||
while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK)
|
||||
__redisRunCallback(ac,&cb,NULL);
|
||||
|
||||
/* Run subscription callbacks callbacks with NULL reply */
|
||||
it = dictGetIterator(ac->sub.channels);
|
||||
while ((de = dictNext(it)) != NULL)
|
||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
||||
dictReleaseIterator(it);
|
||||
dictRelease(ac->sub.channels);
|
||||
|
||||
it = dictGetIterator(ac->sub.patterns);
|
||||
while ((de = dictNext(it)) != NULL)
|
||||
__redisRunCallback(ac,dictGetEntryVal(de),NULL);
|
||||
dictReleaseIterator(it);
|
||||
dictRelease(ac->sub.patterns);
|
||||
|
||||
/* Signal event lib to clean up */
|
||||
if (ac->ev.cleanup) ac->ev.cleanup(ac->ev.data);
|
||||
|
||||
/* Execute disconnect callback. When redisAsyncFree() initiated destroying
|
||||
* this context, the status will always be REDIS_OK. */
|
||||
if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) {
|
||||
if (c->flags & REDIS_FREEING) {
|
||||
ac->onDisconnect(ac,REDIS_OK);
|
||||
} else {
|
||||
ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup self */
|
||||
redisFree(c);
|
||||
}
|
||||
|
||||
/* Free the async context. When this function is called from a callback,
|
||||
* control needs to be returned to redisProcessCallbacks() before actual
|
||||
* free'ing. To do so, a flag is set on the context which is picked up by
|
||||
* redisProcessCallbacks(). Otherwise, the context is immediately free'd. */
|
||||
void redisAsyncFree(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
c->flags |= REDIS_FREEING;
|
||||
if (!(c->flags & REDIS_IN_CALLBACK))
|
||||
__redisAsyncFree(ac);
|
||||
}
|
||||
|
||||
/* Helper function to make the disconnect happen and clean up. */
|
||||
static void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
int status;
|
||||
|
||||
/* Make sure error is accessible if there is any */
|
||||
__redisAsyncCopyError(ac);
|
||||
status = (ac->err == 0) ? REDIS_OK : REDIS_ERR;
|
||||
|
||||
if (status == REDIS_OK) {
|
||||
/* When the connection is cleanly disconnected, there should not
|
||||
* be pending callbacks. */
|
||||
if (ac->err == 0) {
|
||||
/* For clean disconnects, there should be no pending callbacks. */
|
||||
assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);
|
||||
} else {
|
||||
/* Callbacks should not be able to issue new commands. */
|
||||
/* Disconnection is caused by an error, make sure that pending
|
||||
* callbacks cannot call new commands. */
|
||||
c->flags |= REDIS_DISCONNECTING;
|
||||
|
||||
/* Execute pending callbacks with NULL reply. */
|
||||
while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) {
|
||||
if (cb.fn != NULL)
|
||||
cb.fn(ac,NULL,cb.privdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Signal event lib to clean up */
|
||||
if (ac->evCleanup) ac->evCleanup(ac->_adapter_data);
|
||||
/* For non-clean disconnects, __redisAsyncFree() will execute pending
|
||||
* callbacks with a NULL-reply. */
|
||||
__redisAsyncFree(ac);
|
||||
}
|
||||
|
||||
/* Execute callback with proper status */
|
||||
if (ac->onDisconnect) ac->onDisconnect(ac,status);
|
||||
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
|
||||
* from being issued, but tries to flush the output buffer and execute
|
||||
* callbacks for all remaining replies. When this function is called from a
|
||||
* callback, there might be more replies and we can safely defer disconnecting
|
||||
* to redisProcessCallbacks(). Otherwise, we can only disconnect immediately
|
||||
* when there are no pending callbacks. */
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
c->flags |= REDIS_DISCONNECTING;
|
||||
if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
/* Cleanup self */
|
||||
redisFree(c);
|
||||
static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) {
|
||||
redisContext *c = &(ac->c);
|
||||
dict *callbacks;
|
||||
dictEntry *de;
|
||||
int pvariant;
|
||||
char *stype;
|
||||
sds sname;
|
||||
|
||||
/* Custom reply functions are not supported for pub/sub. This will fail
|
||||
* very hard when they are used... */
|
||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
||||
assert(reply->elements >= 2);
|
||||
assert(reply->element[0]->type == REDIS_REPLY_STRING);
|
||||
stype = reply->element[0]->str;
|
||||
pvariant = (tolower(stype[0]) == 'p') ? 1 : 0;
|
||||
|
||||
if (pvariant)
|
||||
callbacks = ac->sub.patterns;
|
||||
else
|
||||
callbacks = ac->sub.channels;
|
||||
|
||||
/* Locate the right callback */
|
||||
assert(reply->element[1]->type == REDIS_REPLY_STRING);
|
||||
sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len);
|
||||
de = dictFind(callbacks,sname);
|
||||
if (de != NULL) {
|
||||
memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb));
|
||||
|
||||
/* If this is an unsubscribe message, remove it. */
|
||||
if (strcasecmp(stype+pvariant,"unsubscribe") == 0) {
|
||||
dictDelete(callbacks,sname);
|
||||
|
||||
/* If this was the last unsubscribe message, revert to
|
||||
* non-subscribe mode. */
|
||||
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
||||
if (reply->element[2]->integer == 0)
|
||||
c->flags &= ~REDIS_SUBSCRIBED;
|
||||
}
|
||||
}
|
||||
sdsfree(sname);
|
||||
} else {
|
||||
/* Shift callback for invalid commands. */
|
||||
__redisShiftCallback(&ac->sub.invalid,dstcb);
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
@@ -213,11 +372,28 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Shift callback and execute it */
|
||||
assert(__redisShiftCallback(&ac->replies,&cb) == REDIS_OK);
|
||||
/* Even if the context is subscribed, pending regular callbacks will
|
||||
* get a reply before pub/sub messages arrive. */
|
||||
if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) {
|
||||
/* No more regular callbacks, the context *must* be subscribed. */
|
||||
assert(c->flags & REDIS_SUBSCRIBED);
|
||||
__redisGetSubscribeCallback(ac,reply,&cb);
|
||||
}
|
||||
|
||||
if (cb.fn != NULL) {
|
||||
cb.fn(ac,reply,cb.privdata);
|
||||
__redisRunCallback(ac,&cb,reply);
|
||||
c->fn->freeObject(reply);
|
||||
|
||||
/* Proceed with free'ing when redisAsyncFree() was called. */
|
||||
if (c->flags & REDIS_FREEING) {
|
||||
__redisAsyncFree(ac);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* No callback for this reply. This can either be a NULL callback,
|
||||
* or there were no callbacks to begin with. Either way, don't
|
||||
* abort with an error, but simply ignore it because the client
|
||||
* doesn't know what the server will spit out over the wire. */
|
||||
c->fn->freeObject(reply);
|
||||
}
|
||||
}
|
||||
@@ -237,7 +413,7 @@ void redisAsyncHandleRead(redisAsyncContext *ac) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
} else {
|
||||
/* Always re-schedule reads */
|
||||
if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
|
||||
if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
|
||||
redisProcessCallbacks(ac);
|
||||
}
|
||||
}
|
||||
@@ -251,13 +427,13 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
||||
} else {
|
||||
/* Continue writing when not done, stop writing otherwise */
|
||||
if (!done) {
|
||||
if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
|
||||
if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
|
||||
} else {
|
||||
if (ac->evDelWrite) ac->evDelWrite(ac->_adapter_data);
|
||||
if (ac->ev.delWrite) ac->ev.delWrite(ac->ev.data);
|
||||
}
|
||||
|
||||
/* Always schedule reads after writes */
|
||||
if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
|
||||
if (ac->ev.addRead) ac->ev.addRead(ac->ev.data);
|
||||
|
||||
/* Fire onConnect when this is the first write event. */
|
||||
if (!(c->flags & REDIS_CONNECTED)) {
|
||||
@@ -267,26 +443,81 @@ void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for the redisAsyncCommand* family of functions.
|
||||
*
|
||||
* Write a formatted command to the output buffer and register the provided
|
||||
* callback function with the context.
|
||||
*/
|
||||
/* Sets a pointer to the first argument and its length starting at p. Returns
|
||||
* the number of bytes to skip to get to the following argument. */
|
||||
static char *nextArgument(char *start, char **str, size_t *len) {
|
||||
char *p = start;
|
||||
if (p[0] != '$') {
|
||||
p = strchr(p,'$');
|
||||
if (p == NULL) return NULL;
|
||||
}
|
||||
|
||||
*len = (int)strtol(p+1,NULL,10);
|
||||
p = strchr(p,'\r');
|
||||
assert(p);
|
||||
*str = p+2;
|
||||
return p+2+(*len)+2;
|
||||
}
|
||||
|
||||
/* Helper function for the redisAsyncCommand* family of functions. Writes a
|
||||
* formatted command to the output buffer and registers the provided callback
|
||||
* function with the context. */
|
||||
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
int pvariant, hasnext;
|
||||
char *cstr, *astr;
|
||||
size_t clen, alen;
|
||||
char *p;
|
||||
sds sname;
|
||||
|
||||
/* Don't accept new commands when the connection is lazily closed. */
|
||||
if (c->flags & REDIS_DISCONNECTING) return REDIS_ERR;
|
||||
__redisAppendCommand(c,cmd,len);
|
||||
/* Don't accept new commands when the connection is about to be closed. */
|
||||
if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR;
|
||||
|
||||
/* Store callback */
|
||||
/* Setup callback */
|
||||
cb.fn = fn;
|
||||
cb.privdata = privdata;
|
||||
__redisPushCallback(&ac->replies,&cb);
|
||||
|
||||
/* Find out which command will be appended. */
|
||||
p = nextArgument(cmd,&cstr,&clen);
|
||||
assert(p != NULL);
|
||||
hasnext = (p[0] == '$');
|
||||
pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0;
|
||||
cstr += pvariant;
|
||||
clen -= pvariant;
|
||||
|
||||
if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) {
|
||||
c->flags |= REDIS_SUBSCRIBED;
|
||||
|
||||
/* Add every channel/pattern to the list of subscription callbacks. */
|
||||
while ((p = nextArgument(p,&astr,&alen)) != NULL) {
|
||||
sname = sdsnewlen(astr,alen);
|
||||
if (pvariant)
|
||||
dictReplace(ac->sub.patterns,sname,&cb);
|
||||
else
|
||||
dictReplace(ac->sub.channels,sname,&cb);
|
||||
}
|
||||
} else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) {
|
||||
/* It is only useful to call (P)UNSUBSCRIBE when the context is
|
||||
* subscribed to one or more channels or patterns. */
|
||||
if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR;
|
||||
|
||||
/* (P)UNSUBSCRIBE does not have its own response: every channel or
|
||||
* pattern that is unsubscribed will receive a message. This means we
|
||||
* should not append a callback function for this command. */
|
||||
} else {
|
||||
if (c->flags & REDIS_SUBSCRIBED)
|
||||
/* This will likely result in an error reply, but it needs to be
|
||||
* received and passed to the callback. */
|
||||
__redisPushCallback(&ac->sub.invalid,&cb);
|
||||
else
|
||||
__redisPushCallback(&ac->replies,&cb);
|
||||
}
|
||||
|
||||
__redisAppendCommand(c,cmd,len);
|
||||
|
||||
/* Always schedule a write when the write buffer is non-empty */
|
||||
if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
|
||||
if (ac->ev.addWrite) ac->ev.addWrite(ac->ev.data);
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
Vendored
+24
-10
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -36,6 +38,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
|
||||
struct dict; /* dictionary header is included in async.c */
|
||||
|
||||
/* Reply callback prototype and container */
|
||||
typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
|
||||
@@ -66,16 +69,18 @@ typedef struct redisAsyncContext {
|
||||
/* Not used by hiredis */
|
||||
void *data;
|
||||
|
||||
/* Used by the different event lib adapters to store their private data */
|
||||
void *_adapter_data;
|
||||
/* Event library data and hooks */
|
||||
struct {
|
||||
void *data;
|
||||
|
||||
/* Called when the library expects to start reading/writing.
|
||||
* The supplied functions should be idempotent. */
|
||||
void (*evAddRead)(void *privdata);
|
||||
void (*evDelRead)(void *privdata);
|
||||
void (*evAddWrite)(void *privdata);
|
||||
void (*evDelWrite)(void *privdata);
|
||||
void (*evCleanup)(void *privdata);
|
||||
/* Hooks that are called when the library expects to start
|
||||
* reading/writing. These functions should be idempotent. */
|
||||
void (*addRead)(void *privdata);
|
||||
void (*delRead)(void *privdata);
|
||||
void (*addWrite)(void *privdata);
|
||||
void (*delWrite)(void *privdata);
|
||||
void (*cleanup)(void *privdata);
|
||||
} ev;
|
||||
|
||||
/* Called when either the connection is terminated due to an error or per
|
||||
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
|
||||
@@ -84,16 +89,25 @@ typedef struct redisAsyncContext {
|
||||
/* Called when the first write event was received. */
|
||||
redisConnectCallback *onConnect;
|
||||
|
||||
/* Reply callbacks */
|
||||
/* Regular command callbacks */
|
||||
redisCallbackList replies;
|
||||
|
||||
/* Subscription callbacks */
|
||||
struct {
|
||||
redisCallbackList invalid;
|
||||
struct dict *channels;
|
||||
struct dict *patterns;
|
||||
} sub;
|
||||
} redisAsyncContext;
|
||||
|
||||
/* Functions that proxy to hiredis */
|
||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
|
||||
redisAsyncContext *redisAsyncConnectUnix(const char *path);
|
||||
int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn);
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac);
|
||||
void redisAsyncFree(redisAsyncContext *ac);
|
||||
|
||||
/* Handle read/write events */
|
||||
void redisAsyncHandleRead(redisAsyncContext *ac);
|
||||
|
||||
Vendored
+403
@@ -0,0 +1,403 @@
|
||||
/* Hash table implementation.
|
||||
*
|
||||
* This file implements in memory hash tables with insert/del/replace/find/
|
||||
* get-random-element operations. Hash tables will auto resize if needed
|
||||
* tables of power of two in size are used, collisions are handled by
|
||||
* chaining. See the source code for more information... :)
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * 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.
|
||||
* * Neither the name of Redis 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 OWNER 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.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include "dict.h"
|
||||
|
||||
/* -------------------------- private prototypes ---------------------------- */
|
||||
|
||||
static int _dictExpandIfNeeded(dict *ht);
|
||||
#ifdef _WIN32
|
||||
static size_t _dictNextPower(size_t size);
|
||||
#else
|
||||
static unsigned long _dictNextPower(unsigned long size);
|
||||
#endif
|
||||
static int _dictKeyIndex(dict *ht, const void *key);
|
||||
static int _dictInit(dict *ht, dictType *type, void *privDataPtr);
|
||||
|
||||
/* -------------------------- hash functions -------------------------------- */
|
||||
|
||||
/* Generic hash function (a popular one from Bernstein).
|
||||
* I tested a few and this was the best. */
|
||||
static unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
|
||||
unsigned int hash = 5381;
|
||||
|
||||
while (len--)
|
||||
hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* ----------------------------- API implementation ------------------------- */
|
||||
|
||||
/* Reset an hashtable already initialized with ht_init().
|
||||
* NOTE: This function should only called by ht_destroy(). */
|
||||
static void _dictReset(dict *ht) {
|
||||
ht->table = NULL;
|
||||
ht->size = 0;
|
||||
ht->sizemask = 0;
|
||||
ht->used = 0;
|
||||
}
|
||||
|
||||
/* Create a new hash table */
|
||||
static dict *dictCreate(dictType *type, void *privDataPtr) {
|
||||
dict *ht = malloc(sizeof(*ht));
|
||||
_dictInit(ht,type,privDataPtr);
|
||||
return ht;
|
||||
}
|
||||
|
||||
/* Initialize the hash table */
|
||||
static int _dictInit(dict *ht, dictType *type, void *privDataPtr) {
|
||||
_dictReset(ht);
|
||||
ht->type = type;
|
||||
ht->privdata = privDataPtr;
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
/* Expand or create the hashtable */
|
||||
#ifdef _WIN32
|
||||
static int dictExpand(dict *ht, size_t size) {
|
||||
dict n; /* the new hashtable */
|
||||
size_t realsize = _dictNextPower(size), i;
|
||||
|
||||
/* the size is invalid if it is smaller than the number of
|
||||
* elements already inside the hashtable */
|
||||
if (ht->used > size)
|
||||
return DICT_ERR;
|
||||
|
||||
_dictInit(&n, ht->type, ht->privdata);
|
||||
n.size = realsize;
|
||||
n.sizemask = realsize-1;
|
||||
n.table = calloc(realsize,sizeof(dictEntry*));
|
||||
|
||||
/* Copy all the elements from the old to the new table:
|
||||
* note that if the old hash table is empty ht->size is zero,
|
||||
* so dictExpand just creates an hash table. */
|
||||
n.used = ht->used;
|
||||
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
||||
dictEntry *he, *nextHe;
|
||||
|
||||
if (ht->table[i] == NULL) continue;
|
||||
|
||||
/* For each hash entry on this slot... */
|
||||
he = ht->table[i];
|
||||
while(he) {
|
||||
unsigned int h;
|
||||
|
||||
nextHe = he->next;
|
||||
/* Get the new element index */
|
||||
h = dictHashKey(ht, he->key) & n.sizemask;
|
||||
he->next = n.table[h];
|
||||
n.table[h] = he;
|
||||
ht->used--;
|
||||
/* Pass to the next element */
|
||||
he = nextHe;
|
||||
}
|
||||
}
|
||||
assert(ht->used == 0);
|
||||
free(ht->table);
|
||||
|
||||
/* Remap the new hashtable in the old */
|
||||
*ht = n;
|
||||
return DICT_OK;
|
||||
}
|
||||
#else
|
||||
static int dictExpand(dict *ht, unsigned long size) {
|
||||
dict n; /* the new hashtable */
|
||||
unsigned long realsize = _dictNextPower(size), i;
|
||||
|
||||
/* the size is invalid if it is smaller than the number of
|
||||
* elements already inside the hashtable */
|
||||
if (ht->used > size)
|
||||
return DICT_ERR;
|
||||
|
||||
_dictInit(&n, ht->type, ht->privdata);
|
||||
n.size = realsize;
|
||||
n.sizemask = realsize-1;
|
||||
n.table = calloc(realsize,sizeof(dictEntry*));
|
||||
|
||||
/* Copy all the elements from the old to the new table:
|
||||
* note that if the old hash table is empty ht->size is zero,
|
||||
* so dictExpand just creates an hash table. */
|
||||
n.used = ht->used;
|
||||
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
||||
dictEntry *he, *nextHe;
|
||||
|
||||
if (ht->table[i] == NULL) continue;
|
||||
|
||||
/* For each hash entry on this slot... */
|
||||
he = ht->table[i];
|
||||
while(he) {
|
||||
unsigned int h;
|
||||
|
||||
nextHe = he->next;
|
||||
/* Get the new element index */
|
||||
h = dictHashKey(ht, he->key) & n.sizemask;
|
||||
he->next = n.table[h];
|
||||
n.table[h] = he;
|
||||
ht->used--;
|
||||
/* Pass to the next element */
|
||||
he = nextHe;
|
||||
}
|
||||
}
|
||||
assert(ht->used == 0);
|
||||
free(ht->table);
|
||||
|
||||
/* Remap the new hashtable in the old */
|
||||
*ht = n;
|
||||
return DICT_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Add an element to the target hash table */
|
||||
static int dictAdd(dict *ht, void *key, void *val) {
|
||||
int index;
|
||||
dictEntry *entry;
|
||||
|
||||
/* Get the index of the new element, or -1 if
|
||||
* the element already exists. */
|
||||
if ((index = _dictKeyIndex(ht, key)) == -1)
|
||||
return DICT_ERR;
|
||||
|
||||
/* Allocates the memory and stores key */
|
||||
entry = malloc(sizeof(*entry));
|
||||
entry->next = ht->table[index];
|
||||
ht->table[index] = entry;
|
||||
|
||||
/* Set the hash entry fields. */
|
||||
dictSetHashKey(ht, entry, key);
|
||||
dictSetHashVal(ht, entry, val);
|
||||
ht->used++;
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
/* Add an element, discarding the old if the key already exists.
|
||||
* Return 1 if the key was added from scratch, 0 if there was already an
|
||||
* element with such key and dictReplace() just performed a value update
|
||||
* operation. */
|
||||
static int dictReplace(dict *ht, void *key, void *val) {
|
||||
dictEntry *entry, auxentry;
|
||||
|
||||
/* Try to add the element. If the key
|
||||
* does not exists dictAdd will suceed. */
|
||||
if (dictAdd(ht, key, val) == DICT_OK)
|
||||
return 1;
|
||||
/* It already exists, get the entry */
|
||||
entry = dictFind(ht, key);
|
||||
/* Free the old value and set the new one */
|
||||
/* Set the new value and free the old one. Note that it is important
|
||||
* to do that in this order, as the value may just be exactly the same
|
||||
* as the previous one. In this context, think to reference counting,
|
||||
* you want to increment (set), and then decrement (free), and not the
|
||||
* reverse. */
|
||||
auxentry = *entry;
|
||||
dictSetHashVal(ht, entry, val);
|
||||
dictFreeEntryVal(ht, &auxentry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Search and remove an element */
|
||||
static int dictDelete(dict *ht, const void *key) {
|
||||
unsigned int h;
|
||||
dictEntry *de, *prevde;
|
||||
|
||||
if (ht->size == 0)
|
||||
return DICT_ERR;
|
||||
h = dictHashKey(ht, key) & ht->sizemask;
|
||||
de = ht->table[h];
|
||||
|
||||
prevde = NULL;
|
||||
while(de) {
|
||||
if (dictCompareHashKeys(ht,key,de->key)) {
|
||||
/* Unlink the element from the list */
|
||||
if (prevde)
|
||||
prevde->next = de->next;
|
||||
else
|
||||
ht->table[h] = de->next;
|
||||
|
||||
dictFreeEntryKey(ht,de);
|
||||
dictFreeEntryVal(ht,de);
|
||||
free(de);
|
||||
ht->used--;
|
||||
return DICT_OK;
|
||||
}
|
||||
prevde = de;
|
||||
de = de->next;
|
||||
}
|
||||
return DICT_ERR; /* not found */
|
||||
}
|
||||
|
||||
/* Destroy an entire hash table */
|
||||
static int _dictClear(dict *ht) {
|
||||
unsigned long i;
|
||||
|
||||
/* Free all the elements */
|
||||
for (i = 0; i < ht->size && ht->used > 0; i++) {
|
||||
dictEntry *he, *nextHe;
|
||||
|
||||
if ((he = ht->table[i]) == NULL) continue;
|
||||
while(he) {
|
||||
nextHe = he->next;
|
||||
dictFreeEntryKey(ht, he);
|
||||
dictFreeEntryVal(ht, he);
|
||||
free(he);
|
||||
ht->used--;
|
||||
he = nextHe;
|
||||
}
|
||||
}
|
||||
/* Free the table and the allocated cache structure */
|
||||
free(ht->table);
|
||||
/* Re-initialize the table */
|
||||
_dictReset(ht);
|
||||
return DICT_OK; /* never fails */
|
||||
}
|
||||
|
||||
/* Clear & Release the hash table */
|
||||
static void dictRelease(dict *ht) {
|
||||
_dictClear(ht);
|
||||
free(ht);
|
||||
}
|
||||
|
||||
static dictEntry *dictFind(dict *ht, const void *key) {
|
||||
dictEntry *he;
|
||||
unsigned int h;
|
||||
|
||||
if (ht->size == 0) return NULL;
|
||||
h = dictHashKey(ht, key) & ht->sizemask;
|
||||
he = ht->table[h];
|
||||
while(he) {
|
||||
if (dictCompareHashKeys(ht, key, he->key))
|
||||
return he;
|
||||
he = he->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static dictIterator *dictGetIterator(dict *ht) {
|
||||
dictIterator *iter = malloc(sizeof(*iter));
|
||||
|
||||
iter->ht = ht;
|
||||
iter->index = -1;
|
||||
iter->entry = NULL;
|
||||
iter->nextEntry = NULL;
|
||||
return iter;
|
||||
}
|
||||
|
||||
static dictEntry *dictNext(dictIterator *iter) {
|
||||
while (1) {
|
||||
if (iter->entry == NULL) {
|
||||
iter->index++;
|
||||
if (iter->index >=
|
||||
(signed)iter->ht->size) break;
|
||||
iter->entry = iter->ht->table[iter->index];
|
||||
} else {
|
||||
iter->entry = iter->nextEntry;
|
||||
}
|
||||
if (iter->entry) {
|
||||
/* We need to save the 'next' here, the iterator user
|
||||
* may delete the entry we are returning. */
|
||||
iter->nextEntry = iter->entry->next;
|
||||
return iter->entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dictReleaseIterator(dictIterator *iter) {
|
||||
free(iter);
|
||||
}
|
||||
|
||||
/* ------------------------- private functions ------------------------------ */
|
||||
|
||||
/* Expand the hash table if needed */
|
||||
static int _dictExpandIfNeeded(dict *ht) {
|
||||
/* If the hash table is empty expand it to the intial size,
|
||||
* if the table is "full" dobule its size. */
|
||||
if (ht->size == 0)
|
||||
return dictExpand(ht, DICT_HT_INITIAL_SIZE);
|
||||
if (ht->used == ht->size)
|
||||
return dictExpand(ht, ht->size*2);
|
||||
return DICT_OK;
|
||||
}
|
||||
|
||||
/* Our hash table capability is a power of two */
|
||||
#ifdef _WIN32
|
||||
static size_t _dictNextPower(size_t size) {
|
||||
size_t i = DICT_HT_INITIAL_SIZE;
|
||||
|
||||
if (size >= LONG_MAX) return LONG_MAX;
|
||||
while(1) {
|
||||
if (i >= size)
|
||||
return i;
|
||||
i *= 2;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static unsigned long _dictNextPower(unsigned long size) {
|
||||
unsigned long i = DICT_HT_INITIAL_SIZE;
|
||||
|
||||
if (size >= LONG_MAX) return LONG_MAX;
|
||||
while(1) {
|
||||
if (i >= size)
|
||||
return i;
|
||||
i *= 2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Returns the index of a free slot that can be populated with
|
||||
* an hash entry for the given 'key'.
|
||||
* If the key already exists, -1 is returned. */
|
||||
static int _dictKeyIndex(dict *ht, const void *key) {
|
||||
unsigned int h;
|
||||
dictEntry *he;
|
||||
|
||||
/* Expand the hashtable if needed */
|
||||
if (_dictExpandIfNeeded(ht) == DICT_ERR)
|
||||
return -1;
|
||||
/* Compute the key hash value */
|
||||
h = dictHashKey(ht, key) & ht->sizemask;
|
||||
/* Search if this slot does not already contain the given key */
|
||||
he = ht->table[h];
|
||||
while(he) {
|
||||
if (dictCompareHashKeys(ht, key, he->key))
|
||||
return -1;
|
||||
he = he->next;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
Vendored
+136
@@ -0,0 +1,136 @@
|
||||
/* Hash table implementation.
|
||||
*
|
||||
* This file implements in memory hash tables with insert/del/replace/find/
|
||||
* get-random-element operations. Hash tables will auto resize if needed
|
||||
* tables of power of two in size are used, collisions are handled by
|
||||
* chaining. See the source code for more information... :)
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * 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.
|
||||
* * Neither the name of Redis 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 OWNER 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.
|
||||
*/
|
||||
|
||||
#ifndef __DICT_H
|
||||
#define __DICT_H
|
||||
|
||||
#define DICT_OK 0
|
||||
#define DICT_ERR 1
|
||||
|
||||
/* Unused arguments generate annoying warnings... */
|
||||
#define DICT_NOTUSED(V) ((void) V)
|
||||
|
||||
typedef struct dictEntry {
|
||||
void *key;
|
||||
void *val;
|
||||
struct dictEntry *next;
|
||||
} dictEntry;
|
||||
|
||||
typedef struct dictType {
|
||||
unsigned int (*hashFunction)(const void *key);
|
||||
void *(*keyDup)(void *privdata, const void *key);
|
||||
void *(*valDup)(void *privdata, const void *obj);
|
||||
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
|
||||
void (*keyDestructor)(void *privdata, void *key);
|
||||
void (*valDestructor)(void *privdata, void *obj);
|
||||
} dictType;
|
||||
|
||||
typedef struct dict {
|
||||
dictEntry **table;
|
||||
dictType *type;
|
||||
#ifdef _WIN32
|
||||
size_t size;
|
||||
size_t sizemask;
|
||||
size_t used;
|
||||
#else
|
||||
unsigned long size;
|
||||
unsigned long sizemask;
|
||||
unsigned long used;
|
||||
#endif
|
||||
void *privdata;
|
||||
} dict;
|
||||
|
||||
typedef struct dictIterator {
|
||||
dict *ht;
|
||||
int index;
|
||||
dictEntry *entry, *nextEntry;
|
||||
} dictIterator;
|
||||
|
||||
/* This is the initial size of every hash table */
|
||||
#define DICT_HT_INITIAL_SIZE 4
|
||||
|
||||
/* ------------------------------- Macros ------------------------------------*/
|
||||
#define dictFreeEntryVal(ht, entry) \
|
||||
if ((ht)->type->valDestructor) \
|
||||
(ht)->type->valDestructor((ht)->privdata, (entry)->val)
|
||||
|
||||
#define dictSetHashVal(ht, entry, _val_) do { \
|
||||
if ((ht)->type->valDup) \
|
||||
entry->val = (ht)->type->valDup((ht)->privdata, _val_); \
|
||||
else \
|
||||
entry->val = (_val_); \
|
||||
} while(0)
|
||||
|
||||
#define dictFreeEntryKey(ht, entry) \
|
||||
if ((ht)->type->keyDestructor) \
|
||||
(ht)->type->keyDestructor((ht)->privdata, (entry)->key)
|
||||
|
||||
#define dictSetHashKey(ht, entry, _key_) do { \
|
||||
if ((ht)->type->keyDup) \
|
||||
entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \
|
||||
else \
|
||||
entry->key = (_key_); \
|
||||
} while(0)
|
||||
|
||||
#define dictCompareHashKeys(ht, key1, key2) \
|
||||
(((ht)->type->keyCompare) ? \
|
||||
(ht)->type->keyCompare((ht)->privdata, key1, key2) : \
|
||||
(key1) == (key2))
|
||||
|
||||
#define dictHashKey(ht, key) (ht)->type->hashFunction(key)
|
||||
|
||||
#define dictGetEntryKey(he) ((he)->key)
|
||||
#define dictGetEntryVal(he) ((he)->val)
|
||||
#define dictSlots(ht) ((ht)->size)
|
||||
#define dictSize(ht) ((ht)->used)
|
||||
|
||||
/* API */
|
||||
static unsigned int dictGenHashFunction(const unsigned char *buf, int len);
|
||||
static dict *dictCreate(dictType *type, void *privDataPtr);
|
||||
#ifdef _WIN32
|
||||
static int dictExpand(dict *ht, size_t size);
|
||||
#else
|
||||
static int dictExpand(dict *ht, unsigned long size);
|
||||
#endif
|
||||
static int dictAdd(dict *ht, void *key, void *val);
|
||||
static int dictReplace(dict *ht, void *key, void *val);
|
||||
static int dictDelete(dict *ht, const void *key);
|
||||
static void dictRelease(dict *ht);
|
||||
static dictEntry * dictFind(dict *ht, const void *key);
|
||||
static dictIterator *dictGetIterator(dict *ht);
|
||||
static dictEntry *dictNext(dictIterator *iter);
|
||||
static void dictReleaseIterator(dictIterator *iter);
|
||||
|
||||
#endif /* __DICT_H */
|
||||
Vendored
+2
-1
@@ -9,7 +9,8 @@ int main(void) {
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
|
||||
c = redisConnect((char*)"127.0.0.1", 6379);
|
||||
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
|
||||
c = redisConnectWithTimeout((char*)"127.0.0.2", 6379, timeout);
|
||||
if (c->err) {
|
||||
printf("Connection error: %s\n", c->errstr);
|
||||
exit(1);
|
||||
|
||||
Vendored
+4
-5
@@ -1,7 +1,9 @@
|
||||
#ifndef _REDIS_FMACRO_H
|
||||
#define _REDIS_FMACRO_H
|
||||
#ifndef __HIREDIS_FMACRO_H
|
||||
#define __HIREDIS_FMACRO_H
|
||||
|
||||
#ifndef _BSD_SOURCE
|
||||
#define _BSD_SOURCE
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#define _XOPEN_SOURCE 700
|
||||
@@ -9,7 +11,4 @@
|
||||
#define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#define _LARGEFILE_SOURCE
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
+160
-59
@@ -31,7 +31,9 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
@@ -50,7 +52,7 @@ typedef struct redisReader {
|
||||
size_t pos; /* buffer cursor */
|
||||
size_t len; /* buffer length */
|
||||
|
||||
redisReadTask rstack[3]; /* stack of read tasks */
|
||||
redisReadTask rstack[9]; /* stack of read tasks */
|
||||
int ridx; /* index of stack */
|
||||
void *privdata; /* user-settable arbitrary field */
|
||||
} redisReader;
|
||||
@@ -271,14 +273,17 @@ static int processLineItem(redisReader *r) {
|
||||
int len;
|
||||
|
||||
if ((p = readLine(r,&len)) != NULL) {
|
||||
if (r->fn) {
|
||||
if (cur->type == REDIS_REPLY_INTEGER) {
|
||||
if (cur->type == REDIS_REPLY_INTEGER) {
|
||||
if (r->fn && r->fn->createInteger)
|
||||
obj = r->fn->createInteger(cur,readLongLong(p));
|
||||
} else {
|
||||
obj = r->fn->createString(cur,p,len);
|
||||
}
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_INTEGER;
|
||||
} else {
|
||||
obj = (void*)(size_t)(cur->type);
|
||||
/* Type will be error or status. */
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,p,len);
|
||||
else
|
||||
obj = (void*)(size_t)(cur->type);
|
||||
}
|
||||
|
||||
/* Set reply if this is the root object. */
|
||||
@@ -293,7 +298,11 @@ static int processBulkItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
void *obj = NULL;
|
||||
char *p, *s;
|
||||
#ifdef _WIN32
|
||||
long long len;
|
||||
#else
|
||||
long len;
|
||||
#endif
|
||||
unsigned long bytelen;
|
||||
int success = 0;
|
||||
|
||||
@@ -306,15 +315,19 @@ static int processBulkItem(redisReader *r) {
|
||||
|
||||
if (len < 0) {
|
||||
/* The nil object can always be created. */
|
||||
obj = r->fn ? r->fn->createNil(cur) :
|
||||
(void*)REDIS_REPLY_NIL;
|
||||
if (r->fn && r->fn->createNil)
|
||||
obj = r->fn->createNil(cur);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_NIL;
|
||||
success = 1;
|
||||
} else {
|
||||
/* Only continue when the buffer contains the entire bulk item. */
|
||||
bytelen += len+2; /* include \r\n */
|
||||
bytelen += (unsigned long)len+2; /* include \r\n */
|
||||
if (r->pos+bytelen <= r->len) {
|
||||
obj = r->fn ? r->fn->createString(cur,s+2,len) :
|
||||
(void*)REDIS_REPLY_STRING;
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,s+2,(size_t)len);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_STRING;
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
@@ -336,13 +349,17 @@ static int processMultiBulkItem(redisReader *r) {
|
||||
redisReadTask *cur = &(r->rstack[r->ridx]);
|
||||
void *obj;
|
||||
char *p;
|
||||
#ifdef _WIN32
|
||||
long long elements;
|
||||
#else
|
||||
long elements;
|
||||
#endif
|
||||
int root = 0;
|
||||
|
||||
/* Set error for nested multi bulks with depth > 1 */
|
||||
if (r->ridx == 2) {
|
||||
if (r->ridx == 8) {
|
||||
redisSetReplyReaderError(r,sdscatprintf(sdsempty(),
|
||||
"No support for nested multi bulk replies with depth > 1"));
|
||||
"No support for nested multi bulk replies with depth > 7"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -351,16 +368,20 @@ static int processMultiBulkItem(redisReader *r) {
|
||||
root = (r->ridx == 0);
|
||||
|
||||
if (elements == -1) {
|
||||
obj = r->fn ? r->fn->createNil(cur) :
|
||||
(void*)REDIS_REPLY_NIL;
|
||||
if (r->fn && r->fn->createNil)
|
||||
obj = r->fn->createNil(cur);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_NIL;
|
||||
moveToNextTask(r);
|
||||
} else {
|
||||
obj = r->fn ? r->fn->createArray(cur,elements) :
|
||||
(void*)REDIS_REPLY_ARRAY;
|
||||
if (r->fn && r->fn->createArray)
|
||||
obj = r->fn->createArray(cur,(int)elements);
|
||||
else
|
||||
obj = (void*)REDIS_REPLY_ARRAY;
|
||||
|
||||
/* Modify task stack when there are more than 0 elements. */
|
||||
if (elements > 0) {
|
||||
cur->elements = elements;
|
||||
cur->elements = (int)elements;
|
||||
cur->obj = obj;
|
||||
r->ridx++;
|
||||
r->rstack[r->ridx].type = -1;
|
||||
@@ -434,7 +455,7 @@ static int processItem(redisReader *r) {
|
||||
}
|
||||
}
|
||||
|
||||
void *redisReplyReaderCreate() {
|
||||
void *redisReplyReaderCreate(void) {
|
||||
redisReader *r = calloc(sizeof(redisReader),1);
|
||||
r->error = NULL;
|
||||
r->fn = &defaultFunctions;
|
||||
@@ -493,7 +514,7 @@ static void redisSetReplyReaderError(redisReader *r, sds err) {
|
||||
if (r->buf != NULL) {
|
||||
sdsfree(r->buf);
|
||||
r->buf = sdsempty();
|
||||
r->pos = 0;
|
||||
r->pos = r->len = 0;
|
||||
}
|
||||
r->ridx = -1;
|
||||
r->error = err;
|
||||
@@ -504,11 +525,18 @@ char *redisReplyReaderGetError(void *reader) {
|
||||
return r->error;
|
||||
}
|
||||
|
||||
void redisReplyReaderFeed(void *reader, char *buf, size_t len) {
|
||||
void redisReplyReaderFeed(void *reader, const char *buf, size_t len) {
|
||||
redisReader *r = reader;
|
||||
|
||||
/* Copy the provided buffer. */
|
||||
if (buf != NULL && len >= 1) {
|
||||
/* Destroy internal buffer when it is empty and is quite large. */
|
||||
if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
|
||||
sdsfree(r->buf);
|
||||
r->buf = sdsempty();
|
||||
r->pos = 0;
|
||||
}
|
||||
|
||||
r->buf = sdscatlen(r->buf,buf,len);
|
||||
r->len = sdslen(r->buf);
|
||||
}
|
||||
@@ -538,15 +566,10 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
|
||||
if (processItem(r) < 0)
|
||||
break;
|
||||
|
||||
/* Discard the consumed part of the buffer. */
|
||||
if (r->pos > 0) {
|
||||
if (r->pos == r->len) {
|
||||
/* sdsrange has a quirck on this edge case. */
|
||||
sdsfree(r->buf);
|
||||
r->buf = sdsempty();
|
||||
} else {
|
||||
r->buf = sdsrange(r->buf,r->pos,r->len);
|
||||
}
|
||||
/* Discard part of the buffer when we've consumed at least 1k, to avoid
|
||||
* doing unnecessary calls to memmove() in sds.c. */
|
||||
if (r->pos >= 1024) {
|
||||
r->buf = sdsrange(r->buf,r->pos,-1);
|
||||
r->pos = 0;
|
||||
r->len = sdslen(r->buf);
|
||||
}
|
||||
@@ -556,13 +579,6 @@ int redisReplyReaderGetReply(void *reader, void **reply) {
|
||||
void *aux = r->reply;
|
||||
r->reply = NULL;
|
||||
|
||||
/* Destroy the buffer when it is empty and is quite large. */
|
||||
if (r->len == 0 && sdsavail(r->buf) > 16*1024) {
|
||||
sdsfree(r->buf);
|
||||
r->buf = sdsempty();
|
||||
r->pos = 0;
|
||||
}
|
||||
|
||||
/* Check if there actually *is* a reply. */
|
||||
if (r->error != NULL) {
|
||||
return REDIS_ERR;
|
||||
@@ -590,7 +606,7 @@ static int intlen(int i) {
|
||||
/* Helper function for redisvFormatCommand(). */
|
||||
static void addArgument(sds a, char ***argv, int *argc, int *totlen) {
|
||||
(*argc)++;
|
||||
if ((*argv = realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM();
|
||||
if ((*argv = (char **)realloc(*argv, sizeof(char*)*(*argc))) == NULL) redisOOM();
|
||||
if (totlen) *totlen = *totlen+1+intlen(sdslen(a))+2+sdslen(a)+2;
|
||||
(*argv)[(*argc)-1] = a;
|
||||
}
|
||||
@@ -601,7 +617,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
char *cmd = NULL; /* final command */
|
||||
int pos; /* position in final command */
|
||||
sds current; /* current argument */
|
||||
int interpolated = 0; /* did we do interpolation on an argument? */
|
||||
int touched = 0; /* was the current argument touched? */
|
||||
char **argv = NULL;
|
||||
int argc = 0, j;
|
||||
int totlen = 0;
|
||||
@@ -615,13 +631,14 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
while(*c != '\0') {
|
||||
if (*c != '%' || c[1] == '\0') {
|
||||
if (*c == ' ') {
|
||||
if (sdslen(current) != 0) {
|
||||
if (touched) {
|
||||
addArgument(current, &argv, &argc, &totlen);
|
||||
current = sdsempty();
|
||||
interpolated = 0;
|
||||
touched = 0;
|
||||
}
|
||||
} else {
|
||||
current = sdscatlen(current,c,1);
|
||||
touched = 1;
|
||||
}
|
||||
} else {
|
||||
switch(c[1]) {
|
||||
@@ -630,14 +647,12 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
size = strlen(arg);
|
||||
if (size > 0)
|
||||
current = sdscatlen(current,arg,size);
|
||||
interpolated = 1;
|
||||
break;
|
||||
case 'b':
|
||||
arg = va_arg(ap,char*);
|
||||
size = va_arg(ap,size_t);
|
||||
if (size > 0)
|
||||
current = sdscatlen(current,arg,size);
|
||||
interpolated = 1;
|
||||
break;
|
||||
case '%':
|
||||
current = sdscat(current,"%");
|
||||
@@ -683,7 +698,6 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
_format[_l] = '\0';
|
||||
va_copy(_cpy,ap);
|
||||
current = sdscatvprintf(current,_format,_cpy);
|
||||
interpolated = 1;
|
||||
va_end(_cpy);
|
||||
|
||||
/* Update current position (note: outer blocks
|
||||
@@ -693,16 +707,21 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
}
|
||||
|
||||
/* Consume and discard vararg */
|
||||
#ifdef _WIN32
|
||||
va_arg(ap,void *);
|
||||
#else
|
||||
va_arg(ap,void);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
touched = 1;
|
||||
c++;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
|
||||
/* Add the last argument if needed */
|
||||
if (interpolated || sdslen(current) != 0) {
|
||||
if (touched) {
|
||||
addArgument(current, &argv, &argc, &totlen);
|
||||
} else {
|
||||
sdsfree(current);
|
||||
@@ -712,11 +731,15 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
||||
totlen += 1+intlen(argc)+2;
|
||||
|
||||
/* Build the command at protocol level */
|
||||
cmd = malloc(totlen+1);
|
||||
cmd = (char *)malloc(totlen+1);
|
||||
if (!cmd) redisOOM();
|
||||
pos = sprintf(cmd,"*%d\r\n",argc);
|
||||
for (j = 0; j < argc; j++) {
|
||||
#ifdef _WIN32
|
||||
pos += sprintf(cmd+pos,"$%llu\r\n",(unsigned long long)sdslen(argv[j]));
|
||||
#else
|
||||
pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(argv[j]));
|
||||
#endif
|
||||
memcpy(cmd+pos,argv[j],sdslen(argv[j]));
|
||||
pos += sdslen(argv[j]);
|
||||
sdsfree(argv[j]);
|
||||
@@ -775,7 +798,11 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz
|
||||
pos = sprintf(cmd,"*%d\r\n",argc);
|
||||
for (j = 0; j < argc; j++) {
|
||||
len = argvlen ? argvlen[j] : strlen(argv[j]);
|
||||
#ifdef _WIN32
|
||||
pos += sprintf(cmd+pos,"$%llu\r\n",(unsigned long long)len);
|
||||
#else
|
||||
pos += sprintf(cmd+pos,"$%zu\r\n",len);
|
||||
#endif
|
||||
memcpy(cmd+pos,argv[j],len);
|
||||
pos += len;
|
||||
cmd[pos++] = '\r';
|
||||
@@ -798,7 +825,7 @@ void __redisSetError(redisContext *c, int type, const sds errstr) {
|
||||
}
|
||||
}
|
||||
|
||||
static redisContext *redisContextInit() {
|
||||
static redisContext *redisContextInit(void) {
|
||||
redisContext *c = calloc(sizeof(redisContext),1);
|
||||
c->err = 0;
|
||||
c->errstr = NULL;
|
||||
@@ -809,9 +836,12 @@ static redisContext *redisContextInit() {
|
||||
}
|
||||
|
||||
void redisFree(redisContext *c) {
|
||||
/* Disconnect before free'ing if not yet disconnected. */
|
||||
if (c->flags & REDIS_CONNECTED)
|
||||
if (c->fd > 0)
|
||||
#ifdef _WIN32
|
||||
closesocket(c->fd);
|
||||
#else
|
||||
close(c->fd);
|
||||
#endif
|
||||
if (c->errstr != NULL)
|
||||
sdsfree(c->errstr);
|
||||
if (c->obuf != NULL)
|
||||
@@ -827,31 +857,67 @@ void redisFree(redisContext *c) {
|
||||
redisContext *redisConnect(const char *ip, int port) {
|
||||
redisContext *c = redisContextInit();
|
||||
c->flags |= REDIS_BLOCK;
|
||||
redisContextConnectTcp(c,ip,port);
|
||||
redisContextConnectTcp(c,ip,port,NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv) {
|
||||
redisContext *c = redisContextInit();
|
||||
c->flags |= REDIS_BLOCK;
|
||||
redisContextConnectTcp(c,ip,port,&tv);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectNonBlock(const char *ip, int port) {
|
||||
redisContext *c = redisContextInit();
|
||||
c->flags &= ~REDIS_BLOCK;
|
||||
redisContextConnectTcp(c,ip,port);
|
||||
redisContextConnectTcp(c,ip,port,NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectUnix(const char *path) {
|
||||
redisContext *c = redisContextInit();
|
||||
c->flags |= REDIS_BLOCK;
|
||||
redisContextConnectUnix(c,path);
|
||||
redisContextConnectUnix(c,path,NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv) {
|
||||
redisContext *c = redisContextInit();
|
||||
c->flags |= REDIS_BLOCK;
|
||||
redisContextConnectUnix(c,path,&tv);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectUnixNonBlock(const char *path) {
|
||||
redisContext *c = redisContextInit();
|
||||
c->flags &= ~REDIS_BLOCK;
|
||||
redisContextConnectUnix(c,path);
|
||||
redisContextConnectUnix(c,path,NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* initializers if caller handles connection */
|
||||
redisContext *redisConnected() {
|
||||
redisContext *c = redisContextInit();
|
||||
c->fd = -1;
|
||||
c->flags |= REDIS_BLOCK;
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectedNonBlock() {
|
||||
redisContext *c = redisContextInit();
|
||||
c->fd = -1;
|
||||
c->flags &= ~REDIS_BLOCK;
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Set read/write timeout on a blocking socket. */
|
||||
int redisSetTimeout(redisContext *c, struct timeval tv) {
|
||||
if (c->flags & REDIS_BLOCK)
|
||||
return redisContextSetTimeout(c,tv);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Set the replyObjectFunctions to use. Returns REDIS_ERR when the reader
|
||||
* was already initialized and the function set could not be re-set.
|
||||
* Return REDIS_OK when they could be set. */
|
||||
@@ -877,9 +943,18 @@ static void __redisCreateReplyReader(redisContext *c) {
|
||||
* see if there is a reply available. */
|
||||
int redisBufferRead(redisContext *c) {
|
||||
char buf[2048];
|
||||
int nread = read(c->fd,buf,sizeof(buf));
|
||||
#ifdef _WIN32
|
||||
int nread = recv((SOCKET)c->fd,buf,sizeof(buf),0);
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
errno = WSAGetLastError();
|
||||
if ((errno == ENOENT) || (errno == WSAEWOULDBLOCK))
|
||||
errno = EAGAIN;
|
||||
}
|
||||
#else
|
||||
int nread = read(c->fd,buf,sizeof(buf));
|
||||
#endif
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
|
||||
/* Try again later */
|
||||
} else {
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
@@ -896,6 +971,23 @@ int redisBufferRead(redisContext *c) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/* Use this function if the caller has already read the data. It will
|
||||
* feed bytes to the reply parser.
|
||||
*
|
||||
* After this function is called, you may use redisContextReadReply to
|
||||
* see if there is a reply available. */
|
||||
int redisBufferReadDone(redisContext *c, char *buf, int nread) {
|
||||
if (nread == 0) {
|
||||
__redisSetError(c,REDIS_ERR_EOF,
|
||||
sdsnew("Server closed the connection"));
|
||||
return REDIS_ERR;
|
||||
} else {
|
||||
__redisCreateReplyReader(c);
|
||||
redisReplyReaderFeed(c->reader,buf,nread);
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
/* Write the output buffer to the socket.
|
||||
*
|
||||
* Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was
|
||||
@@ -908,9 +1000,18 @@ int redisBufferRead(redisContext *c) {
|
||||
int redisBufferWrite(redisContext *c, int *done) {
|
||||
int nwritten;
|
||||
if (sdslen(c->obuf) > 0) {
|
||||
nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
|
||||
#ifdef _WIN32
|
||||
nwritten = send((SOCKET)c->fd,c->obuf,sdslen(c->obuf),0);
|
||||
if (nwritten == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
errno = WSAGetLastError();
|
||||
if ((errno == ENOENT) || (errno == WSAEWOULDBLOCK))
|
||||
errno = EAGAIN;
|
||||
}
|
||||
#else
|
||||
nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
|
||||
#endif
|
||||
if (nwritten == -1) {
|
||||
if (errno == EAGAIN && !(c->flags & REDIS_BLOCK)) {
|
||||
/* Try again later */
|
||||
} else {
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
|
||||
Vendored
+36
-2
@@ -33,6 +33,20 @@
|
||||
#define __HIREDIS_H
|
||||
#include <stdio.h> /* for size_t */
|
||||
#include <stdarg.h> /* for va_list */
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#ifndef FD_SETSIZE
|
||||
#define FD_SETSIZE 16000
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef va_copy
|
||||
#define va_copy(d,s) d = (s)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define HIREDIS_MAJOR 0
|
||||
#define HIREDIS_MINOR 9
|
||||
@@ -64,6 +78,16 @@
|
||||
* should be terminated once all replies have been read. */
|
||||
#define REDIS_DISCONNECTING 0x4
|
||||
|
||||
/* Flag specific to the async API which means that the context should be clean
|
||||
* up as soon as possible. */
|
||||
#define REDIS_FREEING 0x8
|
||||
|
||||
/* Flag that is set when an async callback is executed. */
|
||||
#define REDIS_IN_CALLBACK 0x10
|
||||
|
||||
/* Flag that is set when the async context has one or more subscriptions. */
|
||||
#define REDIS_SUBSCRIBED 0x20
|
||||
|
||||
#define REDIS_REPLY_STRING 1
|
||||
#define REDIS_REPLY_ARRAY 2
|
||||
#define REDIS_REPLY_INTEGER 3
|
||||
@@ -106,7 +130,11 @@ struct redisContext; /* need forward declaration of redisContext */
|
||||
|
||||
/* Context for a connection to Redis */
|
||||
typedef struct redisContext {
|
||||
#ifdef _WIN32
|
||||
SOCKET fd;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
int flags;
|
||||
char *obuf; /* Write buffer */
|
||||
int err; /* Error flags, 0 when there is no error */
|
||||
@@ -118,13 +146,13 @@ typedef struct redisContext {
|
||||
} redisContext;
|
||||
|
||||
void freeReplyObject(void *reply);
|
||||
void *redisReplyReaderCreate();
|
||||
void *redisReplyReaderCreate(void);
|
||||
int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn);
|
||||
int redisReplyReaderSetPrivdata(void *reader, void *privdata);
|
||||
void *redisReplyReaderGetObject(void *reader);
|
||||
char *redisReplyReaderGetError(void *reader);
|
||||
void redisReplyReaderFree(void *ptr);
|
||||
void redisReplyReaderFeed(void *reader, char *buf, size_t len);
|
||||
void redisReplyReaderFeed(void *reader, const char *buf, size_t len);
|
||||
int redisReplyReaderGetReply(void *reader, void **reply);
|
||||
|
||||
/* Functions to format a command according to the protocol. */
|
||||
@@ -133,13 +161,19 @@ int redisFormatCommand(char **target, const char *format, ...);
|
||||
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
|
||||
|
||||
redisContext *redisConnect(const char *ip, int port);
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, struct timeval tv);
|
||||
redisContext *redisConnectNonBlock(const char *ip, int port);
|
||||
redisContext *redisConnectUnix(const char *path);
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, struct timeval tv);
|
||||
redisContext *redisConnectUnixNonBlock(const char *path);
|
||||
redisContext *redisConnected();
|
||||
redisContext *redisConnectedNonBlock();
|
||||
int redisSetTimeout(redisContext *c, struct timeval tv);
|
||||
int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn);
|
||||
void redisFree(redisContext *c);
|
||||
int redisBufferRead(redisContext *c);
|
||||
int redisBufferWrite(redisContext *c, int *done);
|
||||
int redisBufferReadDone(redisContext *c, char *buf, int nread);
|
||||
|
||||
/* In a blocking context, this function first checks if there are unconsumed
|
||||
* replies to return and returns one if so. Otherwise, it flushes the output
|
||||
|
||||
Vendored
+279
-19
@@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
@@ -31,15 +32,26 @@
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <sys/types.h>
|
||||
#ifdef _WIN32
|
||||
#ifndef FD_SETSIZE
|
||||
#define FD_SETSIZE 16000
|
||||
#endif
|
||||
#include "winsock2.h"
|
||||
#include "windows.h"
|
||||
#define socklen_t int
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@@ -50,6 +62,31 @@
|
||||
/* Forward declaration */
|
||||
void __redisSetError(redisContext *c, int type, sds err);
|
||||
|
||||
#ifdef _WIN32
|
||||
static int redisCreateSocket(redisContext *c, int type) {
|
||||
SOCKET s;
|
||||
int on=1;
|
||||
|
||||
s = socket(type, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (s == INVALID_SOCKET) {
|
||||
__redisSetError(c,REDIS_ERR_IO,sdscatprintf(sdsempty(), "socket error: %d\n", WSAGetLastError()));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (type == AF_INET) {
|
||||
LINGER l;
|
||||
l.l_onoff = 1;
|
||||
l.l_linger = 2;
|
||||
setsockopt(s, SOL_SOCKET, SO_LINGER, (const char *) &l, sizeof(l));
|
||||
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)) == -1) {
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
closesocket(s);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
return (int)s;
|
||||
}
|
||||
#else
|
||||
static int redisCreateSocket(redisContext *c, int type) {
|
||||
int s, on = 1;
|
||||
if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
|
||||
@@ -65,8 +102,32 @@ static int redisCreateSocket(redisContext *c, int type) {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int redisSetNonBlock(redisContext *c, int fd) {
|
||||
|
||||
#ifdef _WIN32
|
||||
static int redisSetBlocking(redisContext *c, int fd, int blocking) {
|
||||
/* If iMode = 0, blocking is enabled; */
|
||||
/* If iMode != 0, non-blocking mode is enabled. */
|
||||
u_long flags;
|
||||
|
||||
if (blocking)
|
||||
flags = (u_long)0;
|
||||
else
|
||||
flags = (u_long)1;
|
||||
|
||||
if (ioctlsocket((SOCKET)fd, FIONBIO, &flags) == SOCKET_ERROR) {
|
||||
errno = WSAGetLastError();
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "ioctlsocket(FIONBIO): %d\n", errno));
|
||||
closesocket(fd);
|
||||
return REDIS_ERR;
|
||||
};
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
static int redisSetBlocking(redisContext *c, int fd, int blocking) {
|
||||
int flags;
|
||||
|
||||
/* Set the socket nonblocking.
|
||||
@@ -78,33 +139,219 @@ static int redisSetNonBlock(redisContext *c, int fd) {
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
|
||||
if (blocking)
|
||||
flags &= ~O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(fd, F_SETFL, flags) == -1) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)));
|
||||
sdscatprintf(sdsempty(), "fcntl(F_SETFL): %s", strerror(errno)));
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
static int redisSetTcpNoDelay(redisContext *c, int fd) {
|
||||
int yes = 1;
|
||||
if (setsockopt((SOCKET)fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&yes, sizeof(yes)) == -1) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "setsockopt(TCP_NODELAY): %d", (int)GetLastError()));
|
||||
closesocket(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
#else
|
||||
static int redisSetTcpNoDelay(redisContext *c, int fd) {
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "setsockopt(TCP_NODELAY): %s", strerror(errno)));
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
|
||||
static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) {
|
||||
struct timeval to;
|
||||
struct timeval *toptr = NULL;
|
||||
fd_set wfd;
|
||||
int err;
|
||||
socklen_t errlen;
|
||||
|
||||
/* Only use timeout when not NULL. */
|
||||
if (timeout != NULL) {
|
||||
to = *timeout;
|
||||
toptr = &to;
|
||||
}
|
||||
|
||||
if (errno == EINPROGRESS) {
|
||||
FD_ZERO(&wfd);
|
||||
#ifdef _WIN32
|
||||
FD_SET((SOCKET)fd, &wfd);
|
||||
#else
|
||||
FD_SET(fd, &wfd);
|
||||
#endif
|
||||
|
||||
if (select(FD_SETSIZE, NULL, &wfd, NULL, toptr) == -1) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "select(2): %s", strerror(errno)));
|
||||
#ifdef _WIN32
|
||||
closesocket(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
if (!FD_ISSET(fd, &wfd)) {
|
||||
#ifdef _WIN32
|
||||
errno = WSAGetLastError();
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
closesocket(fd);
|
||||
#else
|
||||
errno = ETIMEDOUT;
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
close(fd);
|
||||
#endif
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
errlen = sizeof(err);
|
||||
#ifdef _WIN32
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen) == SOCKET_ERROR) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "getsockopt(SO_ERROR): %d", WSAGetLastError()));
|
||||
closesocket(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#else
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "getsockopt(SO_ERROR): %s", strerror(errno)));
|
||||
close(fd);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (err) {
|
||||
errno = err;
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
#ifdef _WIN32
|
||||
closesocket(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
#ifdef _WIN32
|
||||
closesocket(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
int redisContextSetTimeout(redisContext *c, struct timeval tv) {
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,(const char *)&tv,sizeof(tv)) == SOCKET_ERROR ) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "setsockopt(SO_RCVTIMEO): %d", WSAGetLastError()));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,(const char *)&tv,sizeof(tv)) == SOCKET_ERROR ) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "setsockopt(SO_SNDTIMEO): %d", WSAGetLastError()));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
#else
|
||||
int redisContextSetTimeout(redisContext *c, struct timeval tv) {
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "setsockopt(SO_RCVTIMEO): %s", strerror(errno)));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(), "setsockopt(SO_SNDTIMEO): %s", strerror(errno)));
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
|
||||
int s;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
struct sockaddr_in sa;
|
||||
unsigned long inAddress;
|
||||
|
||||
if ((s = redisCreateSocket(c,AF_INET)) < 0)
|
||||
return REDIS_ERR;
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(port);
|
||||
if (redisSetTcpNoDelay(c,s) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
inAddress = inet_addr(addr);
|
||||
if (inAddress == INADDR_NONE || inAddress == INADDR_ANY) {
|
||||
struct hostent *he;
|
||||
|
||||
he = gethostbyname(addr);
|
||||
if (he == NULL) {
|
||||
__redisSetError(c,REDIS_ERR_OTHER,
|
||||
sdscatprintf(sdsempty(),"can't resolve: %s\n", addr));
|
||||
closesocket(s);
|
||||
return REDIS_ERR;;
|
||||
}
|
||||
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
|
||||
}
|
||||
else {
|
||||
sa.sin_addr.s_addr = inAddress;
|
||||
}
|
||||
|
||||
if (connect((SOCKET)s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
errno = WSAGetLastError();
|
||||
if ((errno == WSAEINVAL) || (errno == WSAEWOULDBLOCK))
|
||||
errno = EINPROGRESS;
|
||||
if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
c->fd = s;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
}
|
||||
#else
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout) {
|
||||
int s;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
struct sockaddr_in sa;
|
||||
|
||||
if ((s = redisCreateSocket(c,AF_INET)) == REDIS_ERR)
|
||||
if ((s = redisCreateSocket(c,AF_INET)) < 0)
|
||||
return REDIS_ERR;
|
||||
if (!blocking && redisSetNonBlock(c,s) == REDIS_ERR)
|
||||
if (redisSetBlocking(c,s,0) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
sa.sin_family = AF_INET;
|
||||
@@ -126,30 +373,39 @@ int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
|
||||
if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
close(s);
|
||||
return REDIS_ERR;
|
||||
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (redisSetTcpNoDelay(c,s) != REDIS_OK) {
|
||||
close(s);
|
||||
/* Reset socket to be blocking after connect(2). */
|
||||
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
if (redisSetTcpNoDelay(c,s) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
c->fd = s;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
int redisContextConnectUnix(redisContext *c, const char *path) {
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout) {
|
||||
#ifdef _WIN32
|
||||
(void) timeout;
|
||||
__redisSetError(c,REDIS_ERR_IO,
|
||||
sdscatprintf(sdsempty(),"Unix sockets are not suported on Windows platform. (%s)\n", path));
|
||||
|
||||
return REDIS_ERR;
|
||||
#else
|
||||
int s;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
struct sockaddr_un sa;
|
||||
|
||||
if ((s = redisCreateSocket(c,AF_LOCAL)) == REDIS_ERR)
|
||||
if ((s = redisCreateSocket(c,AF_LOCAL)) < 0)
|
||||
return REDIS_ERR;
|
||||
if (!blocking && redisSetNonBlock(c,s) != REDIS_OK)
|
||||
if (redisSetBlocking(c,s,0) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
sa.sun_family = AF_LOCAL;
|
||||
@@ -158,13 +414,17 @@ int redisContextConnectUnix(redisContext *c, const char *path) {
|
||||
if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
close(s);
|
||||
return REDIS_ERR;
|
||||
if (redisContextWaitReady(c,s,timeout) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset socket to be blocking after connect(2). */
|
||||
if (blocking && redisSetBlocking(c,s,1) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
c->fd = s;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
Vendored
+5
-2
@@ -1,6 +1,8 @@
|
||||
/* Extracted from anet.c to work properly with Hiredis error reporting.
|
||||
*
|
||||
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -37,7 +39,8 @@
|
||||
#define AF_LOCAL AF_UNIX
|
||||
#endif
|
||||
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port);
|
||||
int redisContextConnectUnix(redisContext *c, const char *path);
|
||||
int redisContextSetTimeout(redisContext *c, struct timeval tv);
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, struct timeval *timeout);
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, struct timeval *timeout);
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
+130
-15
@@ -30,11 +30,11 @@
|
||||
|
||||
#define SDS_ABORT_ON_OOM
|
||||
|
||||
#include "sds.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "sds.h"
|
||||
|
||||
static void sdsOomAbort(void) {
|
||||
fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
|
||||
@@ -69,11 +69,6 @@ sds sdsnew(const char *init) {
|
||||
return sdsnewlen(init, initlen);
|
||||
}
|
||||
|
||||
size_t sdslen(const sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
return sh->len;
|
||||
}
|
||||
|
||||
sds sdsdup(const sds s) {
|
||||
return sdsnewlen(s, sdslen(s));
|
||||
}
|
||||
@@ -83,11 +78,6 @@ void sdsfree(sds s) {
|
||||
free(s-sizeof(struct sdshdr));
|
||||
}
|
||||
|
||||
size_t sdsavail(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
return sh->free;
|
||||
}
|
||||
|
||||
void sdsupdatelen(sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
int reallen = strlen(s);
|
||||
@@ -115,6 +105,25 @@ static sds sdsMakeRoomFor(sds s, size_t addlen) {
|
||||
return newsh->buf;
|
||||
}
|
||||
|
||||
/* Grow the sds to have the specified length. Bytes that were not part of
|
||||
* the original length of the sds will be set to zero. */
|
||||
sds sdsgrowzero(sds s, size_t len) {
|
||||
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
size_t totlen, curlen = sh->len;
|
||||
|
||||
if (len <= curlen) return s;
|
||||
s = sdsMakeRoomFor(s,len-curlen);
|
||||
if (s == NULL) return NULL;
|
||||
|
||||
/* Make sure added region doesn't contain garbage */
|
||||
sh = (void*)(s-(sizeof(struct sdshdr)));
|
||||
memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */
|
||||
totlen = sh->len+sh->free;
|
||||
sh->len = len;
|
||||
sh->free = totlen-sh->len;
|
||||
return s;
|
||||
}
|
||||
|
||||
sds sdscatlen(sds s, const void *t, size_t len) {
|
||||
struct sdshdr *sh;
|
||||
size_t curlen = sdslen(s);
|
||||
@@ -222,13 +231,16 @@ sds sdsrange(sds s, int start, int end) {
|
||||
}
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
if (newlen != 0) {
|
||||
if (start >= (signed)len) start = len-1;
|
||||
if (end >= (signed)len) end = len-1;
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
if (start >= (signed)len) {
|
||||
newlen = 0;
|
||||
} else if (end >= (signed)len) {
|
||||
end = len-1;
|
||||
newlen = (start > end) ? 0 : (end-start)+1;
|
||||
}
|
||||
} else {
|
||||
start = 0;
|
||||
}
|
||||
if (start != 0) memmove(sh->buf, sh->buf+start, newlen);
|
||||
if (start && newlen) memmove(sh->buf, sh->buf+start, newlen);
|
||||
sh->buf[newlen] = 0;
|
||||
sh->free = sh->free+(sh->len-newlen);
|
||||
sh->len = newlen;
|
||||
@@ -477,3 +489,106 @@ err:
|
||||
if (current) sdsfree(current);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef SDS_TEST_MAIN
|
||||
#include <stdio.h>
|
||||
|
||||
int __failed_tests = 0;
|
||||
int __test_num = 0;
|
||||
#define test_cond(descr,_c) do { \
|
||||
__test_num++; printf("%d - %s: ", __test_num, descr); \
|
||||
if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \
|
||||
} while(0);
|
||||
#define test_report() do { \
|
||||
printf("%d tests, %d passed, %d failed\n", __test_num, \
|
||||
__test_num-__failed_tests, __failed_tests); \
|
||||
if (__failed_tests) { \
|
||||
printf("=== WARNING === We have failed tests here...\n"); \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
int main(void) {
|
||||
{
|
||||
sds x = sdsnew("foo"), y;
|
||||
|
||||
test_cond("Create a string and obtain the length",
|
||||
sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdsnewlen("foo",2);
|
||||
test_cond("Create a string with specified length",
|
||||
sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0)
|
||||
|
||||
x = sdscat(x,"bar");
|
||||
test_cond("Strings concatenation",
|
||||
sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0);
|
||||
|
||||
x = sdscpy(x,"a");
|
||||
test_cond("sdscpy() against an originally longer string",
|
||||
sdslen(x) == 1 && memcmp(x,"a\0",2) == 0)
|
||||
|
||||
x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk");
|
||||
test_cond("sdscpy() against an originally shorter string",
|
||||
sdslen(x) == 33 &&
|
||||
memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdscatprintf(sdsempty(),"%d",123);
|
||||
test_cond("sdscatprintf() seems working in the base case",
|
||||
sdslen(x) == 3 && memcmp(x,"123\0",4) ==0)
|
||||
|
||||
sdsfree(x);
|
||||
x = sdstrim(sdsnew("xxciaoyyy"),"xy");
|
||||
test_cond("sdstrim() correctly trims characters",
|
||||
sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0)
|
||||
|
||||
y = sdsrange(sdsdup(x),1,1);
|
||||
test_cond("sdsrange(...,1,1)",
|
||||
sdslen(y) == 1 && memcmp(y,"i\0",2) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),1,-1);
|
||||
test_cond("sdsrange(...,1,-1)",
|
||||
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),-2,-1);
|
||||
test_cond("sdsrange(...,-2,-1)",
|
||||
sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),2,1);
|
||||
test_cond("sdsrange(...,2,1)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),1,100);
|
||||
test_cond("sdsrange(...,1,100)",
|
||||
sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
y = sdsrange(sdsdup(x),100,100);
|
||||
test_cond("sdsrange(...,100,100)",
|
||||
sdslen(y) == 0 && memcmp(y,"\0",1) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("foo");
|
||||
y = sdsnew("foa");
|
||||
test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("bar");
|
||||
y = sdsnew("bar");
|
||||
test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0)
|
||||
|
||||
sdsfree(y);
|
||||
sdsfree(x);
|
||||
x = sdsnew("aar");
|
||||
y = sdsnew("bar");
|
||||
test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0)
|
||||
}
|
||||
test_report()
|
||||
}
|
||||
#endif
|
||||
|
||||
Vendored
+17
-1
@@ -34,6 +34,11 @@
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define inline __inline
|
||||
#define va_copy(d,s) d = (s)
|
||||
#endif
|
||||
|
||||
typedef char *sds;
|
||||
|
||||
struct sdshdr {
|
||||
@@ -42,13 +47,24 @@ struct sdshdr {
|
||||
char buf[];
|
||||
};
|
||||
|
||||
static inline size_t sdslen(const sds s) {
|
||||
struct sdshdr *sh = (struct sdshdr *)(s-(sizeof(struct sdshdr)));
|
||||
return sh->len;
|
||||
}
|
||||
|
||||
static inline size_t sdsavail(const sds s) {
|
||||
struct sdshdr *sh = (struct sdshdr *)(s-(sizeof(struct sdshdr)));
|
||||
return sh->free;
|
||||
}
|
||||
|
||||
sds sdsnewlen(const void *init, size_t initlen);
|
||||
sds sdsnew(const char *init);
|
||||
sds sdsempty();
|
||||
sds sdsempty(void);
|
||||
size_t sdslen(const sds s);
|
||||
sds sdsdup(const sds s);
|
||||
void sdsfree(sds s);
|
||||
size_t sdsavail(sds s);
|
||||
sds sdsgrowzero(sds s, size_t len);
|
||||
sds sdscatlen(sds s, const void *t, size_t len);
|
||||
sds sdscat(sds s, const char *t);
|
||||
sds sdscpylen(sds s, char *t, size_t len);
|
||||
|
||||
Vendored
+69
-16
@@ -6,6 +6,7 @@
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "hiredis.h"
|
||||
|
||||
@@ -31,7 +32,7 @@ static void __connect(redisContext **target) {
|
||||
}
|
||||
}
|
||||
|
||||
static void test_format_commands() {
|
||||
static void test_format_commands(void) {
|
||||
char *cmd;
|
||||
int len;
|
||||
|
||||
@@ -53,6 +54,12 @@ static void test_format_commands() {
|
||||
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
|
||||
free(cmd);
|
||||
|
||||
test("Format command with an empty string in between proper interpolations: ");
|
||||
len = redisFormatCommand(&cmd,"SET %s %s","","foo");
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$0\r\n\r\n$3\r\nfoo\r\n",len) == 0 &&
|
||||
len == 4+4+(3+2)+4+(0+2)+4+(3+2));
|
||||
free(cmd);
|
||||
|
||||
test("Format command with %%b string interpolation: ");
|
||||
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
|
||||
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
|
||||
@@ -115,7 +122,7 @@ static void test_format_commands() {
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
static void test_blocking_connection() {
|
||||
static void test_blocking_connection(void) {
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
int major, minor;
|
||||
@@ -246,13 +253,21 @@ static void test_blocking_connection() {
|
||||
* conditions, the error will be set to EOF. */
|
||||
assert(c->err == REDIS_ERR_EOF &&
|
||||
strcmp(c->errstr,"Server closed the connection") == 0);
|
||||
|
||||
/* Clean up context and reconnect again */
|
||||
redisFree(c);
|
||||
|
||||
__connect(&c);
|
||||
test("Returns I/O error on socket timeout: ");
|
||||
struct timeval tv = { 0, 1000 };
|
||||
assert(redisSetTimeout(c,tv) == REDIS_OK);
|
||||
test_cond(redisGetReply(c,(void**)&reply) == REDIS_ERR &&
|
||||
c->err == REDIS_ERR_IO && errno == EAGAIN);
|
||||
redisFree(c);
|
||||
|
||||
/* Context should be connected */
|
||||
__connect(&c);
|
||||
}
|
||||
|
||||
static void test_reply_reader() {
|
||||
static void test_reply_reader(void) {
|
||||
void *reader;
|
||||
void *reply;
|
||||
char *err;
|
||||
@@ -309,10 +324,19 @@ static void test_reply_reader() {
|
||||
ret = redisReplyReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
|
||||
redisReplyReaderFree(reader);
|
||||
|
||||
test("Properly reset state after protocol error: ");
|
||||
reader = redisReplyReaderCreate();
|
||||
redisReplyReaderSetReplyObjectFunctions(reader,NULL);
|
||||
redisReplyReaderFeed(reader,(char*)"x",1);
|
||||
ret = redisReplyReaderGetReply(reader,&reply);
|
||||
assert(ret == REDIS_ERR);
|
||||
ret = redisReplyReaderGetReply(reader,&reply);
|
||||
test_cond(ret == REDIS_OK && reply == NULL)
|
||||
}
|
||||
|
||||
static void test_throughput() {
|
||||
int i;
|
||||
static void test_throughput(void) {
|
||||
int i, num;
|
||||
long long t1, t2;
|
||||
redisContext *c = blocking_context;
|
||||
redisReply **replies;
|
||||
@@ -321,31 +345,60 @@ static void test_throughput() {
|
||||
for (i = 0; i < 500; i++)
|
||||
freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*1000);
|
||||
num = 1000;
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < 1000; i++) {
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c,"PING");
|
||||
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < 1000; i++) freeReplyObject(replies[i]);
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
printf("\t(1000x PING: %.2fs)\n", (t2-t1)/1000000.0);
|
||||
printf("\t(%dx PING: %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*1000);
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
t1 = usec();
|
||||
for (i = 0; i < 1000; i++) {
|
||||
for (i = 0; i < num; i++) {
|
||||
replies[i] = redisCommand(c,"LRANGE mylist 0 499");
|
||||
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
|
||||
assert(replies[i] != NULL && replies[i]->elements == 500);
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < 1000; i++) freeReplyObject(replies[i]);
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
printf("\t(1000x LRANGE with 500 elements: %.2fs)\n", (t2-t1)/1000000.0);
|
||||
printf("\t(%dx LRANGE with 500 elements: %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
num = 10000;
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"PING");
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
|
||||
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
printf("\t(%dx PING (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
|
||||
replies = malloc(sizeof(redisReply*)*num);
|
||||
for (i = 0; i < num; i++)
|
||||
redisAppendCommand(c,"LRANGE mylist 0 499");
|
||||
t1 = usec();
|
||||
for (i = 0; i < num; i++) {
|
||||
assert(redisGetReply(c, (void*)&replies[i]) == REDIS_OK);
|
||||
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
|
||||
assert(replies[i] != NULL && replies[i]->elements == 500);
|
||||
}
|
||||
t2 = usec();
|
||||
for (i = 0; i < num; i++) freeReplyObject(replies[i]);
|
||||
free(replies);
|
||||
printf("\t(%dx LRANGE with 500 elements (pipelined): %.3fs)\n", num, (t2-t1)/1000000.0);
|
||||
}
|
||||
|
||||
static void cleanup() {
|
||||
static void cleanup(void) {
|
||||
redisContext *c = blocking_context;
|
||||
redisReply *reply;
|
||||
|
||||
|
||||
Vendored
+257
-4
@@ -82,24 +82,31 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include "linenoise.h"
|
||||
#ifdef _WIN32
|
||||
#include "../../src/win32fixes.h"
|
||||
#define REDIS_NOTUSED(V) ((void) V)
|
||||
#endif
|
||||
|
||||
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
|
||||
#define LINENOISE_MAX_LINE 4096
|
||||
static char *unsupported_term[] = {"dumb","cons25",NULL};
|
||||
static linenoiseCompletionCallback *completionCallback = NULL;
|
||||
|
||||
#ifndef _WIN32
|
||||
static struct termios orig_termios; /* in order to restore at exit */
|
||||
#endif
|
||||
static int rawmode = 0; /* for atexit() function to check if restore is needed*/
|
||||
static int atexit_registered = 0; /* register atexit just 1 time */
|
||||
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
|
||||
@@ -109,13 +116,141 @@ char **history = NULL;
|
||||
static void linenoiseAtExit(void);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef STDIN_FILENO
|
||||
#define STDIN_FILENO (_fileno(stdin))
|
||||
#endif
|
||||
|
||||
HANDLE hOut;
|
||||
HANDLE hIn;
|
||||
DWORD consolemode;
|
||||
|
||||
static int win32read(char *c) {
|
||||
|
||||
DWORD foo;
|
||||
INPUT_RECORD b;
|
||||
KEY_EVENT_RECORD e;
|
||||
|
||||
while (1) {
|
||||
if (!ReadConsoleInput(hIn, &b, 1, &foo)) return 0;
|
||||
if (!foo) return 0;
|
||||
|
||||
if (b.EventType == KEY_EVENT && b.Event.KeyEvent.bKeyDown) {
|
||||
|
||||
e = b.Event.KeyEvent;
|
||||
*c = b.Event.KeyEvent.uChar.AsciiChar;
|
||||
|
||||
//if (e.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {
|
||||
/* Alt+key ignored */
|
||||
//} else
|
||||
if (e.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) {
|
||||
|
||||
/* Ctrl+Key */
|
||||
switch (*c) {
|
||||
case 'D':
|
||||
*c = 4;
|
||||
return 1;
|
||||
case 'C':
|
||||
*c = 3;
|
||||
return 1;
|
||||
case 'H':
|
||||
*c = 8;
|
||||
return 1;
|
||||
case 'T':
|
||||
*c = 20;
|
||||
return 1;
|
||||
case 'B': /* ctrl-b, left_arrow */
|
||||
*c = 2;
|
||||
return 1;
|
||||
case 'F': /* ctrl-f right_arrow*/
|
||||
*c = 6;
|
||||
return 1;
|
||||
case 'P': /* ctrl-p up_arrow*/
|
||||
*c = 16;
|
||||
return 1;
|
||||
case 'N': /* ctrl-n down_arrow*/
|
||||
*c = 14;
|
||||
return 1;
|
||||
case 'U': /* Ctrl+u, delete the whole line. */
|
||||
*c = 21;
|
||||
return 1;
|
||||
case 'K': /* Ctrl+k, delete from current to end of line. */
|
||||
*c = 11;
|
||||
return 1;
|
||||
case 'A': /* Ctrl+a, go to the start of the line */
|
||||
*c = 1;
|
||||
return 1;
|
||||
case 'E': /* ctrl+e, go to the end of the line */
|
||||
*c = 5;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Other Ctrl+KEYs ignored */
|
||||
} else {
|
||||
|
||||
switch (e.wVirtualKeyCode) {
|
||||
|
||||
case VK_ESCAPE: /* ignore - send ctrl-c, will return -1 */
|
||||
*c = 3;
|
||||
return 1;
|
||||
case VK_RETURN: /* enter */
|
||||
*c = 13;
|
||||
return 1;
|
||||
case VK_LEFT: /* left */
|
||||
*c = 2;
|
||||
return 1;
|
||||
case VK_RIGHT: /* right */
|
||||
*c = 6;
|
||||
return 1;
|
||||
case VK_UP: /* up */
|
||||
*c = 16;
|
||||
return 1;
|
||||
case VK_DOWN: /* down */
|
||||
*c = 14;
|
||||
return 1;
|
||||
case VK_HOME:
|
||||
*c = 1;
|
||||
return 1;
|
||||
case VK_END:
|
||||
*c = 5;
|
||||
return 1;
|
||||
case VK_BACK:
|
||||
*c = 8;
|
||||
return 1;
|
||||
case VK_DELETE:
|
||||
*c = 127;
|
||||
return 1;
|
||||
default:
|
||||
if (*c) return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1; /* Makes compiler happy */
|
||||
}
|
||||
|
||||
#ifdef __STRICT_ANSI__
|
||||
char *strdup(const char *s) {
|
||||
size_t l = strlen(s)+1;
|
||||
char *p = malloc(l);
|
||||
|
||||
memcpy(p,s,l);
|
||||
return p;
|
||||
}
|
||||
#endif /* __STRICT_ANSI__ */
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
static int isUnsupportedTerm(void) {
|
||||
#ifndef _WIN32
|
||||
char *term = getenv("TERM");
|
||||
int j;
|
||||
|
||||
if (term == NULL) return 0;
|
||||
for (j = 0; unsupported_term[j]; j++)
|
||||
if (!strcasecmp(term,unsupported_term[j])) return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -130,6 +265,7 @@ static void freeHistory(void) {
|
||||
}
|
||||
|
||||
static int enableRawMode(int fd) {
|
||||
#ifndef _WIN32
|
||||
struct termios raw;
|
||||
|
||||
if (!isatty(STDIN_FILENO)) goto fatal;
|
||||
@@ -157,6 +293,37 @@ static int enableRawMode(int fd) {
|
||||
/* put terminal in raw mode after flushing */
|
||||
if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
|
||||
rawmode = 1;
|
||||
#else
|
||||
REDIS_NOTUSED(fd);
|
||||
|
||||
if (!atexit_registered) {
|
||||
/* Init windows console handles only once */
|
||||
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut==INVALID_HANDLE_VALUE) goto fatal;
|
||||
|
||||
if (!GetConsoleMode(hOut, &consolemode)) {
|
||||
CloseHandle(hOut);
|
||||
errno = ENOTTY;
|
||||
return -1;
|
||||
};
|
||||
|
||||
hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (hIn == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(hOut);
|
||||
errno = ENOTTY;
|
||||
return -1;
|
||||
}
|
||||
|
||||
GetConsoleMode(hIn, &consolemode);
|
||||
SetConsoleMode(hIn, ENABLE_PROCESSED_INPUT);
|
||||
|
||||
/* Cleanup them at exit */
|
||||
atexit(linenoiseAtExit);
|
||||
atexit_registered = 1;
|
||||
}
|
||||
|
||||
rawmode = 1;
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
fatal:
|
||||
@@ -165,26 +332,49 @@ fatal:
|
||||
}
|
||||
|
||||
static void disableRawMode(int fd) {
|
||||
#ifdef _WIN32
|
||||
REDIS_NOTUSED(fd);
|
||||
rawmode = 0;
|
||||
#else
|
||||
/* Don't even check the return value as it's too late. */
|
||||
if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
|
||||
rawmode = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* At exit we'll try to fix the terminal to the initial conditions. */
|
||||
static void linenoiseAtExit(void) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleMode(hIn, consolemode);
|
||||
CloseHandle(hOut);
|
||||
CloseHandle(hIn);
|
||||
#else
|
||||
disableRawMode(STDIN_FILENO);
|
||||
#endif
|
||||
freeHistory();
|
||||
}
|
||||
|
||||
static int getColumns(void) {
|
||||
#ifdef _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO b;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(hOut, &b)) return 80;
|
||||
return b.srWindow.Right - b.srWindow.Left;
|
||||
#else
|
||||
struct winsize ws;
|
||||
|
||||
if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80;
|
||||
return ws.ws_col;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
|
||||
char seq[64];
|
||||
#ifdef _WIN32
|
||||
DWORD pl, bl, w;
|
||||
CONSOLE_SCREEN_BUFFER_INFO b;
|
||||
COORD coord;
|
||||
#endif
|
||||
size_t plen = strlen(prompt);
|
||||
|
||||
while((plen+pos) >= cols) {
|
||||
@@ -196,6 +386,7 @@ static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_
|
||||
len--;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Cursor to left edge */
|
||||
snprintf(seq,64,"\x1b[0G");
|
||||
if (write(fd,seq,strlen(seq)) == -1) return;
|
||||
@@ -208,6 +399,27 @@ static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_
|
||||
/* Move cursor to original position. */
|
||||
snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen));
|
||||
if (write(fd,seq,strlen(seq)) == -1) return;
|
||||
#else
|
||||
|
||||
REDIS_NOTUSED(seq);
|
||||
REDIS_NOTUSED(fd);
|
||||
|
||||
/* Get buffer console info */
|
||||
if (!GetConsoleScreenBufferInfo(hOut, &b)) return;
|
||||
/* Erase Line */
|
||||
coord.X = 0;
|
||||
coord.Y = b.dwCursorPosition.Y;
|
||||
FillConsoleOutputCharacterA(hOut, ' ', b.dwSize.X, coord, &w);
|
||||
/* Cursor to the left edge */
|
||||
SetConsoleCursorPosition(hOut, coord);
|
||||
/* Write the prompt and the current buffer content */
|
||||
WriteConsole(hOut, prompt, plen, &pl, NULL);
|
||||
WriteConsole(hOut, buf, len, &bl, NULL);
|
||||
/* Move cursor to original position. */
|
||||
coord.X = (int)(pos+plen);
|
||||
coord.Y = b.dwCursorPosition.Y;
|
||||
SetConsoleCursorPosition(hOut, coord);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void beep() {
|
||||
@@ -290,6 +502,9 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
|
||||
size_t len = 0;
|
||||
size_t cols = getColumns();
|
||||
int history_index = 0;
|
||||
#ifdef _WIN32
|
||||
DWORD foo;
|
||||
#endif
|
||||
|
||||
buf[0] = '\0';
|
||||
buflen--; /* Make sure there is always space for the nulterm */
|
||||
@@ -298,13 +513,21 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
|
||||
* initially is just an empty string. */
|
||||
linenoiseHistoryAdd("");
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!WriteConsole(hOut, prompt, plen, &foo, NULL)) return -1;
|
||||
#else
|
||||
if (write(fd,prompt,plen) == -1) return -1;
|
||||
#endif
|
||||
while(1) {
|
||||
char c;
|
||||
int nread;
|
||||
char seq[2], seq2[2];
|
||||
|
||||
#ifdef _WIN32
|
||||
nread = win32read(&c);
|
||||
#else
|
||||
nread = read(fd,&c,1);
|
||||
#endif
|
||||
if (nread <= 0) return len;
|
||||
|
||||
/* Only autocomplete when the callback is set. It returns < 0 when
|
||||
@@ -320,14 +543,24 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
|
||||
|
||||
switch(c) {
|
||||
case 13: /* enter */
|
||||
case 4: /* ctrl-d */
|
||||
history_len--;
|
||||
free(history[history_len]);
|
||||
return (len == 0 && c == 4) ? -1 : (int)len;
|
||||
return (int)len;
|
||||
case 3: /* ctrl-c */
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case 127: /* backspace */
|
||||
#ifdef _WIN32
|
||||
/* delete in _WIN32*/
|
||||
/* win32read() will send 127 for DEL and 8 for BS and Ctrl-H */
|
||||
if (pos < len && len > 0) {
|
||||
memmove(buf+pos,buf+pos+1,len-pos);
|
||||
len--;
|
||||
buf[len] = '\0';
|
||||
refreshLine(fd,prompt,buf,len,pos,cols);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case 8: /* ctrl-h */
|
||||
if (pos > 0 && len > 0) {
|
||||
memmove(buf+pos-1,buf+pos,len-pos);
|
||||
@@ -337,6 +570,18 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
|
||||
refreshLine(fd,prompt,buf,len,pos,cols);
|
||||
}
|
||||
break;
|
||||
case 4: /* ctrl-d, remove char at right of cursor */
|
||||
if (len > 1 && pos < (len-1)) {
|
||||
memmove(buf+pos,buf+pos+1,len-pos);
|
||||
len--;
|
||||
buf[len] = '\0';
|
||||
refreshLine(fd,prompt,buf,len,pos,cols);
|
||||
} else if (len == 0) {
|
||||
history_len--;
|
||||
free(history[history_len]);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 20: /* ctrl-t */
|
||||
if (pos > 0 && pos < len) {
|
||||
int aux = buf[pos-1];
|
||||
@@ -419,7 +664,11 @@ up_down_arrow:
|
||||
if (plen+len < cols) {
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
#ifdef _WIN32
|
||||
if (!WriteConsole(hOut, &c, 1, &foo, NULL)) return -1;
|
||||
#else
|
||||
if (write(fd,&c,1) == -1) return -1;
|
||||
#endif
|
||||
} else {
|
||||
refreshLine(fd,prompt,buf,len,pos,cols);
|
||||
}
|
||||
@@ -564,7 +813,11 @@ int linenoiseHistorySetMaxLen(int len) {
|
||||
/* Save the history in the specified file. On success 0 is returned
|
||||
* otherwise -1 is returned. */
|
||||
int linenoiseHistorySave(char *filename) {
|
||||
#ifdef _WIN32
|
||||
FILE *fp = fopen(filename,"wb");
|
||||
#else
|
||||
FILE *fp = fopen(filename,"w");
|
||||
#endif
|
||||
int j;
|
||||
|
||||
if (fp == NULL) return -1;
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
Redis Cluster Design Proposal (work in progress)
|
||||
|
||||
28 Nov 2010: Ver 1.0 - initial version
|
||||
22 APr 2010: Ver 1.1 - more details and rationales
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Redis is a fast key-value store supporting complex aggregate data types as
|
||||
values. For instance keys can be bound to lists with many elements, sets,
|
||||
sub-dictionaries (hashes) and so forth.
|
||||
|
||||
While Redis is very fast, currently it lacks scalability in the form of ability
|
||||
to transparently run across different nodes. This is desirable mainly for the
|
||||
following three rasons:
|
||||
|
||||
A) Fault tolerance. Some node may go off line without affecting the operations.
|
||||
B) Holding bigger datasets without using a single box with a lot of RAM.
|
||||
C) Scaling writes.
|
||||
|
||||
Since a single Redis instance supports 140,000 operations per second in a good
|
||||
Linux box costing less than $1000, the need for Redis Cluster arises more
|
||||
from "A" and "B". Scaling writes can also be useful in very high load
|
||||
environments. Scaling reads is already easily accomplished using Redis built-in
|
||||
replication.
|
||||
|
||||
Design goals
|
||||
============
|
||||
|
||||
Designing a DHT in 2010 is hard as there is too much bias towards good designs
|
||||
that are already well tested in practice, like the Amazon Dynamo design.
|
||||
Still a Dynamo alike DHT may not be the best fit for Redis.
|
||||
|
||||
Redis is very simple and fast at its core, so Redis cluster should try to
|
||||
follow the same guidelines. The first problem with a Dynamo-alike DHT is that
|
||||
Redis supports complex data types. Merging complex values like lsits, where
|
||||
in the case of a netsplit may diverge in very complex ways, is not going to
|
||||
be easy. The "most recent data" wins is not applicable and all the resolution
|
||||
business should be in the application.
|
||||
|
||||
Even a simple application can end up with complex schema of keys and complex
|
||||
values. Writing code in order to resolve conflicts is not going to be
|
||||
programmer friendly.
|
||||
|
||||
So the author of this document claims that Redis does not need to resist to
|
||||
netsplits, but it is enough to resist to M-1 nodes going offline, where
|
||||
M is the number of nodes storing every key-value pair.
|
||||
|
||||
For instance in a three nodes cluster I may configure the cluster in order to
|
||||
store every key into two instances (M=2). Such a cluster can resist to a single
|
||||
node going offline without interruption of the service.
|
||||
|
||||
When more than M-1 nodes are off line the cluster should detect such a condition
|
||||
and refusing any further query. The system administrator should check why
|
||||
M-1 nodes are offline and bring them back again if possible.
|
||||
|
||||
Once resisting to big net splits is no longer a requirement as there is no
|
||||
conflict resolution stage, since at least an original node responsible of
|
||||
holding every possible key must be online for the cluster to work, there is
|
||||
also no need for a design where every node can act as an independent entity
|
||||
receiving queries and forwarding this queries to other nodes as needed.
|
||||
|
||||
Instead a more decoupled approach can be used, in the form of a Redis Proxy
|
||||
node (or multiple Proxy nodes) that is contacted by clients, and
|
||||
is responsible of forwarding queries and replies back and forth from data nodes.
|
||||
|
||||
Data nodes can be just vanilla redis-server instances.
|
||||
|
||||
Network layout
|
||||
==============
|
||||
|
||||
- One ore more Data Nodes. Every node is identified by ip:port.
|
||||
- A single Configuration Node.
|
||||
- One more more Proxy Nodes (redis-cluster nodes).
|
||||
- A single Handling Node.
|
||||
|
||||
Data Nodes and the Configuration Node are just vanilla redis-server instances.
|
||||
|
||||
Configuration Node
|
||||
==================
|
||||
|
||||
- Contains information about all the Data nodes in the cluster.
|
||||
- Contains information about all the Proxy nodes in the cluster.
|
||||
- Contains information about what Data Node holds a given sub-space of keys.
|
||||
|
||||
The keyspace is divided into 1024 different "hashing slots".
|
||||
(1024 is just an example, this value should be configurable)
|
||||
|
||||
Given a key perform SHA1(key) and use the last 10 bits of the result to get a 10 bit number representing the "key slot" (from 0 to 1023).
|
||||
|
||||
The Configuration node maps every slot of the keyspace to M different Data Nodes (every key is stored into M nodes, configurable).
|
||||
|
||||
The Configuration node can be modified by a single client at a time. Locking is performed using SETNX.
|
||||
|
||||
The Configuration node should be replicated as there is a single configuration node for the whole network. It is the only single point of failure of the system.
|
||||
When a Configuration node fails the cluster does not stop operating, but is not
|
||||
able to recover if there is some exceptional condition to handle, like a Data
|
||||
Node going off line or the addition of a new Data Node to the cluster.
|
||||
|
||||
The Configuration node is a standard Redis server, like every other Data node.
|
||||
|
||||
Data Nodes
|
||||
==========
|
||||
|
||||
Data nodes just hold data, and are normal Redis processes. There is no configuration stored on nodes, nor the nodes are "active" in the cluster, they just receive normal Redis commands.
|
||||
|
||||
Proxy Nodes
|
||||
===========
|
||||
|
||||
Proxy nodes get requests from clients and route this requests to the right Redis nodes.
|
||||
|
||||
Proxy nodes take persistent connections to all the Data Nodes and the
|
||||
Configuration Node. This connections are keep alive with PING requests from time
|
||||
to time if there is no traffic. This way Proxy Nodes can understand asap if
|
||||
there is a problem in some Data Node or in the Configuration Node.
|
||||
|
||||
When a Proxy Node is started it needs to know the Configuration node address in order to load the infomration about the Data nodes and the mapping between the key space and the nodes.
|
||||
|
||||
On startup a Proxy Node will also register itself in the Configuration node, and will make sure to refresh it's configuration every N seconds (via an EXPIREing key) so that it's possible to detect when a Proxy node fails.
|
||||
|
||||
Clients can submit queries to any Proxy Node, so well designed clients may ask
|
||||
at startup the list of Proxy Nodes querying the Configuration Node. Then if
|
||||
a query fails against a given Proxy Node it can be retried against the next.
|
||||
|
||||
The Proxy Node is also in charge of signaling failing Data nodes to the Configuration node, so that the Handling Node can take appropriate actions.
|
||||
|
||||
When a new Data node joins or leaves the cluster, and in general when the cluster configuration changes, all the Proxy nodes will receive a notification and will reload the configuration from the Configuration node.
|
||||
|
||||
Proxy Nodes - how queries are submited
|
||||
======================================
|
||||
|
||||
This is how a query is processed:
|
||||
|
||||
1) A client sends a query to a Proxy Node, using the Redis protocol like if it was a plain Redis Node.
|
||||
2) The Proxy Node inspects the command arguments to detect the key. The key is hashed. The Proxy Node has the table mapping a given key to M nodes, and persistent connections to all the nodes.
|
||||
|
||||
At this point the process is different in case of read or write queries:
|
||||
|
||||
WRITE QUERY:
|
||||
|
||||
3a) The Proxy Node forwards the query to M Data Nodes at the same time, waiting for replies.
|
||||
3b) Once all the replies are received the Proxy Node checks that the replies are consistent. For instance all the M nodes need to reply with OK and so forth. If the query fails in a subset of nodes but succeeds in other nodes, the failing nodes are considered unreliable and are put off line notifying the configuration node.
|
||||
3c) The reply is transfered back to the client.
|
||||
|
||||
READ QUERY:
|
||||
|
||||
3d) The Proxy Node forwards the query to a single random client, passing the reply back to the client.
|
||||
|
||||
Handling Node
|
||||
=============
|
||||
|
||||
The handling node is a special Redis client with the following role:
|
||||
|
||||
- Handles the cluster configuration stored in the Config node.
|
||||
- Is in charge for adding and removing nodes dynamically from the net.
|
||||
- Relocates keys on nodes additions / removal.
|
||||
- Signal a configuration change to Proxy nodes.
|
||||
|
||||
More details on hashing slots
|
||||
============================
|
||||
|
||||
The Configuration node holds 1024 keys in the following form:
|
||||
|
||||
hashingslot:0
|
||||
hashingslot:1
|
||||
...
|
||||
hashingslot:1023
|
||||
|
||||
Every hashing slot is actually a Redis list, containing a single or more ip:port pairs. For instance:
|
||||
|
||||
hashingslot:10 => 192.168.1.19:6379, 192.168.1.200:6379
|
||||
|
||||
This mean that keys hashing to slot 10 will be saved in the two Data nodes 192.168.1.19:6379 and 192.168.1.200:6379.
|
||||
|
||||
When a client performs a read operation (via a proxy node), the proxy will contact a random Data node among the data nodes in charge for the given slot.
|
||||
|
||||
For instance a client can ask for the following operation to a given Proxy node:
|
||||
|
||||
GET mykey
|
||||
|
||||
"mykey" hashes to (for instance) slot 10, so the Proxy will forward the request to either Data node 192.168.1.19:6379 or 192.168.1.200:6379, and then forward back the reply to the client.
|
||||
|
||||
When a write operation is performed, it is forwarded to both the Data nodes in the example (and in general to all the data nodes).
|
||||
|
||||
Adding or removing a node
|
||||
=========================
|
||||
|
||||
When a Data node is added to the cluster, it is added via an LPUSH operation into a Redis list representing a queue of Data nodes that are ready to enter the cluster. This list is hold by the Configuration node of course, and can be added manually or via a configuration utility.
|
||||
|
||||
LPUSH newnodes 192.168.1.55:6379
|
||||
|
||||
The Handling node will check from time to time for this new elements in the "newode" list. If there are new nodes pending to enter the cluster, they are processed one after the other in this way:
|
||||
|
||||
For instance let's assume there are already two Data nodes in the cluster:
|
||||
|
||||
192.168.1.1:6379
|
||||
192.168.1.2:6379
|
||||
|
||||
We add a new node 192.168.1.3:6379 via the LPUSH operation.
|
||||
|
||||
We can imagine that the 1024 hash slots are assigned equally among the two inital nodes. In order to add the new (third) node what we have to do is to move incrementally 341 slots form the two old servers to the new one.
|
||||
|
||||
For now we can think that every hash slot is only stored in a single server, to generalize the idea later.
|
||||
|
||||
In order to simplify the implementation every slot can be moved from one Data node to another one in a blocking way, that is, read operations will continue to all the 1024 slots, but a single slot at a time will delay write operations until the moving from one Data node to another is completed.
|
||||
|
||||
In order to do so the Handler node, before to move a given node, marks it as "write-locked" in the Configuration server, than asks all the Proxy nodes to refresh the configuration.
|
||||
|
||||
Then the slot is moved (1/1024 of all the keys). The Configuration server is modified to reflect the new hashing slots configuration, the slot is unlocked, the Proxy nodes notified.
|
||||
|
||||
Implementation details
|
||||
======================
|
||||
|
||||
To run the Handling node and the Configuration node in the same physical computer is probably a good idea.
|
||||
@@ -1,343 +0,0 @@
|
||||
Redis Cluster - Alternative 1
|
||||
|
||||
28 Apr 2010: Ver 1.0 - initial version
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
The motivations and design goals of Redis Cluster are already outlined in the
|
||||
first design document of Redis Cluster. This document is just an attempt to
|
||||
provide a completely alternative approach in order to explore more ideas.
|
||||
|
||||
In this document the alternative explored is a cluster where communication is
|
||||
performed directly from client to the target node, without intermediate layer.
|
||||
|
||||
The intermediate layer can be used, in the form of a proxy, in order to provide
|
||||
the same functionality to clients not able to directly use the cluster protocol.
|
||||
So in a first stage clients can use a proxy to implement the hash ring, but
|
||||
later this clients can switch to a native implementation, following a
|
||||
specification that the Redis project will provide.
|
||||
|
||||
In this new design fault tolerance is achieved by replicating M-1 times every
|
||||
data node instead of storing the same key M times across nodes.
|
||||
|
||||
From the point of view of CAP our biggest sacrifice is about "P", that is
|
||||
resistance to partitioning. Only M-1 nodes can go down for the cluster still
|
||||
be functional. Also when possible "A" is somewhat sacrificed for "L", that
|
||||
is, Latency. Not really in the CAP equation but a very important parameter.
|
||||
|
||||
Network layout
|
||||
==============
|
||||
|
||||
In this alternative design the network layout is simple as there are only
|
||||
clients talking directly to N data nodes. So we can imagine to have:
|
||||
|
||||
- K Redis clients, directly talking to the data nodes.
|
||||
- N Redis data nodes, that are, normal Redis instances.
|
||||
|
||||
Data nodes are replicate M-1 times (so there are a total of M copies for
|
||||
every node). If M is one, the system is not fault tolerant. If M is 2 one
|
||||
data node can go off line without affecting the operations. And so forth.
|
||||
|
||||
Hash slots
|
||||
==========
|
||||
|
||||
The key space is divided into 1024 slots.
|
||||
|
||||
Given a key, the SHA1 function is applied to it.
|
||||
The first 10 bytes of the SHA1 digest are interpreted as an unsigned integer
|
||||
from 0 to 1023. This is the hash slot of the key.
|
||||
|
||||
Data nodes
|
||||
==========
|
||||
|
||||
Data nodes are normal Redis instances, but a few additional commands are
|
||||
provided.
|
||||
|
||||
HASHRING ADD ... list of hash slots ...
|
||||
HASHRING DEL ... list of hash slots ...
|
||||
HASHRING REHASHING slot
|
||||
HASHRING SLOTS => returns the list of configured slots
|
||||
HSAHRING KEYS ... list of hash slots ...
|
||||
|
||||
By default Redis instances are configured to accept operations about all
|
||||
the hash slots. With this commands it's possible to configure a Redis instance
|
||||
to accept only a subset of the key space.
|
||||
|
||||
If an operation is performed against a key hashing to a slot that is not
|
||||
configured to be accepted, the Redis instance will reply with:
|
||||
|
||||
"-ERR wrong hash slot"
|
||||
|
||||
More details on the HASHRING command and sub commands will be showed later
|
||||
in this document.
|
||||
|
||||
Additionally three other commands are added:
|
||||
|
||||
DUMP key
|
||||
RESTORE key <dump data>
|
||||
MIGRATE key host port
|
||||
|
||||
DUMP is used to output a very compact binary representation of the data stored at key.
|
||||
|
||||
RESTORE re-creates a value (storing it at key) starting from the output produced by DUMP.
|
||||
|
||||
MIGRATE is like a server-side DUMP+RESTORE command. This atomic command moves one key from the connected instance to another instance, returning the status code of the operation (+OK or an error).
|
||||
|
||||
The protocol described in this draft only uses the MIGRATE command, but this in turn will use RESTORE internally when connecting to another server, and DUMP is provided for symmetry.
|
||||
|
||||
Querying the cluster
|
||||
====================
|
||||
|
||||
1) Reading the cluster config
|
||||
-----------------------------
|
||||
|
||||
Clients of the cluster are required to have the cluster configuration loaded
|
||||
into memory. The cluster configuration is the sum of the following info:
|
||||
|
||||
- Number of data nodes in the cluster, for instance, 10
|
||||
- A map between hash slots and nodes, so for instnace:
|
||||
hash slot 1 -> node 0
|
||||
hash slot 2 -> node 5
|
||||
hash slot 3 -> node 3
|
||||
... and so forth ...
|
||||
- Physical address of nodes, and their replicas.
|
||||
node 0 addr -> 192.168.1.100
|
||||
node 0 replicas -> 192.168.1.101, 192.168.1.105
|
||||
- Configuration version: the SHA1 of the whole configuration
|
||||
|
||||
The configuration is stored in every single data node of the cluster.
|
||||
|
||||
A client without the configuration in memory is require, as a first step, to
|
||||
read the config. In order to do so the client requires to have a list of IPs
|
||||
that are with good probability data nodes of the cluster.
|
||||
|
||||
The client will try to get the config from all this nodes. If no node is found
|
||||
responding, an error is reported to the user.
|
||||
|
||||
2) Caching and refreshing the configuration
|
||||
-------------------------------------------
|
||||
|
||||
A node is allowed to cache the configuration in memory or in a different way
|
||||
(for instance storing the configuration into a file), but every client is
|
||||
required to check if the configuration changed at max every 10 seconds, asking
|
||||
for the configuration version key with a single GET call, and checking if the
|
||||
configuration version matches the one loaded in memory.
|
||||
|
||||
Also a client is required to refresh the configuration every time a node
|
||||
replies with:
|
||||
|
||||
"-ERR wrong hash slot"
|
||||
|
||||
As this means that hash slots were reassigned in some way.
|
||||
|
||||
Checking the configuration every 10 seconds is not required in theory but is
|
||||
a good protection against errors and failures that may happen in real world
|
||||
environments. It is also very cheap to perform, as a GET operation from time
|
||||
to time is going to have no impact in the overall performance.
|
||||
|
||||
3) Read query
|
||||
-------------
|
||||
|
||||
To perform a read query the client hashes the key argument from the command
|
||||
(in the intiial version of Redis Cluster only single-key commands are
|
||||
allowed). Using the in memory configuration it maps the hash key to the
|
||||
node ID.
|
||||
|
||||
If the client is configured to support read-after-write consistency, then
|
||||
the "master" node for this hash slot is queried.
|
||||
|
||||
Otherwise the client picks a random node from the master and the replicas
|
||||
available.
|
||||
|
||||
4) Write query
|
||||
--------------
|
||||
|
||||
A write query is exactly like a read query, with the difference that the
|
||||
write always targets the master node, instead of the replicas.
|
||||
|
||||
Creating a cluster
|
||||
==================
|
||||
|
||||
In order to create a new cluster, the redis-cluster command line utility is
|
||||
used. It gets a list of available nodes and replicas, in order to write the
|
||||
initial configuration in all the nodes.
|
||||
|
||||
At this point the cluster is usable by clients.
|
||||
|
||||
Adding nodes to the cluster
|
||||
===========================
|
||||
|
||||
The command line utility redis-cluster is used in order to add a node to the
|
||||
cluster:
|
||||
|
||||
1) The cluster configuration is loaded.
|
||||
2) A fair number of hash slots are assigned to the new data node.
|
||||
3) Hash slots moved to the new node are marked as "REHASHING" in the old
|
||||
nodes, using the HASHRING command:
|
||||
|
||||
HASHRING SETREHASHING 1 192.168.1.103 6380
|
||||
|
||||
The above command set the hash slot "1" in rehashing state, with the
|
||||
"forwarding address" to 192.168.1.103:6380. As a result if this node receives
|
||||
a query about a key hashing to hash slot 1, that *is not present* in the
|
||||
current data set, it replies with:
|
||||
|
||||
"-MIGRATED 192.168.1.103:6380"
|
||||
|
||||
The client can then reissue the query against the new node.
|
||||
|
||||
Instead even if the hash slot is marked as rehashing but the requested key
|
||||
is still there, the query is processed. This allows for non blocking
|
||||
rehashing.
|
||||
|
||||
Note that no additional memory is used by Redis in order to provide such a
|
||||
feature.
|
||||
|
||||
4) While the Hash slot is marked as "REHASHING", redis-cluster asks this node
|
||||
the list of all the keys matching the specified hash slot. Then all the keys
|
||||
are moved to the new node using the MIGRATE command.
|
||||
5) Once all the keys are migrated, the hash slot is deleted from the old
|
||||
node configuration with "HASHRING DEL 1". And the configuration is update.
|
||||
|
||||
Using this algorithm all the hash slots are migrated one after the other to the new node. In practical implementation before to start the migration the
|
||||
redis-cluster utility should write a log into the configuration so that
|
||||
in case of crash or any other problem the utility is able to recover from
|
||||
were it left.
|
||||
|
||||
Fault tolerance
|
||||
===============
|
||||
|
||||
Fault tolerance is reached replicating every data node M-1 times, so that we
|
||||
have one master and M-1 replicas for a total of M nodes holding the same
|
||||
hash slots. Up to M-1 nodes can go down without affecting the cluster.
|
||||
|
||||
The tricky part about fault tolerance is detecting when a node is failing and
|
||||
signaling it to all the other clients.
|
||||
|
||||
When a master node is failing in a permanent way, promoting the first slave
|
||||
is easy:
|
||||
1) At some point a client will notice there are problems accessing a given node. It will try to refresh the config, but will notice that the config is already up to date.
|
||||
2) In order to make sure the problem is not about the client connectivity itself, it will try to reach other nodes as well. If more than M-1 nodes appear to be down, it's either a client networking problem or alternatively the cluster can't be fixed as too many nodes are down anyway. So no action is taken, but an error is reported.
|
||||
3) If instead only 1 or at max M-1 nodes appear to be down, the client promotes a slave as master and writes the new configuration to all the data nodes.
|
||||
|
||||
All the other clients will see the data node not working, and as a first step will try to refresh the configuration. They will successful refresh the configuration and the cluster will work again.
|
||||
|
||||
Every time a slave is promoted, the information is written in a log that is actually a Redis list, in all the data nodes, so that system administration tools can detect what happened in order to send notifications to the admin.
|
||||
|
||||
Intermittent problems
|
||||
---------------------
|
||||
|
||||
In the above scenario a master was failing in a permanent way. Now instead
|
||||
let's think to a case where a network cable is not working well so a node
|
||||
appears to be a few seconds up and a few seconds down.
|
||||
|
||||
When this happens recovering can be much harder, as a client may notice the
|
||||
problem and will promote a slave to master as a result, but then the host
|
||||
will be up again and the other clients will not see the problem, writing to
|
||||
the old master for at max 10 seconds (after 10 seconds all the clients are
|
||||
required to perform a few GETs to check the configuration version of the
|
||||
cluster and update if needed).
|
||||
|
||||
One way to fix this problem is to delegate the fail over mechanism to a
|
||||
failover agent. When clients notice problems will not take any active action
|
||||
but will just log the problem into a redis list in all the reachable nodes,
|
||||
wait, check for configuration change, and retry.
|
||||
|
||||
The failover agent constantly monitor this logs: if some client is reporting
|
||||
a failing node, it can take appropriate actions, checking if the failure is
|
||||
permanent or not. If it's not he can send a SHUTDOWN command to the failing
|
||||
master if possible. The failover agent can also consider better the problem
|
||||
checking if the failing mode is advertised by all the clients or just a single
|
||||
one, and can check itself if there is a real problem before to proceed with
|
||||
the fail over.
|
||||
|
||||
Redis proxy
|
||||
===========
|
||||
|
||||
In order to make the switch to the clustered version of Redis simpler, and
|
||||
because the client-side protocol is non trivial to implement compared to the
|
||||
usual Redis client lib protocol (where a minimal lib can be as small as
|
||||
100 lines of code), a proxy will be provided to implement the cluster protocol
|
||||
as a proxy.
|
||||
|
||||
Every client will talk to a redis-proxy node that is responsible of using
|
||||
the new protocol and forwarding back the replies.
|
||||
|
||||
In the long run the aim is to switch all the major client libraries to the
|
||||
new protocol in a native way.
|
||||
|
||||
Supported commands
|
||||
==================
|
||||
|
||||
Because with this design we talk directly to data nodes and there is a single
|
||||
"master" version of every value (that's the big gain dropping "P" from CAP!)
|
||||
almost all the redis commands can be supported by the clustered version
|
||||
including MULTI/EXEC and multi key commands as long as all the keys will hash
|
||||
to the same hash slot. In order to guarantee this, key tags can be used,
|
||||
where when a specific pattern is present in the key name, only that part is
|
||||
hashed in order to obtain the hash index.
|
||||
|
||||
Random remarks
|
||||
==============
|
||||
|
||||
- It's still not clear how to perform an atomic election of a slave to master.
|
||||
- In normal conditions (all the nodes working) this new design is just
|
||||
K clients talking to N nodes without intermediate layers, no routes:
|
||||
this means it is horizontally scalable with O(1) lookups.
|
||||
- The cluster should optionally be able to work with manual fail over
|
||||
for environments where it's desirable to do so. For instance it's possible
|
||||
to setup periodic checks on all the nodes, and switch IPs when needed
|
||||
or other advanced configurations that can not be the default as they
|
||||
are too environment dependent.
|
||||
|
||||
A few ideas about client-side slave election
|
||||
============================================
|
||||
|
||||
Detecting failures in a collaborative way
|
||||
-----------------------------------------
|
||||
|
||||
In order to take the node failure detection and slave election a distributed
|
||||
effort, without any "control program" that is in some way a single point
|
||||
of failure (the cluster will not stop when it stops, but errors are not
|
||||
corrected without it running), it's possible to use a few consensus-alike
|
||||
algorithms.
|
||||
|
||||
For instance all the nodes may take a list of errors detected by clients.
|
||||
|
||||
If Client-1 detects some failure accessing Node-3, for instance a connection
|
||||
refused error or a timeout, it logs what happened with LPUSH commands against
|
||||
all the other nodes. This "error messages" will have a timestamp and the Node
|
||||
id. Something like:
|
||||
|
||||
LPUSH __cluster__:errors 3:1272545939
|
||||
|
||||
So if the error is reported many times in a small amount of time, at some
|
||||
point a client can have enough hints about the need of performing a
|
||||
slave election.
|
||||
|
||||
Atomic slave election
|
||||
---------------------
|
||||
|
||||
In order to avoid races when electing a slave to master (that is in order to
|
||||
avoid that some client can still contact the old master for that node in
|
||||
the 10 seconds timeframe), the client performing the election may write
|
||||
some hint in the configuration, change the configuration SHA1 accordingly and
|
||||
wait for more than 10 seconds, in order to be sure all the clients will
|
||||
refresh the configuration before a new access.
|
||||
|
||||
The config hint may be something like:
|
||||
|
||||
"we are switching to a new master, that is x.y.z.k:port, in a few seconds"
|
||||
|
||||
When a client updates the config and finds such a flag set, it starts to
|
||||
continuously refresh the config until a change is noticed (this will take
|
||||
at max 10-15 seconds).
|
||||
|
||||
The client performing the election will wait that famous 10 seconds time frame
|
||||
and finally will update the config in a definitive way setting the new
|
||||
slave as mater. All the clients at this point are guaranteed to have the new
|
||||
config either because they refreshed or because in the next query their config
|
||||
is already expired and they'll update the configuration.
|
||||
|
||||
EOF
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>AppendCommand: Contents</b><br> <a href="#APPEND _key_ _value_">APPEND _key_ _value_</a><br> <a href="#Return value">Return value</a><br> <a href="#Examples">Examples</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">AppendCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="StringCommandsSidebar.html">StringCommandsSidebar</a><h1><a name="APPEND _key_ _value_">APPEND _key_ _value_</a></h1>
|
||||
<i>Time complexity: O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.</i><blockquote>If the <i>key</i> already exists and is a string, this command appends theprovided value at the end of the string.If the <i>key</i> does not exist it is created and set as an empty string, soAPPEND will be very similar to SET in this special case.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically the total length of the string after the append operation.<h2><a name="Examples">Examples</a></h2><pre class="codeblock python" name="code">
|
||||
redis> exists mykey
|
||||
(integer) 0
|
||||
redis> append mykey "Hello "
|
||||
(integer) 6
|
||||
redis> append mykey "World"
|
||||
(integer) 11
|
||||
redis> get mykey
|
||||
"Hello World"
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>AppendOnlyFileHowto: Contents</b><br> <a href="#Append Only File HOWTO">Append Only File HOWTO</a><br> <a href="#General Information">General Information</a><br> <a href="#Log rewriting">Log rewriting</a><br> <a href="#Wait... but how does this work?">Wait... but how does this work?</a><br> <a href="#How durable is the append only file?">How durable is the append only file?</a><br> <a href="#What should I do if my Append Only File gets corrupted?">What should I do if my Append Only File gets corrupted?</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">AppendOnlyFileHowto</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="RedisGuides.html">RedisGuides</a>
|
||||
<h1><a name="Append Only File HOWTO">Append Only File HOWTO</a></h1><h2><a name="General Information">General Information</a></h2>Append only file is an alternative durability option for Redis. What this mean? Let's start with some fact:<br/><br/><ul><li> For default Redis saves snapshots of the dataset on disk, in a binary file called dump.rdb (by default at least). For instance you can configure Redis to save the dataset every 60 seconds if there are at least 100 changes in the dataset, or every 1000 seconds if there is at least a single change in the dataset. This is known as "Snapshotting".</li><li> Snapshotting is not very durable. If your computer running Redis stops, your power line fails, or you write killall -9 redis-server for a mistake, the latest data written on Redis will get lost. There are applications where this is not a big deal. There are applications where this is not acceptable and Redis <b>was</b> not an option for this applications.</li></ul>
|
||||
What is the solution? To use append only file as alternative to snapshotting. How it works?<br/><br/><ul><li> It is an 1.1 only feature.</li><li> You have to turn it on editing the configuration file. Just make sure you have "appendonly yes" somewhere.</li><li> Append only files work this way: every time Redis receive a command that changes the dataset (for instance a SET or LPUSH command) it appends this command in the append only file. When you restart Redis it will first <b>re-play</b> the append only file to rebuild the state.</li></ul>
|
||||
<h2><a name="Log rewriting">Log rewriting</a></h2>As you can guess... the append log file gets bigger and bigger, every time there is a new operation changing the dataset. Even if you set always the same key "mykey" to the values of "1", "2", "3", ... up to 10000000000 in the end you'll have just a single key in the dataset, just a few bytes! but how big will be the append log file? Very very big.<br/><br/>So Redis supports an interesting feature: it is able to rebuild the append log file, in background, without to stop processing client commands. The key is the command <a href="BGREWRITEAOF.html">BGREWRITEAOF</a>. This command basically is able to use the dataset in memory in order to rewrite the shortest sequence of commands able to rebuild the exact dataset that is currently in memory.<br/><br/>So from time to time when the log gets too big, try this command. It's safe as if it fails you will not lost your old log (but you can make a backup copy given that currently 1.1 is still in beta!).<h2><a name="Wait... but how does this work?">Wait... but how does this work?</a></h2>Basically it uses the same fork() copy-on-write trick that snapshotting already uses. This is how the algorithm works:<br/><br/><ul><li> Redis forks, so now we have a child and a parent.</li><li> The child starts writing the new append log file in a temporary file.</li><li> The parent accumulates all the new changes in an in-memory buffer (but at the same time it writes the new changes in the <b>old</b> append only file, so if the rewriting fails, we are safe).</li><li> When the child finished to rewrite the file, the parent gets a signal, and append the in-memory buffer at the end of the file generated by the child.</li><li> Profit! Now Redis atomically renames the old file into the new one, and starts appending new data into the new file.</li></ul>
|
||||
<h2><a name="How durable is the append only file?">How durable is the append only file?</a></h2>Check redis.conf, you can configure how many times Redis will fsync() data on disk. There are three options:<br/><br/><ul><li> Fsync() every time a new command is appended to the append log file. Very very slow, very safe.</li><li> Fsync() one time every second. Fast enough, and you can lose 1 second of data if there is a disaster.</li><li> Never fsync(), just put your data in the hands of the Operating System. The faster and unsafer method.</li></ul>
|
||||
The suggested (and default) policy is "everysec". It is both very fast and pretty safe. The "always" policy is very slow in practice, even if it was improved in Redis 2.0.0 there is no way to make fsync() faster than it is.<h2><a name="What should I do if my Append Only File gets corrupted?">What should I do if my Append Only File gets corrupted?</a></h2>It is possible that the server crashes while writing the AOF file (this still should never lead to inconsistencies) corrupting the file in a way that is no longer loadable by Redis. When this happens you can fix this problem using the following procedure:<br/><br/><ul><li> Make a backup copy of your AOF file.</li><li> Fix the original file with: ./redis-check-aof --fix <code name="code" class="python"><filename></code></li><li> Optionally use diff -u to check what is the difference between two files.</li><li> Restart the server with the fixed file.</li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>AuthCommand: Contents</b><br> <a href="#AUTH _password_">AUTH _password_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">AuthCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ConnectionHandlingSidebar.html">ConnectionHandlingSidebar</a><h1><a name="AUTH _password_">AUTH _password_</a></h1><blockquote>Request for authentication in a password protected Redis server.A Redis server can be instructed to require a password before to allow clientsto issue commands. This is done using the <i>requirepass</i> directive in theRedis configuration file.</blockquote>
|
||||
<blockquote>If the password given by the client is correct the server replies withan OK status code reply and starts accepting commands from the client.Otherwise an error is returned and the clients needs to try a new password.Note that for the high performance nature of Redis it is possible to trya lot of passwords in parallel in very short time, so make sure to generatea strong and very long password so that this attack is infeasible.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Status code reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Benchmarks: Contents</b><br> <a href="#How Fast is Redis?">How Fast is Redis?</a><br> <a href="#Latency percentiles">Latency percentiles</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Benchmarks</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
<h1><a name="How Fast is Redis?">How Fast is Redis?</a></h1>Redis includes the <code name="code" class="python">redis-benchmark</code> utility that simulates <a href="SETs.html">SETs</a>/GETs done by N clients at the same time sending M total queries (it is similar to the Apache's <code name="code" class="python">ab</code> utility). Below you'll find the full output of the benchmark executed against a Linux box.<br/><br/><ul><li> The test was done with 50 simultaneous clients performing 100000 requests.</li><li> The value SET and GET is a 256 bytes string.</li><li> The Linux box is running <b>Linux 2.6</b>, it's <b>Xeon X3320 2.5Ghz</b>.</li><li> Text executed using the loopback interface (127.0.0.1).</li></ul>
|
||||
Results: <b>about 110000 <a href="SETs.html">SETs</a> per second, about 81000 GETs per second.</b><h1><a name="Latency percentiles">Latency percentiles</a></h1><pre class="codeblock python" name="code">
|
||||
./redis-benchmark -n 100000
|
||||
|
||||
====== SET ======
|
||||
100007 requests completed in 0.88 seconds
|
||||
50 parallel clients
|
||||
3 bytes payload
|
||||
keep alive: 1
|
||||
|
||||
58.50% <= 0 milliseconds
|
||||
99.17% <= 1 milliseconds
|
||||
99.58% <= 2 milliseconds
|
||||
99.85% <= 3 milliseconds
|
||||
99.90% <= 6 milliseconds
|
||||
100.00% <= 9 milliseconds
|
||||
114293.71 requests per second
|
||||
|
||||
====== GET ======
|
||||
100000 requests completed in 1.23 seconds
|
||||
50 parallel clients
|
||||
3 bytes payload
|
||||
keep alive: 1
|
||||
|
||||
43.12% <= 0 milliseconds
|
||||
96.82% <= 1 milliseconds
|
||||
98.62% <= 2 milliseconds
|
||||
100.00% <= 3 milliseconds
|
||||
81234.77 requests per second
|
||||
|
||||
====== INCR ======
|
||||
100018 requests completed in 1.46 seconds
|
||||
50 parallel clients
|
||||
3 bytes payload
|
||||
keep alive: 1
|
||||
|
||||
32.32% <= 0 milliseconds
|
||||
96.67% <= 1 milliseconds
|
||||
99.14% <= 2 milliseconds
|
||||
99.83% <= 3 milliseconds
|
||||
99.88% <= 4 milliseconds
|
||||
99.89% <= 5 milliseconds
|
||||
99.96% <= 9 milliseconds
|
||||
100.00% <= 18 milliseconds
|
||||
68458.59 requests per second
|
||||
|
||||
====== LPUSH ======
|
||||
100004 requests completed in 1.14 seconds
|
||||
50 parallel clients
|
||||
3 bytes payload
|
||||
keep alive: 1
|
||||
|
||||
62.27% <= 0 milliseconds
|
||||
99.74% <= 1 milliseconds
|
||||
99.85% <= 2 milliseconds
|
||||
99.86% <= 3 milliseconds
|
||||
99.89% <= 5 milliseconds
|
||||
99.93% <= 7 milliseconds
|
||||
99.96% <= 9 milliseconds
|
||||
100.00% <= 22 milliseconds
|
||||
100.00% <= 208 milliseconds
|
||||
88109.25 requests per second
|
||||
|
||||
====== LPOP ======
|
||||
100001 requests completed in 1.39 seconds
|
||||
50 parallel clients
|
||||
3 bytes payload
|
||||
keep alive: 1
|
||||
|
||||
54.83% <= 0 milliseconds
|
||||
97.34% <= 1 milliseconds
|
||||
99.95% <= 2 milliseconds
|
||||
99.96% <= 3 milliseconds
|
||||
99.96% <= 4 milliseconds
|
||||
100.00% <= 9 milliseconds
|
||||
100.00% <= 208 milliseconds
|
||||
71994.96 requests per second
|
||||
</pre>Notes: changing the payload from 256 to 1024 or 4096 bytes does not change the numbers significantly (but reply packets are glued together up to 1024 bytes so GETs may be slower with big payloads). The same for the number of clients, from 50 to 256 clients I got the same numbers. With only 10 clients it starts to get a bit slower.<br/><br/>You can expect different results from different boxes. For example a low profile box like <b>Intel core duo T5500 clocked at 1.66Ghz running Linux 2.6</b> will output the following:
|
||||
<pre class="codeblock python python" name="code">
|
||||
./redis-benchmark -q -n 100000
|
||||
SET: 53684.38 requests per second
|
||||
GET: 45497.73 requests per second
|
||||
INCR: 39370.47 requests per second
|
||||
LPUSH: 34803.41 requests per second
|
||||
LPOP: 37367.20 requests per second
|
||||
</pre>Another one using a 64 bit box, a Xeon L5420 clocked at 2.5 Ghz:<br/><br/><pre class="codeblock python python python" name="code">
|
||||
./redis-benchmark -q -n 100000
|
||||
PING: 111731.84 requests per second
|
||||
SET: 108114.59 requests per second
|
||||
GET: 98717.67 requests per second
|
||||
INCR: 95241.91 requests per second
|
||||
LPUSH: 104712.05 requests per second
|
||||
LPOP: 93722.59 requests per second
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>BgrewriteaofCommand: Contents</b><br> <a href="#BGREWRITEAOF">BGREWRITEAOF</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">BgrewriteaofCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ControlCommandsSidebar.html">ControlCommandsSidebar</a><h3><a name="BGREWRITEAOF">BGREWRITEAOF</a></h3>
|
||||
<blockquote>Please for detailed information about the Redis Append Only File check<a href="AppendOnlyFileHowto.html">the Append Only File Howto</a>.</blockquote>
|
||||
<blockquote>BGREWRITEAOF rewrites the Append Only File in background when it gets toobig. The Redis Append Only File is a Journal, so every operation modifyingthe dataset is logged in the Append Only File (and replayed at startup).This means that the Append Only File always grows. In order to rebuildits content the BGREWRITEAOF creates a new version of the append only filestarting directly form the dataset in memory in order to guarantee thegeneration of the minimal number of commands needed to rebuild the database.</blockquote>
|
||||
<blockquote>The <a href="AppendOnlyFileHowto.html">Append Only File Howto</a> contains further details.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Status code reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>BgsaveCommand: Contents</b><br> <a href="#BGSAVE">BGSAVE</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">BgsaveCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ControlCommandsSidebar.html">ControlCommandsSidebar</a><h1><a name="BGSAVE">BGSAVE</a></h1>
|
||||
<blockquote>Save the DB in background. The OK code is immediately returned.Redis forks, the parent continues to server the clients, the childsaves the DB on disk then exit. A client my be able to check if theoperation succeeded using the <a href="LastsaveCommand.html">LASTSAVE</a> command.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Status code reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>BlpopCommand: Contents</b><br> <a href="#BLPOP _key1_ _key2_ ... _keyN_ _timeout_ (Redis >">BLPOP _key1_ _key2_ ... _keyN_ _timeout_ (Redis ></a><br> <a href="#BRPOP _key1_ _key2_ ... _keyN_ _timeout_ (Redis >">BRPOP _key1_ _key2_ ... _keyN_ _timeout_ (Redis ></a><br> <a href="#Non blocking behavior">Non blocking behavior</a><br> <a href="#Blocking behavior">Blocking behavior</a><br> <a href="#Multiple clients blocking for the same keys">Multiple clients blocking for the same keys</a><br> <a href="#blocking POP inside a MULTI/EXEC transaction">blocking POP inside a MULTI/EXEC transaction</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">BlpopCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="BLPOP _key1_ _key2_ ... _keyN_ _timeout_ (Redis >">BLPOP _key1_ _key2_ ... _keyN_ _timeout_ (Redis ></a></h1> 1.3.1) =
|
||||
<h1><a name="BRPOP _key1_ _key2_ ... _keyN_ _timeout_ (Redis >">BRPOP _key1_ _key2_ ... _keyN_ _timeout_ (Redis ></a></h1> 1.3.1) =
|
||||
<i>Time complexity: O(1)</i><blockquote>BLPOP (and BRPOP) is a blocking list pop primitive. You can see this commandsas blocking versions of <a href="LpopCommand.html">LPOP</a> and <a href="LpopCommand.html">RPOP</a> able toblock if the specified keys don't exist or contain empty lists.</blockquote>
|
||||
<blockquote>The following is a description of the exact semantic. We describe BLPOP butthe two commands are identical, the only difference is that BLPOP pops theelement from the left (head) of the list, and BRPOP pops from the right (tail).</blockquote>
|
||||
<h2><a name="Non blocking behavior">Non blocking behavior</a></h2><blockquote>When BLPOP is called, if at least one of the specified keys contain a nonempty list, an element is popped from the head of the list and returned tothe caller together with the name of the key (BLPOP returns a two elementsarray, the first element is the key, the second the popped value).</blockquote>
|
||||
<blockquote>Keys are scanned from left to right, so for instance if youissue <b>BLPOP list1 list2 list3 0</b> against a dataset where <b>list1</b> does notexist but <b>list2</b> and <b>list3</b> contain non empty lists, BLPOP guaranteesto return an element from the list stored at <b>list2</b> (since it is the firstnon empty list starting from the left).</blockquote>
|
||||
<h2><a name="Blocking behavior">Blocking behavior</a></h2><blockquote>If none of the specified keys exist or contain non empty lists, BLPOPblocks until some other client performs a <a href="RpushCommand.html">LPUSH</a> oran <a href="RpushCommand.html">RPUSH</a> operation against one of the lists.</blockquote>
|
||||
<blockquote>Once new data is present on one of the lists, the client finally returnswith the name of the key unblocking it and the popped value.</blockquote>
|
||||
<blockquote>When blocking, if a non-zero timeout is specified, the client will unblockreturning a nil special value if the specified amount of seconds passedwithout a push operation against at least one of the specified keys.</blockquote>
|
||||
<blockquote>The timeout argument is interpreted as an integer value. A timeout of zero means instead to block forever.</blockquote>
|
||||
<h2><a name="Multiple clients blocking for the same keys">Multiple clients blocking for the same keys</a></h2><blockquote>Multiple clients can block for the same key. They are put intoa queue, so the first to be served will be the one that started to waitearlier, in a first-blpopping first-served fashion.</blockquote>
|
||||
<h2><a name="blocking POP inside a MULTI/EXEC transaction">blocking POP inside a MULTI/EXEC transaction</a></h2><blockquote>BLPOP and BRPOP can be used with pipelining (sending multiple commands and reading the replies in batch), but it does not make sense to use BLPOP or BRPOP inside a MULTI/EXEC block (a Redis transaction).</blockquote>
|
||||
<blockquote>The behavior of BLPOP inside MULTI/EXEC when the list is empty is to return a multi-bulk nil reply, exactly what happens when the timeout is reached. If you like science fiction, think at it like if inside MULTI/EXEC the time will flow at infinite speed :) </blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><blockquote>BLPOP returns a two-elements array via a multi bulk reply in order to returnboth the unblocking key and the popped value.</blockquote>
|
||||
<blockquote>When a non-zero timeout is specified, and the BLPOP operation timed out,the return value is a nil multi bulk reply. Most client values will returnfalse or nil accordingly to the programming language used.</blockquote>
|
||||
<a href="ReplyTypes.html">Multi bulk reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>BrpoplpushCommand: Contents</b><br> <a href="#BRPOPLPUSH _srckey_ _dstkey_ _timeout_ (Redis >">BRPOPLPUSH _srckey_ _dstkey_ _timeout_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">BrpoplpushCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="BRPOPLPUSH _srckey_ _dstkey_ _timeout_ (Redis >">BRPOPLPUSH _srckey_ _dstkey_ _timeout_ (Redis ></a></h1> 2.1.8) =
|
||||
<i>Time complexity: O(1)</i><blockquote>Blocking version of the <a href="RpoplpushCommand.html">RPOPLPUSH</a> command. Atomically removes and returnsthe last element (tail) of the source list at <i>srckey</i>, and as a side effect pushes the returned element in the head of the list at <i>dstkey</i>.</blockquote>
|
||||
If the source list is empty, the client blocks until another client pushes against the source list. Of course in such a case the push operation against the destination list will be performed after the command unblocks detecting a push against the source list.<br/><br/>Note that the command returns an error if the target key already exists but is not a list. The error is delayed at the time the push operation is attempted, that is, immediately if the source list is not empty, or when the first push against the source list happens in the case the command would block.<br/><br/>The timeout value can be 0 or a positive integer value. When it is zero the command will block forever, until something is pushed against <i>srckey</i>. Otherwise the command will wait the specified number of seconds at max, returning an nil value when the timeout expires.<br/><br/>The source and destination of the list can be the same, having the effect of rotating the list. Please check <a href="RpoplpushCommand.html">RPOPLPUSH</a> for more information.<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Bulk reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,42 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Comparisons: Contents</b><br> <a href="#Memcached">Memcached</a><br> <a href="#Tokyo Cabinet / Toyo Tyrant">Tokyo Cabinet / Toyo Tyrant</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Comparisons</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
if your are asking yourself how is Redis different fom other key-value stores here you will find it compared to some of the most popular contendors (all great software) in this category. <h2><a name="Memcached">Memcached</a></h2><ul><li> Memcached is not persistent, it just holds everything in memory without saving since its main goal is to be used as a cache, while Redis is <a href="Persistence.html">persistent</a>.</li></ul>
|
||||
<ul><li> Like memcached Redis uses a key-value model, but while keys can just be strings, values in Redis can be <a href="Lists.html">Lists</a>, <a href="Sets.html">Sets</a> or <a href="SortedSets.html">SortedSets</a> and complex operations like intersections, set/get n-th element of lists, pop/push of elements, can be performed against sets and lists.</li></ul>
|
||||
<h2><a name="Tokyo Cabinet / Toyo Tyrant">Tokyo Cabinet / Toyo Tyrant</a></h2>Redis and Tokyo Cabinet can be used for the same applications, but actually they are <i>very different</i> beasts. If you read Twitter messages of people involved in scalable things both products are reported to work well, but surely there are times where one or the other can be the best choice.<br/><br/><ul><li> Tokyo Cabinet writes synchronously on disk, Redis takes the whole dataset on memory and writes on disk asynchronously. Tokyo Cabinet is safer and probably a better idea if your dataset is going to be bigger than RAM, but Redis is faster (note that Redis supports master-slave replication that is trivial to setup, so you are safe anyway if you want a setup where data can't be lost even after a disaster). </li></ul>
|
||||
<ul><li> Redis supports higher level operations and data structures. Tokyo Cabinet supports a kind of database that is able to organize data into rows with named fields (in a way very similar to Berkeley DB) but can't do things like server side List and Set operations Redis is able to do: pushing or popping from Lists in an atomic way, in O(1) time complexity, server side Set intersections, Sorting of schema free data in complex ways (By the way TC supports sorting in the table-based database format). Redis on the other hand does not support the abstraction of tables with fields, the idea is that you can build this stuff in software easily if you really need a table-alike approach. </li></ul>
|
||||
<ul><li> Tokyo Cabinet does not implement a networking layer. You have to use a networking layer called Tokyo Tyrant that interfaces to Tokyo Cabinet so you can talk to Tokyo Cabinet in a client-server fashion. In Redis the networking support is built-in inside the server, and is basically the only interface between the external world and the dataset. </li></ul>
|
||||
<ul><li> Redis is reported to be much faster, especially if you plan to access Tokyo Cabinet via Tokyo Tyrant. Here I can only say that with Redis you can expect 100,000 operations/seconds with a normal Linux box and 50 concurrent clients. You should test Redis, Tokyo, and the other alternatives with your specific work load to get a feeling about performances for your application. </li></ul>
|
||||
<ul><li> Redis is not an on-disk DB engine like Tokyo: the latter can be used as a fast DB engine in your C project without the networking overhead just linking to the library. Still in many scalable applications you need multiple servers talking with multiple clients, so the client-server model is almost always needed, this is why in Redis this is built-in. </li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ConfigCommand: Contents</b><br> <a href="#CONFIG GET _pattern_ (Redis >">CONFIG GET _pattern_ (Redis ></a><br> <a href="#CONFIG SET _parameter_ _value_ (Redis >">CONFIG SET _parameter_ _value_ (Redis ></a><br> <a href="#CONFIG GET _pattern_">CONFIG GET _pattern_</a><br> <a href="#CONFIG SET _parameter_ _value_">CONFIG SET _parameter_ _value_</a><br> <a href="#Parameters value format">Parameters value format</a><br> <a href="#See Also">See Also</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ConfigCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ControlCommandsSidebar.html">ControlCommandsSidebar</a><h1><a name="CONFIG GET _pattern_ (Redis >">CONFIG GET _pattern_ (Redis ></a></h1> 2.0)=
|
||||
<h1><a name="CONFIG SET _parameter_ _value_ (Redis >">CONFIG SET _parameter_ _value_ (Redis ></a></h1> 2.0)=<br/><br/><blockquote>The CONFIG command is able to retrieve or alter the configuration of a runningRedis server. Not all the configuration parameters are supported.</blockquote>
|
||||
<blockquote>CONFIG has two sub commands, GET and SET. The GET command is used to readthe configuration, while the SET command is used to alter the configuration.</blockquote>
|
||||
<h2><a name="CONFIG GET _pattern_">CONFIG GET _pattern_</a></h2><blockquote>CONFIG GET returns the current configuration parameters. This sub commandonly accepts a single argument, that is glob style pattern. All theconfiguration parameters matching this parameter are reported as alist of key-value pairs. Example:</blockquote><pre class="codeblock python" name="code">
|
||||
$ redis-cli config get '*'
|
||||
1. "dbfilename"
|
||||
2. "dump.rdb"
|
||||
3. "requirepass"
|
||||
4. (nil)
|
||||
5. "masterauth"
|
||||
6. (nil)
|
||||
7. "maxmemory"
|
||||
8. "0\n"
|
||||
9. "appendfsync"
|
||||
10. "everysec"
|
||||
11. "save"
|
||||
12. "3600 1 300 100 60 10000"
|
||||
|
||||
$ redis-cli config get 'm*'
|
||||
1. "masterauth"
|
||||
2. (nil)
|
||||
3. "maxmemory"
|
||||
4. "0\n"
|
||||
</pre>The return type of the command is a <a href="ReplyTypes.html">bulk reply</a>.<h2><a name="CONFIG SET _parameter_ _value_">CONFIG SET _parameter_ _value_</a></h2><blockquote>CONFIG SET is used in order to reconfigure the server, setting a specificconfiguration parameter to a new value.</blockquote>
|
||||
<blockquote>The list of configuration parameters supported by CONFIG SET can beobtained issuing a <code name="code" class="python">CONFIG GET *</code> command.</blockquote>
|
||||
<blockquote>The configuration set using CONFIG SET is immediately loaded by the Redisserver that will start acting as specified starting from the next command.</blockquote>
|
||||
<blockquote>Example:</blockquote><pre class="codeblock python python" name="code">
|
||||
$ ./redis-cli
|
||||
redis> set x 10
|
||||
OK
|
||||
redis> config set maxmemory 200
|
||||
OK
|
||||
redis> set y 20
|
||||
(error) ERR command not allowed when used memory > 'maxmemory'
|
||||
redis> config set maxmemory 0
|
||||
OK
|
||||
redis> set y 20
|
||||
OK
|
||||
</pre><h2><a name="Parameters value format">Parameters value format</a></h2><blockquote>The value of the configuration parameter is the same as the one of thesame parameter in the Redis configuration file, with the following exceptions:</blockquote>
|
||||
<ul><li> The <code name="code" class="python">save</code> paramter is a list of space-separated integers. Every pair of integers specify the time and number of changes limit to trigger a save. For instance the command <code name="code" class="python">CONFIG SET save "3600 10 60 10000"</code> will configure the server to issue a background saving of the RDB file every 3600 seconds if there are at least 10 changes in the dataset, and every 60 seconds if there are at least 10000 changes. To completely disable automatic snapshots just set the parameter as an empty string.</li><li> All the integer parameters representing memory are returned and accepted only using bytes as unit.</li></ul>
|
||||
<h2><a name="See Also">See Also</a></h2>The <a href="InfoCommand.html">INFO</a> command can be used in order to read configuriaton parameters that are not available in the CONFIG command.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Configuration: Contents</b>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Configuration</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
The <code name="code" class="python">redis.conf</code> file included in the source code distribution is a starting point, you should be able to modify it in order do adapt it to your needs without troubles reading the comments inside the file.<br/><br/>In order to start Redis using a configuration file just pass the file name as the sole argument when starting the server:<br/><br/><pre class="codeblock python" name="code">
|
||||
$ ./redis-server redis.conf
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ConnectionHandlingSidebar: Contents</b>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ConnectionHandlingSidebar</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
== Connection handling ==<br/><br/><ul><li> <a href="QuitCommand.html">QUIT</a></li><li> <a href="AuthCommand.html">AUTH</a></li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ControlCommandsSidebar: Contents</b>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ControlCommandsSidebar</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
== Control Commands ==<br/><br/><ul><li> <a href="SaveCommand.html">SAVE</a></li><li> <a href="BgsaveCommand.html">BGSAVE</a></li><li> <a href="BgrewriteaofCommand.html">BGREWRITEAOF</a></li><li> <a href="LastsaveCommand.html">LASTSAVE</a></li><li> <a href="ShutdownCommand.html">SHUTDOWN</a></li><li> <a href="InfoCommand.html">INFO</a></li><li> <a href="MonitorCommand.html">MONITOR</a></li><li> <a href="SlaveofCommand.html">SLAVEOF</a></li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Credits: Contents</b><br> <a href="#Credits">Credits</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Credits</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
<h1><a name="Credits">Credits</a></h1><ul><li> The Redis server was designed and written by <a href="http://invece.org" target="_blank">Salvatore Sanfilippo (aka antirez)</a></li><li> <a href="http://brainspl.at/" target="_blank">Ezra Zygmuntowicz (aka ezmobius)</a> - Ruby client lib initial version and hacking</li><li> <a href="http://qix.it" target="_blank">Ludovico Magnocavallo (aka ludo)</a> - Python clinet lib</li><li> <a href="http://www.adroll.com/" target="_blank">Valentino Volonghi of Adroll</a> - Erlang client lib</li><li> <b>brettbender</b> - found and fixed a bug in sds.c that caused the server to crash at least on 64 bit systems, and anyway to be buggy since we used the same vararg thing against vsprintf without to call va_start and va_end every time.</li><li> <a href="http://www.rot13.org/~dpavlin" target="_blank">Dobrica Pavlinusic</a> - Perl client lib</li><li> Brian Hammond - AUTH command implementation, C++ client lib</li><li> <a href="http://www.clorophilla.net/" target="_blank">Daniele Alessandri</a> - Lua client lib</li><li> Corey Stup - C99 cleanups</li><li> Taylor Weibley - Ruby client improvements</li><li> Bob Potter - Rearrange redisObject struct to reduce memory usage in 64bit environments</li><li> Luca Guidi and Brian McKinney - Ruby client improvements</li><li> Aman Gupta - SDIFF / SDIFFSTORE, other Set operations improvements, ability to disable clients timeout.</li><li> Diego Rosario Brogna - Code and ideas about dumping backtrace on sigsegv and similar error conditions.</li></ul>
|
||||
p.s. sorry to take this file in sync is hard in this early days. Please drop me an email if I forgot to add your name here!
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>DbsizeCommand: Contents</b><br> <a href="#DBSIZE">DBSIZE</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">DbsizeCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="DBSIZE">DBSIZE</a></h1><blockquote>Return the number of keys in the currently selected database.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>DelCommand: Contents</b><br> <a href="#DEL _key1_ _key2_ ... _keyN_">DEL _key1_ _key2_ ... _keyN_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">DelCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="DEL _key1_ _key2_ ... _keyN_">DEL _key1_ _key2_ ... _keyN_</a></h1>
|
||||
<i>Time complexity: O(1)</i><blockquote>Remove the specified keys. If a given key does not existno operation is performed for this key. The command returns the number ofkeys removed.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically:<br/><br/><pre class="codeblock python" name="code">
|
||||
an integer greater than 0 if one or more keys were removed
|
||||
0 if none of the specified key existed
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>DesignPatterns: Contents</b>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">DesignPatterns</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
Use random keys instead of incremental keys in order to avoid a single-key that gets incremented by many servers. This can can't be distributed among servers.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>EventLibray: Contents</b><br> <a href="#Event Library">Event Library</a><br> <a href="#Why is an Event Library needed at all?">Why is an Event Library needed at all?</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">EventLibray</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="Event Library">Event Library</a></h1><h2><a name="Why is an Event Library needed at all?">Why is an Event Library needed at all?</a></h2>Let us figure it out through a series of Q&As.<br/><br/>Q: What do you expect a network server to be doing all the time? <br/>
|
||||
A: Watch for inbound connections on the port its listening and accept them.<br/><br/>Q: Calling <a href="http://man.cx/accept%282%29" target="_blank">accept</a> yields a descriptor. What do I do with it?<br/>
|
||||
A: Save the descriptor and do a non-blocking read/write operation on it.<br/><br/>Q: Why does the read/write have to be non-blocking?<br/>
|
||||
A: If the file operation ( even a socket in Unix is a file ) is blocking how could the server for example accept other connection requests when its blocked in a file I/O operation.<br/><br/>Q: I guess I have to do many such non-blocking operations on the socket to see when it's ready. Am I right?<br/>
|
||||
A: Yes. That is what an event library does for you. Now you get it.<br/><br/>Q: How do Event Libraries do what they do?<br/>
|
||||
A: They use the operating system's <a href="http://www.devshed.com/c/a/BrainDump/Linux-Files-and-the-Event-Poll-Interface/" target="_blank">polling</a> facility along with timers.<br/><br/>Q: So are there any open source event libraries that do what you just described? <br/>
|
||||
A: Yes. Libevent and Libev are two such event libraries that I can recall off the top of my head.<br/><br/>Q: Does Redis use such open source event libraries for handling socket I/O?<br/>
|
||||
A: No. For various <a href="http://groups.google.com/group/redis-db/browse_thread/thread/b52814e9ef15b8d0/" target="_blank">reasons</a> Redis uses its own event library.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ExistsCommand: Contents</b><br> <a href="#EXISTS _key_">EXISTS _key_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ExistsCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="EXISTS _key_">EXISTS _key_</a></h1>
|
||||
<i>Time complexity: O(1)</i><blockquote>Test if the specified key exists. The command returns"0" if the key exists, otherwise "1" is returned.Note that even keys set with an empty string as value willreturn "1".</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically:<br/><br/><pre class="codeblock python" name="code">
|
||||
1 if the key exists.
|
||||
0 if the key does not exist.
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ExpireCommand: Contents</b><br> <a href="#EXPIRE _key_ _seconds_">EXPIRE _key_ _seconds_</a><br> <a href="#EXPIREAT _key_ _unixtime_ (Redis >">EXPIREAT _key_ _unixtime_ (Redis ></a><br> <a href="#PERSIST _key_ (Redis >">PERSIST _key_ (Redis ></a><br> <a href="#How the expire is removed from a key">How the expire is removed from a key</a><br> <a href="#Restrictions with write operations against volatile keys">Restrictions with write operations against volatile keys</a><br> <a href="#Restrictions for write operations with volatile keys as sources">Restrictions for write operations with volatile keys as sources</a><br> <a href="#Setting the timeout again on already volatile keys">Setting the timeout again on already volatile keys</a><br> <a href="#Enhanced Lazy Expiration algorithm">Enhanced Lazy Expiration algorithm</a><br> <a href="#Version 1.0">Version 1.0</a><br> <a href="#Version 1.1">Version 1.1</a><br> <a href="#Return value">Return value</a><br> <a href="#FAQ: Can you explain better why Redis < 2.1.3 deletes keys with an EXPIRE on write operations?">FAQ: Can you explain better why Redis < 2.1.3 deletes keys with an EXPIRE on write operations?</a><br> <a href="#FAQ: How this limitations were solved in Redis versions > 2.1.3?">FAQ: How this limitations were solved in Redis versions > 2.1.3?</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ExpireCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="EXPIRE _key_ _seconds_">EXPIRE _key_ _seconds_</a></h1>
|
||||
<h1><a name="EXPIREAT _key_ _unixtime_ (Redis >">EXPIREAT _key_ _unixtime_ (Redis ></a></h1> 1.1)=
|
||||
<h1><a name="PERSIST _key_ (Redis >">PERSIST _key_ (Redis ></a></h1> 2.1.3) =
|
||||
<i>Time complexity: O(1)</i><blockquote>Set a timeout on the specified key. After the timeout the key will beautomatically deleted by the server. A key with an associated timeout issaid to be <i>volatile</i> in Redis terminology.</blockquote>
|
||||
<blockquote>Voltile keys are stored on disk like the other keys, the timeout is persistenttoo like all the other aspects of the dataset. Saving a dataset containingexpires and stopping the server does not stop the flow of time as Redisstores on disk the time when the key will no longer be available as Unixtime, and not the remaining seconds.</blockquote>
|
||||
<blockquote>EXPIREAT works exctly like EXPIRE but instead to get the number of secondsrepresenting the Time To Live of the key as a second argument (that is arelative way of specifing the TTL), it takes an absolute one in the form ofa UNIX timestamp (Number of seconds elapsed since 1 Gen 1970).</blockquote>
|
||||
<blockquote>EXPIREAT was introduced in order to implement <a href="AppendOnlyFileHowto.html">the Append Only File persistence mode</a>so that EXPIRE commands are automatically translated into EXPIREAT commands for the append only file. Of course EXPIREAT can alsoused by programmers that need a way to simply specify that a given key should expire at a given time in the future.</blockquote>
|
||||
<blockquote>Since Redis 2.1.3 you can update the value of the timeout of a key alreadyhaving an expire set. It is also possible to undo the expire at allturning the key into a normal key using the PERSIST command.</blockquote>
|
||||
<h2><a name="How the expire is removed from a key">How the expire is removed from a key</a></h2><blockquote>When the key is set to a new value using the SET command, or when a keyis destroied via DEL, the timeout is removed from the key.</blockquote>
|
||||
<h2><a name="Restrictions with write operations against volatile keys">Restrictions with write operations against volatile keys</a></h2><blockquote>IMPORTANT: Since Redis 2.1.3 or greater, there are no restrictions aboutthe operations you can perform against volatile keys, however older versionsof Redis, including the current stable version 2.0.0, has the followinglimitations:</blockquote>
|
||||
<blockquote>Write operations like LPUSH, LSET and every other command that has theeffect of modifying the value stored at a volatile key have a special semantic:basically a volatile key is destroyed when it is target of a write operation.See for example the following usage pattern:</blockquote>
|
||||
<pre class="codeblock python" name="code">
|
||||
% ./redis-cli lpush mylist foobar /Users/antirez/hack/redis
|
||||
OK
|
||||
% ./redis-cli lpush mylist hello /Users/antirez/hack/redis
|
||||
OK
|
||||
% ./redis-cli expire mylist 10000 /Users/antirez/hack/redis
|
||||
1
|
||||
% ./redis-cli lpush mylist newelement
|
||||
OK
|
||||
% ./redis-cli lrange mylist 0 -1 /Users/antirez/hack/redis
|
||||
1. newelement
|
||||
</pre><blockquote>What happened here is that LPUSH against the key with a timeout set deletedthe key before to perform the operation. There is so a simple rule, writeoperations against volatile keys will destroy the key before to perform theoperation. Why Redis uses this behavior? In order to retain an importantproperty: a server that receives a given number of commands in the samesequence will end with the same dataset in memory. Without the delete-on-writesemantic what happens is that the state of the server depends on the timethe commands were issued. This is not a desirable property in a distributed databasethat supports replication.</blockquote>
|
||||
<h2><a name="Restrictions for write operations with volatile keys as sources">Restrictions for write operations with volatile keys as sources</a></h2>Even when the volatile key is not modified as part of a write operation, if it is
|
||||
read in a composite write operation (such as SINTERSTORE) it will be cleared at the
|
||||
start of the operation. This is done to avoid concurrency issues in replication.
|
||||
Imagine a key that is about to expire and the composite operation is run against it.
|
||||
On a slave node, this key might already be expired, which leaves you with a
|
||||
desync in your dataset.<h2><a name="Setting the timeout again on already volatile keys">Setting the timeout again on already volatile keys</a></h2><blockquote>Trying to call EXPIRE against a key that already has an associated timeoutwill not change the timeout of the key, but will just return 0. If insteadthe key does not have a timeout associated the timeout will be set and EXPIREwill return 1.</blockquote>
|
||||
<h2><a name="Enhanced Lazy Expiration algorithm">Enhanced Lazy Expiration algorithm</a></h2><blockquote>Redis does not constantly monitor keys that are going to be expired.Keys are expired simply when some client tries to access a key, andthe key is found to be timed out.</blockquote>
|
||||
<blockquote>Of course this is not enough as there are expired keys that will neverbe accessed again. This keys should be expired anyway, so once everysecond Redis test a few keys at random among keys with an expire set.All the keys that are already expired are deleted from the keyspace. </blockquote>
|
||||
<h3><a name="Version 1.0">Version 1.0</a></h3><blockquote>Each time a fixed number of keys where tested (100 by default). So ifyou had a client setting keys with a very short expire faster than 100for second the memory continued to grow. When you stopped to insertnew keys the memory started to be freed, 100 keys every second in thebest conditions. Under a peak Redis continues to use more and more RAMeven if most keys are expired in each sweep.</blockquote>
|
||||
<h3><a name="Version 1.1">Version 1.1</a></h3><blockquote>Each time Redis:</blockquote>
|
||||
<ol><li> Tests 100 random keys from expired keys set.</li><li> Deletes all the keys found expired.</li><li> If more than 25 keys were expired, it start again from 1.</li></ol>
|
||||
<blockquote>This is a trivial probabilistic algorithm, basically the assumption isthat our sample is representative of the whole key space,and we continue to expire until the percentage of keys that are likelyto be expired is under 25%</blockquote>
|
||||
<blockquote>This means that at any given moment the maximum amount of keys alreadyexpired that are using memory is at max equal to max setting operations per second divided by 4.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically:<br/><br/><pre class="codeblock python python" name="code">
|
||||
1: the timeout was set.
|
||||
0: the timeout was not set since the key already has an associated timeout
|
||||
(this may happen only in Redis versions < 2.1.3, Redis >= 2.1.3 will
|
||||
happily update the timeout), or the key does not exist.
|
||||
</pre><h2><a name="FAQ: Can you explain better why Redis < 2.1.3 deletes keys with an EXPIRE on write operations?">FAQ: Can you explain better why Redis < 2.1.3 deletes keys with an EXPIRE on write operations?</a></h2>
|
||||
Ok let's start with the problem:
|
||||
<pre class="codeblock python python python" name="code">
|
||||
redis> set a 100
|
||||
OK
|
||||
redis> expire a 360
|
||||
(integer) 1
|
||||
redis> incr a
|
||||
(integer) 1
|
||||
</pre>
|
||||
I set a key to the value of 100, then set an expire of 360 seconds, and then incremented the key (before the 360 timeout expired of course). The obvious result would be: 101, instead the key is set to the value of 1. Why?
|
||||
There is a very important reason involving the Append Only File and Replication. Let's rework a bit our example adding the notion of time to the mix:
|
||||
<pre class="codeblock python python python python" name="code">
|
||||
SET a 100
|
||||
EXPIRE a 5
|
||||
... wait 10 seconds ...
|
||||
INCR a
|
||||
</pre>
|
||||
Imagine a Redis version that does not implement the "Delete keys with an expire set on write operation" semantic.
|
||||
Running the above example with the 10 seconds pause will lead to 'a' being set to the value of 1, as it no longer exists when INCR is called 10 seconds later.<br/><br/>Instead if we drop the 10 seconds pause, the result is that 'a' is set to 101.<br/><br/>And in the practice timing changes! For instance the client may wait 10 seconds before INCR, but the sequence written in the Append Only File (and later replayed-back as fast as possible when Redis is restarted) will not have the pause. Even if we add a timestamp in the AOF, when the time difference is smaller than our timer resolution, we have a race condition.<br/><br/>The same happens with master-slave replication. Again, consider the example above: the client will use the same sequence of commands without the 10 seconds pause, but the replication link will slow down for a few seconds due to a network problem. Result? The master will contain 'a' set to 101, the slave 'a' set to 1.<br/><br/>The only way to avoid this but at the same time have reliable non time dependent timeouts on keys is to destroy volatile keys when a write operation is attempted against it.<br/><br/>After all Redis is one of the rare fully persistent databases that will give you EXPIRE. This comes to a cost :)<h2><a name="FAQ: How this limitations were solved in Redis versions > 2.1.3?">FAQ: How this limitations were solved in Redis versions > 2.1.3?</a></h2>Since Redis 2.1.3 there are no longer restrictions in the use you can do of write commands against volatile keys, still the replication and AOF file are guaranteed to be fully consistent.<br/><br/>In order to obtain a correct behavior without sacrificing consistency now when a key expires, a DEL operation is synthesized in both the AOF file and against all the attached slaves. This way the expiration process is centralized in the master instance, and there is no longer a chance of consistency errors.<br/><br/>However while the slaves while connected to a master will not expire keys independently, they'll still take the full state of the expires existing in the dataset, so when a slave is elected to a master it will be able to expire the keys independently, fully acting as a master.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>FlushallCommand: Contents</b><br> <a href="#FLUSHALL">FLUSHALL</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">FlushallCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="FLUSHALL">FLUSHALL</a></h1>
|
||||
<blockquote>Delete all the keys of all the existing databases, not just the currently selected one. This command never fails.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Status code reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>FlushdbCommand: Contents</b><br> <a href="#FLUSHDB">FLUSHDB</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">FlushdbCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="FLUSHDB">FLUSHDB</a></h1>
|
||||
<blockquote>Delete all the keys of the currently selected DB. This command never fails.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Status code reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>FromSqlToDataStructures: Contents</b><br> <a href="#Introduction (IDEA MORE THAN A DRAFT)">Introduction (IDEA MORE THAN A DRAFT)</a><br> <a href="#Data Structures">Data Structures</a><br> <a href="#Dude where is my SELECT statement?">Dude where is my SELECT statement?</a><br> <a href="#LISTs">LISTs</a><br> <a href="#SETs">SETs</a><br> <a href="#SORT to the rescue">SORT to the rescue</a><br> <a href="#SORT BY">SORT BY</a><br> <a href="#HASHEs">HASHEs</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">FromSqlToDataStructures</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="Introduction (IDEA MORE THAN A DRAFT)">Introduction (IDEA MORE THAN A DRAFT)</a></h1><b>¿Coming from SQLand?</b> <i>¿Who doesn't?</i> Redis is simple, <i>primitive</i> when comapred to the world you are used to in the world of Relational Database Managers (RDBMS) and Structure Query Language (SQL), here you will find insight to build bridges between both worlds to model real life problems.<h2><a name="Data Structures">Data Structures</a></h2>When I was young, happy and single ;) I studied <b>Data Structures</b> at the university, actually I learnt Data Structures and Algorithms <i>before</i> learning anything about Databases, and particularly RDBMS and SQL. This is natural because you need to know about Data Structures and Algorithms to understand a Database.<br/><br/>Redis can be seen as a <b>Data Structures Server</b>, a very simple interface to a extremly fast and efficient <h2><a name="Dude where is my SELECT statement?">Dude where is my SELECT statement?</a></h2><h2><a name="LISTs">LISTs</a></h2>In SQL there is no such thing as a "natural" order, a SELECT statement without a ORDER BY clause will return data in a undefined order. In Redis <a href="LISTs.html">LISTs</a> address the problem of natural ordering, ...<h2><a name="SETs">SETs</a></h2>So you have a bunch of unordered data, <h2><a name="SORT to the rescue">SORT to the rescue</a></h2>But sometimes we <i>need</i> to actually sort a LIST in a order different from its natural or take a SET and have it ordered, there is where the <i>fast</i> SORT commands comes handy... <h3><a name="SORT BY">SORT BY</a></h3>Just SORTing keys would be kind of boring, sometimes useless right? Well, you can SORT...<h2><a name="HASHEs">HASHEs</a></h2>Umm, sorry you will have to wait for a <a href="RoadMap.html">upcoming version of Redis</a> to have Hashes, but here are Idioms you should house to manage Dictionary like data...
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>GenericCommandsSidebar: Contents</b>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">GenericCommandsSidebar</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
== Generic Commands ==<br/><br/><ul><li> <a href="ExistsCommand.html">EXISTS</a></li><li> <a href="DelCommand.html">DEL</a></li><li> <a href="TypeCommand.html">TYPE</a></li><li> <a href="KeysCommand.html">KEYS</a></li><li> <a href="RandomkeyCommand.html">RANDOMKEY</a></li><li> <a href="RenameCommand.html">RENAME</a></li><li> <a href="RenamenxCommand.html">RENAMENX</a></li><li> <a href="DbsizeCommand.html">DBSIZE</a></li><li> <a href="ExpireCommand.html">EXPIRE</a></li><li> <a href="ExpireCommand.html">PERSIST</a></li><li> <a href="TtlCommand.html">TTL</a></li><li> <a href="SelectCommand.html">SELECT</a></li><li> <a href="MoveCommand.html">MOVE</a></li><li> <a href="FlushdbCommand.html">FLUSHDB</a></li><li> <a href="FlushallCommand.html">FLUSHALL</a></li><li> <a href="MultiExecCommand.html">Redis Transactions</a></li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>GetCommand: Contents</b><br> <a href="#GET _key_">GET _key_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">GetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="StringCommandsSidebar.html">StringCommandsSidebar</a><h1><a name="GET _key_">GET _key_</a></h1>
|
||||
<i>Time complexity: O(1)</i><blockquote>Get the value of the specified key. If the keydoes not exist the special value 'nil' is returned.If the value stored at <i>key</i> is not a string an erroris returned because GET can only handle string values.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Bulk reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>GetbitCommand: Contents</b><br> <a href="#GETBIT _key_ _offset_ (Redis >">GETBIT _key_ _offset_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">GetbitCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="GETBIT _key_ _offset_ (Redis >">GETBIT _key_ _offset_ (Redis ></a></h1> 2.1.8) =
|
||||
<i>Time complexity: O(1)</i><blockquote>Returns the bit value at <i>offset</i> in the string value stored at <i>key</i>.</blockquote>
|
||||
When <i>offset</i> is beyond the string length, the string is assumed to be a contiguous space with 0 bits. When <i>key</i> does not exist it is assumed to be an empty string, so <i>offset</i> is always out of range and the value is also assumed to be a contiguous space with 0 bits.<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically: the bit value stored at <i>offset</i>.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>GetsetCommand: Contents</b><br> <a href="#GETSET _key_ _value_">GETSET _key_ _value_</a><br> <a href="#Return value">Return value</a><br> <a href="#Design patterns">Design patterns</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">GetsetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="StringCommandsSidebar.html">StringCommandsSidebar</a><h1><a name="GETSET _key_ _value_">GETSET _key_ _value_</a></h1>
|
||||
<i>Time complexity: O(1)</i><blockquote>GETSET is an atomic <i>set this value and return the old value</i> command.Set <i>key</i> to the string <i>value</i> and return the old value stored at <i>key</i>.The string can't be longer than 1073741824 bytes (1 GB).</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Bulk reply</a><h2><a name="Design patterns">Design patterns</a></h2><blockquote>GETSET can be used together with INCR for counting with atomic reset whena given condition arises. For example a process may call INCR against thekey <i>mycounter</i> every time some event occurred, but from time totime we need to get the value of the counter and reset it to zero atomicallyusing <code name="code" class="python">GETSET mycounter 0</code>.</blockquote>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HackingStrings: Contents</b><br> <a href="#Hacking Strings">Hacking Strings</a><br> <a href="#Creating Redis Strings">Creating Redis Strings</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HackingStrings</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="Hacking Strings">Hacking Strings</a></h1>The implementation of Redis strings is contained in <b></b>sds.c<b></b> ( sds stands for Simple Dynamic Strings ).<br/><br/>The C structure <i>sdshdr</i> declared in <b>sds.h</b> represents a Redis string:<br/><br/><pre class="codeblock python" name="code">
|
||||
struct sdshdr {
|
||||
long len;
|
||||
long free;
|
||||
char buf[];
|
||||
};
|
||||
</pre>The <i>buf</i> character array stores the actual string.<br/><br/>The <i>len</i> field stores the length of <i>buf</i>. This makes obtaining the length
|
||||
of a Redis string an O(1) operation.<br/><br/>The <i>free</i> field stores the number of additional bytes available for use.<br/><br/>Together the <i>len</i> and <i>free</i> field can be thought of as holding the metadata of the
|
||||
<i>buf</i> character array.<h2><a name="Creating Redis Strings">Creating Redis Strings</a></h2>A new data type named <code name="code" class="python">sds</code> is defined in <b>sds.h</b> to be a synonymn for a character pointer:<br/><br/><pre class="codeblock python python" name="code">
|
||||
typedef char *sds;
|
||||
</pre><code name="code" class="python">sdsnewlen</code> function defined in <b>sds.c</b> creates a new Redis String: <br/><br/><pre class="codeblock python python python" name="code">
|
||||
sds sdsnewlen(const void *init, size_t initlen) {
|
||||
struct sdshdr *sh;
|
||||
|
||||
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
|
||||
#ifdef SDS_ABORT_ON_OOM
|
||||
if (sh == NULL) sdsOomAbort();
|
||||
#else
|
||||
if (sh == NULL) return NULL;
|
||||
#endif
|
||||
sh->len = initlen;
|
||||
sh->free = 0;
|
||||
if (initlen) {
|
||||
if (init) memcpy(sh->buf, init, initlen);
|
||||
else memset(sh->buf,0,initlen);
|
||||
}
|
||||
sh->buf[initlen] = '\0';
|
||||
return (char*)sh->buf;
|
||||
}
|
||||
</pre>Remember a Redis string is a variable of type <code name="code" class="python">struct sdshdr</code>. But <code name="code" class="python">sdsnewlen</code> returns a character pointer!!<br/><br/>That's a trick and needs some explanation.<br/><br/>Suppose I create a Redis string using <code name="code" class="python">sdsnewlen</code> like below:<br/><br/><pre class="codeblock python python python python" name="code">
|
||||
sdsnewlen("redis", 5);
|
||||
</pre>This creates a new variable of type <code name="code" class="python">struct sdshdr</code> allocating memory for <i>len</i> and <i>free</i>
|
||||
fields as well as for the <i>buf</i> character array.<br/><br/><pre class="codeblock python python python python python" name="code">
|
||||
sh = zmalloc(sizeof(struct sdshdr)+initlen+1); // initlen is length of init argument.
|
||||
</pre>After <code name="code" class="python">sdsnewlen</code> succesfully creates a Redis string the result is something like:<br/><br/><pre class="codeblock python python python python python python" name="code">
|
||||
-----------
|
||||
|5|0|redis|
|
||||
-----------
|
||||
^ ^
|
||||
sh sh->buf
|
||||
</pre><code name="code" class="python">sdsnewlen</code> returns sh->buf to the caller.<br/><br/>What do you do if you need to free the Redis string pointed by <code name="code" class="python">sh</code>?<br/><br/>You want the pointer <code name="code" class="python">sh</code> but you only have the pointer <code name="code" class="python">sh->buf</code>.<br/><br/>Can you get the pointer <code name="code" class="python">sh</code> from <code name="code" class="python">sh->buf</code>?<br/><br/>Yes. Pointer arithmetic. Notice from the above ASCII art that if you subtract
|
||||
the size of two longs from <code name="code" class="python">sh->buf</code> you get the pointer <code name="code" class="python">sh</code>. <br/><br/>The sizeof two longs happens to be the size of <code name="code" class="python">struct sdshdr</code>.<br/><br/>Look at <code name="code" class="python">sdslen</code> function and see this trick at work:<br/><br/><pre class="codeblock python python python python python python python" name="code">
|
||||
size_t sdslen(const sds s) {
|
||||
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
|
||||
return sh->len;
|
||||
}
|
||||
</pre>Knowing this trick you could easily go through the rest of the functions in <b>sds.c</b>.<br/><br/>The Redis string implementation is hidden behind an interface that accepts only character pointers. The users of Redis strings need not care about how its implemented and treat Redis strings as a character pointer.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HashCommandsSidebar: Contents</b>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HashCommandsSidebar</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
== Hash Commands ==<br/><br/><ul><li> <a href="HsetCommand.html">HSET</a></li><li> <a href="HgetCommand.html">HGET</a></li><li> <a href="HsetnxCommand.html">HSETNX</a></li><li> <a href="HmsetCommand.html">HMSET</a></li><li> <a href="HmgetCommand.html">HMGET</a></li><li> <a href="HincrbyCommand.html">HINCRBY</a></li><li> <a href="HexistsCommand.html">HEXISTS</a></li><li> <a href="HdelCommand.html">HDEL</a></li><li> <a href="HlenCommand.html">HLEN</a></li><li> <a href="HgetallCommand.html">HKEYS</a></li><li> <a href="HgetallCommand.html">HVALS</a></li><li> <a href="HgetallCommand.html">HGETALL</a></li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Hashes: Contents</b><br> <a href="#Redis Hash Type">Redis Hash Type</a><br> <a href="#Implementation details">Implementation details</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Hashes</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="HashCommandsSidebar.html">HashCommandsSidebar</a><h1><a name="Redis Hash Type">Redis Hash Type</a></h1>Redis Hashes are unordered maps of <a href="String.html">Redis Strings</a> between fields and values. It is possible to add, remove, test for existence of fields in O(1) amortized time. It is also possible to enumerate all the keys, values, or both, in O(N) (where N is the number of fields inside the hash).<br/><br/>Redis Hashes are interesting because they are very well suited to represent objects. For instance web applications users can be represented by a Redis Hash containing fields such username, encrpypted_password, lastlogin, and so forth.<br/><br/>Another very important property of Redis Hashes is that they use very little memory for hashes composed of a small number of fields (configurable, check redis.conf for details), compared to storing every field as a top level Redis key. This is obtained using a different specialized representation for small hashes. See the implementation details paragraph below for more information.<br/><br/>Commands operating on hashes try to make a good use of the return value in order to signal the application about previous existence of fields. For instance the <a href="HsetCommand.html">HSET</a> command will return 1 if the field set was not already present in the hash, otherwise will return 0 (and the user knows this was just an update operation).<br/><br/>The max number of fields in a set is 232-1 (4294967295, more than 4 billion of members per hash).<h1><a name="Implementation details">Implementation details</a></h1>The obvious internal representation of hashes is indeed an hash table, as the name of the data structure itself suggests. Still the drawback of this representation is that there is a lot of space overhead for hash table metadata.<br/><br/>Because one of the most interesting uses of Hashes is object encoding, and objects are often composed of a few fields each, Redis uses a different internal representation for small hashes (for Redis to consider a hash small, this must be composed a limited number of fields, and each field and value can't exceed a given number of bytes. All this is user-configurable).<br/><br/>Small hashes are thus encoded using a data structure called zipmap (is not something you can find in a CS book, the name is a Redis invention), that is a very memory efficient data structure to represent string to string maps, at the cost of being O(N) instead of O(1) for most operations. Since the constant times of this data structure are very small, and the zipmaps are converted into real hash tables once they are big enough, the amortized time of Redis hashes is still O(1), and in the practice small zipmaps are not slower than small hash tables because they are designed for good cache locality and fast access.<br/><br/>The result is that small hashes are both memory efficient and fast, while bigger hashes are fast but not as memory efficient than small hashes.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HdelCommand: Contents</b><br> <a href="#HDEL _key_ _field_ (Redis >">HDEL _key_ _field_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HdelCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="HashCommandsSidebar.html">HashCommandsSidebar</a><h1><a name="HDEL _key_ _field_ (Redis >">HDEL _key_ _field_ (Redis ></a></h1> 1.3.10)=
|
||||
<i>Time complexity: O(1)</i><blockquote>Remove the specified <i>field</i> from an hash stored at <i>key</i>.</blockquote>
|
||||
<blockquote>If the <i>field</i> was present in the hash it is deleted and 1 is returned, otherwise 0 is returned and no operation is performed.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HexistsCommand: Contents</b><br> <a href="#HEXISTS _key_ _field_ (Redis >">HEXISTS _key_ _field_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HexistsCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="HashCommandsSidebar.html">HashCommandsSidebar</a><h1><a name="HEXISTS _key_ _field_ (Redis >">HEXISTS _key_ _field_ (Redis ></a></h1> 1.3.10)=
|
||||
<i>Time complexity: O(1)</i><blockquote>Return 1 if the hash stored at <i>key</i> contains the specified <i>field</i>.</blockquote>
|
||||
<blockquote>Return 0 if the <i>key</i> is not found or the <i>field</i> is not present.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HgetCommand: Contents</b><br> <a href="#HGET _key_ _field_ (Redis >">HGET _key_ _field_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HgetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="HashCommandsSidebar.html">HashCommandsSidebar</a><h1><a name="HGET _key_ _field_ (Redis >">HGET _key_ _field_ (Redis ></a></h1> 1.3.10)=
|
||||
<i>Time complexity: O(1)</i><blockquote>If <i>key</i> holds a hash, retrieve the value associated to the specified <i>field</i>.</blockquote>
|
||||
<blockquote>If the <i>field</i> is not found or the <i>key</i> does not exist, a special 'nil' value is returned.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Bulk reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HgetallCommand: Contents</b><br> <a href="#HKEYS _key_ (Redis >">HKEYS _key_ (Redis ></a><br> <a href="#HVALS _key_ (Redis >">HVALS _key_ (Redis ></a><br> <a href="#HGETALL _key_ (Redis >">HGETALL _key_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HgetallCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="HashCommandsSidebar.html">HashCommandsSidebar</a><h1><a name="HKEYS _key_ (Redis >">HKEYS _key_ (Redis ></a></h1> 1.3.10)=
|
||||
<h1><a name="HVALS _key_ (Redis >">HVALS _key_ (Redis ></a></h1> 1.3.10)=
|
||||
<h1><a name="HGETALL _key_ (Redis >">HGETALL _key_ (Redis ></a></h1> 1.3.10)=
|
||||
<i>Time complexity: O(N), where N is the total number of fields in the hash</i><blockquote>HKEYS returns all the fields names contained into a hash, HVALS all the associated values, while HGETALL returns both the fields and values in the form of <i>field1</i>, <i>value1</i>, <i>field2</i>, <i>value2</i>, ..., <i>fieldN</i>, <i>valueN</i>.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Multi Bulk Reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HincrbyCommand: Contents</b><br> <a href="#HINCRBY _key_ _field_ _value_ (Redis >">HINCRBY _key_ _field_ _value_ (Redis ></a><br> <a href="#Examples">Examples</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HincrbyCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="HINCRBY _key_ _field_ _value_ (Redis >">HINCRBY _key_ _field_ _value_ (Redis ></a></h1> 1.3.10)=
|
||||
<i>Time complexity: O(1)</i><blockquote>Increment the number stored at <i>field</i> in the hash at <i>key</i> by <i>value</i>. If <i>key</i> does not exist, a new key holding a hash is created. If <i>field</i> does not exist or holds a string, the value is set to 0 before applying the operation.</blockquote>
|
||||
<blockquote>The range of values supported by HINCRBY is limited to 64 bit signed integers.</blockquote><h2><a name="Examples">Examples</a></h2>
|
||||
Since the <i>value</i> argument is signed you can use this command to perform both increments and decrements:<br/><br/><pre class="codeblock python" name="code">
|
||||
HINCRBY key field 1 (increment by one)
|
||||
HINCRBY key field -1 (decrement by one, just like the DECR command)
|
||||
HINCRBY key field -10 (decrement by 10)
|
||||
</pre>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a> The new value at <i>field</i> after the increment operation.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HlenCommand: Contents</b><br> <a href="#HLEN _key_ (Redis >">HLEN _key_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HlenCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="HashCommandsSidebar.html">HashCommandsSidebar</a><h1><a name="HLEN _key_ (Redis >">HLEN _key_ (Redis ></a></h1> 1.3.10)=
|
||||
<i>Time complexity: O(1)</i><blockquote>Return the number of entries (fields) contained in the hash stored at <i>key</i>. If the specified <i>key</i> does not exist, 0 is returned assuming an empty hash. </blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HmgetCommand: Contents</b><br> <a href="#HMGET _key_ _field1_ ... _fieldN_ (Redis >">HMGET _key_ _field1_ ... _fieldN_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HmgetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="HashCommandsSidebar.html">HashCommandsSidebar</a><h1><a name="HMGET _key_ _field1_ ... _fieldN_ (Redis >">HMGET _key_ _field1_ ... _fieldN_ (Redis ></a></h1> 1.3.10) =
|
||||
<i>Time complexity: O(N) (with N being the number of fields)</i><blockquote>Retrieve the values associated to the specified <i>fields</i>.</blockquote>
|
||||
<blockquote>If some of the specified <i>fields</i> do not exist, nil values are returned.Non existing keys are considered like empty hashes.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Multi Bulk Reply</a> specifically a list of all the values associated with the specified fields, in the same order of the request.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HmsetCommand: Contents</b><br> <a href="#HMSET _key_ _field1_ _value1_ ... _fieldN_ _valueN_ (Redis >">HMSET _key_ _field1_ _value1_ ... _fieldN_ _valueN_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HmsetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="HMSET _key_ _field1_ _value1_ ... _fieldN_ _valueN_ (Redis >">HMSET _key_ _field1_ _value1_ ... _fieldN_ _valueN_ (Redis ></a></h1> 1.3.10) =
|
||||
<i>Time complexity: O(N) (with N being the number of fields)</i><blockquote>Set the respective fields to the respective values. HMSET replaces old values with new values.</blockquote>
|
||||
<blockquote>If <i>key</i> does not exist, a new key holding a hash is created.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Status code reply</a> Always +OK because HMSET can't fail
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HsetCommand: Contents</b><br> <a href="#HSET _key_ _field_ _value_ (Redis >">HSET _key_ _field_ _value_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HsetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="HashCommandsSidebar.html">HashCommandsSidebar</a><h1><a name="HSET _key_ _field_ _value_ (Redis >">HSET _key_ _field_ _value_ (Redis ></a></h1> 1.3.10)=
|
||||
<i>Time complexity: O(1)</i><blockquote>Set the specified hash <i>field</i> to the specified <i>value</i>.</blockquote>
|
||||
<blockquote>If <i>key</i> does not exist, a new key holding a hash is created.</blockquote>
|
||||
<blockquote>If the field already exists, and the HSET just produced an update of thevalue, 0 is returned, otherwise if a new field is created 1 is returned.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>HsetnxCommand: Contents</b><br> <a href="#HSETNX _key_ _field_ _value_ (Redis >">HSETNX _key_ _field_ _value_ (Redis ></a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">HsetnxCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="HSETNX _key_ _field_ _value_ (Redis >">HSETNX _key_ _field_ _value_ (Redis ></a></h1> 1.3.10)=
|
||||
<i>Time complexity: O(1)</i><blockquote>Set the specified hash <i>field</i> to the specified <i>value</i>, if <i>field</i> does not exist yet.</blockquote>
|
||||
<blockquote>If <i>key</i> does not exist, a new key holding a hash is created.</blockquote>
|
||||
<blockquote>If the field already exists, this operation has no effect and returns 0.Otherwise, the field is set to <i>value</i> and the operation returns 1.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>IncrCommand: Contents</b><br> <a href="#INCR _key_">INCR _key_</a><br> <a href="#INCRBY _key_ _integer_">INCRBY _key_ _integer_</a><br> <a href="#DECR _key_ _integer_">DECR _key_ _integer_</a><br> <a href="#DECRBY _key_ _integer_">DECRBY _key_ _integer_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">IncrCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="StringCommandsSidebar.html">StringCommandsSidebar</a><h1><a name="INCR _key_">INCR _key_</a></h1>
|
||||
<h1><a name="INCRBY _key_ _integer_">INCRBY _key_ _integer_</a></h1>
|
||||
<h1><a name="DECR _key_ _integer_">DECR _key_ _integer_</a></h1>
|
||||
<h1><a name="DECRBY _key_ _integer_">DECRBY _key_ _integer_</a></h1>
|
||||
<i>Time complexity: O(1)</i><blockquote>Increment or decrement the number stored at <i>key</i> by one. If the key doesnot exist or contains a value of a wrong type, set the key to thevalue of "0" before to perform the increment or decrement operation.</blockquote>
|
||||
<blockquote>INCRBY and DECRBY work just like INCR and DECR but instead toincrement/decrement by 1 the increment/decrement is <i>integer</i>.</blockquote>
|
||||
<blockquote>INCR commands are limited to 64 bit signed integers.</blockquote>
|
||||
Note: this is actually a string operation, that is, in Redis there are not "integer" types. Simply the string stored at the key is parsed as a base 10 64 bit signed integer, incremented, and then converted back as a string.<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, this commands will reply with the new value of <i>key</i> after the increment or decrement.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>InfoCommand: Contents</b><br> <a href="#INFO">INFO</a><br> <a href="#Return value">Return value</a><br> <a href="#Notes">Notes</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">InfoCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ControlCommandsSidebar.html">ControlCommandsSidebar</a><h1><a name="INFO">INFO</a></h1><blockquote>The info command returns different information and statistics about the server in an format that's simple to parse by computers and easy to red by huamns.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Bulk reply</a>, specifically in the following format:<br/><br/><pre class="codeblock python" name="code">
|
||||
edis_version:0.07
|
||||
connected_clients:1
|
||||
connected_slaves:0
|
||||
used_memory:3187
|
||||
changes_since_last_save:0
|
||||
last_save_time:1237655729
|
||||
total_connections_received:1
|
||||
total_commands_processed:1
|
||||
uptime_in_seconds:25
|
||||
uptime_in_days:0
|
||||
</pre>All the fields are in the form <code name="code" class="python">field:value</code><h2><a name="Notes">Notes</a></h2><ul><li> <code name="code" class="python">used_memory</code> is returned in bytes, and is the total number of bytes allocated by the program using <code name="code" class="python">malloc</code>.</li><li> <code name="code" class="python">uptime_in_days</code> is redundant since the uptime in seconds contains already the full uptime information, this field is only mainly present for humans.</li><li> <code name="code" class="python">changes_since_last_save</code> does not refer to the number of key changes, but to the number of operations that produced some kind of change in the dataset.</li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>IntroductionToRedisDataTypes: Contents</b><br> <a href="#A fifteen minutes introduction to Redis data types">A fifteen minutes introduction to Redis data types</a><br> <a href="#Redis keys">Redis keys</a><br> <a href="#The string type">The string type</a><br> <a href="#The List type">The List type</a><br> <a href="#First steps with Redis lists">First steps with Redis lists</a><br> <a href="#Pushing IDs instead of the actual data in Redis lists">Pushing IDs instead of the actual data in Redis lists</a><br> <a href="#Redis Sets">Redis Sets</a><br> <a href="#A digression. How to get unique identifiers for strings">A digression. How to get unique identifiers for strings</a><br> <a href="#Sorted sets">Sorted sets</a><br> <a href="#Operating on ranges">Operating on ranges</a><br> <a href="#Back to the reddit example">Back to the reddit example</a><br> <a href="#Updating the scores of a sorted set">Updating the scores of a sorted set</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">IntroductionToRedisDataTypes</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="RedisGuides.html">RedisGuides</a>
|
||||
<h1><a name="A fifteen minutes introduction to Redis data types">A fifteen minutes introduction to Redis data types</a></h1>As you already probably know Redis is not a plain key-value store, actually it is a <b>data structures server</b>, supporting different kind of values. That is, you can't just set strings as values of keys. All the following data types are supported as values:<br/><br/><ul><li> Binary-safe strings.</li><li> Lists of binary-safe strings.</li><li> Sets of binary-safe strings, that are collection of unique unsorted elements. You can think at this as a Ruby hash where all the keys are set to the 'true' value.</li><li> Sorted sets, similar to Sets but where every element is associated to a floating number score. The elements are taken sorted by score. You can think at this as Ruby hashes where the key is the element and the value is the score, but where elements are always taken in order without requiring a sorting operation.</li></ul>
|
||||
It's not always trivial to grasp how this data types work and what to use in order to solve a given problem from the <a href="CommandReference.html">Redis command reference</a>, so this document is a crash course to Redis data types and their most used patterns.<br/><br/>For all the examples we'll use the <b>redis-cli</b> utility, that's a simple but handy command line utility to issue commands against the Redis server.<h2><a name="Redis keys">Redis keys</a></h2>Before to start talking about the different kind of values supported by Redis it is better to start saying that keys are not binary safe strings in Redis, but just strings not containing a space or a newline character. For instance "foo" or "123456789" or "foo_bar" are valid keys, while "hello world" or "hello\n" are not.<br/><br/>Actually there is nothing inside the Redis internals preventing the use of binary keys, it's just a matter of protocol, and actually the new protocol introduced with Redis 1.2 (1.2 betas are 1.1.x) in order to implement commands like MSET, is totally binary safe. Still for now consider this as an hard limit as the database is only tested with "normal" keys.<br/><br/>A few other rules about keys:<br/><br/><ul><li> Too long keys are not a good idea, for instance a key of 1024 bytes is not a good idea not only memory-wise, but also because the lookup of the key in the dataset may require several costly key-comparisons.</li><li> Too short keys are not a good idea. There is no point in writing "u:1000:pwd" as key if you can write instead "user:1000:password", the latter is more readable and the added space is very little compared to the space used by the key object itself.</li><li> Try to stick with a schema. For instance "object-type:id:field" can be a nice idea, like in "user:1000:password". I like to use dots for multi-words fields, like in "comment:1234:reply.to".</li></ul>
|
||||
<h2><a name="The string type">The string type</a></h2>This is the simplest Redis type. If you use only this type, Redis will be something like a memcached server with persistence.<br/><br/>Let's play a bit with the string type:<br/><br/><pre class="codeblock python" name="code">
|
||||
$ ./redis-cli set mykey "my binary safe value"
|
||||
OK
|
||||
$ ./redis-cli get mykey
|
||||
my binary safe value
|
||||
</pre>As you can see using the <a href="SetCommand.html">Set command</a> and the <a href="GetCommand.html">Get command</a> is trivial to set values to strings and have this strings returned back.<br/><br/>Values can be strings (including binary data) of every kind, for instance you can store a jpeg image inside a key. A value can't be bigger than 1 Gigabyte.<br/><br/>Even if strings are the basic values of Redis, there are interesting operations you can perform against them. For instance one is atomic increment:<br/><br/><pre class="codeblock python python" name="code">
|
||||
$ ./redis-cli set counter 100
|
||||
OK
|
||||
$ ./redis-cli incr counter
|
||||
(integer) 101
|
||||
$ ./redis-cli incr counter
|
||||
(integer) 102
|
||||
$ ./redis-cli incrby counter 10
|
||||
(integer) 112
|
||||
</pre>The <a href="IncrCommand.html">INCR</a> command parses the string value as an integer, increments it by one, and finally sets the obtained value as the new string value. There are other similar commands like <a href="IncrCommand.html">INCRBY</a>, <a href="IncrCommand.html">DECR</a> and <a href="IncrCommand.html">DECRBY</a>. Actually internally it's always the same command, acting in a slightly different way.<br/><br/>What means that INCR is atomic? That even multiple clients issuing INCR against the same key will never incur into a race condition. For instance it can't never happen that client 1 read "10", client 2 read "10" at the same time, both increment to 11, and set the new value of 11. The final value will always be of 12 ad the read-increment-set operation is performed while all the other clients are not executing a command at the same time.<br/><br/>Another interesting operation on string is the <a href="GetsetCommand.html">GETSET</a> command, that does just what its name suggests: Set a key to a new value, returning the old value, as result. Why this is useful? Example: you have a system that increments a Redis key using the <a href="IncrCommand.html">INCR</a> command every time your web site receives a new visit. You want to collect this information one time every hour, without loosing a single key. You can GETSET the key assigning it the new value of "0" and reading the old value back.<h2><a name="The List type">The List type</a></h2>To explain the List data type it's better to start with a little of theory, as the term <b>List</b> is often used in an improper way by information technology folks. For instance "Python Lists" are not what the name may suggest (Linked Lists), but them are actually Arrays (the same data type is called Array in Ruby actually).<br/><br/>From a very general point of view a List is just a sequence of ordered elements: 10,20,1,2,3 is a list, but when a list of items is implemented using an Array and when instead a <b>Linked List</b> is used for the implementation, the properties change a lot.<br/><br/>Redis lists are implemented via Linked Lists, this means that even if you have million of elements inside a list, the operation of adding a new element in the head or in the tail of the list is performed <b>in constant time</b>. Adding a new element with the <a href="LpushCommand.html">LPUSH</a> command to the head of a ten elements list is the same speed as adding an element to the head of a 10 million elements list.<br/><br/>What's the downside? That accessing an element <b>by index</b> is very fast in lists implemented with an Array and not so fast in lists implemented by linked lists.<br/><br/>Redis Lists are implemented with linked lists because for a database system is crucial to be able to add elements to a very long list in a very fast way. Another strong advantage is, as you'll see in a moment, that Redis Lists can be taken at constant length in constant time.<h3><a name="First steps with Redis lists">First steps with Redis lists</a></h3>The <a href="RpushCommand.html">LPUSH</a> command add a new element into a list, on the left (on head), while the <a href="RpushCommand.html">RPUSH</a> command add a new element into alist, ot the right (on tail). Finally the <a href="LrangeCommand.html">LRANGE</a> command extract ranges of elements from lists:<br/><br/><pre class="codeblock python python python" name="code">
|
||||
$ ./redis-cli rpush messages "Hello how are you?"
|
||||
OK
|
||||
$ ./redis-cli rpush messages "Fine thanks. I'm having fun with Redis"
|
||||
OK
|
||||
$ ./redis-cli rpush messages "I should look into this NOSQL thing ASAP"
|
||||
OK
|
||||
$ ./redis-cli lrange messages 0 2
|
||||
1. Hello how are you?
|
||||
2. Fine thanks. I'm having fun with Redis
|
||||
3. I should look into this NOSQL thing ASAP
|
||||
</pre>Note that <a href="LrangeCommand.html">LRANGE</a> takes two indexes, the first and the last element of the range to return. Both the indexes can be negative to tell Redis to start to count for the end, so -1 is the last element, -2 is the penultimate element of the list, and so forth.<br/><br/>As you can guess from the example above, lists can be used, for instance, in order to implement a chat system. Another use is as queues in order to route messages between different processes. But the key point is that <b>you can use Redis lists every time you require to access data in the same order they are added</b>. This will not require any SQL ORDER BY operation, will be very fast, and will scale to millions of elements even with a toy Linux box.<br/><br/>For instance in ranking systems like the social news reddit.com you can add every new submitted link into a List, and with <a href="LrangeCommand.html">LRANGE</a> it's possible to paginate results in a trivial way.<br/><br/>In a blog engine implementation you can have a list for every post, where to push blog comments, and so forth.<h3><a name="Pushing IDs instead of the actual data in Redis lists">Pushing IDs instead of the actual data in Redis lists</a></h3>In the above example we pushed our "objects" (simply messages in the example) directly inside the Redis list, but this is often not the way to go, as objects can be referenced in multiple times: in a list to preserve their chronological order, in a Set to remember they are about a specific category, in another list but only if this object matches some kind of requisite, and so forth.<br/><br/>Let's return back to the reddit.com example. A more credible pattern for adding submitted links (news) to the list is the following:<br/><br/><pre class="codeblock python python python python" name="code">
|
||||
$ ./redis-cli incr next.news.id
|
||||
(integer) 1
|
||||
$ ./redis-cli set news:1:title "Redis is simple"
|
||||
OK
|
||||
$ ./redis-cli set news:1:url "http://code.google.com/p/redis"
|
||||
OK
|
||||
$ ./redis-cli lpush submitted.news 1
|
||||
OK
|
||||
</pre>We obtained an unique incremental ID for our news object just incrementing a key, then used this ID to create the object setting a key for every field in the object. Finally the ID of the new object was pushed on the <b>submitted.news</b> list.<br/><br/>This is just the start. Check the <a href="CommandReference.html">Command Reference</a> and read about all the other list related commands. You can remove elements, rotate lists, get and set elements by index, and of course retrieve the length of the list with <a href="LLenCommand.html">LLEN</a>.<h2><a name="Redis Sets">Redis Sets</a></h2>Redis Sets are unordered collection of binary-safe strings. The <a href="SaddCommand.html">SADD</a> command adds a new element to a set. It's also possible to do a number of other operations against sets like testing if a given element already exists, performing the intersection, union or difference between multiple sets and so forth. An example is worth 1000 words:<br/><br/><pre class="codeblock python python python python python" name="code">
|
||||
$ ./redis-cli sadd myset 1
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd myset 2
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd myset 3
|
||||
(integer) 1
|
||||
$ ./redis-cli smembers myset
|
||||
1. 3
|
||||
2. 1
|
||||
3. 2
|
||||
</pre>I added three elements to my set and told Redis to return back all the elements. As you can see they are not sorted.<br/><br/>Now let's check if a given element exists:<br/><br/><pre class="codeblock python python python python python python" name="code">
|
||||
$ ./redis-cli sismember myset 3
|
||||
(integer) 1
|
||||
$ ./redis-cli sismember myset 30
|
||||
(integer) 0
|
||||
</pre>"3" is a member of the set, while "30" is not. Sets are very good in order to express relations between objects. For instance we can easily use Redis Sets in order to implement tags.<br/><br/>A simple way to model this is to have, for every object you want to tag, a Set with all the IDs of the tags associated with the object, and for every tag that exists, a Set of of all the objects tagged with this tag.<br/><br/>For instance if our news ID 1000 is tagged with tag 1,2,5 and 77, we can specify the following two Sets:<br/><br/><pre class="codeblock python python python python python python python" name="code">
|
||||
$ ./redis-cli sadd news:1000:tags 1
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd news:1000:tags 2
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd news:1000:tags 5
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd news:1000:tags 77
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd tag:1:objects 1000
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd tag:2:objects 1000
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd tag:5:objects 1000
|
||||
(integer) 1
|
||||
$ ./redis-cli sadd tag:77:objects 1000
|
||||
(integer) 1
|
||||
</pre>To get all the tags for a given object is trivial:<br/><br/>$ ./redis-cli smembers <a href="news:1000:tags" target="_blank">news:1000:tags</a>
|
||||
1. 5
|
||||
2. 1
|
||||
3. 77
|
||||
4. 2<br/><br/>But there are other non trivial operations that are still easy to implement using the right Redis commands. For instance we may want the list of all the objects having as tags 1, 2, 10, and 27 at the same time. We can do this using the <a href="SinterCommand.html">SinterCommand</a> that performs the intersection between different sets. So in order to reach our goal we can just use:<br/><br/><pre class="codeblock python python python python python python python python" name="code">
|
||||
$ ./redis-cli sinter tag:1:objects tag:2:objects tag:10:objects tag:27:objects
|
||||
... no result in our dataset composed of just one object ;) ...
|
||||
</pre>Look at the <a href="CommandReference.html">Command Reference</a> to discover other Set related commands, there are a bunch of interesting one. Also make sure to check the <a href="SortCommand.html">SORT</a> command as both Redis Sets and Lists are sortable.<h2><a name="A digression. How to get unique identifiers for strings">A digression. How to get unique identifiers for strings</a></h2>In our tags example we showed tag IDs without to mention how this IDs can be obtained. Basically for every tag added to the system, you need an unique identifier. You also want to be sure that there are no race conditions if multiple clients are trying to add the same tag at the same time. Also, if a tag already exists, you want its ID returned, otherwise a new unique ID should be created and associated to the tag.<br/><br/>Redis 1.4 will add the Hash type. With it it will be trivial to associate strings with unique IDs, but how to do this today with the current commands exported by Redis in a reliable way?<br/><br/>Our first attempt (that is broken) can be the following. Let's suppose we want to get an unique ID for the tag "redis":<br/><br/><ul><li> In order to make this algorithm binary safe (they are just tags but think to utf8, spaces and so forth) we start performing the SHA1 sum of the tag. SHA1(redis) = b840fc02d524045429941cc15f59e41cb7be6c52.</li><li> Let's check if this tag is already associated with an unique ID with the command <b>GET tag:b840fc02d524045429941cc15f59e41cb7be6c52:id</b>.</li><li> If the above GET returns an ID, return it back to the user. We already have the unique ID.</li><li> Otherwise... create a new unique ID with <b>INCR next.tag.id</b> (assume it returned 123456).</li><li> Finally associate this new ID to our tag with <b>SET tag:b840fc02d524045429941cc15f59e41cb7be6c52:id 123456</b> and return the new ID to the caller.</li></ul>
|
||||
Nice. Or better.. broken! What about if two clients perform this commands at the same time trying to get the unique ID for the tag "redis"? If the timing is right they'll both get <b>nil</b> from the GET operation, will both increment the <b>next.tag.id</b> key and will set two times the key. One of the two clients will return the wrong ID to the caller. To fix the algorithm is not hard fortunately, and this is the sane version:<br/><br/><ul><li> In order to make this algorithm binary safe (they are just tags but think to utf8, spaces and so forth) we start performing the SHA1 sum of the tag. SHA1(redis) = b840fc02d524045429941cc15f59e41cb7be6c52.</li><li> Let's check if this tag is already associated with an unique ID with the command <b>GET tag:b840fc02d524045429941cc15f59e41cb7be6c52:id</b>.</li><li> If the above GET returns an ID, return it back to the user. We already have the unique ID.</li><li> Otherwise... create a new unique ID with <b>INCR next.tag.id</b> (assume it returned 123456).</li><li> Finally associate this new ID to our tag with <b>SETNX tag:b840fc02d524045429941cc15f59e41cb7be6c52:id 123456</b>. By using SETNX if a different client was faster than this one the key wil not be setted. Not only, SETNX returns 1 if the key is set, 0 otherwise. So... let's add a final step to our computation.</li><li> If SETNX returned 1 (We set the key) return 123456 to the caller, it's our tag ID, otherwise perform <b>GET tag:b840fc02d524045429941cc15f59e41cb7be6c52:id</b> and return the value to the caller.</li></ul>
|
||||
<h2><a name="Sorted sets">Sorted sets</a></h2>Sets are a very handy data type, but... they are a bit too unsorted in order to fit well for a number of problems ;) This is why Redis 1.2 introduced Sorted Sets. They are very similar to Sets, collections of binary-safe strings, but this time with an associated score, and an operation similar to the List LRANGE operation to return items in order, but working against Sorted Sets, that is, the <a href="ZrangeCommand.html">ZRANGE</a> command.<br/><br/>Basically Sorted Sets are in some way the Redis equivalent of Indexes in the SQL world. For instance in our reddit.com example above there was no mention about how to generate the actual home page with news raked by user votes and time. We'll see how sorted sets can fix this problem, but it's better to start with something simpler, illustrating the basic working of this advanced data type. Let's add a few selected hackers with their year of birth as "score".<br/><br/><pre class="codeblock python python python python python python python python python" name="code">
|
||||
$ ./redis-cli zadd hackers 1940 "Alan Kay"
|
||||
(integer) 1
|
||||
$ ./redis-cli zadd hackers 1953 "Richard Stallman"
|
||||
(integer) 1
|
||||
$ ./redis-cli zadd hackers 1965 "Yukihiro Matsumoto"
|
||||
(integer) 1
|
||||
$ ./redis-cli zadd hackers 1916 "Claude Shannon"
|
||||
(integer) 1
|
||||
$ ./redis-cli zadd hackers 1969 "Linus Torvalds"
|
||||
(integer) 1
|
||||
$ ./redis-cli zadd hackers 1912 "Alan Turing"
|
||||
(integer) 1
|
||||
</pre>For sorted sets it's a joke to return these hackers sorted by their birth year because actually <b>they are already sorted</b>. Sorted sets are implemented via a dual-ported data structure containing both a skip list and an hash table, so every time we add an element Redis performs an O(log(N)) operation, that's good, but when we ask for sorted elements Redis does not have to do any work at all, it's already all sorted:<br/><br/><pre class="codeblock python python python python python python python python python python" name="code">
|
||||
$ ./redis-cli zrange hackers 0 -1
|
||||
1. Alan Turing
|
||||
2. Claude Shannon
|
||||
3. Alan Kay
|
||||
4. Richard Stallman
|
||||
5. Yukihiro Matsumoto
|
||||
6. Linus Torvalds
|
||||
</pre>Didn't know that Linus was younger than Yukihiro btw ;)<br/><br/>Anyway I want to order this elements the other way around, using <a href="ZREVRANGE.html">ZrangeCommand</a> instead of <a href="ZRANGE.html">ZrangeCommand</a> this time:<br/><br/><pre class="codeblock python python python python python python python python python python python" name="code">
|
||||
$ ./redis-cli zrevrange hackers 0 -1
|
||||
1. Linus Torvalds
|
||||
2. Yukihiro Matsumoto
|
||||
3. Richard Stallman
|
||||
4. Alan Kay
|
||||
5. Claude Shannon
|
||||
6. Alan Turing
|
||||
</pre>A very important note, ZSets have just a "default" ordering but you are still free to call the <a href="SortCommand.html">SORT</a> command against sorted sets to get a different ordering (but this time the server will waste CPU). An alternative for having multiple orders is to add every element in multiple sorted sets at the same time.<h3><a name="Operating on ranges">Operating on ranges</a></h3>Sorted sets are more powerful than this. They can operate on ranges. For instance let's try to get all the individuals that born up to the 1950. We use the <a href="ZrangebyscoreCommand.html">ZRANGEBYSCORE</a> command to do it:<br/><br/><pre class="codeblock python python python python python python python python python python python python" name="code">
|
||||
$ ./redis-cli zrangebyscore hackers -inf 1950
|
||||
1. Alan Turing
|
||||
2. Claude Shannon
|
||||
3. Alan Kay
|
||||
</pre>We asked Redis to return all the elements with a score between negative infinite and 1950 (both extremes are included).<br/><br/>It's also possible to remove ranges of elements. For instance let's remove all the hackers born between 1940 and 1960 from the sorted set:<br/><br/><pre class="codeblock python python python python python python python python python python python python python" name="code">
|
||||
$ ./redis-cli zremrangebyscore hackers 1940 1960
|
||||
(integer) 2
|
||||
</pre><a href="ZremrangebyscoreCommand.html">ZREMRANGEBYSCORE</a> is not the best command name, but it can be very useful, and returns the number of removed elements.<h3><a name="Back to the reddit example">Back to the reddit example</a></h3>For the last time, back to the Reddit example. Now we have a decent plan to populate a sorted set in order to generate the home page. A sorted set can contain all the news that are not older than a few days (we remove old entries from time to time using ZREMRANGEBYSCORE). A background job gets all the elements from this sorted set, get the user votes and the time of the news, and compute the score to populate the <b>reddit.home.page</b> sorted set with the news IDs and associated scores. To show the home page we have just to perform a blazingly fast call to ZRANGE.<br/><br/>From time to time we'll remove too old news from the <b>reddit.home.page</b> sorted set as well in order for our system to work always against a limited set of news.<h3><a name="Updating the scores of a sorted set">Updating the scores of a sorted set</a></h3>Just a final note before to finish this tutorial. Sorted sets scores can be updated at any time. Just calling again ZADD against an element already included in the sorted set will update its score (and position) in O(log(N)), so sorted sets are suitable even when there are tons of updates.<br/><br/>This tutorial is in no way complete, this is just the basics to get started with Redis, read the <a href="CommandReference.html">Command Reference</a> to discover a lot more.<br/><br/>Thanks for reading. Salvatore.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>KeysCommand: Contents</b><br> <a href="#KEYS _pattern_">KEYS _pattern_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">KeysCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="KEYS _pattern_">KEYS _pattern_</a></h1>
|
||||
<i>Time complexity: O(n) (with n being the number of keys in the DB, and assuming keys and pattern of limited length)</i><blockquote>Returns all the keys matching the glob-style <i>pattern</i> asspace separated strings. For example if you have in thedatabase the keys "foo" and "foobar" the command "KEYS foo<code name="code" class="python">*</code>"will return "foo foobar".</blockquote>
|
||||
<blockquote>Note that while the time complexity for this operation is O(n)the constant times are pretty low. For example Redis runningon an entry level laptop can scan a 1 million keys databasein 40 milliseconds. <b>Still it's better to consider this one of
|
||||
<blockquote>the slow commands that may ruin the DB performance if not usedwith care*.</blockquote>
|
||||
<blockquote>In other words this command is intended only for debugging and *special* operations like creating a script to change the DB schema. Don't use it in your normal code. Use Redis <a href="Sets.html">Sets</a> in order to group together a subset of objects.</blockquote>
|
||||
Glob style patterns examples:
|
||||
<blockquote>* h?llo will match hello hallo hhllo* h*llo will match hllo heeeello* h<code name="code" class="python">[</code>ae<code name="code" class="python">]</code>llo will match hello and hallo, but not hillo</blockquote>Use \ to escape special chars if you want to match them verbatim.<h2><a name="Return value">Return value</a></h2>
|
||||
<a href="ReplyTypes.html">Multi bulk reply</a></b></blockquote>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>LastsaveCommand: Contents</b><br> <a href="#LASTSAVE">LASTSAVE</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">LastsaveCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ControlCommandsSidebar.html">ControlCommandsSidebar</a><h1><a name="LASTSAVE">LASTSAVE</a></h1>
|
||||
<blockquote>Return the UNIX TIME of the last DB save executed with success.A client may check if a <a href="BgsaveCommand.html">BGSAVE</a> command succeeded reading the LASTSAVEvalue, then issuing a <a href="BgsaveCommand.html">BGSAVE</a> command and checking at regular intervalsevery N seconds if LASTSAVE changed.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically an UNIX time stamp.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>LindexCommand: Contents</b><br> <a href="#LINDEX _key_ _index_">LINDEX _key_ _index_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">LindexCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="LINDEX _key_ _index_">LINDEX _key_ _index_</a></h1>
|
||||
<i>Time complexity: O(n) (with n being the length of the list)</i><blockquote>Return the specified element of the list stored at the specifiedkey. 0 is the first element, 1 the second and so on. Negative indexesare supported, for example -1 is the last element, -2 the penultimateand so on.</blockquote>
|
||||
<blockquote>If the value stored at key is not of list type an error is returned.If the index is out of range a 'nil' reply is returned.</blockquote>
|
||||
<blockquote>Note that even if the average time complexity is O(n) asking forthe first or the last element of the list is O(1).</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Bulk reply</a>, specifically the requested element.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ListCommandsSidebar: Contents</b>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ListCommandsSidebar</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
== List Commands ==<br/><br/><ul><li> <a href="RpushCommand.html">RPUSH</a></li><li> <a href="RpushCommand.html">LPUSH</a></li><li> <a href="LlenCommand.html">LLEN</a></li><li> <a href="LrangeCommand.html">LRANGE</a></li><li> <a href="LtrimCommand.html">LTRIM</a></li><li> <a href="LindexCommand.html">LINDEX</a></li><li> <a href="LsetCommand.html">LSET</a></li><li> <a href="LremCommand.html">LREM</a></li><li> <a href="LpopCommand.html">LPOP</a></li><li> <a href="LpopCommand.html">RPOP</a></li><li> <a href="BlpopCommand.html">BLPOP</a></li><li> <a href="BlpopCommand.html">BRPOP</a></li><li> <a href="RpoplpushCommand.html">RPOPLPUSH</a></li><li> <a href="BrpoplpushCommand.html">BRPOPLPUSH</a></li><li> <a href="SortCommand.html">SORT</a></li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Lists: Contents</b><br> <a href="#Redis List Type">Redis List Type</a><br> <a href="#Implementation details">Implementation details</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Lists</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="Redis List Type">Redis List Type</a></h1>Redis Lists are lists of <a href="Strings.html">Redis Strings</a>, sorted by insertion order. It's possible to add elements to a Redis List pushing new elements on the head (on the left) or on the tail (on the right) of the list.<br/><br/>The <a href="RpushCommand.html">LPUSH</a> command inserts a new elmenet on head, while <a href="RpushCommand.html">RPUSH</a> inserts a new element on tail. A new list is created when one of this operations is performed against an empty key.<br/><br/>For instance if perform the following operations:
|
||||
<pre class="codeblock python" name="code">
|
||||
LPUSH mylist a # now the list is "a"
|
||||
LPUSH mylist b # now the list is "b","a"
|
||||
RPUSH mylist c # now the list is "b","a","c" (RPUSH was used this time)
|
||||
</pre>
|
||||
The resulting list stored at <i>mylist</i> will contain the elements "b","a","c".<br/><br/>The max length of a list is 232-1 elements (4294967295, more than 4 billion of elements per list).<h1><a name="Implementation details">Implementation details</a></h1>Redis Lists are implemented as doubly liked lists. A few commands benefit from the fact the lists are doubly linked in order to reach the needed element starting from the nearest extreme (head or tail). <a href="LrangeCommand.html">LRANGE</a> and <a href="LindexCommand.html">LINDEX</a> are examples of such commands.<br/><br/>The use of linked lists also guarantees that regardless of the length of the list pushing and popping are O(1) operations.<br/><br/>Redis Lists cache length information so <a href="LlenCommand.html">LLEN</a> is O(1) as well.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>LlenCommand: Contents</b><br> <a href="#LLEN _key_">LLEN _key_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">LlenCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="LLEN _key_">LLEN _key_</a></h1>
|
||||
<i>Time complexity: O(1)</i><blockquote>Return the length of the list stored at the specified key. If thekey does not exist zero is returned (the same behaviour as forempty lists). If the value stored at <i>key</i> is not a list an error is returned.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically:<br/><br/><pre class="codeblock python" name="code">
|
||||
The length of the list.
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>LpopCommand: Contents</b><br> <a href="#LPOP _key_">LPOP _key_</a><br> <a href="#RPOP _key_">RPOP _key_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">LpopCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="LPOP _key_">LPOP _key_</a></h1>
|
||||
<h1><a name="RPOP _key_">RPOP _key_</a></h1>
|
||||
<i>Time complexity: O(1)</i><blockquote>Atomically return and remove the first (LPOP) or last (RPOP) elementof the list. For example if the list contains the elements "a","b","c" LPOPwill return "a" and the list will become "b","c".</blockquote>
|
||||
<blockquote>If the <i>key</i> does not exist or the list is already empty the specialvalue 'nil' is returned.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Bulk reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>LrangeCommand: Contents</b><br> <a href="#LRANGE _key_ _start_ _end_">LRANGE _key_ _start_ _end_</a><br> <a href="#Consistency with range functions in various programming languages">Consistency with range functions in various programming languages</a><br> <a href="#Out-of-range indexes">Out-of-range indexes</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">LrangeCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="LRANGE _key_ _start_ _end_">LRANGE _key_ _start_ _end_</a></h1>
|
||||
<i>Time complexity: O(start+n) (with n being the length of the range and start being the start offset)</i>Return the specified elements of the list stored at the specified
|
||||
key. Start and end are zero-based indexes. 0 is the first element
|
||||
of the list (the list head), 1 the next element and so on.<br/><br/>For example LRANGE foobar 0 2 will return the first three elements
|
||||
of the list.<br/><br/><i>start</i> and <i>end</i> can also be negative numbers indicating offsets
|
||||
from the end of the list. For example -1 is the last element of
|
||||
the list, -2 the penultimate element and so on.<h2><a name="Consistency with range functions in various programming languages">Consistency with range functions in various programming languages</a></h2>Note that if you have a list of numbers from 0 to 100, LRANGE 0 10 will return
|
||||
11 elements, that is, rightmost item is included. This <b>may or may not</b> be consistent with
|
||||
behavior of range-related functions in your programming language of choice (think Ruby's Range.new, Array#slice or Python's range() function).<br/><br/>LRANGE behavior is consistent with one of Tcl.<h2><a name="Out-of-range indexes">Out-of-range indexes</a></h2>Indexes out of range will not produce an error: if start is over
|
||||
the end of the list, or start <code name="code" class="python">></code> end, an empty list is returned.
|
||||
If end is over the end of the list Redis will threat it just like
|
||||
the last element of the list.<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Multi bulk reply</a>, specifically a list of elements in the specified range.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>LremCommand: Contents</b><br> <a href="#LREM _key_ _count_ _value_">LREM _key_ _count_ _value_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">LremCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="LREM _key_ _count_ _value_">LREM _key_ _count_ _value_</a></h1>
|
||||
<i>Time complexity: O(N) (with N being the length of the list)</i><blockquote>Remove the first <i>count</i> occurrences of the <i>value</i> element from the list.If <i>count</i> is zero all the elements are removed. If <i>count</i> is negativeelements are removed from tail to head, instead to go from head to tailthat is the normal behaviour. So for example LREM with count -2 and_hello_ as value to remove against the list (a,b,c,hello,x,hello,hello) willlave the list (a,b,c,hello,x). The number of removed elements is returnedas an integer, see below for more information about the returned value.Note that non existing keys are considered like empty lists by LREM, so LREMagainst non existing keys will always return 0.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer Reply</a>, specifically:<br/><br/><pre class="codeblock python" name="code">
|
||||
The number of removed elements if the operation succeeded
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>LsetCommand: Contents</b><br> <a href="#LSET _key_ _index_ _value_">LSET _key_ _index_ _value_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">LsetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="LSET _key_ _index_ _value_">LSET _key_ _index_ _value_</a></h1>
|
||||
<i>Time complexity: O(N) (with N being the length of the list)</i><blockquote>Set the list element at <i>index</i> (see LINDEX for information about the_index_ argument) with the new <i>value</i>. Out of range indexes willgenerate an error. Note that setting the first or last elements ofthe list is O(1).</blockquote>
|
||||
<blockquote>Similarly to other list commands accepting indexes, the index can be negative to access elements starting from the end of the list. So -1 is the last element, -2 is the penultimate, and so forth.</blockquote><h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Status code reply</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>LtrimCommand: Contents</b><br> <a href="#LTRIM _key_ _start_ _end_">LTRIM _key_ _start_ _end_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">LtrimCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ListCommandsSidebar.html">ListCommandsSidebar</a><h1><a name="LTRIM _key_ _start_ _end_">LTRIM _key_ _start_ _end_</a></h1>
|
||||
<i>Time complexity: O(n) (with n being len of list - len of range)</i><blockquote>Trim an existing list so that it will contain only the specifiedrange of elements specified. Start and end are zero-based indexes.0 is the first element of the list (the list head), 1 the next elementand so on.</blockquote>
|
||||
<blockquote>For example LTRIM foobar 0 2 will modify the list stored at foobarkey so that only the first three elements of the list will remain.</blockquote>
|
||||
<blockquote>_start_ and <i>end</i> can also be negative numbers indicating offsetsfrom the end of the list. For example -1 is the last element ofthe list, -2 the penultimate element and so on.</blockquote>
|
||||
<blockquote>Indexes out of range will not produce an error: if start is overthe end of the list, or start > end, an empty list is left as value.If end over the end of the list Redis will threat it just likethe last element of the list.</blockquote>
|
||||
<blockquote>Hint: the obvious use of LTRIM is together with LPUSH/RPUSH. For example:</blockquote>
|
||||
<pre class="codeblock python" name="code">
|
||||
LPUSH mylist <someelement>
|
||||
LTRIM mylist 0 99
|
||||
</pre><blockquote>The above two commands will push elements in the list taking care thatthe list will not grow without limits. This is very useful when usingRedis to store logs for example. It is important to note that when usedin this way LTRIM is an O(1) operation because in the average casejust one element is removed from the tail of the list.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Status code reply</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>MgetCommand: Contents</b><br> <a href="#MGET _key1_ _key2_ ... _keyN_">MGET _key1_ _key2_ ... _keyN_</a><br> <a href="#Return value">Return value</a><br> <a href="#Example">Example</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">MgetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="StringCommandsSidebar.html">StringCommandsSidebar</a><h1><a name="MGET _key1_ _key2_ ... _keyN_">MGET _key1_ _key2_ ... _keyN_</a></h1>
|
||||
<i>Time complexity: O(1) for every key</i><blockquote>Get the values of all the specified keys. If one or more keys dont existor is not of type String, a 'nil' value is returned instead of the valueof the specified key, but the operation never fails.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Multi bulk reply</a><h2><a name="Example">Example</a></h2><pre class="codeblock python" name="code">
|
||||
$ ./redis-cli set foo 1000
|
||||
+OK
|
||||
$ ./redis-cli set bar 2000
|
||||
+OK
|
||||
$ ./redis-cli mget foo bar
|
||||
1. 1000
|
||||
2. 2000
|
||||
$ ./redis-cli mget foo bar nokey
|
||||
1. 1000
|
||||
2. 2000
|
||||
3. (nil)
|
||||
$
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>MonitorCommand: Contents</b><br> <a href="#MONITOR">MONITOR</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">MonitorCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ControlCommandsSidebar.html">ControlCommandsSidebar</a><h1><a name="MONITOR">MONITOR</a></h1><blockquote>MONITOR is a debugging command that outputs the whole sequence of commandsreceived by the Redis server. is very handy in order to understandwhat is happening into the database. This command is used directlyvia telnet.</blockquote>
|
||||
<pre class="codeblock python" name="code">
|
||||
% telnet 127.0.0.1 6379
|
||||
Trying 127.0.0.1...
|
||||
Connected to segnalo-local.com.
|
||||
Escape character is '^]'.
|
||||
MONITOR
|
||||
+OK
|
||||
monitor
|
||||
keys *
|
||||
dbsize
|
||||
set x 6
|
||||
foobar
|
||||
get x
|
||||
del x
|
||||
get x
|
||||
set key_x 5
|
||||
hello
|
||||
set key_y 5
|
||||
hello
|
||||
set key_z 5
|
||||
hello
|
||||
set foo_a 5
|
||||
hello
|
||||
</pre><blockquote>The ability to see all the requests processed by the server is useful in orderto spot bugs in the application both when using Redis as a database and asa distributed caching system.</blockquote>
|
||||
<blockquote>In order to end a monitoring session just issue a QUIT command by hand.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><b>Non standard return value</b>, just dumps the received commands in an infinite flow.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>MoveCommand: Contents</b><br> <a href="#MOVE _key_ _dbindex_">MOVE _key_ _dbindex_</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">MoveCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="MOVE _key_ _dbindex_">MOVE _key_ _dbindex_</a></h1>
|
||||
<blockquote>Move the specified key from the currently selected DB to the specifieddestination DB. Note that this command returns 1 only if the key wassuccessfully moved, and 0 if the target key was already there or if thesource key was not found at all, so it is possible to use MOVE as a lockingprimitive.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically:<br/><br/><pre class="codeblock python" name="code">
|
||||
1 if the key was moved
|
||||
0 if the key was not moved because already present on the target DB or was not found in the current DB.
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>MsetCommand: Contents</b><br> <a href="#MSET _key1_ _value1_ _key2_ _value2_ ... _keyN_ _valueN_ (Redis >">MSET _key1_ _value1_ _key2_ _value2_ ... _keyN_ _valueN_ (Redis ></a><br> <a href="#MSETNX _key1_ _value1_ _key2_ _value2_ ... _keyN_ _valueN_ (Redis >">MSETNX _key1_ _value1_ _key2_ _value2_ ... _keyN_ _valueN_ (Redis ></a><br> <a href="#MSET Return value">MSET Return value</a><br> <a href="#MSETNX Return value">MSETNX Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">MsetCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="StringCommandsSidebar.html">StringCommandsSidebar</a><h1><a name="MSET _key1_ _value1_ _key2_ _value2_ ... _keyN_ _valueN_ (Redis >">MSET _key1_ _value1_ _key2_ _value2_ ... _keyN_ _valueN_ (Redis ></a></h1> 1.1) =
|
||||
<h1><a name="MSETNX _key1_ _value1_ _key2_ _value2_ ... _keyN_ _valueN_ (Redis >">MSETNX _key1_ _value1_ _key2_ _value2_ ... _keyN_ _valueN_ (Redis ></a></h1> 1.1) =
|
||||
<i>Time complexity: O(1) to set every key</i><blockquote>Set the the respective keys to the respective values. MSET will replace oldvalues with new values, while MSETNX will not perform any operation at alleven if just a single key already exists.</blockquote>
|
||||
<blockquote>Because of this semantic MSETNX can be used in order to set different keysrepresenting different fields of an unique logic object in a way thatensures that either all the fields or none at all are set.</blockquote>
|
||||
<blockquote>Both MSET and MSETNX are atomic operations. This means that for instanceif the keys A and B are modified, another client talking to Redis can eithersee the changes to both A and B at once, or no modification at all.</blockquote>
|
||||
<h2><a name="MSET Return value">MSET Return value</a></h2><a href="ReplyTypes.html">Status code reply</a> Basically +OK as MSET can't fail<h2><a name="MSETNX Return value">MSETNX Return value</a></h2><a href="ReplyTypes.html">Integer reply</a>, specifically:<br/><br/><pre class="codeblock python" name="code">
|
||||
1 if the all the keys were set
|
||||
0 if no key was set (at least one key already existed)
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>MultiExecCommand: Contents</b><br> <a href="#WATCH key1 key2 ... keyN (Redis >">WATCH key1 key2 ... keyN (Redis ></a><br> <a href="#UNWATCH">UNWATCH</a><br> <a href="#MULTI">MULTI</a><br> <a href="#COMMAND_1 ...">COMMAND_1 ...</a><br> <a href="#COMMAND_2 ...">COMMAND_2 ...</a><br> <a href="#COMMAND_N ...">COMMAND_N ...</a><br> <a href="#EXEC or DISCARD">EXEC or DISCARD</a><br> <a href="#Usage">Usage</a><br> <a href="#The DISCARD command">The DISCARD command</a><br> <a href="#Check and Set (CAS) transactions using WATCH">Check and Set (CAS) transactions using WATCH</a><br> <a href="#WATCH explained">WATCH explained</a><br> <a href="#WATCH used to implement ZPOP">WATCH used to implement ZPOP</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">MultiExecCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="WATCH key1 key2 ... keyN (Redis >">WATCH key1 key2 ... keyN (Redis ></a></h1> 2.1.0)=
|
||||
<h1><a name="UNWATCH">UNWATCH</a></h1>
|
||||
<h1><a name="MULTI">MULTI</a></h1>
|
||||
<h1><a name="COMMAND_1 ...">COMMAND_1 ...</a></h1>
|
||||
<h1><a name="COMMAND_2 ...">COMMAND_2 ...</a></h1>
|
||||
<h1><a name="COMMAND_N ...">COMMAND_N ...</a></h1>
|
||||
<h1><a name="EXEC or DISCARD">EXEC or DISCARD</a></h1>MULTI, EXEC, DISCARD and WATCH commands are the foundation of Redis Transactions.
|
||||
A Redis Transaction allows the execution of a group of Redis commands in a single
|
||||
step, with two important guarantees:<br/><br/><ul><li> All the commands in a transaction are serialized and executed sequentially. It can never happen that a request issued by another client is served <b>in the middle</b> of the execution of a Redis transaction. This guarantees that the commands are executed as a single atomic operation.</li><li> Either all of the commands or none are processed. The EXEC command triggers the execution of all the commands in the transaction, so if a client loses the connection to the server in the context of a transaction before calling the MULTI command none of the operations are performed, instead if the EXEC command is called, all the operations are performed. An exception to this rule is when the Append Only File is enabled: every command that is part of a Redis transaction will log in the AOF as long as the operation is completed, so if the Redis server crashes or is killed by the system administrator in some hard way it is possible that only a partial number of operations are registered.</li></ul>
|
||||
Since Redis 2.1.0, it's also possible to add a further guarantee to the above two, in the form of optimistic locking of a set of keys in a way very similar to a CAS (check and set) operation. This is documented later in this manual page.<h2><a name="Usage">Usage</a></h2>A Redis transaction is entered using the MULTI command. The command always
|
||||
replies with OK. At this point the user can issue multiple commands. Instead
|
||||
of executing these commands, Redis will "queue" them. All the commands are
|
||||
executed once EXEC is called.<br/><br/>Calling DISCARD instead will flush the transaction queue and will exit
|
||||
the transaction.<br/><br/>The following is an example using the Ruby client:
|
||||
<pre class="codeblock python" name="code">
|
||||
?> r.multi
|
||||
=> "OK"
|
||||
>> r.incr "foo"
|
||||
=> "QUEUED"
|
||||
>> r.incr "bar"
|
||||
=> "QUEUED"
|
||||
>> r.incr "bar"
|
||||
=> "QUEUED"
|
||||
>> r.exec
|
||||
=> [1, 1, 2]
|
||||
</pre>
|
||||
As it is possible to see from the session above, MULTI returns an "array" of
|
||||
replies, where every element is the reply of a single command in the
|
||||
transaction, in the same order the commands were queued.<br/><br/>When a Redis connection is in the context of a MULTI request, all the commands
|
||||
will reply with a simple string "QUEUED" if they are correct from the
|
||||
point of view of the syntax and arity (number of arguments) of the commaand.
|
||||
Some commands are still allowed to fail during execution time.<br/><br/>This is more clear on the protocol level; In the following example one command
|
||||
will fail when executed even if the syntax is right:
|
||||
<pre class="codeblock python python" name="code">
|
||||
Trying 127.0.0.1...
|
||||
Connected to localhost.
|
||||
Escape character is '^]'.
|
||||
MULTI
|
||||
+OK
|
||||
SET a 3
|
||||
abc
|
||||
+QUEUED
|
||||
LPOP a
|
||||
+QUEUED
|
||||
EXEC
|
||||
*2
|
||||
+OK
|
||||
-ERR Operation against a key holding the wrong kind of value
|
||||
</pre>
|
||||
MULTI returned a two elements bulk reply where one is an +OK
|
||||
code and one is a -ERR reply. It's up to the client lib to find a sensible
|
||||
way to provide the error to the user.<br/><br/><blockquote>IMPORTANT: even when a command will raise an error, all the other commandsin the queue will be processed. Redis will NOT stop the processing ofcommands once an error is found.</blockquote>
|
||||
Another example, again using the write protocol with telnet, shows how
|
||||
syntax errors are reported ASAP instead:
|
||||
<pre class="codeblock python python python" name="code">
|
||||
MULTI
|
||||
+OK
|
||||
INCR a b c
|
||||
-ERR wrong number of arguments for 'incr' command
|
||||
</pre>
|
||||
This time due to the syntax error the "bad" INCR command is not queued
|
||||
at all.<h2><a name="The DISCARD command">The DISCARD command</a></h2>DISCARD can be used in order to abort a transaction. No command will be executed, and the state of the client is again the normal one, outside of a transaction. Example using the Ruby client:
|
||||
<pre class="codeblock python python python python" name="code">
|
||||
?> r.set("foo",1)
|
||||
=> true
|
||||
>> r.multi
|
||||
=> "OK"
|
||||
>> r.incr("foo")
|
||||
=> "QUEUED"
|
||||
>> r.discard
|
||||
=> "OK"
|
||||
>> r.get("foo")
|
||||
=> "1"
|
||||
</pre><h2><a name="Check and Set (CAS) transactions using WATCH">Check and Set (CAS) transactions using WATCH</a></h2>WATCH is used in order to provide a CAS (Check and Set) behavior to
|
||||
Redis Transactions.<br/><br/>WATCHed keys are monitored in order to detect changes against this keys.
|
||||
If at least a watched key will be modified before the EXEC call, the
|
||||
whole transaction will abort, and EXEC will return a nil object
|
||||
(A Null Multi Bulk reply) to notify that the transaction failed.<br/><br/>For example imagine we have the need to atomically increment the value
|
||||
of a key by 1 (I know we have INCR, let's suppose we don't have it).<br/><br/>The first try may be the following:
|
||||
<pre class="codeblock python python python python python" name="code">
|
||||
val = GET mykey
|
||||
val = val + 1
|
||||
SET mykey $val
|
||||
</pre>
|
||||
This will work reliably only if we have a single client performing the operation in a given time.
|
||||
If multiple clients will try to increment the key about at the same time
|
||||
there will be a race condition. For instance client A and B will read the
|
||||
old value, for instance, 10. The value will be incremented to 11 by both
|
||||
the clients, and finally SET as the value of the key. So the final value
|
||||
will be "11" instead of "12".<br/><br/>Thanks to WATCH we are able to model the problem very well:
|
||||
<pre class="codeblock python python python python python python" name="code">
|
||||
WATCH mykey
|
||||
val = GET mykey
|
||||
val = val + 1
|
||||
MULTI
|
||||
SET mykey $val
|
||||
EXEC
|
||||
</pre>
|
||||
Using the above code, if there are race conditions and another client
|
||||
modified the result of <i>val</i> in the time between our call to WATCH and
|
||||
our call to EXEC, the transaction will fail.<br/><br/>We'll have just to re-iterate the operation hoping this time we'll not get
|
||||
a new race. This form of locking is called <b>optimistic locking</b> and is
|
||||
a very powerful form of locking as in many problems there are multiple
|
||||
clients accessing a much bigger number of keys, so it's very unlikely that
|
||||
there are collisions: usually operations don't need to be performed
|
||||
multiple times.<h2><a name="WATCH explained">WATCH explained</a></h2>So what is WATCH really about? It is a command that will make the EXEC
|
||||
conditional: we are asking Redis to perform the transaction only if no
|
||||
other client modified any of the WATCHed keys. Otherwise the transaction is not
|
||||
entered at all. (Note that if you WATCH a volatile key and Redis expires the key after you WATCHed it, EXEC will still work. <a href="http://code.google.com/p/redis/issues/detail?id=270" target="_blank">More</a>.)<br/><br/>WATCH can be called multiple times. Simply all the WATCH calls will
|
||||
have the effects to watch for changes starting from the call, up to the
|
||||
moment EXEC is called.<br/><br/>When EXEC is called, either if it will fail or succeed, all keys are
|
||||
UNWATCHed. Also when a client connection is closed, everything gets
|
||||
UNWATCHed.<br/><br/>It is also possible to use the UNWATCH command (without arguments) in order
|
||||
to flush all the watched keys. Sometimes this is useful as we
|
||||
optimistically lock a few keys, since possibly we need to perform a transaction
|
||||
to alter those keys, but after reading the current content of the keys
|
||||
we don't want to proceed. When this happens we just call UNWATCH so that
|
||||
the connection can already be used freely for new transactions.<h2><a name="WATCH used to implement ZPOP">WATCH used to implement ZPOP</a></h2>A good example to illustrate how WATCH can be used to create new atomic
|
||||
operations otherwise not supported by Redis is to implement ZPOP, that is
|
||||
a command that pops the element with the lower score from a sorted set
|
||||
in an atomic way. This is the simplest implementation:
|
||||
<pre class="codeblock python python python python python python python" name="code">
|
||||
WATCH zset
|
||||
ele = ZRANGE zset 0 0
|
||||
MULTI
|
||||
ZREM zset ele
|
||||
EXEC
|
||||
</pre>
|
||||
If EXEC fails (returns a nil value) we just re-iterate the operation.<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Multi bulk reply</a>, specifically:<br/><br/><pre class="codeblock python python python python python python python python" name="code">
|
||||
The result of a MULTI/EXEC command is a multi bulk reply where every element is the return value of every command in the atomic transaction.
|
||||
</pre>If a MULTI/EXEC transaction is aborted because of WATCH detected modified keys, a <a href="ReplyTypes.html">Null Multi Bulk reply</a> is returned.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>NonexistentCommands: Contents</b><br> <a href="#HGETSET">HGETSET</a><br> <a href="#SET with expire">SET with expire</a><br> <a href="#ZADDNX">ZADDNX</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">NonexistentCommands</h1>
|
||||
|
||||
<div class="summary">
|
||||
A list of commands that don't exist in Redis, but can be accomplished in a different way.
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
This is a list of commands that don't exist in Redis, but can be accomplished in a different way, usually by means of <a href="MultiExecCommand.html">WATCH/MULTI/EXEC</a>.<br/><br/>For better performance, you can pipeline multiple commands.<h1><a name="HGETSET">HGETSET</a></h1><a href="GetsetCommand.html">GETSET</a> for Hashes.<br/><br/><pre class="codeblock python" name="code">
|
||||
WATCH foo
|
||||
old_value = HGET foo field
|
||||
MULTI
|
||||
HSET foo field new_value
|
||||
EXEC
|
||||
</pre><h1><a name="SET with expire">SET with expire</a></h1>See <a href="SetexCommand.html">SETEX</a>.<h1><a name="ZADDNX">ZADDNX</a></h1>Add an element to a sorted set, only if the element doesn't already exist (by default, <a href="ZaddCommand.html">ZADD</a> would update the element's score if it already exists). <a href="http://groups.google.com/group/redis-db/browse_thread/thread/fc4c79d72e5bd346/6cdc07ecc36b81e7" target="_blank">See thread</a>.<br/><br/><pre class="codeblock python python" name="code">
|
||||
WATCH foo
|
||||
score = ZSCORE foo bar
|
||||
IF score != NIL
|
||||
MULTI
|
||||
ZADD foo 1 bar
|
||||
EXEC
|
||||
ENDIF
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ObjectHashMappers: Contents</b><br> <a href="#Object Hash Mappers">Object Hash Mappers</a><br> <a href="#Ruby">Ruby</a><br> <a href="#Ohm">Ohm</a><br> <a href="#dm-redis-adapter">dm-redis-adapter</a><br> <a href="#redis-models">redis-models</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ObjectHashMappers</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="Object Hash Mappers">Object Hash Mappers</a></h1>Looking for a higher level if abstraction for your Objects, their Properties and Relationships?<br/><br/>There is not need to stick to the <a href="SupportedLanguages.html">client libraries</a> exposing the raw features of Redis, here you will find a list of <b>Object Hash Mappers</b>, working in the same fashion a ORM does.<h2><a name="Ruby">Ruby</a></h2><h3><a name="Ohm">Ohm</a></h3><ul><li> Object-hash mapping library for Redis. It includes an extensible list of validations and has very good performance.</li><li> Authors: <a href="http://soveran.com/" target="_blank">Michel Martens</a>, <a href="http://twitter.com/soveran" target="_blank">@soveran</a>; and Damian Janowski <a href="http://twitter.com/djanowski" target="_blank">@djanowski</a>.</li><li> Repository: <a href="http://github.com/soveran/ohm" target="_blank">http://github.com/soveran/ohm</a></li><li> Group: <a href="http://groups.google.com/group/ohm-ruby" target="_blank">http://groups.google.com/group/ohm-ruby</a></li></ul>
|
||||
<h3><a name="dm-redis-adapter">dm-redis-adapter</a></h3><ul><li> This is a DataMapper (ORM that is based on the IdentityMap pattern) adapter for the Redis key-value database.</li><li> Author: <a href="http://whoahbot.com/" target="_blank">Whoahbot</a>, <a href="http://twitter.com/whoahbot" target="_blank">@whoahbot</a>.</li><li> Repository: <a href="http://github.com/whoahbot/dm-redis-adapter/" target="_blank">http://github.com/whoahbot/dm-redis-adapter/</a></li></ul>
|
||||
<h3><a name="redis-models">redis-models</a></h3><ul><li> Minimal model support for Redis. Directly maps Ruby properties to model_name:id:field_name keys in redis. Scalar, List and Set properties are supported. Values can be marshaled to/from Integer, Float, DateTime, JSON. </li><li> Repository: <a href="http://github.com/voloko/redis-model" target="_blank">http://github.com/voloko/redis-model</a></li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Pipelining: Contents</b><br> <a href="#Pipelining (DRAFT)">Pipelining (DRAFT)</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Pipelining</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
<h1><a name="Pipelining (DRAFT)">Pipelining (DRAFT)</a></h1>A client library can use the same connection in order to issue multiple commands. But Redis supports <b>pipelining</b>, so multiple commands can be sent to the server with a single write operation by the client, without need to read the server reply in order to issue the next command. All the replies can be read at the end.<br/><br/>Usually Redis server and client will have a very fast link so this is not very important to support this feature in a client implementation, still if an application needs to issue a very large number of commands in s short time, using pipelining can be much faster.<br/><br/>Please read the <a href="ProtocolSpecification.html">ProtocolSpecification</a> if you want to learn more about the way Redis <a href="SupportedLanguages.html">clients</a> and the server communicate.<br/><br/>Pipelining is one of the <a href="Speed.html">Speed</a> <a href="Features.html">Features</a> of Redis, you can also check the support for <a href="MultiBulkCommands.html">send and receive multiple values in a single command</a>.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ProgrammingExamples: Contents</b><br> <a href="#Programming Examples (DRAFT)">Programming Examples (DRAFT)</a><br> <a href="#TODO">TODO</a><br> <a href="#Java">Java</a><br> <a href="#Twayis">Twayis</a><br> <a href="#PHP">PHP</a><br> <a href="#Retwis">Retwis</a><br> <a href="#Ruby">Ruby</a><br> <a href="#twatcher-lite">twatcher-lite</a><br> <a href="#Resque">Resque</a><br> <a href="#Retwis-rb">Retwis-rb</a><br> <a href="#scanty-redis">scanty-redis</a><br> <a href="#Note Taking">Note Taking</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ProgrammingExamples</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
|
||||
<h1><a name="Programming Examples (DRAFT)">Programming Examples (DRAFT)</a></h1><h2><a name="TODO">TODO</a></h2><ul><li> Add <a href="http://github.com/jodosha/redis-store" target="_blank">http://github.com/jodosha/redis-store</a></li></ul>
|
||||
Nothing speaks better than code examples, here you are:<h2><a name="Java">Java</a></h2><h3><a name="Twayis">Twayis</a></h3> <br/><br/>A Java clone of <b>Retwis</b> showcase integration between the <a href="http://www.playframework.org/" target="_blank">Play! framework</a> and Redis <a href="http://code.google.com/p/twayis/" target="_blank">Google Code Project Page</a><h2><a name="PHP">PHP</a></h2><h3><a name="Retwis">Retwis</a></h3>A PHP Twitter clone, the original example of Redis capabilities. With a <a href="http://retwis.antirez.com/" target="_blank">live demo</a>, and an <a href="http://code.google.com/p/redis/wiki/TwitterAlikeExample" target="_blank">article explaining it design</a>. You can find the code in the Downloads tab.<h2><a name="Ruby">Ruby</a></h2><h3><a name="twatcher-lite">twatcher-lite</a></h3>A simplied version of the application running <a href="http://twatcher.com/" target="_blank">http://twatcher.com/</a> from Mirko Froehlich (<a href="http://twitter.com/digitalhobbit" target="_blank">@digitalhobbit</a>) with a full blog post explaining its development at <a href="http://www.digitalhobbit.com/2009/11/08/building-a-twitter-filter-with-sinatra-redis-and-tweetstream/" target="_blank"> Building a Twitter Filter With Sinatra, Redis, and TweetStream</a><h3><a name="Resque">Resque</a></h3>The "simple" Redis-based queue behind Github background jobs, that replaced SQS, Starling, ActiveMessaging, BackgroundJob, DelayedJob, and Beanstalkd. Developed by Chris Wanstrath (<a href="http://twitter.com/defunkt" target="_blank">@defunkt</a>) the code is at <a href="http://github.com/defunkt/resque" target="_blank">http://github.com/defunkt/resque</a>, be sure to read <a href="http://github.com/blog/542-introducing-resque" target="_blank">the introduction</a><h3><a name="Retwis-rb">Retwis-rb</a></h3>A port of <b>Retwis</b> to Ruby and <a href="http://www.sinatrarb.com/" target="_blank">Sinatra</a> written by Daniel Lucraft (<a href="http://twitter.com/DanLucraft" target="_blank">@DanLucraft</a>) Full source code is available at <a href="http://github.com/danlucraft/retwis-rb" target="_blank">http://github.com/danlucraft/retwis-rb</a><h3><a name="scanty-redis">scanty-redis</a></h3>Scanty is <i>minimal</i> blogging software developed by Adam Wiggins (<a href="http://twitter.com/hirodusk" target="_blank">@hirodusk</a>) It is not a blogging engine, but it’s small and easy to modify, so it could be the starting point for your blog. <a href="http://github.com/adamwiggins/scanty-redis" target="_blank">This fork</a> is modified to use Redis, a full featured key-value database, instead of SQL. <h3><a name="Note Taking">Note Taking</a></h3>A <i>very simple</i> note taking example of Ruby and Redis application using <a href="http://www.sinatrarb.com/" target="_blank">Sinatra</a>. Developed by Pieter Noordhuis <a href="http://twitter.com/pnoordhuis" target="_blank">@pnoordhuis</a>, you can check the code at <a href="http://gist.github.com/86714" target="_blank">http://gist.github.com/86714</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>ProtocolSpecification: Contents</b><br> <a href="#Networking layer">Networking layer</a><br> <a href="#Requests">Requests</a><br> <a href="#The new unified request protocol">The new unified request protocol</a><br> <a href="#Replies">Replies</a><br> <a href="#Single line reply">Single line reply</a><br> <a href="#Error reply">Error reply</a><br> <a href="#Integer reply">Integer reply</a><br> <a href="#Bulk replies">Bulk replies</a><br> <a href="#Multi-Bulk replies">Multi-Bulk replies</a><br> <a href="#Nil elements in Multi-Bulk replies">Nil elements in Multi-Bulk replies</a><br> <a href="#Multiple commands and pipelining">Multiple commands and pipelining</a><br> <a href="#The old protocol for sending commands">The old protocol for sending commands</a><br> <a href="#Inline Commands">Inline Commands</a><br> <a href="#Bulk commands">Bulk commands</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">ProtocolSpecification</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
= Protocol Specification =<br/><br/>The Redis protocol is a compromise between the following things:<br/><br/><ul><li> Simple to implement.</li><li> Fast to parse by a computer.</li><li> Easy enough to parse by a human.</li></ul>
|
||||
<h2><a name="Networking layer">Networking layer</a></h2>A client connects to a Redis server creating a TCP connection to the port 6379.
|
||||
Every Redis command or data transmitted by the client and the server is
|
||||
terminated by "\r\n" (CRLF).<h1><a name="Requests">Requests</a></h1>Redis accepts commands composed of different arguments.
|
||||
Once a command is received, it is processed and a reply is sent back to the client.<h2><a name="The new unified request protocol">The new unified request protocol</a></h2>The new unified protocol was introduced in Redis 1.2, but it became the standard way for talking with the Redis server in Redis 2.0.<br/><br/>In the unified protocol all the arguments sent to the Redis server are binary safe. This is the general form:<br/><br/><pre class="codeblock python" name="code">
|
||||
*<number of arguments> CR LF
|
||||
$<number of bytes of argument 1> CR LF
|
||||
<argument data> CR LF
|
||||
...
|
||||
$<number of bytes of argument N> CR LF
|
||||
<argument data> CR LF
|
||||
</pre>See the following example:<br/><br/><pre class="codeblock python python" name="code">
|
||||
*3
|
||||
$3
|
||||
SET
|
||||
$5
|
||||
mykey
|
||||
$7
|
||||
myvalue
|
||||
</pre>This is how the above command looks as a quoted string, so that it is possible to see the exact value of every byte in the query:<br/><br/><pre class="codeblock python python python" name="code">
|
||||
"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$8\r\nmyvalue\r\n"
|
||||
</pre>As you will see in a moment this format is also used in Redis replies.
|
||||
The format used for every argument "$6\r\nmydata\r\n" is called a Bulk Reply.
|
||||
While the actual unified request protocol is what Redis uses to return list of items, and is called a Multi Bulk Reply. It is just the sum of N different
|
||||
Bulk Replies prefixed by a <code name="code" class="python">*<argc>\r\n</code> string where <code name="code" class="python"><argc></code> is the number of arguments (Bulk Replies) that will follow.<h1><a name="Replies">Replies</a></h1>Redis will reply to commands with different kinds of replies. It is possible to check the kind of reply from the first byte sent by the server:<br/><br/><ul><li> With a single line reply the first byte of the reply will be "+"</li><li> With an error message the first byte of the reply will be "-"</li><li> With an integer number the first byte of the reply will be ":"</li><li> With bulk reply the first byte of the reply will be "$"</li><li> With multi-bulk reply the first byte of the reply will be "<code name="code" class="python">*</code>"</li></ul>
|
||||
<h2><a name="Single line reply">Single line reply</a></h2>A single line reply is in the form of a single line string
|
||||
starting with "+" terminated by "\r\n". For example:<br/><br/><pre class="codeblock python python python python" name="code">
|
||||
+OK
|
||||
</pre>The client library should return everything after the "+", that is, the string "OK" in the example.<br/><br/>The following commands reply with a single line reply:
|
||||
PING, SET, SELECT, SAVE, BGSAVE, SHUTDOWN, RENAME, LPUSH, RPUSH, LSET, LTRIM<h2><a name="Error reply">Error reply</a></h2>Errors are sent exactly like Single Line Replies. The only difference is that the first byte is "-" instead of "+".<br/><br/>Error replies are only sent when something strange happened, for instance if you try to perform an operation against the wrong data type, or if the command does not exist and so forth. So an exception should be raised by the library client when an Error Reply is received.<h2><a name="Integer reply">Integer reply</a></h2>This type of reply is just a CRLF terminated string representing an integer, prefixed by a ":" byte. For example ":0\r\n", or ":1000\r\n" are integer replies.<br/><br/>With commands like INCR or LASTSAVE using the integer reply to actually return a value there is no special meaning for the returned integer. It is just an incremental number for INCR, a UNIX time for LASTSAVE and so on.<br/><br/>Some commands like EXISTS will return 1 for true and 0 for false.<br/><br/>Other commands like SADD, SREM and SETNX will return 1 if the operation was actually done, 0 otherwise.<br/><br/>The following commands will reply with an integer reply: SETNX, DEL, EXISTS, INCR, INCRBY, DECR, DECRBY, DBSIZE, LASTSAVE, RENAMENX, MOVE, LLEN, SADD, SREM, SISMEMBER, SCARD<h2><a name="Bulk replies">Bulk replies</a></h2>Bulk replies are used by the server in order to return a single binary safe string.<br/><br/><pre class="codeblock python python python python python" name="code">
|
||||
C: GET mykey
|
||||
S: $6
|
||||
S: foobar
|
||||
</pre>The server sends as the first line a "$" byte followed by the number of bytes
|
||||
of the actual reply, followed by CRLF, then the actual data bytes are sent,
|
||||
followed by additional two bytes for the final CRLF.
|
||||
The exact sequence sent by the server is:<br/><br/><pre class="codeblock python python python python python python" name="code">
|
||||
"$6\r\nfoobar\r\n"
|
||||
</pre>If the requested value does not exist the bulk reply will use the special
|
||||
value -1 as data length, example:<br/><br/><pre class="codeblock python python python python python python python" name="code">
|
||||
C: GET nonexistingkey
|
||||
S: $-1
|
||||
</pre>The client library API should not return an empty string, but a nil object, when the requested object does not exist.
|
||||
For example a Ruby library should return 'nil' while a C library should return
|
||||
NULL (or set a special flag in the reply object), and so forth.<h2><a name="Multi-Bulk replies">Multi-Bulk replies</a></h2>Commands like LRANGE need to return multiple values (every element
|
||||
of the list is a value, and LRANGE needs to return more than a single element). This is accomplished using multiple bulk writes,
|
||||
prefixed by an initial line indicating how many bulk writes will follow.
|
||||
The first byte of a multi bulk reply is always <code name="code" class="python">*</code>. Example:<br/><br/><pre class="codeblock python python python python python python python python" name="code">
|
||||
C: LRANGE mylist 0 3
|
||||
S: *4
|
||||
S: $3
|
||||
S: foo
|
||||
S: $3
|
||||
S: bar
|
||||
S: $5
|
||||
S: Hello
|
||||
S: $5
|
||||
S: World
|
||||
</pre>As you can see the multi bulk reply is exactly the same format used in order
|
||||
to send commands to the Redis server unsing the unified protocol.<br/><br/>The first line the server sent is "<b>4\r\n" in order to specify that four bulk
|
||||
replies will follow. Then every bulk write is transmitted.<br/><br/>If the specified key does not exist, instead of the number of elements in the
|
||||
list the special value -1 is sent as count. Example:<br/><br/><pre class="codeblock python python python python python python python python python" name="code">
|
||||
C: LRANGE nokey 0 1
|
||||
S: *-1
|
||||
</pre>A client library API SHOULD return a nil object and not an empty list when this
|
||||
happens. This makes possible to distinguish between empty list and other error conditions (for instance a timeout condition in the BLPOP command).<h2><a name="Nil elements in Multi-Bulk replies">Nil elements in Multi-Bulk replies</a></h2>Single elements of a multi bulk reply may have -1 length, in order to signal that this elements are missing and not empty strings. This can happen with the SORT command when used with the GET <i>pattern</i> option when the specified key is missing. Example of a multi bulk reply containing an empty element:<br/><br/><pre class="codeblock python python python python python python python python python python" name="code">
|
||||
S: *3
|
||||
S: $3
|
||||
S: foo
|
||||
S: $-1
|
||||
S: $3
|
||||
S: bar
|
||||
</pre>The second element is nul. The client library should return something like this:<br/><br/><pre class="codeblock python python python python python python python python python python python" name="code">
|
||||
["foo",nil,"bar"]
|
||||
</pre><h2><a name="Multiple commands and pipelining">Multiple commands and pipelining</a></h2>A client can use the same connection in order to issue multiple commands.
|
||||
Pipelining is supported so multiple commands can be sent with a single
|
||||
write operation by the client, it is not needed to read the server reply
|
||||
in order to issue the next command. All the replies can be read at the end.<br/><br/>Usually Redis server and client will have a very fast link so this is not
|
||||
very important to support this feature in a client implementation, still
|
||||
if an application needs to issue a very large number of commands in short
|
||||
time to use pipelining can be much faster.<h1><a name="The old protocol for sending commands">The old protocol for sending commands</a></h1>Before of the Unified Request Protocol Redis used a different protocol to send
|
||||
commands, that is still supported since it is simpler to type by hand via telnet. In this protocol there are two kind of commands:<br/><br/><blockquote>* Inline commands: simple commands where argumnets are just space separated strings. No binary safeness is possible.* Bulk commands: bulk commands are exactly like inline commands, but the last argument is handled in a special way in order to allow for a binary-safe last argument.</blockquote>
|
||||
<h2><a name="Inline Commands">Inline Commands</a></h2>The simplest way to send Redis a command is via </b>Inline Commands.
|
||||
The following is an example of a server/client chat using an inline command (the server chat starts with S:, the client chat with C:)<br/><br/><pre class="codeblock python python python python python python python python python python python python" name="code">
|
||||
C: PING
|
||||
S: +PONG
|
||||
</pre>The following is another example of an INLINE command returning an integer:<br/><br/><pre class="codeblock python python python python python python python python python python python python python" name="code">
|
||||
C: EXISTS somekey
|
||||
S: :0
|
||||
</pre>Since 'somekey' does not exist the server returned ':0'.<br/><br/>Note that the EXISTS command takes one argument. Arguments are separated
|
||||
by spaces.<h2><a name="Bulk commands">Bulk commands</a></h2>Some commands when sent as inline commands require a special form in order
|
||||
to support a binary safe last argument. This commands will use the last argument
|
||||
for a "byte count", then the bulk data is sent (that can be binary safe since
|
||||
the server knows how many bytes to read).<br/><br/>See for instance the following example:<br/><br/><pre class="codeblock python python python python python python python python python python python python python python" name="code">
|
||||
C: SET mykey 6
|
||||
C: foobar
|
||||
S: +OK
|
||||
</pre>The last argument of the commnad is '6'. This specify the number of DATA
|
||||
bytes that will follow, that is, the string "foobar". Note that even this bytes are terminated by two additional bytes of CRLF.<br/><br/>All the bulk commands are in this exact form: instead of the last argument
|
||||
the number of bytes that will follow is specified, followed by the bytes
|
||||
composing the argument itself, and CRLF. In order to be more clear for the programmer this is the string sent by the client in the above sample:<br/><br/><blockquote>"SET mykey 6\r\nfoobar\r\n"</blockquote>
|
||||
Redis has an internal list of what command is inline and what command is bulk, so you have to send this commands accordingly. It is strongly suggested to use the new Unified Request Protocol instead.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>PublishSubscribe: Contents</b><br> <a href="#UNSUBSCRIBE channel_1 channel_2 ... channel_N">UNSUBSCRIBE channel_1 channel_2 ... channel_N</a><br> <a href="#UNSUBSCRIBE (unsubscribe from all channels)">UNSUBSCRIBE (unsubscribe from all channels)</a><br> <a href="#PSUBSCRIBE pattern_1 pattern_2 ... pattern_N">PSUBSCRIBE pattern_1 pattern_2 ... pattern_N</a><br> <a href="#PUNSUBSCRIBE pattern_1 pattern_2 ... pattern_N">PUNSUBSCRIBE pattern_1 pattern_2 ... pattern_N</a><br> <a href="#PUNSUBSCRIBE (unsubscribe from all patterns)">PUNSUBSCRIBE (unsubscribe from all patterns)</a><br> <a href="#PUBLISH channel message">PUBLISH channel message</a><br> <a href="#Format of pushed messages">Format of pushed messages</a><br> <a href="#Unsubscribing from all the channels at once">Unsubscribing from all the channels at once</a><br> <a href="#Wire protocol example">Wire protocol example</a><br> <a href="#PSUBSCRIBE and PUNSUBSCRIBE: pattern matching subscriptions">PSUBSCRIBE and PUNSUBSCRIBE: pattern matching subscriptions</a><br> <a href="#Messages matching both a pattern and a channel subscription">Messages matching both a pattern and a channel subscription</a><br> <a href="#The meaning of the count of subscriptions with pattern matching">The meaning of the count of subscriptions with pattern matching</a><br> <a href="#More details on the PUBLISH command">More details on the PUBLISH command</a><br> <a href="#Programming Example">Programming Example</a><br> <a href="#Client library implementations hints">Client library implementations hints</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">PublishSubscribe</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
=SUBSCRIBE channel_1 channel_2 ... channel_N=
|
||||
<h1><a name="UNSUBSCRIBE channel_1 channel_2 ... channel_N">UNSUBSCRIBE channel_1 channel_2 ... channel_N</a></h1>
|
||||
<h1><a name="UNSUBSCRIBE (unsubscribe from all channels)">UNSUBSCRIBE (unsubscribe from all channels)</a></h1>
|
||||
<h1><a name="PSUBSCRIBE pattern_1 pattern_2 ... pattern_N">PSUBSCRIBE pattern_1 pattern_2 ... pattern_N</a></h1>
|
||||
<h1><a name="PUNSUBSCRIBE pattern_1 pattern_2 ... pattern_N">PUNSUBSCRIBE pattern_1 pattern_2 ... pattern_N</a></h1>
|
||||
<h1><a name="PUNSUBSCRIBE (unsubscribe from all patterns)">PUNSUBSCRIBE (unsubscribe from all patterns)</a></h1>
|
||||
<h1><a name="PUBLISH channel message">PUBLISH channel message</a></h1>Time complexity: subscribe is O(1), unsubscribe is O(N) where N is the number of clients already subscribed to a channel, publish is O(N+M) where N is the number of clients subscribed to the receiving channel, and M is the total number of subscribed patterns (by any client). Psubscribe is O(N) where N is the number of patterns the Psubscribing client is already subscribed to. Punsubscribe is O(N+M) where N is the number of patterns the Punsubscribing client is already subscribed and M is the number of total patterns subscribed in the system (by any client).<br/><br/><b>Note</b>: this commands are available starting form Redis 2.0.0<br/><br/><blockquote>SUBSCRIBE, UNSUBSCRIBE and PUBLISH commands implement the<a href="http://en.wikipedia.org/wiki/Publish/subscribe" target="_blank">Publish/Subscribe messaging paradigm</a> where (citing Wikipedia) senders (publishers) are not programmed to send their messages to specific receivers (subscribers). Rather, published messages are characterized into channels, without knowledge of what (if any) subscribers there may be. Subscribers express interest in one or more channels, and only receive messages that are of interest, without knowledge of what (if any) publishers there are. This decoupling of publishers and subscribers can allow for greater scalability and a more dynamic network topology.</blockquote>
|
||||
<blockquote>For instance in order to subscribe to the channels foo and bar the clientwill issue the SUBSCRIBE command followed by the names of the channels.</blockquote><pre class="codeblock python" name="code">
|
||||
SUBSCRIBE foo bar
|
||||
</pre>
|
||||
<blockquote>All the messages sent by other clients to this channels will be pushed bythe Redis server to all the subscribed clients, in the form of a threeelements bulk reply, where the first element is the message type, thesecond the originating channel, and the third argument the message payload.</blockquote>
|
||||
<blockquote>A client subscribed to 1 or more channels should NOT issue other commandsother than SUBSCRIBE and UNSUBSCRIBE, but can subscribe or unsubscribeto other channels dynamically.</blockquote>
|
||||
<blockquote>The reply of the SUBSCRIBE and UNSUBSCRIBE operations are sent in the formof messages, so that the client can just read a coherent stream of messageswhere the first element indicates the kind of message.</blockquote><h2><a name="Format of pushed messages">Format of pushed messages</a></h2>
|
||||
<blockquote>Messages are in the form of multi bulk replies with three elements.The first element is the kind of message:</blockquote><ul><li> "subscribe": means that we successfully subscribed to the channel given as second element of the multi bulk reply. The third argument represents the number of channels we are currently subscribed to.</li><li> "unsubscribe": means that we successfully unsubscribed from the channel given as second element of the multi bulk reply. The third argument represents the number of channels we are currently subscribed to. If this latest argument is zero, we are no longer subscribed to any channel, and the client can issue any kind of Redis command as we are outside the Pub/sub state.</li><li> "message": it is a message received as result of a PUBLISH command issued by another client. The second element is the name of the originating channel, and the third the actual message payload.</li></ul><h2><a name="Unsubscribing from all the channels at once">Unsubscribing from all the channels at once</a></h2>
|
||||
If the UNSUBSCRIBE command is issued without additional arguments, it is equivalent to unsubscribing to all the channels we are currently subscribed. A message for every unsubscribed channel will be received.
|
||||
<h2><a name="Wire protocol example">Wire protocol example</a></h2>
|
||||
<pre class="codeblock python python" name="code">
|
||||
SUBSCRIBE first second
|
||||
*3
|
||||
$9
|
||||
subscribe
|
||||
$5
|
||||
first
|
||||
:1
|
||||
*3
|
||||
$9
|
||||
subscribe
|
||||
$6
|
||||
second
|
||||
:2
|
||||
</pre>
|
||||
at this point from another client we issue a PUBLISH operation against the channel named "second". This is what the first client receives:
|
||||
<pre class="codeblock python python python" name="code">
|
||||
*3
|
||||
$7
|
||||
message
|
||||
$6
|
||||
second
|
||||
$5
|
||||
Hello
|
||||
</pre>
|
||||
Now the client unsubscribes itself from all the channels using the UNSUBSCRIBE command without additional arguments:
|
||||
<pre class="codeblock python python python python" name="code">
|
||||
UNSUBSCRIBE
|
||||
*3
|
||||
$11
|
||||
unsubscribe
|
||||
$6
|
||||
second
|
||||
:1
|
||||
*3
|
||||
$11
|
||||
unsubscribe
|
||||
$5
|
||||
first
|
||||
:0
|
||||
</pre>
|
||||
<h2><a name="PSUBSCRIBE and PUNSUBSCRIBE: pattern matching subscriptions">PSUBSCRIBE and PUNSUBSCRIBE: pattern matching subscriptions</a></h2>
|
||||
Redis Pub/Sub implementation supports pattern matching. Clients may subscribe to glob style patterns in order to receive all the messages sent to channel names matching a given pattern.<br/><br/>For instance the command:
|
||||
<pre class="codeblock python python python python python" name="code">
|
||||
PSUBSCRIBE news.*
|
||||
</pre>
|
||||
Will receive all the messages sent to the channel news.art.figurative and news.music.jazz and so forth. All the glob style patterns as valid, so multiple wild cards are supported.<br/><br/>Messages received as a result of pattern matching are sent in a different format:
|
||||
<ul><li> The type of the message is "pmessage": it is a message received as result of a PUBLISH command issued by another client, matching a pattern matching subscription. The second element is the original pattern matched, the third element is the name of the originating channel, and the last element the actual message payload.</li></ul>
|
||||
Similarly to SUBSCRIBE and UNSUBSCRIBE, PSUBSCRIBE and PUNSUBSCRIBE commands are acknowledged by the system sending a message of type "psubscribe" and "punsubscribe" using the same format as the "subscribe" and "unsubscribe" message format.
|
||||
<h2><a name="Messages matching both a pattern and a channel subscription">Messages matching both a pattern and a channel subscription</a></h2>
|
||||
A client may receive a single message multiple time if it's subscribed to multiple patterns matching a published message, or it is subscribed to both patterns and channels matching the message. Like in the following example:
|
||||
<pre class="codeblock python python python python python python" name="code">
|
||||
SUBSCRIBE foo
|
||||
PSUBSCRIBE f*
|
||||
</pre>
|
||||
In the above example, if a message is sent to the <b>foo</b> channel, the client will receive two messages, one of type "message" and one of type "pmessage".
|
||||
<h2><a name="The meaning of the count of subscriptions with pattern matching">The meaning of the count of subscriptions with pattern matching</a></h2>
|
||||
In <b>subscribe</b>, <b>unsubscribe</b>, <b>psubscribe</b> and <b>punsubscribe</b> message types, the last argument is the count of subscriptions still active. This number is actually the total number of channels and patterns the client is still subscribed to. So the client will exit the Pub/Sub state only when this count will drop to zero as a result of unsubscription from all the channels and patterns.
|
||||
<h2><a name="More details on the PUBLISH command">More details on the PUBLISH command</a></h2>
|
||||
The Publish command is a bulk command where the first argument is the target class, and the second argument the data to send. It returns an Integer Reply representing the number of clients that received the message (that is, the number of clients that were listening for this class).
|
||||
<h2><a name="Programming Example">Programming Example</a></h2>
|
||||
Pieter Noordhuis provided a great example using Event-machine and Redis to create <a href="http://chat.redis-db.com" target="_blank">a multi user high performance web chat</a>, with source code included of course!
|
||||
<h2><a name="Client library implementations hints">Client library implementations hints</a></h2>
|
||||
Because all the messages received contain the original subscription causing the message delivery (the channel in the case of "message" type, and the original pattern in the case of "pmessage" type) clinet libraries may bind the original subscription to callbacks (that can be anonymous functions, blocks, function pointers, and so forth), using an hash table.<br/><br/>When a message is received an O(1) lookup can be done in order to deliver the message to the registered callback.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>QuickStart: Contents</b><br> <a href="#Quick Start">Quick Start</a><br> <a href="#Obtain the latest version">Obtain the latest version</a><br> <a href="#Compile">Compile</a><br> <a href="#Run the server">Run the server</a><br> <a href="#Play with the built in client">Play with the built in client</a><br> <a href="#Further reading">Further reading</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">QuickStart</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="RedisGuides.html">RedisGuides</a>
|
||||
<h1><a name="Quick Start">Quick Start</a></h1>This quickstart is a five minutes howto on how to get started with Redis. For more information on Redis check <a href="http://code.google.com/p/redis/wiki/index" target="_blank">Redis Documentation Index</a>.<h2><a name="Obtain the latest version">Obtain the latest version</a></h2>The latest stable source distribution of Redis can be obtained <a href="http://code.google.com/p/redis/downloads/list" target="_blank">at this location as a tarball</a>.<br/><br/><pre class="codeblock python" name="code">
|
||||
$ wget http://redis.googlecode.com/files/redis-1.02.tar.gz
|
||||
</pre>The unstable source code, with more features but not ready for production, can be downloaded using git:<br/><br/><pre class="codeblock python python" name="code">
|
||||
$ git clone git://github.com/antirez/redis.git
|
||||
</pre><h2><a name="Compile">Compile</a></h2>Redis can be compiled in most <a href="SupportedPlatforms.html">POSIX systems</a>. To compile Redis just untar the tar.gz, enter the directly and type 'make'.<br/><br/><pre class="codeblock python python python" name="code">
|
||||
$ tar xvzf redis-1.02.tar.gz
|
||||
$ cd redis-1.02
|
||||
$ make
|
||||
</pre>In order to test if the Redis server is working well in your computer make sure to run <code name="code" class="python">make test</code> and check that all the tests are passed.<h2><a name="Run the server">Run the server</a></h2>Redis can run just fine without a configuration file (when executed without a config file a standard configuration is used). To run Redis just type the following command:<br/><br/><pre class="codeblock python python python python" name="code">
|
||||
$ ./redis-server
|
||||
</pre>With the <a href="Configuration.html">default configuration</a> Redis will log to the standard output so you can check what happens. Later, you can <a href="Configuration.html">change the default settings</a>.<h2><a name="Play with the built in client">Play with the built in client</a></h2>Redis ships with a command line client that is automatically compiled when you ran <code name="code" class="python">make</code> and it is called <code name="code" class="python">redis-cli</code>For instance to set a key and read back the value use the following:<br/><br/><pre class="codeblock python python python python python" name="code">
|
||||
$ ./redis-cli set mykey somevalue
|
||||
OK
|
||||
$ ./redis-cli get mykey
|
||||
somevalue
|
||||
</pre>What about adding elements to a <a href="Lists.html">list</a>:<br/><br/><pre class="codeblock python python python python python python" name="code">
|
||||
$ ./redis-cli lpush mylist firstvalue
|
||||
OK
|
||||
$ ./redis-cli lpush mylist secondvalue
|
||||
OK
|
||||
$ ./redis-cli lpush mylist thirdvalue
|
||||
OK
|
||||
$ ./redis-cli lrange mylist 0 -1
|
||||
1. thirdvalue
|
||||
2. secondvalue
|
||||
3. firstvalue
|
||||
$ ./redis-cli rpop mylist
|
||||
firstvalue
|
||||
$ ./redis-cli lrange mylist 0 -1
|
||||
1. thirdvalue
|
||||
2. secondvalue
|
||||
</pre><h2><a name="Further reading">Further reading</a></h2><ul><li> What to play more with Redis? Read <a href="IntroductionToRedisDataTypes.html">Fifteen minutes introduction to Redis data types</a>.</li><li> Check all the <a href="Features.html">Features</a></li><li> Read the full list of available commands in the <a href="CommandReference.html">Command Reference</a>.</li><li> Start using Redis from your <a href="SupportedLanguages.html">favorite language</a>.</li><li> Take a look at some <a href="ProgrammingExamples.html">Programming Examples</a>. </li></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>QuitCommand: Contents</b><br> <a href="#Quit">Quit</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">QuitCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="ConnectionHandlingSidebar.html">ConnectionHandlingSidebar</a><h1><a name="Quit">Quit</a></h1><blockquote>Ask the server to silently close the connection.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2>None. The connection is closed as soon as the QUIT command is received.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>README: Contents</b><br> <a href="#All data in memory, but saved on disk">All data in memory, but saved on disk</a><br> <a href="#Master-Slave replication made trivial">Master-Slave replication made trivial</a><br> <a href="#It's persistent but supports expires">It's persistent but supports expires</a><br> <a href="#Beyond key-value databases">Beyond key-value databases</a><br> <a href="#Multiple databases support">Multiple databases support</a><br> <a href="#Know more about Redis!">Know more about Redis!</a><br> <a href="#Redis Tutorial">Redis Tutorial</a><br> <a href="#License">License</a><br> <a href="#Credits">Credits</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">README</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
= Introduction =<br/><br/>Redis is an extremely fast and powerful key-value store database and server implemented in ANSI C. Redis offers many different ways to do one straightforward thing: store a value ("antirez") to a key ("redis"). While the format of keys must always be simple strings, the power is with the values, which support the following data types:<br/><br/><ul><li> <a href="Strings.html">Strings</a></li><li> <a href="Lists.html">Lists</a></li><li> <a href="Sets.html">Sets</a></li><li> <a href="SortedSets.html">Sorted Sets (zsets)</a></li><li> <a href="Hashes.html">Hashes</a></li></ul>
|
||||
Each value type has an associated list of commands which can operate on them, and the <a href="CommandReference.html">The Redis Command Reference</a> contains an up to date list of these commands, organized primarily by data type. The Redis source also includes a <a href="RedisCLI.html">Redis command line interface</a> which allows you to interact directly with the server, and is the means by which this introduction will provide examples. Once you walk through the <a href="QuickStart.html">Redis Quick Start Guide</a> to get your instance of Redis running, you can follow along. <br/><br/>One of the most powerful aspects of Redis is the wide range of commands which are optimized to work with specific data value types and executed as atomic server-side operations. The <a href="Lists.html">List</a> type is a great example - Redis implements O(1) operations such as <a href="RpushCommand.html">LPUSH</a> or <a href="RpushCommand.html">RPUSH</a>, which have accompanying <a href="LpopCommand.html">LPOP</a> and <a href="LpopCommand.html">RPOP</a> methods:<br/><br/><pre class="codeblock python" name="code">
|
||||
redis> lpush programming_languages C
|
||||
OK
|
||||
redis> lpush programming_languages Ruby
|
||||
OK
|
||||
redis> rpush programming_languages Python
|
||||
OK
|
||||
redis> rpop programming_languages
|
||||
Python
|
||||
redis> lpop programming_languages
|
||||
Ruby
|
||||
</pre>More complex operations are available for each data type as well. Continuing with lists, you can get a range of elements with <a href="LrangeCommand.html">LRANGE</a> (O(start+n)) or trim the list with <a href="LtrimCommand.html">LTRIM</a> (O(n)):<br/><br/><pre class="codeblock python python" name="code">
|
||||
redis> lpush cities NYC
|
||||
OK
|
||||
redis> lpush cities SF
|
||||
OK
|
||||
redis> lpush cities Tokyo
|
||||
OK
|
||||
redis> lpush cities London
|
||||
OK
|
||||
redis> lpush cities Paris
|
||||
OK
|
||||
redis> lrange cities 0 2
|
||||
1. Paris
|
||||
2. London
|
||||
3. Tokyo
|
||||
redis> ltrim cities 0 1
|
||||
OK
|
||||
redis> lpop cities
|
||||
Paris
|
||||
redis> lpop cities
|
||||
London
|
||||
redis> lpop cities
|
||||
(nil)
|
||||
</pre>You can also add and remove elements from a set, and perform intersections, unions, and differences. <br/><br/>Redis can also be looked at as a data structures server. A Redis user is virtually provided with an interface to <a href="http://en.wikipedia.org/wiki/Abstract_data_type" target="_blank">Abstract Data Types</a>, saving them from the responsibility of implementing concrete data structures and algorithms -- indeed both algorithms and data structures in Redis are properly chosen in order to obtain the best performance.<h1><a name="All data in memory, but saved on disk">All data in memory, but saved on disk</a></h1>Redis loads and mantains the whole dataset into memory, but the dataset is persistent, since at the same time it is saved on disk, so that when the server is restarted data can be loaded back in memory.<br/><br/>There are two kinds of persistence supported: the first one is called snapshotting. In this mode Redis periodically writes to disk asynchronously. The dataset is loaded from the dump every time the server is (re)started.<br/><br/>Redis can be configured to save the dataset when a certain number of changes is reached and after a given number of seconds elapses. For example, you can configure Redis to save after 1000 changes and at most 60 seconds since the last save. You can specify any combination for these numbers.<br/><br/>Because data is written asynchronously, when a system crash occurs, the last few queries can get lost (that is acceptable in many applications but not in all). In order to make this a non issue Redis supports another, safer persistence mode, called <a href="AppendOnlyFileHowto.html">Append Only File</a>, where every command received altering the dataset (so not a read-only command, but a write command) is written on an append only file ASAP. This commands are <i>replayed</i> when the server is restarted in order to rebuild the dataset in memory.<br/><br/>Redis Append Only File supports a very handy feature: the server is able to safely rebuild the append only file in background in a non-blocking fashion when it gets too long. You can find <a href="AppendOnlyFileHowto.html">more details in the Append Only File HOWTO</a>.<h1><a name="Master-Slave replication made trivial">Master-Slave replication made trivial</a></h1>Whatever will be the persistence mode you'll use Redis supports master-slave replications if you want to stay really safe or if you need to scale to huge amounts of reads.<br/><br/><b>Redis Replication is trivial to setup</b>. So trivial that all you need to do in order to configure a Redis server to be a slave of another one, with automatic synchronization if the link will go down and so forth, is the following config line: <code name="code" class="python">slaveof 192.168.1.100 6379</code>. <a href="ReplicationHowto.html">We provide a Replication Howto</a> if you want to know more about this feature.<h1><a name="It's persistent but supports expires">It's persistent but supports expires</a></h1>Redis can be used as a <b>memcached on steroids</b> because is as fast as memcached but with a number of features more. Like memcached, Redis also supports setting timeouts to keys so that this key will be automatically removed when a given amount of time passes.<h1><a name="Beyond key-value databases">Beyond key-value databases</a></h1>All these features allow to use Redis as the sole DB for your scalable application without the need of any relational database. <a href="TwitterAlikeExample.html">We wrote a simple Twitter clone in PHP + Redis</a> to show a real world example, the link points to an article explaining the design and internals in very simple words.<h1><a name="Multiple databases support">Multiple databases support</a></h1>Redis supports multiple databases with commands to atomically move keys from one database to the other. By default DB 0 is selected for every new connection, but using the SELECT command it is possible to select a different database. The MOVE operation can move an item from one DB to another atomically. This can be used as a base for locking free algorithms together with the 'RANDOMKEY' commands.<h1><a name="Know more about Redis!">Know more about Redis!</a></h1>To really get a feeling about what Redis is and how it works please try reading <a href="IntroductionToRedisDataTypes.html">A fifteen minutes introduction to Redis data types</a>.<br/><br/>To know a bit more about how Redis works <i>internally</i> continue reading.<h1><a name="Redis Tutorial">Redis Tutorial</a></h1>(note, you can skip this section if you are only interested in "formal" doc.)<br/><br/>Later in this document you can find detailed information about Redis commands,
|
||||
the protocol specification, and so on. This kind of documentation is useful
|
||||
but... if you are new to Redis it is also BORING! The Redis protocol is designed
|
||||
so that is both pretty efficient to be parsed by computers, but simple enough
|
||||
to be used by humans just poking around with the 'telnet' command, so this
|
||||
section will show to the reader how to play a bit with Redis to get an initial
|
||||
feeling about it, and how it works.<br/><br/>To start just compile redis with 'make' and start it with './redis-server'.
|
||||
The server will start and log stuff on the standard output, if you want
|
||||
it to log more edit redis.conf, set the loglevel to debug, and restart it.<br/><br/>You can specify a configuration file as unique parameter:<br/><br/><blockquote>./redis-server /etc/redis.conf</blockquote>
|
||||
This is NOT required. The server will start even without a configuration file
|
||||
using a default built-in configuration.<br/><br/>Now let's try to set a key to a given value:<br/><br/><pre class="codeblock python python python" name="code">
|
||||
$ telnet localhost 6379
|
||||
Trying 127.0.0.1...
|
||||
Connected to localhost.
|
||||
Escape character is '^]'.
|
||||
SET foo 3
|
||||
bar
|
||||
+OK
|
||||
</pre>The first line we sent to the server is "set foo 3". This means "set the key
|
||||
foo with the following three bytes I'll send you". The following line is
|
||||
the "bar" string, that is, the three bytes. So the effect is to set the
|
||||
key "foo" to the value "bar". Very simple!<br/><br/>(note that you can send commands in lowercase and it will work anyway,
|
||||
commands are not case sensitive)<br/><br/>Note that after the first and the second line we sent to the server there
|
||||
is a newline at the end. The server expects commands terminated by "\r\n"
|
||||
and sequence of bytes terminated by "\r\n". This is a minimal overhead from
|
||||
the point of view of both the server and client but allows us to play with
|
||||
Redis with the telnet command easily.<br/><br/>The last line of the chat between server and client is "+OK". This means
|
||||
our key was added without problems. Actually SET can never fail but
|
||||
the "+OK" sent lets us know that the server received everything and
|
||||
the command was actually executed.<br/><br/>Let's try to get the key content now:<br/><br/><pre class="codeblock python python python python" name="code">
|
||||
GET foo
|
||||
$3
|
||||
bar
|
||||
</pre>Ok that's very similar to 'set', just the other way around. We sent "get foo",
|
||||
the server replied with a first line that is just the $ character follwed by
|
||||
the number of bytes the value stored at key contained, followed by the actual
|
||||
bytes. Again "\r\n" are appended both to the bytes count and the actual data. In Redis slang this is called a bulk reply.<br/><br/>What about requesting a non existing key?<br/><br/><pre class="codeblock python python python python python" name="code">
|
||||
GET blabla
|
||||
$-1
|
||||
</pre>When the key does not exist instead of the length, just the "$-1" string is sent. Since a -1 length of a bulk reply has no meaning it is used in order to specifiy a 'nil' value and distinguish it from a zero length value. Another way to check if a given key exists or not is indeed the EXISTS command:<br/><br/><pre class="codeblock python python python python python python" name="code">
|
||||
EXISTS nokey
|
||||
:0
|
||||
EXISTS foo
|
||||
:1
|
||||
</pre>As you can see the server replied ':0' the first time since 'nokey' does not
|
||||
exist, and ':1' for 'foo', a key that actually exists. Replies starting with the colon character are integer reply.<br/><br/>Ok... now you know the basics, read the <a href="CommandReference.html">REDIS COMMAND REFERENCE</a> section to
|
||||
learn all the commands supported by Redis and the <a href="ProtocolSpecification.html">PROTOCOL SPECIFICATION</a>
|
||||
section for more details about the protocol used if you plan to implement one
|
||||
for a language missing a decent client implementation.<h1><a name="License">License</a></h1>Redis is released under the BSD license. See the COPYING file for more information.<h1><a name="Credits">Credits</a></h1>Redis is written and maintained by Salvatore Sanfilippo, Aka 'antirez'.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>RandomkeyCommand: Contents</b><br> <a href="#RANDOMKEY">RANDOMKEY</a><br> <a href="#Return value">Return value</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">RandomkeyCommand</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
#sidebar <a href="GenericCommandsSidebar.html">GenericCommandsSidebar</a><h1><a name="RANDOMKEY">RANDOMKEY</a></h1>
|
||||
<i>Time complexity: O(1)</i><blockquote>Return a randomly selected key from the currently selected DB.</blockquote>
|
||||
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Singe line reply</a>, specifically the randomly selected key or an empty string is the database is empty.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Redis0100ChangeLog: Contents</b><br> <a href="#Redis 0.100 Changelog">Redis 0.100 Changelog</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Redis0100ChangeLog</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
<h1><a name="Redis 0.100 Changelog">Redis 0.100 Changelog</a></h1>
|
||||
<pre class="codeblock python" name="code">
|
||||
- SUNION, SDIFF, SUNIONSTORE, SDIFFSTORE commands implemented. (Aman Gupta, antirez)
|
||||
- Non blocking replication. Now while N slaves are synchronizing, the master will continue to ask to client queries. (antirez)
|
||||
- PHP client ported to PHP5 (antirez)
|
||||
- FLUSHALL/FLUSHDB no longer sync on disk. Just increment the dirty counter by the number of elements removed, that will probably trigger a background saving operation (antirez)
|
||||
- INCRBY/DECRBY now support 64bit increments, with tests (antirez)
|
||||
- New fields in INFO command, bgsave_in_progress and replication related (antirez)
|
||||
- Ability to specify a different file name for the DB (... can't remember ...)
|
||||
- GETSET command, atomic GET + SET (antirez)
|
||||
- SMOVE command implemented, atomic move-element across sets operation (antirez)
|
||||
- Ability to work with huge data sets, tested up to 350 million keys (antirez)
|
||||
- Warns if /proc/sys/vm/overcommit_memory is set to 0 on Linux. Also make sure to don't resize the hash tables while the child process is saving in order to avoid copy-on-write of memory pages (antirez)
|
||||
- Infinite number of arguments for MGET and all the other commands (antirez)
|
||||
- CPP client (Brian Hammond)
|
||||
- DEL is now a vararg, IMPORTANT: memory leak fixed in loading DB code (antirez)
|
||||
- Benchmark utility now supports random keys (antirez)
|
||||
- Timestamp in log lines (antirez)
|
||||
- Fix SINTER/UNIONSTORE to allow for &=/|= style operations (i.e. SINTERSTORE set1 set1 set2) (Aman Gupta)
|
||||
- Partial qsort implemented in SORT command, only when both BY and LIMIT is used (antirez)
|
||||
- Allow timeout=0 config to disable client timeouts (Aman Gupta)
|
||||
- Alternative (faster/simpler) ruby client API compatible with Redis-rb (antirez)
|
||||
- S*STORE now return the cardinality of the resulting set (antirez)
|
||||
- TTL command implemented (antirez)
|
||||
- Critical bug about glueoutputbuffers=yes fixed. Under load and with pipelining and clients disconnecting on the middle of the chat with the server, Redis could block. (antirez)
|
||||
- Different replication fixes (antirez)
|
||||
- SLAVEOF command implemented for remote replication management (antirez)
|
||||
- Issue with redis-client used in scripts solved, now to check if the latest argument must come from standard input we do not check that stdin is or not a tty but the command arity (antirez)
|
||||
- Warns if using the default config (antirez)
|
||||
- maxclients implemented, see redis.conf for details (antirez)
|
||||
- max bytes of a received command enlarged from 1k to 32k (antirez)
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html>
|
||||
<head>
|
||||
<link type="text/css" rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<div id='header'>
|
||||
<a href="index.html">
|
||||
<img style="border:none" alt="Redis Documentation" src="redis.png">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="pagecontent">
|
||||
<div class="index">
|
||||
<!-- This is a (PRE) block. Make sure it's left aligned or your toc title will be off. -->
|
||||
<b>Redis0900ChangeLog: Contents</b><br> <a href="#CHANGELOG for Redis 0.900">CHANGELOG for Redis 0.900</a>
|
||||
</div>
|
||||
|
||||
<h1 class="wikiname">Redis0900ChangeLog</h1>
|
||||
|
||||
<div class="summary">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="narrow">
|
||||
<h1><a name="CHANGELOG for Redis 0.900">CHANGELOG for Redis 0.900</a></h1><pre class="codeblock python" name="code">
|
||||
2009-06-16 client libraries updated (antirez)
|
||||
2009-06-16 Better handling of background saving process killed or crashed (antirez)
|
||||
2009-06-14 number of keys info in INFO command (Diego Rosario Brogna)
|
||||
2009-06-14 SPOP documented (antirez)
|
||||
2009-06-14 Clojure library (Ragnar Dahlén)
|
||||
2009-06-10 It is now possible to specify - as config file name to read it from stdin (antirez)
|
||||
2009-06-10 max bytes in an inline command raised to 1024*1024 bytes, in order to allow for very large MGETs and still protect from client crashes (antirez)
|
||||
2009-06-08 SPOP implemented. Hash table resizing for Sets and Expires too. Changed the resize policy to play better with RANDOMKEY and SPOP. (antirez)
|
||||
2009-06-07 some minor changes to the backtrace code (antirez)
|
||||
2009-06-07 enable backtrace capabilities only for Linux and MacOSX (antirez)
|
||||
2009-06-07 Dump a backtrace on sigsegv/sigbus, original coded (Diego Rosario Brogna)
|
||||
2009-06-05 Avoid a busy loop while sending very large replies against very fast links, this allows to be more responsive with other clients even under a KEY * against the loopback interface (antirez)
|
||||
2009-06-05 Kill the background saving process before performing SHUTDOWN to avoid races (antirez)
|
||||
2009-06-05 LREM now returns :0 for non existing keys (antirez)
|
||||
2009-06-05 added config.h for #ifdef business isolation, added fstat64 for Mac OS X (antirez)
|
||||
2009-06-04 macosx specific zmalloc.c, uses malloc_size function in order to avoid to waste memory and time to put an additional header (antirez)
|
||||
2009-06-04 DEBUG OBJECT implemented (antirez)
|
||||
2009-06-03 shareobjectspoolsize implemented in reds.conf, in order to control the pool size when object sharing is on (antirez)
|
||||
2009-05-27 maxmemory implemented (antirez)
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user