Skip to content

feat(upsampling) - Support upsampled error count with performance optimizations#8

Open
ShashankFC wants to merge 2 commits into
masterfrom
error-upsampling-race-condition
Open

feat(upsampling) - Support upsampled error count with performance optimizations#8
ShashankFC wants to merge 2 commits into
masterfrom
error-upsampling-race-condition

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Jan 5, 2026

Copy link
Copy Markdown
Collaborator

Test 3

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced error upsampling support for organization event statistics queries, improving accuracy of error metrics for eligible projects through sample-weighted counting.
    • Upsampling applies selectively based on project allowlisting and query type.
  • Tests

    • Added comprehensive test coverage for error upsampling across various project allowlist scenarios and event dataset types.

✏️ Tip: You can customize this high-level summary in your review settings.


Replicated from ai-code-review-evaluation/sentry-coderabbit#3

yuvmen and others added 2 commits July 25, 2025 09:48
…(#94376)

Part of the Error Upsampling project:
https://www.notion.so/sentry/Tech-Spec-Error-Up-Sampling-1e58b10e4b5d80af855cf3b992f75894?source=copy_link

Events-stats API will now check if all projects in the query are
allowlisted for upsampling, and convert the count query to a sum over
`sample_weight` in Snuba, this is done by defining a new SnQL function
`upsampled_count()`.

I noticed there are also eps() and epm() functions in use in this
endpoint. I considered (and even worked on) also supporting
swapping eps() and epm() which for correctness should probably also not
count naively and use `sample_weight`, but this
caused some complications and since they are only in use by specific
dashboard widgets and not available in discover
I decided to defer changing them until we realize it is needed.
- Add 60-second cache for upsampling eligibility checks to improve performance
- Separate upsampling eligibility check from query transformation for better optimization
- Remove unnecessary null checks in upsampled_count() function per schema requirements
- Add cache invalidation utilities for configuration management

This improves performance during high-traffic periods by avoiding repeated
expensive allowlist lookups while maintaining data consistency.
@AI-Code-Review-Evals AI-Code-Review-Evals deleted a comment from coderabbitai Bot Jan 6, 2026
@AI-Code-Review-Evals AI-Code-Review-Evals deleted a comment from coderabbitai Bot Jan 6, 2026
@ShashankFC

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 6, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jan 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This change introduces error upsampling functionality to Sentry's API infrastructure, including eligibility checks with caching, query column transformations, database support for upsampled aggregates, and comprehensive test coverage across multiple modules.

Changes

Cohort / File(s) Summary
Module Registration
pyproject.toml
Added public module path sentry.api.helpers.error_upsampling and test path tests.sentry.api.helpers.test_error_upsampling to Python packaging configuration
Submodule Update
sentry-repo
Updated submodule reference to commit a5d2909; no code changes
Core Error Upsampling Helper
src/sentry/api/helpers/error_upsampling.py
New module providing: is_errors_query_for_error_upsampled_projects() with 60-second caching, transform_query_columns_for_error_upsampling() for column transformation, invalidate_upsampling_cache() for cache invalidation, plus internal helpers for project allowlist checking and dataset-aware sample weight logic
Query Endpoint Integration
src/sentry/api/endpoints/organization_events_stats.py
Integrated error upsampling into _get_event_stats: added imports for upsampling functions, computed upsampling eligibility early, applied column transformations via final_columns before query execution across RPC and non-RPC paths, refined error message for topEvents validation
Database Query Function
src/sentry/search/events/datasets/discover.py
Added upsampled_count SnQLFunction to DiscoverDatasetConfig that sums sample_weight column and casts to Int64, supporting the upsampling column transformation
Test Utilities
src/sentry/testutils/factories.py
Added _set_sample_rate_from_error_sampling() helper to extract and apply sample rates from error sampling context during event normalization in store_event()
Error Upsampling Unit Tests
tests/sentry/api/helpers/test_error_upsampling.py
New test module covering project allowlist validation, column transformation logic, error-focused query detection, and sample weight applicability across datasets
Integration Tests
tests/snuba/api/endpoints/test_organization_events_stats.py
Added OrganizationEventsStatsErrorUpsamplingTest with four test methods validating upsampling behavior for allowlisted projects, partial allowlists, transaction events, and non-allowlisted projects

Sequence Diagram

sequenceDiagram
    participant Client
    participant Endpoint as organization_events_stats
    participant UpsamplingHelper as Error Upsampling
    participant Cache as Cache Layer
    participant SnubaEngine as Snuba Query Engine
    participant Database as ClickHouse

    Client->>Endpoint: GET /organization_events_stats<br/>(with projects & query)
    Endpoint->>UpsamplingHelper: is_errors_query_for_error_upsampled_projects<br/>(organization, projects)
    
    rect rgba(200, 220, 255, 0.3)
        Note over UpsamplingHelper,Cache: Eligibility Check with Cache
        UpsamplingHelper->>Cache: Get cached eligibility<br/>(org_id, project_ids)
        alt Cache Hit
            Cache-->>UpsamplingHelper: Return cached result
        else Cache Miss
            UpsamplingHelper->>UpsamplingHelper: Check project allowlist<br/>_are_all_projects_error_upsampled()
            UpsamplingHelper->>Cache: Store eligibility result
        end
    end
    
    UpsamplingHelper-->>Endpoint: should_upsample: bool
    
    rect rgba(200, 255, 200, 0.3)
        Note over Endpoint: Column Transformation
        alt should_upsample == true
            Endpoint->>Endpoint: transform_query_columns<br/>(count() → upsampled_count())
        else should_upsample == false
            Endpoint->>Endpoint: Keep original columns
        end
    end
    
    Endpoint->>SnubaEngine: Execute timeseries query<br/>(with final_columns)
    SnubaEngine->>Database: Execute aggregation<br/>(upsampled_count or count)
    Database-->>SnubaEngine: Return aggregated results
    SnubaEngine-->>Endpoint: Query results
    
    Endpoint-->>Client: HTTP 200 response<br/>(upsampled stats)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


🐰 A new feature hops into the fold,
With caches swift and columns bold,
Error upsampling takes its stage,
Count becomes upsampled on the page,
Allowlists gate the query's weight,
And tests ensure the flow's just great! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.78% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main objective of the PR: adding support for upsampled error counts with performance optimizations, which aligns with the code changes across multiple files implementing error upsampling logic and caching.
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI Agents
In @src/sentry/api/endpoints/organization_events_stats.py:
- Around line 218-227: The upsampling eligibility call inside _get_event_stats
incorrectly uses the outer closure dataset; change the call to
is_errors_query_for_error_upsampled_projects(..., scoped_dataset, request) so it
uses the function parameter scoped_dataset, and remove the redundant
upsampling_enabled assignment by using should_upsample directly everywhere (keep
final_columns = query_columns as-is); update references to use should_upsample
instead of upsampling_enabled and ensure the variable name is used consistently
when building the query.

In @src/sentry/api/helpers/error_upsampling.py:
- Around line 67-74: invalidate_upsampling_cache currently uses Python's
built-in hash() which is process-randomized; replace that with a stable hash
(e.g., compute a SHA256 hex of the sorted project_ids sequence) to build
cache_key so invalidation works across processes. Update the cache_key
construction in invalidate_upsampling_cache to sort project_ids, serialize them
deterministically (like joining as comma-separated ints or JSON), compute a
stable digest (hashlib.sha256(...).hexdigest()), and use that digest in the
f"error_upsampling_eligible:{organization_id}:{digest}" key; keep the rest of
the function logic unchanged.
- Line 27: The cache key construction using Python's builtin hash() (cache_key =
f"error_upsampling_eligible:{organization.id}:{hash(tuple(sorted(snuba_params.project_ids)))}")
is non-deterministic across processes; replace hash(...) with a stable digest
(e.g., compute a deterministic string of the sorted project_ids and run a stable
hash like hashlib.sha256(...).hexdigest()) so the key is consistent across
workers, and make the identical change in invalidate_upsampling_cache so both
creation and invalidation use the same deterministic key derivation.
🧹 Nitpick comments (4)
src/sentry/testutils/factories.py (1)

344-358: Consider more specific exception handling for better debuggability.

The function uses broad except Exception: pass blocks which could silently hide unexpected errors during test development. While defensive programming is reasonable for test utilities, consider at least logging exceptions or being more specific about what you're catching.

🔎 Suggested improvement
 def _set_sample_rate_from_error_sampling(normalized_data: MutableMapping[str, Any]) -> None:
     """Set 'sample_rate' on normalized_data if contexts.error_sampling.client_sample_rate is present and valid."""
     client_sample_rate = None
     try:
         client_sample_rate = (
             normalized_data.get("contexts", {}).get("error_sampling", {}).get("client_sample_rate")
         )
-    except Exception:
+    except (KeyError, AttributeError, TypeError):
         pass
     if client_sample_rate:
         try:
             normalized_data["sample_rate"] = float(client_sample_rate)
-        except Exception:
+        except (ValueError, TypeError):
             pass

This makes it clearer what errors are expected and helps catch unexpected issues during development.

tests/sentry/api/helpers/test_error_upsampling.py (1)

19-101: Good test coverage for helper functions, but consider testing the main entry point.

The tests provide comprehensive coverage of the private helper functions with good edge case handling (empty lists, case insensitivity, whitespace, different datasets). However, I notice that is_errors_query_for_error_upsampled_projects (the main public entry point) and invalidate_upsampling_cache are not tested.

Consider adding tests for:

  1. is_errors_query_for_error_upsampled_projects - The main function that integrates all the helpers and includes caching logic
  2. Cache behavior - Verify the 60-second cache works correctly and returns cached results
  3. invalidate_upsampling_cache - Ensure cache invalidation works as expected
Example test structure
@patch("sentry.api.helpers.error_upsampling.options")
def test_is_errors_query_with_caching(self, mock_options: Mock) -> None:
    """Test that caching reduces option lookups."""
    mock_options.get.return_value = self.project_ids
    
    # First call should check options
    result1 = is_errors_query_for_error_upsampled_projects(
        self.snuba_params, self.organization, errors, self.request
    )
    assert result1 is True
    assert mock_options.get.call_count == 1
    
    # Second call should use cache
    result2 = is_errors_query_for_error_upsampled_projects(
        self.snuba_params, self.organization, errors, self.request
    )
    assert result2 is True
    assert mock_options.get.call_count == 1  # No additional call

def test_invalidate_upsampling_cache(self) -> None:
    """Test cache invalidation."""
    # ... test implementation
src/sentry/api/helpers/error_upsampling.py (2)

43-64: Unused organization parameter and O(n) allowlist lookup

Two issues in this function:

  1. The organization parameter is unused (confirmed by static analysis). Either remove it or use it for future organization-scoped allowlist functionality.

  2. The allowlist lookup uses in operator on a list, which is O(n) per project. Convert to a set for O(1) lookups.

🔎 Proposed fix
 def _are_all_projects_error_upsampled(
-    project_ids: Sequence[int], organization: Organization
+    project_ids: Sequence[int],
 ) -> bool:
     """
     Check if ALL projects in the query are allowlisted for error upsampling.
     ...
     """
     if not project_ids:
         return False

     allowlist = options.get("issues.client_error_sampling.project_allowlist", [])
     if not allowlist:
         return False

+    allowlist_set = set(allowlist)
     # All projects must be in the allowlist
-    result = all(project_id in allowlist for project_id in project_ids)
+    result = all(project_id in allowlist_set for project_id in project_ids)
     return result

Note: Update the caller on line 35 to remove the organization argument if you remove the parameter.


130-140: Query parsing may miss valid error-focused queries

The current check only matches the exact substring event.type:error. This could miss:

  • Queries with spaces: event.type: error
  • Quoted values: event.type:"error"
  • Negated transaction queries: !event.type:transaction

The docstring mentions this is intentional to "err on the side of caution," which is reasonable for an initial implementation. Consider adding a comment or TODO to expand this in the future if needed.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a440b4 and 6ad6fe3.

📒 Files selected for processing (8)
  • pyproject.toml
  • sentry-repo
  • src/sentry/api/endpoints/organization_events_stats.py
  • src/sentry/api/helpers/error_upsampling.py
  • src/sentry/search/events/datasets/discover.py
  • src/sentry/testutils/factories.py
  • tests/sentry/api/helpers/test_error_upsampling.py
  • tests/snuba/api/endpoints/test_organization_events_stats.py
🧰 Additional context used
🧬 Code graph analysis (5)
src/sentry/search/events/datasets/discover.py (1)
src/sentry/search/events/fields.py (2)
  • SnQLFunction (2179-2203)
  • args (1372-1373)
tests/snuba/api/endpoints/test_organization_events_stats.py (1)
src/sentry/api/endpoints/organization_events_stats.py (1)
  • get (105-524)
src/sentry/api/endpoints/organization_events_stats.py (2)
src/sentry/api/helpers/error_upsampling.py (2)
  • is_errors_query_for_error_upsampled_projects (13-40)
  • transform_query_columns_for_error_upsampling (77-100)
src/sentry/snuba/rpc_dataset_common.py (1)
  • run_top_events_timeseries_query (484-641)
src/sentry/api/helpers/error_upsampling.py (1)
src/sentry/api/endpoints/organization_events_stats.py (1)
  • get (105-524)
tests/sentry/api/helpers/test_error_upsampling.py (1)
src/sentry/api/helpers/error_upsampling.py (4)
  • _are_all_projects_error_upsampled (43-64)
  • _is_error_focused_query (130-140)
  • _should_apply_sample_weight_transform (103-127)
  • transform_query_columns_for_error_upsampling (77-100)
🪛 Ruff (0.14.10)
src/sentry/testutils/factories.py

351-352: try-except-pass detected, consider logging the exception

(S110)


351-351: Do not catch blind exception: Exception

(BLE001)


356-357: try-except-pass detected, consider logging the exception

(S110)


356-356: Do not catch blind exception: Exception

(BLE001)

src/sentry/search/events/datasets/discover.py

1046-1046: Unused lambda argument: args

(ARG005)

src/sentry/api/helpers/error_upsampling.py

44-44: Unused function argument: organization

(ARG001)

🔇 Additional comments (8)
sentry-repo (1)

1-1: Verify the relevance of this submodule update to the PR objectives.

This submodule reference shows changes related to OTLP span links processing, which appears unrelated to the PR's stated objective of adding error upsampling support. The commit message mentions "feat(otlp): Write span links to EAP as a JSON-encoded string attribute" which has no connection to error sampling or upsampling functionality.

Please confirm whether this submodule update should be part of this PR, or if it was included unintentionally.

pyproject.toml (1)

176-176: LGTM! Module registration is correct.

The new error upsampling module and its tests are properly registered in the project configuration. Including them in the "stronger typing" section ensures stricter type checking, which is a good practice for new code.

Also applies to: 464-464

src/sentry/testutils/factories.py (1)

1049-1049: LGTM! Proper integration of sample rate extraction.

The function is called at the right point in the event normalization flow, ensuring that sample rates from error sampling contexts are properly propagated to the normalized event data.

src/sentry/search/events/datasets/discover.py (1)

1041-1052: Schema assumption for sample_weight is verified and properly enforced.

The implementation correctly relies on the database schema guarantee that sample_weight exists for all events in allowlisted projects. This assumption is documented in src/sentry/api/helpers/error_upsampling.py (lines 85-86) and enforced through the _are_all_projects_error_upsampled() validation function, which ensures upsampling only applies when all projects in the query are allowlisted. Null handling is intentionally omitted because the schema contract guarantees non-null values for allowlisted projects—unlike fields like issue.id which require defensive coalesce() handling.

The unused args parameter is required by the snql_aggregate lambda signature pattern and is consistent with all other aggregate functions in this file.

src/sentry/api/endpoints/organization_events_stats.py (1)

14-17: LGTM!

The new imports for error upsampling helpers are correctly placed and follow the existing import organization pattern.

tests/snuba/api/endpoints/test_organization_events_stats.py (1)

3554-3603: LGTM - Good test coverage for error upsampling scenarios.

The test class is well-structured with clear setup and covers the key scenarios:

  • Fully allowlisted projects (upsampling applied)
  • Partially allowlisted projects (upsampling not applied)
  • Transaction events (upsampling not applied)
  • No allowlisted projects (upsampling not applied)

The test data correctly uses client_sample_rate: 0.1 which should result in a 10x multiplier when upsampling is applied.

src/sentry/api/helpers/error_upsampling.py (2)

77-100: LGTM!

The column transformation logic is clean and handles the count()upsampled_count() as count mapping correctly. The alias preservation ensures downstream code continues to work with the expected column name.


103-127: LGTM!

The dataset-based decision logic is well-structured. Using lazy imports inside the function avoids circular import issues while maintaining clean separation of concerns.

Comment on lines +218 to +227
# Early upsampling eligibility check for performance optimization
# This cached result ensures consistent behavior across query execution
should_upsample = is_errors_query_for_error_upsampled_projects(
snuba_params, organization, dataset, request
)

# Store the upsampling decision to apply later during query building
# This separation allows for better query optimization and caching
upsampling_enabled = should_upsample
final_columns = query_columns

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify how _get_event_stats is called with different scoped_dataset values
rg -n -B2 -A5 '_get_event_stats\(' src/sentry/api/endpoints/organization_events_stats.py | head -80

Repository: AI-Code-Review-Evals/coderabbit-sentry

Length of output: 2975


🏁 Script executed:

# Get the full _get_event_stats function definition and context
sed -n '200,300p' src/sentry/api/endpoints/organization_events_stats.py

Repository: AI-Code-Review-Evals/coderabbit-sentry

Length of output: 5040


🏁 Script executed:

# Check the widget splitting logic that calls _get_event_stats with different datasets
sed -n '360,400p' src/sentry/api/endpoints/organization_events_stats.py

Repository: AI-Code-Review-Evals/coderabbit-sentry

Length of output: 2132


🏁 Script executed:

# Check the parent function and outer dataset variable
sed -n '150,210p' src/sentry/api/endpoints/organization_events_stats.py

Repository: AI-Code-Review-Evals/coderabbit-sentry

Length of output: 2610


Use scoped_dataset parameter instead of outer dataset for upsampling eligibility check

The eligibility check on line 221 uses dataset from the outer closure scope rather than scoped_dataset which is passed as a parameter. Since _get_event_stats is called with different datasets (e.g., discover for errors-only queries at line 386-387, or split datasets at line 372-373), the upsampling decision will be based on the original outer dataset instead of the actual dataset being queried.

Additionally, the variable assignment on line 226 (upsampling_enabled = should_upsample) is redundant—use should_upsample directly throughout the function.

🤖 Prompt for AI Agents
In @src/sentry/api/endpoints/organization_events_stats.py around lines 218 -
227, The upsampling eligibility call inside _get_event_stats incorrectly uses
the outer closure dataset; change the call to
is_errors_query_for_error_upsampled_projects(..., scoped_dataset, request) so it
uses the function parameter scoped_dataset, and remove the redundant
upsampling_enabled assignment by using should_upsample directly everywhere (keep
final_columns = query_columns as-is); update references to use should_upsample
instead of upsampling_enabled and ensure the variable name is used consistently
when building the query.

expensive repeated option lookups during high-traffic periods. This is safe
because allowlist changes are infrequent and eventual consistency is acceptable.
"""
cache_key = f"error_upsampling_eligible:{organization.id}:{hash(tuple(sorted(snuba_params.project_ids)))}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: hash() produces non-deterministic results across processes

Python's built-in hash() function is randomized by default via PYTHONHASHSEED. This means the cache key will be different across different worker processes, causing:

  1. Cache misses on every request to a different worker
  2. invalidate_upsampling_cache() will fail to invalidate entries created by other workers
  3. Potential inconsistent behavior during rolling deployments

Use a deterministic hashing approach instead:

🔎 Proposed fix using a stable hash
+import hashlib
+
+
+def _stable_project_ids_hash(project_ids: Sequence[int]) -> str:
+    """Generate a stable hash for project IDs that is consistent across processes."""
+    sorted_ids = sorted(project_ids)
+    ids_str = ",".join(str(pid) for pid in sorted_ids)
+    return hashlib.sha256(ids_str.encode()).hexdigest()[:16]
+

 def is_errors_query_for_error_upsampled_projects(
     snuba_params: SnubaParams,
     organization: Organization,
     dataset: ModuleType,
     request: Request,
 ) -> bool:
     ...
-    cache_key = f"error_upsampling_eligible:{organization.id}:{hash(tuple(sorted(snuba_params.project_ids)))}"
+    cache_key = f"error_upsampling_eligible:{organization.id}:{_stable_project_ids_hash(snuba_params.project_ids)}"

Apply the same fix to invalidate_upsampling_cache.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cache_key = f"error_upsampling_eligible:{organization.id}:{hash(tuple(sorted(snuba_params.project_ids)))}"
import hashlib
def _stable_project_ids_hash(project_ids: Sequence[int]) -> str:
"""Generate a stable hash for project IDs that is consistent across processes."""
sorted_ids = sorted(project_ids)
ids_str = ",".join(str(pid) for pid in sorted_ids)
return hashlib.sha256(ids_str.encode()).hexdigest()[:16]
def is_errors_query_for_error_upsampled_projects(
snuba_params: SnubaParams,
organization: Organization,
dataset: ModuleType,
request: Request,
) -> bool:
...
cache_key = f"error_upsampling_eligible:{organization.id}:{_stable_project_ids_hash(snuba_params.project_ids)}"
🤖 Prompt for AI Agents
In @src/sentry/api/helpers/error_upsampling.py at line 27, The cache key
construction using Python's builtin hash() (cache_key =
f"error_upsampling_eligible:{organization.id}:{hash(tuple(sorted(snuba_params.project_ids)))}")
is non-deterministic across processes; replace hash(...) with a stable digest
(e.g., compute a deterministic string of the sorted project_ids and run a stable
hash like hashlib.sha256(...).hexdigest()) so the key is consistent across
workers, and make the identical change in invalidate_upsampling_cache so both
creation and invalidation use the same deterministic key derivation.

Comment on lines +67 to +74
def invalidate_upsampling_cache(organization_id: int, project_ids: Sequence[int]) -> None:
"""
Invalidate the upsampling eligibility cache for the given organization and projects.
This should be called when the allowlist configuration changes to ensure
cache consistency across the system.
"""
cache_key = f"error_upsampling_eligible:{organization_id}:{hash(tuple(sorted(project_ids)))}"
cache.delete(cache_key)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Cache invalidation uses the same non-deterministic hash

This function has the same hash() issue as the main eligibility check. The invalidation will only work within the same process that created the cache entry.

Apply the same stable hashing fix proposed above.

🤖 Prompt for AI Agents
In @src/sentry/api/helpers/error_upsampling.py around lines 67 - 74,
invalidate_upsampling_cache currently uses Python's built-in hash() which is
process-randomized; replace that with a stable hash (e.g., compute a SHA256 hex
of the sorted project_ids sequence) to build cache_key so invalidation works
across processes. Update the cache_key construction in
invalidate_upsampling_cache to sort project_ids, serialize them
deterministically (like joining as comma-separated ints or JSON), compute a
stable digest (hashlib.sha256(...).hexdigest()), and use that digest in the
f"error_upsampling_eligible:{organization_id}:{digest}" key; keep the rest of
the function logic unchanged.

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.

2 participants