Skip to content

Stream form submission logs to the submission events table - #2326

Merged
theseanything merged 4 commits into
mainfrom
submission-events-subscription-filter
Oct 22, 2026
Merged

theseanything merged 4 commits into
mainfrom
submission-events-subscription-filter

Conversation

@theseanything

Copy link
Copy Markdown
Contributor

What problem does this pull request solve?

The submission events Firehose stream and S3 table are in place, but nothing feeds them yet. This adds a subscription filter on the forms-runner log group that sends form_submission and form_preview_submission events to a Lambda. The Lambda maps each event onto the table schema and puts it onto the stream as its own record, because Firehose's Iceberg destination accepts only one JSON object per record.

If Firehose rejects records, the invocation fails. Lambda retries it twice, then writes it to the analytics error bucket. Retries and CloudWatch Logs' at-least-once delivery mean the table can contain duplicates, so queries should count distinct submission_reference values. Submissions to preview and test forms are kept and should be excluded at query time.

This filter uses the second and last subscription filter slot on the forms-runner log group; the first sends logs to Splunk.

Replaces #2313.

Things to consider when reviewing

  • Ensure that you consider the wider context.
  • Does it work when run on your machine?
  • Is it clear what the code is doing?
  • Do the commit messages explain why the changes were made?
  • Are there all the unit tests needed?
  • Has all relevant documentation been updated?

Reminders

If you've made changes to the deployer role (files in modules/deployer-access):

  • Remember to run make <environment> forms/account apply on the relevant environments (dev, staging and/or prod)
  • Check the #govuk-forms-deployment-notifications Slack channel to ensure the apply-forms-terraform-<environment> pipelines have run successfully

theseanything and others added 4 commits October 19, 2026 09:32
CloudWatch Logs batches several log events into one gzipped record,
but the Firehose stream needs one JSON object per record. The Lambda is
invoked by CloudWatch Logs, maps each event onto the table schema and
puts each one to the stream as its own record.

An event that cannot be mapped onto the table schema is logged and
skipped, so that it does not fail the rest of its batch. If Firehose
rejects records the invocation fails; Lambda retries it and then
writes it to the error bucket.

Co-Authored-By: Claude <noreply@anthropic.com>
The health pipeline will set the transform Lambda's retries and failure
destination. The deployer role's Lambda permissions do not include the
event invoke config actions. The Lambda's role is already covered by
the submission events roles statement.

The statement is in forms_infra_1 with the stream's because
forms_infra_2 is close to the 6,144 character managed policy limit.
A subscription filter on the forms-runner log group invokes the Lambda
with form_submission and form_preview_submission log events, which is
what feeds the Firehose stream and so the submission_events table.

It takes the second and last subscription filter slot on the log group;
the first sends logs to Splunk.
The specs need a Gemfile and spec directory next to the handler, so the
archive now packages only transform.rb rather than the whole directory.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 19, 2026 08:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-service delivery and failed-invocation recovery need end-to-end validation in AWS.

No open issues 🎉

What changed in this PR

Connects forms-runner submission logs to the existing Firehose stream and submission events table.

Changes:

  • Adds a CloudWatch Logs subscription and Lambda that emits one record per submission.
  • Configures permissions, retries, and failed-invocation storage.
  • Adds transformation tests and integrates them into make spec.
File Description
Makefile Runs analytics Lambda specs.
infra/​modules/​deployer-access/​policy.tf Allows managing Lambda invoke configuration.
infra/​modules/​analytics/​subscription-filter.tf Routes submission logs to Lambda.
infra/​modules/​analytics/​main.tf Defines function and source log group names.
infra/​modules/​analytics/​lambda/​transform.rb Maps log events and batches Firehose writes.
infra/​modules/​analytics/​lambda/​spec/​transform_spec.rb Tests mapping, batching, and failure handling.
infra/​modules/​analytics/​lambda/​spec/​spec_helper.rb Configures SDK stubs and RSpec.
infra/​modules/​analytics/​lambda/​Gemfile.lock Locks development and test dependencies.
infra/​modules/​analytics/​lambda/​Gemfile Declares development and test dependencies.
infra/​modules/​analytics/​lambda/​.ruby-version Pins local Ruby version.
infra/​modules/​analytics/​lambda/​.gitignore Ignores vendored dependencies.
infra/​modules/​analytics/​lambda.tf Provisions Lambda, permissions, and failure destination.
infra/​modules/​analytics/​error-bucket.tf Documents Lambda error-bucket usage.
infra/​modules/​analytics/​.gitignore Ignores generated Lambda archives.

Review effort: Balanced

@theseanything
theseanything marked this pull request as draft October 20, 2026 09:17
@theseanything
theseanything marked this pull request as ready for review October 20, 2026 14:51
@theseanything
theseanything requested a review from whi-tw October 20, 2026 14:51
@theseanything
theseanything added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit 1e847fa Oct 22, 2026
21 checks passed
@theseanything
theseanything deleted the submission-events-subscription-filter branch October 22, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants