Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Using cache factory method #10887

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions UPDATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ assists people when migrating to a new version.

## Next

* [10887](https://github.com/apache/incubator-superset/pull/10887): Breaking change: The custom cache backend changed in order to support the Flask-Caching factory method approach and thus must be registered as a custom type. See [here](https://flask-caching.readthedocs.io/en/latest/#custom-cache-backends) for specifics.

* [10794](https://github.com/apache/incubator-superset/pull/10794): Breaking change: `uuid` python package is not supported on Jinja2 anymore, only uuid functions are exposed eg: `uuid1`, `uuid3`, `uuid4`, `uuid5`.

* [10674](https://github.com/apache/incubator-superset/pull/10674): Breaking change: PUBLIC_ROLE_LIKE_GAMMA was removed is favour of the new PUBLIC_ROLE_LIKE so it can be set it whatever role you want.
Expand Down
18 changes: 1 addition & 17 deletions docs/src/pages/docs/installation/caching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,7 @@ CACHE_CONFIG = {
}
```

It is also possible to pass a custom cache initialization function in the config to handle
additional caching use cases. The function must return an object that is compatible with the
[Flask-Cache API](https://pythonhosted.org/Flask-Cache/).

```python
from custom_caching import CustomCache

def init_cache(app):
"""Takes an app instance and returns a custom cache backend"""
config = {
'CACHE_DEFAULT_TIMEOUT': 60 * 60 * 24, # 1 day default (in secs)
'CACHE_KEY_PREFIX': 'superset_results',
}
return CustomCache(app, config)

CACHE_CONFIG = init_cache
```
Custom cache backends are also supported. See [here](https://flask-caching.readthedocs.io/en/latest/#custom-cache-backends) for specifics.

Superset has a Celery task that will periodically warm up the cache based on different strategies.
To use it, add the following to the `CELERYBEAT_SCHEDULE` section in `config.py`:
Expand Down
2 changes: 1 addition & 1 deletion superset/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
# then initialize it in app.create_app(). These fields will be removed
# in subsequent PRs as things are migrated towards the factory pattern
app: Flask = current_app
cache = LocalProxy(lambda: cache_manager.cache)
cache = cache_manager.cache
conf = LocalProxy(lambda: current_app.config)
get_feature_flags = feature_flag_manager.get_feature_flags
get_manifest_files = manifest_processor.get_manifest_files
Expand Down
28 changes: 6 additions & 22 deletions superset/utils/cache_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,19 @@
from flask import Flask
from flask_caching import Cache

from superset.typing import CacheConfig


class CacheManager:
def __init__(self) -> None:
super().__init__()

self._tables_cache = None
self._cache = None
Copy link
Member Author

@john-bodley john-bodley Sep 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's interesting that the

@property
def cache(self) -> Cache:
    return self._cache

method didn't barf from a MyPy perspective given that self._cache is actually Optional[Cache] per the definition on line 28.

self._thumbnail_cache = None
self._cache = Cache()
self._tables_cache = Cache()
self._thumbnail_cache = Cache()

def init_app(self, app: Flask) -> None:
self._cache = self._setup_cache(app, app.config["CACHE_CONFIG"])
self._tables_cache = self._setup_cache(
app, app.config["TABLE_NAMES_CACHE_CONFIG"]
)
self._thumbnail_cache = self._setup_cache(
app, app.config["THUMBNAIL_CACHE_CONFIG"]
)

@staticmethod
def _setup_cache(app: Flask, cache_config: CacheConfig) -> Cache:
"""Setup the flask-cache on a flask app"""
if isinstance(cache_config, dict):
return Cache(app, config=cache_config)

# Accepts a custom cache initialization function, returning an object compatible
# with Flask-Caching API.
return cache_config(app)
self._cache.init_app(app, app.config["CACHE_CONFIG"])
self._tables_cache.init_app(app, app.config["TABLE_NAMES_CACHE_CONFIG"])
self._thumbnail_cache.init_app(app, app.config["THUMBNAIL_CACHE_CONFIG"])

@property
def tables_cache(self) -> Cache:
Expand Down
23 changes: 8 additions & 15 deletions tests/superset_test_config_thumbnails.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
# type: ignore
from copy import copy

from cachelib.redis import RedisCache
from flask import Flask

from superset.config import *

AUTH_USER_REGISTRATION_ROLE = "alpha"
Expand Down Expand Up @@ -79,15 +76,11 @@ class CeleryConfig(object):
"THUMBNAILS_SQLA_LISTENERS": False,
}


def init_thumbnail_cache(app: Flask) -> RedisCache:
return RedisCache(
host=REDIS_HOST,
port=REDIS_PORT,
db=REDIS_CELERY_DB,
key_prefix="superset_thumbnails_",
default_timeout=10000,
)


THUMBNAIL_CACHE_CONFIG = init_thumbnail_cache
THUMBNAIL_CACHE_CONFIG = {
"CACHE_TYPE": "redis",
"CACHE_DEFAULT_TIMEOUT": 10000,
"CACHE_KEY_PREFIX": "superset_thumbnails_",
"CACHE_REDIS_HOST": REDIS_HOST,
"CACHE_REDIS_PORT": REDIS_PORT,
"CACHE_REDIS_DB": REDIS_CELERY_DB,
}
28 changes: 0 additions & 28 deletions tests/utils_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

import numpy
from flask import Flask, g
from flask_caching import Cache
import marshmallow
from sqlalchemy.exc import ArgumentError

Expand All @@ -37,7 +36,6 @@
from superset.models.core import Database, Log
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.utils.cache_manager import CacheManager
from superset.utils.core import (
base_json_conv,
cast_to_num,
Expand Down Expand Up @@ -834,32 +832,6 @@ def test_parse_js_uri_path_items_item_optional(self):
self.assertIsNone(parse_js_uri_path_item(None))
self.assertIsNotNone(parse_js_uri_path_item("item"))

def test_setup_cache_null_config(self):
app = Flask(__name__)
cache_config = {"CACHE_TYPE": "null"}
assert isinstance(CacheManager._setup_cache(app, cache_config), Cache)

def test_setup_cache_standard_config(self):
app = Flask(__name__)
cache_config = {
"CACHE_TYPE": "redis",
"CACHE_DEFAULT_TIMEOUT": 60,
"CACHE_KEY_PREFIX": "superset_results",
"CACHE_REDIS_URL": "redis://localhost:6379/0",
}
assert isinstance(CacheManager._setup_cache(app, cache_config), Cache) is True

def test_setup_cache_custom_function(self):
app = Flask(__name__)
CustomCache = type("CustomCache", (object,), {"__init__": lambda *args: None})

def init_cache(app):
return CustomCache(app, {})

assert (
isinstance(CacheManager._setup_cache(app, init_cache), CustomCache) is True
)

def test_get_stacktrace(self):
with app.app_context():
app.config["SHOW_STACKTRACE"] = True
Expand Down