25
25
import io .netty .channel .pool .ChannelPoolHandler ;
26
26
import io .netty .util .concurrent .EventExecutor ;
27
27
28
+ import java .util .HashMap ;
28
29
import java .util .Map ;
29
- import java .util .concurrent .ConcurrentHashMap ;
30
- import java .util .concurrent .atomic .AtomicInteger ;
30
+ import java .util .concurrent .locks .Lock ;
31
+ import java .util .concurrent .locks .ReentrantReadWriteLock ;
32
+ import java .util .function .Supplier ;
31
33
34
+ import org .neo4j .driver .Logger ;
35
+ import org .neo4j .driver .Logging ;
32
36
import org .neo4j .driver .internal .BoltServerAddress ;
33
37
import org .neo4j .driver .internal .messaging .BoltProtocol ;
34
38
import org .neo4j .driver .internal .metrics .ListenerEvent ;
35
39
import org .neo4j .driver .internal .metrics .MetricsListener ;
36
- import org .neo4j .driver .Logger ;
37
- import org .neo4j .driver .Logging ;
38
40
39
41
import static org .neo4j .driver .internal .async .connection .ChannelAttributes .poolId ;
40
42
import static org .neo4j .driver .internal .async .connection .ChannelAttributes .serverAddress ;
41
43
42
44
public class NettyChannelTracker implements ChannelPoolHandler
43
45
{
44
- private final Map <BoltServerAddress ,AtomicInteger > addressToInUseChannelCount = new ConcurrentHashMap <>();
45
- private final Map <BoltServerAddress ,AtomicInteger > addressToIdleChannelCount = new ConcurrentHashMap <>();
46
+ private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock ();
47
+ private final Lock read = lock .readLock ();
48
+ private final Lock write = lock .writeLock ();
49
+ private final Map <BoltServerAddress ,Integer > addressToInUseChannelCount = new HashMap <>();
50
+ private final Map <BoltServerAddress ,Integer > addressToIdleChannelCount = new HashMap <>();
46
51
private final Logger log ;
47
52
private final MetricsListener metricsListener ;
48
53
private final ChannelFutureListener closeListener = future -> channelClosed ( future .channel () );
@@ -60,24 +65,57 @@ public NettyChannelTracker( MetricsListener metricsListener, ChannelGroup channe
60
65
this .allChannels = channels ;
61
66
}
62
67
68
+ private void doInWriteLock ( Runnable work )
69
+ {
70
+ try
71
+ {
72
+ write .lock ();
73
+ work .run ();
74
+ }
75
+ finally
76
+ {
77
+ write .unlock ();
78
+ }
79
+ }
80
+
81
+ private <T > T retrieveInReadLock ( Supplier <T > work )
82
+ {
83
+ try
84
+ {
85
+ read .lock ();
86
+ return work .get ();
87
+ }
88
+ finally
89
+ {
90
+ read .unlock ();
91
+ }
92
+ }
93
+
63
94
@ Override
64
95
public void channelReleased ( Channel channel )
65
96
{
66
- log .debug ( "Channel [0x%s] released back to the pool" , channel .id () );
67
- decrementInUse ( channel );
68
- incrementIdle ( channel );
97
+ doInWriteLock ( () ->
98
+ {
99
+ decrementInUse ( channel );
100
+ incrementIdle ( channel );
101
+ } );
102
+
69
103
channel .closeFuture ().addListener ( closeListener );
104
+ log .debug ( "Channel [0x%s] released back to the pool" , channel .id () );
70
105
}
71
106
72
107
@ Override
73
108
public void channelAcquired ( Channel channel )
74
109
{
75
- log .debug ( "Channel [0x%s] acquired from the pool. Local address: %s, remote address: %s" ,
76
- channel .id (), channel .localAddress (), channel .remoteAddress () );
110
+ doInWriteLock ( () ->
111
+ {
112
+ incrementInUse ( channel );
113
+ decrementIdle ( channel );
114
+ } );
77
115
78
- incrementInUse ( channel );
79
- decrementIdle ( channel );
80
116
channel .closeFuture ().removeListener ( closeListener );
117
+ log .debug ( "Channel [0x%s] acquired from the pool. Local address: %s, remote address: %s" , channel .id (), channel .localAddress (),
118
+ channel .remoteAddress () );
81
119
}
82
120
83
121
@ Override
@@ -86,14 +124,14 @@ public void channelCreated( Channel channel )
86
124
throw new IllegalStateException ( "Untraceable channel created." );
87
125
}
88
126
89
- public synchronized void channelCreated ( Channel channel , ListenerEvent creatingEvent )
127
+ public void channelCreated ( Channel channel , ListenerEvent creatingEvent )
90
128
{
91
- log . debug ( "Channel [0x%s] created. Local address: %s, remote address: %s" ,
92
- channel . id (), channel . localAddress (), channel . remoteAddress ( ) );
129
+ // when it is created, we count it as idle as it has not been acquired out of the pool
130
+ doInWriteLock ( () -> incrementIdle ( channel ) );
93
131
94
- incrementIdle ( channel ); // when it is created, we count it as idle as it has not been acquired out of the pool
95
132
metricsListener .afterCreated ( poolId ( channel ), creatingEvent );
96
133
allChannels .add ( channel );
134
+ log .debug ( "Channel [0x%s] created. Local address: %s, remote address: %s" , channel .id (), channel .localAddress (), channel .remoteAddress () );
97
135
}
98
136
99
137
public ListenerEvent channelCreating ( String poolId )
@@ -110,20 +148,18 @@ public void channelFailedToCreate( String poolId )
110
148
111
149
public void channelClosed ( Channel channel )
112
150
{
113
- decrementIdle ( channel );
151
+ doInWriteLock ( () -> decrementIdle ( channel ) );
114
152
metricsListener .afterClosed ( poolId ( channel ) );
115
153
}
116
154
117
155
public int inUseChannelCount ( BoltServerAddress address )
118
156
{
119
- AtomicInteger count = addressToInUseChannelCount .get ( address );
120
- return count == null ? 0 : count .get ();
157
+ return retrieveInReadLock ( () -> addressToInUseChannelCount .getOrDefault ( address , 0 ) );
121
158
}
122
159
123
160
public int idleChannelCount ( BoltServerAddress address )
124
161
{
125
- AtomicInteger count = addressToIdleChannelCount .get ( address );
126
- return count == null ? 0 : count .get ();
162
+ return retrieveInReadLock ( () -> addressToIdleChannelCount .getOrDefault ( address , 0 ) );
127
163
}
128
164
129
165
public void prepareToCloseChannels ()
@@ -139,7 +175,8 @@ public void prepareToCloseChannels()
139
175
{
140
176
// only logging it
141
177
log .debug ( "Failed to prepare to close Channel %s due to error %s. " +
142
- "It is safe to ignore this error as the channel will be closed despite if it is successfully prepared to close or not." , channel , e .getMessage () );
178
+ "It is safe to ignore this error as the channel will be closed despite if it is successfully prepared to close or not." , channel ,
179
+ e .getMessage () );
143
180
}
144
181
}
145
182
}
@@ -164,21 +201,21 @@ private void decrementIdle( Channel channel )
164
201
decrement ( channel , addressToIdleChannelCount );
165
202
}
166
203
167
- private void increment ( Channel channel , Map <BoltServerAddress ,AtomicInteger > countMap )
204
+ private void increment ( Channel channel , Map <BoltServerAddress ,Integer > countMap )
168
205
{
169
206
BoltServerAddress address = serverAddress ( channel );
170
- AtomicInteger count = countMap .computeIfAbsent ( address , k -> new AtomicInteger () );
171
- count . incrementAndGet ( );
207
+ Integer count = countMap .computeIfAbsent ( address , k -> 0 );
208
+ countMap . put ( address , count + 1 );
172
209
}
173
210
174
- private void decrement ( Channel channel , Map <BoltServerAddress ,AtomicInteger > countMap )
211
+ private void decrement ( Channel channel , Map <BoltServerAddress ,Integer > countMap )
175
212
{
176
213
BoltServerAddress address = serverAddress ( channel );
177
- AtomicInteger count = countMap .get ( address );
178
- if ( count == null )
214
+ if ( !countMap .containsKey ( address ) )
179
215
{
180
216
throw new IllegalStateException ( "No count exist for address '" + address + "'" );
181
217
}
182
- count .decrementAndGet ();
218
+ Integer count = countMap .get ( address );
219
+ countMap .put ( address , count - 1 );
183
220
}
184
221
}
0 commit comments