0

I have deployed a Dash-Plotly Python application to Heroku using Heroku's "heroku git CLI" option. I followed Heroku's instructions and the deployment itself was uneventful. In fact, the app works like its supposed to 75% of the time. (Also note that the app works perfectly when run locally.)

The app itself is designed to search a product database and display performance characteristics about a selected product. The user inputs search criteria into a webform and initiates the search with a "search" button press. All product data is stored local to the app in a "data" folder (excel files).

The issue I'm having is that the app "appears" un-responsive on Heroku, requiring multiple clicks on buttons or checkboxes to elicit the desired action.

When I dug into the logs, I see the following output after a button press:

2021-10-15T19:25:17.768450+00:00 app[web.1]: Exception on /_dash-update-component [POST]
2021-10-15T19:25:17.768474+00:00 app[web.1]: Traceback (most recent call last):
2021-10-15T19:25:17.768475+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 2070, in wsgi_app
2021-10-15T19:25:17.768475+00:00 app[web.1]: response = self.full_dispatch_request()
2021-10-15T19:25:17.768476+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 1515, in full_dispatch_request
2021-10-15T19:25:17.768476+00:00 app[web.1]: rv = self.handle_user_exception(e)
2021-10-15T19:25:17.768476+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 1513, in full_dispatch_request
2021-10-15T19:25:17.768477+00:00 app[web.1]: rv = self.dispatch_request()
2021-10-15T19:25:17.768477+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 1499, in dispatch_request
2021-10-15T19:25:17.768477+00:00 app[web.1]: return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
2021-10-15T19:25:17.768478+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash/dash.py", line 1096, in dispatch
2021-10-15T19:25:17.768478+00:00 app[web.1]: response.set_data(func(*args, outputs_list=outputs_list))
2021-10-15T19:25:17.768478+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.9/site-packages/dash/dash.py", line 1017, in add_context
2021-10-15T19:25:17.768479+00:00 app[web.1]: output_value = func(*args, **kwargs)  # %% callback invoked %%
2021-10-15T19:25:17.768479+00:00 app[web.1]: File "/app/productsearch.py", line 1329, in table_interact
2021-10-15T19:25:17.768480+00:00 app[web.1]: products = search_products(classname, inputs)
2021-10-15T19:25:17.768480+00:00 app[web.1]: File "/app/productsearch.py", line 943, in search_products
2021-10-15T19:25:17.768480+00:00 app[web.1]: elif (low == None or (product['freq-low'] <= low and high <= product['freq-high'])):
2021-10-15T19:25:17.768481+00:00 app[web.1]: KeyError: 'freq-low'

'freq-low' is a value input by the user before a "search" button press. I know the value is there because its manually entered into the webpage before pressing search.

Note that the logs above are for an example of the 'freq-low' variable causing a KeyError but the error will occur with other variables too. Its not simply limited to the code around the 'freq-low' variable.

Edited below to show the function in the code where the exception occurs:

def search_products(class_name, inputs):
    '''
    This function is used to create a list of products that meet the search
    criteria (product type "class_name" and user "inputs").
    
    Parameters:
        inputs: list of Float64 values
            input list of Float64 values from user inputs
        class_name: string 
            input string referencing the product class type (i.e "B" for Balun)
    Returns:
        lists of dicts
            list of dicts with product data from the productspec.xls sheet           
    '''
    if class_name == M:
        low_rf, high_rf, low_lo, high_lo, low_if, high_if, low_lodr, high_lodr = inputs
    else:
        low, high = inputs

    data = []

    for product in class_name.products.values():
        if class_name == M:
            if (
                (low_rf == None or (product['rf-low'] <= low_rf and high_rf <= product['rf-high']))
                and (low_lo == None or (product['lo-low'] <= low_lo and high_lo <= product['lo-high']))
                and (low_if == None or (product['if-low'] <= low_if and high_if <= product['if-high']))
                and (low_lodr == None or (product['lodr-low'] <= low_lodr and high_lodr <= product['lodr-high']))
            ):
                data.append(product)
        elif (low == None or (product['freq-low'] <= low and high <= product['freq-high'])):
            data.append(product)

    return data

The object "class_name" is created by dash during the generation of the html input form. During creation of the object, the class DOES pull data in from a local Excel (.xlsx) file. One of the headers in this file is "freq-low" which is converted to a key-value pair for each row of the Excel input. The rows of the excel sheet represent different products whose properties are read into the class as dictionairies. So, "class_name.products.values()" is a list of dictionaries. I am relying on the correct and timely read of the local excel data to be complete before the "search_products" function call. The amount of data being read-in is quite small at ~12KB.

The other important thing to note is that this behavior is not deterministic. Its not repeatable in the sense that it always fails on the first or second try etc. Sometimes it works right away and sometimes it takes 3-4 clicks. But so far, it ALWAYS works given enough clicks.

I would mention troubleshooting but so far I don't know what to try since I'm not getting indication of any single variable or function failure.

UPDATE: I had several global variables that were persistent and being used to limit calls to the data source. After removing all of these and verifying the code still performs locally... the problem of intermittent keyErrors persists on Heroku. : (

5eb
  • 14,798
  • 5
  • 21
  • 65
bbaxter256
  • 41
  • 4
  • Please show the code where the error occurs. Are you relying on anything being in memory? Are you using global variables? – ChrisGPT was on strike Oct 15 '21 at 21:38
  • Hi @Chris. Thanks for taking a look. I updated the post with code showing the location where the error occurs. I think you could be onto something with the memory question. I do rely on a read of data from an excel file to be complete before the function call runs but I don't have anything in place to validate that read has been successful before the call is made. – bbaxter256 Oct 18 '21 at 18:26

1 Answers1

0

Have you tried messing around with some variation of the following

from dash.exceptions import PreventUpdate

...

 def search_products(class_name, inputs):
     if not class_name and not inputs:
        raise PreventUpdate

Then the rest of your code?

Raindata
  • 31
  • 2
  • 5