Skip to content

Release v1.13.0: console polish, API log sort, R2 multipart leak fix#259

Merged
alexanderwanyoike merged 22 commits intomainfrom
dev
Apr 12, 2026
Merged

Release v1.13.0: console polish, API log sort, R2 multipart leak fix#259
alexanderwanyoike merged 22 commits intomainfrom
dev

Conversation

@alexanderwanyoike
Copy link
Copy Markdown
Owner

@alexanderwanyoike alexanderwanyoike commented Apr 6, 2026

Summary

Release cut covering console/dashboard polish, an API-side log sort refactor, and a runtime fix for leaking R2 multipart uploads. Helm chart bumped to 0.8.0 / appVersion 1.13.0.

Console & dashboard

API: log sort refactor (#261)

  • Added sort=asc|desc param to the logs endpoint (default desc / newest-first).
  • Capped desc stream reads to prevent memory exhaustion.
  • Frontend removes the in-memory reversal and scroll gymnastics - the API owns sort order now. followOutput simplified to connected && sort === "asc".

Runtime: R2 multipart upload leak fix (#262)

  • Root cause fix for ~70 GB of phantom storage on bot-logs and ~8,000 orphaned multipart uploads on bot-state. Two upload paths were leaking incomplete multipart uploads whenever their sync context expired mid-operation, because neither path guaranteed an abort on a fresh context.
  • AppendBotLogs: replaced minio.Client.ComposeObject (which has no abort-on-error) with an owned multipart-copy flow via minio.Core. A deferred abort runs on a fresh background context so caller cancellation cannot prevent cleanup. Safe because the per-bot mutex serializes writes to the same daily key.
  • UploadState: wraps PutObject (unknown size → multipart streaming) with a fresh-context RemoveIncompleteUpload on any error. minio-go's internal defer abort was using the already-cancelled caller context and failing silently.
  • GC safety net: new ListIncompleteUploads + AbortIncompleteUpload on the MinIOClient interface; cleanupStaleIncompleteUploads sweep runs on both logs and state buckets each GC cycle, filtering by a 1h staleIncompleteUploadAge so no in-flight upload can be touched.
  • Bumped per-sync upload timeouts via injectable functional options: logs 30s → 120s, state 60s → 180s. R2 tail latency was shredding legitimate uploads at the old values.
  • Live cleanup of existing orphans was done pre-deploy: 1,558 aborted on bot-logs and 7,961 aborted on bot-state, so the billing baseline is already clean.

Test plan

  • API: cd api && yarn test --no-coverage - 501 pass
  • Frontend: cd frontend && yarn test --no-coverage && yarn build - 598 pass
  • Runtime: go test ./... - full repo suite green (gc, minio-logger, runtime/storage, daemon)
  • Scheduled bot: load more on 7d grows console count; sort toggle switches between newest/oldest first
  • Realtime bot: Live SSE stays connected, sort toggle works on date ranges
  • No scroll jolt on load more or interval changes
  • Post-deploy smoke: confirm aws s3api list-multipart-uploads on both bot-logs and bot-state stays at zero for 24h; verify nssuhlwv2mw2cihrnnxpbqxx daily log keeps growing through the day without gaps

🤖 Generated with Claude Code

alexanderwanyoike and others added 13 commits April 6, 2026 18:32
1d loaded too many metrics for active bots. 1h provides a tighter
window that loads quickly while showing recent data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: default interval 1h for scheduled bots
The polling interval captured a stale fetchLogs closure. Using a
ref ensures the interval always calls the latest version without
needing to recreate the timer on every query change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: prevent console refresh loop on interval change
…ries

Both useBotLogs and useBotLogsStream had separate expansion logic
that dropped the timestamp field. Extracted a shared expandLogEntries
function in lib/log-utils.ts that preserves timestamp. Both hooks
now import from the same source.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-expansion

fix: preserve timestamp field when expanding log entries
Users can now select polling frequency: 10s / 30s / 60s / Off.
Previously hardcoded at 60s for console and 30s for dashboard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Users can now select polling frequency: 10s / 30s / 60s / Off.
Previously hardcoded at 60s for console and 30s for dashboard.

Fixes from review:
- Lifecycle effect depends on autoRefresh/refreshInterval
- Pass refreshInterval directly (no coercion for Off)
- Guard setInterval calls with refreshInterval > 0
- Accessibility: role=group, aria-label, aria-pressed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: configurable refresh interval for console and dashboard
The stream hook (useBotLogsStream) hardcoded hasMore=false, so when a
realtime bot switched to a date range interval its REST fetches never
exposed pagination. This adds hasMore state tracking and a loadMore
callback to the stream hook for its REST mode, and wires them through
bot-detail-panel so the console shows "load more" for historical
browsing on realtime bots.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: enable load more for realtime bots on non-live intervals
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 6, 2026

Deploy Preview for the0-docs canceled.

Name Link
🔨 Latest commit 9521faa
🔍 Latest deploy log https://app.netlify.com/projects/the0-docs/deploys/69da49ee058a0200087e4498

alexanderwanyoike and others added 3 commits April 8, 2026 02:07
- Raise MAX_LOG_ENTRIES from 2000 to 10000 so load more grows the buffer
- Advance query offset after loadMore (consecutive calls work)
- Add loadingMoreRef guard to prevent concurrent loadMore calls
- Skip auto-refresh polling when user has paginated
- Remove Virtuoso endReached auto-trigger (explicit button only)
- Only auto-scroll to top when user is already at the top
- Disable followOutput when not connected (no jolt on REST data)
- Use ref for refreshInterval in stream hook (no SSE reconnection)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
…and-polish

fix: console load more, scroll jolt, and SSE reconnection
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 647312b4-093d-4c78-af53-0e22505770f8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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 and usage tips.

alexanderwanyoike and others added 6 commits April 8, 2026 22:48
Add sort=asc|desc to the logs API. Default is desc (newest-first).
- tailFilteredLogs (single date) already returns newest-first; reverses for asc
- streamFilteredLogs (date ranges) reads all, reverses, applies offset/limit for desc
- Backward compatible: single-date path was already newest-first

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove newestFirst state, displayLogs reversal memo, auto-scroll
  effect, isUserAtTop/prevLogCountRef from console-interface.tsx
- Add sort/onSortChange props to ConsoleInterface (API-controlled)
- Send sort=desc in URL params from both hooks by default
- Add updateQuery to stream hook for sort changes
- Wire sort toggle through bot-detail-panel and mobile-bot-detail
- followOutput now checks sort==="asc" instead of !newestFirst

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace limit: Infinity with a bounded max of offset + limit or 10000,
whichever is larger. Prevents unbounded memory usage on large date
ranges while still supporting pagination.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two upload paths were leaking incomplete multipart uploads whenever their
sync context expired mid-operation, which bloated R2 billed storage (~70 GB
phantom on bot-logs, ~8000 orphans on bot-state).

Root causes:
- minio-go ComposeObject never aborts on error. The AppendBotLogs compose
  branch drove it directly, so any UploadPartCopy/Complete failure leaked
  the upload ID.
- minio-go PutObject streaming (unknown size) DOES defer an abort, but
  uses the caller's context which is already cancelled by the time the
  defer runs, so the abort request itself fails silently.
- The 30s / 60s sync timeouts were too tight for R2 tail latency on the
  multipart-copy path, amplifying the leak rate.

Fixes:
- Own the multipart-copy flow in AppendBotLogs via a minio.Core seam and
  defer an abort that fires on a fresh background context, so caller
  cancellation cannot prevent cleanup.
- Wrap UploadState's PutObject with a fresh-context RemoveIncompleteUpload
  on any error, closing the pipe with the error first so the tar.gz
  goroutine exits.
- Extend the GC sweep with ListIncompleteUploads + AbortIncompleteUpload
  on both buckets, filtering by a 1h staleIncompleteUploadAge so no
  in-flight upload can be touched.
- Bump injectable per-sync upload timeouts: logs 30s->120s, state 60s->180s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-uploads

fix: stop leaking R2 multipart uploads on bot-logs and bot-state
…agination

fix: simplify console pagination with API-side sort=desc
@alexanderwanyoike alexanderwanyoike changed the title Release v1.13.0 Release v1.13.0: console polish, API log sort, R2 multipart leak fix Apr 11, 2026
@alexanderwanyoike alexanderwanyoike merged commit f6312f6 into main Apr 12, 2026
17 checks passed
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.

1 participant