Cache
Installation
pip install Flask-Caching
        
FileSystemCache
import time
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from os import path
from flask_caching import Cache

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

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
server = app.server

cache = Cache(app.server, config={
    'CACHE_TYPE': 'FileSystemCache',
    'CACHE_DIR': 'cache',
    'CACHE_THRESHOLD': 200, # The maximum number of items the cache will store
    'CACHE_DEFAULT_TIMEOUT': 60 # The default timeout, unit of time is seconds
})

cache.clear() # clean up cache files

app.layout = html.Div([
    dcc.Input(id = 'input'),
    html.Hr(),
    dcc.Loading(html.H1(id = 'content')),
    html.Button('Click', id = 'button'),
], className='container')

# for the same parameter value, first call save results to cache
# second and later calls will reuse the results
# changing the parameter value, a new cache will be created
@cache.memoize()
def inside(value):
    time.sleep(5)
    return str(2*value)

@app.callback(
    Output('content', 'children'),
    Input('button', 'n_clicks'),
    State('input', 'value')
)
def update_output_1(n_clicks, value):
    if not n_clicks:
        raise PreventUpdate
    return inside(value) # pause 5 seconds

if __name__ == '__main__':
    app.run_server(debug=True)
        
NullCache
  • Not cache
  • import time
    import dash
    from dash import dcc
    from dash import html
    from dash.dependencies import Input, Output, State
    from dash.exceptions import PreventUpdate
    from flask_caching import Cache
    
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
    
    app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
    server = app.server
    
    cache = Cache(app.server, config={
        'CACHE_TYPE': 'NullCache',
    })
    
    app.layout = html.Div([
        dcc.Input(id = 'input'),
        html.Hr(),
        dcc.Loading(html.H1(id = 'content')),
        html.Button('Click', id = 'button'),
    ], className='container')
    
    @cache.memoize()
    def inside(value):
        time.sleep(5)
        return str(2*value)
    
    @app.callback(
        Output('content', 'children'),
        Input('button', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_1(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
        return inside(value) # pause 5 seconds
    
    if __name__ == '__main__':
        app.run_server(debug=True)
            
    SimpleCache
  • Uses a python dictionary for caching, is not really thread safe
  • import time
    import dash
    from dash import dcc
    from dash import html
    from dash.dependencies import Input, Output, State
    from dash.exceptions import PreventUpdate
    from flask_caching import Cache
    
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
    
    app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
    server = app.server
    
    cache = Cache(app.server, config={
        'CACHE_TYPE': 'SimpleCache',
        'CACHE_THRESHOLD': 200, # The maximum number of items the cache will store
        'CACHE_DEFAULT_TIMEOUT': 30 # The default timeout, unit of time is seconds
    })
    
    app.layout = html.Div([
        dcc.Input(id = 'input'),
        html.Hr(),
        dcc.Loading(html.H1(id = 'content')),
        html.Button('Click', id = 'button'),
    ], className='container')
    
    @cache.memoize()
    def inside(value):
        time.sleep(5)
        return str(2*value)
    
    @app.callback(
        Output('content', 'children'),
        Input('button', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_1(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
        return inside(value) # pause 5 seconds
    
    if __name__ == '__main__':
        app.run_server(debug=True)
            
    RedisCache
    import time
    import dash
    from dash import dcc
    from dash import html
    from dash.dependencies import Input, Output, State
    from dash.exceptions import PreventUpdate
    from flask_caching import Cache
    
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
    
    app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
    server = app.server
    
    cache = Cache(app.server, config={
        'CACHE_TYPE': 'RedisCache',
        'CACHE_DEFAULT_TIMEOUT': 60, # The default timeout, unit of time is seconds
        'CACHE_REDIS_HOST': '127.0.0.1',
        'CACHE_REDIS_PORT': 6379
    })
    
    app.layout = html.Div([
        dcc.Input(id = 'input'),
        html.Hr(),
        dcc.Loading(html.H1(id = 'content')),
        html.Button('Click', id = 'button'),
    ], className='container')
    
    # for the same parameter value, first call save results to cache
    # second and later calls will reuse the results
    # changing the parameter value, a new cache will be created
    @cache.memoize()
    def inside(value):
        time.sleep(5)
        return str(2*value)
    
    @app.callback(
        Output('content', 'children'),
        Input('button', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_1(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
        return inside(value) # pause 5 seconds
    
    if __name__ == '__main__':
        app.run_server(debug=True)
            
    Cashed Function Calls Cashed Function
    import time
    import dash
    from dash import dcc
    from dash import html
    from dash.dependencies import Input, Output, State
    from dash.exceptions import PreventUpdate
    from os import path
    from flask_caching import Cache
    
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
    
    app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
    server = app.server
    
    cache = Cache(app.server, config={
        'CACHE_TYPE': 'RedisCache',
        'CACHE_DEFAULT_TIMEOUT': 60, # The default timeout, unit of time is seconds
        'CACHE_REDIS_HOST': '127.0.0.1',
        'CACHE_REDIS_PORT': 6379
    })
    
    app.layout = html.Div([
        dcc.Input(id = 'input'),
        html.Hr(),
        dcc.Loading(html.H1(id = 'content')),
        html.Button('Click', id = 'button'),
        html.Hr(),
        dcc.Loading(html.H1(id = 'content_2')),
        html.Button('Click 2', id = 'button2')
    ], className='container')
    
    @cache.memoize()
    def compute_expensive_data(value):
        time.sleep(5)
        return str(inside(value))
    
    @cache.memoize()
    def inside(value):
        time.sleep(5)
        return str(2*value)
    
    @app.callback(
        Output('content', 'children'),
        Input('button', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_1(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
        return inside(value) # pause 5 seconds
    
    @app.callback(
        Output('content_2', 'children'),
        Input('button2', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_2(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
        return compute_expensive_data(value) # pause 10 seconds
    
    if __name__ == '__main__':
        app.run_server(debug=True)
            
    Store Cache Data for Each Session on Server Side
  • Each tab creates a new session
  • import time
    import uuid
    import dash
    from dash import dcc
    from dash import html
    from dash.dependencies import Input, Output, State
    from dash.exceptions import PreventUpdate
    from os import path
    from flask_caching import Cache
    
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
    
    app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
    server = app.server
    
    cache = Cache(app.server, config={
        'CACHE_TYPE': 'RedisCache',
        'CACHE_DEFAULT_TIMEOUT': 60, # The default timeout, unit of time is seconds
        'CACHE_REDIS_HOST': '127.0.0.1',
        'CACHE_REDIS_PORT': 6379
    })
    
    def get_layout():
        uid = str(uuid.uuid4())
    
        return html.Div([
            dcc.Loading(html.H1(id = 'content')),
            html.Button('Click', id = 'button'),
            dcc.Store(data = uid, id='session-id')
        ], className='container')
    
    # a new session is created each time the page is loaded
    app.layout = get_layout # not get_layout()
    
    @cache.memoize()
    def compute_expensive_data(session_id):
        time.sleep(5)
        f = open('readme.txt', "r")
        output_str = f.readline()
        f.close()
        return output_str + ' ' + str(session_id)
    
    @app.callback(
        Output('content', 'children'),
        Input('button', 'n_clicks'),
        State('session-id', 'data')
    )
    def update_output_1(n_clicks, data):
        if not n_clicks:
            raise PreventUpdate
        print(data) # display session id
        return compute_expensive_data(data)
    
    if __name__ == '__main__':
        app.run_server(debug=True)
            
    API
    cached
    memoize
    Example

  • Button_1, get value by key, if key does not exist, create the key/value pair
  • Button_2, update value, if key does not exist, create the key/value pair
  • # redis-py
    import time
    import dash
    from dash import dcc
    from dash import html
    from dash.dependencies import Input, Output, State
    from dash.exceptions import PreventUpdate
    import redis
    import pickle
    
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
    
    app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
    server = app.server
    
    re = redis.Redis(host='localhost', port=6379, db=0)
    
    app.layout = html.Div([
        dcc.Input(id = 'input'),
        html.Hr(),
        dcc.Loading(html.H1(id = 'content')),
        html.Button('Button_1', id = 'button'),
        dcc.Loading(html.H1(id = 'content2')),
        html.Button('Button_2', id = 'button2'),
    ], className='container')
    
    @app.callback(
        Output('content', 'children'),
        Input('button', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_1(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
    
        # get
        if re.exists('container'):
            return str(pickle.loads(re.get('container')))
    
        # initialization
        l = [False, value, {'k1':'v1', 'k2':'v2'}]
        re.set('container', pickle.dumps(l), ex=50)
    
        return str(l)
    
    @app.callback(
        Output('content2', 'children'),
        Input('button2', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_2(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
    
        # initialization
        if not re.exists('container'):
            temp = [False, value, {'k1':'v1', 'k2':'v2'}]
            re.set('container', pickle.dumps(temp), ex=50)
            return str(temp)
    
        # update
        temp = pickle.loads(re.get('container'))
        temp[1] = value
        re.set('container', pickle.dumps(temp), ex=50)
    
        return str(temp)
    
    if __name__ == '__main__':
        app.run_server(debug=True)
            
    # flask-caching
    import time
    import dash
    from dash import dcc
    from dash import html
    from dash.dependencies import Input, Output, State
    from dash.exceptions import PreventUpdate
    from flask_caching import Cache
    from flask import request
    
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
    
    app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
    server = app.server
    
    cache = Cache(app.server, config={
        'CACHE_TYPE': 'RedisCache',
        'CACHE_DEFAULT_TIMEOUT': 60, # The default timeout, unit of time is seconds
        'CACHE_REDIS_HOST': '127.0.0.1',
        'CACHE_REDIS_PORT': 6379
    })
    
    app.layout = html.Div([
        dcc.Input(id = 'input'),
        html.Hr(),
        dcc.Loading(html.H1(id = 'content')),
        html.Button('Button_1', id = 'button'),
        dcc.Loading(html.H1(id = 'content2')),
        html.Button('Button_2', id = 'button2'),
    ], className='container')
    
    def force_function(*args, **kwargs):
        print('args', args) # parameter values
        print('kwargs', kwargs) # parameters
        return args[0]
    
    # generate key by ignore parameter update
    # the update value is used to decide whether force update or not
    @cache.memoize(timeout=50, args_to_ignore = ['update'], forced_update = force_function)
    def index(update, data, l):
        time.sleep(5)
        return str(update)+' '+data+'Cached for 50s'
    
    @app.callback(
        Output('content', 'children'),
        Input('button', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_1(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
        return index(False, value, {'k1':'v1', 'k2':'v2'}) # get value, if key does not exist, create the key/value pair
    
    @app.callback(
        Output('content2', 'children'),
        Input('button2', 'n_clicks'),
        State('input', 'value')
    )
    def update_output_2(n_clicks, value):
        if not n_clicks:
            raise PreventUpdate
        return index(True, value, {'k1':'v1', 'k2':'v2'}) # update value
    
    if __name__ == '__main__':
        app.run_server(debug=True)
            
    Reference
  • API
  • Github
  • Flask-Caching