Managing pull_session and server_session from FastAPI

Hello Everyone!

I’ve followed the example from Embed Panel in fastApi and have a pretty neat panel app running as part of my broader FastAPI implementation, the one issue I am having is that it all seems to be one app instance running.

e.g. if I capture user inputs to update a parameter, that parameter change persists for a second user connecting to the same app, rather than a separate instance starting up for the new user.

I suspect this relates to the way @t-houssian set stuff up in his (fantastic!) example:

@app.get("/")
async def bkapp_page(request: Request):
    script = server_document('http://127.0.0.1:5000/app')
    return templates.TemplateResponse("base.html", {"request": request, "script": script})

pn.serve({'/app': createApp},
        port=5000, allow_websocket_origin=["127.0.0.1:8000"],
         address="127.0.0.1", show=False)

I read the flask example from here: Deploying Bokeh Apps — HoloViews v1.14.7

… and it seems like maybe I need to leverage pull_session from bokeh.client and server_session from bokeh.embed?

I tried something like this:

@app.get("/")
async def bokeh_app_page(request: Request):
    panel_url = f'http://{local_ip}:5000/app'
    with pull_session(panel_url) as session:
        script = server_session(session_id=session.id,url=panel_url)
    return templates.TemplateResponse("base.html", {"request": request,"script":script})

main_app = createApp()
panel_app = pn.serve({'/app': main_app.gspec},
        port=5000, address=f"{local_ip}",allow_websocket_origin=[f"{local_ip}:8000",], show=False)

but now when I try to run my app, I get the following error:

…\lib\asyncio\base_events.py", line 526, in _check_runnung
‘Cannot run the event loop while another loop is running’)
RuntimeError: Cannot run the event loop while another loop is running

Am I on the right track? Any advice from others who are running the panel server in parallel with another framework like fastapi?

I have played around with the same thing, and also trying to add a login to access the panel apps. Try to take a look at my repo and see if there is something you can use.

2 Likes

great work !! thanks for sharing !

2 Likes

@Hoxbro , great example, and it’s helped me set up some login stuff (which was also an issue I had on my to-do list). My original question however, was more related to the bokeh document sessions? (not sure if I am using the right term)

The issue I am running into is that if I have 2 users on the site at same time, they seem to be accessing the same underlying bokeh/panel document… if user 1 changes a pn.widgets.CheckButtonGroup value, for example, user 2’s browser reflects that selection too… they aren’t independent sessions!

I figured that panel/bokeh would instantiate a new class instance for each connection, but that doesn’t seem to be the case.

I updated parts of my code to reflect how used the server_session function in your repo example:

 script = server_session(
        session_id=generate_session_id(SECRET_KEY, signed=True), url=url
    )

…and updated my pn.serve section as well:

pn.serve(
    serving,
    port=5006,
    allow_websocket_origin=ALLOWED_HOSTS,
    address=f"{local_ip}",
    show=False,
    sign_sessions=True,
    secret_key=SECRET_KEY,
    generate_session_ids=False,
    num_process= 2,
)

…but I am still seeing the same behaviour (linked plots between 2 users).

How do I get two separate instances of the same app class? Do I need to launch a separate pn.serve() on a different port for each?

It sounds kind of similar to what @khannaum was describing in October:

Looks like maybe @t-houssian had a solution using param.ObjectSelector instead of pn.widgets that I haven’t quite wrapped my head around… I think I need to go back and read through the panel docs again; feel like I am missing something fundamental in the way it works… I thought each connection would create a new server document based on …
https://panel.holoviz.org/user_guide/Server_Deployment.html

Whenever a user accesses the app or dashboard in a browser a new session is created which executes the app code and creates a new Document containing the models served to the browser where they are rendered by BokehJS.

Without seeing other part of your code I think the problem happens because you have initialized a widget or a param.Parameterized class in the “open”. Try to move all of them inside functions and call them from there.

For example in the code example in your first post do this: pn.serve({'/app': lambda: createApp().gspec}, ...)

Yes! That was it, thank you very much for the help!

2 Likes