Reduce extra S3 ops when working with letter PDFs

Previously we did some unnecessary work:

- Collate task. This had one S3 request to get a summary of the object,
which was then used in another request to get the full object. We only
need the size of the object, which is included in the summary [1].

- Archive task. This had one S3 request to get a summary of the object,
which was then used to make another request to delete it. We still need
both requests, but we can remove the S3.Object in the middle.

[1]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#objectsummary
This commit is contained in:
Ben Thorner
2021-03-16 11:57:33 +00:00
parent ff7eebc90a
commit c76e789f1e
5 changed files with 22 additions and 38 deletions

View File

@@ -72,8 +72,7 @@ def test_should_delete_notifications_by_type_after_seven_days(
expected_email_count,
expected_letter_count
):
mocker.patch("app.dao.notifications_dao.find_letter_pdf_filename")
mocker.patch("app.dao.notifications_dao.remove_s3_object")
mocker.patch("app.dao.notifications_dao.find_letter_pdf_in_s3")
email_template, letter_template, sms_template = _create_templates(sample_service)
# create one notification a day between 1st and 10th from 11:00 to 19:00 of each type
for i in range(1, 11):
@@ -120,17 +119,16 @@ def test_should_not_delete_notification_history(sample_service, mocker):
@pytest.mark.parametrize('notification_type', ['sms', 'email', 'letter'])
def test_delete_notifications_for_days_of_retention(sample_service, notification_type, mocker):
mocker.patch('app.dao.notifications_dao.find_letter_pdf_filename')
mock_remove_s3 = mocker.patch("app.dao.notifications_dao.remove_s3_object")
mock_s3_object = mocker.patch('app.dao.notifications_dao.find_letter_pdf_in_s3').return_value
create_test_data(notification_type, sample_service)
assert Notification.query.count() == 9
delete_notifications_older_than_retention_by_type(notification_type)
assert Notification.query.count() == 7
assert Notification.query.filter_by(notification_type=notification_type).count() == 1
if notification_type == 'letter':
assert mock_remove_s3.call_count == 2
assert mock_s3_object.delete.call_count == 2
else:
mock_remove_s3.assert_not_called()
mock_s3_object.delete.assert_not_called()
@mock_s3
@@ -227,7 +225,7 @@ def test_delete_notifications_delete_notification_type_for_default_time_if_no_da
def test_delete_notifications_deletes_letters_not_sent_and_in_final_state_from_table_but_not_s3(
sample_service, mocker, notification_status
):
mock_remove_s3 = mocker.patch("app.dao.notifications_dao.remove_s3_object")
mock_s3_object = mocker.patch("app.dao.notifications_dao.find_letter_pdf_in_s3").return_value
letter_template = create_template(service=sample_service, template_type='letter')
create_notification(
template=letter_template,
@@ -242,7 +240,7 @@ def test_delete_notifications_deletes_letters_not_sent_and_in_final_state_from_t
assert Notification.query.count() == 0
assert NotificationHistory.query.count() == 1
mock_remove_s3.assert_not_called()
mock_s3_object.assert_not_called()
@mock_s3
@@ -289,7 +287,7 @@ def test_delete_notifications_deletes_letters_sent_and_in_final_state_from_table
def test_delete_notifications_does_not_delete_letters_not_yet_in_final_state(
sample_service, mocker, notification_status
):
mock_remove_s3 = mocker.patch("app.dao.notifications_dao.remove_s3_object")
mock_s3_object = mocker.patch("app.dao.notifications_dao.find_letter_pdf_in_s3").return_value
letter_template = create_template(service=sample_service, template_type='letter')
create_notification(
template=letter_template,
@@ -304,7 +302,7 @@ def test_delete_notifications_does_not_delete_letters_not_yet_in_final_state(
assert Notification.query.count() == 1
assert NotificationHistory.query.count() == 0
mock_remove_s3.assert_not_called()
mock_s3_object.assert_not_called()
@freeze_time('2020-03-25 00:01')

View File

@@ -10,7 +10,7 @@ from moto import mock_s3
from app.letters.utils import (
LetterPDFNotFound,
ScanErrorType,
find_letter_pdf_filename,
find_letter_pdf_in_s3,
generate_letter_pdf_filename,
get_bucket_name_and_prefix_for_notification,
get_folder_name,
@@ -49,7 +49,7 @@ def _sample_precompiled_letter_notification_using_test_key(sample_precompiled_le
@mock_s3
def test_find_letter_pdf_filename_returns_filename(sample_notification):
def test_find_letter_pdf_in_s3_returns_object(sample_notification):
bucket_name = current_app.config['LETTERS_PDF_BUCKET_NAME']
s3 = boto3.client('s3', region_name='eu-west-1')
s3.create_bucket(
@@ -60,11 +60,11 @@ def test_find_letter_pdf_filename_returns_filename(sample_notification):
_, prefix = get_bucket_name_and_prefix_for_notification(sample_notification)
s3.put_object(Bucket=bucket_name, Key=f'{prefix}-and-then-some', Body=b'f')
assert find_letter_pdf_filename(sample_notification) == f'{prefix}-and-then-some'
assert find_letter_pdf_in_s3(sample_notification).key == f'{prefix}-and-then-some'
@mock_s3
def test_find_letter_pdf_filename_raises_if_not_found(sample_notification):
def test_find_letter_pdf_in_s3_raises_if_not_found(sample_notification):
bucket_name = current_app.config['LETTERS_PDF_BUCKET_NAME']
s3 = boto3.client('s3', region_name='eu-west-1')
s3.create_bucket(
@@ -73,7 +73,7 @@ def test_find_letter_pdf_filename_raises_if_not_found(sample_notification):
)
with pytest.raises(LetterPDFNotFound):
find_letter_pdf_filename(sample_notification)
find_letter_pdf_in_s3(sample_notification)
@pytest.mark.parametrize('created_at,folder', [