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

Fix/optimization #97

Closed
wants to merge 2 commits into from
Closed
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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ flask --debug run

flask db stamp head # to start the upgrade db models
```
### Redis
It needs to run with redis;
Environment variables can be configured using the "SDB" prefix.
```
"SDB_CACHE_TYPE"
"SDB_CACHE_KEY_PREFIX"
"SDB_CACHE_REDIS_HOST"
"SDB_CACHE_REDIS_PORT"
"SDB_CACHE_REDIS_URL"
"SDB_CACHE_REDIS_PASSWORD"
"SDB_CACHE_DEFAULT_TIMEOUT"
```

## Bootstraping

Expand Down
44 changes: 41 additions & 3 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,38 @@

from flask_migrate import Migrate

from flask_session import Session

from flask_smorest import Api

from flask_sqlalchemy import SQLAlchemy

import redis

import yaml


db = SQLAlchemy()
migrate = Migrate()
oauth = OAuth()
cache = Cache(config={"CACHE_TYPE": "SimpleCache"})

# Cache settings
cache_config_options = [
"CACHE_TYPE",
"CACHE_KEY_PREFIX",
"CACHE_REDIS_HOST",
"CACHE_REDIS_PORT",
"CACHE_REDIS_URL",
"CACHE_REDIS_PASSWORD",
"CACHE_DEFAULT_TIMEOUT",
]

cache_config = {
option: os.getenv(f"SDB_{option}", getattr(DefaultConfiguration, option))
for option in cache_config_options
}
cache = Cache(config=cache_config)
session = Session()


def create_app(test_config=None):
Expand All @@ -44,6 +65,11 @@ def create_app(test_config=None):
api = Api(app) # noqa

app.config.from_prefixed_env(prefix="SDB")
app.config['SESSION_REDIS'] = redis.StrictRedis(
host=os.getenv("SDB_REDIS_HOST", DefaultConfiguration.REDIS_HOST),
port=os.getenv("SDB_REDIS_PORT", DefaultConfiguration.REDIS_PORT),
password=os.getenv("SDB_REDIS_PASS", DefaultConfiguration.REDIS_PASS),
)

if test_config is None:
# load the instance config, if it exists, when not testing
Expand Down Expand Up @@ -72,10 +98,23 @@ def create_app(test_config=None):
except OSError:
pass

cache.init_app(app)
# enabling redis caching
try:
r = redis.StrictRedis(
host=cache_config["CACHE_REDIS_HOST"],
port=cache_config["CACHE_REDIS_PORT"],
password=cache_config.get("CACHE_REDIS_PASSWORD"),
)
r.ping()
app.logger.debug("Connection to redis was successful")
cache.init_app(app)
except redis.ConnectionError:
app.logger.error("Error connecting to redis")

db.init_app(app)
migrate.init_app(app, db)
oauth.init_app(app, cache=cache)
session.init_app(app)

if (
"GITHUB_CLIENT_ID" in app.config
Expand Down Expand Up @@ -125,5 +164,4 @@ def create_app(test_config=None):
with app.app_context():
# Ensure there is some DB when we start the app
db.create_all()

return app
17 changes: 17 additions & 0 deletions app/default_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ class DefaultConfiguration:
SECRET_KEY = "dev"
SQLALCHEMY_ECHO = False
JSON_SORT_KEYS = True
# session settings
SESSION_TYPE = "redis"
SESSION_KEY_PREFIX = 'redis_session:'
SESSION_PERMANENT = False
SESSION_USE_SIGNER = True
# redis settings
REDIS_HOST = "localhost"
REDIS_PORT = 6379
REDIS_PASS = "password"
# cache settings
CACHE_TYPE = "RedisCache"
CACHE_KEY_PREFIX = "redis_cache"
CACHE_REDIS_HOST = "localhost"
CACHE_REDIS_PORT = 6379
CACHE_REDIS_PASSWORD = "password"
CACHE_REDIS_URL = f"redis://:{REDIS_PASS}@{REDIS_HOST}:{REDIS_PORT}"
CACHE_DEFAULT_TIMEOUT = 30

# Incident impacts map
# key - integer to identify impact and compare "severity"
Expand Down
5 changes: 3 additions & 2 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,11 @@ class Incident(Base):
id = mapped_column(Integer, primary_key=True, index=True)
text: Mapped[str] = mapped_column(String())
start_date: Mapped[datetime.datetime] = mapped_column(
insert_default=func.now()
insert_default=func.now(),
index=True
)
end_date: Mapped[datetime.datetime] = mapped_column(nullable=True)
impact: Mapped[int] = mapped_column(db.SmallInteger)
impact: Mapped[int] = mapped_column(db.SmallInteger, index=True)
# upgrade: system: Mapped[bool] = mapped_column(Boolean, default=False)
system: Mapped[bool] = mapped_column(Boolean, default=False)

Expand Down
41 changes: 41 additions & 0 deletions app/tests/unit/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# under the License.
#
import datetime
import threading
import time
from unittest import TestCase

from app import create_app
Expand All @@ -20,6 +22,8 @@
from app.models import ComponentAttribute
from app.models import Incident

import redis


class TestBase(TestCase):

Expand All @@ -28,6 +32,11 @@ class TestBase(TestCase):
)

def setUp(self):
self.redis_port = 6379
self.redis_password = "password"
self.start_redis_server()
print("STARTING redis server")
time.sleep(0.5)
self.app = create_app(self.test_config)
with self.app.app_context():
Base.metadata.create_all(bind=db.engine)
Expand All @@ -37,6 +46,38 @@ def tearDown(self):
with self.app.app_context():
db.session.remove()
Base.metadata.drop_all(bind=db.engine)
print("STOPPING redis server")
self.stop_redis_server()

def start_redis_server(self):
self.redis_server = threading.Thread(target=self.run_redis)
self.redis_server.daemon = True
self.redis_server.start()
self.wait_for_redis()

def stop_redis_server(self):
self.redis_process.terminate()
self.redis_process.wait()

def run_redis(self):
import subprocess
self.redis_process = subprocess.Popen(
['redis-server',
'--port', str(self.redis_port),
'--requirepass', self.redis_password]
)

def wait_for_redis(self):
while True:
try:
redis_client = redis.Redis(
port=self.redis_port,
password=self.redis_password
)
redis_client.ping()
break
except redis.ConnectionError:
time.sleep(0.2)


class TestWeb(TestBase):
Expand Down
17 changes: 17 additions & 0 deletions app/web/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from datetime import timezone

from app import authorization
from app import cache
from app import oauth
from app.models import Component
from app.models import ComponentAttribute
Expand All @@ -31,12 +32,19 @@
from flask import flash
from flask import redirect
from flask import render_template
from flask import request
from flask import session
from flask import url_for


@bp.route("/", methods=["GET"])
@bp.route("/index", methods=["GET"])
@cache.cached(
timeout=30,
key_prefix=lambda: (f':{session["user"]["sub"]}'
if "user" in session
else ':shared_cache')
)
def index():
return render_template(
"index.html",
Expand Down Expand Up @@ -142,9 +150,16 @@ def new_incident(current_user):


@bp.route("/incidents/<incident_id>", methods=["GET", "POST"])
@cache.cached(
timeout=30,
key_prefix=lambda: f":{request.path}" if "user" not in session else ""
)
def incident(incident_id):
"""Manage incident by ID"""
incident = Incident.get_by_id(incident_id)
if not incident:
abort(404)

form = None
if "user" in session:
form = IncidentUpdateForm(id)
Expand Down Expand Up @@ -219,6 +234,7 @@ def separate_incident(current_user, incident_id, component_id):


@bp.route("/history", methods=["GET"])
@cache.cached(timeout=30)
def history():
return render_template(
"history.html",
Expand All @@ -228,6 +244,7 @@ def history():


@bp.route("/availability", methods=["GET"])
@cache.cached(timeout=300)
def sla():
time_now = datetime.now()
months = [time_now + relativedelta(months=-mon) for mon in range(6)]
Expand Down
Binary file added dump.rdb
Binary file not shown.
2 changes: 1 addition & 1 deletion migrations/alembic.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# file_template = %(rev)s_%(slug)s

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
Expand Down
19 changes: 9 additions & 10 deletions migrations/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def get_engine():
try:
# this works with Flask-SQLAlchemy<3 and Alchemical
return current_app.extensions['migrate'].db.get_engine()
except TypeError:
except (TypeError, AttributeError):
# this works with Flask-SQLAlchemy>=3
return current_app.extensions['migrate'].db.engine

Expand All @@ -39,20 +39,16 @@ def get_engine_url():
config.set_main_option('sqlalchemy.url', get_engine_url())
target_db = current_app.extensions['migrate'].db

from app import models
target_metadata = models.Base.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def get_metadata():
return target_metadata
# if hasattr(target_db, 'metadatas'):
# return target_db.metadatas[None]
# return target_db.metadata
if hasattr(target_db, 'metadatas'):
return target_db.metadatas[None]
return target_db.metadata


def run_migrations_offline():
Expand Down Expand Up @@ -94,14 +90,17 @@ def process_revision_directives(context, revision, directives):
directives[:] = []
logger.info('No changes in schema detected.')

conf_args = current_app.extensions['migrate'].configure_args
if conf_args.get("process_revision_directives") is None:
conf_args["process_revision_directives"] = process_revision_directives

connectable = get_engine()

with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=get_metadata(),
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args
**conf_args
)

with context.begin_transaction():
Expand Down
Loading
Loading