Listening for updates from an API server

Summary

I have Streamlit UI with a FastAPI backend.

Streamlit is used to add long-running jobs for the backend and keep track of their progress live.

The backend implements a background task that picks new tasks from a queue, processes them, and reports progress back via Server-Sent Events (SSE) by pushing โ€œupdateโ€ messages every time there is progress. The update messages is supposed to trigger the Streamlit app to re-run and query the backend for the job status:


# <Task-making UI that submits the new jobs to the backend>

@st.cache_data(ttl=30, show_spinner=True)
def get_job_status() -> List[RecJob]:
    return # <fetches a list of jobs with their status from the server>

# <Progress displaying UI that displays the jobs in different state of progress>

# Wait for an "update" message.
# Clear the cache for get_job_status
# Trigger a re-run

from sseclient import SSEClient

print("Listening for updates...")
if "messages" in st.session_state:
    print("Closing old connection")
    st.session_state["messages"].resp.close()

st.session_state["messages"] = SSEClient(env.api_base + "/heartbeat", headers={"Authorization": f"Bearer {env.api_token}"})

for msg in st.session_state["messages"]:
    if not st.session_state:
        print("The client disconnected")
        break

    if msg.event == "update":
        print(msg.dump())
        print("Reloading page")
        get_job_status.clear()
        # jobs_records = get_job_status()
        st.experimental_rerun()

This almost works as it should, the reload on the update events is consistent and smooth.
The problem is, any other action results in a re-render and Iโ€™m getting some random ghosted elements after it:

Am I missing some small details, or am I just trying to use Streamlit in a way that is fundamentally wrong? I understand that the way I do it, the script does not reach the end before the next re-run starts, and I guess thatโ€™s the reason Streamlit behaves weirdly.

Is there a better way to implement what Iโ€™m trying to achieve here?

Debug info

  • Streamlit version: 1.25.0
  • OS version: Ubuntu 22.04
  • Browser version: Fresh Google Chrome

Hi @xl0

Have you tried adjusting the ttl parameter in @st.cache_data() to see if this would solve the redundant display of the expander widget owing to the cached data.

Also, could you try profiling the app using streamlit-profiler

@dataprofessor , ttl is not it, as I need the page to reload (rerun) when the update events arrive.

I will look at the profiler, thank you for the tip.

@dataprofessor , misunderstood you.

I donโ€™t see how ttl could be of use. My problem is not with having redundant or inconsistent data - the data is fetched in one piece, itโ€™s an array, and the function takes no arguments. So I either get the new data, or the old, but never something in between.