Skip to content

feat(seer): Add dual-write for Seer project preferences to ProjectOptions and SeerProjectRepository#110704

Draft
srest2021 wants to merge 16 commits intomasterfrom
srest2021/AIML-2605
Draft

feat(seer): Add dual-write for Seer project preferences to ProjectOptions and SeerProjectRepository#110704
srest2021 wants to merge 16 commits intomasterfrom
srest2021/AIML-2605

Conversation

@srest2021
Copy link
Member

@srest2021 srest2021 commented Mar 13, 2026

relates to AIML-2605

Adds dual-write so that all Seer project preference writes also persist to Sentry's DB (ProjectOption keys + SeerProjectRepository rows), gated behind the organizations:seer-project-settings-dual-write feature flag. This is Phase 1 Step 4 of the Seer preferences migration — once reads are cut over in later phases, the Seer API calls get removed and these DB writes become the primary path.

We only write to DB if the Seer call succeeded.

srest2021 and others added 3 commits March 13, 2026 13:43
Phase 1 step 4 of the Seer preference migration. After every
successful write to Seer's preference endpoints, also write the
same data to Sentry's DB (ProjectOption keys + SeerProjectRepository
rows) when the organizations:seer-project-settings-dual-write
feature flag is enabled. If the Sentry write fails, log and
continue — Seer remains the read source.

Write sites updated:
- set_project_seer_preference (single project)
- bulk_set_project_preferences (bulk)
- ProjectSeerPreferencesEndpoint.post (direct endpoint)

Also registers the feature flag, adds preference default constants
to constants.py, and wires them into projectoptions/defaults.py.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Replace dict access with typed SeerProjectPreference attributes in
write_preference_project_options and _dual_write_preference_to_sentry_db.
Move inline imports to top-level.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
@linear-code
Copy link

linear-code bot commented Mar 13, 2026

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Mar 13, 2026
Remove four single-use constants for seer automation handoff defaults
and inline their values directly in the register() calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SeerProjectRepositoryBranchOverride.objects.bulk_create(overrides_to_create)


def write_preference_to_sentry_db(project: Project, preference: SeerProjectPreference) -> None:
Copy link
Member Author

@srest2021 srest2021 Mar 13, 2026

Choose a reason for hiding this comment

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

"to_sentry_db" is not the best naming but I think this can get cleaned up after we remove the old write paths. Otherwise it will get too confusing having many seer project preference functions with similar names doing different things.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 13, 2026

Backend Test Failures

Failures on f2b661c in this run:

tests/sentry/seer/autofix/test_autofix_utils.py::TestSetProjectSeerPreference::test_set_project_seer_preference_http_error_raiseslog
tests/sentry/seer/autofix/test_autofix_utils.py:664: in test_set_project_seer_preference_http_error_raises
    set_project_seer_preference(preference)
E   TypeError: set_project_seer_preference() missing 2 required positional arguments: 'organization' and 'project'
tests/sentry/autofix/test_utils.py::TestGetRepoFromCodeMappings::test_get_repos_from_project_code_mappings_with_datalog
tests/sentry/autofix/test_utils.py:48: in test_get_repos_from_project_code_mappings_with_data
    assert repos == expected_repos
E   AssertionError: assert [{'external_i...8630528, ...}] == [{'external_i...8630528, ...}]
E     
E     At index 0 diff: {'repository_id': 8, 'organization_id': 4557787748630528, 'integration_id': '234', 'provider': 'github', 'owner': 'getsentry', 'name': 'sentry', 'external_id': '123'} != {'integration_id': '234', 'organization_id': 4557787748630528, 'provider': 'github', 'owner': 'getsentry', 'name': 'sentry', 'external_id': '123'}
E     
E     Full diff:
E       [
E           {
E               'external_id': '123',
E               'integration_id': '234',
E               'name': 'sentry',
E               'organization_id': 4557787748630528,
E               'owner': 'getsentry',
E               'provider': 'github',
E     +         'repository_id': 8,
E           },
E       ]
tests/sentry/seer/autofix/test_autofix_utils.py::TestSetProjectSeerPreference::test_set_project_seer_preference_with_open_pr_stopping_pointlog
tests/sentry/seer/autofix/test_autofix_utils.py:643: in test_set_project_seer_preference_with_open_pr_stopping_point
    set_project_seer_preference(preference)
E   TypeError: set_project_seer_preference() missing 2 required positional arguments: 'organization' and 'project'

@github-actions
Copy link
Contributor

Backend Test Failures

Failures on 9f2a955 in this run:

tests/sentry/autofix/test_utils.py::TestGetRepoFromCodeMappings::test_get_repos_from_project_code_mappings_with_datalog
tests/sentry/autofix/test_utils.py:48: in test_get_repos_from_project_code_mappings_with_data
    assert repos == expected_repos
E   AssertionError: assert [{'external_i...3966336, ...}] == [{'external_i...3966336, ...}]
E     
E     At index 0 diff: {'repository_id': 8, 'organization_id': 4557787933966336, 'integration_id': '234', 'provider': 'github', 'owner': 'getsentry', 'name': 'sentry', 'external_id': '123'} != {'integration_id': '234', 'organization_id': 4557787933966336, 'provider': 'github', 'owner': 'getsentry', 'name': 'sentry', 'external_id': '123'}
E     
E     Full diff:
E       [
E           {
E               'external_id': '123',
E               'integration_id': '234',
E               'name': 'sentry',
E               'organization_id': 4557787933966336,
E               'owner': 'getsentry',
E               'provider': 'github',
E     +         'repository_id': 8,
E           },
E       ]

Separate the dual-write logic from set_project_seer_preference and
bulk_set_project_preferences so each callsite controls its own
feature-flagged write. This avoids re-fetching projects that callers
already have and makes the Seer API helpers single-responsibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

Backend Test Failures

Failures on 6c79842 in this run:

tests/sentry/autofix/test_utils.py::TestGetRepoFromCodeMappings::test_get_repos_from_project_code_mappings_with_datalog
tests/sentry/autofix/test_utils.py:48: in test_get_repos_from_project_code_mappings_with_data
    assert repos == expected_repos
E   AssertionError: assert [{'external_i...7143040, ...}] == [{'external_i...7143040, ...}]
E     
E     At index 0 diff: {'repository_id': 8, 'organization_id': 4557787997143040, 'integration_id': '234', 'provider': 'github', 'owner': 'getsentry', 'name': 'sentry', 'external_id': '123'} != {'integration_id': '234', 'organization_id': 4557787997143040, 'provider': 'github', 'owner': 'getsentry', 'name': 'sentry', 'external_id': '123'}
E     
E     Full diff:
E       [
E           {
E               'external_id': '123',
E               'integration_id': '234',
E               'name': 'sentry',
E               'organization_id': 4557787997143040,
E               'owner': 'getsentry',
E               'provider': 'github',
E     +         'repository_id': 8,
E           },
E       ]

if repo is None:
return Response({"detail": "Invalid repository"}, status=400)

repo_data["repository_id"] = repo.id
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure how to handle the edge case where there is more than one repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant