renamed metrics schema to metric, because I am one anal MF
parent
1d80903939
commit
abf3556b36
|
@ -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)
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
-- create the schema that will be used to store system metrics data
|
||||
CREATE SCHEMA metrics;
|
||||
CREATE SCHEMA metric;
|
|
@ -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;
|
|
@ -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)
|
||||
;
|
|
@ -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
|
||||
)
|
|
@ -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
|
|
@ -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,
|
|
@ -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'
|
||||
)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
||||
)
|
|
@ -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)
|
|
@ -1,5 +1,5 @@
|
|||
INSERT INTO
|
||||
metrics.job
|
||||
metric.job
|
||||
VALUES (
|
||||
DEFAULT,
|
||||
%(job_name)s,
|
|
@ -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
|
|
@ -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)
|
|
@ -1,4 +1,4 @@
|
|||
DELETE FROM
|
||||
metrics.api
|
||||
metric.api
|
||||
WHERE
|
||||
access_ts::date = %(delete_date)s
|
|
@ -1,6 +1,6 @@
|
|||
SELECT
|
||||
*
|
||||
FROM
|
||||
metrics.api
|
||||
metric.api
|
||||
WHERE
|
||||
access_ts::date = %(metrics_date)s
|
|
@ -5,6 +5,6 @@ SELECT
|
|||
insert_ts,
|
||||
metric_value
|
||||
FROM
|
||||
metrics.core
|
||||
metric.core
|
||||
WHERE
|
||||
device_id = %(device_id)s
|
|
@ -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)
|
Loading…
Reference in New Issue