mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-17 10:42:25 -05:00
This PR will optimize this query to use a more efficient index.
- Add notification_type to the dao_get_last_template_usage to optimize the query.
- Tested and analyzed query on production database with very significant results.
Before:
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.43..1711.35 rows=1 width=935) (actual time=21186.053..21186.053 rows=0 loops=1)
-> Index Scan Backward using ix_notifications_created_at on notifications (cost=0.43..4607493.80 rows=2693 width=935) (actual time=21186.052..21186.052 rows=0 loops=1)
Filter: (((key_type)::text <> 'test'::text) AND (template_id = 'xxxxxx'::uuid))
Rows Removed by Filter: 8244071
Planning time: 0.112 ms
Execution time: 21186.082 ms
After:
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Limit (cost=5323.10..5323.10 rows=1 width=935)
-> Sort (cost=5323.10..5323.74 rows=258 width=935)
Sort Key: created_at DESC
-> Index Scan using ix_notifications_template_id on notifications (cost=0.56..5321.81 rows=258 width=935)
Index Cond: (template_id = 'xxxxx'::uuid)
Filter: (((key_type)::text <> 'test'::text) AND (notification_type = 'sms'::notification_type))
Planning time: 1.102 ms
Execution time: 0.584 ms
86 lines
3.0 KiB
Python
86 lines
3.0 KiB
Python
from flask import (
|
|
Blueprint,
|
|
jsonify,
|
|
request,
|
|
current_app)
|
|
|
|
from app import redis_store
|
|
from app.dao.notifications_dao import (
|
|
dao_get_template_usage,
|
|
dao_get_last_template_usage
|
|
)
|
|
from app.dao.templates_dao import (
|
|
dao_get_templates_for_cache,
|
|
dao_get_template_by_id_and_service_id
|
|
)
|
|
|
|
from app.schemas import notification_with_template_schema
|
|
from app.utils import cache_key_for_service_template_counter
|
|
from app.errors import register_errors, InvalidRequest
|
|
|
|
template_statistics = Blueprint('template-statistics',
|
|
__name__,
|
|
url_prefix='/service/<service_id>/template-statistics')
|
|
|
|
register_errors(template_statistics)
|
|
|
|
|
|
@template_statistics.route('')
|
|
def get_template_statistics_for_service_by_day(service_id):
|
|
if request.args.get('limit_days'):
|
|
try:
|
|
limit_days = int(request.args['limit_days'])
|
|
except ValueError as e:
|
|
error = '{} is not an integer'.format(request.args['limit_days'])
|
|
message = {'limit_days': [error]}
|
|
raise InvalidRequest(message, status_code=400)
|
|
else:
|
|
limit_days = None
|
|
|
|
if limit_days == 7:
|
|
stats = get_template_statistics_for_7_days(limit_days, service_id)
|
|
else:
|
|
stats = dao_get_template_usage(service_id, limit_days=limit_days)
|
|
|
|
def serialize(data):
|
|
return {
|
|
'count': data.count,
|
|
'template_id': str(data.template_id),
|
|
'template_name': data.name,
|
|
'template_type': data.template_type,
|
|
'is_precompiled_letter': data.is_precompiled_letter
|
|
}
|
|
|
|
return jsonify(data=[serialize(row) for row in stats])
|
|
|
|
|
|
@template_statistics.route('/<template_id>')
|
|
def get_template_statistics_for_template_id(service_id, template_id):
|
|
template = dao_get_template_by_id_and_service_id(template_id, service_id)
|
|
if not template:
|
|
message = 'No template found for id {}'.format(template_id)
|
|
errors = {'template_id': [message]}
|
|
raise InvalidRequest(errors, status_code=404)
|
|
|
|
data = None
|
|
notification = dao_get_last_template_usage(template_id, template.template_type)
|
|
if notification:
|
|
data = notification_with_template_schema.dump(notification).data
|
|
|
|
return jsonify(data=data)
|
|
|
|
|
|
def get_template_statistics_for_7_days(limit_days, service_id):
|
|
cache_key = cache_key_for_service_template_counter(service_id)
|
|
template_stats_by_id = redis_store.get_all_from_hash(cache_key)
|
|
if not template_stats_by_id:
|
|
stats = dao_get_template_usage(service_id, limit_days=limit_days)
|
|
cache_values = dict([(x.template_id, x.count) for x in stats])
|
|
if cache_values:
|
|
redis_store.set_hash_and_expire(cache_key,
|
|
cache_values,
|
|
current_app.config.get('EXPIRE_CACHE_IN_SECONDS', 600))
|
|
else:
|
|
stats = dao_get_templates_for_cache(template_stats_by_id.items())
|
|
return stats
|