Skip to content

Commit 7471079

Browse files
authored
Merge pull request #1849 from AntelopeIO/merge_sync_call_resource_alloc_fix
[2.0 -> 2.0.0-dev1] Allocate resources required by sync calls up front
2 parents c62a656 + bea07f9 commit 7471079

File tree

4 files changed

+104
-9
lines changed

4 files changed

+104
-9
lines changed

libraries/chain/controller.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2091,6 +2091,10 @@ struct controller_impl {
20912091

20922092
protocol_features.init( db );
20932093

2094+
// Allocate resources required by nested sync calls.
2095+
const auto max_call_depth = db.get<global_property_object>().configuration.max_sync_call_depth;
2096+
set_max_call_depth_for_call_res_pools(max_call_depth);
2097+
20942098
// At startup, no transaction specific logging is possible
20952099
if (auto dm_logger = get_deep_mind_logger(false)) {
20962100
dm_logger->on_startup(db, chain_head.block_num());
@@ -6468,9 +6472,6 @@ void controller_impl::on_activation<builtin_protocol_feature_t::sync_call>() {
64686472
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "get_call_data" );
64696473
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "set_call_return_value" );
64706474
} );
6471-
6472-
const auto max_call_depth = db.get<global_property_object>().configuration.max_sync_call_depth;
6473-
set_max_call_depth_for_call_res_pools(max_call_depth);
64746475
}
64756476

64766477
/// End of protocol feature activation handlers

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/production_pause_vote_timeout_test_sh
8989
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/production_restart.py ${CMAKE_CURRENT_BINARY_DIR}/production_restart.py COPYONLY)
9090
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/production_restart_test_shape.json ${CMAKE_CURRENT_BINARY_DIR}/production_restart_test_shape.json COPYONLY)
9191
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/pause_at_block_test.py ${CMAKE_CURRENT_BINARY_DIR}/pause_at_block_test.py COPYONLY)
92+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/sync_call_restart.py ${CMAKE_CURRENT_BINARY_DIR}/sync_call_restart.py COPYONLY)
9293
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/trx_finality_status_test.py ${CMAKE_CURRENT_BINARY_DIR}/trx_finality_status_test.py COPYONLY)
9394
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/trx_finality_status_forked_test.py ${CMAKE_CURRENT_BINARY_DIR}/trx_finality_status_forked_test.py COPYONLY)
9495
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugin_http_api_test.py ${CMAKE_CURRENT_BINARY_DIR}/plugin_http_api_test.py COPYONLY)
@@ -185,6 +186,7 @@ add_np_test(NAME interrupt-read-only-trx-parallel-if-eos-vm-oc-test COMMAND test
185186
add_np_test(NAME subjective_billing_test COMMAND tests/subjective_billing_test.py -v -p 2 -n 4 ${UNSHARE})
186187
add_np_test(NAME get_account_test COMMAND tests/get_account_test.py -v -p 2 -n 3 ${UNSHARE})
187188
add_np_test(NAME pause_at_block_test COMMAND tests/pause_at_block_test.py -v ${UNSHARE})
189+
add_np_test(NAME sync_call_restart COMMAND tests/sync_call_restart.py -v ${UNSHARE})
188190

189191
add_np_test(NAME distributed-transactions-test COMMAND tests/distributed-transactions-test.py -d 2 -p 4 -n 6 -v ${UNSHARE})
190192
add_np_test(NAME distributed-transactions-if-test COMMAND tests/distributed-transactions-test.py -d 2 -p 4 -n 6 --activate-if -v ${UNSHARE})

tests/read_only_trx_test.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,9 @@ def verifyOcVirtualMemory():
154154
# In OC tierup a memory slice is 8GB.
155155
# One extra slice is added for each allocation.
156156
# For actions, the main thread uses 529 slices; each read-only thread uses 11 slices.
157-
# For sync calls, total 2 slices per call depth for a total of max_sync_call_depth on
158-
# each thread
159-
# Total virtual memory allocated by OC is around:
160-
# 529 slices * 8GB (for main thread) + numReadOnlyThreads * 11 slices * 8GB
157+
# For sync calls, depth 1 reserves 5 slices, depth 2 reserves 4 slices, ...
158+
# depths 5 to 16 each 1 slices. Total is 26 slices for a max_sync_call_depth
159+
# of 16 for each thread.
161160
#
162161
# In addition, main thread and each read-only thread pre-allocates
163162
# `max_sync_call_depth` wasm allocator. Each wasm allocator mmap 8GB
@@ -173,10 +172,10 @@ def verifyOcVirtualMemory():
173172
memoryPerSlice = 8 * GB
174173
actionMainThreadSlices = 529
175174
actionRoThreadsSlices = args.read_only_threads * 11
176-
syncCallSlices = 2 * totalThreads * maxSyncCallDepth
175+
syncCallSlices = 26 * totalThreads
177176
memoryByOC = (actionMainThreadSlices + actionRoThreadsSlices + syncCallSlices) * memoryPerSlice
178177

179-
memoryForOthers = 1000 * GB # add 1TB for virtual memory used by others
178+
memoryForOthers = 2500 * GB # add virtual memory used by others
180179
expectedVmSize = memoryByOC + memoryByWasmAllocators + memoryForOthers
181180
Utils.Print(f"pid: {apiNode.pid}, totalThreads: {totalThreads}, memoryByWasmAllocators: {memoryByWasmAllocators}, memoryByOC: {memoryByOC}, memoryForOthers: {memoryForOthers}, actualVmSize: {actualVmSize}, expectedVmSize: {expectedVmSize}")
182181
assert(actualVmSize < expectedVmSize)

tests/sync_call_restart.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
#!/usr/bin/env python3
2+
import signal
3+
import platform
4+
5+
from TestHarness import Account, Cluster, TestHelper, Utils, WalletMgr
6+
7+
###############################################################
8+
# sync_call_restart
9+
#
10+
# Tests restart of a node with sync_call protocol feature already activated.
11+
#
12+
# We had a bug that resources required by sync calls (in particular for OC) were
13+
# not allocated on restart if sync_call protocol feature had already been activated
14+
# https://github.com/AntelopeIO/spring/issues/1847
15+
#
16+
# This test activates sync_call protocol feature, does a snapshot, kills the node,
17+
# restarts the node, pushes a transaction containing 2-levels of nested sync calls,
18+
# and verifies node not crashed.
19+
###############################################################
20+
21+
Print=Utils.Print
22+
errorExit=Utils.errorExit
23+
24+
args=TestHelper.parse_args({"--keep-logs","--dump-error-details","-v","--leave-running","--unshared"})
25+
pnodes=1
26+
debug=args.v
27+
total_nodes=pnodes
28+
dumpErrorDetails=args.dump_error_details
29+
30+
Utils.Debug=debug
31+
testSuccessful=False
32+
33+
cluster=Cluster(unshared=args.unshared, keepRunning=args.leave_running, keepLogs=args.keep_logs)
34+
walletMgr=WalletMgr(True, keepRunning=args.leave_running, keepLogs=args.keep_logs)
35+
36+
try:
37+
if platform.system() != "Linux":
38+
Print("OC not run on Linux. Skip the test")
39+
exit(True) # Do not fail the test
40+
41+
TestHelper.printSystemInfo("BEGIN")
42+
43+
cluster.setWalletMgr(walletMgr)
44+
45+
# Enable OC so that the test can execute paths invloved OC reources
46+
specificExtraNodeosArgs={0: " --eos-vm-oc-enable all "}
47+
48+
Print("Stand up cluster")
49+
if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, activateIF=True, specificExtraNodeosArgs=specificExtraNodeosArgs) is False:
50+
errorExit("Failed to stand up cluster.")
51+
52+
cluster.waitOnClusterSync(blockAdvancing=5)
53+
54+
node=cluster.getNode(0)
55+
56+
def deployContract(account_name, contract_name):
57+
acct=Account(account_name)
58+
acct.ownerPublicKey="PUB_K1_6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5BoDq63"
59+
acct.activePublicKey="PUB_K1_6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5BoDq63"
60+
cluster.createAccountAndVerify(acct, cluster.eosioAccount)
61+
node.publishContract(acct, f"unittests/test-contracts/{contract_name}", f"{contract_name}.wasm", f"{contract_name}.abi", waitForTransBlock=True)
62+
63+
Print("Create accounts and publish contracts")
64+
deployContract("caller", "sync_caller");
65+
deployContract("callee", "sync_callee");
66+
deployContract("callee1", "sync_callee1");
67+
68+
Print("Create snapshot");
69+
ret=node.createSnapshot()
70+
assert ret is not None, "Snapshot creation failed"
71+
72+
Print("Stopping snapshot");
73+
node.kill(signal.SIGTERM)
74+
assert not node.verifyAlive(), "Node did not shutdown"
75+
76+
node.removeState()
77+
node.removeReversibleBlks()
78+
79+
Print("Restart from snapshot");
80+
isRelaunchSuccess=node.relaunch(chainArg=f"--snapshot {node.getLatestSnapshot()}")
81+
assert isRelaunchSuccess, "node relaunch from snapshot failed"
82+
83+
Print("Push a transaction containing 2-levels of a nested sync call");
84+
trx={"actions": [{"account": "caller", "name": "nestedcalls", "authorization": [{"actor": "caller","permission": "active"}], "data": {}}]}
85+
results=node.pushTransaction(trx)
86+
assert results[0], "pushTransaction failed"
87+
88+
testSuccessful=True
89+
finally:
90+
TestHelper.shutdown(cluster, walletMgr, testSuccessful=testSuccessful, dumpErrorDetails=dumpErrorDetails)
91+
92+
exitCode=0 if testSuccessful else 1
93+
exit(exitCode)

0 commit comments

Comments
 (0)