Hi there 👋
| 🌥️ Tech |
🌤️ Feat |
| 4 high, 7 medium |
1 mitigated |
This MR covers two distinct areas. The span-buffer changes migrate segment storage from Redis sets to sorted sets and introduce eviction-during-insert via a Lua zpopmin when the segment exceeds 1000 spans. The plumbing is internally consistent, but the eviction mechanism is score-ordered and ignores the span tree, so it can evict parent/root spans and silently produce corrupt segments; there is also no test coverage for the new eviction path, existing set-typed Redis keys are not migrated (WRONGTYPE risk), and end_timestamp_precise is read non-defensively. The pagination changes add an OptimizedCursorPaginator that crashes on the audit-log endpoint's -datetime order key (numeric-vs-datetime mismatches), a broken negative-offset branch that relies on unsupported Django negative slicing, and a None-dereference on organization_context.member. Note that the review is partial: the pagination area was produced from a prose pass and may be less verified than the span-buffer area.
These might need a close look
- 🔴 src/sentry/api/endpoints/organization_auditlogs.py (L79)
OptimizedCursorPaginator crashes on the audit-log '-datetime' order key: it is a copy of the datetime-agnostic Paginator, so get_item_key uses math.floor/ceil on datetime objects (TypeError) and value_from_cursor returns a numeric cursor compared against the datetime column (type mismatch). The endpoint previously used DateTimePaginator for this reason. Make the paginator datetime-aware or don't wire it to this endpoint.
- 🔴 src/sentry/api/paginator.py (L877)
Negative-offset 'advanced feature' uses Django negative slicing, which the ORM does not support (ValueError). The branch is effectively dead since cursors never produce negative offsets; when triggered via a hand-crafted cursor it 500s. Remove the branch and enable_advanced_features flag or implement correctly.
- 🔴 src/sentry/api/endpoints/organization_auditlogs.py (L71)
organization_context.member is None-able and dereferenced unconditionally on every audit-log request, causing AttributeError/500 for non-superusers without a member. Guard with 'is not None'.
- 🔴 src/sentry/spans/buffer.py (L197)
Eviction via zpopmin removes lowest-score spans by end_timestamp_precise, ignoring tree structure, so parent/root spans can be evicted while children remain, producing broken segments (missing parent_span_id or no root). Evicted spans are silently dropped without metric. Make eviction tree-aware and add an incr metric.
Worth checking
- 🟡 src/sentry/api/paginator.py (L182)
Unconditional modification of shared BasePaginator.get_result affects 128 dependents with asymmetric clamping for forward vs prev cursors; offsets are never negative under normal construction, so this is mostly a no-op but an untested behavior-changing edit. Move it to the new subclass or add explicit tests.
- 🟡 src/sentry/utils/cursors.py (L26)
Comment-only change claims 'Allow negative offsets for advanced pagination' but no functional change was made; the advertised feature is not implemented and the comments are misleading.
- 🟡 src/sentry/api/paginator.py (L845)
~90 lines of get_result duplicated from BasePaginator and get_item_key/value_from_cursor duplicate Paginator; no tests added for the new paginator or the optimized_pagination branch, so the crash is uncaught. Prefer composing DateTimePaginator over a parallel class.
- 🟡 src/sentry/spans/buffer.py (L447)
max_segment_spans is now dead config (set but never read) and inconsistent with the hardcoded 1000 cap in the Lua script; either thread the config as ARGV or remove the parameter and update docstring.
- 🟡 src/sentry/spans/consumers/process/factory.py (L141)
end_timestamp_precise is read with a hard subscript while sibling fields use .get(); missing/None value causes KeyError or a None score to zadd (TypeError/DataError), crashing the batch. Use .get and define None-score handling.
- 🟡 tests/sentry/spans/test_buffer.py (L120)
No test covers the new eviction mechanism: no >1000-span insert, no assertion that root is not evicted/orphans, no TTL/leak check post-eviction, no tie-break coverage. Given the corruption risk, this must be tested.
- 🟡 src/sentry/spans/buffer.py (L197)
Migration from set to sorted set is unhandled: pre-existing set-typed keys hit WRONGTYPE with zadd/zscan/zpopmin and can crash the flusher (restart loop). Drain/convert keys during rollout or add tolerant handling (detect WRONGTYPE, unlink, continue).
✅ Feature-level checklist
Partially covered
- ⚠️ Optimize spans buffer insertion with eviction during insert
Eviction-during-insert is implemented via zpopmin in the Lua script and wired into the new zadd-based writes, but the implementation is not tree-aware and can evict parent/root spans causing corrupt segments, lacks test coverage, and does not handle migration of existing set-typed keys. The optimization is present but not safe/complete.
Recall is 100% (3/3) but the score is generous: goldens #1 and #2 are the same bug described twice in the dataset — negative slicing on
OptimizedCursorPaginatorand onBasePaginator, two classes sharing the same buggy branch. A single FriendlyReviewer candidate (paginator.py L877) covers both, so the per-golden recall overstates the unique-bug recall (2/2).More importantly, the dataset entry is misaligned: titled "Optimize spans buffer insertion with eviction during insert", its 3 goldens all concern the pagination zone — the fork branch is composite (pagination + spans buffer). The actual subject of the PR therefore has zero goldens, and FriendlyReviewer's most valuable findings on this PR are unscored: the eviction mechanism (L197) is score-ordered via
zpopminand ignores the span tree, so it can evict parent/root spans while children remain and silently produce corrupt segments; the set → sorted-set migration is unhandled (WRONGTYPErisk, flusher restart loop); andmax_segment_spansis dead config inconsistent with the hardcoded 1000 cap. These are exactly the findings a human reviewer of this PR would care about most, and the Martian Code dataset cannot credit them.Honesty about the caveats: this review is marked partial by FriendlyReviewer itself — the pagination area was produced from a prose pass and is less verified than the span-buffer area. The datetime golden (#3) was nonetheless found within that less-verified zone (organization_auditlogs.py L79), and the negative-slicing pair was found as well. The main weakness is a cluster of overlapping findings around the paginator (L182, L845, cursors.py L26) that re-analyzes the same code region from several angles — legitimate but redundant.
The extra findings are all defensible. The None-dereference on
organization_context.member(L71) is a real crash outside the reference set; the hard-subscript read ofend_timestamp_precise(factory.py L141) and the missing eviction tests (test_buffer.py L120) are concrete and localizable. The comment-only finding on cursors.py L26 is the most minor — a documentation-accuracy nit — and is the acceptable noise in an otherwise high-value review.