Keep the mail TTL policy from being deleted by every deploy - #212
Merged
Conversation
The TTL on mail/delivery.expireAt was created by hand with gcloud, as the runbook told us to. The next deploy deleted it: 14:51 gcloud ... --enable-ttl --project=datapipe-test -> ACTIVE 15:02 PR #211 merged to `test` 15:04 firestore: Deleting 1 field overrides... <- the deploy 15:10 TTL gone `firebase deploy --only firestore` reconciles field overrides against firestore.indexes.json, and that file said `"fieldOverrides": []` -- so a hand-made TTL was not merely unmanaged, it was something the deploy was actively instructed to delete. --force suppressed the prompt that would have said so, and the deploy reported success. The retention promise stops being kept, silently, while the runbook says it is. Declared in firestore.indexes.json instead, so it is version-controlled and recreated by the same deploy that would otherwise remove it. The block keeps the three default single-field indexes: a fieldOverride replaces the field's whole index configuration, so `"indexes": []` would additionally turn off single-field indexing for delivery.expireAt. What is committed here is what `firebase firestore:indexes` emits for a field with TTL on and default indexing, read off osf-relay while its policy was still active, so it round-trips. Runbook 4 rewritten around the declarative mechanism, keeping the timeline above -- following the old instructions produced a policy that survived exactly until the next deploy, and that is worth being unable to rediscover the hard way. Also corrects two comments in mail-delivery.ts that said the TTL keys on delivery.endTime. It keys on delivery.expireAt; endTime only gates whether expireAt gets written. That same confusion is what put the wrong field in runbook 4's console instructions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D1dNU7EnmBBMTn8MWfojDL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The TTL on
mail/delivery.expireAtwas created by hand withgcloud, as runbook §4 instructed. The next deploy deleted it.That middle line is from the deploy log, verbatim.
Why
firebase deploy --only firestorereconciles field overrides againstfirestore.indexes.json, and that file said"fieldOverrides": []— an explicit assertion that no field overrides should exist. So a hand-made TTL wasn't merely unmanaged; it was something the deploy was actively instructed to delete.--forcesuppressed the confirmation prompt that would have said so, and the deploy reported success.A manual TTL therefore survives exactly until the next deploy of any kind, silently. The retention promise stops being kept while the runbook says it is — and the only symptom is a
mailcollection quietly accumulating researcher email addresses forever.This currently affects prod too.
osf-relay's TTL isACTIVEright now, but the next deploy tomainremoves it by the same mechanism.The fix
Declared in
firestore.indexes.json, so it's version-controlled and created by the same deploy that was deleting it. Merging this restores thedatapipe-testpolicy automatically — nogcloudneeded — and protects prod ahead of its next deploy.Two details that aren't arbitrary:
firebase firestore:indexesemits, read offosf-relaywhile its policy was still active, so it round-trips rather than being a guess at the schema.fieldOverridereplaces the field's entire index configuration, so"indexes": []would additionally turn off single-field indexing fordelivery.expireAt— an unrelated second change nobody asked for.Also
mail-delivery.tscorrected — they said the TTL keys ondelivery.endTime. It keys ondelivery.expireAt;endTimeonly gates whetherexpireAtgets written. That same confusion is what put the wrong field in §4's console instructions, fixed in Send transactional mail through Resend, not SES #211.Checked for wider drift
No real indexes are at risk. Local
firestore.indexes.jsonhas oneuploadQueueindex (experimentID, status, providerErrorCode) that prod lacks — a create on next deploy, not a delete. Nothing deployed is missing from the file.Verifying after merge
state: ACTIVEis the goal.CREATINGmeans the field backfill is still running — several minutes regardless of collection size, since it's a control-plane operation.Listed 0 itemswould mean this didn't take.🤖 Generated with Claude Code