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 }