Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable /v1/producer/pause_at_block #1057

Merged
merged 9 commits into from
Jan 22, 2025
33 changes: 33 additions & 0 deletions plugins/producer_api_plugin/producer.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,39 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/Error"
/producer/pause_at_block:
post:
summary: pause_at_block
description: |
Pause node at the specified block. Use /producer/resume to un-pause. If specified block is less than head then returns an error.

### Usage

Note that this pauses all nodes not just producer nodes. The common use case is to use on non-producer nodes as a way of freezing state. For example, a user might wish to download all the rows of a table. This would allow the user to freeze at a block number and then iterate through an entire table.
operationId: pause_at_block
requestBody:
content:
application/json:
schema:
type: object
properties:
block_num:
type: integer
description: Block number
example: 5102
responses:
"201":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/OK"
"400":
description: client error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/producer/resume:
post:
summary: resume
Expand Down
7 changes: 3 additions & 4 deletions plugins/producer_api_plugin/producer_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,9 @@ void producer_api_plugin::plugin_startup() {
app().get_plugin<http_plugin>().add_api({
CALL_WITH_400(producer, producer_rw, producer, pause,
INVOKE_V_V(producer, pause), 201),
// TODO: Enable for Spring 2.0.0
// CALL_WITH_400(producer, producer_rw, producer, pause_at_block,
// INVOKE_V_R(producer, pause_at_block, producer_plugin::pause_at_block_params), 201),
CALL_WITH_400(producer, producer_rw, producer, resume,
CALL_WITH_400(producer, producer_rw, producer, pause_at_block,
INVOKE_V_R(producer, pause_at_block, producer_plugin::pause_at_block_params), 201),
CALL_WITH_400(producer, producer_rw, producer, resume,
INVOKE_V_V(producer, resume), 201),
CALL_WITH_400(producer, producer_rw, producer, update_runtime_options,
INVOKE_V_R(producer, update_runtime_options, producer_plugin::runtime_options), 201),
Expand Down
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/production_pause_vote_timeout.py ${CM
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/production_pause_vote_timeout_test_shape.json ${CMAKE_CURRENT_BINARY_DIR}/production_pause_vote_timeout_test_shape.json COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/production_restart.py ${CMAKE_CURRENT_BINARY_DIR}/production_restart.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/production_restart_test_shape.json ${CMAKE_CURRENT_BINARY_DIR}/production_restart_test_shape.json COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pause_at_block_test.py ${CMAKE_CURRENT_BINARY_DIR}/pause_at_block_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/trx_finality_status_test.py ${CMAKE_CURRENT_BINARY_DIR}/trx_finality_status_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/trx_finality_status_forked_test.py ${CMAKE_CURRENT_BINARY_DIR}/trx_finality_status_forked_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugin_http_api_test.py ${CMAKE_CURRENT_BINARY_DIR}/plugin_http_api_test.py COPYONLY)
Expand Down Expand Up @@ -225,6 +226,8 @@ add_test(NAME subjective_billing_test COMMAND tests/subjective_billing_test.py -
set_property(TEST subjective_billing_test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME get_account_test COMMAND tests/get_account_test.py -v -p 2 -n 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST get_account_test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME pause_at_block_test COMMAND tests/pause_at_block_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST pause_at_block_test PROPERTY LABELS nonparallelizable_tests)

add_test(NAME distributed-transactions-test COMMAND tests/distributed-transactions-test.py -d 2 -p 4 -n 6 -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST distributed-transactions-test PROPERTY LABELS nonparallelizable_tests)
Expand Down
100 changes: 100 additions & 0 deletions tests/pause_at_block_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env python3

import json
import signal

from TestHarness import Account, Cluster, Node, ReturnType, TestHelper, Utils, WalletMgr
from TestHarness.TestHelper import AppArgs

###############################################################
# pause_at_block_test
#
# Verify /v1/producer/pause_at_block pauses node in all read modes
#
###############################################################

# Parse command line arguments
args = TestHelper.parse_args({"-v","--dump-error-details","--leave-running","--keep-logs","--unshared"})
Utils.Debug = args.v
dumpErrorDetails=args.dump_error_details
dontKill=args.leave_running
keepLogs=args.keep_logs

walletMgr=WalletMgr(True)
cluster=Cluster(unshared=args.unshared, keepRunning=args.leave_running, keepLogs=args.keep_logs)
cluster.setWalletMgr(walletMgr)

testSuccessful = False
try:
TestHelper.printSystemInfo("BEGIN")

specificNodeosArgs = {
0 : "--enable-stale-production",

3 : "--read-mode head",
4 : "--read-mode speculative",
5 : "--read-mode irreversible"
}
assert cluster.launch(
pnodes=3,
prodCount=3,
totalProducers=3,
totalNodes=6,
loadSystemContract=False,
activateIF=True)

prodNode = cluster.getNode(0)
prodNode2 = cluster.getNode(1)
headNode = cluster.getNode(3)
specNode = cluster.getNode(4)
irrvNode = cluster.getNode(5)

prodNode.waitForProducer("defproducerb");
prodNode.waitForProducer("defproducera");

blockNum = prodNode.getHeadBlockNum()

blockNum += 5

Utils.Print(f"Pausing at block {blockNum}")
prodNode2.processUrllibRequest("producer", "pause_at_block", {"block_num":blockNum}),
headNode.processUrllibRequest("producer", "pause_at_block", {"block_num":blockNum}),
specNode.processUrllibRequest("producer", "pause_at_block", {"block_num":blockNum}),
irrvNode.processUrllibRequest("producer", "pause_at_block", {"block_num":blockNum}),

assert prodNode.waitForLibToAdvance(), "LIB did not advance with paused nodes"
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
assert prodNode2.waitForBlock(blockNum), f"Block {blockNum} did not arrive after pausing"

Utils.Print(f"Verify paused at block {blockNum}")
assert prodNode2.getHeadBlockNum() == blockNum, "Prod Node_01 did not pause at block"
assert headNode.getHeadBlockNum() == blockNum, "Head Node_03 did not pause at block"
assert specNode.getHeadBlockNum() == blockNum, "Speculative Node_04 did not pause at block"
assert irrvNode.getHeadBlockNum() == blockNum, "Irreversible Node_05 did not pause at block"

Utils.Print(f"Verify prod node still producing blocks")
assert prodNode.waitForLibToAdvance(), "LIB did not advance with paused nodes"

Utils.Print(f"Verify still paused at block {blockNum}")
assert prodNode2.getHeadBlockNum() == blockNum, "Prod Node_01 did not pause at block"
assert headNode.getHeadBlockNum() == blockNum, "Head Node_03 did not pause at block"
assert specNode.getHeadBlockNum() == blockNum, "Speculative Node_04 did not pause at block"
assert irrvNode.getHeadBlockNum() == blockNum, "Irreversible Node_05 did not pause at block"

Utils.Print(f"Resume paused nodes")
prodNode2.processUrllibRequest("producer", "resume", {})
headNode.processUrllibRequest("producer", "resume", {})
specNode.processUrllibRequest("producer", "resume",{})
irrvNode.processUrllibRequest("producer", "resume", {})

Utils.Print(f"Verify nodes resumed")
assert prodNode2.waitForLibToAdvance(), "Prod Node_01 did not resume"
assert headNode.waitForLibToAdvance(), "Head Node_03 did not resume"
assert specNode.waitForLibToAdvance(), "Speculative Node_04 did not resume"
assert irrvNode.waitForLibToAdvance(), "Irreversible Node_05 did not resume"

testSuccessful = True
finally:
TestHelper.shutdown(cluster, walletMgr, testSuccessful, dumpErrorDetails)

exitCode = 0 if testSuccessful else 1
exit(exitCode)
Loading