@celerispay/hazelcast-client 3.12.7-2 → 3.12.7-4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -115,6 +115,13 @@ export declare class ClusterService {
|
|
|
115
115
|
private tryConnectingToAddresses(index, remainingAttemptLimit, attemptPeriod, cause?);
|
|
116
116
|
private handleMember(member, eventType);
|
|
117
117
|
private handleMemberList(members);
|
|
118
|
+
/**
|
|
119
|
+
* Proactively opens connections to all non-owner cluster members.
|
|
120
|
+
* Required for smart routing: the client needs a live connection to every
|
|
121
|
+
* node so it can route operations to the correct partition owner.
|
|
122
|
+
* Failures are silently ignored — the periodic reconnection task will retry.
|
|
123
|
+
*/
|
|
124
|
+
private connectToNonOwnerMembers(members);
|
|
118
125
|
private detectMembershipEvents(prevMembers);
|
|
119
126
|
private fireMembershipEvent(membershipEvent);
|
|
120
127
|
private handleMemberAttributeChange(uuid, key, operationType, value);
|
|
@@ -280,6 +280,13 @@ var ClusterService = /** @class */ (function () {
|
|
|
280
280
|
this.client.getConnectionManager().forceCleanupDeadConnections();
|
|
281
281
|
// Clear partition information
|
|
282
282
|
this.client.getPartitionService().clearPartitionTable();
|
|
283
|
+
// IMPORTANT: Unblock all down addresses before attempting reconnection.
|
|
284
|
+
// The address was just marked as down by onConnectionClosed/onHeartbeatStopped,
|
|
285
|
+
// but connectToCluster() skips blocked addresses — so without clearing the block
|
|
286
|
+
// it will immediately fail with "Unable to connect to any address".
|
|
287
|
+
// clearAllCredentials() already clears failedConnections inside ConnectionManager.
|
|
288
|
+
this.logger.info('ClusterService', '🔓 SINGLE-NODE RESET: Unblocking all addresses for fresh reconnection...');
|
|
289
|
+
this.downAddresses.clear();
|
|
283
290
|
// Direct reconnection without waiting for member events
|
|
284
291
|
this.logger.info('ClusterService', '🔄 SINGLE-NODE RESET: Attempting direct reconnection...');
|
|
285
292
|
this.connectToCluster()
|
|
@@ -467,12 +474,46 @@ var ClusterService = /** @class */ (function () {
|
|
|
467
474
|
this.logger.info('ClusterService', 'Members received.', this.members);
|
|
468
475
|
// Log current state after member list update
|
|
469
476
|
this.logCurrentState();
|
|
477
|
+
// In smart routing mode, proactively open connections to all non-owner members
|
|
478
|
+
// so that partition-aware routing can work across all nodes.
|
|
479
|
+
if (this.client.getConfig().networkConfig.smartRouting) {
|
|
480
|
+
this.connectToNonOwnerMembers(members);
|
|
481
|
+
}
|
|
470
482
|
var events = this.detectMembershipEvents(prevMembers);
|
|
471
483
|
for (var _i = 0, events_1 = events; _i < events_1.length; _i++) {
|
|
472
484
|
var event = events_1[_i];
|
|
473
485
|
this.fireMembershipEvent(event);
|
|
474
486
|
}
|
|
475
487
|
};
|
|
488
|
+
/**
|
|
489
|
+
* Proactively opens connections to all non-owner cluster members.
|
|
490
|
+
* Required for smart routing: the client needs a live connection to every
|
|
491
|
+
* node so it can route operations to the correct partition owner.
|
|
492
|
+
* Failures are silently ignored — the periodic reconnection task will retry.
|
|
493
|
+
*/
|
|
494
|
+
ClusterService.prototype.connectToNonOwnerMembers = function (members) {
|
|
495
|
+
var _this = this;
|
|
496
|
+
var ownerAddress = this.ownerConnection ? this.ownerConnection.getAddress().toString() : null;
|
|
497
|
+
members.forEach(function (member) {
|
|
498
|
+
var memberAddressStr = member.address.toString();
|
|
499
|
+
// Skip the owner — already connected
|
|
500
|
+
if (memberAddressStr === ownerAddress) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
// Skip if already connected
|
|
504
|
+
if (_this.client.getConnectionManager().hasConnection(member.address)) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
_this.logger.info('ClusterService', "\uD83D\uDD17 SMART-ROUTING: Opening connection to non-owner member " + memberAddressStr);
|
|
508
|
+
_this.client.getConnectionManager().getOrConnect(member.address, false)
|
|
509
|
+
.then(function () {
|
|
510
|
+
_this.logger.info('ClusterService', "\u2705 SMART-ROUTING: Connected to member " + memberAddressStr);
|
|
511
|
+
})
|
|
512
|
+
.catch(function (err) {
|
|
513
|
+
_this.logger.warn('ClusterService', "\u26A0\uFE0F SMART-ROUTING: Could not connect to member " + memberAddressStr + ": " + err.message);
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
};
|
|
476
517
|
ClusterService.prototype.detectMembershipEvents = function (prevMembers) {
|
|
477
518
|
var events = [];
|
|
478
519
|
var eventMembers = Array.from(this.members);
|
|
@@ -572,6 +613,7 @@ var ClusterService = /** @class */ (function () {
|
|
|
572
613
|
* @param member The member that was added
|
|
573
614
|
*/
|
|
574
615
|
ClusterService.prototype.handleMemberAdded = function (member) {
|
|
616
|
+
var _this = this;
|
|
575
617
|
this.logger.info('ClusterService', "\u2705 SERVER CONFIRMED: Member[ uuid: " + member.uuid + ", address: " + member.address.toString() + "] added to cluster");
|
|
576
618
|
// SERVER-FIRST: Store server data as credentials
|
|
577
619
|
// The server is the authority - we store what it tells us
|
|
@@ -607,6 +649,22 @@ var ClusterService = /** @class */ (function () {
|
|
|
607
649
|
// Refresh partition table (KEEPING REFRESH METHOD UNTOUCHED as requested)
|
|
608
650
|
this.client.getPartitionService().refresh();
|
|
609
651
|
this.logger.info('ClusterService', "\u2705 SERVER-FIRST: Member " + member.uuid + " at " + member.address.toString() + " credentials stored from server data");
|
|
652
|
+
// In smart routing mode, proactively open a connection to this member
|
|
653
|
+
// so it is immediately available for partition-aware routing.
|
|
654
|
+
if (this.client.getConfig().networkConfig.smartRouting) {
|
|
655
|
+
var ownerAddress = this.ownerConnection ? this.ownerConnection.getAddress().toString() : null;
|
|
656
|
+
var memberAddressStr_1 = member.address.toString();
|
|
657
|
+
if (memberAddressStr_1 !== ownerAddress && !this.client.getConnectionManager().hasConnection(member.address)) {
|
|
658
|
+
this.logger.info('ClusterService', "\uD83D\uDD17 SMART-ROUTING: Opening connection to newly joined member " + memberAddressStr_1);
|
|
659
|
+
this.client.getConnectionManager().getOrConnect(member.address, false)
|
|
660
|
+
.then(function () {
|
|
661
|
+
_this.logger.info('ClusterService', "\u2705 SMART-ROUTING: Connected to newly joined member " + memberAddressStr_1);
|
|
662
|
+
})
|
|
663
|
+
.catch(function (err) {
|
|
664
|
+
_this.logger.warn('ClusterService', "\u26A0\uFE0F SMART-ROUTING: Could not connect to newly joined member " + memberAddressStr_1 + ": " + err.message);
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
610
668
|
};
|
|
611
669
|
/**
|
|
612
670
|
* Finds the current owner from the cluster state
|
|
@@ -755,21 +813,35 @@ var ClusterService = /** @class */ (function () {
|
|
|
755
813
|
}
|
|
756
814
|
// Remove from down addresses to allow connection attempt
|
|
757
815
|
this.downAddresses.delete(addressStr);
|
|
758
|
-
|
|
759
|
-
//
|
|
760
|
-
|
|
816
|
+
// Determine whether to connect as owner.
|
|
817
|
+
// If we have no owner connection (e.g. after a single-node restart), we must
|
|
818
|
+
// authenticate as owner so the server assigns fresh UUIDs. Connecting as a
|
|
819
|
+
// non-owner with null UUIDs will always be rejected by the server.
|
|
820
|
+
var needsOwner = !this.ownerConnection || !this.ownerConnection.isHealthy();
|
|
821
|
+
var connectAsOwner = needsOwner;
|
|
822
|
+
this.logger.info('ClusterService', "Attempting reconnection to " + addressStr + " (asOwner=" + connectAsOwner + ", needsOwner=" + needsOwner + ")");
|
|
823
|
+
this.client.getConnectionManager().getOrConnect(address, connectAsOwner)
|
|
761
824
|
.then(function (connection) {
|
|
762
825
|
_this.logger.info('ClusterService', "Successfully reconnected to " + addressStr);
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
_this.
|
|
826
|
+
if (connectAsOwner) {
|
|
827
|
+
// We authenticated as owner — make it official and re-register membership listener
|
|
828
|
+
connection.setAuthenticatedAsOwner(true);
|
|
829
|
+
_this.ownerConnection = connection;
|
|
830
|
+
_this.logger.info('ClusterService', "Promoted " + addressStr + " to owner after reconnection");
|
|
831
|
+
// Re-register membership listener so member events resume
|
|
832
|
+
return _this.initMembershipListener().then(function () {
|
|
833
|
+
_this.logger.info('ClusterService', "Membership listener re-registered after reconnection to " + addressStr);
|
|
834
|
+
_this.client.getPartitionService().refresh();
|
|
835
|
+
}).catch(function (err) {
|
|
836
|
+
_this.logger.warn('ClusterService', "Failed to re-register membership listener after reconnection: " + err.message);
|
|
837
|
+
_this.client.getPartitionService().refresh();
|
|
838
|
+
});
|
|
767
839
|
}
|
|
768
840
|
else {
|
|
841
|
+
// Non-owner: evaluate if ownership should change (multi-node case)
|
|
769
842
|
_this.logger.debug('ClusterService', "Keeping " + addressStr + " as member connection, current owner is healthy");
|
|
843
|
+
_this.client.getPartitionService().refresh();
|
|
770
844
|
}
|
|
771
|
-
// Trigger partition service refresh to update routing information
|
|
772
|
-
_this.client.getPartitionService().refresh();
|
|
773
845
|
}).catch(function (error) {
|
|
774
846
|
_this.logger.warn('ClusterService', "Reconnection attempt to " + addressStr + " failed:", error);
|
|
775
847
|
// Mark the address as down again, but with a shorter block duration for reconnection attempts
|
package/package.json
CHANGED