├── .gitignore ├── LICENSE ├── README.md ├── dub.json ├── examples ├── SimpleDemo │ ├── .vscode │ │ └── launch.json │ ├── dub.json │ └── source │ │ ├── RedisClusterDemo.d │ │ ├── RedisDemo.d │ │ ├── RedisLockDemo.d │ │ └── app.d └── UnitTest │ ├── .vscode │ └── launch.json │ ├── dub.json │ └── source │ ├── app.d │ └── test │ ├── HostAndPortTest.d │ ├── HostAndPortUtil.d │ ├── RedisClusterTest.d │ ├── RedisPoolTest.d │ ├── RedisTest.d │ ├── commands │ ├── GeoCommandsTest.d │ ├── RedisCommandTestBase.d │ ├── SortedSetCommandsTest.d │ └── StreamsCommandsTest.d │ └── utils │ ├── RedisClusterTestUtil.d │ └── RedisURIHelperTest.d ├── hunt-redis.code-workspace └── source └── hunt └── redis ├── AbstractClient.d ├── BinaryClient.d ├── BinaryRedis.d ├── BinaryRedisCluster.d ├── BinaryRedisPubSub.d ├── BinaryShardedRedis.d ├── BitOP.d ├── BitPosParams.d ├── Builder.d ├── BuilderFactory.d ├── Client.d ├── ClusterReset.d ├── DebugParams.d ├── Exceptions.d ├── GeoCoordinate.d ├── GeoRadiusResponse.d ├── GeoUnit.d ├── HostAndPort.d ├── ListPosition.d ├── Module.d ├── MultiKeyPipelineBase.d ├── Pipeline.d ├── PipelineBase.d ├── Protocol.d ├── Queable.d ├── Redis.d ├── RedisCluster.d ├── RedisClusterCommand.d ├── RedisClusterConnectionHandler.d ├── RedisClusterHostAndPortMap.d ├── RedisClusterInfoCache.d ├── RedisLock.d ├── RedisMonitor.d ├── RedisPool.d ├── RedisPoolOptions.d ├── RedisPubSub.d ├── RedisSentinelPool.d ├── RedisShardInfo.d ├── RedisSlotBasedConnectionHandler.d ├── Response.d ├── ScanParams.d ├── ScanResult.d ├── ShardedRedis.d ├── ShardedRedisPipeline.d ├── ShardedRedisPool.d ├── SortingParams.d ├── StreamEntry.d ├── StreamEntryID.d ├── StreamPendingEntry.d ├── Transaction.d ├── Tuple.d ├── ZParams.d ├── commands ├── AdvancedBinaryRedisCommands.d ├── AdvancedRedisCommands.d ├── BasicCommands.d ├── BasicRedisPipeline.d ├── BinaryRedisClusterCommands.d ├── BinaryRedisCommands.d ├── BinaryRedisPipeline.d ├── BinaryScriptingCommands.d ├── BinaryScriptingCommandsPipeline.d ├── ClusterCommands.d ├── ClusterPipeline.d ├── Commands.d ├── ModuleCommands.d ├── MultiKeyBinaryCommands.d ├── MultiKeyBinaryRedisClusterCommands.d ├── MultiKeyBinaryRedisPipeline.d ├── MultiKeyCommands.d ├── MultiKeyCommandsPipeline.d ├── MultiKeyRedisClusterCommands.d ├── RedisClusterBinaryScriptingCommands.d ├── RedisClusterCommands.d ├── RedisClusterScriptingCommands.d ├── RedisCommands.d ├── RedisPipeline.d ├── ScriptingCommands.d ├── ScriptingCommandsPipeline.d ├── SentinelCommands.d └── package.d ├── package.d ├── params ├── ClientKillParams.d ├── GeoRadiusParam.d ├── MigrateParams.d ├── Params.d ├── SetParams.d ├── ZAddParams.d ├── ZIncrByParams.d └── package.d └── util ├── ByteArrayComparator.d ├── Hashing.d ├── IOUtils.d ├── KeyMergeUtil.d ├── MurmurHash.d ├── Pool.d ├── RedisByteHashMap.d ├── RedisClusterCRC16.d ├── RedisClusterHashTagUtil.d ├── RedisInputStream.d ├── RedisOutputStream.d ├── RedisURIHelper.d ├── SafeEncoder.d ├── ShardInfo.d ├── Sharded.d ├── Slowlog.d └── package.d /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.o 3 | *.obj 4 | 5 | # Compiled Dynamic libraries 6 | *.so 7 | *.dylib 8 | *.dll 9 | 10 | # Compiled Static libraries 11 | *.a 12 | *.lib 13 | 14 | # Executables 15 | *.exe 16 | core 17 | 18 | # DUB 19 | .dub 20 | docs.json 21 | dub.*.json 22 | __dummy.html 23 | docs/ 24 | *-test-library 25 | *-test-application 26 | 27 | # Code coverage 28 | *.lst 29 | 30 | 31 | # Examples 32 | examples/UnitTest/unittest 33 | examples/UnitTest/reset-cluster 34 | examples/SimpleDemo/simple-demo 35 | 36 | # Others 37 | /java/ 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Hunt Redis 2 | A powerfull redis client for D Programming Language. Port from java [Jedis](https://github.com/xetorthio/jedis) project. 3 | 4 | ## So what can I do with Redis? 5 | All of the following redis features are supported: 6 | 7 | - Sorting 8 | - Connection handling 9 | - Commands operating on any kind of values 10 | - Commands operating on string values 11 | - Commands operating on hashes 12 | - Commands operating on lists 13 | - Commands operating on sets 14 | - Commands operating on sorted sets 15 | - Transactions 16 | - Pipelining 17 | - Publish/Subscribe 18 | - Persistence control commands 19 | - Remote server control commands 20 | - Connection pooling 21 | - Sharding (MD5, MurmurHash) 22 | - Key-tags for sharding 23 | - Sharding with pipelining 24 | - Scripting with pipelining 25 | - Redis Cluster 26 | 27 | ## To use it just: 28 | 29 | ```D 30 | Redis redis = new Redis("localhost"); 31 | redis.set("foo", "bar"); 32 | string value = redis.get("foo"); 33 | ``` 34 | 35 | ## Redis Cluster 36 | 37 | Redis cluster [specification](http://redis.io/topics/cluster-spec) (still under development) is implemented 38 | 39 | ```D 40 | Set!(HostAndPort) redisClusterNodes = new HashSet!(HostAndPort)(); 41 | //Redis Cluster will attempt to discover cluster nodes automatically 42 | redisClusterNodes.add(new HostAndPort("127.0.0.1", 7379)); 43 | RedisCluster rc = new RedisCluster(redisClusterNodes); 44 | rc.set("foo", "bar"); 45 | string value = rc.get("foo"); 46 | ``` 47 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hunt-redis", 3 | "targetType": "library", 4 | "copyright": "Copyright © 2019-2021, HuntLabs", 5 | "description": "A redis client library for D.", 6 | "homepage": "https://www.huntLabs.net", 7 | "license": "Apache-2.0", 8 | "dependencies": { 9 | "hunt-net": "~>0.7.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/SimpleDemo/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/simple-demo", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /examples/SimpleDemo/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-demo", 3 | "description": "All the unittests for hunt-pool.", 4 | "copyright": "Copyright (C) 2019, HuntLabs.net", 5 | "homepage": "https://www.huntlabs.net", 6 | "license": "Apache-2.0", 7 | "dependencies": { 8 | "hunt-redis" :{"path": "../../"} 9 | }, 10 | "versions": [ 11 | "HUNT_DEBUG1", "HUNT_REDIS_DEBUG1", "HUNT_IO_MORE1", "HUNT_CONCURRENCY_DEBUG" 12 | ] 13 | } -------------------------------------------------------------------------------- /examples/SimpleDemo/source/RedisClusterDemo.d: -------------------------------------------------------------------------------- 1 | module RedisClusterDemo; 2 | 3 | import hunt.redis.RedisPoolOptions; 4 | 5 | import hunt.logging.ConsoleLogger; 6 | import hunt.util.DateTime; 7 | 8 | import hunt.redis; 9 | import std.conv; 10 | 11 | enum string RedisServerHost = "10.1.11.115"; 12 | enum int RedisServerPort = 6379; 13 | // enum string RedisPassword = "foobared"; 14 | enum int DEFAULT_TIMEOUT = 2000; 15 | enum int DEFAULT_REDIRECTIONS = 5; 16 | 17 | class RedisClusterBenchmark { 18 | 19 | static void run() { 20 | 21 | Redis node2 = new Redis(new HostAndPort(RedisServerHost, 6380)); 22 | // node2.auth(RedisPassword); 23 | node2.flushAll(); 24 | 25 | Redis node3 = new Redis(new HostAndPort(RedisServerHost, 6381)); 26 | // node3.auth(RedisPassword); 27 | node3.flushAll(); 28 | 29 | // 30 | RedisPoolOptions DEFAULT_CONFIG = new RedisPoolOptions(); 31 | // DEFAULT_CONFIG.password = RedisPassword; 32 | DEFAULT_CONFIG.clientName = "cluster-demo"; 33 | DEFAULT_CONFIG.connectionTimeout = DEFAULT_TIMEOUT; 34 | DEFAULT_CONFIG.soTimeout = DEFAULT_TIMEOUT; 35 | 36 | HostAndPort jedisClusterNode = new HostAndPort(RedisServerHost, RedisServerPort); 37 | 38 | RedisCluster rc = new RedisCluster(jedisClusterNode, DEFAULT_CONFIG); 39 | rc.set("foo", "bar"); 40 | rc.set("test", "test"); 41 | assert("bar" == node3.get("foo")); 42 | assert("test" == node2.get("test")); 43 | 44 | RedisCluster rc2 = new RedisCluster(jedisClusterNode, DEFAULT_CONFIG); 45 | rc2.set("foo", "bar2"); 46 | rc2.set("test", "test2"); 47 | 48 | string foo = node3.get("foo"); 49 | warningf("foo=> %s", foo); 50 | assert("bar2" == foo); 51 | 52 | string test = node2.get("test"); 53 | warningf("test=> %s", test); 54 | assert("test2" == test); 55 | 56 | info("Done."); 57 | rc.close(); 58 | rc2.close(); 59 | node2.close(); 60 | node3.close(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/SimpleDemo/source/RedisDemo.d: -------------------------------------------------------------------------------- 1 | module RedisDemo; 2 | 3 | import hunt.logging.ConsoleLogger; 4 | import hunt.util.DateTime; 5 | 6 | import hunt.redis; 7 | import std.conv; 8 | 9 | class RedisBenchmark { 10 | // private enum int TOTAL_OPERATIONS = 100000; 11 | private enum int TOTAL_OPERATIONS = 1; 12 | 13 | static void run() { 14 | // HostAndPort hnp = HostAndPortUtil.getRedisServers().get(0); 15 | // Redis redis = new Redis(hnp); 16 | // Redis redis = new Redis("10.1.11.115", 6379); 17 | // Redis redis = new Redis("10.1.23.222", 6379); 18 | Redis redis = new Redis("10.1.11.114", 6379); 19 | 20 | redis.connect(); 21 | // redis.auth("foobared"); 22 | redis.flushAll(); 23 | 24 | long begin = DateTime.currentTimeMillis(); 25 | 26 | for (int n = 0; n < TOTAL_OPERATIONS; n++) { 27 | string key = "foo" ~ n.to!string(); 28 | redis.set(key, "bar" ~ n.to!string()); 29 | string v = redis.get(key); 30 | tracef("key=%s, value=%s", key, v); 31 | } 32 | 33 | long elapsed = DateTime.currentTimeMillis() - begin; 34 | 35 | redis.close(); 36 | 37 | if(elapsed > 0) 38 | trace(((1000 * 2 * TOTAL_OPERATIONS) / elapsed).to!string() ~ " ops"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/SimpleDemo/source/RedisLockDemo.d: -------------------------------------------------------------------------------- 1 | module RedisLockDemo; 2 | 3 | import hunt.redis; 4 | 5 | import hunt.logging.ConsoleLogger; 6 | 7 | import core.thread; 8 | import std.stdio; 9 | import std.conv; 10 | 11 | // string RedisHosts = "10.1.11.115:6379;10.1.11.115:6380;10.1.11.115:6381;10.1.11.115:7000;10.1.11.115:7001;10.1.11.115:7002"; 12 | // string RedisHosts = "10.1.23.222:6379"; 13 | 14 | void run() 15 | { 16 | 17 | TestThread[] list; 18 | for (uint i = 0; i < 10; i++) 19 | { 20 | auto test = new TestThread("client " ~ to!string(i), 1); 21 | test.start(); 22 | list ~= test; 23 | } 24 | 25 | Thread.sleep(dur!"seconds"(5)); 26 | 27 | foreach (t; list) 28 | { 29 | t.stop(); 30 | } 31 | 32 | int i = 0; 33 | i++; 34 | // foreach (t; list) 35 | // t.join(); 36 | 37 | } 38 | 39 | class TestThread : Thread 40 | { 41 | string _name; 42 | int _second; 43 | bool _flag; 44 | RedisLock _lock; 45 | Redis _redis; 46 | 47 | this(string name, int second) 48 | { 49 | super(&run); 50 | _name = name; 51 | _second = second; 52 | _redis = new Redis("10.1.23.222", 6379); 53 | _redis.auth("foobared"); 54 | _lock = new RedisLock(_redis, "test1"); 55 | _flag = true; 56 | } 57 | 58 | void stop() 59 | { 60 | if(_flag) { 61 | _flag = false; 62 | _redis.close(); 63 | } 64 | } 65 | 66 | void run() 67 | { 68 | while (_flag) 69 | { 70 | if (!_lock.lock(1000)) 71 | { 72 | warningf(" timeout: %s ", _name); 73 | continue; 74 | } 75 | 76 | info(_name, " locked"); 77 | Thread.sleep(dur!"msecs"(500)); 78 | info(_name, " unlocked"); 79 | _lock.unlock(); 80 | Thread.sleep(dur!"seconds"(1)); 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/SimpleDemo/source/app.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import hunt.net.NetUtil; 3 | import hunt.net.EventLoopPool; 4 | 5 | import RedisDemo; 6 | import RedisClusterDemo; 7 | import RedisLockDemo; 8 | 9 | /** 10 | "versions": [ 11 | "HUNT_DEBUG", "HUNT_NET_DEBUG" 12 | ], 13 | */ 14 | 15 | void main() 16 | { 17 | import hunt.logging.ConsoleLogger; 18 | // for(int i=0; i< 1; i++) { 19 | // RedisBenchmark.run(); 20 | // } 21 | 22 | warning("done"); 23 | 24 | RedisClusterBenchmark.run(); 25 | // RedisLockDemo.run(); 26 | getchar(); 27 | shutdownEventLoopPool(); 28 | } 29 | -------------------------------------------------------------------------------- /examples/UnitTest/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/unittest", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /examples/UnitTest/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unittest", 3 | "description": "All the unittests for hunt-pool.", 4 | "copyright": "Copyright (C) 2019, HuntLabs.net", 5 | "homepage": "https://www.huntlabs.net", 6 | "license": "Apache-2.0", 7 | "dependencies": { 8 | "hunt-redis" :{"path": "../../"} 9 | }, 10 | "versions": [ 11 | "HUNT_DEBUG", "HUNT_REDIS_DEBUG" 12 | ] 13 | } -------------------------------------------------------------------------------- /examples/UnitTest/source/app.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | import hunt.util.UnitTest; 4 | import hunt.logging.ConsoleLogger; 5 | 6 | import test.HostAndPortTest; 7 | import test.RedisTest; 8 | import test.RedisClusterTest; 9 | import test.RedisPoolTest; 10 | import test.utils.RedisURIHelperTest; 11 | 12 | import test.commands.GeoCommandsTest; 13 | import test.commands.SortedSetCommandsTest; 14 | import test.commands.StreamsCommandsTest; 15 | 16 | import hunt.net.EventLoopPool; 17 | 18 | void main() { 19 | 20 | // testUnits!(HostAndPortTest); 21 | // testUnits!(RedisTest); 22 | testUnits!(RedisClusterTest); 23 | // testUnits!(RedisPoolTest); 24 | 25 | // testUnits!(RedisURIHelperTest); 26 | 27 | // testUnits!(GeoCommandsTest); 28 | // testUnits!(SortedSetCommandsTest); 29 | // testUnits!(StreamsCommandsTest); 30 | 31 | getchar(); 32 | shutdownEventLoopPool(); 33 | } 34 | -------------------------------------------------------------------------------- /examples/UnitTest/source/test/HostAndPortTest.d: -------------------------------------------------------------------------------- 1 | module test.HostAndPortTest; 2 | 3 | import hunt.Assert; 4 | import hunt.collection; 5 | import hunt.Exceptions; 6 | import hunt.logging.ConsoleLogger; 7 | import hunt.util.Common; 8 | import hunt.util.UnitTest; 9 | 10 | import hunt.redis.HostAndPort; 11 | 12 | import std.conv; 13 | 14 | 15 | class HostAndPortTest { 16 | @Test 17 | void checkExtractParts() { 18 | string host = "2a11:1b1:0:111:e111:1f11:1111:1f1e:1999"; 19 | string port = "6379"; 20 | 21 | assertArrayEquals([host, port], HostAndPort.extractParts(host ~ ":" ~ port)); 22 | 23 | host = ""; 24 | port = ""; 25 | assertArrayEquals([host, port], HostAndPort.extractParts(host ~ ":" ~ port)); 26 | 27 | host = "localhost"; 28 | port = ""; 29 | assertArrayEquals([host, port], HostAndPort.extractParts(host ~ ":" ~ port)); 30 | 31 | host = ""; 32 | port = "6379"; 33 | assertArrayEquals([host, port], HostAndPort.extractParts(host ~ ":" ~ port)); 34 | 35 | host = "11:22:33:44:55"; 36 | port = ""; 37 | assertArrayEquals([host, port], HostAndPort.extractParts(host ~ ":" ~ port)); 38 | } 39 | 40 | @Test 41 | void checkParseString() { 42 | string host = "2a11:1b1:0:111:e111:1f11:1111:1f1e:1999"; 43 | int port = 6379; 44 | HostAndPort hp = HostAndPort.parseString(host ~ ":" ~ to!string(port)); 45 | assertEquals(host, hp.getHost()); 46 | assertEquals(port, hp.getPort()); 47 | } 48 | 49 | @Test 50 | void checkParseStringWithoutPort() { 51 | try { 52 | string host = "localhost"; 53 | HostAndPort.parseString(host ~ ":"); 54 | } catch(IllegalArgumentException e) { 55 | 56 | } 57 | } 58 | 59 | @Test 60 | void checkConvertHost() { 61 | string host = "2a11:1b1:0:111:e111:1f11:1111:1f1e"; 62 | assertEquals(host, HostAndPort.convertHost(host)); 63 | } 64 | } -------------------------------------------------------------------------------- /examples/UnitTest/source/test/HostAndPortUtil.d: -------------------------------------------------------------------------------- 1 | module test.HostAndPortUtil; 2 | 3 | import hunt.collection; 4 | import hunt.Exceptions; 5 | 6 | import hunt.redis.HostAndPort; 7 | import hunt.redis.Protocol; 8 | 9 | import std.array; 10 | import std.conv; 11 | 12 | final class HostAndPortUtil { 13 | private __gshared List!(HostAndPort) redisHostAndPortList; 14 | private __gshared List!(HostAndPort) sentinelHostAndPortList; 15 | private __gshared List!(HostAndPort) clusterHostAndPortList; 16 | 17 | private this() { 18 | throw new InstantiationError("Must not instantiate this class"); 19 | } 20 | 21 | shared static this() { 22 | redisHostAndPortList = new ArrayList!(HostAndPort)(); 23 | sentinelHostAndPortList = new ArrayList!(HostAndPort)(); 24 | clusterHostAndPortList = new ArrayList!(HostAndPort)(); 25 | 26 | redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT)); 27 | redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 1)); 28 | redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 2)); 29 | redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 3)); 30 | redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 4)); 31 | redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 5)); 32 | redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 6)); 33 | redisHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_PORT + 7)); 34 | 35 | sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT)); 36 | sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 1)); 37 | sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 2)); 38 | sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 3)); 39 | 40 | clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); 41 | clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); 42 | clusterHostAndPortList.add(new HostAndPort("localhost", 7381)); 43 | clusterHostAndPortList.add(new HostAndPort("localhost", 7382)); 44 | clusterHostAndPortList.add(new HostAndPort("localhost", 7383)); 45 | clusterHostAndPortList.add(new HostAndPort("localhost", 7384)); 46 | 47 | // string envRedisHosts = System.getProperty("redis-hosts"); 48 | // string envSentinelHosts = System.getProperty("sentinel-hosts"); 49 | // string envClusterHosts; // = System.getProperty("cluster-hosts"); 50 | 51 | string envRedisHosts = "10.1.11.114:6379, 10.1.11.115:6379"; 52 | string envSentinelHosts = ""; 53 | // string envClusterHosts = "127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:7380,127.0.0.1:7381,127.0.0.1:7382"; 54 | string envClusterHosts = "10.1.11.15:6379,10.1.11.15:6479,10.1.11.15:6579,10.1.11.15:6679,10.1.11.15:6779,10.1.11.15:6879"; 55 | 56 | redisHostAndPortList = parseHosts(envRedisHosts, redisHostAndPortList); 57 | sentinelHostAndPortList = parseHosts(envSentinelHosts, sentinelHostAndPortList); 58 | clusterHostAndPortList = parseHosts(envClusterHosts, clusterHostAndPortList); 59 | } 60 | 61 | static List!(HostAndPort) parseHosts(string envHosts, List!(HostAndPort) existingHostsAndPorts) { 62 | if (!envHosts.empty()) { 63 | string[] hostDefs = envHosts.split(","); 64 | if (2 <= hostDefs.length) { 65 | List!(HostAndPort) envHostsAndPorts = new ArrayList!(HostAndPort)(cast(int)hostDefs.length); 66 | foreach (string hostDef ; hostDefs) { 67 | string[] hostAndPortParts = HostAndPort.extractParts(hostDef); 68 | 69 | if (2 == hostAndPortParts.length) { 70 | string host = hostAndPortParts[0]; 71 | int port = Protocol.DEFAULT_PORT; 72 | try { 73 | port = to!int(hostAndPortParts[1]); 74 | } catch (NumberFormatException nfe) { 75 | } 76 | envHostsAndPorts.add(new HostAndPort(host, port)); 77 | } 78 | } 79 | 80 | return envHostsAndPorts; 81 | } 82 | } 83 | 84 | return existingHostsAndPorts; 85 | } 86 | 87 | static List!(HostAndPort) getRedisServers() { 88 | return redisHostAndPortList; 89 | } 90 | 91 | static List!(HostAndPort) getSentinelServers() { 92 | return sentinelHostAndPortList; 93 | } 94 | 95 | static List!(HostAndPort) getClusterServers() { 96 | return clusterHostAndPortList; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /examples/UnitTest/source/test/commands/RedisCommandTestBase.d: -------------------------------------------------------------------------------- 1 | module test.commands.RedisCommandTestBase; 2 | 3 | import test.HostAndPortUtil; 4 | import test.commands.RedisCommandTestBase; 5 | 6 | import hunt.Assert; 7 | import hunt.collection; 8 | import hunt.Exceptions; 9 | import hunt.logging.ConsoleLogger; 10 | import hunt.util.Common; 11 | import hunt.util.UnitTest; 12 | 13 | import hunt.redis.HostAndPort; 14 | import hunt.redis.Redis; 15 | 16 | 17 | abstract class RedisCommandTestBase { 18 | protected HostAndPort hnp; 19 | protected Redis redis; 20 | 21 | this() { 22 | hnp = HostAndPortUtil.getRedisServers().get(0); 23 | } 24 | 25 | @Before void setUp() { 26 | trace(hnp.toString()); 27 | redis = new Redis(hnp.getHost(), hnp.getPort(), 500); 28 | redis.connect(); 29 | // redis.auth("foobared"); 30 | redis.flushAll(); 31 | } 32 | 33 | @After void tearDown() { 34 | // redis.disconnect(); 35 | redis.close(); 36 | } 37 | 38 | protected Redis createRedis() { 39 | Redis j = new Redis(hnp); 40 | j.connect(); 41 | j.auth("foobared"); 42 | j.flushAll(); 43 | return j; 44 | } 45 | 46 | protected bool arrayContains(List!(byte[]) array, byte[] expected) { 47 | foreach (byte[] a ; array) { 48 | try { 49 | assertArrayEquals(a, expected); 50 | return true; 51 | } catch (AssertionError e) { 52 | 53 | } 54 | } 55 | return false; 56 | } 57 | 58 | protected bool setContains(Set!(byte[]) set, byte[] expected) { 59 | foreach(byte[] a ; set) { 60 | try { 61 | assertArrayEquals(a, expected); 62 | return true; 63 | } catch (AssertionError e) { 64 | 65 | } 66 | } 67 | return false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/UnitTest/source/test/utils/RedisClusterTestUtil.d: -------------------------------------------------------------------------------- 1 | module test.utils.RedisClusterTestUtil; 2 | 3 | import hunt.redis.HostAndPort; 4 | import hunt.redis.Redis; 5 | import hunt.redis.Exceptions; 6 | 7 | import core.thread; 8 | import core.time; 9 | 10 | import hunt.Exceptions; 11 | import hunt.util.DateTime; 12 | 13 | import std.algorithm; 14 | import std.string; 15 | 16 | class RedisClusterTestUtil { 17 | 18 | static void waitForClusterReady(Redis[] nodes...) { 19 | bool clusterOk = false; 20 | while (!clusterOk) { 21 | bool isOk = true; 22 | foreach (Redis node ; nodes) { 23 | if (!node.clusterInfo().split("\n")[0].canFind("ok")) { 24 | isOk = false; 25 | break; 26 | } 27 | } 28 | 29 | if (isOk) { 30 | clusterOk = true; 31 | } 32 | 33 | Thread.sleep(50.msecs); 34 | } 35 | } 36 | 37 | static string getNodeId(string infoOutput) { 38 | foreach (string infoLine ; infoOutput.split("\n")) { 39 | if (infoLine.canFind("myself")) { 40 | return infoLine.split(" ")[0]; 41 | } 42 | } 43 | return ""; 44 | } 45 | 46 | static string getNodeId(string infoOutput, HostAndPort node) { 47 | 48 | foreach (string infoLine ; infoOutput.split("\n")) { 49 | if (infoLine.canFind(node.toString())) { 50 | return infoLine.split(" ")[0]; 51 | } 52 | } 53 | return ""; 54 | } 55 | 56 | static void assertNodeIsKnown(Redis node, string targetNodeId, int timeoutMs) { 57 | assertNodeRecognizedStatus(node, targetNodeId, true, timeoutMs); 58 | } 59 | 60 | static void assertNodeIsUnknown(Redis node, string targetNodeId, int timeoutMs) { 61 | assertNodeRecognizedStatus(node, targetNodeId, false, timeoutMs); 62 | } 63 | 64 | private static void assertNodeRecognizedStatus(Redis node, string targetNodeId, 65 | bool shouldRecognized, int timeoutMs) { 66 | int sleepInterval = 100; 67 | for (int sleepTime = 0; sleepTime <= timeoutMs; sleepTime += sleepInterval) { 68 | bool known = isKnownNode(node, targetNodeId); 69 | if (shouldRecognized == known) return; 70 | 71 | try { 72 | Thread.sleep(sleepInterval.msecs); 73 | } catch (InterruptedException e) { 74 | } 75 | } 76 | 77 | throw new RedisException("Node recognize check error"); 78 | } 79 | 80 | private static bool isKnownNode(Redis node, string nodeId) { 81 | string infoOutput = node.clusterNodes(); 82 | foreach (string infoLine ; infoOutput.split("\n")) { 83 | if (infoLine.canFind(nodeId)) { 84 | return true; 85 | } 86 | } 87 | return false; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /examples/UnitTest/source/test/utils/RedisURIHelperTest.d: -------------------------------------------------------------------------------- 1 | module test.utils.RedisURIHelperTest; 2 | 3 | import hunt.redis.util.RedisURIHelper; 4 | 5 | import hunt.Assert; 6 | import hunt.collection; 7 | import hunt.Exceptions; 8 | import hunt.logging.ConsoleLogger; 9 | import hunt.util.Common; 10 | import hunt.util.UnitTest; 11 | 12 | import hunt.net.util.HttpURI; 13 | 14 | alias URI = HttpURI; 15 | 16 | class RedisURIHelperTest { 17 | 18 | @Test void shouldGetPasswordFromURIWithCredentials() { 19 | URI uri = new URI("redis://user:password@host:9000/0"); 20 | assertEquals("password", RedisURIHelper.getPassword(uri)); 21 | } 22 | 23 | @Test void shouldReturnNullIfURIDoesNotHaveCredentials() { 24 | URI uri = new URI("redis://host:9000/0"); 25 | assertNull(RedisURIHelper.getPassword(uri)); 26 | } 27 | 28 | @Test void shouldGetDbFromURIWithCredentials() { 29 | URI uri = new URI("redis://user:password@host:9000/3"); 30 | assertEquals(3, RedisURIHelper.getDBIndex(uri)); 31 | } 32 | 33 | @Test void shouldGetDbFromURIWithoutCredentials() { 34 | URI uri = new URI("redis://host:9000/4"); 35 | assertEquals(4, RedisURIHelper.getDBIndex(uri)); 36 | } 37 | 38 | @Test void shouldGetDefaultDbFromURIIfNoDbWasSpecified() { 39 | URI uri = new URI("redis://host:9000"); 40 | assertEquals(0, RedisURIHelper.getDBIndex(uri)); 41 | } 42 | 43 | @Test void shouldValidateInvalidURIs() { 44 | assertFalse(RedisURIHelper.isValid(new URI("host:9000"))); 45 | assertFalse(RedisURIHelper.isValid(new URI("user:password@host:9000/0"))); 46 | assertFalse(RedisURIHelper.isValid(new URI("host:9000/0"))); 47 | assertFalse(RedisURIHelper.isValid(new URI("redis://host/0"))); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /hunt-redis.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | }, 6 | { 7 | "path": "examples/UnitTest" 8 | }, 9 | { 10 | "path": "examples/SimpleDemo" 11 | } 12 | ], 13 | "settings": {} 14 | } -------------------------------------------------------------------------------- /source/hunt/redis/BinaryRedisPubSub.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.BinaryRedisPubSub; 13 | 14 | import hunt.redis.Client; 15 | import hunt.redis.Exceptions; 16 | import hunt.redis.Protocol; 17 | 18 | import hunt.util.ArrayHelper; 19 | import hunt.collection.List; 20 | import hunt.Exceptions; 21 | 22 | abstract class BinaryRedisPubSub { 23 | private int subscribedChannels = 0; 24 | private Client client; 25 | 26 | void onMessage(const(ubyte)[] channel, const(ubyte)[] message) { 27 | } 28 | 29 | void onPMessage(const(ubyte)[] pattern, const(ubyte)[] channel, const(ubyte)[] message) { 30 | } 31 | 32 | void onSubscribe(const(ubyte)[] channel, int subscribedChannels) { 33 | } 34 | 35 | void onUnsubscribe(const(ubyte)[] channel, int subscribedChannels) { 36 | } 37 | 38 | void onPUnsubscribe(const(ubyte)[] pattern, int subscribedChannels) { 39 | } 40 | 41 | void onPSubscribe(const(ubyte)[] pattern, int subscribedChannels) { 42 | } 43 | 44 | void unsubscribe() { 45 | client.unsubscribe(); 46 | client.flush(); 47 | } 48 | 49 | void unsubscribe(const(ubyte)[][] channels...) { 50 | client.unsubscribe(channels); 51 | client.flush(); 52 | } 53 | 54 | void subscribe(const(ubyte)[][] channels...) { 55 | client.subscribe(channels); 56 | client.flush(); 57 | } 58 | 59 | void psubscribe(const(ubyte)[][] patterns...) { 60 | client.psubscribe(patterns); 61 | client.flush(); 62 | } 63 | 64 | void punsubscribe() { 65 | client.punsubscribe(); 66 | client.flush(); 67 | } 68 | 69 | void punsubscribe(const(ubyte)[][] patterns...) { 70 | client.punsubscribe(patterns); 71 | client.flush(); 72 | } 73 | 74 | bool isSubscribed() { 75 | return subscribedChannels > 0; 76 | } 77 | 78 | void proceedWithPatterns(Client client, const(ubyte)[][] patterns...) { 79 | this.client = client; 80 | client.psubscribe(patterns); 81 | client.flush(); 82 | process(client); 83 | } 84 | 85 | void proceed(Client client, const(ubyte)[][] channels...) { 86 | this.client = client; 87 | client.subscribe(channels); 88 | client.flush(); 89 | process(client); 90 | } 91 | 92 | private void process(Client client) { 93 | implementationMissing(false); 94 | // do { 95 | // List!(Object) reply = client.getUnflushedObjectMultiBulkReply(); 96 | // Object firstObj = reply.get(0); 97 | // if (!(firstObj instanceof const(ubyte)[])) { 98 | // throw new RedisException("Unknown message type: " ~ firstObj); 99 | // } 100 | // const(ubyte)[] resp = (const(ubyte)[]) firstObj; 101 | // if (Arrays.equals(SUBSCRIBE.raw, resp)) { 102 | // subscribedChannels = ((Long) reply.get(2)).intValue(); 103 | // const(ubyte)[] bchannel = (const(ubyte)[]) reply.get(1); 104 | // onSubscribe(bchannel, subscribedChannels); 105 | // } else if (Arrays.equals(UNSUBSCRIBE.raw, resp)) { 106 | // subscribedChannels = ((Long) reply.get(2)).intValue(); 107 | // const(ubyte)[] bchannel = (const(ubyte)[]) reply.get(1); 108 | // onUnsubscribe(bchannel, subscribedChannels); 109 | // } else if (Arrays.equals(MESSAGE.raw, resp)) { 110 | // const(ubyte)[] bchannel = (const(ubyte)[]) reply.get(1); 111 | // const(ubyte)[] bmesg = (const(ubyte)[]) reply.get(2); 112 | // onMessage(bchannel, bmesg); 113 | // } else if (Arrays.equals(PMESSAGE.raw, resp)) { 114 | // const(ubyte)[] bpattern = (const(ubyte)[]) reply.get(1); 115 | // const(ubyte)[] bchannel = (const(ubyte)[]) reply.get(2); 116 | // const(ubyte)[] bmesg = (const(ubyte)[]) reply.get(3); 117 | // onPMessage(bpattern, bchannel, bmesg); 118 | // } else if (Arrays.equals(PSUBSCRIBE.raw, resp)) { 119 | // subscribedChannels = ((Long) reply.get(2)).intValue(); 120 | // const(ubyte)[] bpattern = (const(ubyte)[]) reply.get(1); 121 | // onPSubscribe(bpattern, subscribedChannels); 122 | // } else if (Arrays.equals(PUNSUBSCRIBE.raw, resp)) { 123 | // subscribedChannels = ((Long) reply.get(2)).intValue(); 124 | // const(ubyte)[] bpattern = (const(ubyte)[]) reply.get(1); 125 | // onPUnsubscribe(bpattern, subscribedChannels); 126 | // } else { 127 | // throw new RedisException("Unknown message type: " ~ firstObj); 128 | // } 129 | // } while (isSubscribed()); 130 | } 131 | 132 | int getSubscribedChannels() { 133 | return subscribedChannels; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /source/hunt/redis/BitOP.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.BitOP; 13 | 14 | enum BitOP { 15 | AND, OR, XOR, NOT 16 | } 17 | -------------------------------------------------------------------------------- /source/hunt/redis/BitPosParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.BitPosParams; 13 | 14 | import hunt.redis.Protocol; 15 | 16 | import hunt.collection.ArrayList; 17 | import hunt.collection.Collection; 18 | import hunt.collection.Collections; 19 | import hunt.collection.List; 20 | 21 | class BitPosParams { 22 | private List!(const(ubyte)[]) params; 23 | 24 | this() { 25 | params = new ArrayList!(const(ubyte)[])(); 26 | } 27 | 28 | this(long start) { 29 | this(); 30 | params.add(Protocol.toByteArray(start)); 31 | } 32 | 33 | this(long start, long end) { 34 | this(start); 35 | 36 | params.add(Protocol.toByteArray(end)); 37 | } 38 | 39 | Collection!(const(ubyte)[]) getParams() { 40 | return params; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/hunt/redis/Builder.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.Builder; 13 | 14 | abstract class Builder(T) { 15 | abstract T build(Object data); 16 | } 17 | -------------------------------------------------------------------------------- /source/hunt/redis/ClusterReset.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.ClusterReset; 13 | 14 | enum ClusterReset { 15 | SOFT, HARD 16 | } 17 | -------------------------------------------------------------------------------- /source/hunt/redis/DebugParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.DebugParams; 13 | 14 | class DebugParams { 15 | private string[] command; 16 | 17 | private this() { 18 | 19 | } 20 | 21 | string[] getCommand() { 22 | return command; 23 | } 24 | 25 | static DebugParams SEGFAULT() { 26 | DebugParams debugParams = new DebugParams(); 27 | debugParams.command = ["SEGFAULT"]; 28 | return debugParams; 29 | } 30 | 31 | static DebugParams OBJECT(string key) { 32 | DebugParams debugParams = new DebugParams(); 33 | debugParams.command = ["OBJECT", key]; 34 | return debugParams; 35 | } 36 | 37 | static DebugParams RELOAD() { 38 | DebugParams debugParams = new DebugParams(); 39 | debugParams.command = ["RELOAD"]; 40 | return debugParams; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/hunt/redis/Exceptions.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.Exceptions; 13 | 14 | import hunt.Exceptions; 15 | 16 | import hunt.redis.HostAndPort; 17 | 18 | class RedisException : RuntimeException { 19 | mixin BasicExceptionCtors; 20 | } 21 | 22 | class InvalidURIException : RedisException { 23 | mixin BasicExceptionCtors; 24 | } 25 | 26 | class RedisConnectionException : RedisException { 27 | mixin BasicExceptionCtors; 28 | } 29 | 30 | class RedisExhaustedPoolException : RedisException { 31 | mixin BasicExceptionCtors; 32 | } 33 | 34 | class RedisNoReachableClusterNodeException : RedisConnectionException { 35 | mixin BasicExceptionCtors; 36 | } 37 | 38 | class RedisClusterOperationException : RedisException { 39 | mixin BasicExceptionCtors; 40 | } 41 | 42 | class RedisDataException : RedisException { 43 | mixin BasicExceptionCtors; 44 | } 45 | 46 | class RedisBusyException : RedisDataException { 47 | mixin BasicExceptionCtors; 48 | } 49 | 50 | class RedisClusterException : RedisDataException { 51 | mixin BasicExceptionCtors; 52 | } 53 | 54 | class RedisNoScriptException : RedisDataException { 55 | mixin BasicExceptionCtors; 56 | } 57 | 58 | 59 | class RedisClusterMaxAttemptsException : RedisClusterOperationException { 60 | mixin BasicExceptionCtors; 61 | } 62 | 63 | class RedisRedirectionException : RedisDataException { 64 | 65 | private HostAndPort targetNode; 66 | private int slot; 67 | 68 | this(string message, HostAndPort targetNode, int slot) { 69 | super(message); 70 | this.targetNode = targetNode; 71 | this.slot = slot; 72 | } 73 | 74 | this(Throwable cause, HostAndPort targetNode, int slot) { 75 | super(cause); 76 | this.targetNode = targetNode; 77 | this.slot = slot; 78 | } 79 | 80 | this(string message, Throwable cause, HostAndPort targetNode, int slot) { 81 | super(message, cause); 82 | this.targetNode = targetNode; 83 | this.slot = slot; 84 | } 85 | 86 | HostAndPort getTargetNode() { 87 | return targetNode; 88 | } 89 | 90 | int getSlot() { 91 | return slot; 92 | } 93 | } 94 | 95 | class RedisMovedDataException : RedisRedirectionException { 96 | this(string message, HostAndPort targetNode, int slot) { 97 | super(message, targetNode, slot); 98 | } 99 | 100 | this(Throwable cause, HostAndPort targetNode, int slot) { 101 | super(cause, targetNode, slot); 102 | } 103 | 104 | this(string message, Throwable cause, HostAndPort targetNode, int slot) { 105 | super(message, cause, targetNode, slot); 106 | } 107 | } 108 | 109 | class RedisAskDataException : RedisRedirectionException { 110 | this(Throwable cause, HostAndPort targetHost, int slot) { 111 | super(cause, targetHost, slot); 112 | } 113 | 114 | this(string message, Throwable cause, HostAndPort targetHost, int slot) { 115 | super(message, cause, targetHost, slot); 116 | } 117 | 118 | this(string message, HostAndPort targetHost, int slot) { 119 | super(message, targetHost, slot); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /source/hunt/redis/GeoCoordinate.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.GeoCoordinate; 13 | 14 | import hunt.Double; 15 | import hunt.util.Comparator; 16 | 17 | import std.conv; 18 | 19 | class GeoCoordinate { 20 | private double longitude; 21 | private double latitude; 22 | 23 | this(double longitude, double latitude) { 24 | this.longitude = longitude; 25 | this.latitude = latitude; 26 | } 27 | 28 | double getLongitude() { 29 | return longitude; 30 | } 31 | 32 | double getLatitude() { 33 | return latitude; 34 | } 35 | 36 | override 37 | bool opEquals(Object o) { 38 | if (o is null) return false; 39 | if (o is this) return true; 40 | 41 | GeoCoordinate that = cast(GeoCoordinate) o; 42 | if(that is null) 43 | return false; 44 | 45 | if (compare(that.longitude, longitude) != 0) return false; 46 | return compare(that.latitude, latitude) == 0; 47 | } 48 | 49 | override 50 | size_t toHash() @trusted nothrow { 51 | // follows IntelliJ default hashCode implementation 52 | int result; 53 | long temp; 54 | temp = cast(long)longitude; // Double.doubleToLongBits(longitude); 55 | result = cast(int) (temp ^ (temp >>> 32)); 56 | temp = cast(long)latitude; // Double.doubleToLongBits(latitude); 57 | result = 31 * result + cast(int) (temp ^ (temp >>> 32)); 58 | return result; 59 | } 60 | 61 | override 62 | string toString() { 63 | return "(" ~ longitude.to!string() ~ "," ~ latitude.to!string() ~ ")"; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /source/hunt/redis/GeoRadiusResponse.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.GeoRadiusResponse; 13 | 14 | import hunt.redis.GeoCoordinate; 15 | 16 | import hunt.redis.util.SafeEncoder; 17 | 18 | class GeoRadiusResponse { 19 | private string member; 20 | private double distance; 21 | private GeoCoordinate coordinate; 22 | 23 | this(string member) { 24 | this.member = member; 25 | } 26 | 27 | void setDistance(double distance) { 28 | this.distance = distance; 29 | } 30 | 31 | void setCoordinate(GeoCoordinate coordinate) { 32 | this.coordinate = coordinate; 33 | } 34 | 35 | string getMember() { 36 | return member; 37 | } 38 | 39 | string getMemberByString() { 40 | return cast(string)SafeEncoder.encode(member); 41 | } 42 | 43 | double getDistance() { 44 | return distance; 45 | } 46 | 47 | GeoCoordinate getCoordinate() { 48 | return coordinate; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/hunt/redis/GeoUnit.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.GeoUnit; 13 | 14 | import hunt.redis.util.SafeEncoder; 15 | 16 | enum GeoUnit { 17 | M, KM, MI, FT 18 | } 19 | 20 | 21 | string toString(GeoUnit value) { 22 | final switch(value) { 23 | case GeoUnit.M: 24 | return "m"; 25 | 26 | case GeoUnit.KM: 27 | return "km"; 28 | 29 | case GeoUnit.MI: 30 | return "mi"; 31 | 32 | case GeoUnit.FT: 33 | return "ft"; 34 | } 35 | } -------------------------------------------------------------------------------- /source/hunt/redis/HostAndPort.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.HostAndPort; 13 | 14 | import hunt.Exceptions; 15 | import hunt.logging.ConsoleLogger; 16 | 17 | import hunt.text.Common; 18 | import hunt.text.StringUtils; 19 | 20 | import std.algorithm; 21 | import std.conv; 22 | import std.socket; 23 | import std.string; 24 | 25 | /** 26 | */ 27 | class HostAndPort { // : Serializable 28 | 29 | __gshared string localhost; 30 | 31 | private string host; 32 | private int port; 33 | 34 | this(string host, int port) { 35 | this.host = host; 36 | this.port = port; 37 | } 38 | 39 | string getHost() { 40 | return host; 41 | } 42 | 43 | int getPort() { 44 | return port; 45 | } 46 | 47 | override bool opEquals(Object obj) { 48 | if (obj is null) 49 | return false; 50 | if (obj is this) 51 | return true; 52 | 53 | HostAndPort hp = cast(HostAndPort) obj; 54 | if (hp is null) 55 | return false; 56 | 57 | string thisHost = convertHost(host); 58 | string hpHost = convertHost(hp.host); 59 | return port == hp.port && thisHost == hpHost; 60 | } 61 | 62 | override size_t toHash() @trusted nothrow { 63 | return 31 * convertHost(host).hashOf() + port; 64 | } 65 | 66 | override string toString() { 67 | return host ~ ":" ~ port.to!string(); 68 | } 69 | 70 | /** 71 | * Splits string into host and port parts. 72 | * string must be in ( host ~ ":" ~ port ) format. 73 | * Port is optional 74 | * @param from string to parse 75 | * @return array of host and port strings 76 | */ 77 | static string[] extractParts(string from) { 78 | int idx = cast(int)from.lastIndexOf(":"); 79 | string host = idx != -1 ? from.substring(0, idx) : from; 80 | string port = idx != -1 ? from.substring(idx + 1) : ""; 81 | return [host, port]; 82 | } 83 | 84 | /** 85 | * Creates HostAndPort instance from string. 86 | * string must be in ( host ~ ":" ~ port ) format. 87 | * Port is mandatory. Can convert host part. 88 | * @see #convertHost(string) 89 | * @param from string to parse 90 | * @return HostAndPort instance 91 | */ 92 | static HostAndPort parseString(string from) { 93 | // NOTE: redis answers with 94 | // '99aa9999aa9a99aa099aaa990aa99a09aa9a9999 9a09:9a9:a090:9a::99a slave 8c88888888cc08088cc8c8c888c88c8888c88cc8 0 1468251272993 37 connected' 95 | // for CLUSTER NODES, ASK and MOVED scenarios. That's why there is no possibility to parse address in 'correct' way. 96 | // Redis should switch to 'bracketized' (RFC 3986) IPv6 address. 97 | try { 98 | string[] parts = extractParts(from); 99 | string host = parts[0]; 100 | int port = to!int(parts[1]); 101 | return new HostAndPort(convertHost(host), port); 102 | } catch (Exception ex) { 103 | throw new IllegalArgumentException(ex); 104 | } 105 | } 106 | 107 | static string convertHost(string host) @trusted nothrow { 108 | try { 109 | /* 110 | * Validate the host name as an IPV4/IPV6 address. 111 | * If this is an AWS ENDPOINT it will not parse. 112 | * In that case accept host as is. 113 | * 114 | * Costs: If this is an IPV4/6 encoding, e.g. 127.0.0.1 then no DNS lookup 115 | * is done. If it is a name then a DNS lookup is done but it is normally cached. 116 | * Secondarily, this class is typically used to create a connection once 117 | * at the beginning of processing and then not used again. So even if the DNS 118 | * lookup needs to be done then the cost is miniscule. 119 | */ 120 | 121 | // FIXME: Needing refactor or cleanup -@zxp at 7/16/2019, 6:35:54 PM 122 | // 123 | Address inetAddress = parseAddress(host); 124 | // InetAddress inetAddress = InetAddress.getByName(host); 125 | // inetAddress.isLoopbackAddress() || 126 | 127 | // isLoopbackAddress() handles both IPV4 and IPV6 128 | if (host == "0.0.0.0" 129 | || host.startsWith("169.254")) 130 | return getLocalhost(); 131 | else 132 | return host; 133 | } catch (Exception e) { 134 | // Not a valid IP address 135 | warning("{}.convertHost '" ~ host ~ "' is not a valid IP address. ", 136 | HostAndPort.stringof, e); 137 | return host; 138 | } 139 | } 140 | 141 | static void setLocalhost(string localhost) { 142 | HostAndPort.localhost = localhost; 143 | } 144 | 145 | /** 146 | * This method resolves the localhost in a 'lazy manner'. 147 | * 148 | * @return localhost 149 | */ 150 | static string getLocalhost() { 151 | if (localhost is null) { 152 | synchronized { 153 | if (localhost is null) { 154 | return localhost = getLocalHostQuietly(); 155 | } 156 | } 157 | } 158 | return localhost; 159 | } 160 | 161 | static string getLocalHostQuietly() { 162 | string localAddress; 163 | try { 164 | // FIXME: Needing refactor or cleanup -@zxp at 7/16/2019, 6:34:32 PM 165 | // 166 | localAddress = "localhost"; // InetAddress.getLocalHost().getHostAddress(); 167 | } catch (Exception ex) { 168 | error("%s.getLocalHostQuietly : cant resolve localhost address", 169 | HostAndPort.stringof, ex); 170 | localAddress = "localhost"; 171 | } 172 | return localAddress; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /source/hunt/redis/ListPosition.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.ListPosition; 13 | 14 | enum ListPosition { 15 | BEFORE, AFTER 16 | } 17 | -------------------------------------------------------------------------------- /source/hunt/redis/Module.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.Module; 13 | 14 | class Module { 15 | private string name; 16 | private int ver; 17 | 18 | this(string name, int ver) { 19 | this.name = name; 20 | this.ver = ver; 21 | } 22 | 23 | 24 | string getName() { 25 | return name; 26 | } 27 | 28 | int getVersion() { 29 | return ver; 30 | } 31 | 32 | override 33 | bool opEquals(Object o) { 34 | if (o is null) return false; 35 | if (o is this) return true; 36 | 37 | Module mod = cast(Module) o; 38 | if(mod is null) 39 | return false; 40 | 41 | if (ver != mod.ver) return false; 42 | return name == mod.name; 43 | 44 | } 45 | 46 | override 47 | size_t toHash() @trusted nothrow { 48 | size_t result = name !is null ? name.hashOf() : 0; 49 | result = 31 * result + ver; 50 | return result; 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /source/hunt/redis/Pipeline.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.Pipeline; 13 | 14 | import hunt.redis.Builder; 15 | import hunt.redis.BuilderFactory; 16 | import hunt.redis.Client; 17 | import hunt.redis.Exceptions; 18 | import hunt.redis.MultiKeyPipelineBase; 19 | import hunt.redis.Response; 20 | 21 | import hunt.util.Common; 22 | import hunt.collection; 23 | 24 | 25 | import std.conv; 26 | 27 | class Pipeline : MultiKeyPipelineBase, Closeable { 28 | 29 | private MultiResponseBuilder currentMulti; 30 | 31 | private class MultiResponseBuilder : Builder!(List!(Object)) { 32 | private List!AbstractResponse responses; 33 | 34 | this() { 35 | responses = new ArrayList!AbstractResponse(); 36 | } 37 | 38 | override 39 | List!(Object) build(Object data) { 40 | 41 | List!(Object) list = cast(List!(Object)) data; 42 | List!(Object) values = new ArrayList!(Object)(); 43 | 44 | if (list.size() != responses.size()) { 45 | throw new RedisDataException("Expected data size " ~ responses.size().to!string() ~ " but was " 46 | ~ list.size().to!string()); 47 | } 48 | 49 | for (int i = 0; i < list.size(); i++) { 50 | // FIXME: Needing refactor or cleanup -@zxp at 7/16/2019, 8:15:19 PM 51 | // 52 | AbstractResponse response = responses.get(i); 53 | response.set(list.get(i)); 54 | Object builtResponse; 55 | try { 56 | builtResponse = response; 57 | } catch (RedisDataException e) { 58 | builtResponse = e; 59 | } 60 | values.add(builtResponse); 61 | } 62 | return values; 63 | } 64 | 65 | void setResponseDependency(AbstractResponse dependency) { 66 | foreach(AbstractResponse response ; responses) { 67 | response.setDependency(dependency); 68 | } 69 | } 70 | 71 | void addResponse(AbstractResponse response) { 72 | responses.add(response); 73 | } 74 | } 75 | 76 | // override 77 | // protected Response!(T) getResponse(Builder!(T) builder) { 78 | // if (currentMulti !is null) { 79 | // super.getResponse(BuilderFactory.STRING); // Expected QUEUED 80 | 81 | // Response!(T) lr = new Response!(T)(builder); 82 | // currentMulti.addResponse(lr); 83 | // return lr; 84 | // } else { 85 | // return super.getResponse(builder); 86 | // } 87 | // } 88 | 89 | void setClient(Client client) { 90 | this.client = client; 91 | } 92 | 93 | override 94 | protected Client getClient(string key) { 95 | return client; 96 | } 97 | 98 | override 99 | protected Client getClient(const(ubyte)[] key) { 100 | return client; 101 | } 102 | 103 | void clear() { 104 | if (isInMulti()) { 105 | discard(); 106 | } 107 | 108 | sync(); 109 | } 110 | 111 | bool isInMulti() { 112 | return currentMulti !is null; 113 | } 114 | 115 | /** 116 | * Synchronize pipeline by reading all responses. This operation close the pipeline. In order to 117 | * get return values from pipelined commands, capture the different Response<?> of the 118 | * commands you execute. 119 | */ 120 | void sync() { 121 | if (getPipelinedResponseLength() > 0) { 122 | List!(Object) unformatted = client.getMany(getPipelinedResponseLength()); 123 | foreach(Object o ; unformatted) { 124 | generateResponse(o); 125 | } 126 | } 127 | } 128 | 129 | /** 130 | * Synchronize pipeline by reading all responses. This operation close the pipeline. Whenever 131 | * possible try to avoid using this version and use Pipeline.sync() as it won't go through all the 132 | * responses and generate the right response type (usually it is a waste of time). 133 | * @return A list of all the responses in the order you executed them. 134 | */ 135 | List!(Object) syncAndReturnAll() { 136 | if (getPipelinedResponseLength() > 0) { 137 | List!(Object) unformatted = client.getMany(getPipelinedResponseLength()); 138 | List!(Object) formatted = new ArrayList!(Object)(); 139 | foreach(Object o ; unformatted) { 140 | try { 141 | formatted.add(generateResponse(o)); 142 | } catch (RedisDataException e) { 143 | formatted.add(e); 144 | } 145 | } 146 | return formatted; 147 | } else { 148 | return Collections.emptyList!Object(); 149 | } 150 | } 151 | 152 | Response!(string) discard() { 153 | if (currentMulti is null) throw new RedisDataException("DISCARD without MULTI"); 154 | client.discard(); 155 | currentMulti = null; 156 | return getResponse(BuilderFactory.STRING); 157 | } 158 | 159 | Response!(List!(Object)) exec() { 160 | if (currentMulti is null) throw new RedisDataException("EXEC without MULTI"); 161 | 162 | client.exec(); 163 | Response!(List!(Object)) response = super.getResponse(currentMulti); 164 | currentMulti.setResponseDependency(response); 165 | currentMulti = null; 166 | return response; 167 | } 168 | 169 | Response!(string) multi() { 170 | if (currentMulti !is null) throw new RedisDataException("MULTI calls can not be nested"); 171 | 172 | client.multi(); 173 | Response!(string) response = getResponse(BuilderFactory.STRING); // Expecting 174 | // OK 175 | currentMulti = new MultiResponseBuilder(); 176 | return response; 177 | } 178 | 179 | override 180 | void close() { 181 | clear(); 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /source/hunt/redis/Queable.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.Queable; 13 | 14 | import hunt.redis.Builder; 15 | import hunt.redis.Response; 16 | 17 | import hunt.collection.LinkedList; 18 | import hunt.collection.Queue; 19 | 20 | class Queable { 21 | private Queue!AbstractResponse pipelinedResponses; 22 | 23 | this() { 24 | pipelinedResponses = new LinkedList!AbstractResponse(); 25 | } 26 | 27 | protected void clean() { 28 | pipelinedResponses.clear(); 29 | } 30 | 31 | protected AbstractResponse generateResponse(Object data) { 32 | AbstractResponse response = pipelinedResponses.poll(); 33 | if (response !is null) { 34 | response.set(data); 35 | } 36 | return response; 37 | } 38 | 39 | protected Response!(T) getResponse(T)(Builder!(T) builder) { 40 | Response!(T) lr = new Response!(T)(builder); 41 | pipelinedResponses.add(lr); 42 | return lr; 43 | } 44 | 45 | bool hasPipelinedResponse() { 46 | return !pipelinedResponses.isEmpty(); 47 | } 48 | 49 | protected int getPipelinedResponseLength() { 50 | return pipelinedResponses.size(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /source/hunt/redis/RedisClusterCommand.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.RedisClusterCommand; 13 | 14 | import hunt.redis.Redis; 15 | import hunt.redis.RedisClusterConnectionHandler; 16 | import hunt.redis.Exceptions; 17 | import hunt.redis.util.RedisClusterCRC16; 18 | 19 | abstract class RedisClusterCommand(T) { 20 | 21 | private RedisClusterConnectionHandler connectionHandler; 22 | private int maxAttempts; 23 | 24 | this(RedisClusterConnectionHandler connectionHandler, int maxAttempts) { 25 | this.connectionHandler = connectionHandler; 26 | this.maxAttempts = maxAttempts; 27 | } 28 | 29 | abstract T execute(Redis connection); 30 | 31 | T run(string key) { 32 | return runWithRetries(RedisClusterCRC16.getSlot(key), this.maxAttempts, false, null); 33 | } 34 | 35 | T run(string[] keys...) { // int keyCount, 36 | if (keys is null || keys.length == 0) { 37 | throw new RedisClusterOperationException("No way to dispatch this command to Redis Cluster."); 38 | } 39 | int keyCount = cast(int)keys.length; 40 | 41 | // For multiple keys, only execute if they all share the same connection slot. 42 | int slot = RedisClusterCRC16.getSlot(keys[0]); 43 | if (keys.length > 1) { 44 | for (int i = 1; i < keyCount; i++) { 45 | int nextSlot = RedisClusterCRC16.getSlot(keys[i]); 46 | if (slot != nextSlot) { 47 | throw new RedisClusterOperationException("No way to dispatch this command to Redis " 48 | ~ "Cluster because keys have different slots."); 49 | } 50 | } 51 | } 52 | 53 | return runWithRetries(slot, this.maxAttempts, false, null); 54 | } 55 | 56 | T runBinary(const(ubyte)[] key) { 57 | return runWithRetries(RedisClusterCRC16.getSlot(key), this.maxAttempts, false, null); 58 | } 59 | 60 | T runBinary(const(ubyte)[][] keys...) { // int keyCount, 61 | if (keys is null || keys.length == 0) { 62 | throw new RedisClusterOperationException("No way to dispatch this command to Redis Cluster."); 63 | } 64 | 65 | int keyCount = cast(int)keys.length; 66 | 67 | // For multiple keys, only execute if they all share the same connection slot. 68 | int slot = RedisClusterCRC16.getSlot(keys[0]); 69 | if (keys.length > 1) { 70 | for (int i = 1; i < keyCount; i++) { 71 | int nextSlot = RedisClusterCRC16.getSlot(keys[i]); 72 | if (slot != nextSlot) { 73 | throw new RedisClusterOperationException("No way to dispatch this command to Redis " 74 | ~ "Cluster because keys have different slots."); 75 | } 76 | } 77 | } 78 | 79 | return runWithRetries(slot, this.maxAttempts, false, null); 80 | } 81 | 82 | T runWithAnyNode() { 83 | Redis connection = null; 84 | try { 85 | connection = connectionHandler.getConnection(); 86 | return execute(connection); 87 | } catch (RedisConnectionException e) { 88 | throw e; 89 | } finally { 90 | releaseConnection(connection); 91 | } 92 | } 93 | 94 | private T runWithRetries(int slot, int attempts, bool tryRandomNode, RedisRedirectionException redirect) { 95 | if (attempts <= 0) { 96 | throw new RedisClusterMaxAttemptsException("No more cluster attempts left."); 97 | } 98 | 99 | Redis connection = null; 100 | try { 101 | 102 | if (redirect !is null) { 103 | connection = this.connectionHandler.getConnectionFromNode(redirect.getTargetNode()); 104 | RedisAskDataException ex = cast(RedisAskDataException)redirect; 105 | if (ex !is null) { 106 | // TODO: Pipeline asking with the original command to make it[] faster.... 107 | connection.asking(); 108 | } 109 | } else { 110 | if (tryRandomNode) { 111 | connection = connectionHandler.getConnection(); 112 | } else { 113 | connection = connectionHandler.getConnectionFromSlot(slot); 114 | } 115 | } 116 | 117 | return execute(connection); 118 | 119 | } catch (RedisNoReachableClusterNodeException jnrcne) { 120 | throw jnrcne; 121 | } catch (RedisConnectionException jce) { 122 | // release current connection before recursion 123 | releaseConnection(connection); 124 | connection = null; 125 | 126 | if (attempts <= 1) { 127 | //We need this because if node is not reachable anymore - we need to finally initiate slots 128 | //renewing, or we can stuck with cluster state without one node in opposite case. 129 | //But now if maxAttempts = [1 or 2] we will do it too often. 130 | //TODO make tracking of successful/unsuccessful operations for node - do renewing only 131 | //if there were no successful responses from this node last few seconds 132 | this.connectionHandler.renewSlotCache(); 133 | } 134 | 135 | return runWithRetries(slot, attempts - 1, tryRandomNode, redirect); 136 | } catch (RedisRedirectionException jre) { 137 | // if MOVED redirection occurred, 138 | RedisMovedDataException ex = cast(RedisMovedDataException)jre; 139 | if (jre !is null) { 140 | // it rebuilds cluster's slot cache recommended by Redis cluster specification 141 | this.connectionHandler.renewSlotCache(connection); 142 | } 143 | 144 | // release current connection before recursion 145 | releaseConnection(connection); 146 | connection = null; 147 | 148 | return runWithRetries(slot, attempts - 1, false, jre); 149 | } finally { 150 | releaseConnection(connection); 151 | } 152 | } 153 | 154 | private void releaseConnection(Redis connection) { 155 | if (connection !is null) { 156 | connection.close(); 157 | } 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /source/hunt/redis/RedisClusterConnectionHandler.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.RedisClusterConnectionHandler; 13 | 14 | import hunt.redis.Exceptions; 15 | import hunt.redis.HostAndPort; 16 | import hunt.redis.Redis; 17 | import hunt.redis.RedisClusterInfoCache; 18 | import hunt.redis.RedisPool; 19 | import hunt.redis.RedisPoolOptions; 20 | 21 | import hunt.collection.Map; 22 | import hunt.collection.Set; 23 | import hunt.logging.ConsoleLogger; 24 | import hunt.util.Common; 25 | import hunt.util.pool; 26 | 27 | 28 | abstract class RedisClusterConnectionHandler : Closeable { 29 | protected RedisClusterInfoCache cache; 30 | 31 | 32 | this(HostAndPort[] nodes, RedisPoolOptions poolConfig) { 33 | // this(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName, false, null, null, null, null); 34 | 35 | this.cache = new RedisClusterInfoCache(poolConfig); 36 | initializeSlotsCache(nodes, poolConfig.connectionTimeout, 37 | poolConfig.soTimeout, poolConfig.password, poolConfig.name, poolConfig.ssl); 38 | } 39 | 40 | // this(Set!(HostAndPort) nodes, 41 | // PoolOptions poolConfig, int connectionTimeout, int soTimeout, string password, string clientName, 42 | // bool ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, 43 | // HostnameVerifier hostnameVerifier, RedisClusterHostAndPortMap portMap) { 44 | // this.cache = new RedisClusterInfoCache(poolConfig, connectionTimeout, soTimeout, password, clientName, 45 | // ssl, sslSocketFactory, sslParameters, hostnameVerifier, portMap); 46 | // initializeSlotsCache(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier); 47 | // } 48 | 49 | abstract Redis getConnection(); 50 | 51 | abstract Redis getConnectionFromSlot(int slot); 52 | 53 | Redis getConnectionFromNode(HostAndPort node) { 54 | return cache.setupNodeIfNotExist(node).borrow(); 55 | } 56 | 57 | Map!(string, RedisPool) getNodes() { 58 | return cache.getNodes(); 59 | } 60 | 61 | // private void initializeSlotsCache(Set!(HostAndPort) startNodes, PoolOptions poolConfig, 62 | // int connectionTimeout, int soTimeout, string password, string clientName, 63 | // bool ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) 64 | 65 | private void initializeSlotsCache(HostAndPort[] startNodes, 66 | int connectionTimeout, int soTimeout, string password, string clientName, bool ssl) { 67 | foreach (HostAndPort hostAndPort; startNodes) { 68 | Redis redis = null; 69 | scope(exit) { 70 | if (redis !is null) { 71 | redis.close(); 72 | } 73 | } 74 | 75 | try { 76 | // redis = new Redis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier); 77 | redis = new Redis(hostAndPort.getHost(), hostAndPort.getPort(), 78 | connectionTimeout, soTimeout, ssl); 79 | if (password !is null) { 80 | redis.auth(password); 81 | } 82 | if (clientName !is null) { 83 | redis.clientSetname(clientName); 84 | } 85 | cache.discoverClusterNodesAndSlots(redis); 86 | break; 87 | } catch (RedisConnectionException ex) { 88 | // try next nodes 89 | debug warning(ex.msg); 90 | version(HUNT_REDIS_DEBUG) warning(ex); 91 | } catch(Exception ex) { 92 | version(HUNT_REDIS_DEBUG) warning(ex); 93 | } 94 | } 95 | } 96 | 97 | void renewSlotCache() { 98 | cache.renewClusterSlots(null); 99 | } 100 | 101 | void renewSlotCache(Redis redis) { 102 | cache.renewClusterSlots(redis); 103 | } 104 | 105 | override void close() { 106 | cache.reset(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /source/hunt/redis/RedisClusterHostAndPortMap.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.RedisClusterHostAndPortMap; 13 | 14 | import hunt.redis.HostAndPort; 15 | 16 | interface RedisClusterHostAndPortMap { 17 | HostAndPort getSSLHostAndPort(string host, int port); 18 | } 19 | -------------------------------------------------------------------------------- /source/hunt/redis/RedisLock.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.RedisLock; 13 | 14 | import std.array; 15 | import std.string; 16 | import std.random; 17 | import std.conv; 18 | import std.stdio; 19 | import std.uuid; 20 | 21 | import core.time; 22 | import core.thread; 23 | import core.stdc.stdlib; 24 | 25 | import hunt.logging.ConsoleLogger; 26 | import hunt.redis.Redis; 27 | import hunt.redis.params.SetParams; 28 | 29 | 30 | struct LockedObject { 31 | string key; 32 | string uniqueId; 33 | size_t validTime; 34 | } 35 | 36 | /** 37 | https://redis.io/topics/distlock 38 | https://github.com/abelaska/jedis-lock 39 | */ 40 | class RedisLock { 41 | 42 | private Redis[] _redisClients; 43 | private LockedObject _lock; 44 | private string _lockKey; 45 | 46 | 47 | this(Redis client, string lockKey, int delaytime = 10, float clockFactor = 0.01f) { 48 | this([client], lockKey, delaytime, clockFactor); 49 | } 50 | 51 | this(Redis[] client, string lockKey, int delaytime = 10, float clockFactor = 0.01f) { 52 | _redisClients = client; 53 | _lockKey = lockKey; 54 | 55 | _quornum = _redisClients.length / 2 + 1; 56 | _delaytime = delaytime; 57 | _clockFactor = clockFactor; 58 | } 59 | 60 | bool lock(uint timeout = uint.max, uint ttl = 60000) { 61 | string key = _lockKey; 62 | string val = to!string(randomUUID()); 63 | auto end_tick = nsecsToTicks(cast(long) timeout * 1000 * 1000) + MonoTime.currTime.ticks(); 64 | synchronized (this) { 65 | _lock.key = key; 66 | _lock.uniqueId = val; 67 | 68 | do { 69 | size_t n = 0; 70 | auto t1 = MonoTime.currTime.ticks(); 71 | foreach (c; _redisClients) { 72 | if (lockInstance(c, key, val, ttl)) 73 | ++n; 74 | } 75 | 76 | auto t2 = MonoTime.currTime.ticks(); 77 | auto clockdrat = cast(size_t)(_clockFactor * ttl) + 2; 78 | auto validtime = ttl - ticksToNSecs(t2 - t1) / 1000 - clockdrat; 79 | 80 | version(HUNT_REDIS_DEBUG) { 81 | tracef("validtime=%d, n=%d, _quornum=%d", validtime, n, _quornum); 82 | } 83 | 84 | 85 | if (validtime > 0 && n >= _quornum) { 86 | _lock.validTime = cast(size_t)validtime; 87 | return true; 88 | } else { 89 | unlock(); 90 | } 91 | size_t delay = rand() % _delaytime + _delaytime / 2; 92 | Thread.sleep(dur!"msecs"(delay)); 93 | } while (MonoTime.currTime.ticks() < end_tick); 94 | 95 | return false; 96 | } 97 | 98 | } 99 | 100 | void unlock() { 101 | synchronized (this) { 102 | foreach (c; _redisClients) { 103 | unlockInstance(c, _lock.key, _lock.uniqueId); 104 | } 105 | } 106 | } 107 | 108 | private static bool lockInstance(Redis redis, string key, string value, long ttl) { 109 | try { 110 | SetParams para = new SetParams(); 111 | string r = redis.set(_prefix ~ key, value, para.nx().px(ttl)); 112 | return r == "OK"; 113 | } catch (Throwable e) { 114 | warning(e); 115 | return false; 116 | } 117 | } 118 | 119 | private static void unlockInstance(Redis redis, string key, string value) { 120 | try { 121 | Object r = redis.eval(`if redis.call('get', KEYS[1]) == ARGV[1] 122 | then return redis.call('del', KEYS[1]) 123 | else 124 | return 0 125 | end`, 126 | [_prefix ~ key], [value]); 127 | } catch (Throwable e) { 128 | warning(e); 129 | } 130 | 131 | } 132 | 133 | 134 | immutable size_t _quornum; 135 | immutable size_t _delaytime = 10; 136 | immutable float _clockFactor = 0.01; 137 | 138 | static immutable string _prefix = "hunt_redlock_"; 139 | } 140 | 141 | -------------------------------------------------------------------------------- /source/hunt/redis/RedisMonitor.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.RedisMonitor; 13 | 14 | import hunt.redis.Client; 15 | 16 | abstract class RedisMonitor { 17 | protected Client client; 18 | 19 | void proceed(Client client) { 20 | this.client = client; 21 | this.client.setTimeoutInfinite(); 22 | do { 23 | string command = client.getBulkReply(); 24 | onCommand(command); 25 | } while (client.isConnected()); 26 | } 27 | 28 | abstract void onCommand(string command); 29 | } -------------------------------------------------------------------------------- /source/hunt/redis/RedisPool.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.RedisPool; 13 | 14 | import hunt.redis.Exceptions; 15 | import hunt.redis.Redis; 16 | import hunt.redis.RedisPoolOptions; 17 | import hunt.redis.Protocol; 18 | 19 | // import hunt.pool.impl.GenericObjectPool; 20 | // import hunt.util.pool; 21 | 22 | // import hunt.redis.util.RedisURIHelper; 23 | // import hunt.net.util.HttpURI; 24 | 25 | import hunt.logging.ConsoleLogger; 26 | import hunt.util.pool; 27 | 28 | import core.atomic; 29 | 30 | import std.conv; 31 | import std.range; 32 | 33 | alias RedisPool = ObjectPool!Redis; 34 | 35 | /** 36 | * 37 | */ 38 | class RedisFactory : ObjectFactory!(Redis) { 39 | private RedisPool _pool; 40 | private shared int counter = 0; 41 | private RedisPoolOptions _options; 42 | 43 | this(RedisPoolOptions options) { 44 | _options = options; 45 | _pool = new RedisPool(this, options); 46 | } 47 | 48 | RedisPool pool() { 49 | return _pool; 50 | } 51 | 52 | override Redis makeObject() { 53 | int c = atomicOp!("+=")(counter, 1); 54 | string name = "RedisObject-" ~ c.to!string(); 55 | version(HUNT_DEBUG) { 56 | tracef("Making a Redis: %s", name); 57 | } 58 | 59 | Redis redis = new PooledRedis(_options); 60 | 61 | version(HUNT_DEBUG) { 62 | infof("Redis making finished: %s", name); 63 | } 64 | 65 | try { 66 | redis.connect(); 67 | 68 | if (!_options.password.empty) { 69 | redis.auth(_options.password); 70 | } 71 | 72 | if (_options.database != 0) { 73 | redis.select(_options.database); 74 | } 75 | if (_options.clientName !is null) { 76 | redis.clientSetname(_options.clientName); 77 | } 78 | } catch (RedisException je) { 79 | debug warning(je.msg); 80 | version(HUNT_DEBUG) warning(je); 81 | redis.close(); 82 | throw je; 83 | } 84 | 85 | return redis; 86 | } 87 | 88 | override void destroyObject(Redis redis) { 89 | if(redis is null) { 90 | warning("The Redis is null"); 91 | return; 92 | } 93 | version(HUNT_DEBUG) { 94 | tracef("Redis [%s] destroying.", redis.toString()); 95 | } 96 | 97 | if (!redis.isConnected()) return; 98 | 99 | try { 100 | try { 101 | redis.quit(); 102 | } catch (Exception e) { 103 | warning(e); 104 | } 105 | // redis.disconnect(); 106 | // redis.close(); 107 | } catch (Exception e) { 108 | warning(e); 109 | } 110 | } 111 | 112 | override bool isValid(Redis p) { 113 | if(p is null) { 114 | return false; 115 | } else { 116 | return p.isConnected(); 117 | } 118 | } 119 | 120 | /** 121 | * 122 | */ 123 | class PooledRedis : Redis { 124 | 125 | this(RedisPoolOptions options) { 126 | super(options.host, options.port, 127 | options.connectionTimeout, options.soTimeout, options.ssl); 128 | } 129 | 130 | override string quit() { 131 | ObjectPoolState state = _pool.state(); 132 | version(HUNT_DEBUG) { 133 | tracef("Quiting Redis, Pool state: %s", state); 134 | } 135 | 136 | if(state == ObjectPoolState.Open) { 137 | _pool.returnObject(this); 138 | return "Rejected by Pool"; 139 | } else { 140 | return super.quit(); 141 | } 142 | } 143 | 144 | override void close() { 145 | ObjectPoolState state = _pool.state(); 146 | version(HUNT_POOL_DEBUG) { 147 | tracef("Closing Redis, Pool state: %s", state); 148 | } 149 | 150 | if(state == ObjectPoolState.Open) { 151 | _pool.returnObject(this); 152 | } else { 153 | super.close(); 154 | } 155 | } 156 | } 157 | } 158 | 159 | 160 | @property RedisPool defaultRedisPool() @trusted { 161 | import std.concurrency : initOnce; 162 | 163 | __gshared RedisPool pool; 164 | return initOnce!pool({ 165 | RedisPoolOptions config = defalutPoolConfig(); 166 | RedisFactory factory = new RedisFactory(config); 167 | return factory.pool(); 168 | }()); 169 | } 170 | 171 | private __gshared RedisPoolOptions _defalutPoolConfig; 172 | 173 | RedisPoolOptions defalutPoolConfig() { 174 | if(_defalutPoolConfig is null) { 175 | _defalutPoolConfig = new RedisPoolOptions(); 176 | } 177 | return _defalutPoolConfig; 178 | } 179 | 180 | 181 | void defalutPoolConfig(RedisPoolOptions config) { 182 | _defalutPoolConfig = config; 183 | } -------------------------------------------------------------------------------- /source/hunt/redis/RedisPoolOptions.d: -------------------------------------------------------------------------------- 1 | module hunt.redis.RedisPoolOptions; 2 | 3 | import hunt.redis.Protocol; 4 | import hunt.util.pool.ObjectPool; 5 | 6 | import std.format; 7 | 8 | /** 9 | * 10 | */ 11 | struct RedisClusterConfig { 12 | string[] nodes; 13 | uint redirections = 5; 14 | } 15 | 16 | /** 17 | * 18 | */ 19 | class RedisPoolOptions : PoolOptions { 20 | 21 | string host = Protocol.DEFAULT_HOST; 22 | 23 | int port = Protocol.DEFAULT_PORT; 24 | 25 | int connectionTimeout = Protocol.DEFAULT_TIMEOUT; 26 | 27 | int soTimeout = Protocol.DEFAULT_TIMEOUT; 28 | 29 | string password; 30 | 31 | int database = Protocol.DEFAULT_DATABASE; 32 | 33 | string clientName = ""; 34 | 35 | int maxAttempts = Protocol.DEFAULT_MAX_ATTEMPTS; 36 | 37 | bool ssl = false; 38 | 39 | this() { 40 | name = "RedisPool"; 41 | clientName = "default-client"; 42 | } 43 | 44 | this(RedisPoolOptions other) { 45 | host = other.host; 46 | port = other.port; 47 | connectionTimeout = other.connectionTimeout; 48 | soTimeout = other.soTimeout; 49 | password = other.password; 50 | database = other.database; 51 | clientName = other.clientName; 52 | maxAttempts = other.maxAttempts; 53 | ssl = other.ssl; 54 | } 55 | 56 | override string toString() { 57 | return format("Host: %s:%d Timeout: %s", host, port, connectionTimeout); 58 | } 59 | } -------------------------------------------------------------------------------- /source/hunt/redis/RedisPubSub.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.RedisPubSub; 13 | 14 | import hunt.redis.Client; 15 | import hunt.redis.Protocol; 16 | import hunt.redis.Exceptions; 17 | import hunt.redis.util.SafeEncoder; 18 | 19 | import hunt.Exceptions; 20 | import hunt.util.ArrayHelper; 21 | import hunt.collection.List; 22 | 23 | abstract class RedisPubSub { 24 | 25 | private enum string JEDIS_SUBSCRIPTION_MESSAGE = "RedisPubSub is not subscribed to a Redis instance."; 26 | private int subscribedChannels = 0; 27 | private Client client; 28 | 29 | void onMessage(string channel, string message) { 30 | } 31 | 32 | void onPMessage(string pattern, string channel, string message) { 33 | } 34 | 35 | void onSubscribe(string channel, int subscribedChannels) { 36 | } 37 | 38 | void onUnsubscribe(string channel, int subscribedChannels) { 39 | } 40 | 41 | void onPUnsubscribe(string pattern, int subscribedChannels) { 42 | } 43 | 44 | void onPSubscribe(string pattern, int subscribedChannels) { 45 | } 46 | 47 | void onPong(string pattern) { 48 | 49 | } 50 | 51 | void unsubscribe() { 52 | if (client is null) { 53 | throw new RedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); 54 | } 55 | client.unsubscribe(); 56 | client.flush(); 57 | } 58 | 59 | void unsubscribe(string[] channels...) { 60 | if (client is null) { 61 | throw new RedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); 62 | } 63 | client.unsubscribe(channels); 64 | client.flush(); 65 | } 66 | 67 | void subscribe(string[] channels...) { 68 | if (client is null) { 69 | throw new RedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); 70 | } 71 | client.subscribe(channels); 72 | client.flush(); 73 | } 74 | 75 | void psubscribe(string[] patterns...) { 76 | if (client is null) { 77 | throw new RedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); 78 | } 79 | client.psubscribe(patterns); 80 | client.flush(); 81 | } 82 | 83 | void punsubscribe() { 84 | if (client is null) { 85 | throw new RedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); 86 | } 87 | client.punsubscribe(); 88 | client.flush(); 89 | } 90 | 91 | void punsubscribe(string[] patterns...) { 92 | if (client is null) { 93 | throw new RedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); 94 | } 95 | client.punsubscribe(patterns); 96 | client.flush(); 97 | } 98 | 99 | void ping() { 100 | if (client is null) { 101 | throw new RedisConnectionException(JEDIS_SUBSCRIPTION_MESSAGE); 102 | } 103 | client.ping(); 104 | client.flush(); 105 | } 106 | 107 | bool isSubscribed() { 108 | return subscribedChannels > 0; 109 | } 110 | 111 | void proceedWithPatterns(Client client, string[] patterns...) { 112 | this.client = client; 113 | client.psubscribe(patterns); 114 | client.flush(); 115 | process(client); 116 | } 117 | 118 | void proceed(Client client, string[] channels...) { 119 | this.client = client; 120 | client.subscribe(channels); 121 | client.flush(); 122 | process(client); 123 | } 124 | 125 | private void process(Client client) { 126 | 127 | implementationMissing(false); 128 | // do { 129 | // List!(Object) reply = client.getUnflushedObjectMultiBulkReply(); 130 | // Object firstObj = reply.get(0); 131 | // if (!(firstObj instanceof byte[])) { 132 | // throw new RedisException("Unknown message type: " ~ firstObj); 133 | // } 134 | // byte[] resp = (byte[]) firstObj; 135 | // if (Arrays.equals(SUBSCRIBE.raw, resp)) { 136 | // subscribedChannels = ((Long) reply.get(2)).intValue(); 137 | // byte[] bchannel = (byte[]) reply.get(1); 138 | // string strchannel = (bchannel is null) ? null : SafeEncoder.encode(bchannel); 139 | // onSubscribe(strchannel, subscribedChannels); 140 | // } else if (Arrays.equals(UNSUBSCRIBE.raw, resp)) { 141 | // subscribedChannels = ((Long) reply.get(2)).intValue(); 142 | // byte[] bchannel = (byte[]) reply.get(1); 143 | // string strchannel = (bchannel is null) ? null : SafeEncoder.encode(bchannel); 144 | // onUnsubscribe(strchannel, subscribedChannels); 145 | // } else if (Arrays.equals(MESSAGE.raw, resp)) { 146 | // byte[] bchannel = (byte[]) reply.get(1); 147 | // byte[] bmesg = (byte[]) reply.get(2); 148 | // string strchannel = (bchannel is null) ? null : SafeEncoder.encode(bchannel); 149 | // string strmesg = (bmesg is null) ? null : SafeEncoder.encode(bmesg); 150 | // onMessage(strchannel, strmesg); 151 | // } else if (Arrays.equals(PMESSAGE.raw, resp)) { 152 | // byte[] bpattern = (byte[]) reply.get(1); 153 | // byte[] bchannel = (byte[]) reply.get(2); 154 | // byte[] bmesg = (byte[]) reply.get(3); 155 | // string strpattern = (bpattern is null) ? null : SafeEncoder.encode(bpattern); 156 | // string strchannel = (bchannel is null) ? null : SafeEncoder.encode(bchannel); 157 | // string strmesg = (bmesg is null) ? null : SafeEncoder.encode(bmesg); 158 | // onPMessage(strpattern, strchannel, strmesg); 159 | // } else if (Arrays.equals(PSUBSCRIBE.raw, resp)) { 160 | // subscribedChannels = ((Long) reply.get(2)).intValue(); 161 | // byte[] bpattern = (byte[]) reply.get(1); 162 | // string strpattern = (bpattern is null) ? null : SafeEncoder.encode(bpattern); 163 | // onPSubscribe(strpattern, subscribedChannels); 164 | // } else if (Arrays.equals(PUNSUBSCRIBE.raw, resp)) { 165 | // subscribedChannels = ((Long) reply.get(2)).intValue(); 166 | // byte[] bpattern = (byte[]) reply.get(1); 167 | // string strpattern = (bpattern is null) ? null : SafeEncoder.encode(bpattern); 168 | // onPUnsubscribe(strpattern, subscribedChannels); 169 | // } else if (Arrays.equals(PONG.raw, resp)) { 170 | // byte[] bpattern = (byte[]) reply.get(1); 171 | // string strpattern = (bpattern is null) ? null : SafeEncoder.encode(bpattern); 172 | // onPong(strpattern); 173 | // } else { 174 | // throw new RedisException("Unknown message type: " ~ firstObj); 175 | // } 176 | // } while (isSubscribed()); 177 | 178 | /* Invalidate instance since this thread is no longer listening */ 179 | this.client = null; 180 | } 181 | 182 | int getSubscribedChannels() { 183 | return subscribedChannels; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /source/hunt/redis/RedisSlotBasedConnectionHandler.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.RedisSlotBasedConnectionHandler; 13 | 14 | import hunt.redis.RedisClusterConnectionHandler; 15 | 16 | import hunt.redis.Exceptions; 17 | import hunt.redis.HostAndPort; 18 | import hunt.redis.Redis; 19 | import hunt.redis.RedisPool; 20 | import hunt.redis.RedisPoolOptions; 21 | 22 | import hunt.text.Common; 23 | 24 | import hunt.collection.List; 25 | import hunt.collection.Set; 26 | import hunt.util.pool; 27 | 28 | 29 | class RedisSlotBasedConnectionHandler : RedisClusterConnectionHandler { 30 | 31 | this(HostAndPort[] nodes, RedisPoolOptions poolConfig) { 32 | super(nodes, poolConfig); 33 | } 34 | 35 | 36 | override 37 | Redis getConnection() { 38 | // In antirez's redis-rb-cluster implementation, 39 | // getRandomConnection always return valid connection (able to 40 | // ping-pong) 41 | // or exception if all connections are invalid 42 | 43 | List!(RedisPool) pools = cache.getShuffledNodesPool(); 44 | 45 | foreach(RedisPool pool ; pools) { 46 | Redis redis = null; 47 | try { 48 | redis = pool.borrow(); 49 | 50 | if (redis is null) { 51 | continue; 52 | } 53 | 54 | string result = redis.ping(); 55 | 56 | if (result.equalsIgnoreCase("pong")) return redis; 57 | 58 | redis.close(); 59 | } catch (RedisException ex) { 60 | if (redis !is null) { 61 | redis.close(); 62 | } 63 | } 64 | } 65 | 66 | throw new RedisNoReachableClusterNodeException("No reachable node in cluster"); 67 | } 68 | 69 | override 70 | Redis getConnectionFromSlot(int slot) { 71 | RedisPool connectionPool = cache.getSlotPool(slot); 72 | if (connectionPool !is null) { 73 | // It can't guaranteed to get valid connection because of node 74 | // assignment 75 | return connectionPool.borrow(); 76 | } else { 77 | renewSlotCache(); //It's abnormal situation for cluster mode, that we have just nothing for slot, try to rediscover state 78 | connectionPool = cache.getSlotPool(slot); 79 | if (connectionPool !is null) { 80 | return connectionPool.borrow(); 81 | } else { 82 | //no choice, fallback to new connection to random node 83 | return getConnection(); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /source/hunt/redis/Response.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.Response; 13 | 14 | import hunt.redis.Builder; 15 | import hunt.redis.Exceptions; 16 | 17 | abstract class AbstractResponse { 18 | protected AbstractResponse dependency = null; 19 | protected bool building = false; 20 | protected bool built = false; 21 | protected bool _set = false; 22 | 23 | protected RedisDataException exception = null; 24 | protected Object data; 25 | 26 | protected void build(); 27 | 28 | void set(Object data) { 29 | this.data = data; 30 | _set = true; 31 | } 32 | 33 | void setDependency(AbstractResponse dependency) { 34 | this.dependency = dependency; 35 | } 36 | 37 | } 38 | 39 | alias Response = RedisResponse; 40 | 41 | class RedisResponse(T) : AbstractResponse { 42 | protected T response = null; 43 | 44 | private Builder!(T) builder; 45 | 46 | this(Builder!(T) b) { 47 | this.builder = b; 48 | } 49 | 50 | T get() { 51 | // if response has dependency response and dependency is not built, 52 | // build it first and no more!! 53 | if (dependency !is null && dependency._set && !dependency.built) { 54 | dependency.build(); 55 | } 56 | if (!_set) { 57 | throw new RedisDataException( 58 | "Please close pipeline or multi block before calling this method."); 59 | } 60 | if (!built) { 61 | build(); 62 | } 63 | if (exception !is null) { 64 | throw exception; 65 | } 66 | return response; 67 | } 68 | 69 | override protected void build() { 70 | // check build state to prevent recursion 71 | if (building) { 72 | return; 73 | } 74 | 75 | building = true; 76 | try { 77 | if (data !is null) { 78 | exception = cast(RedisDataException) data; 79 | if (exception is null) { 80 | response = builder.build(data); 81 | } 82 | } 83 | 84 | data = null; 85 | } finally { 86 | building = false; 87 | built = true; 88 | } 89 | } 90 | 91 | override 92 | string toString() { 93 | return "Response " ~ builder.toString(); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /source/hunt/redis/ScanParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.ScanParams; 13 | 14 | import hunt.redis.Protocol; 15 | import hunt.redis.util.SafeEncoder; 16 | 17 | import hunt.collection; 18 | import hunt.Integer; 19 | 20 | import std.conv; 21 | 22 | class ScanParams { 23 | 24 | private Map!(Protocol.Keyword, ByteBuffer) params; 25 | 26 | enum string SCAN_POINTER_START = "0"; 27 | enum const(ubyte)[] SCAN_POINTER_START_BINARY = SafeEncoder.encode(SCAN_POINTER_START); 28 | 29 | this() { 30 | params = new HashMap!(Protocol.Keyword, ByteBuffer); // new EnumMap!(Keyword, ByteBuffer)(Keyword.class); 31 | } 32 | 33 | ScanParams match(const(ubyte)[] pattern) { 34 | params.put(Protocol.Keyword.MATCH, BufferUtils.toBuffer(cast(byte[])pattern)); 35 | return this; 36 | } 37 | 38 | /** 39 | * @see MATCH option in Redis documentation 40 | * 41 | * @param pattern 42 | * @return 43 | */ 44 | ScanParams match(string pattern) { 45 | params.put(Protocol.Keyword.MATCH, BufferUtils.toBuffer(cast(byte[])SafeEncoder.encode(pattern))); 46 | return this; 47 | } 48 | 49 | /** 50 | * @see COUNT option in Redis documentation 51 | * 52 | * @param count 53 | * @return 54 | */ 55 | ScanParams count(int count) { 56 | params.put(Protocol.Keyword.COUNT, BufferUtils.toBuffer(cast(byte[])Protocol.toByteArray(count))); 57 | return this; 58 | } 59 | 60 | Collection!(const(ubyte)[]) getParams() { 61 | List!(const(ubyte)[]) paramsList = new ArrayList!(const(ubyte)[])(params.size()); 62 | foreach (Protocol.Keyword key, ByteBuffer value; params) { 63 | paramsList.add(cast(const(ubyte)[])key.to!string()); 64 | paramsList.add(cast(const(ubyte)[])value.array()); 65 | } 66 | // return Collections.unmodifiableCollection(paramsList); 67 | return paramsList; 68 | } 69 | 70 | const(ubyte)[] binaryMatch() { 71 | if (params.containsKey(Protocol.Keyword.MATCH)) { 72 | return cast(const(ubyte)[])(params.get(Protocol.Keyword.MATCH).array()); 73 | } else { 74 | return null; 75 | } 76 | } 77 | 78 | string match() { 79 | if (params.containsKey(Protocol.Keyword.MATCH)) { 80 | return cast(string)(params.get(Protocol.Keyword.MATCH).array()); 81 | } else { 82 | return null; 83 | } 84 | } 85 | 86 | Integer count() { 87 | if (params.containsKey(Protocol.Keyword.COUNT)) { 88 | return new Integer(params.get(Protocol.Keyword.COUNT).getInt()); 89 | } else { 90 | return null; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /source/hunt/redis/ScanResult.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.ScanResult; 13 | 14 | import hunt.redis.ScanParams; 15 | 16 | import hunt.collection.List; 17 | 18 | 19 | class ScanResult(T) { 20 | private string cursor; 21 | private List!(T) results; 22 | 23 | this(string cursor, List!(T) results) { 24 | this.cursor = cursor; 25 | this.results = results; 26 | } 27 | 28 | /** 29 | * Returns the new value of the cursor 30 | * @return the new cursor value. {@link ScanParams#SCAN_POINTER_START} when a complete iteration has finished 31 | */ 32 | string getCursor() { 33 | return cursor; 34 | } 35 | 36 | /** 37 | * Is the iteration complete. I.e. was the complete dataset scanned. 38 | * 39 | * @return true if the iteration is complete 40 | */ 41 | bool isCompleteIteration() { 42 | return ScanParams.SCAN_POINTER_START == getCursor(); 43 | } 44 | 45 | string getCursorAsBytes() { 46 | return cursor; 47 | } 48 | 49 | /** 50 | * The scan results from the current call. 51 | * @return the scan results 52 | */ 53 | List!(T) getResult() { 54 | return results; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /source/hunt/redis/ShardedRedisPipeline.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.ShardedRedisPipeline; 13 | 14 | import hunt.redis.Client; 15 | import hunt.redis.BinaryShardedRedis; 16 | import hunt.redis.PipelineBase; 17 | 18 | import hunt.collection; 19 | import hunt.Exceptions; 20 | 21 | 22 | private class FutureResult { 23 | private Client client; 24 | 25 | this(Client client) { 26 | this.client = client; 27 | } 28 | 29 | Object get() { 30 | return client.getOne(); 31 | } 32 | } 33 | 34 | /** 35 | */ 36 | class ShardedRedisPipeline : PipelineBase { 37 | private BinaryShardedRedis jedis; 38 | private List!(FutureResult) results; 39 | private Queue!(Client) clients; 40 | 41 | this() { 42 | results = new ArrayList!(FutureResult)(); 43 | clients = new LinkedList!(Client)(); 44 | } 45 | 46 | 47 | void setShardedRedis(BinaryShardedRedis jedis) { 48 | this.jedis = jedis; 49 | } 50 | 51 | List!(Object) getResults() { 52 | List!(Object) r = new ArrayList!(Object)(); 53 | foreach(FutureResult fr ; results) { 54 | r.add(fr.get()); 55 | } 56 | return r; 57 | } 58 | 59 | /** 60 | * Synchronize pipeline by reading all responses. This operation closes the pipeline. In order to 61 | * get return values from pipelined commands, capture the different Response<?> of the 62 | * commands you execute. 63 | */ 64 | void sync() { 65 | foreach(Client client ; clients) { 66 | generateResponse(client.getOne()); 67 | } 68 | } 69 | 70 | /** 71 | * Synchronize pipeline by reading all responses. This operation closes the pipeline. Whenever 72 | * possible try to avoid using this version and use ShardedRedisPipeline.sync() as it won't go 73 | * through all the responses and generate the right response type (usually it is a waste of time). 74 | * @return A list of all the responses in the order you executed them. 75 | */ 76 | List!(Object) syncAndReturnAll() { 77 | // List!(Object) formatted = new ArrayList!(Object)(); 78 | // foreach(Client client ; clients) { 79 | // formatted.add(generateResponse(client.getOne()).get()); 80 | // } 81 | // return formatted; 82 | implementationMissing(false); 83 | return null; 84 | } 85 | 86 | override 87 | protected Client getClient(string key) { 88 | Client client = jedis.getShard(key).getClient(); 89 | clients.add(client); 90 | results.add(new FutureResult(client)); 91 | return client; 92 | } 93 | 94 | override 95 | protected Client getClient(const(ubyte)[] key) { 96 | Client client = jedis.getShard(key).getClient(); 97 | clients.add(client); 98 | results.add(new FutureResult(client)); 99 | return client; 100 | } 101 | } -------------------------------------------------------------------------------- /source/hunt/redis/ShardedRedisPool.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.ShardedRedisPool; 13 | 14 | // import hunt.redis.BinaryRedis; 15 | // import hunt.redis.BinaryShardedRedis; 16 | // import hunt.redis.Redis; 17 | // import hunt.redis.RedisShardInfo; 18 | // import hunt.redis.ShardedRedis; 19 | // import hunt.redis.ShardedRedisPool; 20 | // import hunt.redis.util.Hashing; 21 | // import hunt.redis.util.Pool; 22 | 23 | // import hunt.collection.List; 24 | 25 | // // import hunt.pool.PooledObject; 26 | // // import hunt.pool.PooledObjectFactory; 27 | // // import hunt.pool.impl.DefaultPooledObject; 28 | // import hunt.util.pool; 29 | 30 | // import hunt.Boolean; 31 | 32 | // import std.regex; 33 | // alias Pattern = Regex!char; 34 | // alias GenericObjectPoolConfig = PoolOptions; 35 | 36 | // class ShardedRedisPool : Pool!(ShardedRedis) { 37 | // this(GenericObjectPoolConfig poolConfig, List!(RedisShardInfo) shards) { 38 | // this(poolConfig, shards, Hashing.MURMUR_HASH); 39 | // } 40 | 41 | // this(GenericObjectPoolConfig poolConfig, List!(RedisShardInfo) shards, 42 | // Hashing algo) { 43 | // this(poolConfig, shards, algo, Pattern.init); 44 | // } 45 | 46 | // this(GenericObjectPoolConfig poolConfig, List!(RedisShardInfo) shards, 47 | // Pattern keyTagPattern) { 48 | // this(poolConfig, shards, Hashing.MURMUR_HASH, keyTagPattern); 49 | // } 50 | 51 | // this(GenericObjectPoolConfig poolConfig, List!(RedisShardInfo) shards, 52 | // Hashing algo, Pattern keyTagPattern) { 53 | // super(poolConfig, new ShardedRedisFactory(shards, algo, keyTagPattern)); 54 | // } 55 | 56 | // override 57 | // ShardedRedis getResource() { 58 | // ShardedRedis jedis = super.getResource(); 59 | // jedis.setDataSource(this); 60 | // return jedis; 61 | // } 62 | 63 | // override 64 | // void returnBrokenResource(ShardedRedis resource) { 65 | // if (resource !is null) { 66 | // returnBrokenResourceObject(resource); 67 | // } 68 | // } 69 | 70 | // override 71 | // void returnResource(ShardedRedis resource) { 72 | // if (resource !is null) { 73 | // resource.resetState(); 74 | // returnResourceObject(resource); 75 | // } 76 | // } 77 | // } 78 | 79 | 80 | // /** 81 | // * PoolableObjectFactory custom impl. 82 | // */ 83 | // private class ShardedRedisFactory : PooledObjectFactory!(ShardedRedis) { 84 | // private List!(RedisShardInfo) shards; 85 | // private Hashing algo; 86 | // private Pattern keyTagPattern; 87 | 88 | // this(List!(RedisShardInfo) shards, Hashing algo, Pattern keyTagPattern) { 89 | // this.shards = shards; 90 | // this.algo = algo; 91 | // this.keyTagPattern = keyTagPattern; 92 | // } 93 | 94 | // override 95 | // IPooledObject makeObject() { 96 | // ShardedRedis jedis = new ShardedRedis(shards, algo, keyTagPattern); 97 | // return new DefaultPooledObject!(ShardedRedis)(jedis); 98 | // } 99 | 100 | // override 101 | // void destroyObject(IPooledObject pooledRedis) { 102 | // ShardedRedis shardedRedis = (cast(PooledObject!ShardedRedis)pooledRedis).getObject(); 103 | // assert(shardedRedis !is null); 104 | // foreach(Redis jedis ; shardedRedis.getAllShards()) { 105 | // if (jedis.isConnected()) { 106 | // try { 107 | // try { 108 | // jedis.quit(); 109 | // } catch (Exception e) { 110 | 111 | // } 112 | // jedis.disconnect(); 113 | // } catch (Exception e) { 114 | 115 | // } 116 | // } 117 | // } 118 | // } 119 | 120 | // override 121 | // bool validateObject(IPooledObject pooledRedis) { 122 | // try { 123 | // ShardedRedis jedis = (cast(PooledObject!ShardedRedis)pooledRedis).getObject(); 124 | // assert(jedis !is null); 125 | 126 | // foreach(Redis shard ; jedis.getAllShards()) { 127 | // if (shard.ping() != "PONG") { 128 | // return false; 129 | // } 130 | // } 131 | // return true; 132 | // } catch (Exception ex) { 133 | // return false; 134 | // } 135 | // } 136 | 137 | // override 138 | // void activateObject(IPooledObject p) { 139 | 140 | // } 141 | 142 | // override 143 | // void passivateObject(IPooledObject p) { 144 | 145 | // } 146 | // } -------------------------------------------------------------------------------- /source/hunt/redis/SortingParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.SortingParams; 13 | 14 | import hunt.redis.Protocol; 15 | 16 | import hunt.collection.ArrayList; 17 | import hunt.collection.Collection; 18 | import hunt.collection.Collections; 19 | import hunt.collection.List; 20 | 21 | import hunt.redis.util.SafeEncoder; 22 | 23 | alias Keyword = Protocol.Keyword; 24 | 25 | import std.conv; 26 | 27 | /** 28 | * Builder Class for {@link Redis#sort(string, SortingParams) SORT} Parameters. 29 | */ 30 | class SortingParams { 31 | private List!(const(ubyte)[]) params; 32 | 33 | this() { 34 | params = new ArrayList!(const(ubyte)[])(); 35 | } 36 | 37 | /** 38 | * Sort by weight in keys. 39 | *

40 | * Takes a pattern that is used in order to generate the key names of the weights used for 41 | * sorting. Weight key names are obtained substituting the first occurrence of * with the actual 42 | * value of the elements on the list. 43 | *

44 | * The pattern for a normal key/value pair is "field*" and for a value in a hash 45 | * "field*->fieldname". 46 | * @param pattern 47 | * @return the SortingParams Object 48 | */ 49 | SortingParams by(const(ubyte)[] pattern) { 50 | params.add(cast(const(ubyte)[])Keyword.BY.to!string()); 51 | params.add(pattern); 52 | return this; 53 | } 54 | 55 | /** 56 | * No sorting. 57 | *

58 | * This is useful if you want to retrieve a external key (using {@link #get(string...) GET}) but 59 | * you don't want the sorting overhead. 60 | * @return the SortingParams Object 61 | */ 62 | SortingParams nosort() { 63 | params.add(cast(const(ubyte)[])Keyword.BY.to!string()); 64 | params.add(cast(const(ubyte)[])Keyword.NOSORT.to!string()); 65 | return this; 66 | } 67 | 68 | Collection!(const(ubyte)[]) getParams() { 69 | // return Collections.unmodifiableCollection(params); 70 | return params; 71 | } 72 | 73 | /** 74 | * Get the Sorting in Descending Order. 75 | * @return the sortingParams Object 76 | */ 77 | SortingParams desc() { 78 | params.add(cast(const(ubyte)[])Keyword.DESC.to!string()); 79 | return this; 80 | } 81 | 82 | /** 83 | * Get the Sorting in Ascending Order. This is the default order. 84 | * @return the SortingParams Object 85 | */ 86 | SortingParams asc() { 87 | params.add(cast(const(ubyte)[])Keyword.ASC.to!string()); 88 | return this; 89 | } 90 | 91 | /** 92 | * Limit the Numbers of returned Elements. 93 | * @param start is zero based 94 | * @param count 95 | * @return the SortingParams Object 96 | */ 97 | SortingParams limit(int start, int count) { 98 | params.add(cast(const(ubyte)[])Keyword.LIMIT.to!string()); 99 | params.add(Protocol.toByteArray(start)); 100 | params.add(Protocol.toByteArray(count)); 101 | return this; 102 | } 103 | 104 | /** 105 | * Sort lexicographicaly. Note that Redis is utf-8 aware assuming you set the right value for the 106 | * LC_COLLATE environment variable. 107 | * @return the SortingParams Object 108 | */ 109 | SortingParams alpha() { 110 | params.add(cast(const(ubyte)[])Keyword.ALPHA.to!string()); 111 | return this; 112 | } 113 | 114 | /** 115 | * Retrieving external keys from the result of the search. 116 | *

117 | * Takes a pattern that is used in order to generate the key names of the result of sorting. The 118 | * key names are obtained substituting the first occurrence of * with the actual value of the 119 | * elements on the list. 120 | *

121 | * The pattern for a normal key/value pair is "field*" and for a value in a hash 122 | * "field*->fieldname". 123 | *

124 | * To get the list itself use the char # as pattern. 125 | * @param patterns 126 | * @return the SortingParams Object 127 | */ 128 | SortingParams get(string[] patterns...) { 129 | foreach(string pattern ; patterns) { 130 | params.add(cast(const(ubyte)[])Keyword.GET.to!string()); 131 | params.add(SafeEncoder.encode(pattern)); 132 | } 133 | return this; 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /source/hunt/redis/StreamEntry.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.StreamEntry; 13 | 14 | import hunt.redis.StreamEntryID; 15 | 16 | import hunt.Exceptions; 17 | import hunt.collection.Map; 18 | 19 | class StreamEntry { // : Serializable 20 | private StreamEntryID id; 21 | private Map!(string, string) fields; 22 | 23 | this(StreamEntryID id, Map!(string, string) fields) { 24 | this.id = id; 25 | this.fields = fields; 26 | } 27 | 28 | StreamEntryID getID() { 29 | return id; 30 | } 31 | 32 | Map!(string, string) getFields() { 33 | return fields; 34 | } 35 | 36 | override 37 | string toString() { 38 | return id.toString() ~ " " ~ fields.toString(); 39 | } 40 | 41 | // private void writeObject(ObjectOutputStream outputStream) { 42 | // outputStream.writeUnshared(this.id); 43 | // outputStream.writeUnshared(this.fields); 44 | // } 45 | 46 | // private void readObject(ObjectInputStream inputStream) { 47 | // this.id = cast(StreamEntryID) inputStream.readUnshared(); 48 | // this.fields = cast(Map!(string, string)) inputStream.readUnshared(); 49 | // } 50 | } 51 | -------------------------------------------------------------------------------- /source/hunt/redis/StreamEntryID.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.StreamEntryID; 13 | 14 | import hunt.Exceptions; 15 | import hunt.util.Common; 16 | import hunt.util.Comparator; 17 | 18 | import std.concurrency : initOnce; 19 | import std.conv; 20 | import std.string; 21 | 22 | class StreamEntryID : Comparable!(StreamEntryID) { // , Serializable 23 | 24 | // dfmt off 25 | /** 26 | * Should be used only with XADD 27 | * 28 | * 29 | * XADD mystream * field1 value1 30 | * 31 | */ 32 | static StreamEntryID NEW_ENTRY() { 33 | __gshared StreamEntryID inst; 34 | return initOnce!inst(new class StreamEntryID { 35 | override string toString(){ 36 | return "*"; 37 | } 38 | }); 39 | } 40 | 41 | 42 | /** 43 | * Should be used only with XGROUP CREATE 44 | * 45 | * 46 | * XGROUP CREATE mystream consumer-group-name $ 47 | * 48 | */ 49 | static StreamEntryID LAST_ENTRY() { 50 | __gshared StreamEntryID inst; 51 | return initOnce!inst(new class StreamEntryID { 52 | override string toString(){ 53 | return "$"; 54 | } 55 | }); 56 | } 57 | 58 | /** 59 | * Should be used only with XREADGROUP 60 | * 61 | * XREADGROUP $GroupName $ConsumerName BLOCK 2000 COUNT 10 STREAMS mystream > 62 | * 63 | */ 64 | static StreamEntryID UNRECEIVED_ENTRY() { 65 | __gshared StreamEntryID inst; 66 | return initOnce!inst(new class StreamEntryID { 67 | override string toString(){ 68 | return ">"; 69 | } 70 | }); 71 | } 72 | 73 | // dfmt on 74 | 75 | private long time; 76 | private long sequence; 77 | 78 | this() { 79 | this(0, 0L); 80 | } 81 | 82 | this(string id) { 83 | string[] split = id.split("-"); 84 | this.time = split[0].to!long; 85 | this.sequence = split[1].to!long; 86 | } 87 | 88 | this(long time, long sequence) { 89 | this.time = time; 90 | this.sequence = sequence; 91 | } 92 | 93 | override string toString() { 94 | return time.to!string() ~ "-" ~ sequence.to!string(); 95 | } 96 | 97 | override bool opEquals(Object obj) { 98 | if (this is obj) 99 | return true; 100 | if (obj is null) 101 | return false; 102 | StreamEntryID other = cast(StreamEntryID) obj; 103 | if (other is null) 104 | return false; 105 | return this.time == other.time && this.sequence == other.sequence; 106 | } 107 | 108 | override size_t toHash() @trusted nothrow { 109 | try { 110 | return this.toString().hashOf(); 111 | } catch (Exception ex) { 112 | // do nothing 113 | return 0; 114 | } 115 | } 116 | 117 | override int opCmp(StreamEntryID other) { 118 | int timeComapre = compare(this.time, other.time); 119 | return timeComapre != 0 ? timeComapre : compare(this.sequence, other.sequence); 120 | } 121 | 122 | alias opCmp = Object.opCmp; 123 | 124 | long getTime() { 125 | return time; 126 | } 127 | 128 | long getSequence() { 129 | return sequence; 130 | } 131 | 132 | // private void writeObject(java.io.ObjectOutputStream out) { 133 | // out.writeLong(this.time); 134 | // out.writeLong(this.sequence); 135 | // } 136 | 137 | // private void readObject(java.io.ObjectInputStream in) { 138 | // this.time = in.readLong(); 139 | // this.sequence = in.readLong(); 140 | // } 141 | } 142 | -------------------------------------------------------------------------------- /source/hunt/redis/StreamPendingEntry.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.StreamPendingEntry; 13 | 14 | import hunt.Exceptions; 15 | import hunt.redis.StreamEntryID; 16 | 17 | import std.conv; 18 | 19 | class StreamPendingEntry { // : Serializable 20 | 21 | 22 | private StreamEntryID id; 23 | private string consumerName; 24 | private long idleTime; 25 | private long deliveredTimes; 26 | 27 | this(StreamEntryID id, string consumerName, long idleTime, long deliveredTimes) { 28 | this.id = id; 29 | this.consumerName = consumerName; 30 | this.idleTime = idleTime; 31 | this.deliveredTimes = deliveredTimes; 32 | } 33 | 34 | StreamEntryID getID() { 35 | return id; 36 | } 37 | 38 | long getIdleTime() { 39 | return idleTime; 40 | } 41 | 42 | long getDeliveredTimes() { 43 | return deliveredTimes; 44 | } 45 | 46 | string getConsumerName() { 47 | return consumerName; 48 | } 49 | 50 | override 51 | string toString() { 52 | return this.id.toString() ~ " " ~ this.consumerName ~ " idle:" ~ 53 | this.idleTime.to!string() ~ " times:" ~ this.deliveredTimes.to!string(); 54 | } 55 | 56 | // private void writeObject(java.io.ObjectOutputStream out) { 57 | // out.writeUnshared(this.id); 58 | // out.writeUTF(this.consumerName); 59 | // out.writeLong(idleTime); 60 | // out.writeLong(this.deliveredTimes); 61 | // } 62 | 63 | // private void readObject(java.io.ObjectInputStream in) { 64 | // this.id = (StreamEntryID) in.readUnshared(); 65 | // this.consumerName = in.readUTF(); 66 | // this.idleTime = in.readLong(); 67 | // this.deliveredTimes = in.readLong(); 68 | // } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /source/hunt/redis/Transaction.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.Transaction; 13 | 14 | import hunt.redis.Client; 15 | import hunt.redis.MultiKeyPipelineBase; 16 | import hunt.redis.Response; 17 | 18 | import hunt.Exceptions; 19 | import hunt.logging.ConsoleLogger; 20 | import hunt.util.Common; 21 | import hunt.collection.ArrayList; 22 | import hunt.collection.List; 23 | 24 | import hunt.redis.Exceptions; 25 | 26 | /** 27 | * Transaction is nearly identical to Pipeline, only differences are the multi/discard behaviors 28 | */ 29 | class Transaction : MultiKeyPipelineBase, Closeable { 30 | 31 | protected bool inTransaction = true; 32 | 33 | protected this() { 34 | // client will be set later in transaction block 35 | } 36 | 37 | this(Client client) { 38 | this.client = client; 39 | } 40 | 41 | override protected Client getClient(string key) { 42 | return client; 43 | } 44 | 45 | override protected Client getClient(const(ubyte)[] key) { 46 | return client; 47 | } 48 | 49 | void clear() { 50 | if (inTransaction) { 51 | discard(); 52 | } 53 | } 54 | 55 | List!(Object) exec() { 56 | // Discard QUEUED or ERROR 57 | client.getMany(getPipelinedResponseLength()); 58 | client.exec(); 59 | inTransaction = false; 60 | 61 | List!(Object) unformatted = client.getObjectMultiBulkReply(); 62 | if (unformatted is null) { 63 | return null; 64 | } 65 | List!(Object) formatted = new ArrayList!(Object)(); 66 | foreach (Object o; unformatted) { 67 | try { 68 | implementationMissing(false); 69 | trace(typeid(generateResponse(o))); 70 | 71 | 72 | // formatted.add(generateResponse(o).get()); 73 | // FIXME: Needing refactor or cleanup -@zxp at 7/17/2019, 11:21:20 AM 74 | // 75 | } catch (RedisDataException e) { 76 | formatted.add(e); 77 | } 78 | } 79 | return formatted; 80 | } 81 | 82 | List!(AbstractResponse) execGetResponse() { 83 | // Discard QUEUED or ERROR 84 | client.getMany(getPipelinedResponseLength()); 85 | client.exec(); 86 | inTransaction = false; 87 | 88 | List!(Object) unformatted = client.getObjectMultiBulkReply(); 89 | if (unformatted is null) { 90 | return null; 91 | } 92 | List!AbstractResponse response = new ArrayList!AbstractResponse(); 93 | foreach (Object o; unformatted) { 94 | response.add(generateResponse(o)); 95 | } 96 | return response; 97 | } 98 | 99 | string discard() { 100 | client.getMany(getPipelinedResponseLength()); 101 | client.discard(); 102 | inTransaction = false; 103 | clean(); 104 | return client.getStatusCodeReply(); 105 | } 106 | 107 | void setClient(Client client) { 108 | this.client = client; 109 | } 110 | 111 | override void close() { 112 | clear(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /source/hunt/redis/Tuple.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.Tuple; 13 | 14 | import hunt.redis.util.ByteArrayComparator; 15 | import hunt.redis.util.SafeEncoder; 16 | 17 | import hunt.util.ArrayHelper; 18 | 19 | import hunt.Double; 20 | import hunt.util.Common; 21 | import hunt.util.Comparator; 22 | 23 | import std.conv; 24 | 25 | class Tuple : Comparable!(Tuple) { 26 | private const(ubyte)[] element; 27 | private double score; 28 | 29 | this(string element, double score) { 30 | this(SafeEncoder.encode(element), score); 31 | } 32 | 33 | this(const(ubyte)[] element, double score) { 34 | this.element = element; 35 | this.score = score; 36 | } 37 | 38 | override size_t toHash() @trusted nothrow { 39 | int prime = 31; 40 | int result = 1; 41 | result = prime * result; 42 | if (null != element) { 43 | foreach (byte b; element) { 44 | result = prime * result + b; 45 | } 46 | } 47 | // FIXME: Needing refactor or cleanup -@zxp at 7/17/2019, 11:17:51 AM 48 | // 49 | long temp = cast(long)score; // Double.doubleToLongBits(score); 50 | result = prime * result + cast(int)(temp ^ (temp >>> 32)); 51 | return result; 52 | } 53 | 54 | override bool opEquals(Object obj) { 55 | if (obj is null) 56 | return false; 57 | if (obj is this) 58 | return true; 59 | 60 | Tuple other = cast(Tuple) obj; 61 | if (other is null) 62 | return false; 63 | if (element != other.element) 64 | return false; 65 | return score == other.score; 66 | } 67 | 68 | override int opCmp(Tuple other) { 69 | return compare(this, other); 70 | } 71 | alias opCmp = Object.opCmp; 72 | 73 | static int compare(Tuple t1, Tuple t2) { 74 | int compScore = hunt.util.Comparator.compare(t1.score, t2.score); 75 | if (compScore != 0) 76 | return compScore; 77 | 78 | return t1.element == t2.element; 79 | } 80 | 81 | string getElement() { 82 | return SafeEncoder.encode(element); 83 | } 84 | 85 | const(ubyte)[] getBinaryElement() { 86 | return element; 87 | } 88 | 89 | double getScore() { 90 | return score; 91 | } 92 | 93 | override string toString() { 94 | return "[" ~ cast(string)element ~ "," ~ score.to!string ~ "]"; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /source/hunt/redis/ZParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.ZParams; 13 | 14 | // import hunt.redis.Protocol.Keyword.AGGREGATE; 15 | // import hunt.redis.Protocol.Keyword.WEIGHTS; 16 | 17 | import hunt.redis.Protocol; 18 | 19 | import hunt.collection.ArrayList; 20 | import hunt.collection.Collection; 21 | import hunt.collection.Collections; 22 | import hunt.collection.List; 23 | 24 | import std.conv; 25 | 26 | import hunt.redis.util.SafeEncoder; 27 | 28 | enum Aggregate { 29 | SUM, 30 | MIN, 31 | MAX 32 | } 33 | 34 | class ZParams { 35 | 36 | private List!(const(ubyte)[]) params; 37 | 38 | this() { 39 | params = new ArrayList!(const(ubyte)[])(); 40 | } 41 | 42 | /** 43 | * Set weights. 44 | * @param weights weights. 45 | * @return 46 | */ 47 | ZParams weights(double[] weights...) { 48 | params.add(SafeEncoder.encode(Protocol.Keyword.WEIGHTS.to!string())); 49 | foreach (double weight; weights) { 50 | params.add(Protocol.toByteArray(weight)); 51 | } 52 | 53 | return this; 54 | } 55 | 56 | Collection!(const(ubyte)[]) getParams() { 57 | return params; // Collections.unmodifiableCollection(params); 58 | } 59 | 60 | ZParams aggregate(Aggregate aggregate) { 61 | params.add(SafeEncoder.encode(Protocol.Keyword.AGGREGATE.to!string())); 62 | params.add(SafeEncoder.encode(aggregate.to!string())); 63 | return this; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/AdvancedBinaryRedisCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.AdvancedBinaryRedisCommands; 13 | 14 | import hunt.collection.List; 15 | 16 | import hunt.redis.params.MigrateParams; 17 | import hunt.redis.params.ClientKillParams; 18 | import hunt.Long; 19 | 20 | interface AdvancedBinaryRedisCommands { 21 | 22 | List!(const(ubyte)[]) configGet(const(ubyte)[] pattern); 23 | 24 | const(ubyte)[] configSet(const(ubyte)[] parameter, const(ubyte)[] value); 25 | 26 | string slowlogReset(); 27 | 28 | Long slowlogLen(); 29 | 30 | List!(const(ubyte)[]) slowlogGetBinary(); 31 | 32 | List!(const(ubyte)[]) slowlogGetBinary(long entries); 33 | 34 | Long objectRefcount(const(ubyte)[] key); 35 | 36 | const(ubyte)[] objectEncoding(const(ubyte)[] key); 37 | 38 | Long objectIdletime(const(ubyte)[] key); 39 | 40 | string migrate(string host, int port, const(ubyte)[] key, int destinationDB, int timeout); 41 | 42 | string migrate(string host, int port, int destinationDB, int timeout, 43 | MigrateParams params, const(ubyte)[][] keys...); 44 | 45 | string clientKill(const(ubyte)[] ipPort); 46 | 47 | string clientKill(string ip, int port); 48 | 49 | Long clientKill(ClientKillParams params); 50 | 51 | const(ubyte)[] clientGetnameBinary(); 52 | 53 | const(ubyte)[] clientListBinary(); 54 | 55 | string clientSetname(const(ubyte)[] name); 56 | 57 | const(ubyte)[] memoryDoctorBinary(); 58 | } 59 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/AdvancedRedisCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.AdvancedRedisCommands; 13 | 14 | import hunt.collection.List; 15 | 16 | import hunt.redis.params.MigrateParams; 17 | import hunt.redis.params.ClientKillParams; 18 | import hunt.redis.util.Slowlog; 19 | 20 | import hunt.Long; 21 | 22 | interface AdvancedRedisCommands { 23 | List!(string) configGet(string pattern); 24 | 25 | string configSet(string parameter, string value); 26 | 27 | string slowlogReset(); 28 | 29 | Long slowlogLen(); 30 | 31 | List!(Slowlog) slowlogGet(); 32 | 33 | List!(Slowlog) slowlogGet(long entries); 34 | 35 | Long objectRefcount(string key); 36 | 37 | string objectEncoding(string key); 38 | 39 | Long objectIdletime(string key); 40 | 41 | string migrate(string host, int port, string key, int destinationDB, int timeout); 42 | 43 | string migrate(string host, int port, int destinationDB, int timeout, 44 | MigrateParams params, string[] keys...); 45 | 46 | string clientKill(string ipPort); 47 | 48 | string clientKill(string ip, int port); 49 | 50 | Long clientKill(ClientKillParams params); 51 | 52 | string clientGetname(); 53 | 54 | string clientList(); 55 | 56 | string clientSetname(string name); 57 | 58 | string memoryDoctor(); 59 | } 60 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/BasicRedisPipeline.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.BasicRedisPipeline; 13 | 14 | import hunt.redis.Module; 15 | import hunt.redis.Response; 16 | 17 | import hunt.collection.List; 18 | import hunt.Long; 19 | 20 | /** 21 | * Pipelined responses for all of the low level, non key related commands 22 | */ 23 | interface BasicRedisPipeline { 24 | 25 | Response!(string) bgrewriteaof(); 26 | 27 | Response!(string) bgsave(); 28 | 29 | Response!(List!(string)) configGet(string pattern); 30 | 31 | Response!(string) configSet(string parameter, string value); 32 | 33 | Response!(string) configResetStat(); 34 | 35 | Response!(string) save(); 36 | 37 | Response!(Long) lastsave(); 38 | 39 | Response!(string) flushDB(); 40 | 41 | Response!(string) flushAll(); 42 | 43 | Response!(string) info(); 44 | 45 | Response!(List!(string)) time(); 46 | 47 | Response!(Long) dbSize(); 48 | 49 | Response!(string) shutdown(); 50 | 51 | Response!(string) ping(); 52 | 53 | Response!(string) select(int index); 54 | 55 | Response!(string) swapDB(int index1, int index2); 56 | 57 | Response!(string) migrate(string host, int port, string key, int destinationDB, int timeout); 58 | 59 | Response!(string) moduleLoad(string path); 60 | 61 | Response!(List!(Module)) moduleList(); 62 | 63 | Response!(string) moduleUnload(string name); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/BinaryScriptingCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.BinaryScriptingCommands; 13 | 14 | import hunt.collection.List; 15 | 16 | interface BinaryScriptingCommands { 17 | 18 | Object eval(const(ubyte)[] script, const(ubyte)[] keyCount, const(ubyte)[][] params...); 19 | 20 | Object eval(const(ubyte)[] script, int keyCount, const(ubyte)[][] params...); 21 | 22 | Object eval(const(ubyte)[] script, List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args); 23 | 24 | Object eval(const(ubyte)[] script); 25 | 26 | Object evalsha(const(ubyte)[] sha1); 27 | 28 | Object evalsha(const(ubyte)[] sha1, List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args); 29 | 30 | Object evalsha(const(ubyte)[] sha1, int keyCount, const(ubyte)[][] params...); 31 | 32 | // TODO: should be Boolean, add singular version 33 | List!(long) scriptExists(const(ubyte)[][] sha1...); 34 | 35 | const(ubyte)[] scriptLoad(const(ubyte)[] script); 36 | 37 | string scriptFlush(); 38 | 39 | string scriptKill(); 40 | } 41 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/BinaryScriptingCommandsPipeline.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.BinaryScriptingCommandsPipeline; 13 | 14 | import hunt.redis.Response; 15 | 16 | import hunt.collection.List; 17 | 18 | interface BinaryScriptingCommandsPipeline { 19 | 20 | Response!(Object) eval(const(ubyte)[] script, const(ubyte)[] keyCount, const(ubyte)[][] params...); 21 | 22 | Response!(Object) eval(const(ubyte)[] script, int keyCount, const(ubyte)[][] params...); 23 | 24 | Response!(Object) eval(const(ubyte)[] script, List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args); 25 | 26 | Response!(Object) eval(const(ubyte)[] script); 27 | 28 | Response!(Object) evalsha(const(ubyte)[] sha1); 29 | 30 | Response!(Object) evalsha(const(ubyte)[] sha1, List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args); 31 | 32 | Response!(Object) evalsha(const(ubyte)[] sha1, int keyCount, const(ubyte)[][] params...); 33 | } 34 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/ClusterCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.ClusterCommands; 13 | 14 | import hunt.collection.List; 15 | 16 | import hunt.redis.ClusterReset; 17 | import hunt.Long; 18 | 19 | /** 20 | * 21 | */ 22 | interface ClusterCommands { 23 | string clusterNodes(); 24 | 25 | string clusterMeet(string ip, int port); 26 | 27 | string clusterAddSlots(int[] slots...); 28 | 29 | string clusterDelSlots(int[] slots...); 30 | 31 | string clusterInfo(); 32 | 33 | List!(string) clusterGetKeysInSlot(int slot, int count); 34 | 35 | string clusterSetSlotNode(int slot, string nodeId); 36 | 37 | string clusterSetSlotMigrating(int slot, string nodeId); 38 | 39 | string clusterSetSlotImporting(int slot, string nodeId); 40 | 41 | string clusterSetSlotStable(int slot); 42 | 43 | string clusterForget(string nodeId); 44 | 45 | string clusterFlushSlots(); 46 | 47 | Long clusterKeySlot(string key); 48 | 49 | Long clusterCountKeysInSlot(int slot); 50 | 51 | string clusterSaveConfig(); 52 | 53 | string clusterReplicate(string nodeId); 54 | 55 | List!(string) clusterSlaves(string nodeId); 56 | 57 | string clusterFailover(); 58 | 59 | List!(Object) clusterSlots(); 60 | 61 | string clusterReset(ClusterReset resetType); 62 | 63 | string readonly(); 64 | } 65 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/ClusterPipeline.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.ClusterPipeline; 13 | 14 | import hunt.redis.Response; 15 | 16 | import hunt.collection.List; 17 | 18 | interface ClusterPipeline { 19 | Response!(string) clusterNodes(); 20 | 21 | Response!(string) clusterMeet(string ip, int port); 22 | 23 | Response!(string) clusterAddSlots(int[] slots...); 24 | 25 | Response!(string) clusterDelSlots(int[] slots...); 26 | 27 | Response!(string) clusterInfo(); 28 | 29 | Response!(List!(string)) clusterGetKeysInSlot(int slot, int count); 30 | 31 | Response!(string) clusterSetSlotNode(int slot, string nodeId); 32 | 33 | Response!(string) clusterSetSlotMigrating(int slot, string nodeId); 34 | 35 | Response!(string) clusterSetSlotImporting(int slot, string nodeId); 36 | } 37 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/ModuleCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.ModuleCommands; 13 | 14 | import hunt.redis.Module; 15 | 16 | import hunt.collection.List; 17 | 18 | interface ModuleCommands { 19 | string moduleLoad(string path); 20 | string moduleUnload(string name); 21 | List!(Module) moduleList(); 22 | } 23 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/MultiKeyBinaryCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.MultiKeyBinaryCommands; 13 | 14 | 15 | import hunt.redis.BinaryRedisPubSub; 16 | import hunt.redis.BitOP; 17 | import hunt.redis.SortingParams; 18 | import hunt.redis.ZParams; 19 | 20 | import hunt.collection.List; 21 | import hunt.collection.Map; 22 | import hunt.collection.Set; 23 | 24 | 25 | interface MultiKeyBinaryCommands { 26 | long del(const(ubyte)[][] keys...); 27 | 28 | long unlink(const(ubyte)[][] keys...); 29 | 30 | long exists(const(ubyte)[][] keys...); 31 | 32 | List!(const(ubyte)[]) blpop(int timeout, const(ubyte)[][] keys...); 33 | 34 | List!(const(ubyte)[]) brpop(int timeout, const(ubyte)[][] keys...); 35 | 36 | List!(const(ubyte)[]) blpop(const(ubyte)[][] args...); 37 | 38 | List!(const(ubyte)[]) brpop(const(ubyte)[][] args...); 39 | 40 | Set!(const(ubyte)[]) keys(const(ubyte)[] pattern); 41 | 42 | List!(const(ubyte)[]) mget(const(ubyte)[][] keys...); 43 | 44 | string mset(const(ubyte)[][] keysvalues...); 45 | 46 | long msetnx(const(ubyte)[][] keysvalues...); 47 | 48 | string rename(const(ubyte)[] oldkey, const(ubyte)[] newkey); 49 | 50 | long renamenx(const(ubyte)[] oldkey, const(ubyte)[] newkey); 51 | 52 | const(ubyte)[] rpoplpush(const(ubyte)[] srckey, const(ubyte)[] dstkey); 53 | 54 | Set!(const(ubyte)[]) sdiff(const(ubyte)[][] keys...); 55 | 56 | long sdiffstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 57 | 58 | Set!(const(ubyte)[]) sinter(const(ubyte)[][] keys...); 59 | 60 | long sinterstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 61 | 62 | long smove(const(ubyte)[] srckey, const(ubyte)[] dstkey, const(ubyte)[] member); 63 | 64 | long sort(const(ubyte)[] key, SortingParams sortingParameters, const(ubyte)[] dstkey); 65 | 66 | long sort(const(ubyte)[] key, const(ubyte)[] dstkey); 67 | 68 | Set!(const(ubyte)[]) sunion(const(ubyte)[][] keys...); 69 | 70 | long sunionstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 71 | 72 | string watch(const(ubyte)[][] keys...); 73 | 74 | string unwatch(); 75 | 76 | long zinterstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...); 77 | 78 | long zinterstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...); 79 | 80 | long zunionstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...); 81 | 82 | long zunionstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...); 83 | 84 | const(ubyte)[] brpoplpush(const(ubyte)[] source, const(ubyte)[] destination, int timeout); 85 | 86 | long publish(const(ubyte)[] channel, const(ubyte)[] message); 87 | 88 | void subscribe(BinaryRedisPubSub jedisPubSub, const(ubyte)[][] channels...); 89 | 90 | void psubscribe(BinaryRedisPubSub jedisPubSub, const(ubyte)[][] patterns...); 91 | 92 | const(ubyte)[] randomBinaryKey(); 93 | 94 | long bitop(BitOP op, const(ubyte)[] destKey, const(ubyte)[][] srcKeys...); 95 | 96 | string pfmerge(const(ubyte)[] destkey, const(ubyte)[][] sourcekeys...); 97 | 98 | long pfcount(const(ubyte)[][] keys...); 99 | 100 | long touch(const(ubyte)[][] keys...); 101 | 102 | List!(const(ubyte)[]) xread(int count, long block, Map!(const(ubyte)[], const(ubyte)[]) streams); 103 | 104 | List!(const(ubyte)[]) xreadGroup(const(ubyte)[] groupname, const(ubyte)[] consumer, int count, long block, bool noAck, Map!(const(ubyte)[], const(ubyte)[]) streams); 105 | } 106 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/MultiKeyBinaryRedisClusterCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.MultiKeyBinaryRedisClusterCommands; 13 | 14 | import hunt.redis.BinaryRedisPubSub; 15 | import hunt.redis.BitOP; 16 | import hunt.redis.ScanParams; 17 | import hunt.redis.ScanResult; 18 | import hunt.redis.SortingParams; 19 | import hunt.redis.ZParams; 20 | 21 | import hunt.collection.List; 22 | import hunt.collection.Map; 23 | import hunt.collection.Set; 24 | 25 | import hunt.Long; 26 | 27 | 28 | interface MultiKeyBinaryRedisClusterCommands { 29 | Long del(const(ubyte)[][] keys...); 30 | 31 | Long unlink(const(ubyte)[][] keys...); 32 | 33 | Long exists(const(ubyte)[][] keys...); 34 | 35 | // List!(const(ubyte)[]) blpop(int timeout, const(ubyte)[][] keys...); 36 | 37 | // List!(const(ubyte)[]) brpop(int timeout, const(ubyte)[][] keys...); 38 | 39 | // List!(const(ubyte)[]) mget(const(ubyte)[][] keys...); 40 | 41 | // string mset(const(ubyte)[][] keysvalues...); 42 | 43 | // long msetnx(const(ubyte)[][] keysvalues...); 44 | 45 | // string rename(const(ubyte)[] oldkey, const(ubyte)[] newkey); 46 | 47 | // long renamenx(const(ubyte)[] oldkey, const(ubyte)[] newkey); 48 | 49 | // const(ubyte)[] rpoplpush(const(ubyte)[] srckey, const(ubyte)[] dstkey); 50 | 51 | // Set!(const(ubyte)[]) sdiff(const(ubyte)[][] keys...); 52 | 53 | // long sdiffstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 54 | 55 | // Set!(const(ubyte)[]) sinter(const(ubyte)[][] keys...); 56 | 57 | // long sinterstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 58 | 59 | // long smove(const(ubyte)[] srckey, const(ubyte)[] dstkey, const(ubyte)[] member); 60 | 61 | // long sort(const(ubyte)[] key, SortingParams sortingParameters, const(ubyte)[] dstkey); 62 | 63 | // long sort(const(ubyte)[] key, const(ubyte)[] dstkey); 64 | 65 | // Set!(const(ubyte)[]) sunion(const(ubyte)[][] keys...); 66 | 67 | // long sunionstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 68 | 69 | // long zinterstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...); 70 | 71 | // long zinterstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...); 72 | 73 | // long zunionstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...); 74 | 75 | // long zunionstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...); 76 | 77 | // const(ubyte)[] brpoplpush(const(ubyte)[] source, const(ubyte)[] destination, int timeout); 78 | 79 | // long publish(const(ubyte)[] channel, const(ubyte)[] message); 80 | 81 | // void subscribe(BinaryRedisPubSub jedisPubSub, const(ubyte)[][] channels...); 82 | 83 | // void psubscribe(BinaryRedisPubSub jedisPubSub, const(ubyte)[][] patterns...); 84 | 85 | // long bitop(BitOP op, const(ubyte)[] destKey, const(ubyte)[][] srcKeys...); 86 | 87 | // string pfmerge(const(ubyte)[] destkey, const(ubyte)[][] sourcekeys...); 88 | 89 | // long pfcount(const(ubyte)[][] keys...); 90 | 91 | // long touch(const(ubyte)[][] keys...); 92 | 93 | // ScanResult!(const(ubyte)[]) scan(const(ubyte)[] cursor, ScanParams params); 94 | 95 | // Set!(const(ubyte)[]) keys(const(ubyte)[] pattern); 96 | 97 | // List!(const(ubyte)[]) xread(int count, long block, Map!(const(ubyte)[], const(ubyte)[]) streams); 98 | 99 | // List!(const(ubyte)[]) xreadGroup(const(ubyte)[] groupname, const(ubyte)[] consumer, int count, long block, bool noAck, Map!(const(ubyte)[], const(ubyte)[]) streams); 100 | } 101 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/MultiKeyBinaryRedisPipeline.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.MultiKeyBinaryRedisPipeline; 13 | 14 | import hunt.redis.BitOP; 15 | import hunt.redis.Response; 16 | import hunt.redis.SortingParams; 17 | import hunt.redis.ZParams; 18 | import hunt.redis.params.MigrateParams; 19 | 20 | import hunt.collection.List; 21 | import hunt.collection.Set; 22 | import hunt.Long; 23 | 24 | /** 25 | * Multikey related commands (these are split out because they are non-shardable) 26 | */ 27 | interface MultiKeyBinaryRedisPipeline { 28 | 29 | Response!(Long) del(const(ubyte)[][] keys...); 30 | 31 | Response!(Long) unlink(const(ubyte)[][] keys...); 32 | 33 | Response!(Long) exists(const(ubyte)[][] keys...); 34 | 35 | Response!(List!(const(ubyte)[])) blpop(const(ubyte)[][] args...); 36 | 37 | Response!(List!(const(ubyte)[])) brpop(const(ubyte)[][] args...); 38 | 39 | Response!(Set!(const(ubyte)[])) keys(const(ubyte)[] pattern); 40 | 41 | Response!(List!(const(ubyte)[])) mget(const(ubyte)[][] keys...); 42 | 43 | Response!(string) mset(const(ubyte)[][] keysvalues...); 44 | 45 | Response!(Long) msetnx(const(ubyte)[][] keysvalues...); 46 | 47 | Response!(string) rename(const(ubyte)[] oldkey, const(ubyte)[] newkey); 48 | 49 | Response!(Long) renamenx(const(ubyte)[] oldkey, const(ubyte)[] newkey); 50 | 51 | Response!(const(ubyte)[]) rpoplpush(const(ubyte)[] srckey, const(ubyte)[] dstkey); 52 | 53 | Response!(Set!(const(ubyte)[])) sdiff(const(ubyte)[][] keys...); 54 | 55 | Response!(Long) sdiffstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 56 | 57 | Response!(Set!(const(ubyte)[])) sinter(const(ubyte)[][] keys...); 58 | 59 | Response!(Long) sinterstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 60 | 61 | Response!(Long) smove(const(ubyte)[] srckey, const(ubyte)[] dstkey, const(ubyte)[] member); 62 | 63 | Response!(Long) sort(const(ubyte)[] key, SortingParams sortingParameters, const(ubyte)[] dstkey); 64 | 65 | Response!(Long) sort(const(ubyte)[] key, const(ubyte)[] dstkey); 66 | 67 | Response!(Set!(const(ubyte)[])) sunion(const(ubyte)[][] keys...); 68 | 69 | Response!(Long) sunionstore(const(ubyte)[] dstkey, const(ubyte)[][] keys...); 70 | 71 | Response!(string) watch(const(ubyte)[][] keys...); 72 | 73 | Response!(Long) zinterstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...); 74 | 75 | Response!(Long) zinterstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...); 76 | 77 | Response!(Long) zunionstore(const(ubyte)[] dstkey, const(ubyte)[][] sets...); 78 | 79 | Response!(Long) zunionstore(const(ubyte)[] dstkey, ZParams params, const(ubyte)[][] sets...); 80 | 81 | Response!(const(ubyte)[]) brpoplpush(const(ubyte)[] source, const(ubyte)[] destination, int timeout); 82 | 83 | Response!(Long) publish(const(ubyte)[] channel, const(ubyte)[] message); 84 | 85 | Response!(const(ubyte)[]) randomKeyBinary(); 86 | 87 | Response!(Long) bitop(BitOP op, const(ubyte)[] destKey, const(ubyte)[][] srcKeys...); 88 | 89 | Response!(string) pfmerge(const(ubyte)[] destkey, const(ubyte)[][] sourcekeys...); 90 | 91 | Response!(Long) pfcount(const(ubyte)[][] keys...); 92 | 93 | Response!(Long) touch(const(ubyte)[][] keys...); 94 | 95 | Response!(string) migrate(string host, int port, int destinationDB, int timeout, MigrateParams params, const(ubyte)[][] keys...); 96 | } 97 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/MultiKeyCommandsPipeline.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.MultiKeyCommandsPipeline; 13 | 14 | import hunt.redis.BitOP; 15 | import hunt.redis.Response; 16 | import hunt.redis.SortingParams; 17 | import hunt.redis.ZParams; 18 | import hunt.redis.params.MigrateParams; 19 | 20 | import hunt.collection.List; 21 | import hunt.collection.Set; 22 | 23 | import hunt.Long; 24 | 25 | /** 26 | * Multikey related commands (these are split out because they are non-shardable) 27 | */ 28 | interface MultiKeyCommandsPipeline { 29 | Response!(Long) del(string[] keys...); 30 | 31 | Response!(Long) unlink(string[] keys...); 32 | 33 | Response!(Long) exists(string[] keys...); 34 | 35 | Response!(List!(string)) blpop(string[] args...); 36 | 37 | Response!(List!(string)) brpop(string[] args...); 38 | 39 | Response!(Set!(string)) keys(string pattern); 40 | 41 | Response!(List!(string)) mget(string[] keys...); 42 | 43 | Response!(string) mset(string[] keysvalues...); 44 | 45 | Response!(Long) msetnx(string[] keysvalues...); 46 | 47 | Response!(string) rename(string oldkey, string newkey); 48 | 49 | Response!(Long) renamenx(string oldkey, string newkey); 50 | 51 | Response!(string) rpoplpush(string srckey, string dstkey); 52 | 53 | Response!(Set!(string)) sdiff(string[] keys...); 54 | 55 | Response!(Long) sdiffstore(string dstkey, string[] keys...); 56 | 57 | Response!(Set!(string)) sinter(string[] keys...); 58 | 59 | Response!(Long) sinterstore(string dstkey, string[] keys...); 60 | 61 | Response!(Long) smove(string srckey, string dstkey, string member); 62 | 63 | Response!(Long) sort(string key, SortingParams sortingParameters, string dstkey); 64 | 65 | Response!(Long) sort(string key, string dstkey); 66 | 67 | Response!(Set!(string)) sunion(string[] keys...); 68 | 69 | Response!(Long) sunionstore(string dstkey, string[] keys...); 70 | 71 | Response!(string) watch(string[] keys...); 72 | 73 | Response!(Long) zinterstore(string dstkey, string[] sets...); 74 | 75 | Response!(Long) zinterstore(string dstkey, ZParams params, string[] sets...); 76 | 77 | Response!(Long) zunionstore(string dstkey, string[] sets...); 78 | 79 | Response!(Long) zunionstore(string dstkey, ZParams params, string[] sets...); 80 | 81 | Response!(string) brpoplpush(string source, string destination, int timeout); 82 | 83 | Response!(Long) publish(string channel, string message); 84 | 85 | Response!(string) randomKey(); 86 | 87 | Response!(Long) bitop(BitOP op, string destKey, string[] srcKeys...); 88 | 89 | Response!(string) pfmerge(string destkey, string[] sourcekeys...); 90 | 91 | Response!(Long) pfcount(string[] keys...); 92 | 93 | Response!(Long) touch(string[] keys...); 94 | 95 | Response!(string) migrate(string host, int port, int destinationDB, int timeout, MigrateParams params, string[] keys...); 96 | } 97 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/MultiKeyRedisClusterCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.MultiKeyRedisClusterCommands; 13 | 14 | import hunt.redis.BitOP; 15 | import hunt.redis.RedisPubSub; 16 | import hunt.redis.ScanParams; 17 | import hunt.redis.ScanResult; 18 | import hunt.redis.SortingParams; 19 | import hunt.redis.ZParams; 20 | 21 | import hunt.collection.List; 22 | import hunt.collection.Set; 23 | 24 | import hunt.Long; 25 | 26 | interface MultiKeyRedisClusterCommands { 27 | Long del(string[] keys...); 28 | 29 | Long unlink(string[] keys...); 30 | 31 | Long exists(string[] keys...); 32 | 33 | // List!(string) blpop(int timeout, string[] keys...); 34 | 35 | // List!(string) brpop(int timeout, string[] keys...); 36 | 37 | List!(string) mget(string[] keys...); 38 | 39 | string mset(string[] keysvalues...); 40 | 41 | // long msetnx(string[] keysvalues...); 42 | 43 | // string rename(string oldkey, string newkey); 44 | 45 | // long renamenx(string oldkey, string newkey); 46 | 47 | // string rpoplpush(string srckey, string dstkey); 48 | 49 | // Set!(string) sdiff(string[] keys...); 50 | 51 | // long sdiffstore(string dstkey, string[] keys...); 52 | 53 | // Set!(string) sinter(string[] keys...); 54 | 55 | // long sinterstore(string dstkey, string[] keys...); 56 | 57 | // long smove(string srckey, string dstkey, string member); 58 | 59 | // long sort(string key, SortingParams sortingParameters, string dstkey); 60 | 61 | // long sort(string key, string dstkey); 62 | 63 | // Set!(string) sunion(string[] keys...); 64 | 65 | // long sunionstore(string dstkey, string[] keys...); 66 | 67 | // long zinterstore(string dstkey, string[] sets...); 68 | 69 | // long zinterstore(string dstkey, ZParams params, string[] sets...); 70 | 71 | // long zunionstore(string dstkey, string[] sets...); 72 | 73 | // long zunionstore(string dstkey, ZParams params, string[] sets...); 74 | 75 | // string brpoplpush(string source, string destination, int timeout); 76 | 77 | // long publish(string channel, string message); 78 | 79 | // void subscribe(RedisPubSub redisPubSub, string[] channels...); 80 | 81 | // void psubscribe(RedisPubSub redisPubSub, string[] patterns...); 82 | 83 | // long bitop(BitOP op, string destKey, string[] srcKeys...); 84 | 85 | // string pfmerge(string destkey, string[] sourcekeys...); 86 | 87 | // long pfcount(string[] keys...); 88 | 89 | // long touch(string[] keys...); 90 | 91 | // ScanResult!(string) scan(string cursor, ScanParams params); 92 | 93 | // Set!(string) keys(string pattern); 94 | } 95 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/RedisClusterBinaryScriptingCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.RedisClusterBinaryScriptingCommands; 13 | 14 | import hunt.collection.List; 15 | import hunt.Double; 16 | import hunt.Long; 17 | 18 | interface RedisClusterBinaryScriptingCommands { 19 | // Object eval(const(ubyte)[] script, const(ubyte)[] keyCount, const(ubyte)[][] params...); 20 | 21 | // Object eval(const(ubyte)[] script, int keyCount, const(ubyte)[][] params...); 22 | 23 | // Object eval(const(ubyte)[] script, List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args); 24 | 25 | // /** 26 | // * @param script 27 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 28 | // * @return 29 | // */ 30 | // Object eval(const(ubyte)[] script, const(ubyte)[] sampleKey); 31 | 32 | // /** 33 | // * @param sha1 34 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 35 | // * @return 36 | // */ 37 | // Object evalsha(const(ubyte)[] sha1, const(ubyte)[] sampleKey); 38 | 39 | // Object evalsha(const(ubyte)[] sha1, List!(const(ubyte)[]) keys, List!(const(ubyte)[]) args); 40 | 41 | // Object evalsha(const(ubyte)[] sha1, int keyCount, const(ubyte)[][] params...); 42 | 43 | // /** 44 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 45 | // * @param sha1 46 | // * @return 47 | // */ 48 | // List!(Long) scriptExists(const(ubyte)[] sampleKey, const(ubyte)[][] sha1...); 49 | 50 | // /** 51 | // * @param script 52 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 53 | // * @return 54 | // */ 55 | // const(ubyte)[] scriptLoad(const(ubyte)[] script, const(ubyte)[] sampleKey); 56 | 57 | // /** 58 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 59 | // * @return 60 | // */ 61 | // string scriptFlush(const(ubyte)[] sampleKey); 62 | 63 | // /** 64 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 65 | // * @return 66 | // */ 67 | // string scriptKill(const(ubyte)[] sampleKey); 68 | } 69 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/RedisClusterScriptingCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.RedisClusterScriptingCommands; 13 | 14 | import hunt.collection.List; 15 | import hunt.Boolean; 16 | 17 | 18 | interface RedisClusterScriptingCommands { 19 | // Object eval(string script, int keyCount, string[] params...); 20 | 21 | // Object eval(string script, List!(string) keys, List!(string) args); 22 | 23 | // /** 24 | // * @param script 25 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 26 | // * @return 27 | // */ 28 | // Object eval(string script, string sampleKey); 29 | 30 | // /** 31 | // * @param sha1 32 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 33 | // * @return 34 | // */ 35 | // Object evalsha(string sha1, string sampleKey); 36 | 37 | // Object evalsha(string sha1, List!(string) keys, List!(string) args); 38 | 39 | // Object evalsha(string sha1, int keyCount, string[] params...); 40 | 41 | // /** 42 | // * @param sha1 43 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 44 | // * @return 45 | // */ 46 | // bool scriptExists(string sha1, string sampleKey); 47 | 48 | // /** 49 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 50 | // * @param sha1 51 | // * @return 52 | // */ 53 | // bool[] scriptExists(string sampleKey, string[] sha1...); 54 | 55 | // /** 56 | // * @param script 57 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 58 | // * @return 59 | // */ 60 | // string scriptLoad(string script, string sampleKey); 61 | 62 | // /** 63 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 64 | // * @return 65 | // */ 66 | // string scriptFlush(string sampleKey); 67 | 68 | // /** 69 | // * @param sampleKey Command will be executed in the node where the hash slot of this key is assigned to 70 | // * @return 71 | // */ 72 | // string scriptKill(string sampleKey); 73 | } 74 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/ScriptingCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.ScriptingCommands; 13 | 14 | import hunt.collection.List; 15 | 16 | interface ScriptingCommands { 17 | Object eval(string script, int keyCount, string[] params...); 18 | 19 | Object eval(string script, List!(string) keys, List!(string) args); 20 | 21 | Object eval(string script); 22 | 23 | Object evalsha(string sha1); 24 | 25 | Object evalsha(string sha1, List!(string) keys, List!(string) args); 26 | 27 | Object evalsha(string sha1, int keyCount, string[] params...); 28 | 29 | bool scriptExists(string sha1); 30 | 31 | bool[] scriptExists(string[] sha1...); 32 | 33 | string scriptLoad(string script); 34 | } 35 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/ScriptingCommandsPipeline.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.ScriptingCommandsPipeline; 13 | 14 | import hunt.redis.Response; 15 | 16 | import hunt.collection.List; 17 | 18 | interface ScriptingCommandsPipeline { 19 | Response!(Object) eval(string script, int keyCount, string[] params...); 20 | 21 | Response!(Object) eval(string script, List!(string) keys, List!(string) args); 22 | 23 | Response!(Object) eval(string script); 24 | 25 | Response!(Object) evalsha(string sha1); 26 | 27 | Response!(Object) evalsha(string sha1, List!(string) keys, List!(string) args); 28 | 29 | Response!(Object) evalsha(string sha1, int keyCount, string[] params...); 30 | } 31 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/SentinelCommands.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands.SentinelCommands; 13 | 14 | import hunt.collection.List; 15 | import hunt.collection.Map; 16 | import hunt.Long; 17 | 18 | interface SentinelCommands { 19 | List!(Map!(string, string)) sentinelMasters(); 20 | 21 | List!(string) sentinelGetMasterAddrByName(string masterName); 22 | 23 | Long sentinelReset(string pattern); 24 | 25 | List!(Map!(string, string)) sentinelSlaves(string masterName); 26 | 27 | string sentinelFailover(string masterName); 28 | 29 | string sentinelMonitor(string masterName, string ip, int port, int quorum); 30 | 31 | string sentinelRemove(string masterName); 32 | 33 | string sentinelSet(string masterName, Map!(string, string) parameterMap); 34 | } 35 | -------------------------------------------------------------------------------- /source/hunt/redis/commands/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.commands; 13 | 14 | public import hunt.redis.commands.AdvancedBinaryRedisCommands; 15 | public import hunt.redis.commands.AdvancedRedisCommands; 16 | public import hunt.redis.commands.BasicCommands; 17 | public import hunt.redis.commands.BasicRedisPipeline; 18 | public import hunt.redis.commands.BinaryRedisClusterCommands; 19 | public import hunt.redis.commands.BinaryRedisCommands; 20 | public import hunt.redis.commands.BinaryRedisPipeline; 21 | public import hunt.redis.commands.BinaryScriptingCommands; 22 | public import hunt.redis.commands.BinaryScriptingCommandsPipeline; 23 | public import hunt.redis.commands.ClusterCommands; 24 | public import hunt.redis.commands.ClusterPipeline; 25 | public import hunt.redis.commands.Commands; 26 | public import hunt.redis.commands.ModuleCommands; 27 | public import hunt.redis.commands.MultiKeyBinaryCommands; 28 | public import hunt.redis.commands.MultiKeyBinaryRedisClusterCommands; 29 | public import hunt.redis.commands.MultiKeyBinaryRedisPipeline; 30 | public import hunt.redis.commands.MultiKeyCommands; 31 | public import hunt.redis.commands.MultiKeyCommandsPipeline; 32 | public import hunt.redis.commands.MultiKeyRedisClusterCommands; 33 | public import hunt.redis.Protocol; 34 | public import hunt.redis.commands.RedisClusterBinaryScriptingCommands; 35 | public import hunt.redis.commands.RedisClusterCommands; 36 | public import hunt.redis.commands.RedisClusterScriptingCommands; 37 | public import hunt.redis.commands.RedisCommands; 38 | public import hunt.redis.commands.RedisPipeline; 39 | public import hunt.redis.commands.ScriptingCommands; 40 | public import hunt.redis.commands.ScriptingCommandsPipeline; 41 | public import hunt.redis.commands.SentinelCommands; -------------------------------------------------------------------------------- /source/hunt/redis/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis; 13 | 14 | public import hunt.redis.AbstractClient; 15 | public import hunt.redis.BitOP; 16 | public import hunt.redis.BitPosParams; 17 | public import hunt.redis.Builder; 18 | public import hunt.redis.BuilderFactory; 19 | public import hunt.redis.Client; 20 | public import hunt.redis.ClusterReset; 21 | public import hunt.redis.DebugParams; 22 | public import hunt.redis.Exceptions; 23 | public import hunt.redis.GeoCoordinate; 24 | public import hunt.redis.GeoRadiusResponse; 25 | public import hunt.redis.GeoUnit; 26 | public import hunt.redis.HostAndPort; 27 | public import hunt.redis.ListPosition; 28 | public import hunt.redis.Module; 29 | public import hunt.redis.MultiKeyPipelineBase; 30 | public import hunt.redis.Pipeline; 31 | public import hunt.redis.PipelineBase; 32 | public import hunt.redis.Protocol; 33 | public import hunt.redis.Queable; 34 | public import hunt.redis.Redis; 35 | public import hunt.redis.RedisCluster; 36 | public import hunt.redis.RedisClusterCommand; 37 | public import hunt.redis.RedisClusterConnectionHandler; 38 | public import hunt.redis.RedisClusterHostAndPortMap; 39 | public import hunt.redis.RedisClusterInfoCache; 40 | public import hunt.redis.RedisLock; 41 | public import hunt.redis.RedisMonitor; 42 | public import hunt.redis.RedisPool; 43 | public import hunt.redis.RedisPoolOptions; 44 | public import hunt.redis.RedisPubSub; 45 | public import hunt.redis.RedisSentinelPool; 46 | public import hunt.redis.RedisShardInfo; 47 | public import hunt.redis.RedisSlotBasedConnectionHandler; 48 | public import hunt.redis.Response; 49 | public import hunt.redis.ScanParams; 50 | public import hunt.redis.ScanResult; 51 | public import hunt.redis.ShardedRedis; 52 | public import hunt.redis.ShardedRedisPipeline; 53 | public import hunt.redis.ShardedRedisPool; 54 | public import hunt.redis.SortingParams; 55 | public import hunt.redis.StreamEntry; 56 | public import hunt.redis.StreamEntryID; 57 | public import hunt.redis.StreamPendingEntry; 58 | public import hunt.redis.Transaction; 59 | public import hunt.redis.Tuple; 60 | public import hunt.redis.ZParams; 61 | 62 | public import hunt.redis.commands; 63 | public import hunt.redis.util; -------------------------------------------------------------------------------- /source/hunt/redis/params/ClientKillParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.params.ClientKillParams; 13 | 14 | import hunt.redis.params.Params; 15 | 16 | import std.conv; 17 | import std.variant; 18 | 19 | class ClientKillParams : Params { 20 | 21 | private enum string ID = "ID"; 22 | private enum string TYPE = "TYPE"; 23 | private enum string ADDR = "ADDR"; 24 | private enum string SKIPME = "SKIPME"; 25 | 26 | alias addParam = Params.addParam; 27 | 28 | static enum Type { 29 | NORMAL, 30 | MASTER, 31 | SLAVE, 32 | PUBSUB 33 | } 34 | 35 | static enum SkipMe { 36 | YES, 37 | NO 38 | } 39 | 40 | this() { 41 | } 42 | 43 | static ClientKillParams clientKillParams() { 44 | return new ClientKillParams(); 45 | } 46 | 47 | ClientKillParams id(string clientId) { 48 | addParam(ID, clientId); 49 | return this; 50 | } 51 | 52 | ClientKillParams id(byte[] clientId) { 53 | addParam(ID, clientId); 54 | return this; 55 | } 56 | 57 | ClientKillParams type(Type type) { 58 | addParam(TYPE, Variant(type)); 59 | return this; 60 | } 61 | 62 | ClientKillParams addr(string ipPort) { 63 | addParam(ADDR, ipPort); 64 | return this; 65 | } 66 | 67 | ClientKillParams addr(byte[] ipPort) { 68 | addParam(ADDR, ipPort); 69 | return this; 70 | } 71 | 72 | ClientKillParams addr(string ip, int port) { 73 | addParam(ADDR, ip ~ ":" ~ port.to!string()); 74 | return this; 75 | } 76 | 77 | ClientKillParams skipMe(SkipMe skipMe) { 78 | addParam(SKIPME, Variant(skipMe)); 79 | return this; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /source/hunt/redis/params/GeoRadiusParam.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.params.GeoRadiusParam; 13 | 14 | import hunt.redis.params.Params; 15 | 16 | import hunt.redis.Protocol; 17 | import hunt.redis.util.SafeEncoder; 18 | 19 | import hunt.collection.ArrayList; 20 | import hunt.Integer; 21 | 22 | class GeoRadiusParam : Params { 23 | private enum string WITHCOORD = "withcoord"; 24 | private enum string WITHDIST = "withdist"; 25 | 26 | // Do not add WITHHASH since we can't classify result of WITHHASH and WITHDIST, 27 | // and WITHHASH is for debugging purposes 28 | 29 | private enum string ASC = "asc"; 30 | private enum string DESC = "desc"; 31 | private enum string COUNT = "count"; 32 | 33 | alias addParam = Params.addParam; 34 | alias getByteParams = Params.getByteParams; 35 | 36 | this() { 37 | } 38 | 39 | static GeoRadiusParam geoRadiusParam() { 40 | return new GeoRadiusParam(); 41 | } 42 | 43 | GeoRadiusParam withCoord() { 44 | addParam(WITHCOORD); 45 | return this; 46 | } 47 | 48 | GeoRadiusParam withDist() { 49 | addParam(WITHDIST); 50 | return this; 51 | } 52 | 53 | GeoRadiusParam sortAscending() { 54 | addParam(ASC); 55 | return this; 56 | } 57 | 58 | GeoRadiusParam sortDescending() { 59 | addParam(DESC); 60 | return this; 61 | } 62 | 63 | GeoRadiusParam count(int count) { 64 | if (count > 0) { 65 | addParam(COUNT, count); 66 | } 67 | return this; 68 | } 69 | 70 | const(ubyte)[][] getByteParams(const(ubyte)[][] args...) { 71 | ArrayList!(const(ubyte)[]) byteParams = new ArrayList!(const(ubyte)[])(); 72 | foreach (const(ubyte)[] arg; args) { 73 | byteParams.add(arg); 74 | } 75 | 76 | if (contains(WITHCOORD)) { 77 | byteParams.add(SafeEncoder.encode(WITHCOORD)); 78 | } 79 | if (contains(WITHDIST)) { 80 | byteParams.add(SafeEncoder.encode(WITHDIST)); 81 | } 82 | 83 | if (contains(COUNT)) { 84 | byteParams.add(SafeEncoder.encode(COUNT)); 85 | byteParams.add(Protocol.toByteArray(getParam!int(COUNT))); 86 | } 87 | 88 | if (contains(ASC)) { 89 | byteParams.add(SafeEncoder.encode(ASC)); 90 | } else if (contains(DESC)) { 91 | byteParams.add(SafeEncoder.encode(DESC)); 92 | } 93 | 94 | return byteParams.toArray(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /source/hunt/redis/params/MigrateParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.params.MigrateParams; 13 | 14 | import hunt.redis.params.Params; 15 | 16 | class MigrateParams : Params { 17 | 18 | private enum string COPY = "COPY"; 19 | private enum string REPLACE = "REPLACE"; 20 | private enum string AUTH = "AUTH"; 21 | 22 | this() { 23 | } 24 | 25 | static MigrateParams migrateParams() { 26 | return new MigrateParams(); 27 | } 28 | 29 | MigrateParams copy() { 30 | addParam(COPY); 31 | return this; 32 | } 33 | 34 | MigrateParams replace() { 35 | addParam(REPLACE); 36 | return this; 37 | } 38 | 39 | MigrateParams auth(string password) { 40 | addParam(AUTH, password); 41 | return this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/hunt/redis/params/Params.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.params.Params; 13 | 14 | import hunt.collection.ArrayList; 15 | import hunt.collection.HashMap; 16 | import hunt.collection.Map; 17 | import hunt.Exceptions; 18 | 19 | import hunt.redis.util.SafeEncoder; 20 | 21 | import hunt.String; 22 | import hunt.Byte; 23 | import hunt.Integer; 24 | 25 | import std.array; 26 | import std.conv; 27 | import std.variant; 28 | 29 | abstract class Params { 30 | 31 | private Map!(string, Variant) params; 32 | 33 | this() { 34 | params = new HashMap!(string, Variant)(); 35 | } 36 | 37 | T getParam(T)(string name) { 38 | if (params is null || params.isEmpty()) 39 | return T.init; 40 | Variant v = params.get(name); 41 | return v.get!T(); 42 | } 43 | 44 | const(ubyte)[][] getByteParams() { 45 | if (params is null) 46 | return null; 47 | ArrayList!(const(ubyte)[]) byteParams = new ArrayList!(const(ubyte)[])(); 48 | 49 | foreach(string key, Variant value; params) { 50 | byteParams.add(SafeEncoder.encode(key)); 51 | 52 | if (value.hasValue()) { 53 | 54 | // byteParams.add(value.toString()); 55 | if (value.type == typeid(byte[]) || value.type == typeid(const(ubyte)[])) { 56 | byteParams.add(value.get!(const(ubyte)[])()); 57 | } else { 58 | byteParams.add(SafeEncoder.encode(to!string(value))); 59 | } 60 | } 61 | } 62 | 63 | return byteParams.toArray(); 64 | 65 | // return byteParams.toArray(new byte[byteParams.size()][]); 66 | } 67 | 68 | protected bool contains(string name) { 69 | if (params is null) 70 | return false; 71 | 72 | return params.containsKey(name); 73 | } 74 | 75 | protected void addParam(string name, string value) { 76 | params.put(name, Variant(value)); 77 | } 78 | 79 | protected void addParam(string name, int value) { 80 | params.put(name, Variant(value)); 81 | } 82 | 83 | protected void addParam(string name, long value) { 84 | params.put(name, Variant(value)); 85 | } 86 | 87 | protected void addParam(string name, byte[] value) { 88 | params.put(name, Variant(value)); 89 | } 90 | 91 | protected void addParam(string name, Object value) { 92 | params.put(name, Variant(value)); 93 | } 94 | 95 | protected void addParam(string name, Variant value) { 96 | params.put(name, value); 97 | } 98 | 99 | protected void addParam(string name) { 100 | params.put(name, Variant(null)); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /source/hunt/redis/params/SetParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.params.SetParams; 13 | 14 | import hunt.redis.params.Params; 15 | 16 | import hunt.collection.ArrayList; 17 | import hunt.io.BufferUtils; 18 | 19 | import hunt.redis.util.SafeEncoder; 20 | 21 | import std.conv; 22 | 23 | class SetParams : Params { 24 | 25 | private enum string XX = "xx"; 26 | private enum string NX = "nx"; 27 | private enum string PX = "px"; 28 | private enum string EX = "ex"; 29 | 30 | alias getByteParams = Params.getByteParams; 31 | 32 | this() { 33 | } 34 | 35 | static SetParams setParams() { 36 | return new SetParams(); 37 | } 38 | 39 | /** 40 | * Set the specified expire time, in seconds. 41 | * @param secondsToExpire 42 | * @return SetParams 43 | */ 44 | SetParams ex(int secondsToExpire) { 45 | addParam(EX, secondsToExpire); 46 | return this; 47 | } 48 | 49 | /** 50 | * Set the specified expire time, in milliseconds. 51 | * @param millisecondsToExpire 52 | * @return SetParams 53 | */ 54 | SetParams px(long millisecondsToExpire) { 55 | addParam(PX, millisecondsToExpire); 56 | return this; 57 | } 58 | 59 | /** 60 | * Only set the key if it does not already exist. 61 | * @return SetParams 62 | */ 63 | SetParams nx() { 64 | addParam(NX); 65 | return this; 66 | } 67 | 68 | /** 69 | * Only set the key if it already exist. 70 | * @return SetParams 71 | */ 72 | SetParams xx() { 73 | addParam(XX); 74 | return this; 75 | } 76 | 77 | const(ubyte)[][] getByteParams(const(ubyte)[][] args...) { 78 | ArrayList!(const(ubyte)[]) byteParams = new ArrayList!(const(ubyte)[])(); 79 | foreach(const(ubyte)[] arg ; args) { 80 | byteParams.add(arg); 81 | } 82 | 83 | if (contains(NX)) { 84 | byteParams.add(SafeEncoder.encode(NX)); 85 | } 86 | if (contains(XX)) { 87 | byteParams.add(SafeEncoder.encode(XX)); 88 | } 89 | 90 | if (contains(EX)) { 91 | byteParams.add(SafeEncoder.encode(EX)); 92 | int v = getParam!int(EX); 93 | byteParams.add(SafeEncoder.encode(v.to!string())); 94 | } 95 | 96 | if (contains(PX)) { 97 | byteParams.add(SafeEncoder.encode(PX)); 98 | long v = getParam!long(PX); 99 | byteParams.add(SafeEncoder.encode(v.to!string())); 100 | } 101 | 102 | return byteParams.toArray(); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /source/hunt/redis/params/ZAddParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.params.ZAddParams; 13 | 14 | import hunt.redis.params.Params; 15 | 16 | import hunt.redis.util.SafeEncoder; 17 | 18 | import hunt.collection.ArrayList; 19 | 20 | class ZAddParams : Params { 21 | 22 | private enum string XX = "xx"; 23 | private enum string NX = "nx"; 24 | private enum string CH = "ch"; 25 | 26 | this() { 27 | } 28 | 29 | static ZAddParams zAddParams() { 30 | return new ZAddParams(); 31 | } 32 | 33 | /** 34 | * Only set the key if it does not already exist. 35 | * @return ZAddParams 36 | */ 37 | ZAddParams nx() { 38 | addParam(NX); 39 | return this; 40 | } 41 | 42 | /** 43 | * Only set the key if it already exist. 44 | * @return ZAddParams 45 | */ 46 | ZAddParams xx() { 47 | addParam(XX); 48 | return this; 49 | } 50 | 51 | /** 52 | * Modify the return value from the number of new elements added to the total number of elements 53 | * changed 54 | * @return ZAddParams 55 | */ 56 | ZAddParams ch() { 57 | addParam(CH); 58 | return this; 59 | } 60 | 61 | const(ubyte)[][] getByteParams(const(ubyte)[] key, const(ubyte)[][] args...) { 62 | ArrayList!(const(ubyte)[]) byteParams = new ArrayList!(const(ubyte)[])(); 63 | byteParams.add(key); 64 | 65 | if (contains(NX)) { 66 | byteParams.add(SafeEncoder.encode(NX)); 67 | } 68 | if (contains(XX)) { 69 | byteParams.add(SafeEncoder.encode(XX)); 70 | } 71 | if (contains(CH)) { 72 | byteParams.add(SafeEncoder.encode(CH)); 73 | } 74 | 75 | foreach (const(ubyte)[] arg; args) { 76 | byteParams.add(arg); 77 | } 78 | 79 | return byteParams.toArray(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /source/hunt/redis/params/ZIncrByParams.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.params.ZIncrByParams; 13 | 14 | import hunt.redis.params.Params; 15 | import hunt.redis.util.SafeEncoder; 16 | 17 | import hunt.collection.ArrayList; 18 | 19 | /** 20 | * Parameters for ZINCRBY commands
21 | *
22 | * In fact, Redis doesn't have parameters for ZINCRBY. Instead Redis has INCR parameter for ZADD.
23 | * When users call ZADD with INCR option, its restriction (only one member) and return type is same 24 | * to ZINCRBY.
25 | * Document page for ZADD also describes INCR option to act like ZINCRBY.
26 | * http://redis.io/commands/zadd
27 | *
28 | * So we decided to wrap "ZADD with INCR option" to ZINCRBY.
29 | * https://github.com/xetorthio/redis/issues/1067
30 | *
31 | * Works with Redis 3.0.2 and onwards. 32 | */ 33 | class ZIncrByParams : Params { 34 | 35 | private enum string XX = "xx"; 36 | private enum string NX = "nx"; 37 | private enum string INCR = "incr"; 38 | 39 | this() { 40 | } 41 | 42 | static ZIncrByParams zIncrByParams() { 43 | return new ZIncrByParams(); 44 | } 45 | 46 | /** 47 | * Only set the key if it does not already exist. 48 | * @return ZIncrByParams 49 | */ 50 | ZIncrByParams nx() { 51 | addParam(NX); 52 | return this; 53 | } 54 | 55 | /** 56 | * Only set the key if it already exist. 57 | * @return ZIncrByParams 58 | */ 59 | ZIncrByParams xx() { 60 | addParam(XX); 61 | return this; 62 | } 63 | 64 | const(ubyte)[][] getByteParams(const(ubyte)[] key, const(ubyte)[][] args...) { 65 | ArrayList!(const(ubyte)[]) byteParams = new ArrayList!(const(ubyte)[])(); 66 | byteParams.add(key); 67 | 68 | if (contains(NX)) { 69 | byteParams.add(SafeEncoder.encode(NX)); 70 | } 71 | if (contains(XX)) { 72 | byteParams.add(SafeEncoder.encode(XX)); 73 | } 74 | 75 | byteParams.add(SafeEncoder.encode(INCR)); 76 | 77 | foreach (const(ubyte)[] arg; args) { 78 | byteParams.add(arg); 79 | } 80 | 81 | return byteParams.toArray(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /source/hunt/redis/params/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.params; 13 | 14 | public import hunt.redis.params.ClientKillParams; 15 | public import hunt.redis.params.GeoRadiusParam; 16 | public import hunt.redis.params.MigrateParams; 17 | public import hunt.redis.params.Params; 18 | public import hunt.redis.params.SetParams; 19 | public import hunt.redis.params.ZAddParams; 20 | public import hunt.redis.params.ZIncrByParams; -------------------------------------------------------------------------------- /source/hunt/redis/util/ByteArrayComparator.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.ByteArrayComparator; 13 | 14 | // class ByteArrayComparator { 15 | // private this() { 16 | // throw new InstantiationError( "Must not instantiate this class" ); 17 | // } 18 | 19 | // static int compare(byte[] val1, byte[] val2) { 20 | // int len1 = val1.length; 21 | // int len2 = val2.length; 22 | // int lmin = Math.min(len1, len2); 23 | 24 | // for (int i = 0; i < lmin; i++) { 25 | // byte b1 = val1[i]; 26 | // byte b2 = val2[i]; 27 | // if(b1 < b2) return -1; 28 | // if(b1 > b2) return 1; 29 | // } 30 | 31 | // if(len1 < len2) return -1; 32 | // if(len1 > len2) return 1; 33 | // return 0; 34 | // } 35 | // } 36 | -------------------------------------------------------------------------------- /source/hunt/redis/util/Hashing.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.Hashing; 13 | 14 | import hunt.Exceptions; 15 | import hunt.redis.util.MurmurHash; 16 | 17 | import std.concurrency : initOnce; 18 | import std.digest.md; 19 | 20 | import object; 21 | 22 | interface Hashing { 23 | static Hashing MURMUR_HASH() { 24 | __gshared Hashing inst; 25 | return initOnce!inst(new MurmurHash()); 26 | } 27 | 28 | static Hashing MD5() { 29 | __gshared Hashing inst; 30 | return initOnce!inst(new class Hashing { 31 | override long hash(string key) { 32 | return cast(long)hashOf(key); 33 | } 34 | 35 | override 36 | long hash(const(ubyte)[] key) { 37 | ubyte[] bKey = md5Of(key).dup; 38 | long res = (cast(long) (bKey[3] & 0xFF) << 24) | (cast(long) (bKey[2] & 0xFF) << 16) 39 | | (cast(long) (bKey[1] & 0xFF) << 8) | cast(long) (bKey[0] & 0xFF); 40 | return res; 41 | } 42 | }); 43 | } 44 | 45 | 46 | long hash(string key); 47 | 48 | long hash(const(ubyte)[] key); 49 | } -------------------------------------------------------------------------------- /source/hunt/redis/util/IOUtils.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.IOUtils; 13 | 14 | import hunt.Exceptions; 15 | import hunt.logging.ConsoleLogger; 16 | 17 | import std.socket; 18 | 19 | class IOUtils { 20 | private this() { 21 | } 22 | 23 | static void closeQuietly(Socket sock) { 24 | // It's same thing as Apache Commons - IOUtils.closeQuietly() 25 | if (sock !is null) { 26 | try { 27 | sock.close(); 28 | } catch (IOException e) { 29 | version(HUNT_DEBUG) warning(e); 30 | // ignored 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/hunt/redis/util/KeyMergeUtil.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.KeyMergeUtil; 13 | 14 | import hunt.Exceptions; 15 | 16 | class KeyMergeUtil { 17 | private this(){ 18 | throw new InstantiationError( "Must not instantiate this class" ); 19 | } 20 | 21 | static string[] merge(string destKey, string[] keys) { 22 | string[] mergedKeys = new string[keys.length + 1]; 23 | mergedKeys[0] = destKey; 24 | // System.arraycopy(keys, 0, mergedKeys, 1, keys.length); 25 | size_t len = keys.length; 26 | mergedKeys[1 .. 1+len] = keys[0 .. $]; 27 | return mergedKeys; 28 | } 29 | 30 | static byte[][] merge(byte[] destKey, byte[][] keys) { 31 | byte[][] mergedKeys = new byte[][keys.length + 1]; 32 | mergedKeys[0] = destKey; 33 | // System.arraycopy(keys, 0, mergedKeys, 1, keys.length); 34 | size_t len = keys.length; 35 | mergedKeys[1 .. 1+len] = keys[0 .. $]; 36 | return mergedKeys; 37 | } 38 | } -------------------------------------------------------------------------------- /source/hunt/redis/util/MurmurHash.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.MurmurHash; 13 | 14 | import hunt.redis.util.SafeEncoder; 15 | import hunt.redis.util.Hashing; 16 | 17 | import hunt.Byte; 18 | import hunt.io.ByteBuffer; 19 | import hunt.io.BufferUtils; 20 | import hunt.util.ByteOrder; 21 | 22 | /** 23 | * This is a very fast, non-cryptographic hash suitable for general hash-based lookup. See 24 | * http://murmurhash.googlepages.com/ for more details.
25 | *

26 | * The C version of MurmurHash 2.0 found at that site was ported to Java by Andrzej Bialecki (ab at 27 | * getopt org). 28 | *

29 | */ 30 | class MurmurHash : Hashing { 31 | /** 32 | * Hashes bytes in an array. 33 | * @param data The bytes to hash. 34 | * @param seed The seed for the hash. 35 | * @return The 32 bit hash of the bytes in question. 36 | */ 37 | static int hash(const(ubyte)[] data, int seed) { 38 | return hash(BufferUtils.toBuffer(cast(byte[])data), seed); 39 | } 40 | 41 | /** 42 | * Hashes bytes in part of an array. 43 | * @param data The data to hash. 44 | * @param offset Where to start munging. 45 | * @param length How many bytes to process. 46 | * @param seed The seed to start with. 47 | * @return The 32-bit hash of the data in question. 48 | */ 49 | static int hash(const(ubyte)[] data, int offset, int length, int seed) { 50 | return hash(BufferUtils.toBuffer(cast(byte[])data, offset, length), seed); 51 | } 52 | 53 | /** 54 | * Hashes the bytes in a buffer from the current position to the limit. 55 | * @param buf The bytes to hash. 56 | * @param seed The seed for the hash. 57 | * @return The 32 bit murmur hash of the bytes in the buffer. 58 | */ 59 | static int hash(ByteBuffer buf, int seed) { 60 | // save byte order for later restoration 61 | ByteOrder byteOrder = buf.order(); 62 | buf.order(ByteOrder.LittleEndian); 63 | 64 | int m = 0x5bd1e995; 65 | int r = 24; 66 | 67 | int h = seed ^ buf.remaining(); 68 | 69 | int k; 70 | while (buf.remaining() >= 4) { 71 | k = buf.getInt(); 72 | 73 | k *= m; 74 | k ^= k >>> r; 75 | k *= m; 76 | 77 | h *= m; 78 | h ^= k; 79 | } 80 | 81 | if (buf.remaining() > 0) { 82 | ByteBuffer finish = BufferUtils.allocate(4).order(ByteOrder.LittleEndian); 83 | // for big-endian version, use this first: 84 | // finish.position(4-buf.remaining()); 85 | finish.put(buf).rewind(); 86 | h ^= finish.getInt(); 87 | h *= m; 88 | } 89 | 90 | h ^= h >>> 13; 91 | h *= m; 92 | h ^= h >>> 15; 93 | 94 | buf.order(byteOrder); 95 | return h; 96 | } 97 | 98 | static long hash64A(const(ubyte)[] data, int seed) { 99 | return hash64A(BufferUtils.toBuffer(cast(byte[])data), seed); 100 | } 101 | 102 | static long hash64A(const(ubyte)[] data, int offset, int length, int seed) { 103 | return hash64A(BufferUtils.toBuffer(cast(byte[])data, offset, length), seed); 104 | } 105 | 106 | static long hash64A(ByteBuffer buf, int seed) { 107 | ByteOrder byteOrder = buf.order(); 108 | buf.order(ByteOrder.LittleEndian); 109 | 110 | long m = 0xc6a4a7935bd1e995L; 111 | int r = 47; 112 | 113 | long h = seed ^ (buf.remaining() * m); 114 | 115 | long k; 116 | while (buf.remaining() >= 8) { 117 | k = buf.getLong(); 118 | 119 | k *= m; 120 | k ^= k >>> r; 121 | k *= m; 122 | 123 | h ^= k; 124 | h *= m; 125 | } 126 | 127 | if (buf.remaining() > 0) { 128 | ByteBuffer finish = BufferUtils.allocate(8).order(ByteOrder.LittleEndian); 129 | // for big-endian version, do this first: 130 | // finish.position(8-buf.remaining()); 131 | finish.put(buf).rewind(); 132 | h ^= finish.getLong(); 133 | h *= m; 134 | } 135 | 136 | h ^= h >>> r; 137 | h *= m; 138 | h ^= h >>> r; 139 | 140 | buf.order(byteOrder); 141 | return h; 142 | } 143 | 144 | override long hash(const(ubyte)[] key) { 145 | return hash64A(key, 0x1234ABCD); 146 | } 147 | 148 | override long hash(string key) { 149 | return hash(SafeEncoder.encode(key)); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /source/hunt/redis/util/RedisByteHashMap.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.RedisByteHashMap; 13 | 14 | import hunt.util.ArrayHelper; 15 | import hunt.collection.Collection; 16 | import hunt.collection.HashMap; 17 | import hunt.collection.HashSet; 18 | import hunt.collection.Iterator; 19 | import hunt.collection.Map; 20 | import hunt.collection.Set; 21 | 22 | import hunt.Byte; 23 | 24 | alias RedisByteHashMap = HashMap!(const(ubyte)[], const(ubyte)[]); 25 | 26 | // class RedisByteHashMap : Map!(const(ubyte)[], const(ubyte)[]), Cloneable { // , Serializable 27 | // private Map!(Bytes, const(ubyte)[]) internalMap = new HashMap!(ByteArrayWrapper, const(ubyte)[])(); 28 | 29 | // this() { 30 | // internalMap = new HashMap!(Bytes, const(ubyte)[])(); 31 | // } 32 | 33 | // override 34 | // void clear() { 35 | // internalMap.clear(); 36 | // } 37 | 38 | // override 39 | // bool containsKey(const(ubyte)[] key) { 40 | // // if (key instanceof const(ubyte)[]) return internalMap.containsKey(new ByteArrayWrapper((const(ubyte)[]) key)); 41 | // // return internalMap.containsKey(key); 42 | // return internalMap.containsKey(new Bytes(key)); 43 | // } 44 | 45 | // override 46 | // bool containsValue(const(ubyte)[] value) { 47 | // return internalMap.containsValue(value); 48 | // } 49 | 50 | // // override 51 | // // Set entrySet() { 52 | // // Iterator iterator = internalMap.entrySet() 53 | // // .iterator(); 54 | // // HashSet!(Entry!(const(ubyte)[], const(ubyte)[])) hashSet = new HashSet(); 55 | // // while (iterator.hasNext()) { 56 | // // Entry!(ByteArrayWrapper, const(ubyte)[]) entry = iterator.next(); 57 | // // hashSet.add(new RedisByteEntry(entry.getKey().data, entry.getValue())); 58 | // // } 59 | // // return hashSet; 60 | // // } 61 | 62 | // override 63 | // const(ubyte)[] get(const(ubyte)[] key) { 64 | // // if (key instanceof const(ubyte)[]) return internalMap.get(new ByteArrayWrapper((const(ubyte)[]) key)); 65 | // // return internalMap.get(key); 66 | // return internalMap.get(new Bytes(key)); 67 | // } 68 | 69 | // override 70 | // bool isEmpty() { 71 | // return internalMap.isEmpty(); 72 | // } 73 | 74 | 75 | // override InputRange!(const(ubyte)[]) byKey() { 76 | // return internalMap.byKey(); 77 | // } 78 | 79 | // override InputRange!(const(ubyte)[]) byValue() { 80 | // return internalMap.byValue(); 81 | // } 82 | 83 | // override int opApply(scope int delegate(const(ubyte)[], const(ubyte)[]) dg) { 84 | // int r = 0; 85 | // foreach(Bytes key, const(ubyte)[] value; internalMap) { 86 | // r = dg(key.value, value); 87 | // } 88 | 89 | // return r; 90 | // } 91 | 92 | // override 93 | // const(ubyte)[] put(const(ubyte)[] key, const(ubyte)[] value) { 94 | // return internalMap.put(new Bytes(key), value); 95 | // } 96 | 97 | // // override 98 | 99 | // // void putAll(Map m) { 100 | // // Iterator iterator = m.entrySet().iterator(); 101 | // // while (iterator.hasNext()) { 102 | // // Entry next = (Entry) iterator 103 | // // .next(); 104 | // // internalMap.put(new ByteArrayWrapper(next.getKey()), next.getValue()); 105 | // // } 106 | // // } 107 | 108 | // override 109 | // const(ubyte)[] remove(const(ubyte)[] key) { 110 | // return internalMap.remove(new Bytes(key)); 111 | // } 112 | 113 | // override 114 | // int size() { 115 | // return internalMap.size(); 116 | // } 117 | 118 | // override 119 | // const(ubyte)[][] values() { 120 | // return internalMap.values(); 121 | // } 122 | 123 | // } 124 | -------------------------------------------------------------------------------- /source/hunt/redis/util/RedisClusterCRC16.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.RedisClusterCRC16; 13 | 14 | import hunt.redis.util.RedisClusterHashTagUtil; 15 | import hunt.redis.util.SafeEncoder; 16 | 17 | import hunt.Exceptions; 18 | import hunt.redis.Exceptions; 19 | 20 | /** 21 | * CRC16 Implementation according to CCITT standard Polynomial : 1021 (x^16 + x^12 + x^5 + 1) See Appendix A. CRC16 reference implementation in ANSI 23 | * C 24 | */ 25 | class RedisClusterCRC16 { 26 | 27 | // dfmt off 28 | private enum int[] LOOKUP_TABLE = [0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 29 | 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 30 | 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 31 | 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 32 | 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 33 | 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 34 | 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 35 | 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 36 | 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 37 | 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 38 | 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 39 | 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 40 | 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 41 | 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 42 | 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 43 | 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 44 | 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 45 | 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 46 | 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 47 | 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 48 | 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 49 | 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 50 | 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 51 | 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0]; 52 | // dfmt on 53 | 54 | private this() { 55 | throw new InstantiationError("Must not instantiate this class"); 56 | } 57 | 58 | static int getSlot(string key) { 59 | if (key is null) { 60 | throw new RedisClusterOperationException("Slot calculation of null is impossible"); 61 | } 62 | 63 | key = RedisClusterHashTagUtil.getHashTag(key); 64 | // optimization with modulo operator with power of 2 equivalent to getCRC16(key) % 16384 65 | return getCRC16(key) & (16384 - 1); 66 | } 67 | 68 | static int getSlot(const(ubyte)[] key) { 69 | if (key is null) { 70 | throw new RedisClusterOperationException("Slot calculation of null is impossible"); 71 | } 72 | 73 | int s = -1; 74 | int e = -1; 75 | bool sFound = false; 76 | for (int i = 0; i < key.length; i++) { 77 | if (key[i] == '{' && !sFound) { 78 | s = i; 79 | sFound = true; 80 | } 81 | if (key[i] == '}' && sFound) { 82 | e = i; 83 | break; 84 | } 85 | } 86 | if (s > -1 && e > -1 && e != s + 1) { 87 | return getCRC16(key, s + 1, e) & (16384 - 1); 88 | } 89 | return getCRC16(key) & (16384 - 1); 90 | } 91 | 92 | /** 93 | * Create a CRC16 checksum from the bytes. implementation is from mp911de/lettuce, modified with 94 | * some more optimizations 95 | * @param bytes 96 | * @param s 97 | * @param e 98 | * @return CRC16 as integer value See Issue 733 100 | */ 101 | static int getCRC16(const(ubyte)[] bytes, size_t s, size_t e) { 102 | int crc = 0x0000; 103 | 104 | for (size_t i = s; i < e; i++) { 105 | crc = ((crc << 8) ^ LOOKUP_TABLE[((crc >>> 8) ^ (bytes[i] & 0xFF)) & 0xFF]); 106 | } 107 | return crc & 0xFFFF; 108 | } 109 | 110 | static int getCRC16(const(ubyte)[] bytes) { 111 | return getCRC16(bytes, 0, bytes.length); 112 | } 113 | 114 | static int getCRC16(string key) { 115 | const(ubyte)[] bytesKey = SafeEncoder.encode(key); 116 | return getCRC16(bytesKey, 0, bytesKey.length); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /source/hunt/redis/util/RedisClusterHashTagUtil.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.RedisClusterHashTagUtil; 13 | 14 | import hunt.redis.util.SafeEncoder; 15 | 16 | import hunt.Exceptions; 17 | 18 | import std.array; 19 | import std.range; 20 | import std.string; 21 | 22 | /** 23 | * Holds various methods/utilities to manipualte and parse redis hash-tags. See Cluster-Spec : Keys hash tags 25 | */ 26 | class RedisClusterHashTagUtil { 27 | 28 | private this() { 29 | throw new InstantiationError("Must not instantiate this class"); 30 | } 31 | 32 | static string getHashTag(string key) { 33 | return extractHashTag(key, true); 34 | } 35 | 36 | static bool isClusterCompliantMatchPattern(const(ubyte)[] matchPattern) { 37 | return isClusterCompliantMatchPattern(SafeEncoder.encode(matchPattern)); 38 | } 39 | 40 | static bool isClusterCompliantMatchPattern(string matchPattern) { 41 | string tag = extractHashTag(matchPattern, false); 42 | return !tag.empty(); 43 | } 44 | 45 | private static string extractHashTag(string key, bool returnKeyOnAbsence) { 46 | ptrdiff_t s = key.indexOf("{"); 47 | if (s > -1) { 48 | ptrdiff_t e = key.indexOf("}", s + 1); 49 | if (e > -1 && e != s + 1) { 50 | return key[s + 1 .. e]; 51 | } 52 | } 53 | return returnKeyOnAbsence ? key : null; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /source/hunt/redis/util/RedisOutputStream.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.RedisOutputStream; 13 | 14 | import hunt.Exceptions; 15 | import hunt.logging.ConsoleLogger; 16 | import hunt.stream.Common; 17 | import hunt.stream.FilterInputStream; 18 | import hunt.stream.FilterOutputStream; 19 | 20 | 21 | /** 22 | * The class implements a buffered output stream without synchronization There are also special 23 | * operations like in-place string encoding. This stream fully ignore mark/reset and should not be 24 | * used outside Redis 25 | */ 26 | class RedisOutputStream : FilterOutputStream { 27 | protected byte[] buf; 28 | 29 | protected int count; 30 | 31 | // dfmt off 32 | private enum int[] sizeTable = [ 33 | 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, int.max 34 | ]; 35 | 36 | private enum byte[] DigitTens = [ 37 | '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', 38 | '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', 39 | '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', 40 | '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', 41 | '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', 42 | '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', 43 | '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', 44 | '9', '9', '9', '9', '9', '9', '9', '9', '9' 45 | ]; 46 | 47 | private enum byte[] DigitOnes = [ 48 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', 49 | '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', 50 | '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', 51 | '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', 52 | '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', 53 | '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', 54 | '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 55 | '1', '2', '3', '4', '5', '6', '7', '8', '9' 56 | ]; 57 | 58 | private enum byte[] digits = [ 59 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 60 | 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 61 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' 62 | ]; 63 | // dfmt on 64 | 65 | this(OutputStream outputStream) { 66 | this(outputStream, 8192); 67 | } 68 | 69 | this(OutputStream outputStream, int size) { 70 | super(outputStream); 71 | if (size <= 0) { 72 | throw new IllegalArgumentException("Buffer size <= 0"); 73 | } 74 | buf = new byte[size]; 75 | } 76 | 77 | private void flushBuffer() { 78 | if (count > 0) { 79 | version(HUNT_REDIS_DEBUG_MORE) { 80 | if(count<32) { 81 | tracef("outgoing: %s", cast(string)buf[0 .. count]); 82 | } else { 83 | tracef("outgoing: %s", cast(string)buf[0 .. 32]); 84 | } 85 | } 86 | outputStream.write(buf, 0, count); 87 | count = 0; 88 | } 89 | } 90 | 91 | void write(byte b) { 92 | if (count == buf.length) { 93 | flushBuffer(); 94 | } 95 | buf[count++] = b; 96 | } 97 | 98 | alias write = FilterOutputStream.write; 99 | alias write = OutputStream.write; 100 | 101 | override void write(byte[] b) { 102 | write(b, 0, cast(int) b.length); 103 | } 104 | 105 | override void write(byte[] b, int off, int len) { 106 | version(HUNT_REDIS_DEBUG_MORE) { 107 | infof("%d bytes: %(%02X %)", len, b[off .. off+len]); 108 | } 109 | 110 | if (len >= buf.length) { 111 | flushBuffer(); 112 | outputStream.write(b, off, len); 113 | } else { 114 | if (len >= buf.length - count) { 115 | flushBuffer(); 116 | } 117 | 118 | // System.arraycopy(b, off, buf, count, len); 119 | buf[count .. count + len] = b[off .. off + len]; 120 | count += len; 121 | } 122 | } 123 | 124 | void writeCrLf() { 125 | if (2 >= buf.length - count) { 126 | flushBuffer(); 127 | } 128 | 129 | buf[count++] = '\r'; 130 | buf[count++] = '\n'; 131 | } 132 | 133 | void writeIntCrLf(int value) { 134 | if (value < 0) { 135 | write(cast(byte) '-'); 136 | value = -value; 137 | } 138 | 139 | int size = 0; 140 | while (value > sizeTable[size]) 141 | size++; 142 | 143 | size++; 144 | if (size >= buf.length - count) { 145 | flushBuffer(); 146 | } 147 | 148 | int q, r; 149 | int charPos = count + size; 150 | 151 | while (value >= 65536) { 152 | q = value / 100; 153 | r = value - ((q << 6) + (q << 5) + (q << 2)); 154 | value = q; 155 | buf[--charPos] = DigitOnes[r]; 156 | buf[--charPos] = DigitTens[r]; 157 | } 158 | 159 | for (;;) { 160 | q = (value * 52429) >>> (16 + 3); 161 | r = value - ((q << 3) + (q << 1)); 162 | buf[--charPos] = digits[r]; 163 | value = q; 164 | if (value == 0) 165 | break; 166 | } 167 | count += size; 168 | 169 | writeCrLf(); 170 | } 171 | 172 | override void flush() { 173 | flushBuffer(); 174 | outputStream.flush(); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /source/hunt/redis/util/RedisURIHelper.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.RedisURIHelper; 13 | 14 | import hunt.net.util.HttpURI; 15 | import hunt.Exceptions; 16 | 17 | import std.array; 18 | import std.conv; 19 | import std.string; 20 | 21 | /** 22 | * 23 | */ 24 | class RedisURIHelper { 25 | 26 | private enum int DEFAULT_DB = 0; 27 | 28 | private enum string REDIS = "redis"; 29 | private enum string REDISS = "rediss"; 30 | 31 | private this(){ 32 | throw new InstantiationError( "Must not instantiate this class" ); 33 | } 34 | 35 | static string getPassword(HttpURI uri) { 36 | string userInfo = uri.getUserInfo(); 37 | if (userInfo !is null) { 38 | return userInfo.split(":")[1]; 39 | } 40 | return null; 41 | } 42 | 43 | static int getDBIndex(HttpURI uri) { 44 | string[] pathSplit = uri.getPath().split("/"); 45 | if (pathSplit.length > 1) { 46 | string dbIndexStr = pathSplit[1]; 47 | if (dbIndexStr.empty()) { 48 | return DEFAULT_DB; 49 | } 50 | return to!int(dbIndexStr); 51 | } else { 52 | return DEFAULT_DB; 53 | } 54 | } 55 | 56 | static bool isValid(HttpURI uri) { 57 | if (isEmpty(uri.getScheme()) || isEmpty(uri.getHost()) || uri.getPort() == -1) { 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | 64 | private static bool isEmpty(string value) { 65 | return value.empty(); 66 | } 67 | 68 | static bool isRedisScheme(HttpURI uri) { 69 | return REDIS == uri.getScheme(); 70 | } 71 | 72 | static bool isRedisSSLScheme(HttpURI uri) { 73 | return REDISS == uri.getScheme(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /source/hunt/redis/util/SafeEncoder.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.SafeEncoder; 13 | 14 | import hunt.redis.Exceptions; 15 | import hunt.redis.Protocol; 16 | 17 | import hunt.Exceptions; 18 | import hunt.text.StringUtils; 19 | 20 | import std.array; 21 | import std.conv; 22 | 23 | /** 24 | * The only reason to have this is to be able to compatible with java 1.5 :( 25 | */ 26 | class SafeEncoder { 27 | 28 | private this(){ 29 | throw new InstantiationError( "Must not instantiate this class" ); 30 | } 31 | 32 | static const(ubyte)[][] encodeMany(string[] strs...) { 33 | const(ubyte)[][] many = new const(ubyte)[][strs.length]; 34 | for (size_t i = 0; i < strs.length; i++) { 35 | many[i] = encode(strs[i]); 36 | } 37 | return many; 38 | } 39 | // static string[] encodeMany(string[] strs...) { 40 | // return strs; 41 | // // byte[][] many = new byte[][strs.length]; 42 | // // for (size_t i = 0; i < strs.length; i++) { 43 | // // many[i] = encode(strs[i]); 44 | // // } 45 | // // return many; 46 | // } 47 | 48 | // static const(ubyte)[] encode(string str) { 49 | // return cast(const(ubyte)[])str; 50 | // } 51 | 52 | static const(ubyte)[] encode(string str) { 53 | try { 54 | if (str.empty) { 55 | throw new RedisDataException("The value sent to redis cannot be null"); 56 | } 57 | return cast(const(ubyte)[])(StringUtils.getBytes(str, Protocol.CHARSET)); 58 | } catch (UnsupportedEncodingException e) { 59 | throw new RedisException(e); 60 | } 61 | } 62 | 63 | static string encode(const(ubyte)[] data) { 64 | try { 65 | return cast(string)data.idup; 66 | } catch (UnsupportedEncodingException e) { 67 | throw new RedisException(e); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /source/hunt/redis/util/ShardInfo.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.ShardInfo; 13 | 14 | abstract class ShardInfo(T) { 15 | private int weight; 16 | 17 | this() { 18 | } 19 | 20 | this(int weight) { 21 | this.weight = weight; 22 | } 23 | 24 | int getWeight() { 25 | return this.weight; 26 | } 27 | 28 | protected abstract T createResource(); 29 | 30 | abstract string getName(); 31 | } 32 | -------------------------------------------------------------------------------- /source/hunt/redis/util/Sharded.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.Sharded; 13 | 14 | import hunt.redis.util.Hashing; 15 | import hunt.redis.util.SafeEncoder; 16 | import hunt.redis.util.ShardInfo; 17 | 18 | import hunt.collection; 19 | import hunt.Long; 20 | 21 | import std.conv; 22 | import std.regex; 23 | 24 | alias Pattern = Regex!char; 25 | 26 | enum int DEFAULT_WEIGHT = 1; 27 | // the tag is anything between {} 28 | enum DEFAULT_KEY_TAG_PATTERN = ctRegex!("\\{(.+?)\\}"); 29 | 30 | class Sharded(R, S) if(is(S : ShardInfo!(R))) { 31 | 32 | private TreeMap!(long, S) nodes; 33 | private Hashing algo; 34 | private Map!(ShardInfo!(R), R) resources; 35 | 36 | /** 37 | * The default pattern used for extracting a key tag. The pattern must have a group (between 38 | * parenthesis), which delimits the tag to be hashed. A null pattern avoids applying the regular 39 | * expression for each lookup, improving performance a little bit is key tags aren't being used. 40 | */ 41 | private Regex!char tagPattern; 42 | 43 | this(List!(S) shards) { 44 | this(shards, Hashing.MURMUR_HASH); // MD5 is really not good as we works 45 | // with 64-bits not 128 46 | } 47 | 48 | this(List!(S) shards, Hashing algo) { 49 | this.algo = algo; 50 | initialize(shards); 51 | } 52 | 53 | this(List!(S) shards, Pattern tagPattern) { 54 | this(shards, Hashing.MURMUR_HASH, tagPattern); // MD5 is really not good 55 | // as we works with 56 | // 64-bits not 128 57 | } 58 | 59 | this(List!(S) shards, Hashing algo, Pattern tagPattern) { 60 | this.algo = algo; 61 | this.tagPattern = tagPattern; 62 | initialize(shards); 63 | } 64 | 65 | private void initialize(List!(S) shards) { 66 | resources = new LinkedHashMap!(ShardInfo!(R), R)(); 67 | nodes = new TreeMap!(long, S)(); 68 | 69 | for (int i = 0; i != shards.size(); ++i) { 70 | S shardInfo = shards.get(i); 71 | if (shardInfo.getName() is null) for (int n = 0; n < 160 * shardInfo.getWeight(); n++) { 72 | long v = this.algo.hash("SHARD-" ~ i.to!string() ~ "-NODE-" ~ n.to!string()); 73 | nodes.put(v, shardInfo); 74 | } else { 75 | for (int n = 0; n < 160 * shardInfo.getWeight(); n++) { 76 | long v = this.algo.hash(shardInfo.getName() ~ "*" ~ n.to!string()); 77 | nodes.put(v, shardInfo); 78 | } 79 | } 80 | resources.put(shardInfo, shardInfo.createResource()); 81 | } 82 | } 83 | 84 | R getShard(const(ubyte)[] key) { 85 | return resources.get(getShardInfo(key)); 86 | } 87 | 88 | R getShard(string key) { 89 | return resources.get(getShardInfo(key)); 90 | } 91 | 92 | S getShardInfo(const(ubyte)[] key) { 93 | SortedMap!(long, S) tail = nodes.tailMap(algo.hash(key)); 94 | if (tail.isEmpty()) { 95 | return nodes.get(nodes.firstKey()); 96 | } 97 | return tail.get(tail.firstKey()); 98 | } 99 | 100 | S getShardInfo(string key) { 101 | return getShardInfo(SafeEncoder.encode(getKeyTag(key))); 102 | } 103 | 104 | /** 105 | * A key tag is a special pattern inside a key that, if preset, is the only part of the key hashed 106 | * in order to select the server for this key. 107 | * @see partitioning 108 | * @param key 109 | * @return The tag if it exists, or the original key 110 | */ 111 | string getKeyTag(string key) { 112 | if (!tagPattern.empty()) { 113 | auto m = matchFirst(key, tagPattern); 114 | if (!m.empty()) return m[1]; 115 | } 116 | return key; 117 | } 118 | 119 | S[] getAllShardInfo() { 120 | return nodes.values(); 121 | } 122 | 123 | R[] getAllShards() { 124 | return resources.values(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /source/hunt/redis/util/Slowlog.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util.Slowlog; 13 | 14 | import hunt.redis.util.SafeEncoder; 15 | 16 | import hunt.collection.ArrayList; 17 | import hunt.collection.List; 18 | import hunt.Long; 19 | import hunt.util.StringBuilder; 20 | 21 | /** 22 | * 23 | */ 24 | class Slowlog { 25 | private long id; 26 | private long timeStamp; 27 | private long executionTime; 28 | private List!(string) args; 29 | private enum string COMMA = ","; 30 | 31 | private this(List!(Object) properties) { 32 | this.id = (cast(Long) properties.get(0)).value(); 33 | this.timeStamp = (cast(Long) properties.get(1)).value(); 34 | this.executionTime = (cast(Long) properties.get(2)).value(); 35 | 36 | List!(string) bargs = cast(List!(string)) properties.get(3); 37 | this.args = new ArrayList!(string)(bargs.size()); 38 | 39 | foreach (string barg; bargs) { 40 | this.args.add(barg); 41 | } 42 | } 43 | 44 | static List!(Slowlog) from(List!(Object) nestedMultiBulkReply) { 45 | List!(Slowlog) logs = new ArrayList!(Slowlog)(nestedMultiBulkReply.size()); 46 | foreach (Object obj; nestedMultiBulkReply) { 47 | List!(Object) properties = cast(List!(Object)) obj; 48 | logs.add(new Slowlog(properties)); 49 | } 50 | 51 | return logs; 52 | } 53 | 54 | long getId() { 55 | return id; 56 | } 57 | 58 | long getTimeStamp() { 59 | return timeStamp; 60 | } 61 | 62 | long getExecutionTime() { 63 | return executionTime; 64 | } 65 | 66 | List!(string) getArgs() { 67 | return args; 68 | } 69 | 70 | override string toString() { 71 | return new StringBuilder().append(id).append(COMMA).append(timeStamp) 72 | .append(COMMA).append(executionTime).append(COMMA) 73 | .append((cast(Object) args).toString()).toString(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /source/hunt/redis/util/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A redis client library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.redis.util; 13 | 14 | public import hunt.redis.util.ByteArrayComparator; 15 | public import hunt.redis.util.Hashing; 16 | public import hunt.redis.util.IOUtils; 17 | public import hunt.redis.util.KeyMergeUtil; 18 | public import hunt.redis.util.MurmurHash; 19 | public import hunt.redis.util.Pool; 20 | public import hunt.redis.util.RedisClusterCRC16; 21 | public import hunt.redis.util.RedisClusterHashTagUtil; 22 | public import hunt.redis.util.RedisInputStream; 23 | public import hunt.redis.util.RedisOutputStream; 24 | public import hunt.redis.util.RedisURIHelper; 25 | public import hunt.redis.util.SafeEncoder; 26 | public import hunt.redis.util.Sharded; 27 | public import hunt.redis.util.ShardInfo; 28 | public import hunt.redis.util.Slowlog; 29 | --------------------------------------------------------------------------------