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.Pool; 13 14 import hunt.redis.Exceptions; 15 16 import hunt.Exceptions; 17 import hunt.logging.ConsoleLogger; 18 import hunt.util.Common; 19 20 import hunt.pool.PooledObjectFactory; 21 import hunt.pool.impl.GenericObjectPool; 22 import hunt.pool.impl.GenericObjectPoolConfig; 23 24 25 26 abstract class Pool(T) : Closeable { 27 protected GenericObjectPool!(T) internalPool; 28 29 /** 30 * Using this constructor means you have to set and initialize the internalPool yourself. 31 */ 32 this() { 33 } 34 35 this(GenericObjectPoolConfig poolConfig, PooledObjectFactory!(T) factory) { 36 initPool(poolConfig, factory); 37 } 38 39 override 40 void close() { 41 destroy(); 42 } 43 44 bool isClosed() { 45 return this.internalPool.isClosed(); 46 } 47 48 void initPool(GenericObjectPoolConfig poolConfig, PooledObjectFactory!(T) factory) { 49 50 if (this.internalPool !is null) { 51 try { 52 closeInternalPool(); 53 } catch (Exception e) { 54 debug warning(e.msg); 55 version(HUNT_REDIS_DEBUG) warning(e); 56 } 57 } 58 59 this.internalPool = new GenericObjectPool!(T)(factory, poolConfig); 60 } 61 62 T getResource() { 63 try { 64 return internalPool.borrowObject(); 65 } catch (NoSuchElementException nse) { 66 debug warning(nse.msg); 67 version(HUNT_REDIS_DEBUG) warning(nse); 68 if (nse.next is null) { // The exception was caused by an exhausted pool 69 throw new RedisExhaustedPoolException( 70 "Could not get a resource since the pool is exhausted", nse); 71 } 72 // Otherwise, the exception was caused by the implemented activateObject() or ValidateObject() 73 throw new RedisException("Could not get a resource from the pool", nse); 74 } catch (Exception e) { 75 debug warning(e.msg); 76 version(HUNT_REDIS_DEBUG) warning(e); 77 throw new RedisConnectionException("Could not get a resource from the pool", e); 78 } 79 } 80 81 protected void returnResourceObject(T resource) { 82 if (resource is null) { 83 return; 84 } 85 86 try { 87 internalPool.returnObject(resource); 88 } catch (Exception e) { 89 debug warning(e.msg); 90 version(HUNT_REDIS_DEBUG) warning(e); 91 throw new RedisException("Could not return the resource to the pool", e); 92 } 93 } 94 95 protected void returnBrokenResource(T resource) { 96 if (resource !is null) { 97 returnBrokenResourceObject(resource); 98 } 99 } 100 101 protected void returnResource(T resource) { 102 if (resource !is null) { 103 returnResourceObject(resource); 104 } 105 } 106 107 void destroy() { 108 closeInternalPool(); 109 } 110 111 protected void returnBrokenResourceObject(T resource) { 112 try { 113 internalPool.invalidateObject(resource); 114 } catch (Exception e) { 115 debug warning(e.msg); 116 version(HUNT_REDIS_DEBUG) warning(e); 117 throw new RedisException("Could not return the broken resource to the pool", e); 118 } 119 } 120 121 protected void closeInternalPool() { 122 try { 123 internalPool.close(); 124 } catch (Exception e) { 125 debug warning(e.msg); 126 version(HUNT_REDIS_DEBUG) warning(e); 127 throw new RedisException("Could not destroy the pool", e); 128 } 129 } 130 131 /** 132 * Returns the number of instances currently borrowed from this pool. 133 * 134 * @return The number of instances currently borrowed from this pool, -1 if 135 * the pool is inactive. 136 */ 137 int getNumActive() { 138 if (poolInactive()) { 139 return -1; 140 } 141 142 return this.internalPool.getNumActive(); 143 } 144 145 /** 146 * Returns the number of instances currently idle in this pool. 147 * 148 * @return The number of instances currently idle in this pool, -1 if the 149 * pool is inactive. 150 */ 151 int getNumIdle() { 152 if (poolInactive()) { 153 return -1; 154 } 155 156 return this.internalPool.getNumIdle(); 157 } 158 159 /** 160 * Returns an estimate of the number of threads currently blocked waiting for 161 * a resource from this pool. 162 * 163 * @return The number of threads waiting, -1 if the pool is inactive. 164 */ 165 int getNumWaiters() { 166 if (poolInactive()) { 167 return -1; 168 } 169 170 return this.internalPool.getNumWaiters(); 171 } 172 173 /** 174 * Returns the mean waiting time spent by threads to obtain a resource from 175 * this pool. 176 * 177 * @return The mean waiting time, in milliseconds, -1 if the pool is 178 * inactive. 179 */ 180 long getMeanBorrowWaitTimeMillis() { 181 if (poolInactive()) { 182 return -1; 183 } 184 185 return this.internalPool.getMeanBorrowWaitTimeMillis(); 186 } 187 188 /** 189 * Returns the maximum waiting time spent by threads to obtain a resource 190 * from this pool. 191 * 192 * @return The maximum waiting time, in milliseconds, -1 if the pool is 193 * inactive. 194 */ 195 long getMaxBorrowWaitTimeMillis() { 196 if (poolInactive()) { 197 return -1; 198 } 199 200 return this.internalPool.getMaxBorrowWaitTimeMillis(); 201 } 202 203 private bool poolInactive() { 204 return this.internalPool is null || this.internalPool.isClosed(); 205 } 206 207 void addObjects(int count) { 208 try { 209 for (int i = 0; i < count; i++) { 210 this.internalPool.addObject(); 211 } 212 } catch (Exception e) { 213 debug warning(e.msg); 214 version(HUNT_REDIS_DEBUG) warning(e); 215 throw new RedisException("Error trying to add idle objects", e); 216 } 217 } 218 }