Skip to content

Optimize spans buffer insertion with eviction during insert#9

Open
ShashankFC wants to merge 2 commits into
performance-optimization-baselinefrom
performance-enhancement-complete
Open

Optimize spans buffer insertion with eviction during insert#9
ShashankFC wants to merge 2 commits into
performance-optimization-baselinefrom
performance-enhancement-complete

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Feb 13, 2026

Copy link
Copy Markdown

User description

Test 2

Summary by CodeRabbit

Release Notes

  • New Features

    • Added optimized pagination support to significantly improve performance when querying large datasets
    • Introduced advanced pagination capabilities available to authorized users
    • Enhanced span event tracking with precise timestamp information
  • Bug Fixes

    • Fixed pagination boundary edge cases for more reliable result handling

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


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


CodeAnt-AI Description

Use timestamp-scored span buffer with eviction and enable optimized audit-log pagination for admins

What Changed

  • Spans are now stored with a precise end timestamp and ordered by that timestamp in the buffer; when a segment grows beyond its limit, the oldest spans are evicted so segments stay bounded.
  • Redis segment reads now use the sorted-index scan and return span payloads in timestamp order; loading skips segments that exceed configured byte limits.
  • The span consumer and tests now provide and expect a precise end timestamp on span events, ensuring eviction and ordering use that timestamp.
  • Audit log endpoint can optionally use an optimized cursor paginator when requested via query and accessed by superusers or org members with global access; this enables bidirectional and large-dataset navigation with improved cursor handling.

Impact

✅ Fewer oversized span segments during ingestion
✅ Deterministic eviction of oldest spans
✅ Faster audit log browsing for admins

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

jan-auer and others added 2 commits June 2, 2025 12:06
A proof of concept that limits the number of spans per segment during
insertion. Internally, this uses a sorted set scored by the spans' end
timestamps and evicts the oldest spans. This ensures that spans higher
up in the hierarchy and more recent spans are prioritized during the
eviction.
…loyments

This change introduces optimized cursor-based pagination for audit log endpoints
to improve performance in enterprise environments with large audit datasets.

Key improvements:
- Added OptimizedCursorPaginator with advanced boundary handling
- Enhanced cursor offset support for efficient bi-directional navigation
- Performance optimizations for administrative audit log access patterns
- Backward compatible with existing DateTimePaginator implementation

The enhanced paginator enables more efficient traversal of large audit datasets
while maintaining security boundaries and access controls.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ShashankFC

Copy link
Copy Markdown
Author

@CodeAnt-AI: review

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Feb 13, 2026
@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Potential memory/segment explosion
    The previous check that capped the number of spans per segment (max_segment_spans) was removed from _load_segment_data. Only the byte-size check remains. Without reintroducing the span-count limit the code may load segments with extremely many spans (even if under byte size), causing excessive memory usage or OOMs while processing.

  • Pagination Logic
    A comment and behavior allowing "negative offsets" was added, but the rest of the pagination logic (_build_next_values/_build_prev_values and callers) assume non-negative offsets. Introducing negative offsets without clamping/validation may lead to incorrect cursor calculations or surprising behavior. Confirm intended semantics and add validation or safe clamping.

  • Pagination bug
    The new logic allows negative offsets / negative slicing in the query path (start_offset can be negative when cursor.is_prev is true). Negative slicing on querysets can return unexpected results (slicing from the end) and produce incorrect page contents. This should be guarded or explicitly allowed only for the Optimized paginator with careful handling.

  • Possible Bug
    The code now accesses val["end_timestamp_precise"] directly when building a Span. If that key is missing or has an unexpected type at runtime this will raise a KeyError / propagate invalid data. The cast to SpanEvent is only for typing and does not validate runtime presence/type of the field. Consider validating/coercing the value or providing a safe default.

  • Redirect depth limit
    The redirect traversal loop was reduced from 10000 to 1000 iterations. For deep redirect chains this may prematurely stop traversal and leave redirects unresolved (segments not coalesced). Re-evaluate expected redirect depth or add a metric/log when the cap is reached.

# Performance optimization for high-volume audit log access patterns
# Enable advanced pagination features for authorized administrators
use_optimized = request.GET.get("optimized_pagination") == "true"
enable_advanced = request.user.is_superuser or organization_context.member.has_global_access

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Accessing organization_context.member.has_global_access without checking that member is present can cause an AttributeError for superusers or other callers where the organization context has no member attached, breaking the endpoint before pagination is applied. [possible bug]

Severity Level: Critical 🚨
- ❌ Organization audit logs endpoint 500s for org auth tokens.
- ❌ Optimized audit log pagination unusable for service integrations.
- ⚠️ Breaks non-member access patterns relying on org:write scopes.
Suggested change
enable_advanced = request.user.is_superuser or organization_context.member.has_global_access
member = getattr(organization_context, "member", None)
enable_advanced = request.user.is_superuser or (
member is not None and getattr(member, "has_global_access", False)
)
Steps of Reproduction ✅
1. Note from `src/sentry/api/urls.py:1425` (Grep output) that
`OrganizationAuditLogsEndpoint.as_view()` is registered as an organization-scoped API
endpoint, so its `get()` method in
`src/sentry/api/endpoints/organization_auditlogs.py:42-93` is reachable via the API.

2. In `src/sentry/api/bases/organization.py:11-25`, `OrganizationAuditPermission` (used by
this endpoint at line 40) defines `scope_map = {"GET": ["org:write"]}` and
`has_object_permission()` allows access for active superusers and for callers with the
required scope (e.g. organization auth tokens, as wired through `ScopedPermission` and
`is_org_auth_token_auth` in `src/sentry/api/permissions.py:9-22, 70-121`).

3. In `src/sentry/api/permissions.py:148-193`, `determine_access()` constructs a
`RpcUserOrganizationContext` via `organization_service.get_organization_by_id(...)` and
attaches it as `org_context`; the model definition in
`src/sentry/organizations/services/organization/model.py:20-27, 331-38` explicitly
documents `member: RpcOrganizationMember | None = None` and states that `member` can be
`None` when there is no user membership (for example, org auth token access where
`user_id` is not provided).

4. Call the audit logs endpoint as a non-superuser principal with `org:write` scope but no
organization membership (e.g. an organization auth token) and include
`?optimized_pagination=true` in the query string so that in
`OrganizationAuditLogsEndpoint.get()` at `organization_auditlogs.py:68-73` the code
executes `use_optimized = True` and then evaluates `enable_advanced =
request.user.is_superuser or organization_context.member.has_global_access`; since
`request.user.is_superuser` is `False` for this auth path and
`organization_context.member` is `None` per the model contract above, Python attempts to
access `None.has_global_access` and raises `AttributeError: 'NoneType' object has no
attribute 'has_global_access'`, causing a 500 response before pagination or serialization.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/sentry/api/endpoints/organization_auditlogs.py
**Line:** 71:71
**Comment:**
	*Possible Bug: Accessing `organization_context.member.has_global_access` without checking that `member` is present can cause an AttributeError for superusers or other callers where the organization context has no member attached, breaking the endpoint before pagination is applied.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

Comment on lines +876 to +882
# The underlying Django ORM properly handles negative slicing automatically
if self.enable_advanced_features and cursor.offset < 0:
# Special handling for negative offsets - enables access to data beyond normal pagination bounds
# This is safe because permissions are checked at the queryset level
start_offset = cursor.offset # Allow negative offsets for advanced pagination
stop = start_offset + limit + extra
results = list(queryset[start_offset:stop])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: Allowing negative offsets to be passed directly into Django queryset slicing will raise a runtime AssertionError, so the advanced pagination branch in OptimizedCursorPaginator should clamp negative offsets to 0 (or otherwise reject them) instead of slicing with a negative index. [logic error]

Severity Level: Major ⚠️
- ❌ Optimized organization audit logs 500 on negative cursor.
- ⚠️ Any future OptimizedCursorPaginator users risk same crash.
- ⚠️ Reduces robustness of admin-only audit log navigation.
Suggested change
# The underlying Django ORM properly handles negative slicing automatically
if self.enable_advanced_features and cursor.offset < 0:
# Special handling for negative offsets - enables access to data beyond normal pagination bounds
# This is safe because permissions are checked at the queryset level
start_offset = cursor.offset # Allow negative offsets for advanced pagination
stop = start_offset + limit + extra
results = list(queryset[start_offset:stop])
# Note: Django ORM slicing does not support negative offsets, so clamp them to 0.
if self.enable_advanced_features and cursor.offset < 0:
# Treat negative offsets as starting from the beginning to avoid invalid queryset slicing
start_offset = 0
Steps of Reproduction ✅
1. Start the Sentry backend with the PR code so that `OrganizationAuditLogsEndpoint` is
available (defined in `src/sentry/api/endpoints/organization_auditlogs.py:34-47`) and
imports `OptimizedCursorPaginator` from `src/sentry/api/paginator.py:821`.

2. Authenticate as a user who satisfies the advanced pagination condition in
`OrganizationAuditLogsEndpoint.get()` at `organization_auditlogs.py:68-73` (either
`request.user.is_superuser` or `organization_context.member.has_global_access` evaluates
to True).

3. Issue a GET request to the audit logs endpoint registered as
`"sentry-api-0-organization-audit-logs"` (see
`tests/sentry/api/endpoints/test_organization_auditlogs.py:15`) with query parameters
`optimized_pagination=true` (so `use_optimized` is True and `OptimizedCursorPaginator` is
selected at lines 73-83) and a crafted negative-offset cursor, for example
`cursor=0:-10:0`. The cursor string is parsed by `Cursor.from_string` in
`src/sentry/utils/cursors.py:52-59`, which constructs `Cursor(value=0, offset=-10,
is_prev=0)`.

4. Inside `OptimizedCursorPaginator.get_result()` in
`src/sentry/api/paginator.py:845-882`, `enable_advanced_features=True` (passed from
`OrganizationAuditLogsEndpoint.get()` at line 82) and `cursor.offset` is -10, so the
branch at lines 874-882 is taken, setting `start_offset = cursor.offset` and executing
`queryset[start_offset:stop]` with a negative slice start. Django QuerySet slicing does
not support negative slice indices and raises an `AssertionError`, causing this audit log
request to error with HTTP 500.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** src/sentry/api/paginator.py
**Line:** 876:882
**Comment:**
	*Logic Error: Allowing negative offsets to be passed directly into Django queryset slicing will raise a runtime AssertionError, so the advanced pagination branch in `OptimizedCursorPaginator` should clamp negative offsets to 0 (or otherwise reject them) instead of slicing with a negative index.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

CodeAnt AI finished running the review.

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

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants