77
77
import java .lang .invoke .MethodHandles ;
78
78
import java .util .function .BiPredicate ;
79
79
80
+ import static org .apache .activemq .artemis .api .core .ActiveMQExceptionType .DISCONNECTED ;
81
+
80
82
public class ClientSessionFactoryImpl implements ClientSessionFactoryInternal , ClientConnectionLifeCycleListener {
81
83
82
84
private static final Logger logger = LoggerFactory .getLogger (MethodHandles .lookup ().lookupClass ());
@@ -93,6 +95,8 @@ public class ClientSessionFactoryImpl implements ClientSessionFactoryInternal, C
93
95
94
96
private volatile TransportConfiguration backupConnectorConfig ;
95
97
98
+ private TransportConfiguration failbackConnectorConfig ;
99
+
96
100
private ConnectorFactory connectorFactory ;
97
101
98
102
private final long callTimeout ;
@@ -135,6 +139,8 @@ public class ClientSessionFactoryImpl implements ClientSessionFactoryInternal, C
135
139
136
140
private int failoverAttempts ;
137
141
142
+ private int failbackAttempts ;
143
+
138
144
private final Set <SessionFailureListener > listeners = new ConcurrentHashSet <>();
139
145
140
146
private final Set <FailoverEventListener > failoverListeners = new ConcurrentHashSet <>();
@@ -144,6 +150,8 @@ public class ClientSessionFactoryImpl implements ClientSessionFactoryInternal, C
144
150
private Future <?> pingerFuture ;
145
151
private PingRunnable pingRunnable ;
146
152
153
+ private FailbackRunnable failbackRunnable ;
154
+
147
155
private final List <Interceptor > incomingInterceptors ;
148
156
149
157
private final List <Interceptor > outgoingInterceptors ;
@@ -244,6 +252,8 @@ public ClientSessionFactoryImpl(final ServerLocatorInternal serverLocator,
244
252
245
253
this .failoverAttempts = locatorConfig .failoverAttempts ;
246
254
255
+ this .failbackAttempts = locatorConfig .failbackAttempts ;
256
+
247
257
this .scheduledThreadPool = scheduledThreadPool ;
248
258
249
259
this .threadPool = threadPool ;
@@ -722,6 +732,12 @@ private void failoverOrReconnect(final Object connectionID,
722
732
int connectorsCount = 0 ;
723
733
int failoverRetries = 0 ;
724
734
long failoverRetryInterval = retryInterval ;
735
+
736
+ //Save current connector config for failback purposes
737
+ if (failbackAttempts != 0 && failbackConnectorConfig == null ) {
738
+ failbackConnectorConfig = connectorConfig ;
739
+ }
740
+
725
741
Pair <TransportConfiguration , TransportConfiguration > connectorPair ;
726
742
BiPredicate <Boolean , Integer > failoverRetryPredicate =
727
743
(reconnected , retries ) -> clientProtocolManager .isAlive () &&
@@ -815,6 +831,128 @@ private void failoverOrReconnect(final Object connectionID,
815
831
}
816
832
}
817
833
834
+ private void failback (final ActiveMQException me ,
835
+ final TransportConfiguration previousConnectorConfig ) {
836
+
837
+ logger .debug ("Original node has come back online, performing failback now" );
838
+
839
+ for (ClientSessionInternal session : sessions ) {
840
+ SessionContext context = session .getSessionContext ();
841
+ if (context instanceof ActiveMQSessionContext ) {
842
+ ActiveMQSessionContext sessionContext = (ActiveMQSessionContext ) context ;
843
+ if (sessionContext .isKilled ()) {
844
+ setReconnectAttempts (0 );
845
+ }
846
+ }
847
+ }
848
+
849
+ Set <ClientSessionInternal > sessionsToClose = null ;
850
+ if (!clientProtocolManager .isAlive ()) {
851
+ return ;
852
+ }
853
+
854
+ Lock localFailoverLock = lockFailover ();
855
+
856
+ try {
857
+
858
+ callFailoverListeners (FailoverEventType .FAILURE_DETECTED );
859
+ callSessionFailureListeners (me , false , false , null );
860
+
861
+ if (clientProtocolManager .cleanupBeforeFailover (me )) {
862
+
863
+ RemotingConnection oldConnection = connection ;
864
+
865
+ connection = null ;
866
+
867
+ Connector localConnector = connector ;
868
+ if (localConnector != null ) {
869
+ try {
870
+ localConnector .close ();
871
+ } catch (Exception ignore ) {
872
+ // no-op
873
+ }
874
+ }
875
+
876
+ cancelScheduledTasks ();
877
+
878
+ connector = null ;
879
+
880
+ HashSet <ClientSessionInternal > sessionsToFailover ;
881
+ synchronized (sessions ) {
882
+ sessionsToFailover = new HashSet <>(sessions );
883
+ }
884
+
885
+ // Notify sessions before failover.
886
+ for (ClientSessionInternal session : sessionsToFailover ) {
887
+ session .preHandleFailover (connection );
888
+ }
889
+
890
+ boolean sessionsReconnected = false ;
891
+
892
+ connectorConfig = previousConnectorConfig ;
893
+ currentConnectorConfig = previousConnectorConfig ;
894
+
895
+ getConnection ();
896
+
897
+ if (connection != null ) {
898
+ sessionsReconnected = reconnectSessions (sessionsToFailover , oldConnection , me );
899
+
900
+ if (!sessionsReconnected ) {
901
+ if (oldConnection != null ) {
902
+ oldConnection .destroy ();
903
+ }
904
+
905
+ oldConnection = connection ;
906
+ connection = null ;
907
+ }
908
+ }
909
+
910
+ // Notify sessions after failover.
911
+ for (ClientSessionInternal session : sessionsToFailover ) {
912
+ session .postHandleFailover (connection , sessionsReconnected );
913
+ }
914
+
915
+ if (oldConnection != null ) {
916
+ oldConnection .destroy ();
917
+ }
918
+
919
+ if (connection != null ) {
920
+ callFailoverListeners (FailoverEventType .FAILOVER_COMPLETED );
921
+
922
+ }
923
+ }
924
+
925
+ if (connection == null ) {
926
+ synchronized (sessions ) {
927
+ sessionsToClose = new HashSet <>(sessions );
928
+ }
929
+ callFailoverListeners (FailoverEventType .FAILOVER_FAILED );
930
+ callSessionFailureListeners (me , true , false , null );
931
+ }
932
+ } finally {
933
+ localFailoverLock .unlock ();
934
+ }
935
+
936
+ // This needs to be outside the failover lock to prevent deadlock
937
+ if (connection != null ) {
938
+ callSessionFailureListeners (me , true , true );
939
+ }
940
+
941
+ if (sessionsToClose != null ) {
942
+ // If connection is null it means we didn't succeed in failing over or reconnecting
943
+ // so we close all the sessions, so they will throw exceptions when attempted to be used
944
+
945
+ for (ClientSessionInternal session : sessionsToClose ) {
946
+ try {
947
+ session .cleanUp (true );
948
+ } catch (Exception cause ) {
949
+ ActiveMQClientLogger .LOGGER .failedToCleanupSession (cause );
950
+ }
951
+ }
952
+ }
953
+
954
+ }
955
+
818
956
private ClientSession createSessionInternal (final String rawUsername ,
819
957
final String rawPassword ,
820
958
final boolean xa ,
@@ -1018,6 +1156,10 @@ public boolean waitForRetry(long interval) {
1018
1156
return false ;
1019
1157
}
1020
1158
1159
+ private long getRetryInterval () {
1160
+ return retryInterval ;
1161
+ }
1162
+
1021
1163
private void cancelScheduledTasks () {
1022
1164
Future <?> pingerFutureLocal = pingerFuture ;
1023
1165
if (pingerFutureLocal != null ) {
@@ -1027,8 +1169,13 @@ private void cancelScheduledTasks() {
1027
1169
if (pingRunnableLocal != null ) {
1028
1170
pingRunnableLocal .cancel ();
1029
1171
}
1172
+ FailbackRunnable failbackRunnableLocal = failbackRunnable ;
1173
+ if (failbackRunnableLocal != null ) {
1174
+ failbackRunnableLocal .cancel ();
1175
+ }
1030
1176
pingerFuture = null ;
1031
1177
pingRunnable = null ;
1178
+ failbackRunnable = null ;
1032
1179
}
1033
1180
1034
1181
private void checkCloseConnection () {
@@ -1492,6 +1639,68 @@ public synchronized void cancel() {
1492
1639
}
1493
1640
}
1494
1641
1642
+ private void attemptFailback () {
1643
+ if (failbackRunnable == null ) {
1644
+ failbackRunnable = new FailbackRunnable ();
1645
+ }
1646
+ threadPool .execute (failbackRunnable );
1647
+ }
1648
+
1649
+ private class FailbackRunnable implements Runnable {
1650
+ private boolean first = true ;
1651
+ private boolean cancelled ;
1652
+
1653
+ @ Override
1654
+ public synchronized void run () {
1655
+
1656
+ if (!first ) {
1657
+ return ;
1658
+ }
1659
+
1660
+ first = false ;
1661
+
1662
+ logger .debug ("Attempting failback. Trying to reach {} for failback" , failbackConnectorConfig .toString ());
1663
+
1664
+ int attempts = 0 ;
1665
+ long failbackRetryInterval = getRetryInterval ();
1666
+
1667
+ ConnectorFactory transportConnectorFactory ;
1668
+ Connector transportConnector ;
1669
+ Connection transportConnection ;
1670
+
1671
+ while (!cancelled && (failbackAttempts == -1 || attempts ++ < failbackAttempts )) {
1672
+
1673
+ waitForRetry (failbackRetryInterval );
1674
+ failbackRetryInterval = getNextRetryInterval (failbackRetryInterval );
1675
+
1676
+ transportConnectorFactory = instantiateConnectorFactory (failbackConnectorConfig .getFactoryClassName ());
1677
+ transportConnector = createConnector (transportConnectorFactory , failbackConnectorConfig );
1678
+ transportConnection = openTransportConnection (transportConnector );
1679
+
1680
+ if (transportConnection != null ) {
1681
+ transportConnector .close ();
1682
+ transportConnection .close ();
1683
+ ActiveMQException exception = new ActiveMQException ("Failing back to original broker: " + failbackConnectorConfig .toString (), DISCONNECTED );
1684
+ failback (exception , failbackConnectorConfig );
1685
+ break ;
1686
+ }
1687
+
1688
+ }
1689
+
1690
+ if (failbackConnectorConfig .equals (currentConnectorConfig )) {
1691
+ failbackConnectorConfig = null ;
1692
+ }
1693
+
1694
+ first = true ;
1695
+
1696
+ }
1697
+
1698
+ public synchronized void cancel () {
1699
+ cancelled = true ;
1700
+ }
1701
+
1702
+ }
1703
+
1495
1704
protected RemotingConnection establishNewConnection () {
1496
1705
Connection transportConnection = createTransportConnection ();
1497
1706
@@ -1572,6 +1781,13 @@ public void notifyNodeUp(long uniqueEventID,
1572
1781
boolean isLast ) {
1573
1782
1574
1783
try {
1784
+
1785
+ if (failbackConnectorConfig != null && connectorPair .getA () != null && TransportConfigurationUtil .isSameHost (connectorPair .getA (), failbackConnectorConfig )) {
1786
+ if (!currentConnectorConfig .equals (failbackConnectorConfig ) && failbackRunnable == null ) {
1787
+ attemptFailback ();
1788
+ }
1789
+ }
1790
+
1575
1791
// if it is our connector then set the live id used for failover
1576
1792
if (connectorPair .getA () != null && TransportConfigurationUtil .isSameHost (connectorPair .getA (), currentConnectorConfig )) {
1577
1793
liveNodeID = nodeID ;
0 commit comments