16
16
17
17
package com .mongodb ;
18
18
19
+ import com .mongodb .connection .Cluster ;
20
+ import com .mongodb .connection .Connection ;
19
21
import com .mongodb .internal .connection .ConcurrentPool ;
20
22
import com .mongodb .internal .connection .ConcurrentPool .Prune ;
23
+ import com .mongodb .internal .connection .NoOpSessionContext ;
24
+ import com .mongodb .internal .validator .NoOpFieldNameValidator ;
25
+ import com .mongodb .selector .ReadPreferenceServerSelector ;
26
+ import org .bson .BsonArray ;
21
27
import org .bson .BsonBinary ;
22
28
import org .bson .BsonDocument ;
23
29
import org .bson .BsonDocumentWriter ;
24
30
import org .bson .UuidRepresentation ;
31
+ import org .bson .codecs .BsonDocumentCodec ;
25
32
import org .bson .codecs .EncoderContext ;
26
33
import org .bson .codecs .UuidCodec ;
27
34
35
+ import java .util .ArrayList ;
36
+ import java .util .List ;
28
37
import java .util .UUID ;
29
38
39
+ import static com .mongodb .assertions .Assertions .isTrue ;
40
+ import static java .util .concurrent .TimeUnit .MINUTES ;
41
+
30
42
class ServerSessionPool {
31
- private final ConcurrentPool <ServerSession > serverSessionPool =
32
- new ConcurrentPool <ServerSession >(Integer .MAX_VALUE , new ServerSessionItemFactory ());
43
+
44
+ private static final int END_SESSIONS_BATCH_SIZE = 10000 ;
45
+
46
+ private final ConcurrentPool <ServerSessionImpl > serverSessionPool =
47
+ new ConcurrentPool <ServerSessionImpl >(Integer .MAX_VALUE , new ServerSessionItemFactory ());
48
+ private final Cluster cluster ;
49
+ private final ServerSessionPool .Clock clock ;
50
+ private volatile boolean closing ;
51
+ private volatile boolean closed ;
52
+ private final List <BsonDocument > closedSessionIdentifiers = new ArrayList <BsonDocument >();
53
+
54
+ interface Clock {
55
+ long millis ();
56
+ }
57
+
58
+ ServerSessionPool (final Cluster cluster ) {
59
+ this (cluster , new Clock () {
60
+ @ Override
61
+ public long millis () {
62
+ return System .currentTimeMillis ();
63
+ }
64
+ });
65
+ }
66
+
67
+ ServerSessionPool (final Cluster cluster , final Clock clock ) {
68
+ this .cluster = cluster ;
69
+ this .clock = clock ;
70
+ }
33
71
34
72
ServerSession get () {
35
- return serverSessionPool .get ();
73
+ isTrue ("server session pool is open" , !closed );
74
+ ServerSessionImpl serverSession = serverSessionPool .get ();
75
+ while (shouldPrune (serverSession )) {
76
+ serverSessionPool .release (serverSession , true );
77
+ serverSession = serverSessionPool .get ();
78
+ }
79
+ return serverSession ;
36
80
}
37
81
38
82
void release (final ServerSession serverSession ) {
39
- serverSessionPool .release (serverSession );
83
+ serverSessionPool .release (( ServerSessionImpl ) serverSession );
40
84
}
41
85
42
86
void close () {
43
- serverSessionPool .close ();
87
+ try {
88
+ closing = true ;
89
+ serverSessionPool .close ();
90
+ endClosedSessions ();
91
+ } finally {
92
+ closed = true ;
93
+ }
94
+ }
95
+
96
+ private void closeSession (final ServerSessionImpl serverSession ) {
97
+ // only track closed sessions when pool is in the process of closing
98
+ if (!closing ) {
99
+ return ;
100
+ }
101
+
102
+ closedSessionIdentifiers .add (serverSession .getIdentifier ());
103
+ if (closedSessionIdentifiers .size () == END_SESSIONS_BATCH_SIZE ) {
104
+ endClosedSessions ();
105
+ }
106
+ }
107
+
108
+ private void endClosedSessions () {
109
+ if (closedSessionIdentifiers .isEmpty ()) {
110
+ return ;
111
+ }
112
+
113
+ Connection connection = cluster .selectServer (new ReadPreferenceServerSelector (ReadPreference .primaryPreferred ())).getConnection ();
114
+ try {
115
+ connection .command ("admin" ,
116
+ new BsonDocument ("endSessions" , new BsonArray (closedSessionIdentifiers )),
117
+ ReadPreference .primaryPreferred (), new NoOpFieldNameValidator (),
118
+ new BsonDocumentCodec (), NoOpSessionContext .INSTANCE );
119
+ } catch (MongoException e ) {
120
+ // ignore exceptions
121
+ } finally {
122
+ closedSessionIdentifiers .clear ();
123
+ connection .release ();
124
+ }
44
125
}
45
126
46
- private static class ServerSessionImpl implements ServerSession {
127
+ private boolean shouldPrune (final ServerSessionImpl serverSession ) {
128
+ Integer logicalSessionTimeoutMinutes = cluster .getDescription ().getLogicalSessionTimeoutMinutes ();
129
+ if (logicalSessionTimeoutMinutes == null ) {
130
+ return false ;
131
+ }
132
+ long currentTimeMillis = clock .millis ();
133
+ final long timeSinceLastUse = currentTimeMillis - serverSession .lastUsedAtMillis ;
134
+ final long oneMinuteFromTimeout = MINUTES .toMillis (logicalSessionTimeoutMinutes - 1 );
135
+ return timeSinceLastUse > oneMinuteFromTimeout ;
136
+ }
137
+
138
+ final class ServerSessionImpl implements ServerSession {
47
139
private final BsonDocument identifier ;
48
140
private int transactionNumber ;
141
+ private volatile long lastUsedAtMillis = clock .millis ();
49
142
50
143
ServerSessionImpl (final BsonBinary identifier ) {
51
144
this .identifier = new BsonDocument ("id" , identifier );
52
145
}
53
146
147
+ long getLastUsedAtMillis () {
148
+ return lastUsedAtMillis ;
149
+ }
150
+
151
+ int getTransactionNumber () {
152
+ return transactionNumber ;
153
+ }
154
+
54
155
@ Override
55
156
public BsonDocument getIdentifier () {
157
+ lastUsedAtMillis = clock .millis ();
56
158
return identifier ;
57
159
}
58
160
@@ -62,19 +164,19 @@ public long advanceTransactionNumber() {
62
164
}
63
165
}
64
166
65
- private static final class ServerSessionItemFactory implements ConcurrentPool .ItemFactory <ServerSession > {
167
+ private final class ServerSessionItemFactory implements ConcurrentPool .ItemFactory <ServerSessionImpl > {
66
168
@ Override
67
- public ServerSession create (final boolean initialize ) {
169
+ public ServerSessionImpl create (final boolean initialize ) {
68
170
return new ServerSessionImpl (createNewServerSessionIdentifier ());
69
171
}
70
172
71
173
@ Override
72
- public void close (final ServerSession serverSession ) {
73
- // TODO: pruning
174
+ public void close (final ServerSessionImpl serverSession ) {
175
+ closeSession ( serverSession );
74
176
}
75
177
76
178
@ Override
77
- public Prune shouldPrune (final ServerSession serverSession ) {
179
+ public Prune shouldPrune (final ServerSessionImpl serverSession ) {
78
180
return Prune .STOP ;
79
181
}
80
182
0 commit comments