# \[BUG\] background thread stops when session is destroyed, leading to weird errors

**URL:** <https://discourse.holoviz.org/t/bug-background-thread-stops-when-session-is-destroyed-leading-to-weird-errors/2962>\
**Category:** Panel\
**Tags:** bokeh, panel, bug, holoviews\
**Created:** [October 19, 2021, 4:01pm UTC](https://discourse.holoviz.org/t/bug-background-thread-stops-when-session-is-destroyed-leading-to-weird-errors/2962 "2021-10-19T16:01:07Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Material-Scientist](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/material-scientist/32/503_2.png) [@Material-Scientist](https://discourse.holoviz.org/u/Material-Scientist)\
**Post date:** [October 19, 2021, 4:01pm UTC](https://discourse.holoviz.org/t/bug-background-thread-stops-when-session-is-destroyed-leading-to-weird-errors/2962/1 "2021-10-19T16:01:08Z")

</div>

I’m trying to set a background process that will continuously gather new data and put it in the panel cache. It then uses Dask futures to compute the results in a non-blocking fashion.

That works.

However, once the session is closed (browser tab closed or connection loss), I get strange errors like:  
`NameError: name 'copy' is not defined`  
or  
`NameError: name 'print' is not defined`.

Minimal reproducible example:

```auto
from bokeh.plotting import figure, curdoc
from dask.distributed import Client
import time, threading, copy, param
import panel as pn
import holoviews as hv

pn.extension()
hv.extension('bokeh')

def dask_fn(entry,counter):
    print('Doing some heavy work with dask')
    time.sleep(3)
    return entry
    
def dask_callback(future):
    if future.status == 'finished':
        entry = future.result()
        print('Future finished successfully')
        del pn.state.cache['futures'][entry]
        print(f'Deleted future for {entry} from cache')
        
    # If future fails, it will automatically retry
    else:
        print('future failed after 10 retries')   

def BG_fn():
    counter = 0
    while True:
        counter += 1
        time.sleep(0.5)
        tickers = copy.deepcopy(list(pn.state.cache['data'].keys()))
        for entry in tickers:
            futures = copy.deepcopy(list(pn.state.cache['futures'].keys()))
            if not entry in futures:
                future = pn.state.cache['dask_client'].submit(dask_fn,entry,counter,retries=10)
                pn.state.cache['futures'][entry] = future
                future.add_done_callback(dask_callback)

# Data cache
if not 'data' in pn.state.cache:
    # Init doc & client in cache
    pn.state.cache['doc'] = curdoc()
    pn.state.cache['dask_client'] = Client(processes=False)
    
    # Init caches
    pn.state.cache['data'] = {}
    pn.state.cache['futures'] = {}
    
    # Init sample data
    pn.state.cache['data']['test'] = 'lalala'
    
    # Start periodic callback as thread
    thread = threading.Thread(target=BG_fn,args={})
    thread.daemon = True
    thread.start()
    
    pn.state.cache['BG_process'] = thread

class App(param.Parameterized):   
    def __init__ (self, **params):
        super(). __init__ (**params)
        # Init important vars
        self.client = pn.state.cache['dask_client']
        self.doc = curdoc()
        self.plot = pn.pane.HoloViews(self.hv_plot)
    
    @property
    def hv_plot(self): 
        return hv.Curve([1,2,3])
        
app = App(name='')

app.plot.servable()

```

Console output:

> 2021-10-19 17:49:35,776 Starting Bokeh server version 2.4.1 (running on Tornado 6.1)  
> 2021-10-19 17:49:35,778 User authentication hooks NOT provided (default user enabled)  
> 2021-10-19 17:49:35,780 Bokeh app running at: [http://localhost:5008/bug](http://localhost:5008/bug)  
> 2021-10-19 17:49:35,780 Starting Bokeh server with process id: 1638246  
> Doing some heavy work with dask  
> Future finished successfully  
> Deleted future for test from cache  
> Doing some heavy work with dask  
> Future finished successfully  
> Deleted future for test from cache  
> Doing some heavy work with dask  
> Future finished successfully  
> Deleted future for test from cache  
> Doing some heavy work with dask  
> Future finished successfully  
> Deleted future for test from cache  
> Doing some heavy work with dask  
> Future finished successfully  
> Deleted future for test from cache  
> Doing some heavy work with dask  
> Future finished successfully  
> Deleted future for test from cache  
> Doing some heavy work with dask  
> Future finished successfully  
> Deleted future for test from cache  
> Doing some heavy work with dask  
> Future finished successfully  
> Deleted future for test from cache  
> Doing some heavy work with dask  
> distributed.client - ERROR - Error in callback \<function dask\_callback at 0x7f5b1a505a60\> of \<Future: finished, type: str, key: dask\_fn-91da05f4aae28bbb3bb1232c7fef7ff3\>:  
> Traceback (most recent call last):  
> File “/root/anaconda3/lib/python3.8/site-packages/distributed/client.py”, line 298, in execute\_callback  
> fn(fut)  
> File “/root/mnt3/02\_Data\_Serving/Development/bug.ipynb”, line 24, in dask\_callback  
> “def dask\_callback(future):\n”,  
> NameError: name ‘print’ is not defined  
> Exception in thread Thread-1:  
> Traceback (most recent call last):  
> File “/root/anaconda3/lib/python3.8/threading.py”, line 932, in \_bootstrap\_inner  
> self.run()  
> File “/root/anaconda3/lib/python3.8/threading.py”, line 870, in run  
> self.\_target(\*self.\_args, \*\*self.\_kwargs)  
> File “/root/mnt3/02\_Data\_Serving/Development/bug.ipynb”, line 38, in BG\_fn  
> " while True:\n",  
> NameError: name ‘copy’ is not defined

The threading is inspired by @Marc but using Dask futures, rather than async (more powerful, I think).

Any suggestions on how to resolve this?

Best,  
Fred

---

<div class="post-metadata">

**Author:** ![nghenzi](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/nghenzi/32/378_2.png) [@nghenzi](https://discourse.holoviz.org/u/nghenzi)\
**Post date:** [October 19, 2021, 4:47pm UTC](https://discourse.holoviz.org/t/bug-background-thread-stops-when-session-is-destroyed-leading-to-weird-errors/2962/2 "2021-10-19T16:47:53Z")

</div>

As I commented in another post, I see similar errors in my notebook with anaconda, but in the office with miniconda works ok, always in windows.

---

<div class="post-metadata">

**Author:** ![Material-Scientist](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/material-scientist/32/503_2.png) [@Material-Scientist](https://discourse.holoviz.org/u/Material-Scientist)\
**Post date:** [October 19, 2021, 5:21pm UTC](https://discourse.holoviz.org/t/bug-background-thread-stops-when-session-is-destroyed-leading-to-weird-errors/2962/3 "2021-10-19T17:21:27Z")

</div>

Hm, but this is on a freshly set up Ubuntu server

---

<div class="post-metadata">

**Author:** ![philippjfr](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/philippjfr/32/7_2.png) [@philippjfr](https://discourse.holoviz.org/u/philippjfr)\
**Post date:** [October 19, 2021, 6:40pm UTC](https://discourse.holoviz.org/t/bug-background-thread-stops-when-session-is-destroyed-leading-to-weird-errors/2962/4 "2021-10-19T18:40:37Z")

</div>

I’ve also seen this, the problem is likely that the dynamically created module for the session is cleaned up which probably clears out all globals even if the periodic function somehow persists. Will think about that in the context of [this PR](https://github.com/holoviz/panel/pull/2661) which is meant to allow global (i.e. non-session dependent) periodic callbacks.

---

<div class="post-metadata">

**Author:** ![Material-Scientist](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/material-scientist/32/503_2.png) [@Material-Scientist](https://discourse.holoviz.org/u/Material-Scientist)\
**Post date:** [October 19, 2021, 8:17pm UTC](https://discourse.holoviz.org/t/bug-background-thread-stops-when-session-is-destroyed-leading-to-weird-errors/2962/5 "2021-10-19T20:17:49Z")

</div>

Awesome, thank you for that!

Edit:  
Bryan over at the Bokeh discourse pointed me towards using lifecycle hooks.

 ![image](https://discourse.holoviz.org/uploads/default/original/2X/5/5aaf0626fa1136e5e046927b3c42b3c9802945ac.png)

So, I’ve moved all the initialization code and callback functions to the app\_hooks.py file, and caught the exception inside the parameterized app upon callbacks from the param object in the cache.

What used to happen when I’d refresh the page was that all sessions would freeze due to modules being unloaded during callbacks. Now, whenever I refresh the page to destroy a session, all other sessions continue to function normally.

[/uploads/default/original/2X/1/1af8a6f17a922f5f219f7a106c2120d808f5fbbf.mp4](https://discourse.holoviz.org/uploads/default/original/2X/1/1af8a6f17a922f5f219f7a106c2120d808f5fbbf.mp4)

---

<div class="post-metadata">

**Author:** ![nghenzi](https://discourse.holoviz.org/user_avatar/discourse.holoviz.org/nghenzi/32/378_2.png) [@nghenzi](https://discourse.holoviz.org/u/nghenzi)\
**Post date:** [October 21, 2021, 2:09pm UTC](https://discourse.holoviz.org/t/bug-background-thread-stops-when-session-is-destroyed-leading-to-weird-errors/2962/6 "2021-10-21T14:09:28Z")

</div>

related problem and solution can be found here

> <https://github.com/bokeh/bokeh/issues/11747>
>
> \### Event loop hooks for server\_lifecylce are gone in Bokeh v2.4.1
> 
> The event …loop functions in the \[lifecycle-hooks\](https://docs.bokeh.org/en/latest/docs/user\_guide/server.html#lifecycle-hooks) of the bokeh server are gone in v2.4.1. These following functions on the \`server\_context\` object no longer exist:
> \- add\_periodic\_callback
> \- add\_next\_tick\_callback
> 
> In short, this code no longer works (straight from the \[docs\](https://docs.bokeh.org/en/latest/docs/user\_guide/server.html#lifecycle-hooks)):
> \`\`\`python
> 
> def foo():
> pass
> 
> def bar():
> pass
> 
> def on\_server\_loaded(server\_context):
> server\_context.add\_next\_tick\_callback(foo) # this is broken
> 
> def on\_server\_unloaded(server\_context):
> server\_context.add\_next\_tick\_callback(bar) # this is broken
> 
> def on\_session\_created(session\_context):
> # If present, this function executes when the server creates a session.
> pass
> 
> def on\_session\_destroyed(session\_context):
> # If present, this function executes when the server closes a session.
> pass
> \`\`\`
> 
> This might just be a missing documentation in the \[migration guide\](https://docs.bokeh.org/en/latest/docs/releases.html#release-2-4-0-migration), but if you relied on this feature - it now no longer works, so might be nice to document how this can be fixed.
> 
> I see that the server\_context now has access to an event\_loop directly, so maybe that's the way to go?
