├── chapter 9 └── .gitkeep ├── chapter 8 ├── nutcracker-0.4.1.tar.gz ├── twemproxy_redis.yml ├── twemproxy.js ├── partitioning.js ├── hashpartitioning.js ├── rangepartitioning.js ├── consistenthashing.js └── tagging.js ├── chapter 7 ├── stunnel-server.conf ├── stunnel-client.conf ├── renamed-commands.conf ├── pythonssl.py ├── cert.pem ├── key.pem └── private.pem ├── chapter 5 ├── composer.json ├── transactions.rb ├── transactions.py ├── blocking.py ├── blocking.rb ├── pipelines.py ├── transactions.php ├── scripting.rb ├── pipelines.rb ├── scripting.py ├── blocking.php ├── pipelines.php ├── basics.py ├── basics.rb ├── scripting.php └── basics.php ├── chapter 1 ├── hello.js ├── producer-worker.js ├── consumer-worker.js ├── queue.js ├── articles-popularity.js └── hash-voting-system.js ├── chapter 4 ├── publisher.js ├── intro-lua.js ├── zpop-lua-evalsha.js ├── zpop-lua.js ├── subscriber.js ├── watch-transaction.js └── bank-transaction.js ├── chapter 6 ├── benchmark-set.js └── benchmark-bitmap.js ├── .gitignore ├── chapter 2 ├── unique-visitors.js ├── metrics-bitmap.js ├── deal-metrics.js └── leaderboard.js ├── chapter 3 ├── using-timeseries.js ├── using-timeseries-unique.js ├── timeseries-hyperloglog.js ├── timeseries-string.js ├── timeseries-sorted-set.js └── timeseries-hash.js └── README.md /chapter 9/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chapter 8/nutcracker-0.4.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-essentials/book/HEAD/chapter 8/nutcracker-0.4.1.tar.gz -------------------------------------------------------------------------------- /chapter 7/stunnel-server.conf: -------------------------------------------------------------------------------- 1 | foreground = yes 2 | cert = private.pem 3 | [redis] 4 | accept = 0.0.0.0:6666 5 | connect = 127.0.0.1:6379 -------------------------------------------------------------------------------- /chapter 5/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redis/chapter5", 3 | "require": { 4 | "predis/predis": "~1.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /chapter 7/stunnel-client.conf: -------------------------------------------------------------------------------- 1 | foreground = yes 2 | cert = private.pem 3 | client = yes 4 | [redis] 5 | accept = 127.0.0.1:5555 6 | connect = 0.0.0.0:6666 -------------------------------------------------------------------------------- /chapter 1/hello.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); // 1 2 | var client = redis.createClient(); // 2 3 | client.set("my_key", "Hello World using Node.js and Redis"); // 3 4 | client.get("my_key", redis.print); // 4 5 | client.quit(); // 5 -------------------------------------------------------------------------------- /chapter 4/publisher.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | 4 | var channel = process.argv[2]; // 1 5 | var command = process.argv[3]; // 2 6 | 7 | client.publish(channel, command); // 3 8 | 9 | client.quit(); 10 | -------------------------------------------------------------------------------- /chapter 7/renamed-commands.conf: -------------------------------------------------------------------------------- 1 | rename-command FLUSHDB e0cc96ad2eab73c2c347011806a76b73 2 | rename-command FLUSHALL a31907b21c437f46808ea49322c91d23a 3 | rename-command CONFIG "" 4 | rename-command KEYS "" 5 | rename-command DEBUG "" 6 | rename-command SAVE "" 7 | -------------------------------------------------------------------------------- /chapter 8/twemproxy_redis.yml: -------------------------------------------------------------------------------- 1 | my_cluster: 2 | listen: 127.0.0.1:22121 3 | hash: md5 4 | distribution: ketama 5 | auto_eject_hosts: true 6 | redis: true 7 | servers: 8 | - 127.0.0.1:6666:1 server1 9 | - 127.0.0.1:7777:1 server2 10 | - 127.0.0.1:8888:1 server3 -------------------------------------------------------------------------------- /chapter 4/intro-lua.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | 4 | client.set("mykey", "myvalue"); // 1 5 | 6 | var luaScript = 'return redis.call("GET", KEYS[1])'; // 2 7 | client.eval(luaScript, 1, "mykey", function(err, reply) { // 3 8 | console.log(reply); // 4 9 | client.quit(); 10 | }); 11 | -------------------------------------------------------------------------------- /chapter 5/transactions.rb: -------------------------------------------------------------------------------- 1 | require 'redis' 2 | @redis = Redis.new(:host => "127.0.0.1", :port => 6379) 3 | 4 | @redis.multi do |multi| 5 | multi.set "transaction:key", "A string in a transactional block" 6 | multi.incr "transaction:counter" 7 | end 8 | 9 | @redis.get "transaction:key" 10 | # => "A string in a transactional block" -------------------------------------------------------------------------------- /chapter 7/pythonssl.py: -------------------------------------------------------------------------------- 1 | import redis # 1 2 | import ssl # 2 3 | 4 | pool = redis.ConnectionPool( 5 | connection_class=redis.SSLConnection, 6 | host='0.0.0.0', 7 | port=6666, 8 | ssl_ca_certs='private.pem', 9 | ssl_cert_reqs=ssl.CERT_REQUIRED) # 3 10 | r = redis.StrictRedis(connection_pool=pool) # 4 11 | 12 | print(r.ping()) # 5 13 | -------------------------------------------------------------------------------- /chapter 1/producer-worker.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | var queue = require("./queue"); // 1 4 | var logsQueue = new queue.Queue("logs", client); // 2 5 | var MAX = 5; 6 | for (var i = 0 ; i < MAX ; i++) { // 3 7 | logsQueue.push("Hello world #" + i); // 4 8 | } 9 | console.log("Created " + MAX + " logs"); // 5 10 | client.quit(); -------------------------------------------------------------------------------- /chapter 4/zpop-lua-evalsha.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | 4 | var luaScript = 'return "Lua script using EVALSHA"'; 5 | client.script("load", luaScript, function(err, reply) { 6 | var scriptId = reply; 7 | 8 | client.evalsha(scriptId, 0, function(err, reply) { 9 | console.log(reply); 10 | client.quit(); 11 | }) 12 | }); 13 | -------------------------------------------------------------------------------- /chapter 5/transactions.py: -------------------------------------------------------------------------------- 1 | import redis 2 | client = redis.StrictRedis(host='localhost', port=6379) 3 | 4 | with client.pipeline(transaction=True) as transaction: 5 | transaction.set('transaction:key', 'A string in a transactional block') 6 | transaction.incr('transaction:counter') 7 | transaction.get('transaction:key') 8 | result = transaction.execute() 9 | # [True, 2, 'A string in a transactional block'] 10 | -------------------------------------------------------------------------------- /chapter 6/benchmark-set.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | var MAX_USERS = 100000; 4 | var MAX_DEALS = 12; 5 | var MAX_DEAL_ID = 10000; 6 | 7 | for (var i = 0 ; i < MAX_USERS ; i++) { 8 | var multi = client.multi(); 9 | for (var j = 0 ; j < MAX_DEALS ; j++) { 10 | multi.sadd("set:user:" + i, MAX_DEAL_ID - j, 1); 11 | } 12 | multi.exec(); 13 | } 14 | 15 | client.quit(); 16 | -------------------------------------------------------------------------------- /chapter 6/benchmark-bitmap.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | var MAX_USERS = 100000; 4 | var MAX_DEALS = 12; 5 | var MAX_DEAL_ID = 10000; 6 | 7 | for (var i = 0 ; i < MAX_USERS ; i++) { 8 | var multi = client.multi(); 9 | for (var j = 0 ; j < MAX_DEALS ; j++) { 10 | multi.setbit("bitmap:user:" + i, MAX_DEAL_ID - j, 1); 11 | } 12 | multi.exec(); 13 | } 14 | 15 | client.quit(); 16 | -------------------------------------------------------------------------------- /chapter 5/blocking.py: -------------------------------------------------------------------------------- 1 | import redis 2 | client = redis.StrictRedis(host='localhost', port=6379) 3 | 4 | client.lpush('blocking:queue', 'first') 5 | client.lpush('blocking:queue', 'second') 6 | 7 | client.blpop(['blocking:queue'], 0) 8 | # ('blocking:queue', 'first') 9 | 10 | client.brpop(['blocking:queue'], 0) 11 | # ('blocking:queue', 'second') 12 | 13 | client.rpush('blocking:source', 'message') 14 | client.brpoplpush('blocking:source', 'blocking:destination', 0) 15 | # 'message' 16 | -------------------------------------------------------------------------------- /chapter 8/twemproxy.js: -------------------------------------------------------------------------------- 1 | var redis = require('redis'); 2 | var options = { // 1 3 | "no_ready_check": true, 4 | }; 5 | var client = redis.createClient(22121, 'localhost', options); // 2 6 | var alphabet = [ 7 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 8 | 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 9 | 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' 10 | ]; // 3 11 | alphabet.forEach(function(letter) { // 4 12 | client.set(letter, letter); // 5 13 | }); 14 | client.quit(); // 6 15 | -------------------------------------------------------------------------------- /chapter 5/blocking.rb: -------------------------------------------------------------------------------- 1 | require 'redis' 2 | @redis = Redis.new(:host => "127.0.0.1", :port => 6379) 3 | 4 | @redis.rpush 'blocking:queue', 'first' 5 | @redis.rpush 'blocking:queue', 'second' 6 | 7 | @redis.blpop ['blocking:queue'], 0 8 | # => ["blocking:queue", "first"] 9 | 10 | @redis.brpop ['blocking:queue'], 0 11 | # => ["blocking:queue", "second"] 12 | 13 | @redis.lpush 'blocking:source', 'message' 14 | @redis.brpoplpush 'blocking:source', 'blocking:destination', 0 15 | # => "message" 16 | -------------------------------------------------------------------------------- /chapter 5/pipelines.py: -------------------------------------------------------------------------------- 1 | import redis 2 | client = redis.StrictRedis(host='localhost', port=6379) 3 | 4 | pipeline = client.pipeline(transaction=False) 5 | pipeline.sadd("cards:suit", "hearts") 6 | pipeline.sadd("cards:suit", "spades") 7 | pipeline.sadd("cards:suit", "diamonds") 8 | pipeline.sadd("cards:suit", "clubs") 9 | pipeline.smembers("cards:suit") 10 | result = pipeline.execute() 11 | # [0, 0, 0, 0, set(['hearts', 'clubs', 'spades', 'diamonds'])] 12 | 13 | with client.pipeline() as pipe: 14 | pipe.scard("cards:suit") 15 | result = pipe.execute() 16 | # [4] 17 | -------------------------------------------------------------------------------- /chapter 8/partitioning.js: -------------------------------------------------------------------------------- 1 | function Partitioning(clients) { // 1 2 | this.clients = clients; 3 | } 4 | 5 | Partitioning.prototype = { 6 | _getClient: function(key) { // 2 7 | throw "Subclass should implement _getClient() method"; 8 | }, 9 | set: function(key, value) { // 3 10 | var client = this._getClient(key); // 4 11 | client.set.apply(client, arguments); // 5 12 | }, 13 | get: function(key) { // 6 14 | var client = this._getClient(key); 15 | client.get.apply(client, arguments); 16 | } 17 | }; 18 | 19 | module.exports = Partitioning; // 7 20 | -------------------------------------------------------------------------------- /chapter 1/consumer-worker.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | var queue = require("./queue"); // 1 4 | var logsQueue = new queue.Queue("logs", client); // 2 5 | 6 | function logMessages() { // 3 7 | logsQueue.pop(function(err, replies) { // 4 8 | var queueName = replies[0]; 9 | var message = replies[1]; 10 | console.log("[consumer] Got log: " + message); // 5 11 | logsQueue.size(function(err, size) { // 6 12 | console.log(size + " logs left"); 13 | }); 14 | logMessages(); // 7 15 | }); 16 | } 17 | logMessages(); // 8 -------------------------------------------------------------------------------- /chapter 5/transactions.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 5 | 'port' => 6379), 6 | array('prefix' => 'php:')); 7 | $client->transaction() 8 | ->set('transaction:key', 9 | 'A string in a transactional block') 10 | ->incr( 'transaction:counter') 11 | ->get('transaction:key') 12 | ->execute(); 13 | # array("Predis\Response\Status => OK", 2, "A string in a transactional block") 14 | 15 | ?> -------------------------------------------------------------------------------- /chapter 4/zpop-lua.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | 4 | client.zadd("presidents", 1732, "George Washington"); 5 | client.zadd("presidents", 1809, "Abraham Lincoln"); 6 | client.zadd("presidents", 1858, "Theodore Roosevelt"); 7 | 8 | var luaScript = [ 9 | 'local elements = redis.call("ZRANGE", KEYS[1], 0, 0)', 10 | 'redis.call("ZREM", KEYS[1], elements[1])', 11 | 'return elements[1]' 12 | ].join('\n'); // 1 13 | 14 | client.eval(luaScript, 1, "presidents", function(err, reply) { // 2 15 | console.log("The first president in the group is:", reply); 16 | client.quit(); 17 | }); 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /chapter 8/hashpartitioning.js: -------------------------------------------------------------------------------- 1 | var util = require("util"); 2 | var crypto = require('crypto'); // 1 3 | var Partitioning = require("partitioning"); 4 | 5 | function HashPartitioning(clients) { 6 | Partitioning.call(this, clients); 7 | } 8 | 9 | util.inherits(HashPartitioning, Partitioning); 10 | 11 | HashPartitioning.prototype._getClient = function(key) { // 2 12 | var index = this._hashFunction(key) % this.clients.length;// 3 13 | return this.clients[index]; // 4 14 | }; 15 | 16 | HashPartitioning.prototype._hashFunction = function(str) { // 5 17 | var hash = crypto.createHash('md5').update(str).digest('hex'); // 6 18 | return parseInt(hash, 16); // 7 19 | }; 20 | -------------------------------------------------------------------------------- /chapter 5/scripting.rb: -------------------------------------------------------------------------------- 1 | require 'redis' 2 | @redis = Redis.new(:host => "127.0.0.1", :port => 6379) 3 | 4 | lua_script = < ["script:my_value"], 15 | :argv => [3] 16 | }) 17 | # => 90 18 | 19 | @redis.set "script:my_value", 30 20 | multiply_script_sha = @redis.script :load, lua_script 21 | @redis.evalsha(multiply_script_sha, { 22 | :keys => ["script:my_value"], 23 | :argv => [3] 24 | }) 25 | # => 90 -------------------------------------------------------------------------------- /chapter 1/queue.js: -------------------------------------------------------------------------------- 1 | function Queue(queueName, redisClient) { // 1 2 | this.queueName = queueName; // 2 3 | this.redisClient = redisClient; // 3 4 | this.queueKey = 'queues:' + queueName; // 4 5 | // zero means no timeout 6 | this.timeout = 0; // 5 7 | } 8 | 9 | Queue.prototype.size = function(callback) { // 1 10 | this.redisClient.llen(this.queueKey, callback); // 2 11 | }; 12 | 13 | 14 | Queue.prototype.push = function(data) { // 1 15 | this.redisClient.lpush(this.queueKey, data); // 2 16 | }; 17 | 18 | Queue.prototype.pop = function(callback) { // 1 19 | this.redisClient.brpop(this.queueKey, this.timeout, callback); // 2 20 | }; 21 | 22 | exports.Queue = Queue; // 1 -------------------------------------------------------------------------------- /chapter 5/pipelines.rb: -------------------------------------------------------------------------------- 1 | require 'redis' 2 | @redis = Redis.new(:host => "127.0.0.1", :port => 6379) 3 | 4 | result = @redis.pipelined do 5 | @redis.sadd "cards:suits", "hearts" 6 | @redis.sadd "cards:suits", "spades" 7 | @redis.sadd "cards:suits", "diamonds" 8 | @redis.sadd "cards:suits", "clubs" 9 | @redis.smembers "cards:suits" 10 | end 11 | # => [false, false, false, false, ["diamonds", "spades", "clubs", "hearts"]] 12 | 13 | 14 | 15 | require 'redis' 16 | @redis = Redis.new(:host => "127.0.0.1", :port => 6379) 17 | 18 | @redis.pipelined do 19 | @redis.set "message", "hello world" 20 | @message = @redis.get "message" 21 | end 22 | 23 | @message.value 24 | # => "hello world" -------------------------------------------------------------------------------- /chapter 5/scripting.py: -------------------------------------------------------------------------------- 1 | import redis 2 | client = redis.StrictRedis(host='localhost', port=6379) 3 | 4 | lua_script = """ 5 | local value = redis.call('GET', KEYS[1]) 6 | value = tonumber(value) 7 | local newvalue = value * ARGV[1] 8 | redis.call('SET', KEYS[1], newvalue) 9 | return newvalue 10 | """ 11 | 12 | client.set('python:value', 30) 13 | client.eval(lua_script, 1, "python:value", 3) 14 | # 90 15 | 16 | client.set('python:value', 30) 17 | sha = client.script_load(lua_script) 18 | client.evalsha(sha, 1, 'python:value', 3) 19 | # 90 20 | 21 | client.set('python:value', 30) 22 | multiply = client.register_script(lua_script) 23 | multiply(keys=['python:value'], args=[3]) 24 | # 90 25 | -------------------------------------------------------------------------------- /chapter 5/blocking.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 5 | 'port' => 6379), 6 | array('prefix' => 'php:')); 7 | 8 | $client->lpush('blocking:queue', 'first'); 9 | $client->lpush('blocking:queue', 'second'); 10 | 11 | $client->blpop(['blocking:queue'], 0); 12 | # array('php:blocking:queue', 'second') 13 | 14 | $client->brpop(['blocking:queue'], 0); 15 | # array('php:blocking:queue', 'first') 16 | 17 | $client->rpush('blocking:source', 'message'); 18 | $client->brpoplpush('blocking:source', 'blocking:destination', 0); 19 | # 'message' 20 | 21 | ?> -------------------------------------------------------------------------------- /chapter 4/subscriber.js: -------------------------------------------------------------------------------- 1 | var os = require("os"); // 1 2 | var redis = require("redis"); 3 | var client = redis.createClient(); 4 | 5 | var COMMANDS = {}; // 2 6 | 7 | COMMANDS.DATE = function() { // 3 8 | var now = new Date(); 9 | console.log("DATE " + now.toISOString()); 10 | }; 11 | 12 | COMMANDS.PING = function() { // 4 13 | console.log("PONG"); 14 | }; 15 | 16 | COMMANDS.HOSTNAME = function() { // 5 17 | console.log("HOSTNAME " + os.hostname()); 18 | }; 19 | 20 | client.on("message", function(channel, commandName) { // 6 21 | if (COMMANDS.hasOwnProperty(commandName)) { // 7 22 | var commandFunction = COMMANDS[commandName]; // 8 23 | commandFunction(); // 9 24 | } else { // 10 25 | console.log("Unknown command: " + commandName); 26 | } 27 | }); 28 | client.subscribe("global", process.argv[2]); // 11 -------------------------------------------------------------------------------- /chapter 5/pipelines.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 5 | 'port' => 6379), 6 | array('prefix' => 'php:')); 7 | # fluent interface 8 | $client->pipeline() 9 | ->sadd("cards:suits", 'hearts') 10 | ->sadd("cards:suits", 'spades') 11 | ->sadd("cards:suits", 'diamonds') 12 | ->sadd("cards:suits", 'clubs') 13 | ->smembers("cards:suits") 14 | ->execute(); 15 | # array(1,1,1,1, array('diamonds', 'hearts', 'clubs', 'spades')) 16 | 17 | # anonymous function 18 | $client->pipeline(function ($pipe) { 19 | $pipe->scard("cards:suits"); 20 | $pipe->smembers("cards:suits"); 21 | }); 22 | # array(4, array('diamonds', 'hearts', 'clubs', 'spades')) 23 | 24 | ?> -------------------------------------------------------------------------------- /chapter 5/basics.py: -------------------------------------------------------------------------------- 1 | import redis 2 | client = redis.StrictRedis(host='localhost', port=6379) 3 | 4 | client.set("string:my_key", "Hello World") 5 | client.get("string:my_key") 6 | # "Hello World" 7 | client.incr("string:counter") 8 | client.mget(["string:my_key", "string:counter"]) 9 | # ['Hello World', '2'] 10 | 11 | client.rpush("list:my_list", "item1", "item2") 12 | client.lpop("list:my_list") 13 | # 'item1' 14 | 15 | client.hset("set:redis_book", "title", "Redis Essentials") 16 | client.hgetall("set:redis_book") 17 | # {'title': 'Redis Essentials'} 18 | 19 | client.sadd("set:users", "alice", "bob") 20 | client.smembers("set:users") 21 | # set(['bob', 'alice']) 22 | 23 | client.zadd("sorted_set:programmers", 1940, "Alan Kay") 24 | client.zadd("sorted_set:programmers", 1912, "Alan Turing") 25 | client.zrange("sorted_set:programmers", 0, -1, withscores=True) 26 | # [('Alan Turing', 1912.0), ('Alan Kay', 1940.0)] 27 | -------------------------------------------------------------------------------- /chapter 5/basics.rb: -------------------------------------------------------------------------------- 1 | require 'redis' 2 | @redis = Redis.new(:host => "127.0.0.1", :port => 6379) 3 | 4 | @redis.set "string:my_key", "Hello World" 5 | @redis.get "string:my_key" 6 | # => "Hello World" 7 | @redis.incr "string:counter" 8 | @redis.mget ["string:my_key", "string:counter"] 9 | # => ["Hello World", "1"] 10 | 11 | @redis.rpush "list:my_list", ["item1", "item2"] 12 | @redis.lpop "list:my_list" 13 | # => "item1" 14 | 15 | @redis.hset "set:redis_book", "title", "Redis Essentials" 16 | @redis.hgetall "set:redis_book" 17 | # => {"title"=>"Redis Essentials"} 18 | 19 | @redis.sadd "set:users", ["alice", "bob"] 20 | @redis.smembers "set:users" 21 | # => ["bob", "alice"] 22 | 23 | @redis.zadd "sorted_set:programmers", 1940, "Alan Kay" 24 | @redis.zadd "sorted_set:programmers", 1912, "Alan Turing" 25 | @redis.zrange "sorted_set:programmers", 0, -1, :withscores => true 26 | # => [["Alan Turing", 1912.0], ["Alan Kay", 1940.0]] 27 | -------------------------------------------------------------------------------- /chapter 5/scripting.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 5 | 'port' => 6379), 6 | array('prefix' => 'php:')); 7 | 8 | class MultiplyValue extends Predis\Command\ScriptCommand { 9 | public function getKeysCount() { 10 | return 1; 11 | } 12 | 13 | public function getScript() { 14 | $lua = <<getProfile()->defineCommand('multiply', 'MultiplyValue'); 26 | $client->set("mynumber", 4); 27 | $client->multiply("mynumber", 2); 28 | #8 29 | 30 | ?> -------------------------------------------------------------------------------- /chapter 8/rangepartitioning.js: -------------------------------------------------------------------------------- 1 | var util = require("util"); // 1 2 | var Partitioning = require("partitioning"); // 2 3 | 4 | function RangePartitioning(clients) { // 3 5 | Partitioning.call(this, clients); // 4 6 | } 7 | 8 | util.inherits(RangePartitioning, Partitioning); // 5 9 | 10 | RangePartitioning.prototype._getClient = function(key) { // 6 11 | var possibleValues = '0123456789abcdefghijklmnopqrstuvwxyz'; // 7 12 | var rangeSize = possibleValues.length / this.clients.length; // 8 13 | for (var i = 0, clientIndex = 0 ; i < possibleValues.length ; i += rangeSize, clientIndex++) { // 9 14 | var range = possibleValues.slice(i, i + rangeSize); // 10 15 | 16 | if (range.indexOf(key[0].toLowerCase()) != -1) { // 11 17 | return this.clients[clientIndex]; // 12 18 | } 19 | } 20 | // if key does not start with 0 to 9 neither A to Z, 21 | // fall back to always using the first client 22 | return this.clients[0]; // 13 23 | }; 24 | -------------------------------------------------------------------------------- /chapter 4/watch-transaction.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | 4 | function zpop(key, callback) { // 1 5 | client.watch(key, function(watchErr, watchReply) { // 2 6 | client.zrange(key, 0, 0, function(zrangeErr, zrangeReply) { // 3 7 | var multi = client.multi(); // 4 8 | multi.zrem(key, zrangeReply); // 5 9 | multi.exec(function(transactionErr, transactionReply) { // 6 10 | if (transactionReply) { 11 | callback(zrangeReply[0]); // 7 12 | } else { 13 | zpop(key, callback); // 8 14 | } 15 | }); 16 | }); 17 | }); 18 | } 19 | 20 | client.zadd("presidents", 1732, "George Washington"); 21 | client.zadd("presidents", 1809, "Abraham Lincoln"); 22 | client.zadd("presidents", 1858, "Theodore Roosevelt"); 23 | 24 | zpop("presidents", function(member) { 25 | console.log("The first president in the group is:", member); 26 | client.quit(); 27 | }); 28 | -------------------------------------------------------------------------------- /chapter 5/basics.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 5 | 'port' => 6379), array('prefix' =>'php:')); 6 | 7 | $client->set("string:my_key", "Hello World"); 8 | $client->get("string:my_key"); 9 | # "Hello World" 10 | $client->incr("string:counter"); 11 | $client->mget(array("string:my_key", "string:counter")); 12 | # array('Hello World', '2') 13 | 14 | $client->rpush("list:my_list", "item1", "item2"); 15 | $client->lpop("list:my_list"); 16 | # 'item1' 17 | 18 | $client->hset("set:redis_book", "title", "Redis Essentials"); 19 | $client->hgetall("set:redis_book"); 20 | # array('title' => 'Redis Essentials') 21 | 22 | $client->sadd("set:users", "alice", "bob"); 23 | $client->smembers("set:users"); 24 | # array('bob', 'alice') 25 | 26 | $client->zadd("sorted_set:programmers", 1940, "Alan Kay"); 27 | $client->zadd("sorted_set:programmers", 1912, "Alan Turing"); 28 | $client->zrange("sorted_set:programmers", 0, -1, "withscores"); 29 | # array('Alan Turing' => 1912, 'Alan Kay' => 1940) 30 | 31 | ?> -------------------------------------------------------------------------------- /chapter 1/articles-popularity.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); // 1 2 | var client = redis.createClient(); // 2 3 | 4 | function upVote(id) { // 3 5 | var key = "article:" + id + ":votes"; // 4 6 | client.incr(key); // 5 7 | } 8 | 9 | function downVote(id) { // 1 10 | var key = "article:" + id + ":votes"; // 2 11 | client.decr(key); // 3 12 | } 13 | 14 | function showResults(id) { 15 | var headlineKey = "article:" + id + ":headline"; 16 | var voteKey = "article:" + id + ":votes"; 17 | client.mget([headlineKey, voteKey], function(err, replies) { // 1 18 | console.log('The article "' + replies[0] + '" has', replies[1], 'votes'); // 2 19 | }); 20 | } 21 | 22 | upVote(12345); // article:12345 has 1 vote 23 | upVote(12345); // article:12345 has 2 votes 24 | upVote(12345); // article:12345 has 3 votes 25 | upVote(10001); // article:10001 has 1 vote 26 | upVote(10001); // article:10001 has 2 votes 27 | downVote(10001); // article:10001 has 1 vote 28 | upVote(60056); // article:60056 has 1 vote 29 | showResults(12345); 30 | showResults(10001); 31 | showResults(60056); 32 | client.quit(); 33 | -------------------------------------------------------------------------------- /chapter 4/bank-transaction.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | 4 | function transfer(from, to, value, callback) { // 1 5 | client.get(from, function(err, balance) { // 2 6 | var multi = client.multi(); // 3 7 | multi.decrby(from, value); // 4 8 | multi.incrby(to, value); // 5 9 | if (balance >= value) { // 6 10 | multi.exec(function(err, reply) { // 7 11 | callback(null, reply[0]); // 8 12 | }); 13 | } else { 14 | multi.discard(); // 9 15 | callback(new Error("Insufficient funds"), null); // 10 16 | } 17 | }); 18 | } 19 | 20 | 21 | 22 | client.mset("max:checkings", 100, "hugo:checkings", 100, function(err, reply) { // 1 23 | console.log("Max checkings: 100"); 24 | console.log("Hugo checkings: 100"); 25 | transfer("max:checkings", "hugo:checkings", 40, function(err, balance) { // 2 26 | if (err) { 27 | console.log(err); 28 | } else { 29 | console.log("Transferred 40 from Max to Hugo") 30 | console.log("Max balance:", balance); 31 | } 32 | client.quit(); 33 | }); 34 | }); 35 | 36 | -------------------------------------------------------------------------------- /chapter 1/hash-voting-system.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); // 1 2 | var client = redis.createClient(); // 2 3 | 4 | function saveLink(id, author, title, link) { // 3 5 | client.hmset("link:" + id, "author", author, "title", title, "link", link, "score", 0); // 4 6 | } 7 | 8 | function upVote(id) { // 1 9 | client.hincrby("link:" + id, "score", 1); // 2 10 | } 11 | 12 | function downVote(id) { //3 13 | client.hincrby("link:" + id, "score", -1); // 4 14 | } 15 | 16 | function showDetails(id) { // 1 17 | client.hgetall("link:" + id, function(err, replies) { // 2 18 | console.log("Title:", replies['title']); // 3 19 | console.log("Author:", replies['author']); // 3 20 | console.log("Link:", replies['link']); // 3 21 | console.log("Score:", replies['score']); // 3 22 | console.log("--------------------------"); 23 | }); 24 | } 25 | 26 | saveLink(123, "dayvson", "Maxwell Dayvson's Github page", "https://github.com/dayvson"); 27 | upVote(123); 28 | upVote(123); 29 | saveLink(456, "hltbra", "Hugo Tavares's Github page", "https://github.com/hltbra"); 30 | upVote(456); 31 | upVote(456); 32 | downVote(456); 33 | showDetails(123); 34 | showDetails(456); 35 | client.quit(); -------------------------------------------------------------------------------- /chapter 2/unique-visitors.js: -------------------------------------------------------------------------------- 1 | var redis = require('redis'); 2 | var client = redis.createClient(); 3 | 4 | function addVisit(date, user) { // 1 5 | var key = 'visits:' + date; // 2 6 | client.pfadd(key, user); // 3 7 | } 8 | 9 | function count(dates) { // 1 10 | var keys = []; // 2 11 | dates.forEach(function(date, index) { // 3 12 | keys.push('visits:' + date); 13 | }); 14 | client.pfcount(keys, function(err, reply) { // 4 15 | console.log('Dates', dates.join(', '), 'had', reply, 'visits'); 16 | }); 17 | } 18 | 19 | function aggregateDate(date) { // 1 20 | var keys = ['visits:' + date]; // 2 21 | for (var i = 0; i < 24; i++) { // 3 22 | keys.push('visits:' + date + 'T' + i); // 4 23 | } 24 | client.pfmerge(keys, function(err, reply) { // 5 25 | console.log('Aggregated date', date); 26 | }); 27 | } 28 | 29 | 30 | var MAX_USERS = 200; // 1 31 | var TOTAL_VISITS = 1000; // 2 32 | for (var i = 0; i < TOTAL_VISITS; i++) { // 3 33 | var username = 'user_' + Math.floor(1 + Math.random() * MAX_USERS); // 4 34 | var hour = Math.floor(Math.random() * 24); // 5 35 | addVisit('2015-01-01T' + hour, username); // 6 36 | } 37 | count(['2015-01-01T0']); // 7 38 | count(['2015-01-01T5', '2015-01-01T6', '2015-01-01T7']); // 8 39 | aggregateDate('2015-01-01'); // 9 40 | count(['2015-01-01']); // 10 41 | 42 | client.quit(); -------------------------------------------------------------------------------- /chapter 3/using-timeseries.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | if (process.argv.length < 3) { // 1 4 | console.log("ERROR: You need to specify a data type!"); 5 | console.log("$ node using-timeseries.js [string|hash]"); 6 | process.exit(1); 7 | } 8 | 9 | var dataType = process.argv[2]; // 2 10 | client.flushall(); // 3 11 | var timeseries = require("./timeseries-" + dataType); // 4 12 | var item1Purchases = new timeseries.TimeSeries(client, "purchases:item1"); // 5 13 | var beginTimestamp = 0; // 6 14 | item1Purchases.insert(beginTimestamp); // 7 15 | item1Purchases.insert(beginTimestamp + 1); // 8 16 | item1Purchases.insert(beginTimestamp + 1); // 9 17 | item1Purchases.insert(beginTimestamp + 3); // 10 18 | item1Purchases.insert(beginTimestamp + 61); // 11 19 | 20 | function displayResults(granularityName, results) { // 12 21 | console.log("Results from " + granularityName + ":"); 22 | console.log("Timestamp \t| Value"); 23 | console.log("--------------- | ------"); 24 | for (var i = 0 ; i < results.length; i++) { 25 | console.log('\t' + results[i].timestamp + '\t| ' + results[i].value); 26 | } 27 | console.log(); 28 | } 29 | 30 | 31 | item1Purchases.fetch("1sec", beginTimestamp, beginTimestamp + 4, displayResults); // 13 32 | item1Purchases.fetch("1min", beginTimestamp, beginTimestamp + 120, displayResults); // 14 33 | 34 | client.quit(); -------------------------------------------------------------------------------- /chapter 3/using-timeseries-unique.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | if (process.argv.length < 3) { 4 | console.log("ERROR: You need to specify a data type!"); 5 | console.log("$ node using-timeseries.js [sorted-set|hyperloglog]"); 6 | process.exit(1); 7 | } 8 | 9 | var dataType = process.argv[2]; 10 | client.flushall(); 11 | var timeseries = require("./timeseries-" + dataType); 12 | var concurrentPlays = new timeseries.TimeSeries(client, "concurrentplays"); 13 | var beginTimestamp = 0; 14 | 15 | concurrentPlays.insert(beginTimestamp, "user:max"); 16 | concurrentPlays.insert(beginTimestamp, "user:max"); 17 | concurrentPlays.insert(beginTimestamp + 1, "user:hugo"); 18 | concurrentPlays.insert(beginTimestamp + 1, "user:renata"); 19 | concurrentPlays.insert(beginTimestamp + 3, "user:hugo"); 20 | concurrentPlays.insert(beginTimestamp + 61, "user:kc"); 21 | 22 | function displayResults(granularityName, results) { 23 | console.log("Results from " + granularityName + ":"); 24 | console.log("Timestamp \t| Value"); 25 | console.log("--------------- | ------"); 26 | for (var i = 0 ; i < results.length; i++) { 27 | console.log('\t' + results[i].timestamp + '\t| ' + results[i].value); 28 | } 29 | console.log(); 30 | } 31 | 32 | concurrentPlays.fetch("1sec", beginTimestamp, beginTimestamp + 4, displayResults); 33 | concurrentPlays.fetch("1min", beginTimestamp, beginTimestamp + 120, displayResults); 34 | client.quit(); -------------------------------------------------------------------------------- /chapter 2/metrics-bitmap.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient({return_buffers: true}); // 1 3 | 4 | function storeDailyVisit(date, userId) { // 2 5 | var key = 'visits:daily:' + date; // 3 6 | client.setbit(key, userId, 1, function(err, reply) { // 4 7 | console.log("User", userId, "visited on", date); // 5 8 | }); 9 | } 10 | 11 | function countVisits(date) { // 1 12 | var key = 'visits:daily:' + date; // 2 13 | client.bitcount(key, function(err, reply) { // 3 14 | console.log(date, "had", reply.toString(), "visits."); // 4 15 | }); 16 | } 17 | 18 | function showUserIdsFromVisit(date) { // 1 19 | var key = 'visits:daily:' + date; // 2 20 | client.get(key, function(err, bitmapValue) { // 3 21 | var userIds = []; // 4 22 | var data = bitmapValue.toJSON().data; // 5 23 | data.forEach(function(byte, byteIndex) { // 6 24 | for (var bitIndex = 7 ; bitIndex >= 0 ; bitIndex--) { // 7 25 | var visited = byte >> bitIndex & 1; // 8 26 | if (visited === 1) { // 9 27 | var userId = byteIndex * 8 + (7 - bitIndex); // 10 28 | userIds.push(userId); // 11 29 | } 30 | } 31 | }); 32 | console.log("Users " + userIds + " visited on " + date); // 12 33 | }); 34 | } 35 | 36 | storeDailyVisit('2015-01-01', '1'); 37 | storeDailyVisit('2015-01-01', '2'); 38 | storeDailyVisit('2015-01-01', '10'); 39 | storeDailyVisit('2015-01-01', '55'); 40 | countVisits('2015-01-01'); 41 | showUserIdsFromVisit('2015-01-01'); 42 | 43 | client.quit(); -------------------------------------------------------------------------------- /chapter 2/deal-metrics.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | 4 | function markDealAsSent(dealId, userId) { // 1 5 | client.sadd(dealId, userId); // 2 6 | } 7 | 8 | function sendDealIfNotSent(dealId, userId) { // 1 9 | client.sismember(dealId, userId, function(err, reply) { // 2 10 | if (reply) { 11 | console.log("Deal", dealId, "was already sent to user", userId); // 3 12 | } else { 13 | console.log("Sending", dealId, "to user", userId); // 4 14 | // code to send the deal to the user would go here... // 5 15 | markDealAsSent(dealId, userId); // 6 16 | } 17 | }); 18 | } 19 | 20 | function showUsersThatReceivedAllDeals(dealIds) { // 1 21 | client.sinter(dealIds, function(err, reply) { // 2 22 | console.log(reply + " received all of the deals: " + dealIds); // 3 23 | }); 24 | } 25 | 26 | function showUsersThatReceivedAtLeastOneOfTheDeals(dealIds) { // 1 27 | client.sunion(dealIds, function(err, reply) { // 2 28 | console.log(reply + " received at least one of the deals: " + dealIds); // 3 29 | }); 30 | } 31 | 32 | markDealAsSent('deal:1', 'user:1'); 33 | markDealAsSent('deal:1', 'user:2'); 34 | markDealAsSent('deal:2', 'user:1'); 35 | markDealAsSent('deal:2', 'user:3'); 36 | sendDealIfNotSent('deal:1', 'user:1'); 37 | sendDealIfNotSent('deal:1', 'user:2'); 38 | sendDealIfNotSent('deal:1', 'user:3'); 39 | showUsersThatReceivedAllDeals(["deal:1", "deal:2"]); 40 | showUsersThatReceivedAtLeastOneOfTheDeals(["deal:1", "deal:2"]); 41 | 42 | client.quit(); -------------------------------------------------------------------------------- /chapter 8/consistenthashing.js: -------------------------------------------------------------------------------- 1 | var util = require("util"); 2 | var crypto = require("crypto"); 3 | var Partitioning = require("partitioning"); 4 | 5 | function ConsistentHashingPartitioning(clients, vnodes) { // 1 6 | this._vnodes = vnodes || 256; // 2 7 | this._ring = {}; // 3 8 | this._setUpRing(clients); // 4 9 | } 10 | 11 | util.inherits(ConsistentHashingPartitioning, Partitioning); // 5 12 | 13 | ConsistentHashingPartitioning.prototype._getClient = function(key) { // 6 14 | var ringHashes = Object.keys(this._ring); // 7 15 | var keyHash = this._hashFunction(key); // 8 16 | ringHashes.sort(); // 9 17 | for (var i = 0 ; i < ringHashes.length ; i++) { // 10 18 | var ringHash = ringHashes[i]; // 11 19 | if (ringHash >= keyHash) { // 12 20 | return this._ring[ringHash]; // 13 21 | } 22 | } 23 | // fallback to the start of the ring 24 | return this._ring[ringHashes[0]]; // 14 25 | }; 26 | 27 | ConsistentHashingPartitioning.prototype._hashFunction = function(str) { // 15 28 | return crypto.createHash('md5').update(str).digest('hex'); 29 | }; 30 | 31 | ConsistentHashingPartitioning.prototype._setUpRing = function(clients) { // 16 32 | for (var i = 0 ; i < clients.length; i++) { 33 | this.addClient(clients[i]); 34 | } 35 | }; 36 | 37 | ConsistentHashingPartitioning.prototype.addClient = function(client) { // 17 38 | for (var i = 0 ; i < this._vnodes ; i++) { // 18 39 | var hash = this._hashFunction(client.address + ":" + i); // 19 40 | this._ring[hash] = client; // 20 41 | } 42 | }; 43 | 44 | ConsistentHashingPartitioning.prototype.removeClient = function(client) { // 21 45 | for (var i = 0 ; i < this._vnodes ; i++) { 46 | var hash = this._hashFunction(client.address + ":" + i); 47 | delete this._ring[hash]; 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /chapter 8/tagging.js: -------------------------------------------------------------------------------- 1 | var util = require("util"); 2 | var crypto = require("crypto"); 3 | var Partitioning = require("partitioning"); 4 | 5 | function ConsistentHashingPartitioning(clients, vnodes) { 6 | this._vnodes = vnodes || 256; 7 | this._ring = {}; 8 | this._setUpRing(clients); 9 | } 10 | 11 | util.inherits(ConsistentHashingPartitioning, Partitioning); 12 | 13 | ConsistentHashingPartitioning.prototype._getClient = function(key) { 14 | var ringHashes = Object.keys(this._ring); 15 | var keyHash = this._hashFunction(key); 16 | ringHashes.sort(); 17 | for (var i = 0 ; i < ringHashes.length ; i++) { 18 | var ringHash = ringHashes[i]; 19 | if (ringHash >= keyHash) { 20 | return this._ring[ringHash]; 21 | } 22 | } 23 | // fallback to the start of the ring 24 | return this._ring[ringHashes[0]]; 25 | }; 26 | 27 | /* 28 | This is the only function that was changed when compared to consistenthashing.js 29 | */ 30 | ConsistentHashingPartitioning.prototype._hashFunction = function(str) { 31 | var stringToHash; 32 | // match string like "foo{bar}" 33 | var tagRegex = /.+\{(.+)\}/; // 1 34 | var regexValues = tagRegex.exec(str); // 2 35 | if (regexValues === null) { // 3 36 | stringToHash = str; 37 | } else { // 4 38 | stringToHash = regexValues[1]; 39 | } 40 | return crypto.createHash('md5').update(stringToHash).digest('hex'); // 5 41 | }; 42 | 43 | 44 | ConsistentHashingPartitioning.prototype._setUpRing = function(clients) { 45 | for (var i = 0 ; i < clients.length; i++) { 46 | this.addClient(clients[i]); 47 | } 48 | }; 49 | 50 | ConsistentHashingPartitioning.prototype.addClient = function(client) { 51 | for (var i = 0 ; i < this._vnodes ; i++) { 52 | var hash = this._hashFunction(client.address + ":" + i); 53 | this._ring[hash] = client; 54 | } 55 | }; 56 | 57 | ConsistentHashingPartitioning.prototype.removeClient = function(client) { 58 | for (var i = 0 ; i < this._vnodes ; i++) { 59 | var hash = this._hashFunction(client.address + ":" + i); 60 | delete this._ring[hash]; 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /chapter 7/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 32 | 5ZuCktgS7MgpMpvm432Nd8QC+ajZXKUWPQ== 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /chapter 3/timeseries-hyperloglog.js: -------------------------------------------------------------------------------- 1 | function TimeSeries(client, namespace) { 2 | this.namespace = namespace; 3 | this.client = client; 4 | this.units = { 5 | second: 1, 6 | minute: 60, 7 | hour: 60 * 60, 8 | day: 24 * 60 * 60 9 | }; 10 | this.granularities = { 11 | '1sec' : { name: '1sec', ttl: this.units.hour * 2, duration: 1 }, 12 | '1min' : { name: '1min', ttl: this.units.day * 7, duration: this.units.minute }, 13 | '1hour': { name: '1hour', ttl: this.units.day * 60 , duration: this.units.hour }, 14 | '1day' : { name: '1day', ttl: null, duration: this.units.day } 15 | }; 16 | }; 17 | 18 | 19 | TimeSeries.prototype.insert = function(timestampInSeconds, thing){ //1 20 | for (var granularityName in this.granularities) { 21 | var granularity = this.granularities[granularityName]; 22 | var key = this._getKeyName(granularity, timestampInSeconds); 23 | this.client.pfadd(key, thing); // 2 24 | if (granularity.ttl !== null) { 25 | this.client.expire(key, granularity.ttl); 26 | } 27 | } 28 | }; 29 | 30 | TimeSeries.prototype._getKeyName = function(granularity, timestampInSeconds) { 31 | var roundedTimestamp = this._getRoundedTimestamp(timestampInSeconds, granularity.duration); 32 | return [this.namespace, granularity.name, roundedTimestamp].join(':'); 33 | }; 34 | 35 | TimeSeries.prototype._getRoundedTimestamp = function(timestampInSeconds, precision) { 36 | return Math.floor(timestampInSeconds / precision) * precision; 37 | }; 38 | 39 | TimeSeries.prototype.fetch = function(granularityName, beginTimestamp, endTimestamp, onComplete) { 40 | var granularity = this.granularities[granularityName]; 41 | var begin = this._getRoundedTimestamp(beginTimestamp, granularity.duration); 42 | var end = this._getRoundedTimestamp(endTimestamp, granularity.duration); 43 | var fields = []; 44 | var multi = this.client.multi(); 45 | for (var timestamp = begin; timestamp <= end; timestamp += granularity.duration) { 46 | var key = this._getKeyName(granularity, timestamp); 47 | multi.pfcount(key); // 3 48 | } 49 | multi.exec(function(err, replies) { 50 | var results = []; 51 | for (var i = 0 ; i < replies.length ; i++) { 52 | var timestamp = beginTimestamp + i * granularity.duration; 53 | var value = parseInt(replies[i], 10) || 0; 54 | results.push({timestamp: timestamp, value: value}); 55 | } 56 | onComplete(granularityName, results); 57 | }); 58 | }; 59 | 60 | exports.TimeSeries = TimeSeries; -------------------------------------------------------------------------------- /chapter 3/timeseries-string.js: -------------------------------------------------------------------------------- 1 | function TimeSeries(client, namespace) { // 1 2 | this.namespace = namespace; // 2 3 | this.client = client; // 3 4 | this.units = { // 4 5 | second: 1, 6 | minute: 60, 7 | hour: 60 * 60, 8 | day: 24 * 60 * 60 9 | }; 10 | this.granularities = { // 5 11 | '1sec' : { name: '1sec', ttl: this.units.hour * 2, duration: this.units.second },// 6 12 | '1min' : { name: '1min', ttl: this.units.day * 7, duration: this.units.minute },// 7 13 | '1hour': { name: '1hour', ttl: this.units.day * 60 , duration: this.units.hour },// 8 14 | '1day' : { name: '1day', ttl: null, duration: this.units.day } // 9 }; 15 | }; 16 | } 17 | 18 | TimeSeries.prototype.insert = function(timestampInSeconds) { // 1 19 | for (var granularityName in this.granularities) { // 2 20 | var granularity = this.granularities[granularityName]; // 3 21 | var key = this._getKeyName(granularity, timestampInSeconds); // 4 22 | this.client.incr(key); // 5 23 | if (granularity.ttl !== null) { // 6 24 | this.client.expire(key, granularity.ttl); // 7 25 | } 26 | } 27 | }; 28 | 29 | 30 | TimeSeries.prototype._getKeyName = function(granularity, timestampInSeconds) { // 1 31 | var roundedTimestamp = this._getRoundedTimestamp(timestampInSeconds, granularity.duration); // 2 32 | return [this.namespace, granularity.name, roundedTimestamp]. join(':'); // 3 33 | }; 34 | 35 | TimeSeries.prototype._getRoundedTimestamp = function(timestampInSeconds, precision) { // 1 36 | return Math.floor(timestampInSeconds/precision) * precision; // 2 37 | }; 38 | 39 | 40 | TimeSeries.prototype.fetch = function(granularityName, beginTimestamp, endTimestamp, onComplete) { // 1 41 | var granularity = this.granularities[granularityName]; // 2 42 | var begin = this._getRoundedTimestamp(beginTimestamp, granularity. duration); // 3 43 | var end = this._getRoundedTimestamp(endTimestamp, granularity. duration); // 4 44 | var keys = []; // 5 45 | for (var timestamp = begin; timestamp <= end; timestamp += granularity.duration) { // 6 46 | var key = this._getKeyName(granularity, timestamp); // 7 47 | keys.push(key); // 8 48 | } 49 | this.client.mget(keys, function(err, replies) { // 9 50 | var results = []; // 10 51 | for (var i = 0 ; i < replies.length ; i++) { // 11 52 | var timestamp = beginTimestamp + i * granularity.duration; // 12 53 | var value = parseInt(replies[i], 10) || 0; // 13 54 | results.push({timestamp: timestamp , value: value}); // 14 55 | } 56 | onComplete(granularityName, results); // 15 57 | }); 58 | }; 59 | 60 | exports.TimeSeries = TimeSeries; // 16 -------------------------------------------------------------------------------- /chapter 3/timeseries-sorted-set.js: -------------------------------------------------------------------------------- 1 | function TimeSeries(client, namespace) { 2 | this.namespace = namespace; 3 | this.client = client; 4 | this.units = { 5 | second: 1, 6 | minute: 60, 7 | hour: 60 * 60, 8 | day: 24 * 60 * 60 9 | }; 10 | this.granularities = { // 1 11 | '1sec' : { name: '1sec', ttl: this.units.hour * 2, duration: 1, quantity: this.units.minute * 2 }, 12 | '1min' : { name: '1min', ttl: this.units.day * 7, duration: this.units.minute, quantity: this.units.hour * 2 }, 13 | '1hour': { name: '1hour', ttl: this.units.day * 60 , duration: this.units.hour, quantity: this.units.day * 5 }, 14 | '1day' : { name: '1day', ttl: null, duration: this.units.day, quantity: this.units.day * 30 }, 15 | }; 16 | }; 17 | 18 | TimeSeries.prototype.insert = function(timestampInSeconds, thing){ // 1 19 | for (var granularityName in this.granularities) { 20 | var granularity = this.granularities[granularityName]; 21 | var key = this._getKeyName(granularity, timestampInSeconds); 22 | var timestampScore = this._getRoundedTimestamp(timestampInSeconds, granularity.duration); // 2 23 | var member = timestampScore + ":" + thing; // 3 24 | this.client.zadd(key, timestampScore, member); // 4 25 | if (granularity.ttl !== null) { 26 | this.client.expire(key, granularity.ttl); 27 | } 28 | } 29 | }; 30 | 31 | TimeSeries.prototype._getKeyName = function(granularity, timestampInSeconds) { 32 | var roundedTimestamp = this._getRoundedTimestamp(timestampInSeconds, granularity.quantity); // 1 33 | return [this.namespace, granularity.name, roundedTimestamp].join(':'); 34 | }; 35 | 36 | TimeSeries.prototype._getRoundedTimestamp = function(timestampInSeconds, precision) { 37 | return Math.floor(timestampInSeconds/precision) * precision; 38 | }; 39 | 40 | TimeSeries.prototype.fetch = function(granularityName, beginTimestamp, endTimestamp, onComplete) { 41 | var granularity = this.granularities[granularityName]; 42 | var begin = this._getRoundedTimestamp(beginTimestamp, granularity.duration); 43 | var end = this._getRoundedTimestamp(endTimestamp, granularity.duration); 44 | var fields = []; 45 | var multi = this.client.multi(); 46 | for (var timestamp = begin; timestamp <= end; timestamp +=granularity.duration) { 47 | var key = this._getKeyName(granularity, timestamp); 48 | multi.zcount(key, timestamp, timestamp); // 1 49 | } 50 | multi.exec(function(err, replies) { 51 | var results = []; 52 | for (var i = 0 ; i < replies.length ; i++) { 53 | var timestamp = beginTimestamp + i * granularity.duration; 54 | var value = parseInt(replies[i], 10) || 0; 55 | results.push({timestamp: timestamp , value: value}); 56 | } 57 | onComplete(granularityName, results); 58 | }); 59 | }; 60 | exports.TimeSeries = TimeSeries; -------------------------------------------------------------------------------- /chapter 3/timeseries-hash.js: -------------------------------------------------------------------------------- 1 | function TimeSeries(client, namespace) { 2 | this.namespace = namespace; 3 | this.client = client; 4 | this.units = { 5 | second: 1, 6 | minute: 60, 7 | hour: 60 * 60, 8 | day: 24 * 60 * 60 9 | }; 10 | this.granularities = { // 1 11 | '1sec' : { name: '1sec', ttl: this.units.hour * 2, duration: 1, quantity: this.units.minute * 5 }, 12 | '1min' : { name: '1min', ttl: this.units.day * 7, duration: this.units.minute, quantity: this.units.hour * 8 }, 13 | '1hour': { name: '1hour', ttl: this.units.day * 60 , duration: this.units.hour, quantity: this.units.day * 10 }, 14 | '1day' : { name: '1day', ttl: null, duration: this.units.day, quantity: this.units.day * 30 }, 15 | }; 16 | }; 17 | 18 | TimeSeries.prototype.insert = function(timestampInSeconds) { 19 | for (var granularityName in this.granularities) { 20 | var granularity = this.granularities[granularityName]; 21 | var key = this._getKeyName(granularity, timestampInSeconds); 22 | var fieldName = this._getRoundedTimestamp(timestampInSeconds, granularity.duration); // 1 23 | this.client.hincrby(key, fieldName, 1); // 2 24 | if (granularity.ttl !== null) { 25 | this.client.expire(key, granularity.ttl); 26 | } 27 | } 28 | }; 29 | 30 | TimeSeries.prototype._getKeyName = function(granularity, timestampInSeconds) { 31 | var roundedTimestamp = this._getRoundedTimestamp(timestampInSeconds, granularity.quantity); // 1 32 | return [this.namespace, granularity.name, roundedTimestamp].join(':'); 33 | }; 34 | 35 | TimeSeries.prototype._getRoundedTimestamp = function(timestampInSeconds, precision) { 36 | return Math.floor(timestampInSeconds/precision) * precision; 37 | }; 38 | 39 | TimeSeries.prototype.fetch = function(granularityName, beginTimestamp, endTimestamp, onComplete) { 40 | var granularity = this.granularities[granularityName]; 41 | var begin = this._getRoundedTimestamp(beginTimestamp, granularity.duration); 42 | var end = this._getRoundedTimestamp(endTimestamp, granularity.duration); 43 | var fields = []; // 1 44 | var multi = this.client.multi(); // 2 45 | for (var timestamp = begin; timestamp <= end; timestamp += granularity.duration) { 46 | var key = this._getKeyName(granularity, timestamp); 47 | var fieldName = this._getRoundedTimestamp(timestamp, granularity.duration); // 3 48 | multi.hget(key, fieldName); // 4 49 | } 50 | multi.exec(function(err, replies) { // 5 51 | var results = []; 52 | for (var i = 0 ; i < replies.length ; i++) { 53 | var timestamp = beginTimestamp + i * granularity.duration; 54 | var value = parseInt(replies[i], 10) || 0; 55 | results.push({timestamp: timestamp , value: value}); 56 | } 57 | onComplete(granularityName, results); 58 | }); 59 | }; 60 | 61 | exports.TimeSeries = TimeSeries; -------------------------------------------------------------------------------- /chapter 2/leaderboard.js: -------------------------------------------------------------------------------- 1 | var redis = require("redis"); 2 | var client = redis.createClient(); 3 | 4 | function LeaderBoard(key) { // 1 5 | this.key = key; // 2 6 | } 7 | 8 | LeaderBoard.prototype.addUser = function(username, score) { // 1 9 | client.zadd([this.key, score, username], function(err, replies) { // 2 10 | console.log("User", username,"added to the leaderboard!"); // 3 11 | }); 12 | }; 13 | 14 | LeaderBoard.prototype.removeUser = function(username) { // 1 15 | client.zrem(this.key, username, function(err, replies) { // 2 16 | console.log("User", username, "removed successfully!"); // 3 17 | }); 18 | }; 19 | 20 | LeaderBoard.prototype.getUserScoreAndRank = function(username) { // 1 21 | var leaderboardKey = this.key; // 2 22 | client.zscore(leaderboardKey, username, function(err, zscoreReply) { // 3 23 | client.zrevrank(leaderboardKey, username, function( err, zrevrankReply) { // 4 24 | console.log("\nDetails of " + username + ":"); 25 | console.log("Score:", zscoreReply + ", Rank: #" + (zrevrankReply + 1)); // 5 26 | }); 27 | }); 28 | }; 29 | 30 | LeaderBoard.prototype.showTopUsers = function(quantity) { // 1 31 | client.zrevrange([this.key, 0, quantity - 1, "WITHSCORES"], function(err, reply) { // 2 32 | console.log("\nTop", quantity, "users:"); 33 | for (var i = 0, rank = 1 ; i < reply.length ; i += 2, rank++) {// 3 34 | console.log("#" + rank, "User: " + reply[i] + ", score:", reply[i + 1]); 35 | } 36 | }); 37 | }; 38 | 39 | 40 | LeaderBoard.prototype.getUsersAroundUser = function(username, quantity, callback) { // 1 41 | var leaderboardKey = this.key; // 2 42 | client.zrevrank(leaderboardKey, username, function(err, zrevrankReply) { // 3 43 | var startOffset = Math.floor(zrevrankReply - (quantity / 2) + 1); // 4 44 | if (startOffset < 0) { // 5 45 | startOffset = 0; 46 | } 47 | var endOffset = startOffset + quantity - 1; // 6 48 | client.zrevrange([leaderboardKey, startOffset, endOffset, "WITHSCORES"], function(err, zrevrangeReply) { // 7 49 | var users = []; // 8 50 | for (var i = 0, rank = 1 ; i < zrevrangeReply.length ; i += 2, rank++) { // 9 51 | var user = { 52 | rank: startOffset + rank, 53 | score: zrevrangeReply[i + 1], 54 | username: zrevrangeReply[i], 55 | }; // 10 56 | users.push(user); // 11 57 | } 58 | callback(users); // 12 59 | }); 60 | }); 61 | }; 62 | 63 | var leaderBoard = new LeaderBoard("game-score"); 64 | leaderBoard.addUser("Arthur", 70); 65 | leaderBoard.addUser("KC", 20); 66 | leaderBoard.addUser("Maxwell", 10); 67 | leaderBoard.addUser("Patrik", 30); 68 | leaderBoard.addUser("Ana", 60); 69 | leaderBoard.addUser("Felipe", 40); 70 | leaderBoard.addUser("Renata", 50); 71 | leaderBoard.addUser("Hugo", 80); 72 | leaderBoard.removeUser("Arthur"); 73 | leaderBoard.getUserScoreAndRank("Maxwell"); 74 | leaderBoard.showTopUsers(3); 75 | leaderBoard.getUsersAroundUser("Felipe", 5, function(users) { // 1 76 | console.log("\nUsers around Felipe:"); 77 | users.forEach(function(user) { 78 | console.log("#" + user.rank, "User:", user.username + ", score:", user.score); 79 | }); 80 | client.quit(); // 2 81 | }); -------------------------------------------------------------------------------- /chapter 7/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Redis Essentials 2 | ================ 3 | 4 | Source code of **Redis Essentials** book. 5 | 6 | Book links: 7 | - [Packt](https://www.packtpub.com/big-data-and-business-intelligence/redis-essentials) 8 | - [Amazon](http://www.amazon.com/Redis-Essentials-Maxwell-Dayvson-Silva/dp/1784392456/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=) 9 | 10 | 11 | ###Table of Contents: 12 | 13 | * [Chapter 1](https://github.com/redis-essentials/book/tree/master/chapter%201): Getting Started (The Baby Steps) 14 | * [Chapter 2](https://github.com/redis-essentials/book/tree/master/chapter%202): Advanced Data Types (Earning a Black Belt) 15 | * [Chapter 3](https://github.com/redis-essentials/book/tree/master/chapter%203): Time Series (A Collection of Observations) 16 | * [Chapter 4](https://github.com/redis-essentials/book/tree/master/chapter%204): Commands (Where the Wild Things Are) 17 | * [Chapter 5](https://github.com/redis-essentials/book/tree/master/chapter%205): Clients for Your Favorite Language (Become a Redis Polyglot) 18 | * [Chapter 6](https://github.com/redis-essentials/book/tree/master/chapter%206): Common Pitfalls (Avoiding Traps) 19 | * [Chapter 7](https://github.com/redis-essentials/book/tree/master/chapter%207): Security Techniques (Guard Your Data) 20 | * [Chapter 8](https://github.com/redis-essentials/book/tree/master/chapter%208): Scaling Redis (Beyond a Single Instance) 21 | * [Chapter 9](https://github.com/redis-essentials/book/tree/master/chapter%209): Redis Cluster and Redis Sentinel (Collective Intelligence) 22 | 23 | 24 | ###About this book: 25 | Redis Essentials is a fast-paced guide that teaches the fundamentals on data types, explains how to manage data through commands, and shares experiences from big players in the industry. 26 | 27 | We start off by explaining the basics of Redis followed by the various data types such as Strings, hashes, lists, and more. Next, Common pitfalls for various scenarios are described, followed by solutions to ensure you do not fall into common traps. 28 | 29 | After this, major differences between client implementations in PHP, Python, and Ruby are presented. Next, you will learn how to extend Redis with Lua, get to know security techniques such as basic authorization, firewall rules, and SSL encryption, and discover how to use Twemproxy, Redis Sentinel, and Redis Cluster to scale infrastructures horizontally. At the end of this book, you will be able to utilize all the essential features of Redis to optimize your project's performance. 30 | 31 | 32 | **Who This Book Is For:** 33 | 34 | If you are a competent developer with experience of working with data structure servers and want to boost your project's performance by learning about features of Redis, then this book is for you. 35 | 36 | **What You Will Learn:** 37 | - Build analytics applications using Bitmaps and Hyperloglogs 38 | - Enhance scalability with Twemproxy, Redis Sentinel, and Redis Cluster 39 | - Build a Time Series implementation in Node.js and Redis 40 | - Create your own Redis commands by extending Redis with Lua 41 | - Get to know security techniques to protect your data (SSL encryption, firewall rules, basic authorization) 42 | - Persist data to disk and learn the trade-offs of AOF and RDB 43 | - Understand how to use Node.js, PHP, Python, and Ruby clients for Redis 44 | - Avoid common pitfalls when designing your next solution 45 | 46 | **Style and approach:** 47 | 48 | A practical guide that offers the foundation upon which you can begin to understand the capabilities of Redis using a step-by-step approach. This book is full of real-world problems and in-depth knowledge of the concepts and features of Redis, with plenty of examples. 49 | 50 | ###Found an issue or have a question? 51 | 52 | Please create an [Issue](https://github.com/redis-essentials/book/issues) or send a [Pull Request](https://github.com/redis-essentials/book/pulls) 53 | -------------------------------------------------------------------------------- /chapter 7/private.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -----BEGIN CERTIFICATE----- 53 | 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 83 | 5ZuCktgS7MgpMpvm432Nd8QC+ajZXKUWPQ== 84 | -----END CERTIFICATE----- 85 | --------------------------------------------------------------------------------