Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("workflows", "0016_drop_emailreputationsnapshot_fk"),
]

# Phase 2 of the two-phase table drop (safe-django-migrations.md, "Dropping Tables").
# 0015 removed EmailReputationSnapshot from Django state and 0016 dropped its FK to
# posthog_hogflow; the physical table has been dead ever since. This drops it now that the
# state removal has shipped. Irreversible by nature — the reverse is a no-op rather than a
# bogus CREATE TABLE, so a rollback past this point simply leaves the table absent (nothing
# reads or writes it).
operations = [
migrations.RunSQL(
sql='DROP TABLE IF EXISTS "posthog_emailreputationsnapshot";',
reverse_sql=migrations.RunSQL.noop,
),
Comment on lines +15 to +19

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.

A separate Node.js/Temporal worker service actively reads and writes this table — not just the Python API endpoint

must_fix bug

Why we think it's a valid issue
  • Checked: The actual base branch this migration deploys onto and merges into (posthog-code/ses-tenant-event-comms, the stack tip), not local master. Verified the Node email-reputation.service.ts, its Temporal worker, and the capability/mode/server wiring directly via the GitHub API on that ref.
  • Found: Unlike the Python side (model deleted, endpoint rewritten), the Node service still runs live SQL against the table on the base branch: SELECT DISTINCT team_id FROM posthog_emailreputationsnapshot (email-reputation.service.ts:121), a second SELECT DISTINCT ON (team_id) ... FROM posthog_emailreputationsnapshot (:418), and INSERT INTO posthog_emailreputationsnapshot ... ON CONFLICT DO NOTHING (:442). Nothing in the stack repointed or removed these.
  • Found: It is wired and scheduled, not dead code — EmailReputationWorkerService is started in server.ts:400-425 under capabilities.emailReputationEvaluator, the temporal worker registers a daily schedule (scheduleId: EMAIL_REPUTATION_SCHEDULE_ID, spec: { calendars: [{ hour }] }, email-reputation-worker.service.ts:118-135), and there is a dedicated deployment mode PluginServerMode.email_reputation_evaluator = 'email-reputation-evaluator' (config.ts:54). The server.ts comment "Dedicated deployments should crash and restart" confirms a dedicated production deployment path (dev merely rides along via isDevEnv() and degrades gracefully; dedicated deployments throw).
  • Impact: Confirmed reliability/data defect. The PR's premise "Nothing reads or writes it anymore" is false for the Node runtime — a scheduled, deployed Temporal worker both reads and writes the table on the exact branch this drop lands on. Running DROP TABLE IF EXISTS "posthog_emailreputationsnapshot" makes the next daily activity fail with relation "posthog_emailreputationsnapshot" does not exist, producing repeated Temporal retries/failures — and at minimum breaks the local dev CDP pipeline where the capability defaults on. This is the genuine blind spot (invisible to Python-only investigation), the inverse of the first finding which was a false positive because the Python endpoint had actually been rewritten. The drop must wait until this Node evaluator is decommissioned or repointed.
Issue description

Beyond the Python HogFlowViewSet.team_reputation endpoint, there is an entirely separate, cross-runtime consumer of posthog_emailreputationsnapshot: the Node.js Temporal worker EmailReputationService (nodejs/src/cdp/services/email-reputation/email-reputation.service.ts), registered as the email-reputation-evaluator capability (nodejs/src/common/config.ts:54, nodejs/src/capabilities.ts:203, nodejs/src/server.ts:417, and nodejs/src/cdp/services/email-reputation/temporal/email-reputation-worker.service.ts:216) — a fully wired, deployed service, not dead code. Its own docstring says it 'runs as Temporal activities' with 'each daily run' appending snapshot rows, and its code does both SELECT DISTINCT team_id FROM posthog_emailreputationsnapshot ... (line ~121) and INSERT INTO posthog_emailreputationsnapshot ... ON CONFLICT DO NOTHING (line ~442). This means the PR's central claim — 'Nothing reads or writes it anymore' — is false in a second, independent way that is invisible to any Python-only investigation: a scheduled background job in a completely different language/runtime/deployment unit depends on this table. Running DROP TABLE IF EXISTS "posthog_emailreputationsnapshot"; while this evaluator is still deployed and scheduled would turn its next Temporal activity run into a hard Postgres error (relation "posthog_emailreputationsnapshot" does not exist), likely producing repeated Temporal activity retries/failures and metric/alerting noise on a completely separate service surface than the one already identified for the Django API endpoint.

Suggested fix

Before merging this drop, confirm the Node.js email-reputation-evaluator Temporal worker has also been decommissioned or repointed away from this table (not just the Python model/API usage). If this worker is still running in any environment, this migration must wait until it is disabled/removed, exactly as the safe-migrations guide's safety-window requirement intends — but the window needs to cover this Node/Temporal consumer specifically, which the PR description and its stacked prerequisites (#74762/#75237/#75249) never mention.

Prompt to fix with AI (copy-paste)
## Context
@products/workflows/backend/migrations/0018_drop_emailreputationsnapshot_table.py#L15-19

<issue_description>
Beyond the Python `HogFlowViewSet.team_reputation` endpoint, there is an entirely separate, cross-runtime consumer of `posthog_emailreputationsnapshot`: the Node.js Temporal worker `EmailReputationService` (nodejs/src/cdp/services/email-reputation/email-reputation.service.ts), registered as the `email-reputation-evaluator` capability (nodejs/src/common/config.ts:54, nodejs/src/capabilities.ts:203, nodejs/src/server.ts:417, and nodejs/src/cdp/services/email-reputation/temporal/email-reputation-worker.service.ts:216) — a fully wired, deployed service, not dead code. Its own docstring says it 'runs as Temporal activities' with 'each daily run' appending snapshot rows, and its code does both `SELECT DISTINCT team_id FROM posthog_emailreputationsnapshot ...` (line ~121) and `INSERT INTO posthog_emailreputationsnapshot ... ON CONFLICT DO NOTHING` (line ~442). This means the PR's central claim — 'Nothing reads or writes it anymore' — is false in a second, independent way that is invisible to any Python-only investigation: a scheduled background job in a completely different language/runtime/deployment unit depends on this table. Running `DROP TABLE IF EXISTS "posthog_emailreputationsnapshot";` while this evaluator is still deployed and scheduled would turn its next Temporal activity run into a hard Postgres error (`relation "posthog_emailreputationsnapshot" does not exist`), likely producing repeated Temporal activity retries/failures and metric/alerting noise on a completely separate service surface than the one already identified for the Django API endpoint.
</issue_description>

<issue_validation>
- **Checked:** The actual base branch this migration deploys onto and merges into (`posthog-code/ses-tenant-event-comms`, the stack tip), not local master. Verified the Node `email-reputation.service.ts`, its Temporal worker, and the capability/mode/server wiring directly via the GitHub API on that ref.
- **Found:** Unlike the Python side (model deleted, endpoint rewritten), the Node service still runs live SQL against the table on the base branch: `SELECT DISTINCT team_id FROM posthog_emailreputationsnapshot` (email-reputation.service.ts:121), a second `SELECT DISTINCT ON (team_id) ... FROM posthog_emailreputationsnapshot` (:418), and `INSERT INTO posthog_emailreputationsnapshot ... ON CONFLICT DO NOTHING` (:442). Nothing in the stack repointed or removed these.
- **Found:** It is wired and scheduled, not dead code — `EmailReputationWorkerService` is started in `server.ts:400-425` under `capabilities.emailReputationEvaluator`, the temporal worker registers a daily schedule (`scheduleId: EMAIL_REPUTATION_SCHEDULE_ID`, `spec: { calendars: [{ hour }] }`, email-reputation-worker.service.ts:118-135), and there is a dedicated deployment mode `PluginServerMode.email_reputation_evaluator = 'email-reputation-evaluator'` (config.ts:54). The server.ts comment "Dedicated deployments should crash and restart" confirms a dedicated production deployment path (dev merely rides along via `isDevEnv()` and degrades gracefully; dedicated deployments `throw`).
- **Impact:** Confirmed reliability/data defect. The PR's premise "Nothing reads or writes it anymore" is false for the Node runtime — a scheduled, deployed Temporal worker both reads and writes the table on the exact branch this drop lands on. Running `DROP TABLE IF EXISTS "posthog_emailreputationsnapshot"` makes the next daily activity fail with `relation "posthog_emailreputationsnapshot" does not exist`, producing repeated Temporal retries/failures — and at minimum breaks the local dev CDP pipeline where the capability defaults on. This is the genuine blind spot (invisible to Python-only investigation), the inverse of the first finding which was a false positive because the Python endpoint had actually been rewritten. The drop must wait until this Node evaluator is decommissioned or repointed.
</issue_validation>

## Task
Investigate the issue and solve it

<potential_solution>
Before merging this drop, confirm the Node.js `email-reputation-evaluator` Temporal worker has also been decommissioned or repointed away from this table (not just the Python model/API usage). If this worker is still running in any environment, this migration must wait until it is disabled/removed, exactly as the safe-migrations guide's safety-window requirement intends — but the window needs to cover this Node/Temporal consumer specifically, which the PR description and its stacked prerequisites (#74762/#75237/#75249) never mention.
</potential_solution>

]
2 changes: 1 addition & 1 deletion products/workflows/backend/migrations/max_migration.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0016_drop_emailreputationsnapshot_fk
0017_drop_emailreputationsnapshot_table
Loading