mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-19 14:08:47 -04:00
Compare commits
36 Commits
01-06-2025
...
cycle-noti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46e92dae13 | ||
|
|
32154fad79 | ||
|
|
d5be82b985 | ||
|
|
bb20ba784b | ||
|
|
25d4d01e1b | ||
|
|
664025b316 | ||
|
|
2e7ae5618c | ||
|
|
1197c4a50a | ||
|
|
bafb71f766 | ||
|
|
e64af5633a | ||
|
|
4a5d4c75d2 | ||
|
|
0d9782c077 | ||
|
|
0d8e0c13d0 | ||
|
|
b425984cd9 | ||
|
|
0d5b78149f | ||
|
|
b9441c528d | ||
|
|
7e4adee84b | ||
|
|
557bf7aa7e | ||
|
|
490d7d8d39 | ||
|
|
ed0aa61e27 | ||
|
|
01bfcdead6 | ||
|
|
37f5981de8 | ||
|
|
8979e19ef3 | ||
|
|
eede6dbda2 | ||
|
|
da3b155ae8 | ||
|
|
abe2555f31 | ||
|
|
dd71db1844 | ||
|
|
3f2cc1df17 | ||
|
|
42009beb0c | ||
|
|
67b03294e2 | ||
|
|
d6344d3370 | ||
|
|
d087e17f7a | ||
|
|
4303eba983 | ||
|
|
5b4b238098 | ||
|
|
cdbec816b3 | ||
|
|
2d8c6ab494 |
@@ -900,3 +900,75 @@ def process_row_from_job(job_id, job_row_number):
|
||||
notification_id = process_row(row, template, job, job.service)
|
||||
current_app.logger.info("Process row {} for job {} created notification_id: {}".format(
|
||||
job_row_number, job_id, notification_id))
|
||||
|
||||
|
||||
@notify_command()
|
||||
@click.option('-l', '--limit_row_count', required=True, help='Limit row count for the insert stmt')
|
||||
@click.option('-s', '--start_date', required=True, help='Start date', type=click_dt(format='%Y-%m-%d %H:%M'))
|
||||
@click.option('-e', '--end_date', required=True, help='End date', type=click_dt(format='%Y-%m-%d %H:%M'))
|
||||
def cycle_notification_history_table(limit_row_count, start_date, end_date):
|
||||
print(f"Starting cycle_notification_history_table for {start_date} to {end_date}")
|
||||
day_start = start_date
|
||||
day_end = start_date + timedelta(days=1)
|
||||
|
||||
while day_end < end_date:
|
||||
populate_notification_history_pivot(limit_row_count, day_start, day_end)
|
||||
day_start = day_start + timedelta(days=1)
|
||||
day_end = day_end + timedelta(days=1)
|
||||
|
||||
|
||||
def populate_notification_history_pivot(limit_row_count, start_date, end_date):
|
||||
# This relies on the notification_history_pivot table being created
|
||||
# and a trigger on notification_history has been created
|
||||
# what limit should we use here
|
||||
|
||||
# If this command needs to be run more than once you will need to drop nh_temp.
|
||||
# Especially if the nightly task to delete notifications has run (more data has been added to notification_history)
|
||||
populate_temp_table = """
|
||||
CREATE TABLE IF NOT EXISTS nh_temp AS SELECT id FROM notification_history
|
||||
where created_at >= :start_date
|
||||
and created_at < :end_date
|
||||
"""
|
||||
index_temp_table = """
|
||||
CREATE INDEX IF NOT EXISTS nh_temp_idx ON nh_temp (id)
|
||||
"""
|
||||
|
||||
rows_in_temp = "SELECT COUNT(*) FROM nh_temp"
|
||||
|
||||
delete_temp_rows = """
|
||||
DELETE FROM nh_temp t
|
||||
USING notification_history_pivot p
|
||||
WHERE t.id = p.id
|
||||
"""
|
||||
|
||||
# In each function call, using same database connection as used for the above SQL
|
||||
# (needs to be in a transaction; this can be inside a stored function or in a transaction from the code)
|
||||
|
||||
insert_sql = """
|
||||
INSERT INTO notification_history_pivot
|
||||
SELECT n.*
|
||||
FROM notification_history n,
|
||||
nh_temp t
|
||||
WHERE n.id = t.id
|
||||
limit :limit_row_count
|
||||
"""
|
||||
print(f"Starting cycle notification history for start_date: {start_date} and "
|
||||
f"end_date {end_date} and limit: {limit_row_count}")
|
||||
|
||||
db.session.execute(populate_temp_table, {"start_date": start_date, "end_date": end_date})
|
||||
db.session.execute(delete_temp_rows)
|
||||
db.session.execute(index_temp_table)
|
||||
rows_remaining = db.session.execute(rows_in_temp).fetchall()[0][0]
|
||||
while rows_remaining > 0:
|
||||
print("rows_remaining: ", rows_remaining)
|
||||
db.session.execute(insert_sql, {"limit_row_count": limit_row_count})
|
||||
db.session.execute(delete_temp_rows)
|
||||
db.session.commit()
|
||||
|
||||
rows_remaining = db.session.execute(rows_in_temp).fetchall()[0][0]
|
||||
|
||||
db.session.execute("DROP TABLE nh_temp")
|
||||
db.session.commit()
|
||||
print("End cycle notification history: ", datetime.utcnow())
|
||||
|
||||
|
||||
|
||||
@@ -372,6 +372,14 @@ def insert_notification_history_delete_notifications(
|
||||
ON CONFLICT ON CONSTRAINT notification_history_pkey
|
||||
DO NOTHING
|
||||
"""
|
||||
# Insert data into the pivot table as well. Only needed while we are trying to cycle the history table.
|
||||
insert_pivot_query = """
|
||||
insert into notification_history_pivot
|
||||
SELECT * from NOTIFICATION_ARCHIVE
|
||||
ON CONFLICT ON CONSTRAINT notification_history_pivot_pkey
|
||||
DO NOTHING
|
||||
"""
|
||||
|
||||
delete_query = """
|
||||
DELETE FROM notifications
|
||||
where id in (select id from NOTIFICATION_ARCHIVE)
|
||||
@@ -390,6 +398,8 @@ def insert_notification_history_delete_notifications(
|
||||
|
||||
db.session.execute(insert_query)
|
||||
|
||||
db.session.execute(insert_pivot_query)
|
||||
|
||||
db.session.execute(delete_query)
|
||||
|
||||
db.session.execute("DROP TABLE NOTIFICATION_ARCHIVE")
|
||||
|
||||
40
database_maintenance/README.md
Normal file
40
database_maintenance/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
Create a new app to test command
|
||||
|
||||
DO NOT commit new manifest file
|
||||
|
||||
Add to MakeFile
|
||||
'notify-cycle-history': {
|
||||
'NOTIFY_APP_NAME': 'api',
|
||||
'instances': {
|
||||
'preview': 0,
|
||||
'staging': 0,
|
||||
'production': 0
|
||||
},
|
||||
},
|
||||
|
||||
CF_APP=notify-cycle-history CF_SPACE=staging make generate-manifest > cycle-history-manifest.yml
|
||||
cf v3-create-app notify-cycle-history
|
||||
cf v3-apply-manifest -f cycle-history-manifest.yml
|
||||
cf v3-push notify-cycle-history
|
||||
|
||||
cf run-task notify-cycle-history "flask command cycle-notification-history-table -l 100000 -s '2020-03-18 00:00' -e '2020-03-19 00:00"
|
||||
|
||||
|
||||
|
||||
|
||||
- Deploy new command - cycle_notification_history_table to prod
|
||||
- script_1.sql
|
||||
- create nh_pivot
|
||||
- create nh trigger
|
||||
- create new index on nh
|
||||
- foreign_keys.sql
|
||||
- run command for each day in nh --> start October 1, 2019
|
||||
-- all data in NH
|
||||
-
|
||||
|
||||
2020-05-05T14:35:47.18+0100 [APP/TASK/6a5ea53b/0] OUT Starting cycle notification history for start_date: 2020-03-19 00:00:00 and end_date 2020-03-29 00:00:00 and limit: 100000
|
||||
2020-05-05T14:41:21.16+0100 [APP/TASK/6a5ea53b/0] OUT rows_remaining: 67800240
|
||||
2020-05-06T02:48:36.84+0100 [APP/TASK/6a5ea53b/0] OUT End cycle notification history: 2020-05-06 01:48:36.849611
|
||||
|
||||
|
||||
5,650,020
|
||||
30
database_maintenance/constraints.sql
Normal file
30
database_maintenance/constraints.sql
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT fk_notification_history_notification_status FOREIGN KEY (notification_status) REFERENCES notification_status_types(name)
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_api_key_id_fkey FOREIGN KEY (api_key_id) REFERENCES api_keys(id)
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_job_id_fkey FOREIGN KEY (job_id) REFERENCES jobs(id)
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_key_type_fkey FOREIGN KEY (key_type) REFERENCES key_types(name)
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_service_id_fkey FOREIGN KEY (service_id) REFERENCES services(id)
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_templates_history_fkey FOREIGN KEY (template_id, template_version) REFERENCES templates_history(id, version)
|
||||
|
||||
--- Create indexes after drop table since the names need to be unique
|
||||
--- Or rename these indexes (index names have to be unique in db)
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_job_id ON notification_history_pivot (job_id);
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_reference ON notification_history_pivot (reference);
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_template_id ON notification_history_pivot (template_id);
|
||||
CREATE INDEX CONCURRENTLY ix_notifications_service_id_composite ON notification_history_pivot (service_id, key_type, notification_type, created_at);
|
||||
|
||||
-- we could possibly not create this check constraint in the new table since we want to drop it in an outstanding PR.
|
||||
--ALTER TABLE notification_history_pivot ADD CONSTRAINT chk_notification_history_postage_null CHECK (
|
||||
--CASE
|
||||
-- WHEN notification_type = 'letter'::notification_type
|
||||
-- THEN postage IS NOT NULL AND (postage::text = ANY (ARRAY['first'::character varying, 'second'::character varying]::text[]))
|
||||
-- ELSE postage IS NULL
|
||||
--END)
|
||||
|
||||
-- Not creating these ones since we want to drop them any way.
|
||||
--DROP INDEX CONCURRENTLY ix_notification_history_api_key_id;
|
||||
--DROP INDEX CONCURRENTLY ix_notification_history_created_at;
|
||||
--DROP INDEX CONCURRENTLY ix_notification_history_notification_status;
|
||||
--DROP INDEX CONCURRENTLY ix_notification_history_notification_type;
|
||||
--DROP INDEX CONCURRENTLY ix_notification_history_service_id;
|
||||
--DROP INDEX CONCURRENTLY ix_notification_history_service_id_created_at;
|
||||
88
database_maintenance/cycle_notification_history.sql
Normal file
88
database_maintenance/cycle_notification_history.sql
Normal file
@@ -0,0 +1,88 @@
|
||||
-- Stage 1 - manual process
|
||||
|
||||
-- Create new notification_history table and primary key.
|
||||
CREATE TABLE notification_history_pivot AS SELECT * FROM notification_history WHERE 1=2;
|
||||
ALTER TABLE notification_history_pivot ADD PRIMARY KEY (id);
|
||||
|
||||
-- Update values of notification_status, billable_units, updated_at, sent_by, sent_at based on new updates coming into the notification_history table.
|
||||
CREATE OR REPLACE FUNCTION update_pivot_table()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql AS
|
||||
$$
|
||||
BEGIN
|
||||
UPDATE notification_history_pivot SET notification_status = NEW.notification_status,
|
||||
billable_units = NEW.billable_units,
|
||||
updated_at = NEW.updated_at,
|
||||
sent_by = NEW.sent_by,
|
||||
sent_at = NEW.sent_at
|
||||
WHERE notification_history_pivot.id = NEW.id;
|
||||
|
||||
RETURN NEW;
|
||||
END
|
||||
$$;
|
||||
|
||||
DROP TRIGGER IF EXISTS update_pivot on notification_history;
|
||||
-- Following may be blocked if running vacuum.
|
||||
-- SELECT pg_cancel_backend(pid);
|
||||
CREATE TRIGGER update_pivot AFTER UPDATE OF notification_status, billable_units, updated_at, sent_by, sent_at ON notification_history
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE update_pivot_table();
|
||||
|
||||
|
||||
-- Create foreign key constraints in notification_history_pivot using same names as used in notification_history
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT fk_notification_history_notification_status FOREIGN KEY (notification_status) REFERENCES notification_status_types(name);
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_api_key_id_fkey FOREIGN KEY (api_key_id) REFERENCES api_keys(id);
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_job_id_fkey FOREIGN KEY (job_id) REFERENCES jobs(id);
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_key_type_fkey FOREIGN KEY (key_type) REFERENCES key_types(name);
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_service_id_fkey FOREIGN KEY (service_id) REFERENCES services(id);
|
||||
ALTER TABLE notification_history_pivot ADD CONSTRAINT notification_history_templates_history_fkey FOREIGN KEY (template_id, template_version) REFERENCES templates_history(id, version);
|
||||
|
||||
-- Index used for data population
|
||||
create index CONCURRENTLY created_id_nh on notification_history (created_at, id);
|
||||
|
||||
-----
|
||||
|
||||
-- Stage 2 - automated process
|
||||
|
||||
cf run-task notify-cycle-history "flask command cycle-notification-history-table -l 100000 -s '2019-10-01 00:00' -e '2019-12-01 00:00'"
|
||||
cf run-task notify-cycle-history "flask command cycle-notification-history-table -l 100000 -s '2020-03-19 00:00' -e '2020-03-29 00:00'"
|
||||
... etc emd = May 11
|
||||
... On Monday - run again to get last nights inserts
|
||||
|
||||
-- Once this Python process has completed, then,
|
||||
|
||||
-- Stage 3 - manual process
|
||||
|
||||
-- Run basic sanity checks on data in notification_history_pivot, ensuring same number of entries present in notification_history and notification_history_pivot
|
||||
SELECT COUNT(*) FROM notification_history_pivot;
|
||||
SELECT COUNT(*) FROM notification_history where created_at >= '2019-10-01 00:00';
|
||||
-- Ensure these counts match.
|
||||
|
||||
ALTER INDEX ix_notification_history_job_id RENAME TO ix_notification_history_job_id_old;
|
||||
ALTER INDEX ix_notification_history_reference RENAME TO ix_notification_history_reference_old;
|
||||
ALTER INDEX ix_notification_history_template_id RENAME TO ix_notification_history_template_id_old;
|
||||
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_job_id ON notification_history_pivot (job_id);
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_reference ON notification_history_pivot (reference);
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_template_id ON notification_history_pivot (template_id);
|
||||
CREATE INDEX CONCURRENTLY ix_notifications_service_id_composite ON notification_history_pivot (service_id, key_type, notification_type, created_at);
|
||||
|
||||
ALTER TABLE notification_history RENAME TO notification_history_old;
|
||||
ALTER TABLE notification_history_pivot RENAME TO notification_history;
|
||||
|
||||
|
||||
|
||||
-- When sure data in new notification_history table are ok:
|
||||
-- This can be done later: DROP TABLE notification_history_old;
|
||||
|
||||
-- Could rename primary key on new notification_history table.
|
||||
ALTER TABLE notification_history DROP CONSTRAINT notification_history_pivot_pkey;
|
||||
ALTER TABLE notification_history ADD PRIMARY KEY (id); -- Will create a new primary key named "notification_history_pkey".
|
||||
|
||||
-- Create remaining indices on notification_history_pivot
|
||||
--- Create indexes after drop table since the names need to be unique
|
||||
--- Or rename these indexes (index names have to be unique in db)
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_job_id ON notification_history (job_id);
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_reference ON notification_history (reference);
|
||||
CREATE INDEX CONCURRENTLY ix_notification_history_template_id ON notification_history (template_id);
|
||||
CREATE INDEX CONCURRENTLY ix_notifications_service_id_composite ON notification_history (service_id, key_type, notification_type, created_at);
|
||||
@@ -0,0 +1,33 @@
|
||||
-- Call once at the start of the process
|
||||
|
||||
create index created_id_nh on notification_history (created_at, id);
|
||||
|
||||
CREATE TABLE notification_history_pivot AS SELECT * from notification_history WHERE 1=2;
|
||||
|
||||
CREATE TEMPORARY TABLE nh_temp AS SELECT id FROM notification_history;
|
||||
|
||||
SELECT COUNT(*) AS "Total number of rows in nh" FROM nh_temp;
|
||||
|
||||
DELETE FROM nh_temp t
|
||||
USING notification_history_pivot p
|
||||
WHERE t.id = p.id;
|
||||
|
||||
SELECT COUNT(*) AS "Number of rows remaining that need moving across from nh to nh_pivot" FROM nh_temp;
|
||||
|
||||
CREATE INDEX nh_temp_idx ON nh_temp (id);
|
||||
|
||||
-- In each function call, using same database connection as used for the above SQL (needs to be in a transaction; this can be inside a stored function or in a transaction from the code)
|
||||
|
||||
INSERT INTO notification_history_pivot
|
||||
SELECT n.*
|
||||
FROM notification_history n,
|
||||
nh_temp t
|
||||
WHERE n.id = t.id;
|
||||
|
||||
DELETE FROM nh_temp t
|
||||
USING notification_history_pivot p
|
||||
WHERE t.id = p.id;
|
||||
|
||||
SELECT COUNT(*) from nh_temp;
|
||||
|
||||
-- Loop until this result is zero.
|
||||
23
migrations/versions/0321_notification_history_pivot.py
Normal file
23
migrations/versions/0321_notification_history_pivot.py
Normal file
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
|
||||
Revision ID: 0321_notification_history_pivot
|
||||
Revises: 0320_optimise_notifications
|
||||
Create Date: 2020-03-26 11:16:12.389524
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision = '0321_notification_history_pivot'
|
||||
down_revision = '0320_optimise_notifications'
|
||||
environment = os.environ['NOTIFY_ENVIRONMENT']
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.execute('CREATE TABLE notification_history_pivot AS SELECT * FROM notification_history WHERE 1=2')
|
||||
op.execute('ALTER TABLE notification_history_pivot ADD PRIMARY KEY (id)')
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.execute('DROP TABLE notifications_history_pivot')
|
||||
Reference in New Issue
Block a user