예제 #1
0
def acceptor_pre_accept(q: Quorum, slot: Slot, peer: int,
                        pre_accept: packet.PreAcceptRequest):
    try:
        inst = yield Store(
            slot,
            InstanceStoreState(
                pre_accept.ballot,
                State(Stage.PreAccepted, pre_accept.command, pre_accept.seq,
                      pre_accept.deps)))  # type: InstanceStoreState

        deps_comm_mask = []

        for dep_slot in inst.state.deps:
            xx = yield Load(dep_slot)  # type: InstanceStoreState

            deps_comm_mask.append(xx.state.stage >= Stage.Committed)

        yield LeaderStop(slot, 'acceptor')
        yield Send(
            peer,
            packet.PreAcceptResponseAck(slot, inst.ballot, inst.state.seq,
                                        inst.state.deps, deps_comm_mask))

    except IncorrectStage:
        return
    except IncorrectBallot as e:
        yield Send(peer,
                   packet.PreAcceptResponseNack(slot, e.inst.ballot, 'BALLOT'))
        return
    except SlotTooOld:
        yield Send(peer, packet.DivergedResponse(slot))
예제 #2
0
    def load(self, slot: Slot):
        if self.cp.earlier(slot):
            raise SlotTooOld(slot, None, None)

        r = self.inst.get(slot)
        exists = True

        if r is None:
            exists = False
            r = InstanceStoreState(slot.ballot_initial(),
                                   State(Stage.Prepared, None, -1, []))

        return LoadResult(exists, r)
예제 #3
0
def leader_client_request(q: Quorum, slot: Slot, cmd: Command):
    inst = yield Store(
        slot,
        InstanceStoreState(
            slot.ballot_initial(q.epoch),
            State(
                Stage.PreAccepted,
                cmd,
                0,
                []
            )
        )
    )
    yield from leader_pre_accept(q, slot, inst, True)
예제 #4
0
def acceptor_commit(q: Quorum, slot: Slot, peer: int,
                    commit: packet.CommitRequest):
    try:
        inst = yield Store(slot,
                           InstanceStoreState(
                               commit.ballot,
                               State(Stage.Committed, commit.command,
                                     commit.seq,
                                     commit.deps)))  # type: InstanceStoreState

        yield LeaderStop(slot, 'acceptor')
        yield Send(peer, packet.AcceptResponseAck(slot, inst.ballot))
    except IncorrectBallot as e:
        return
    except IncorrectStage as e:
        return
    except SlotTooOld:
        yield Send(peer, packet.DivergedResponse(slot))
예제 #5
0
def leader_accept(q: Quorum, slot: Slot, inst: InstanceStoreState):
    for peer in q.peers:
        yield Send(peer, packet.AcceptRequest(slot, inst.ballot, inst.state.command, inst.state.seq, inst.state.deps))

    replies = []

    while len(replies) + 1 < q.slow_size:
        _, (ack, nack) = yield Receive.any(
            packet.AcceptResponseAck,
            packet.AcceptResponseNack,
        )

        ack: Optional[packet.AcceptResponseAck]
        nack: Optional[packet.AcceptResponseNack]

        if ack:
            if ack.ballot != inst.ballot:
                # logger.debug(f'{q.replica_id} accept Raising do to > {ack} {nack} {ack} {inst}')
                # raise ExplicitPrepare('accept:BALLOT')
                pass
            else:
                replies.append(ack)

        if nack:
            # logger.debug(f'{q.replica_id} accept Raising do to nack {ack} {nack} {ack} {inst}')
            pass
            # raise ExplicitPrepare('accept:NACK')

    inst = yield Store(
        slot,
        InstanceStoreState(
            inst.ballot,
            State(
                Stage.Committed,
                inst.state.command,
                inst.state.seq,
                inst.state.deps
            )
        )
    )

    yield from leader_commit(q, slot, inst)
예제 #6
0
    def update(self, slot: Slot, new: InstanceStoreState):
        exists, old = self.load(slot)

        if new.ballot < old.ballot:
            raise IncorrectBallot(slot, old, new)

        if new.state.stage < old.state.stage:
            raise IncorrectStage(slot, old, new)

        if old.state.stage > Stage.PreAccepted and old.state.command is not None and old.state.command != new.state.command:
            raise IncorrectCommand(slot, old, new)

        if new.state.stage == Stage.PreAccepted and new.state.command:
            # rethink the command ordering
            seq, deps = self.deps_cache.xchange(slot, new.state.command)

            upd = InstanceStoreState(
                new.ballot,
                State(new.state.stage, new.state.command,
                      max(seq, new.state.seq),
                      sorted(set(new.state.deps + deps))))
        else:
            upd = new

        self.inst[slot] = upd

        if exists and old.state.command:
            if old.state.command.id in self.cmd_to_slot:
                del self.cmd_to_slot[old.state.command.id]
            else:
                logger.error(
                    f'Command id {old.state.command} not found in self.cmd_to_slot'
                )

        if new.state.command:
            self.cmd_to_slot[new.state.command.id] = slot

        return old, upd
예제 #7
0
def leader_explicit_prepare(q: Quorum, slot: Slot, reason=None):
    inst = yield Load(slot)  # type: InstanceStoreState

    try:
        ballot = inst.ballot.next(q.replica_id)
        inst = yield Store(
            slot,
            InstanceStoreState(
                ballot,
                inst.state
            )
        )  # type: InstanceStoreState
    except (IncorrectBallot, IncorrectStage) as e:
        return

    for peer in q.peers:
        yield Send(peer, packet.PrepareRequest(slot, inst.ballot))

    replies = list()  # type: List[RecoveryReply]

    replies.append(
        RecoveryReply(
            q.replica_id,
            packet.PrepareResponseAck(
                slot,
                ballot,
                inst.state.command,
                inst.state.seq,
                inst.state.deps,
                inst.state.stage
            )
        )
    )

    while len(replies) < q.slow_size:
        peer, (ack, nack, diverged) = yield Receive.any(
            packet.PrepareResponseAck,
            packet.PreAcceptResponseNack,
            packet.DivergedResponse
        )

        peer: int
        ack: Optional[packet.PrepareResponseAck]
        nack: Optional[packet.PrepareResponseNack]
        diverged: Optional[packet.DivergedResponse]

        if ack:
            if ack.ballot != ballot:
                continue

            # todo: should replies from non-current ballots be ignored?
            replies.append(RecoveryReply(peer, ack))

        if nack:
            if nack.ballot != ballot:
                continue

            # logger.debug(f'{q.replica_id} explicit prepare NACK {inst} {ballot}')
            raise ExplicitPrepare('explicit:NACK')

        if diverged:
            raise Exception("I have diverged")

    len_rep = len(replies)

    max_ballot = max(x.r.ballot for x in replies)
    replies = [x for x in replies if x.r.ballot == max_ballot]
    max_state = max(x.r.state for x in replies)

    replies = [x for x in replies if x.r.state == max_state]

    len_rep_fil = len(replies)

    loglog = lambda ni, tag=None: logger.info(f'{q.replica_id} Storing {ni} {len_rep} {len_rep_fil} {ballot} {tag}')
    loglog = lambda ni, tag=None: None

    if max_state == Stage.Committed:
        reply = [x for x in replies if x.r.state == max_state][0].r

        inst = yield Store(
            slot,
            InstanceStoreState(
                ballot,
                State(
                    Stage.Committed,
                    reply.command,
                    reply.seq,
                    reply.deps
                )
            )
        )

        yield from leader_commit(q, slot, inst)
        return
    elif max_state == Stage.Accepted:
        reply = [x for x in replies if x.r.state == max_state][0].r

        inst = yield Store(
            slot,
            InstanceStoreState(
                ballot,
                State(
                    Stage.Accepted,
                    reply.command,
                    reply.seq,
                    reply.deps
                )
            )
        )

        yield from leader_accept(q, slot, inst)
        return

    identic_keys = defaultdict(list)

    for r in replies:
        identic_keys[(r.r.state, r.r.command.id if r.r.command else None, r.r.seq, tuple(sorted(r.r.deps)))].append(r)

    identic_groups = [
        (x, list(y))
        for x, y in identic_keys.items()
    ]  # type: List[Tuple[Tuple[InstanceState, Command, int, List[int]], List[RecoveryReply]]]
    identic_groups = [
        y
        for x, y in identic_groups
        if len(y) >= q.slow_size - 1 and
           all(z.p != slot.replica_id for z in y) and x[0] == Stage.PreAccepted
    ]

    if len(identic_groups):
        reply = identic_groups[0][0].r
        new_inst = yield Store(
            slot,
            InstanceStoreState(
                ballot,
                State(
                    Stage.Accepted,
                    reply.command,
                    reply.seq,
                    reply.deps
                )
            )
        )
        yield from leader_accept(q, slot, new_inst)
    elif max_state == Stage.PreAccepted:
        reply = [x for x in replies if x.r.state == max_state][0].r

        new_inst = yield Store(
            slot,
            InstanceStoreState(
                ballot,
                State(
                    Stage.PreAccepted,
                    reply.command,
                    reply.seq,
                    reply.deps
                )
            )
        )

        yield from leader_pre_accept(q, slot, new_inst, False)
    else:
        new_inst = yield Store(
            slot,
            InstanceStoreState(
                ballot,
                State(
                    Stage.PreAccepted,
                    None,
                    0,
                    []
                )
            )
        )
        yield from leader_pre_accept(q, slot, new_inst, False)
예제 #8
0
def leader_pre_accept(q: Quorum, slot: Slot, inst: InstanceStoreState, allow_fast: True):
    for peer in q.peers:
        yield Send(
            peer,
            packet.PreAcceptRequest(slot, inst.ballot, inst.state.command, inst.state.seq, inst.state.deps)
        )

    replies = []
    replies: List[packet.PreAcceptResponseAck]

    while len(replies) + 1 < q.fast_size:
        _, (ack, nack) = yield Receive.any(
            packet.PreAcceptResponseAck,
            packet.PreAcceptResponseNack,
        )

        ack: Optional[packet.PreAcceptResponseAck]
        nack: Optional[packet.PreAcceptResponseNack]

        if ack:
            if ack.ballot != inst.ballot:
                # logger.debug(f'{q.replica_id} pre_accept Raising do to > {ack} {nack} {ack} {inst}')
                # raise ExplicitPrepare('pre_accept:BALLOT')
                pass
            else:
                replies.append(ack)
        if nack:
            # logger.debug(f'{q.replica_id} pre_accept Raising do to nack {ack} {nack} {ack} {inst}')
            pass
            # raise ExplicitPrepare('pre_accept:NACK')

    if allow_fast and (
        all(x.deps == inst.state.deps for x in replies) and
        all(x.seq == inst.state.seq for x in replies)
    ):
        inst = yield Store(
            slot,
            InstanceStoreState(
                inst.ballot,
                State(
                    Stage.Committed,
                    inst.state.command,
                    inst.state.seq,
                    inst.state.deps
                )
            )
        )
        yield from leader_commit(q, slot, inst)
    else:
        seq = max(inst.state.seq, max(x.seq for x in replies))

        deps = []
        for rep in replies:
            deps.extend(rep.deps)

        deps = sorted(set(deps))

        inst = yield Store(
            slot,
            InstanceStoreState(
                inst.ballot,
                State(
                    Stage.Accepted,
                    inst.state.command,
                    seq,
                    deps
                )
            )
        )

        yield from leader_accept(q, slot, inst)