20
20
import org .apache .commons .logging .LogFactory ;
21
21
22
22
import java .util .concurrent .BlockingQueue ;
23
+ import java .util .concurrent .Semaphore ;
23
24
import java .util .concurrent .ThreadLocalRandom ;
24
25
import java .util .concurrent .TimeUnit ;
26
+ import java .util .concurrent .atomic .AtomicInteger ;
25
27
26
28
import static com .google .common .base .Preconditions .checkState ;
27
29
@@ -35,10 +37,11 @@ public class ObjectPoolPartition<T>
35
37
private final PoolConfig config ;
36
38
private final BlockingQueue <Poolable <T >> objectQueue ;
37
39
private final ObjectFactory <T > objectFactory ;
38
- private int totalCount ;
39
40
private final String host ;
40
41
private final int socketTimeout ;
41
42
private final int connectTimeout ;
43
+ private final Semaphore takeSemaphore ;
44
+ private final AtomicInteger aliveObjectCount ;
42
45
43
46
public ObjectPoolPartition (ObjectPool <T > pool , PoolConfig config ,
44
47
ObjectFactory <T > objectFactory , BlockingQueue <Poolable <T >> queue , String host , String name )
@@ -50,113 +53,41 @@ public ObjectPoolPartition(ObjectPool<T> pool, PoolConfig config,
50
53
this .host = host ;
51
54
this .socketTimeout = config .getSocketTimeoutMilliseconds ();
52
55
this .connectTimeout = config .getConnectTimeoutMilliseconds ();
53
- this .totalCount = 0 ;
56
+ this .aliveObjectCount = new AtomicInteger () ;
54
57
this .log = new CustomLogger (name , host );
55
- for (int i = 0 ; i < config .getMinSize (); i ++) {
56
- T object = objectFactory .create (host , socketTimeout , connectTimeout );
57
- if (object != null ) {
58
- objectQueue .add (new Poolable <>(object , pool , host ));
59
- totalCount ++;
60
- }
61
- }
62
- }
63
-
64
- public void returnObject (Poolable <T > object )
65
- {
66
- if (!objectFactory .validate (object .getObject ())) {
67
- log .debug (String .format ("Invalid object...removing: %s " , object ));
68
- decreaseObject (object );
69
- return ;
70
- }
71
-
72
- log .debug (String .format ("Returning object: %s to queue. Queue size: %d" , object , objectQueue .size ()));
73
- if (!objectQueue .offer (object )) {
74
- log .warn ("Created more objects than configured. Created=" + totalCount + " QueueSize=" + objectQueue .size ());
75
- decreaseObject (object );
76
- }
77
- }
78
-
79
- public Poolable <T > getObject (boolean blocking )
80
- {
81
- if (objectQueue .size () == 0 ) {
82
- // increase objects and return one, it will return null if pool reaches max size or if object creation fails
83
- Poolable <T > object = increaseObjects (this .config .getDelta (), true );
84
-
85
- if (object != null ) {
86
- return object ;
87
- }
88
-
89
- if (totalCount == 0 ) {
90
- // Could not create objects, this is mostly due to connection timeouts hence no point blocking as there is not other producer of sockets
91
- throw new RuntimeException ("Could not add connections to pool" );
92
- }
93
- // else wait for a connection to get free
94
- }
95
-
96
- Poolable <T > freeObject ;
58
+ this .takeSemaphore = new Semaphore (config .getMaxSize (), true );
97
59
try {
98
- if (blocking ) {
99
- freeObject = objectQueue .take ();
100
- }
101
- else {
102
- freeObject = objectQueue .poll (config .getMaxWaitMilliseconds (), TimeUnit .MILLISECONDS );
103
- if (freeObject == null ) {
104
- throw new RuntimeException ("Cannot get a free object from the pool" );
105
- }
60
+ for (int i = 0 ; i < config .getMinSize (); i ++) {
61
+ T object = objectFactory .create (host , socketTimeout , connectTimeout );
62
+ objectQueue .add (new Poolable <>(object , pool , host ));
63
+ aliveObjectCount .incrementAndGet ();
106
64
}
107
65
}
108
- catch (InterruptedException e ) {
109
- throw new RuntimeException ( e ); // will never happen
66
+ catch (Exception e ) {
67
+ // skipping logging the exception as factories are already logging.
110
68
}
111
-
112
- freeObject .setLastAccessTs (System .currentTimeMillis ());
113
- return freeObject ;
114
69
}
115
70
116
- private Poolable <T > increaseObjects ( int delta , boolean returnObject )
71
+ public void returnObject ( Poolable <T > object )
117
72
{
118
- int oldCount = totalCount ;
119
- if (delta + totalCount > config .getMaxSize ()) {
120
- delta = config .getMaxSize () - totalCount ;
121
- }
122
-
123
- Poolable <T > objectToReturn = null ;
124
73
try {
125
- for (int i = 0 ; i < delta ; i ++) {
126
- T object = objectFactory .create (host , socketTimeout , connectTimeout );
127
- if (object != null ) {
128
- // Do not put the first object on queue
129
- // it will be returned to the caller to ensure it's request is satisfied first if object is requested
130
- Poolable <T > poolable = new Poolable <>(object , pool , host );
131
- if (objectToReturn == null && returnObject ) {
132
- objectToReturn = poolable ;
133
- }
134
- else {
135
- objectQueue .put (poolable );
136
- }
137
- totalCount ++;
138
- }
74
+ if (!objectFactory .validate (object .getObject ())) {
75
+ log .debug (String .format ("Invalid object...removing: %s " , object ));
76
+ decreaseObject (object );
77
+ return ;
139
78
}
140
79
141
- if ( delta > 0 && ( totalCount - oldCount ) == 0 ) {
142
- log . warn ( String . format ( "Could not increase pool size. Pool state: totalCount=%d queueSize=%d delta=%d" , totalCount , objectQueue .size (), delta ));
143
- }
144
- else {
145
- log . debug ( String . format ( "Increased pool size by %d, to new size: %d, current queue size: %d, delta: %d" ,
146
- totalCount - oldCount , totalCount , objectQueue . size (), delta ) );
80
+ log . debug ( String . format ( "Returning object: %s to queue. Queue size: %d" , object , objectQueue . size ()));
81
+ if (! objectQueue .offer ( object )) {
82
+ String errorLog = "Created more objects than configured. Created=" + aliveObjectCount + " QueueSize=" + objectQueue . size ();
83
+ log . warn ( errorLog );
84
+ decreaseObject ( object );
85
+ throw new RuntimeException ( errorLog );
147
86
}
148
87
}
149
- catch (Exception e ) {
150
- log .warn (String .format ("Unable to increase pool size. Pool state: totalCount=%d queueSize=%d delta=%d" , totalCount , objectQueue .size (), delta ), e );
151
- // objectToReturn is not on the queue hence untracked, clean it up before forwarding exception
152
- if (objectToReturn != null ) {
153
- objectFactory .destroy (objectToReturn .getObject ());
154
- objectToReturn .destroy ();
155
- }
156
- throw new RuntimeException (e );
88
+ finally {
89
+ takeSemaphore .release ();
157
90
}
158
-
159
- return objectToReturn ;
160
91
}
161
92
162
93
public boolean decreaseObject (Poolable <T > obj )
@@ -165,27 +96,69 @@ public boolean decreaseObject(Poolable<T> obj)
165
96
checkState (obj .getHost ().equals (this .host ),
166
97
"Call to free object of wrong partition, current partition=%s requested partition = %s" ,
167
98
this .host , obj .getHost ());
168
- objectRemoved ();
169
99
log .debug ("Decreasing pool size object: " + obj );
170
100
objectFactory .destroy (obj .getObject ());
101
+ aliveObjectCount .decrementAndGet ();
171
102
obj .destroy ();
172
103
return true ;
173
104
}
174
105
175
- private synchronized void objectRemoved ()
106
+ public Poolable <T > getObject ()
107
+ {
108
+ Poolable <T > object ;
109
+ try {
110
+ takeSemaphore .tryAcquire (config .getMaxWaitMilliseconds (), TimeUnit .MILLISECONDS );
111
+ }
112
+ catch (InterruptedException e ) {
113
+ Thread .currentThread ().interrupt ();
114
+ return null ;
115
+ }
116
+
117
+ try {
118
+ object = tryGetObject ();
119
+ object .setLastAccessTs (System .currentTimeMillis ());
120
+ }
121
+ catch (Exception e ) {
122
+ takeSemaphore .release ();
123
+ throw new RuntimeException ("Cannot get a free object from the pool" , e );
124
+ }
125
+ return object ;
126
+ }
127
+
128
+ private Poolable <T > tryGetObject () throws Exception
176
129
{
177
- totalCount --;
130
+ Poolable <T > poolable = objectQueue .poll ();
131
+ if (poolable == null )
132
+ {
133
+ try {
134
+ T object = objectFactory .create (host , socketTimeout , connectTimeout );
135
+ poolable = new Poolable <>(object , pool , host );
136
+ aliveObjectCount .incrementAndGet ();
137
+ log .debug (String .format ("Added a connection, Pool state: totalCount: %s, queueSize: %d" , aliveObjectCount ,
138
+ objectQueue .size ()));
139
+ }
140
+ catch (Exception e ) {
141
+ log .warn (String .format ("Unable create a connection. Pool state: totalCount=%s queueSize=%d" , aliveObjectCount ,
142
+ objectQueue .size ()), e );
143
+ if (poolable != null ) {
144
+ objectFactory .destroy (poolable .getObject ());
145
+ poolable .destroy ();
146
+ }
147
+ throw e ;
148
+ }
149
+ }
150
+ return poolable ;
178
151
}
179
152
180
- public synchronized int getTotalCount ()
153
+ public int getAliveObjectCount ()
181
154
{
182
- return totalCount ;
155
+ return aliveObjectCount . get () ;
183
156
}
184
157
185
158
// set the scavenge interval carefully
186
159
public void scavenge () throws InterruptedException
187
160
{
188
- int delta = this .totalCount - config .getMinSize ();
161
+ int delta = this .aliveObjectCount . get () - config .getMinSize ();
189
162
if (delta <= 0 ) {
190
163
log .debug ("Scavenge for delta <= 0, Skipping !!!" );
191
164
return ;
@@ -223,7 +196,7 @@ public void scavenge() throws InterruptedException
223
196
public synchronized int shutdown ()
224
197
{
225
198
int removed = 0 ;
226
- while (this .totalCount > 0 ) {
199
+ while (this .aliveObjectCount . get () > 0 ) {
227
200
Poolable <T > obj = objectQueue .poll ();
228
201
if (obj != null ) {
229
202
decreaseObject (obj );
0 commit comments