mutating func mergeForward()

in Sources/DistributedActors/Cluster/MembershipGossip/Cluster+MembershipGossip.swift [61:112]


        mutating func mergeForward(incoming: MembershipGossip) -> MergeDirective {
            var incoming = incoming

            // 1) decide the relationship between this gossip and the incoming one
            let causalRelation: VersionVector.CausalRelation = self.version.compareTo(incoming.version)

            // 1.1) Protect the node from any gossip from a .down node (!), it cannot and MUST NOT be trusted.
            let incomingGossipOwnerKnownLocally = self.membership.uniqueMember(incoming.owner)
            guard let incomingOwnerMember = incoming.membership.uniqueMember(incoming.owner) else {
                return .init(causalRelation: causalRelation, effectiveChanges: [])
            }
            switch incomingGossipOwnerKnownLocally {
            case .some(let locallyKnownMember) where locallyKnownMember.status.isDown:
                // we have NOT removed it yet, but it is down, so we ignore it
                return .init(causalRelation: causalRelation, effectiveChanges: [])
            case .none where incomingOwnerMember.status.isAtLeast(.down):
                // we have likely removed it, and it is down anyway, so we ignore it completely
                return .init(causalRelation: causalRelation, effectiveChanges: [])
            default:
                () // ok, so it is fine and still alive
            }

            // 1.2) Protect from zombies: Any nodes that we know are dead or down, we should not accept any information from
            let incomingConcurrentDownMembers = incoming.membership.members(atLeast: .down)
            for pruneFromIncomingBeforeMerge in incomingConcurrentDownMembers
                where self.membership.uniqueMember(pruneFromIncomingBeforeMerge.uniqueNode) == nil {
                _ = incoming.pruneMember(pruneFromIncomingBeforeMerge)
            }

            // 2) calculate membership changes; if this gossip is strictly more recent than the incoming one,
            // we can skip this as we "know" that we already know everything that the incoming has to offer (optimization)
            let changes: [MembershipChange]
            if case .happenedAfter = causalRelation {
                // ignore all changes >>
                // our local view happened strictly _after_ the incoming one, thus it is guaranteed
                // it will not provide us with new information; This is only an optimization, and would work correctly without it.
                changes = []
            } else {
                // incoming is concurrent, ahead, or same
                changes = self.membership.mergeFrom(incoming: incoming.membership, myself: self.owner)
            }

            self.seen.merge(selfOwner: self.owner, incoming: incoming.seen)

            // 3) if any removals happened, we need to prune the removed nodes from the seen table
            for change in changes
                where change.status.isRemoved && change.member.uniqueNode != self.owner {
                self.seen.prune(change.member.uniqueNode)
            }

            return .init(causalRelation: causalRelation, effectiveChanges: changes)
        }