2222import java .sql .Statement ;
2323import java .util .*;
2424import java .util .concurrent .*;
25+ import java .util .concurrent .atomic .AtomicBoolean ;
2526import java .util .concurrent .locks .Lock ;
2627import java .util .concurrent .locks .ReentrantLock ;
2728
2829import com .alipay .oceanbase .rpc .ObTableClient ;
2930import com .alipay .oceanbase .rpc .exception .ObTableEntryRefreshException ;
3031import com .alipay .oceanbase .rpc .exception .ObTableTryLockTimeoutException ;
31- import com .alipay .oceanbase .rpc .exception .ObTableUnexpectedException ;
3232import com .alipay .oceanbase .rpc .location .LocationUtil ;
3333import com .alipay .oceanbase .rpc .table .ObTable ;
3434import org .slf4j .Logger ;
@@ -49,11 +49,15 @@ public class RouteTableRefresher {
4949
5050 private final ScheduledExecutorService scheduler = Executors .newScheduledThreadPool (2 );
5151
52- private final static ConcurrentHashMap <ObServerAddr , Lock > suspectLocks = new ConcurrentHashMap <>(); // ObServer -> access lock
52+ private final ConcurrentHashMap <ObServerAddr , Lock > suspectLocks = new ConcurrentHashMap <>(); // ObServer -> access lock
5353
54- private final static ConcurrentHashMap <ObServerAddr , SuspectObServer > suspectServers = new ConcurrentHashMap <>(); // ObServer -> information structure
54+ private final ConcurrentHashMap <ObServerAddr , SuspectObServer > suspectServers = new ConcurrentHashMap <>(); // ObServer -> information structure
5555
56- private final static HashMap <ObServerAddr , Long > serverLastAccessTimestamps = new HashMap <>(); // ObServer -> last access timestamp
56+ private final ConcurrentHashMap <ObServerAddr , Long > serverLastAccessTimestamps = new ConcurrentHashMap <>(); // ObServer -> last access timestamp
57+
58+ private final Set <ObServerAddr > activeServers = ConcurrentHashMap .newKeySet ();
59+
60+ private final AtomicBoolean closed = new AtomicBoolean (false );
5761
5862 public RouteTableRefresher (ObTableClient tableClient , ObUserAuth sysUA ) {
5963 this .tableClient = tableClient ;
@@ -70,6 +74,9 @@ public void start() {
7074 }
7175
7276 public void close () {
77+ if (!closed .compareAndSet (false , true )) {
78+ return ;
79+ }
7380 try {
7481 scheduler .shutdown ();
7582 // wait at most 1 seconds to close the scheduler
@@ -79,6 +86,39 @@ public void close() {
7986 } catch (InterruptedException e ) {
8087 logger .warn ("scheduler await for terminate interrupted: {}." , e .getMessage ());
8188 scheduler .shutdownNow ();
89+ Thread .currentThread ().interrupt ();
90+ } finally {
91+ suspectServers .clear ();
92+ suspectLocks .clear ();
93+ serverLastAccessTimestamps .clear ();
94+ activeServers .clear ();
95+ }
96+ }
97+
98+ /**
99+ * Reconcile the keep-alive state with the latest authoritative tenant roster.
100+ * Servers removed from the roster must not remain in, or be re-added to, the suspect set.
101+ */
102+ public void refreshActiveServers (Collection <ObServerAddr > servers ) {
103+ if (closed .get ()) {
104+ return ;
105+ }
106+ Set <ObServerAddr > newServers = new HashSet <>();
107+ if (servers != null ) {
108+ newServers .addAll (servers );
109+ }
110+ activeServers .retainAll (newServers );
111+ activeServers .addAll (newServers );
112+
113+ for (ObServerAddr addr : suspectServers .keySet ()) {
114+ if (!newServers .contains (addr )) {
115+ removeFromSuspectIPs (addr );
116+ }
117+ }
118+ for (ObServerAddr addr : serverLastAccessTimestamps .keySet ()) {
119+ if (!newServers .contains (addr )) {
120+ serverLastAccessTimestamps .remove (addr );
121+ }
82122 }
83123 }
84124
@@ -127,6 +167,10 @@ private void doRsListCheck() {
127167 private void doCheckAliveTask () {
128168 for (Map .Entry <ObServerAddr , SuspectObServer > entry : suspectServers .entrySet ()) {
129169 try {
170+ if (!activeServers .contains (entry .getKey ())) {
171+ removeFromSuspectIPs (entry .getKey ());
172+ continue ;
173+ }
130174 checkAlive (entry .getKey ());
131175 } catch (Exception e ) {
132176 // silence resolving
@@ -161,7 +205,7 @@ private void checkAlive(ObServerAddr addr) {
161205 if (t instanceof SQLException ) {
162206 // occurred during query
163207 calcFailureOrClearCache (addr );
164- } if (t instanceof ObTableEntryRefreshException ) {
208+ } else if (t instanceof ObTableEntryRefreshException ) {
165209 // occurred during connection construction
166210 ObTableEntryRefreshException e = (ObTableEntryRefreshException ) t ;
167211 if (e .isConnectInactive ()) {
@@ -192,11 +236,22 @@ private void checkAlive(ObServerAddr addr) {
192236 }
193237 }
194238
195- public static void addIntoSuspectIPs (SuspectObServer server ) throws InterruptedException {
239+ public void addIntoSuspectIPs (ObServerAddr addr ) {
240+ if (addr == null ) {
241+ return ;
242+ }
243+ addIntoSuspectIPs (new SuspectObServer (addr ));
244+ }
245+
246+ private void addIntoSuspectIPs (SuspectObServer server ) {
196247 if (server == null || server .getAddr () == null ) {
197248 return ;
198249 }
199250 ObServerAddr addr = server .getAddr ();
251+ if (closed .get () || !activeServers .contains (addr )) {
252+ logger .debug ("ignore suspect report for inactive server: {}" , addr );
253+ return ;
254+ }
200255 if (suspectServers .get (addr ) != null ) {
201256 // already in the list, directly return
202257 return ;
@@ -218,6 +273,9 @@ public static void addIntoSuspectIPs(SuspectObServer server) throws InterruptedE
218273 // already in the list, directly break
219274 break ;
220275 }
276+ if (closed .get () || !activeServers .contains (addr )) {
277+ break ;
278+ }
221279 Long lastServerAccessTs = serverLastAccessTimestamps .get (addr );
222280 if (lastServerAccessTs != null ) {
223281 long interval = System .currentTimeMillis () - lastServerAccessTs ;
@@ -235,6 +293,11 @@ public static void addIntoSuspectIPs(SuspectObServer server) throws InterruptedE
235293 ++retryTimes ;
236294 logger .warn ("wait to try lock to timeout 1s when add observer into suspect ips, server: {}, tryTimes: {}" ,
237295 addr .toString (), retryTimes , e );
296+ } catch (InterruptedException e ) {
297+ Thread .currentThread ().interrupt ();
298+ logger .debug ("interrupted while adding observer into suspect ips, server: {}" ,
299+ addr );
300+ break ;
238301 }
239302 } // end while
240303 } finally {
@@ -247,43 +310,33 @@ public static void addIntoSuspectIPs(SuspectObServer server) throws InterruptedE
247310 private void removeFromSuspectIPs (ObServerAddr addr ) {
248311 Lock lock = suspectLocks .get (addr );
249312 if (lock == null ) {
250- // lock must have been added before remove
251- throw new ObTableUnexpectedException ( String . format ( "ObServer [%s:%d] need to be add into suspect ips before remove" ,
252- addr . getIp (), addr . getSvrPort ())) ;
313+ suspectServers . remove ( addr );
314+ logger . debug ( "suspect server has already been removed: {}" , addr );
315+ return ;
253316 }
254317 boolean acquired = false ;
255318 try {
256- int retryTimes = 0 ;
257- while (true ) {
258- try {
259- acquired = lock .tryLock (1 , TimeUnit .SECONDS );
260- if (!acquired ) {
261- throw new ObTableTryLockTimeoutException ("try to get suspect server lock timeout, timeout: 1s" );
262- }
263- // no need to remove lock
264- SuspectObServer server = suspectServers .remove (addr );
265- if (server != null ) {
266- int failure = server .getFailure ();
267- if (failure < failureLimit ) {
268- ObTable obTable = tableClient .getTableRoute ().getTableRoster ().getTable (addr );
269- if (obTable != null && !obTable .isValid ()) {
270- obTable .setValid ();
271- }
272- }
319+ acquired = lock .tryLock (1 , TimeUnit .SECONDS );
320+ if (!acquired ) {
321+ logger .debug ("defer suspect removal because lock is busy, server: {}" , addr );
322+ return ;
323+ }
324+ // Keep the lock and cooldown entry until this refresher closes. This prevents a
325+ // concurrent add from using a different lock and preserves the existing cooldown.
326+ SuspectObServer server = suspectServers .remove (addr );
327+ if (server != null ) {
328+ int failure = server .getFailure ();
329+ if (failure < failureLimit && activeServers .contains (addr )) {
330+ ObTable obTable = tableClient .getTableRoute ().getTableRoster ().getTable (addr );
331+ if (obTable != null && !obTable .isValid ()) {
332+ obTable .setValid ();
273333 }
274- logger .debug ("removed server from suspect list: {}" , addr );
275- break ;
276- } catch (ObTableTryLockTimeoutException e ) {
277- // if try lock timeout, need to retry
278- ++retryTimes ;
279- logger .warn ("wait to try lock to timeout when add observer into suspect ips, server: {}, tryTimes: {}" ,
280- addr .toString (), retryTimes , e );
281- } catch (InterruptedException e ) {
282- // do not throw exception to user layer
283- // next background task will continue to remove it
284- logger .warn ("waiting to get lock while interrupted by other threads" , e );
285334 }
286335 }
336+ logger .debug ("removed server from suspect list: {}" , addr );
337+ } catch (InterruptedException e ) {
338+ Thread .currentThread ().interrupt ();
339+ logger .debug ("interrupted while removing observer from suspect ips, server: {}" , addr );
287340 } finally {
288341 if (acquired ) {
289342 lock .unlock ();
@@ -294,6 +347,10 @@ private void removeFromSuspectIPs(ObServerAddr addr) {
294347 private void calcFailureOrClearCache (ObServerAddr addr ) {
295348 TableRoute tableRoute = tableClient .getTableRoute ();
296349 SuspectObServer server = suspectServers .get (addr );
350+ if (server == null ) {
351+ logger .debug ("skip failure calculation for removed suspect server: {}" , addr );
352+ return ;
353+ }
297354 server .incrementFailure ();
298355 int failure = server .getFailure ();
299356 if (failure >= failureLimit ) {
@@ -304,6 +361,18 @@ private void calcFailureOrClearCache(ObServerAddr addr) {
304361 addr , failure );
305362 }
306363
364+ int suspectServerCount () {
365+ return suspectServers .size ();
366+ }
367+
368+ boolean containsSuspectServer (ObServerAddr addr ) {
369+ return suspectServers .containsKey (addr );
370+ }
371+
372+ int lastAccessTimestampCount () {
373+ return serverLastAccessTimestamps .size ();
374+ }
375+
307376 public static class SuspectObServer {
308377 private final ObServerAddr addr ;
309378 private final long accessTimestamp ;
0 commit comments