Skip to content

#527: remove global caching for fb/github_graph, inject octo into over?#556

Open
VasilevNStas wants to merge 1 commit into
zerocracy:masterfrom
VasilevNStas:fix/527-global-mutex
Open

#527: remove global caching for fb/github_graph, inject octo into over?#556
VasilevNStas wants to merge 1 commit into
zerocracy:masterfrom
VasilevNStas:fix/527-global-mutex

Conversation

@VasilevNStas

@VasilevNStas VasilevNStas commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

This is a new PR replacing the closed #535. The approach was changed after @yegor256 rejected GLOBAL_MUTEX ("global state is pure evil in OOP").

Problem

lib/fbe/octo.rb:53, lib/fbe/fb.rb:34, lib/fbe/github_graph.rb:18 use global[:x] ||= begin...end — a non-atomic read-then-write. Under concurrent access (e.g., multiple judges running in parallel), two threads can both evaluate the block and overwrite each other's result.

Solution

Changes made:

  1. lib/fbe/fb.rb — removed global[:fb] ||=. Each call to Fbe.fb() creates a fresh decorator stack. Thread-safe by definition, no global state.

  2. lib/fbe/github_graph.rb — removed global[:github_graph] ||=. Same approach — each call creates a new GraphQL client.

  3. lib/fbe/octo.rb — kept global[:octo] ||=. Without caching, rate limit tracking breaks: Fbe::Middleware::RateLimit stores state per middleware instance. Different clients don't share request history, so off_quota? always returns false on a fresh client.

  4. lib/fbe/over.rb — added optional octo: parameter. When provided, uses it for off_quota? check; otherwise falls back to Fbe.octo() (backward compatibility).

  5. lib/fbe/iterate.rb — passes octo: oct (its internal client) to all Fbe.over? calls. Proper OOP: dependency injection instead of global cache.

  6. lib/fbe/unmask_repos.rb — creates octo before the first Fbe.over? call and passes it via octo:.

Why not remove caching from octo.rb entirely?

Fbe::Middleware::RateLimit is a Faraday middleware living inside each Octokit client's Faraday stack. It tracks @remaining (requests left before hitting rate limit) and decrements it on each request. If every Fbe.octo call creates a new client with fresh middleware, rate limit tracking stops working — off_quota? always sees a fresh /rate_limit response.

The clean OOP fix would be full DI (create the client once and pass it everywhere), but that requires changing the judge API. Our compromise: octo.rb remains a cached factory, while all internal callers (iterate, unmask_repos, over?) accept and pass octo: explicitly.

Tests

  • 283 tests pass, 0 failures, 0 errors
  • RuboCop — 0 offenses
  • Code coverage: 94%

@VasilevNStas

Copy link
Copy Markdown
Contributor Author

@yegor256 please re-review

@VasilevNStas VasilevNStas force-pushed the fix/527-global-mutex branch from cdf6d81 to 18b72e3 Compare June 10, 2026 18:12
@VasilevNStas

Copy link
Copy Markdown
Contributor Author

The xcop CI failure is a Docker Hub connectivity issue (i/o timeout pulling ruby:3.0 from docker.io), not related to this PR. The rake jobs (macos-15 + ubuntu-24.04, Ruby 3.3) pass successfully. Could someone with admin rights re-run the failed job?

@bibonix bibonix left a comment

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.

The branch sits on top of 8e52719, 17 commits behind master, and the resulting diff folds in unrelated history rather than only the #527 change. Concretely, the diff shows lib/fbe/fake_octokit.rb being removed (1797 lines) and re-inlined into lib/fbe/octo.rb (~1791 additions), because fake_octokit was extracted on master in #534 after this branch forked off. The same staleness pulls test deletions in test_delete.rb, test_delete_one.rb, test_overwrite.rb, test_just_one.rb, test_kill_if.rb, test_repeatedly.rb, test_sec.rb, test_unmask_repos.rb, test_octo.rb, and test_sqlite_store.rb that revert improvements already landed in #503, #517, #521, #531, #532, #533, #534, #541, #543, #545, and #548, plus README, .gitignore, .github/workflows/actionlint.yml, and .github/workflows/codecov.yml changes that have nothing to do with #527. xcop is red on the current head. Rebase onto current master and drop everything outside lib/fbe/fb.rb, lib/fbe/github_graph.rb, lib/fbe/octo.rb, lib/fbe/over.rb, lib/fbe/iterate.rb, lib/fbe/unmask_repos.rb, then resubmit. No inline comments yet because the rebase will rewrite the diff.

@VasilevNStas VasilevNStas force-pushed the fix/527-global-mutex branch from 18b72e3 to b3eda66 Compare June 11, 2026 14:46
@VasilevNStas

Copy link
Copy Markdown
Contributor Author

Rebased onto current master per review. Diff now contains only the 6 relevant files for #527. RuboCop — 0 offenses, 338 tests — 0 failures.

@bibonix @yegor256 please re-review

@yegor256

Copy link
Copy Markdown
Member

@VasilevNStas again, a wrong move. we do need caching and we do need mutex. I suggest keeping mutex inside the global hash

@yegor256

Copy link
Copy Markdown
Member

@VasilevNStas merge conflicts here

@yegor256

Copy link
Copy Markdown
Member

Please fix the merge conflicts so this pull request can be merged.

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.

3 participants