Skip to content

fix(logs): trim log writer redis streams on the target's db_shard - #3953

Open
ryanmelt wants to merge 1 commit into
mainfrom
fix/log-writer-trim-shard
Open

ryanmelt wants to merge 1 commit into
mainfrom
fix/log-writer-trim-shard

Conversation

@ryanmelt

Copy link
Copy Markdown
Member

What

  • Ruby LogWriter: the shard lookup used /__\{?([^}_]+)\}?__/, which on SCOPE__TELEMETRY__{TARGET}__PACKET matches TELEMETRY, not the target. It also always looked up the DEFAULT scope. Added LogWriter.db_shard_for_topic, which reads the {TARGET} hashtag and the scope prefix.
  • Python LogWriter, same fix plus two latent bugs in the same code:
    • it trimmed shard 0 unconditionally
    • cleanup_offsets / last_offsets dicts were iterated without .items(), so any queued cleanup would raise
    • hourly/daily cycling compared utc_now.min (the datetime.min class constant) instead of utc_now.minute, so it could never trigger
  • Moved the Python trim loop into trim_due_cleanups() so it can be tested.

Why

On deployments with more than one Redis db_shard, TELEMETRY__ streams for targets on non-zero shards were never trimmed by the log writer and grew without bound. XTRIM against the wrong shard is a silent no-op. Single-shard deployments are unaffected. The Python LogWriter is currently only used by interface stream logs, which don't record Redis offsets, so the Python dict-iteration bug hasn't been hit in practice.

Testing

  • New openc3/spec/logs/log_writer_spec.rb; bundle exec rspec spec/logs: 60 examples, 0 failures
  • New openc3/python/test/logs/test_log_writer.py (shard lookup, plus close → trim on the correct shard); uv run pytest test/logs: 56 passed; ruff clean

🤖 Generated with Claude Code

The shard lookup regex matched the stream type (TELEMETRY) instead of the
{TARGET} hashtag and ignored scope, so on multi-shard deployments target
streams outside shard 0 were never trimmed by the log writer. Python also
iterated offset dicts without .items() and used datetime.min for minutes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ryanmelt
ryanmelt force-pushed the fix/log-writer-trim-shard branch from 6453ef4 to f44bd4d Compare September 28, 2026 00:42
@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 80.14%. Comparing base (688f928) to head (f44bd4d).
⚠️ Report is 58 commits behind head on main.

Files with missing lines Patch % Lines
openc3/lib/openc3/logs/log_writer.rb 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3953      +/-   ##
==========================================
+ Coverage   80.08%   80.14%   +0.06%     
==========================================
  Files         901      901              
  Lines       68356    68368      +12     
  Branches     2645     2645              
==========================================
+ Hits        54743    54796      +53     
+ Misses      12946    12910      -36     
+ Partials      667      662       -5     
Flag Coverage Δ
frontend 67.00% <ø> (+0.04%) ⬆️
python 80.19% <ø> (+0.06%) ⬆️
ruby-api 82.41% <ø> (+0.40%) ⬆️
ruby-backend 85.66% <80.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

# Returns the Redis db_shard holding a target stream such as
# SCOPE__TELEMETRY__{TARGET}__PACKET, or 0 if no target can be determined
def self.db_shard_for_topic(redis_topic)
target_match = redis_topic.match(/\{([^}]+)\}/)
@ryanmelt ryanmelt added the review-and-merge-ready When this PR is approved, the approver can click the merge button label Oct 12, 2026
Comment on lines 213 to 214
instance.cycle_minute
and not instance.cycle_hour

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Truthiness skips cycle_minute=0 and treats cycle_hour=0 as unset (cycles hourly); use is None / is not None.

Comment on lines 203 to 204
instance.cycle_hour
and instance.cycle_minute

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Truthiness skips 0, so cycle_hour=0 or cycle_minute=0 never cycles daily; use is not None.

This branch has not been deployed

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

Labels

review-and-merge-ready When this PR is approved, the approver can click the merge button

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants