renamed metrics schema to metric, because I am one anal MF

pull/191/head
Chris Veilleux 2019-06-28 22:38:38 -05:00
parent 1d80903939
commit abf3556b36
32 changed files with 73 additions and 49 deletions

View File

@ -2,11 +2,11 @@ from datetime import datetime
from http import HTTPStatus
from selene.api import PublicEndpoint
from selene.data.metrics import CoreMetric, CoreMetricRepository
from selene.data.metric import CoreMetric, CoreMetricRepository
class DeviceMetricsEndpoint(PublicEndpoint):
"""Endpoint to communicate with the metrics service"""
"""Endpoint to communicate with the metric service"""
def post(self, device_id, metric):
self._authenticate(device_id)

View File

@ -4,7 +4,7 @@ import uuid
from behave import given, then, when
from hamcrest import assert_that, equal_to
from selene.data.metrics import CoreMetricRepository
from selene.data.metric import CoreMetricRepository
METRIC_TYPE_TIMING = 'timing'
metric_value = dict(
@ -23,7 +23,7 @@ def define_unauthorized_device(context):
context.metric_device_id = str(uuid.uuid4())
@when('the metrics endpoint is called')
@when('the metric endpoint is called')
def call_metrics_endpoint(context):
headers = dict(Authorization='Bearer {token}'.format(
token=context.device_login['accessToken'])

View File

@ -1,11 +1,11 @@
"""Copy api metrics from a transient table to a partitioned table.
"""Copy api metric from a transient table to a partitioned table.
Millions of rows per day are added to the metrics.api table. To help with
Millions of rows per day are added to the metric.api table. To help with
query performance, copy the rows from this table to a partitioned table on a
daily basis.
"""
from selene.batch.base import SeleneScript
from selene.data.metrics import ApiMetricsRepository
from selene.data.metric import ApiMetricsRepository
from selene.util.db import use_transaction

View File

@ -1,2 +1,2 @@
-- create the schema that will be used to store system metrics data
CREATE SCHEMA metrics;
CREATE SCHEMA metric;

View File

@ -1,2 +1,2 @@
GRANT USAGE ON SCHEMA metrics TO selene;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA metrics TO selene;
GRANT USAGE ON SCHEMA metric TO selene;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA metric TO selene;

View File

@ -1,12 +1,13 @@
CREATE TABLE metrics.api (
CREATE TABLE metric.api (
id uuid PRIMARY KEY
DEFAULT gen_random_uuid(),
url text NOT NULL,
http_method text NOT NULL,
http_status CHAR(3) NOT NULL,
duration NUMERIC NOT NULL,
access_ts timestamp NOT NULL
DEFAULT now(),
api text NOT NULL,
duration NUMERIC NOT NULL,
http_status CHAR(3) NOT NULL,
url text NOT NULL,
account_id uuid,
device_id uuid
);
@ -14,5 +15,5 @@ CREATE TABLE metrics.api (
CREATE INDEX IF NOT EXISTS
api_access_ts_idx
ON
metrics.api (access_ts)
metric.api (access_ts)
;

View File

@ -1,10 +1,11 @@
CREATE TABLE metrics.api_history (
CREATE TABLE metric.api_history (
id uuid NOT NULL,
url text NOT NULL,
http_method text NOT NULL,
http_status CHAR(3) NOT NULL,
duration NUMERIC NOT NULL,
access_ts timestamp NOT NULL,
api text NOT NULL,
duration NUMERIC NOT NULL,
http_status CHAR(3) NOT NULL,
url text NOT NULL,
account_id uuid,
device_id uuid
)

View File

@ -1,4 +1,4 @@
CREATE TABLE metrics.core (
CREATE TABLE metric.core (
id uuid PRIMARY KEY
DEFAULT gen_random_uuid(),
device_id uuid NOT NULL

View File

@ -1,4 +1,4 @@
CREATE TABLE metrics.job (
CREATE TABLE metric.job (
id uuid PRIMARY KEY
DEFAULT gen_random_uuid(),
job_name text NOT NULL,

View File

@ -5,7 +5,7 @@ from markdown import markdown
from psycopg2 import connect
MYCROFT_DB_DIR = path.join(path.abspath('..'), 'mycroft')
SCHEMAS = ('account', 'skill', 'device', 'geography', 'metrics')
SCHEMAS = ('account', 'skill', 'device', 'geography', 'metric')
DB_DESTROY_FILES = (
'drop_mycroft_db.sql',
'drop_template_db.sql',
@ -45,7 +45,7 @@ GEOGRAPHY_TABLE_ORDER = (
'city'
)
METRICS_TABLE_ORDER = (
METRIC_TABLE_ORDER = (
'api',
'api_history,'
'job',
@ -184,7 +184,7 @@ for table in DEVICE_TABLE_ORDER:
print('Creating the metrics schema tables')
for table in METRICS_TABLE_ORDER:
create_table_file = path.join(
'metrics_schema',
'metric_schema',
'tables',
table + '.sql'
)

View File

@ -31,10 +31,11 @@ class SeleneEndpoint(MethodView):
- override the _build_response_data method
"""
def __init__(self):
global_context.url = request.url
global_context.http_method = request.method
self.config: dict = current_app.config
self.request = request
self.response: tuple = None
global_context.url = request.url
self.account: Account = None
self.access_token = self._init_access_token()
self.refresh_token = self._init_refresh_token()

View File

@ -6,7 +6,7 @@ from http import HTTPStatus
from flask import current_app, Blueprint, g as global_context
from schematics.exceptions import DataError
from selene.data.metrics import ApiMetric, ApiMetricsRepository
from selene.data.metric import ApiMetric, ApiMetricsRepository
from selene.util.auth import AuthenticationError
from selene.util.cache import DEVICE_LAST_CONTACT_KEY
from selene.util.db import connect_to_db
@ -44,9 +44,9 @@ def teardown_request(response):
def add_api_metric(http_status):
"""Add a row to the table tracking metrics for API calls"""
"""Add a row to the table tracking metric for API calls"""
api = None
# We are not logging metrics for the public API until after the socket
# We are not logging metric for the public API until after the socket
# implementation to avoid putting millions of rows a day on the table
for api_name in ('account', 'sso', 'market', 'public'):
if api_name in current_app.name:
@ -74,6 +74,7 @@ def add_api_metric(http_status):
api=api,
device_id=device_id,
duration=Decimal(str(duration.total_seconds())),
http_method=global_context.http_method,
http_status=int(http_status),
url=global_context.url
)

View File

@ -83,9 +83,10 @@ class PublicEndpoint(MethodView):
"""Abstract class for all endpoints used by Mycroft devices"""
def __init__(self):
global_context.url = request.url
global_context.http_method = request.method
self.config: dict = current_app.config
self.request = request
global_context.url = request.url
self.cache: SeleneCache = self.config['SELENE_CACHE']
global_context.cache = self.cache
self.etag_manager: ETagManager = ETagManager(self.cache, self.config)

View File

@ -9,7 +9,7 @@ from datetime import date, datetime
from os import environ, path
import sys
from selene.data.metrics import JobMetric, JobRepository
from selene.data.metric import JobMetric, JobRepository
from selene.util.db import DatabaseConnectionConfig, connect_to_db
from selene.util.log import configure_logger
@ -96,7 +96,7 @@ class SeleneScript(object):
self.log.info('* * * * * END OF JOB * * * * *')
def _insert_metrics(self):
"""Add a row to the job metrics table for monitoring purposes."""
"""Add a row to the job metric table for monitoring purposes."""
if self.args is not None:
job_repository = JobRepository(self.db)
job_metric = JobMetric(

View File

@ -9,6 +9,7 @@ class ApiMetric(object):
access_ts: datetime
api: str
duration: Decimal
http_method: str
http_status: int
id: str = None
account_id: str = None

View File

@ -1,12 +1,12 @@
"""CRUD operations for the metrics.api and metrics.api_history tables.
"""CRUD operations for the metric.api and metric.api_history tables.
The metrics.api table contains performance metrics for the Selene APIs. There
The metric.api table contains performance metric for the Selene APIs. There
are millions of API requests made per day. This can lead to poor performance
querying the table after only a few days. This problem is solved by
partitioning the table into smaller daily tables.
The declarative partitioning scheme provided by Postgres is used to create
the partitions of the metrics.api_history table
the partitions of the metric.api_history table
"""
import os
from dataclasses import asdict
@ -30,7 +30,7 @@ class ApiMetricsRepository(RepositoryBase):
self.cursor.insert(db_request)
def create_partition(self, partition_date: date):
"""Create a daily partition for the metrics.api_history table."""
"""Create a daily partition for the metric.api_history table."""
start_ts = datetime.combine(partition_date, time.min)
end_ts = datetime.combine(partition_date, time.max)
db_request = self._build_db_request(
@ -47,20 +47,20 @@ class ApiMetricsRepository(RepositoryBase):
self.cursor.execute(db_request)
def copy_to_partition(self, partition_date: date):
"""Copy rows from metrics.api table to metrics.api_history."""
"""Copy rows from metric.api table to metric.api_history."""
dump_file_name = 'api_metrics_' + str(partition_date)
dump_file_path = os.path.join(DUMP_FILE_DIR, dump_file_name)
db_request = self._build_db_request(
sql_file_name='get_api_metrics_for_date.sql',
args=dict(metrics_date=partition_date)
)
table_name = 'metrics.api_history_' + partition_date.strftime('%Y%m%d')
table_name = 'metric.api_history_' + partition_date.strftime('%Y%m%d')
self.cursor.dump_query_result_to_file(db_request, dump_file_path)
self.cursor.load_dump_file_to_table(table_name, dump_file_path)
os.remove(dump_file_path)
def remove_by_date(self, partition_date: date):
"""Delete from metrics.api table after copying to metrics.api_history"""
"""Delete from metric.api table after copying to metric.api_history"""
db_request = self._build_db_request(
sql_file_name='delete_api_metrics_by_date.sql',
args=dict(delete_date=partition_date)

View File

@ -0,0 +1,22 @@
INSERT INTO
metric.api (
url,
access_ts,
api,
account_id,
device_id,
duration,
http_method,
http_status
)
VALUES
(
%(url)s,
%(access_ts)s,
%(api)s,
%(account_id)s,
%(device_id)s,
%(duration)s,
%(http_method)s,
%(http_status)s
)

View File

@ -1,4 +1,4 @@
INSERT INTO
metrics.core (device_id, metric_type, metric_value)
metric.core (device_id, metric_type, metric_value)
VALUES
(%(device_id)s, %(metric_type)s, %(metric_value)s)

View File

@ -1,5 +1,5 @@
INSERT INTO
metrics.job
metric.job
VALUES (
DEFAULT,
%(job_name)s,

View File

@ -1,5 +1,5 @@
CREATE TABLE IF NOT EXISTS
metrics.api_history_{partition}
metric.api_history_{partition}
PARTITION OF
metrics.api_history
FOR VALUES FROM

View File

@ -1,4 +1,4 @@
CREATE INDEX IF NOT EXISTS
api_history_{partition}_access_ts_idx
ON
metrics.api_history_{partition} (access_ts)
metric.api_history_{partition} (access_ts)

View File

@ -1,4 +1,4 @@
DELETE FROM
metrics.api
metric.api
WHERE
access_ts::date = %(delete_date)s

View File

@ -1,6 +1,6 @@
SELECT
*
FROM
metrics.api
metric.api
WHERE
access_ts::date = %(metrics_date)s

View File

@ -5,6 +5,6 @@ SELECT
insert_ts,
metric_value
FROM
metrics.core
metric.core
WHERE
device_id = %(device_id)s

View File

@ -1,4 +0,0 @@
INSERT INTO
metrics.api (url, access_ts, api, account_id, device_id, duration, http_status)
VALUES
(%(url)s, %(access_ts)s, %(api)s, %(account_id)s, %(device_id)s, %(duration)s, %(http_status)s)