Repository navigation
Conversation
An accepted send can carry hogFlowVersion, the version of the template's workflow Django graded the hash against. When the cached workflow is older, cdp-api reads it from the primary. When the primary is older too, it answers 503 workflow_version_behind and queues nothing. The version stays out of the idempotency fingerprint, so a retry graded after an upload still replays. Without the field, the send behaves as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hogflow worker reads the job's workflow from the primary when its cached copy is older than the version the accepted send carries, and fails the job when the primary is older too, so the worker never sends older content than Django graded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Concurrent sends that found an older cached workflow each read the primary, and the fresh copy was never cached, so the next send read the replica and the primary again. Primary reads are now shared per workflow and the result goes into the cache. A later replica load keeps a newer cached version. A send that joins a read which cannot meet its graded version reads the primary once more. The worker logs a version-behind job with both versions and counts it on cdp_hogflow_worker_jobs_failed_total, and the cdp-api refusal carries code workflow_version_behind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The workflow_version_behind sub-code stays in logs, the outcome label and the cdp-api-to-Django body. Django maps it onto its existing public 503. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Part of the code-first rebuild of the email send stack; new in the rebuild, with no closed PR to replace: a template send delivers the content Django graded (decision D3 on map #461).
Problem
send/email/right after an upload can deliver the previous content, while Django answered that the caller'stemplate.hashis current.Impact
Pays into Grow Workflows through billed
workflows_emailsactions per paying org: a developer who uploads a template and sends it at once gets the new content, so the first send from code is right. In scope: CDP email sends.Merge Danger
Warning
🟡 Merge risk: medium · two-way door: a revert sends from the cached copy again, as today
state.send.hogFlowVersionis set, which only Django from feat(messaging): send the workflow version a template send graded #585 sets, behindworkflows-email-from-code.Changes
hogFlowVersion. When the cached copy is older, cdp-api and the hogflow worker read the workflow from the primary.503 delivery_unavailable, and puts the version lag indetailandwhy(feat(messaging): send the workflow version a template send graded #585).workflow_version_behindappears only in the body cdp-api sends to Django, in the metric label and in the log. No public answer carries it.workflow_version_behindcount. Versions only go up, so this state should not happen.version. An upload that lands between the grade and the send delivers the newer content. It is never older.How did you test this code?
Test seams: cdp-api's accepted-send route over HTTP (
cdp-api.serial.test.ts),processInvocationson the hogflow worker, and the hogflow manager service for the read count.Test rationale, each run red first:
{error}shape of cdp-api's other 503s.Proofs: live runs saved with
fleet proof, curl into maildev. A lost reload is simulated in SQL: the workflow's version and subject change with no reload published.2bdc74b, Django #5854d76e98)replayed: true, one email in totalThe read count comes from cdp-api's Postgres query metric for primary reads of workflows. It rose by 1 across the five sends.
This head,
ae46ab4, differs from2bdc74bonly by the 503 body. No case above reaches that 503.Each mixed window delivers the cached copy, which is today's behavior: the older side drops or ignores the version.
Prod shape: a gap until read-only production access exists on 2026-11-02.
CI: the fork has no Depot runners, so those jobs never start, and no secret for the project-board app, so
flags-project-boardfails; the other GitHub-hosted checks pass. The lane check passes: against master the PR changes onlynodejs/.Release status
Docs update
None.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Claude Opus 5.5 (claude-opus-5-5)
srv2of map Map: send code-managed email templates with one PostHog call #461, for decision D3.tdd,writing-tests,pr-description,writing-pr-descriptions.delivery_unavailablewith the version lag indetailandwhy; replaces a new publicworkflow_version_behindcodeInvolvement: one decision from Michael and one from the map's conductor; review round 1 (GPT-6.1 Sol, Opus fresh eyes) fixed, then final reads by GPT-6.1 Sol; live and cross-version proof captured.
🤖 Generated with Claude Code