summary
when compute server is hit with 2 requests for the same grasshopper script, it seems to run both of them samualtaneously (instead of creating a que), which results in errors, inconsistent results, or request failure (sometimes a combination of these).
example test file
the following grasshopper script and python code was created to illiustrate this issue
grasshopper script
The grasshopper script is very simple, it takes two numbers, squres them, and in the first case it returns it immediately, and in the second case it returns after waiting 3 sec [note1].
python code
the following python script [note2] simply makes 2 API calls a compute API with inputs 2 and 3 for the grasshopper script discribed above
Once Each API call returns, it will print out the returned result
An async API call is used here to simulate two users sending requests at the same time
result
expected print (maybe 1/10 times it does return correctly)
making FIRST call... with inputs:
[{'ParamName': 'RH_IN:in_inst', 'InnerTree': {'0': [{'data': 2}]}}, {'ParamName': 'RH_IN:in_wait', 'InnerTree': {'0': [{'data': 2}]}}]
making SECOND call... with inputs:
[{'ParamName': 'RH_IN:in_inst', 'InnerTree': {'0': [{'data': 3}]}}, {'ParamName': 'RH_IN:in_wait', 'InnerTree': {'0': [{'data': 3}]}}]
output for FIRST call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '4.0'}]}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '4.0'}]}}]
output for SECOND call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '9.0'}]}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '9.0'}]}}]
actural printed result example 1
output for SECOND call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '4.0'}]}}]
output for FIRST call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '4.0'}]}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '4.0'}]}}]
actural printed result example 2
output for SECOND call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {}}]
output for FIRST call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '4.0'}]}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {}}]
actural printed result example 3
output for FIRST call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {}}]
output for SECOND call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {}}]
actural printed result example 4
output for SECOND call:
output for FIRST call:
[{'ParamName': 'RH_OUT:out_wait', 'InnerTree': {'{ 0; }': [{'type': 'System.Double', 'data': '4.0'}]}}, {'ParamName': 'RH_OUT:out_inst', 'InnerTree': {}}]
Traceback (most recent call last):
File "C:/Users/kpflaptop/cloud_based_tools/resthopper/calling compute at the same time/SimultaneousCalls.py", line 73, in <module>
loop.run_until_complete(future2)
File "C:\Users\kpflaptop\anaconda3\envs\compute_rhino_test\lib\asyncio\base_events.py", line 616, in run_until_complete
return future.result()
File "C:/Users/kpflaptop/cloud_based_tools/resthopper/calling compute at the same time/SimultaneousCalls.py", line 44, in print_async_evaluate_definition
print(output['values'])
KeyError: 'values'
error on server:
17:40:05 ERR] Error in grasshopper component: "Num" (ab3ad373-558d-4408-990d-db711d20c6e7): Cyclical data stream detected, parameter Num is recursive.
[17:40:05 ERR] Error in grasshopper component: "Num" (ab3ad373-558d-4408-990d-db711d20c6e7): Object reference not set to an instance of an object.
[17:40:05 ERR] Error in grasshopper component: "Wait 3 sec" (7af0522f-ddaa-4f07-b16d-f8975fcd5254): Object reference not set to an instance of an object.
notes
note 1: the code inside the "wait 3 sec" component is:
import time
time.sleep(3)
a = x
note2: python code that was run
import asyncio
import aiofiles
import aiohttp
import compute_rhino3d.Util
import compute_rhino3d.Curve
import json
from ghload import *
compute_rhino3d.Util.url = "..."
compute_rhino3d.Util.authToken = "..."
grasshopper_link = "https://rhino.kpfui.dev/api/definition/SimultaneousCalls.gh"
grasshopper_path = "SimultaneousCalls.gh"
async def async_evaluate_definition(definition, trees):
args = {'algo': None, 'pointer': definition, 'values': [tree.data for tree in trees]}
class __Rhino3dmEncoder(json.JSONEncoder):
def default(self, o):
if hasattr(o, "Encode"):
return o.Encode()
return json.JSONEncoder.default(self, o)
auth_token = compute_rhino3d.Util.authToken
url = "http://3.236.150.240:80/grasshopper"
postdata = json.dumps(args, cls=__Rhino3dmEncoder)
headers = {
'Authorization': 'Bearer ' + auth_token,
'User-Agent': 'compute.rhino3d.py/' + compute_rhino3d.Util.__version__,
'RhinoComputeKey': 'F$YWdl07jYi*GF&g9EEMhn0R&4*$6def^nE'
}
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, data=postdata) as resp:
data = await resp.json()
return data
async def print_async_evaluate_definition(ghf, trees, name):
print("making " + name + " call... with inputs: ")
print([tree.data for tree in trees])
output = await async_evaluate_definition(ghf, trees)
print("")
print("output for " + name + " call: ")
print(output['values'])
# grasshopper file input and output names
input_list = [
"in_inst",
"in_wait"
]
output_list = [
"out_inst",
"out_wait"
]
# set numerical inputs
inst1 = 2
wait1 = 2
inst2 = 3
wait2 = 3
# create list of input trees for two api calls
trees1 = [create_tree(input_list[0], inst1), create_tree(input_list[1], wait1)]
trees2 = [create_tree(input_list[0], inst2), create_tree(input_list[1], wait2)]
# api call
print("---------------------------test begin--------------------------")
loop = asyncio.get_event_loop()
future1 = asyncio.ensure_future(print_async_evaluate_definition(grasshopper_link, trees1, "FIRST"))
future2 = asyncio.ensure_future(print_async_evaluate_definition(grasshopper_link, trees2, "SECOND"))
loop.run_until_complete(future1)
loop.run_until_complete(future2)
# output2 = gh.EvaluateDefinition(grasshopper_link, trees2)['values']
# print("output for SECOND call!!!: ")
# print("inst out = " + output2[0]['InnerTree']['{ 0; }'][0]['data'])
# print("wait out = " + output2[1]['InnerTree']['{ 0; }'][0]['data'])
print("---------------------------test end--------------------------")