Skip to content

Update trickle local publisher streaming behavior#3990

Merged
j0sh merged 2 commits into
masterfrom
codex/trickle-scope-support
Jul 17, 2026
Merged

Update trickle local publisher streaming behavior#3990
j0sh merged 2 commits into
masterfrom
codex/trickle-scope-support

Conversation

@j0sh

@j0sh j0sh commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Port the trickle support hunks needed by Scope serverless event/payment streams.
  • Advance the local publisher stream head when bytes are first written so leading-edge subscribers do not reread the same segment.
  • Add segment buffering and trickle coverage from the live-runner branch.

Tests

  • go test ./trickle -count=1
  • go test ./server -run TestDoesNotExist -count=1

The test runs used an unstaged local replace: github.com/livepeer/lpms => /Users/josh/lpms.

Summary by CodeRabbit

  • New Features

    • Added configurable checks before streams are created or deleted, with clearer HTTP error responses.
    • Added reset handling to unblock subscribers and advance publishing to the appropriate segment.
    • Improved publisher metadata for segment sequencing and latest available data.
    • Added support for controlling whether missing streams are created automatically.
  • Bug Fixes

    • Improved reliability for concurrent streaming, segment growth, empty writes, and subscriber reads.
  • Documentation

    • Clarified HTTP metadata, restart behavior, segment ordering, and sequence tracking semantics.

@github-actions github-actions Bot added the go Pull requests that update Go code label Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Trickle streaming and lifecycle controls

Layer / File(s) Summary
Paged segment buffering
trickle/segment_buffer.go, trickle/segment_buffer_test.go
Adds an atomic, append-only paged buffer with cursor-based reads, tail growth, race handling, and cross-page tests.
Concurrent segment streaming and reset flow
trickle/trickle_server.go, trickle/local_publisher.go, trickle/README.md, trickle/trickle_test.go
Integrates cursor-based segment reads, atomic completion, reset closure, sequence and termination headers, publisher write timing, and reset/empty-segment coverage.
Stream creation and deletion policies
trickle/trickle_server.go, trickle/local_publisher.go, trickle/trickle_test.go
Adds create/delete hooks, structured request errors, non-autocreating local writes, and lifecycle contract tests.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Publisher
  participant TrickleServer
  participant Segment
  participant Subscriber
  Publisher->>TrickleServer: POST with Lp-Trickle-Reset
  TrickleServer->>Segment: close prior segments
  Segment-->>Subscriber: unblock waiting read
  TrickleServer->>Segment: write reset payload at next index
  Segment-->>Subscriber: return reset payload and sequence headers
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clearly related to the main change and describes the updated local publisher streaming behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/trickle-scope-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
trickle/trickle_server.go (2)

690-699: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Reject writes after a segment is closed.

Reset closes prior segments, but in-flight publishers can subsequently append through writeData. Those late bytes become visible to future readers, mixing pre-reset and post-reset data in a completed segment—the new test explicitly demonstrates this path. Make writeData return an error when closed and propagate it through local and HTTP publishers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@trickle/trickle_server.go` around lines 690 - 699, Update Segment.writeData
to return an error when the segment is closed, checking closed while holding
segment.mutex before appending or broadcasting. Propagate this error through the
local and HTTP publisher paths so late writes are rejected rather than exposed
after reset.

466-490: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Centralize stream-head advancement as a monotonic operation.

The duplicated assignments allow stale writers to rewind the head, while local zero-byte writes skip advancement entirely.

  • trickle/trickle_server.go#L466-L490: replace both direct assignments with a monotonic head-advance helper.
  • trickle/local_publisher.go#L63-L68: use that helper on the first payload and when completing an empty segment.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@trickle/trickle_server.go` around lines 466 - 490, The stream head can be
rewound by direct assignments, and zero-byte local segments do not advance it.
In trickle/trickle_server.go lines 466-490, replace both direct s.nextWrite
assignments with the shared monotonic head-advance helper; in
trickle/local_publisher.go lines 63-68, use the same helper for the first
payload and when completing an empty segment. Locate the affected logic via
s.nextWrite and the local publisher’s segment completion handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@trickle/trickle_server.go`:
- Around line 345-351: Update writeHookError so non-RequestError failures are
logged server-side and returned with a generic HTTP 500 message instead of
exposing err.Error() to the client. Preserve the existing RequestError response
behavior and status handling unchanged.
- Around line 41-43: Update the publish-triggered stream creation path for POST
/{stream}/{idx} to invoke the BeforeCreate hook before autocreating a missing
stream, matching the existing HTTP channel creation path and propagating any
RequestError. Add a test covering a blocked publish attempt that verifies the
stream is not created.

In `@trickle/trickle_test.go`:
- Around line 556-559: Update the test goroutines around the publisher write and
subscriber read to send their returned errors and data through result channels
instead of discarding them. Replace unbounded receives at the affected sections
with timed select statements, and assert the publisher error, subscriber read
error, and received data while preserving the existing synchronization behavior.
- Around line 638-639: Update the test flow around sub.Read() to check and
handle err before accessing resp or registering the resp.Body.Close defer;
preserve the existing response-body cleanup only after a successful read.

---

Outside diff comments:
In `@trickle/trickle_server.go`:
- Around line 690-699: Update Segment.writeData to return an error when the
segment is closed, checking closed while holding segment.mutex before appending
or broadcasting. Propagate this error through the local and HTTP publisher paths
so late writes are rejected rather than exposed after reset.
- Around line 466-490: The stream head can be rewound by direct assignments, and
zero-byte local segments do not advance it. In trickle/trickle_server.go lines
466-490, replace both direct s.nextWrite assignments with the shared monotonic
head-advance helper; in trickle/local_publisher.go lines 63-68, use the same
helper for the first payload and when completing an empty segment. Locate the
affected logic via s.nextWrite and the local publisher’s segment completion
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e71199de-a585-4270-8dbe-1b34abc27e94

📥 Commits

Reviewing files that changed from the base of the PR and between ab7a91b and 1224f87.

📒 Files selected for processing (6)
  • trickle/README.md
  • trickle/local_publisher.go
  • trickle/segment_buffer.go
  • trickle/segment_buffer_test.go
  • trickle/trickle_server.go
  • trickle/trickle_test.go

Comment thread trickle/trickle_server.go
Comment on lines +41 to +43
// BeforeCreate runs before HTTP channel creation.
// Return RequestError for expected client/policy failures.
BeforeCreate func(r *http.Request, streamName string) error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
ast-grep outline trickle/trickle_server.go --items all \
  --match 'handlePost|getOrCreateStream|BeforeCreate' --view expanded
rg -n -C3 '\bBeforeCreate\b|\bgetOrCreateStream\s*\(' trickle

Repository: livepeer/go-livepeer

Length of output: 4769


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '180,250p' trickle/trickle_server.go
printf '\n---\n'
sed -n '320,460p' trickle/trickle_server.go
printf '\n---\n'
sed -n '200,330p' trickle/trickle_test.go

Repository: livepeer/go-livepeer

Length of output: 9166


Apply BeforeCreate to publish-triggered creation too. POST /{stream}/{idx} can autocreate a missing stream, but that path skips BeforeCreate, so a publisher can bypass creation policy by posting directly to a segment. Route both creation paths through the hook and add a blocked-publish test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@trickle/trickle_server.go` around lines 41 - 43, Update the publish-triggered
stream creation path for POST /{stream}/{idx} to invoke the BeforeCreate hook
before autocreating a missing stream, matching the existing HTTP channel
creation path and propagating any RequestError. Add a test covering a blocked
publish attempt that verifies the stream is not created.

Comment thread trickle/trickle_server.go
Comment on lines +345 to +351
func writeHookError(w http.ResponseWriter, err error) {
var requestErr *RequestError
if errors.As(err, &requestErr) {
http.Error(w, requestErr.Error(), requestErr.httpStatus())
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not expose generic hook errors to clients.

err.Error() may contain internal policy, database, or identity details. Log it server-side and return a generic 500 body.

Proposed fix
 	if errors.As(err, &requestErr) {
 		http.Error(w, requestErr.Error(), requestErr.httpStatus())
 		return
 	}
-	http.Error(w, err.Error(), http.StatusInternalServerError)
+	slog.Error("HTTP lifecycle hook failed", "err", err)
+	http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
📝 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
func writeHookError(w http.ResponseWriter, err error) {
var requestErr *RequestError
if errors.As(err, &requestErr) {
http.Error(w, requestErr.Error(), requestErr.httpStatus())
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
func writeHookError(w http.ResponseWriter, err error) {
var requestErr *RequestError
if errors.As(err, &requestErr) {
http.Error(w, requestErr.Error(), requestErr.httpStatus())
return
}
slog.Error("HTTP lifecycle hook failed", "err", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@trickle/trickle_server.go` around lines 345 - 351, Update writeHookError so
non-RequestError failures are logged server-side and returned with a generic
HTTP 500 message instead of exposing err.Error() to the client. Preserve the
existing RequestError response behavior and status handling unchanged.

Comment thread trickle/trickle_test.go
Comment on lines +556 to +559
writeDone := make(chan struct{})
go func() {
defer close(writeDone)
_ = lp.Write(r0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Propagate goroutine errors and bound channel waits.

The publisher error is discarded, the subscriber read error is ignored, and both receives can hang until the suite timeout. Return results through channels, assert the errors/data, and use a timed select.

Also applies to: 575-593, 616-617

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@trickle/trickle_test.go` around lines 556 - 559, Update the test goroutines
around the publisher write and subscriber read to send their returned errors and
data through result channels instead of discarding them. Replace unbounded
receives at the affected sections with timed select statements, and assert the
publisher error, subscriber read error, and received data while preserving the
existing synchronization behavior.

Comment thread trickle/trickle_test.go
Comment on lines +638 to +639
resp, err := sub.Read()
defer resp.Body.Close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Check err before dereferencing resp.

A failed sub.Read() currently panics in the deferred call and hides the actual failure.

 	resp, err := sub.Read()
+	require.NoError(err)
 	defer resp.Body.Close()
📝 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
resp, err := sub.Read()
defer resp.Body.Close()
resp, err := sub.Read()
require.NoError(err)
defer resp.Body.Close()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@trickle/trickle_test.go` around lines 638 - 639, Update the test flow around
sub.Read() to check and handle err before accessing resp or registering the
resp.Body.Close defer; preserve the existing response-body cleanup only after a
successful read.

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.85106% with 27 lines in your changes missing coverage. Please review.
✅ Project coverage is 33.53732%. Comparing base (ab7a91b) to head (7e1dbaf).

Files with missing lines Patch % Lines
trickle/segment_buffer.go 76.38889% 9 Missing and 8 partials ⚠️
trickle/trickle_server.go 83.60656% 8 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@                 Coverage Diff                 @@
##              master       #3990         +/-   ##
===================================================
+ Coverage   33.28594%   33.53732%   +0.25138%     
===================================================
  Files            171         172          +1     
  Lines          42204       42323        +119     
===================================================
+ Hits           14048       14194        +146     
+ Misses         27101       27063         -38     
- Partials        1055        1066         +11     
Files with missing lines Coverage Δ
trickle/local_publisher.go 79.54545% <100.00000%> (+57.92383%) ⬆️
trickle/trickle_server.go 75.76471% <83.60656%> (+6.15432%) ⬆️
trickle/segment_buffer.go 76.38889% <76.38889%> (ø)

... and 5 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ab7a91b...7e1dbaf. Read the comment docs.

Files with missing lines Coverage Δ
trickle/local_publisher.go 79.54545% <100.00000%> (+57.92383%) ⬆️
trickle/trickle_server.go 75.76471% <83.60656%> (+6.15432%) ⬆️
trickle/segment_buffer.go 76.38889% <76.38889%> (ø)

... and 5 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@j0sh
j0sh requested a review from rickstaa July 16, 2026 18:18

@rickstaa rickstaa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@j0sh, confirmed that this is byte-for-byte identical to the code I reviewed and tested in https://github.com/livepeer/go-livepeer/pull/3938/files. Good to merge.

My only comment on the combined PR was that the third argument to getOrCreateStream is a bit ambiguous: https://github.com/livepeer/go-livepeer/pull/3938/files#r3505950363. If you agree you could include this in this PR or a follow up PR.

@j0sh

j0sh commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

Renamed isLocal to forceCreate in 7e1dbaf

@j0sh
j0sh merged commit 1c6f3ac into master Jul 17, 2026
26 of 34 checks passed
@j0sh
j0sh deleted the codex/trickle-scope-support branch July 17, 2026 15:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go Pull requests that update Go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants