CCF/tests/committable.py

86 строки
2.8 KiB
Python

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the Apache 2.0 License.
import infra.e2e_args
import infra.network
import infra.proc
import time
import http
from ccf.tx_status import TxStatus
from loguru import logger as LOG
def wait_for_pending(client, view, seqno, timeout=3):
end_time = time.time() + timeout
while time.time() < end_time:
r = client.get(f"/node/tx?view={view}&seqno={seqno}")
assert (
r.status_code == http.HTTPStatus.OK
), f"tx request returned HTTP status {r.status_code}"
status = TxStatus(r.body.json()["status"])
if status == TxStatus.Pending:
return
elif status == TxStatus.Invalid:
raise RuntimeError(
f"Transaction ID {view}.{seqno} is marked invalid and will never be committed"
)
elif status == TxStatus.Committed:
raise RuntimeError(
f"Transaction ID {view}.{seqno} is unexpectedly marked committed"
)
else:
time.sleep(0.1)
raise TimeoutError("Timed out waiting for commit")
def run(args):
# This is deliberately 5, because the rest of the test depends on this
# to grow a prefix and allow just enough nodes to resume to reach the
# desired election result. Conversion to a general f isn't trivial.
hosts = ["local://localhost"] * 5
with infra.network.network(
hosts, args.binary_dir, args.debug_nodes, args.perf_nodes, pdb=args.pdb
) as network:
network.start_and_join(args)
primary, backups = network.find_nodes()
# Suspend three of the backups to prevent commit
backups[1].suspend()
backups[2].suspend()
backups[3].stop()
txs = []
# Run some transactions that can't be committed
with primary.client("user0") as uc:
for i in range(3):
txs.append(
uc.post("/app/log/private", {"id": 100 + i, "msg": "Hello world"})
)
sig_view, sig_seqno = txs[-1].view, txs[-1].seqno + 1
with backups[0].client() as bc:
wait_for_pending(bc, sig_view, sig_seqno)
# Kill the primary, restore other backups
primary.stop()
backups[1].resume()
backups[2].resume()
new_primary, new_term = network.wait_for_new_primary(
primary.node_id, timeout_multiplier=6
)
LOG.debug(f"New primary is {new_primary.node_id} in term {new_term}")
# Check that uncommitted but committable suffix is preserved
with new_primary.client("user0") as uc:
check_commit = infra.checker.Checker(uc)
for tx in txs:
check_commit(tx)
if __name__ == "__main__":
args = infra.e2e_args.cli_args()
args.package = "liblogging"
run(args)