Plot renders on development server but error loading layout when deployed

I’m plotting a lot of points (~800,000) and two subplots using plotly go.Scatter.
When I run my app on the development server, everything renders fine and the plot looks good. But when I deploy the app on gcloud app engine I’m getting “error loading layout”.

The plot is constantly updating as we get more data from a real-time instrument so everything looked ok until recently when the error popped up. I assumed it had to do with the addition of more points because when I shorten the record by 50,000 points or so and try deploying the app again, it loads fine.

I tried using go.Scattergl instead and it also doesn’t work. Why would it load fine on the development server but not when I actually deploy the app?

My code looks like this:

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets, assets_folder='assets')
server = app.server

app.config.suppress_callback_exceptions = True

colors = {
    'background': '#FFFFFF',
    'text': '#000000'
}

fig = make_subplots(specs=[[{"secondary_y": True}]])

fig.add_trace(
    go.Scatter({'x': tnode, 'y': xnode, 'type': 'line', 'line':dict(color='rgb(0,0,0)'), 'name': 'x-tilt'}), secondary_y=False
)

fig.add_trace(
    go.Scatter({'x': tnode, 'y': tempnode, 'type': 'line', 'line':dict(color='rgb(179, 17, 0 )'), 'name': 'temperature','yaxis': 'y2'}), secondary_y=True
)

fig.update_xaxes(
    range=[tnode[len(tnode)-1]-timedelta(days=7),tnode[len(tnode)-1]]
)

# Set y-axes titles
fig.update_yaxes(
    title_text="Temperature (°C)",
    color='rgb(179, 17, 0 )',
    secondary_y=True,
    fixedrange=True,
    side='right',
    range=[5,40]
)

fig.update_yaxes(
    title_text="X-axis Tilt (degrees)",
    secondary_y=False,
    fixedrange=False,
    side='left',
    range=rangex
)

fig.update_layout(
    font_size=20
)

fig.update_layout(legend=dict(
    orientation="h",
    yanchor="bottom",
    y=1.02,
    xanchor="right",
    x=0.8
))

# Add range slider
fig.update_layout(
    xaxis=dict(
        rangeselector=dict(font_size=12,
            buttons=list([
                dict(count=7,
                     label="1w",
                     step="day",
                     stepmode="backward"),
                dict(count=1,
                     label="1m",
                     step="month",
                     stepmode="backward"),
                dict(count=6,
                     label="6m",
                     step="month",
                     stepmode="backward"),
                dict(count=1,
                     label="1y",
                     step="year",
                     stepmode="backward"),
                dict(step="all")
            ])
        ),
        rangeslider=dict(
            visible=True
        ),
        type="date"
    )
)

fig.update_layout(hovermode="x unified",hoverlabel=dict(font_size=16),height=600,autosize=True)
config = {'responsive': True}
fig.layout._config= config

# -------------------------- PROJECT DASHBOARD ---------------------------- #

app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[
    dcc.Graph(
        id='beacons-0920',
        figure=fig
    )
])

@app.callback(
    Output('beacons-0920', 'figure'),
    Input('beacons-0920', 'relayoutData'),
    State('beacons-0920', 'figure'),
    prevent_initial_call=True
)
def update_graph(relOut, fig):

    # calculate separately for xmin and xmax
    if 'T' in fig['layout']['xaxis']['range'][0]:
        xmin = dt.datetime.strptime(fig['layout']['xaxis']['range'][0],'%Y-%m-%dT%H:%M:%S')
    elif '.' in fig['layout']['xaxis']['range'][0]:
        xmin = dt.datetime.strptime(fig['layout']['xaxis']['range'][0],'%Y-%m-%d %H:%M:%S.%f')
    else:
        xmin = dt.datetime.strptime(fig['layout']['xaxis']['range'][0],'%Y-%m-%d %H:%M:%S')

    if 'T' in fig['layout']['xaxis']['range'][1]:
        xmax = dt.datetime.strptime(fig['layout']['xaxis']['range'][1],'%Y-%m-%dT%H:%M:%S')
    elif '.' in fig['layout']['xaxis']['range'][1]:
        xmax = dt.datetime.strptime(fig['layout']['xaxis']['range'][1],'%Y-%m-%d %H:%M:%S.%f')
    else:
        xmax = dt.datetime.strptime(fig['layout']['xaxis']['range'][1],'%Y-%m-%d %H:%M:%S')

    # calculate range for y-axes
    # find timestamps in tnode 
    irange = np.where(np.logical_and(tnode>=xmin, tnode<=xmax))[0]

    ystd = np.nanstd(xnode[irange])
    yrange = [np.nanmin(xnode[irange])-ystd,np.nanmax(xnode[irange])+ystd]

    fig['layout']['yaxis']['range'][0] = yrange[0]
    fig['layout']['yaxis']['range'][1] = yrange[1]

    return fig
'''

Hi @seapig1903, welcome to the forums. How long does it take to render this on the development server?

Maybe you are running in a timeout

Maybe you could format your code so that it is better readable:

Sorry, fixed the code. It does take longer than 30 seconds to render so I’ll look into the timeout issue. Thanks!

2 Likes