kmcgrady / streamlit-autorefresh

An autorefresh component for Streamlit apps
MIT License
176 stars 16 forks source link

st_autorefresh getting delayed everytime there is a user_interaction #6

Closed Sghost35T closed 1 year ago

Sghost35T commented 1 year ago

I am developing a streamlit application that displays data that changes for every n minutes i used st_autorefresh to rerun my code after that n minute interval , but if there is any user_interaction between that n minutes (suppose take the user-interaction was at n-2 minutes) the code is being runned at n minutes after the user interaction and not at n before the user interaction , can anyone please provide a solution for this,

appreciate any replies Thank you

kmcgrady commented 1 year ago

Hey @Sghost35T, can you share the snippet you used it here. From how it's supposed to work. Changes in the parameters lead to a reset of the timer. Are you hard coding values here, or are they changing?

Sghost35T commented 1 year ago

hi @kmcgrady thanks for the early response iam calling the function "st_autorefresh(interval=time_threshold601000,key="auto_refresh")" where time_threshold is read from a file , the value in the file is fixed , so i have two functions that needs to run at every refresh iam using cache_data(ttl=threshold*60) to achieve that and i have a filter function dataframe_Explorer to filter out the data, so whenever iam trying ti filter the data the auto_refresh is getting delayed

function to read the config_file

@st.cache_data() def read_config(file_path): with open(file_path, "r") as f: content = f.read().split() time_threshold = int(content[0].replace('time_threshold=', '').strip()) email_ids = str(content[1].replace('email_ids=', '').strip()) email_ccs = str(content[2].replace('email_ccs=', '').strip()) time1 = datetime.now() current_time = time1.strftime("%H:%M:%S") return time_threshold,email_ids,email_ccs time_threshold,email_ids,email_ccs=read_config("config.txt")

Quajijalon commented 1 year ago

I can confirm this issue. Setting a key doesn't preserve the widget between page reloads. It remounts every time and the counter begins again from zero. In the following code, if you keep pressing the button, the message will never be written

import streamlit as st from streamlit_autorefresh import st_autorefresh

count = st_autorefresh(interval=5000, limit=100, key="fizzbuzzcounter")

if count == 1: st.write('asdsajhkkjh')

st.button('RELOAD')

Sghost35T commented 1 year ago

Any update on this @kmcgrady 😊

kmcgrady commented 1 year ago

Hey @Sghost35T and @Quajijalon . I made an update that has an additional field debounce. I think it's worthwhile to keep that delaying behavior to avoid refreshes causing unintended effects when the user is interacting, but you can turn this behavior off with debounce=False, and it should give you the intended effect. Give it a try and let me know if anything else is wrong.

kmcgrady commented 1 year ago

Should add that I also added a fix that resets the interval when the interval, limit, or key changes.

Sghost35T commented 1 year ago

Just tested the latest update and can confirm that the issue is resolved

Thank you very much @kmcgrady

Sghost35T commented 1 year ago

hi @kmcgrady, having a small doubt about the debounce feature, what if debounce=False and the value of limit changes will that affect the refresh time ?

kmcgrady commented 1 year ago

@Sghost35T It will effectively reset the timer. This is because the function on autorefresh checks the limit, and so there's a need to reset that function when the limit changes.

What's the ideal outcome? that it refreshes on the same interval even if the limit changes?

Sghost35T commented 1 year ago

@kmcgrady so i tried to change the limit when debounce was "False" but the timer was not getting reset , the refresh was not getting delayed , i wanted the refresh get delayed(i.e: the timer to reset after the limit changes)

Sghost35T commented 1 year ago

hi @kmcgrady , any updates on this ?

kmcgrady commented 1 year ago

Hey @Sghost35T. So I tried playing with the limit, and I don't see any issues with resetting the timer. It looks like it works as expected. Are you saying you wanted to reset the count? I think that's fair, but we start entering an undefined behavior. Should the count reset when we reset the time, limit, etc? I think if it were me. I would reset the count if the key changed.

Sghost35T commented 1 year ago

Hi @kmcgrady, so i tried to reproduce the issue i was telling , in the below code the app is set to auto refresh every 2 mins , but there is also a button for manual refresh where we clear the cache of the function to be run and change the limit by adding 1 ,so whenever there is a manual refresh we have to reset the interval so that the manual refresh doesnt effect the ttl of the function and run when the ttl is over, suppose i have started the code at 11:25 , at 11:27 it should autorefresh but if i manual refresh at 11:26 app should auto-refresh at 11:28 at the next iteration but the app is not running at 11:28 rather its running at 11:29(i.e 11:25->11:27->11:29),and the cache is getting cleared at 11:28 and any user_interaction between 11:28 to 11:29 is causing the app to re-run i hope this will help u in understanding the issue Thank you.

`import streamlit as st import time from datetime import datetime from streamlit_autorefresh import st_autorefresh time_threshold=2

if "limit" not in st.session_state: st.session_state.limit =10

@st.cache_data(ttl=time_threshold*60) def hello(y): time1 = datetime.now() time_formatted = time1.strftime("%d/%m/%y %H:%M:%S") st.text(y) st.text(time_formatted)

if st.button("Refresh",key="Manual refresh"): st.session_state.limit+=1 hello.clear() st.experimental_rerun()

hello(st.session_state.limit) st_autorefresh(interval=time_threshold601000,key="Refresh",limit=st.session_state.limit,debounce=False)`

Sghost35T commented 1 year ago

hi @kmcgrady , any update

kmcgrady commented 1 year ago

Hey @Sghost35T Any chance you can remove the experimental_rerun part of the problem. Like can you do this:

def refresh():
    st.session_state.limit+=1
    hello.clear()
st.button("Refresh",key="Manual refresh", on_click=refresh):

I worry the issue might relate to the experimental_rerun with https://github.com/kmcgrady/streamlit-autorefresh/issues/7

Sghost35T commented 1 year ago

Hey @kmcgrady, i tried by removing the st.experimental_rerun() but still its the same issue , i think the problem is even though there is a change in value of limit parameter , the interval is not getting reset . can u check on this Thank you

kmcgrady commented 1 year ago

Ah. Looking into the code more thoroughly. I'm not seeing how the code performs this issue. Interaction is changing the limit which is causing the correct delay in manual testing. Based on how I've played with it, there's a couple issues that could be:

  1. The times are fine, but not quite on the minute. The component relies on milliseconds, so if it's off, it can certainly show the wrong time if we look at the minute granularlity.
  2. It seems tied to a race condition of While there is a timer functionality is not lining up with your caching. It's not necessarily a guarantee on specifics and it can be synced though it works reasonably in practice.

It seems like you should be increasing the interval time to check more repeatedly (Every 15-30 seconds?). The only difference will be the limit setting, which you can increase the size of the limits.

The code is not that large, so you're welcome to take a look if the logic needs tweaking.