@@ -505,9 +505,87 @@ export class ConatServer extends EventEmitter {
505
505
// effort. The main advantage is that no communication
506
506
// or coordination between nodes is needed to "fix or agree
507
507
// on something", and that's a huge advantage!!
508
+
509
+ // This choice algorithm is used in saveNonredundantStickyState below!
510
+ // **Don't change this without changing that!!**
508
511
return Array . from ( targets ) . sort ( ) [ 0 ] ;
509
512
} ;
510
513
514
+ private saveNonredundantStickyState = ( link : ClusterLink ) => {
515
+ // When a link is about to be closed (e.g., the node died or is lost),
516
+ // we store its non-redundant sticky state in our own sticky state,
517
+ // so those routing choices aren't lost. Hopefully only a single
518
+ // node in the cluster stores this info (due to the randomness of removing,
519
+ // it'll be the one that happens to do this first), but if more than one
520
+ // does, it's just less efficient.
521
+
522
+ if ( link . clusterName != this . clusterName ) {
523
+ // only worry about nodes in the same cluster
524
+ return ;
525
+ }
526
+ const cluster = this . clusterLinks [ this . clusterName ] ;
527
+ const isRedundant = ( pattern , subject , target ) => {
528
+ let t = this . sticky [ pattern ] ?. [ subject ] ;
529
+ if ( t == target ) {
530
+ // we already have it -- not redundnant
531
+ return true ;
532
+ }
533
+
534
+ for ( const id in cluster ) {
535
+ const link = cluster [ id ] ;
536
+ if ( id == link . id ) {
537
+ continue ;
538
+ }
539
+ const s = cluster [ id ] . sticky [ pattern ] ?. subject ;
540
+ if ( s !== undefined ) {
541
+ if ( s == target ) {
542
+ // someone else has it, so definitely not redundant
543
+ return true ;
544
+ }
545
+ if ( t === undefined || s < t ) {
546
+ t = s ;
547
+ }
548
+ }
549
+ }
550
+ // nobody else has this mapping... but maybe it's not used
551
+ // due to other conflicting ones?
552
+ if ( t !== undefined && t < target ) {
553
+ // target wouldn't be used since there's conflicting ones that are smaller
554
+ return true ;
555
+ }
556
+ // target *would* be used, but nobody else knows it, so we probably must save it.
557
+ // Make sure the pattern is still of interest first
558
+ if ( this . interest . hasPattern ( pattern ) ) {
559
+ // we need it!
560
+ return false ;
561
+ }
562
+ for ( const id in cluster ) {
563
+ const link = cluster [ id ] ;
564
+ if ( id == link . id ) {
565
+ continue ;
566
+ }
567
+ if ( link . interest . hasPattern ( pattern ) ) {
568
+ return false ;
569
+ }
570
+ }
571
+ // nothing in the remaining cluster is subscribed to this pattern, so
572
+ // no point in preserving this sticky routing info
573
+ return true ;
574
+ } ;
575
+
576
+ // { [pattern: string]: { [subject: string]: string } }
577
+ for ( const pattern in link . sticky ) {
578
+ const x = link . sticky [ pattern ] ;
579
+ for ( const subject in x ) {
580
+ const target = x [ subject ] ;
581
+ if ( ! isRedundant ( pattern , subject , target ) ) {
582
+ // we save the assignment
583
+ this . updateSticky ( { pattern, subject, target } ) ;
584
+ }
585
+ }
586
+ }
587
+ } ;
588
+
511
589
///////////////////////////////////////
512
590
// SUBSCRIBE and PUBLISH
513
591
///////////////////////////////////////
@@ -1223,6 +1301,8 @@ export class ConatServer extends EventEmitter {
1223
1301
// already gone
1224
1302
return ;
1225
1303
}
1304
+
1305
+ this . saveNonredundantStickyState ( link ) ;
1226
1306
link . close ( ) ;
1227
1307
delete this . clusterLinks [ link . clusterName ] [ link . id ] ;
1228
1308
delete this . clusterLinksByAddress [ link . address ] ;
@@ -1474,7 +1554,7 @@ export class ConatServer extends EventEmitter {
1474
1554
signal ,
1475
1555
) ;
1476
1556
}
1477
- // check if there is already interest in the local cluster
1557
+ // check if there is already known interest
1478
1558
const links = this . superclusterLinks ( ) ;
1479
1559
for ( const link of links ) {
1480
1560
if ( link . hasInterest ( subject ) ) {
0 commit comments