Skip to content

fix(python): make async pipe transport fork-safe - #6679

Open
jeremyprime wants to merge 4 commits into
valkey-io:mainfrom
jeremyprime:fix/python-pipe-fork-safety
Open

fix(python): make async pipe transport fork-safe#6679
jeremyprime wants to merge 4 commits into
valkey-io:mainfrom
jeremyprime:fix/python-pipe-fork-safety

Conversation

@jeremyprime

Copy link
Copy Markdown
Collaborator

Summary

Make the Python async pipe transport fork-safe. After fork(), the flush thread introduced in #5637 is gone but OnceLock prevented reinitialization, causing all commands in forked child processes (e.g. PySpark workers) to hang indefinitely.

Issue link

Closes #6673

Features / Behaviour Changes

  • Forked child processes can now create and use glide clients without hanging
  • No API changes, the fix is transparent to users
  • No behavior change for non-fork scenarios (single-process usage is unaffected)

Implementation

Rust (ffi/src/lib.rs):

  • Replace OnceLock with Mutex<Option<>> so the pipe can be reinitialized
  • Add reinit_async_pipe() FFI function for post-fork recovery
  • Extract shared create_pipe_writer() helper

Python (glide_client.py, client_pool.py):

  • Detect fork via PID comparison in _setup_pipe(), close inherited fds, call reinit_async_pipe
  • Raise ClosingError if a pre-fork client is used in a child process

CFFI (_glide_ffi.py):

  • Add reinit_async_pipe declaration

Testing

  • Added test_fork_safety.py with 3 integration tests:
    • test_forked_children_can_use_clients: parent inits pipe, forks 3 children, children create clients and issue commands (core regression test)
    • test_parent_functional_after_fork: parent remains operational after children fork and complete
    • test_multiple_fork_cycles: 3 sequential fork/use/exit cycles all work
  • Verified that 2.5.0 hangs and with fix it does not

Checklist

  • This Pull Request is related to one issue.
  • Commit message has a detailed description of what changed and why.
  • Tests are added or updated.
  • CHANGELOG.md and documentation files are updated.
  • Linters have been run (make *-lint targets) and Prettier has been run (make prettier-fix).
  • Destination branch is correct - main or release
  • Create merge commit if merging release branch into main, squash otherwise.
  • Make sure to update the documentation in the valkey-glide-docs repository if necessary

Signed-off-by: Jeremy Parr-Pearson <jeremy.parr-pearson@improving.com>
@jeremyprime
jeremyprime requested a review from a team as a code owner July 31, 2026 19:41
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f7a10d04-f060-4826-8364-bafc05600b5d

📥 Commits

Reviewing files that changed from the base of the PR and between cae9259 and 9108c49.

📒 Files selected for processing (1)
  • python/glide-async/python/glide/glide_client.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • python/glide-async/python/glide/glide_client.py

📝 Walkthrough

Walkthrough

The async pipe transport now detects process forks, resets inherited Python state, recreates the native pipe, and rejects parent-client reuse in child processes. Integration tests cover child operations, parent recovery, timeout handling, repeated fork cycles, and stale-client rejection.

Changes

Fork-safe async pipe transport

Layer / File(s) Summary
Native async pipe state
ffi/src/lib.rs
The native pipe uses a replaceable atomic writer. It adds post-fork reinitialization, terminates flush threads on unrecoverable write errors, and routes async deliveries through the current writer.
Python fork detection and reinitialization
python/glide-async/python/glide/client_pool.py, python/glide-async/python/glide/glide_client.py, python/glide-shared/glide_shared/_glide_ffi.py
Python code tracks process IDs and pipe descriptors, clears inherited state after a fork, calls reinit_async_pipe, and raises ClosingError when a client crosses process boundaries.
Fork-safety integration coverage
python/tests/async_tests/test_fork_safety.py, CHANGELOG.md
Tests cover child client operations, parent operation after child exit, repeated fork cycles, timeout-based hang detection, and stale-client rejection. The changelog documents the behavior.

Sequence Diagram(s)

sequenceDiagram
  participant ParentProcess
  participant ChildProcess
  participant BaseClient
  participant NativePipe
  ParentProcess->>BaseClient: initialize async pipe
  ParentProcess->>ChildProcess: fork process
  ChildProcess->>BaseClient: create child client
  BaseClient->>BaseClient: detect PID change and clear inherited state
  BaseClient->>NativePipe: reinit_async_pipe(pipe_write_fd)
  NativePipe-->>BaseClient: use recreated child pipe
  ChildProcess->>BaseClient: execute commands
  BaseClient-->>ChildProcess: return command results
  ParentProcess->>BaseClient: execute commands after child exit
  BaseClient-->>ParentProcess: return command results
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: making Python async pipe transport fork-safe.
Description check ✅ Passed The description covers the summary, issue, behavior, implementation, testing, and checklist; only the optional Limitations section is missing.
Linked Issues check ✅ Passed The changes address issue #6673 by reinitializing inherited pipe state and preventing fork-related hangs in child processes.
Out of Scope Changes check ✅ Passed The Rust, Python, CFFI, changelog, and test changes all support fork-safe async pipe transport and the linked issue.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

🧹 Nitpick comments (5)
ffi/src/lib.rs (3)

1481-1487: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Do not busy-spin on the pipe state without a yield.

This loop calls get_async_pipe() continuously until the pipe exists. With the mutex-backed state, each iteration takes the global lock, so the spinning task competes with the response delivery path. Add std::thread::yield_now() or a short tokio::time::sleep inside the loop, or wait on a notification instead.

🤖 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 `@ffi/src/lib.rs` around lines 1481 - 1487, Update the ASYNC_PIPE
initialization wait loop around get_async_pipe() to yield between failed checks,
using std::thread::yield_now() or an equivalent short async delay/notification.
Preserve the existing behavior of returning the pipe immediately once
get_async_pipe() succeeds.

1142-1145: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Take the pipe writer once.

Line 1142 and Line 1143 both call get_async_pipe(). The second call repeats the state read and can observe a different writer than the first. Bind the writer once and branch on it.

♻️ Proposed diff
-                if cid != 0 && get_async_pipe().is_some() {
-                    if let Some(w) = get_async_pipe() {
-                        w.push_error(cid, request_id, error_type, error_string);
-                    }
-                } else {
+                let pipe = if cid != 0 { get_async_pipe() } else { None };
+                if let Some(w) = pipe {
+                    w.push_error(cid, request_id, error_type, error_string);
+                } else {
🤖 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 `@ffi/src/lib.rs` around lines 1142 - 1145, Update the async error handling
around get_async_pipe so it retrieves the pipe writer once, then uses that
single bound writer for the presence check and push_error call. Preserve the
existing cid != 0 guard and error arguments.

831-838: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Log the write failure before the flush thread exits.

When the flush thread returns, no further async response reaches Python. Every in-flight future then waits until its request timeout. A log record at this point names the cause (EPIPE/EBADF) and shortens diagnosis of exactly the hang class this PR targets.

🔎 Proposed diff
                     } else {
                         let e = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
                         if e == libc::EINTR || e == libc::EAGAIN {
                             continue;
                         }
-                        // EPIPE/EBADF — fd is gone, exit the flush thread.
-                        return;
+                        // EPIPE/EBADF — fd is gone, exit the flush thread.
+                        logger_core::log_warn(
+                            "async-pipe",
+                            format!("flush thread exiting: write to fd {fd} failed with errno {e}"),
+                        );
+                        return;
                     }
🤖 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 `@ffi/src/lib.rs` around lines 831 - 838, In the write-failure branch of the
flush thread, before the `return` reached after filtering `EINTR` and `EAGAIN`,
log the non-retryable OS error using the existing logging mechanism. Include the
captured error code and enough context to identify the failed write, while
preserving the current retry behavior and thread exit.
python/tests/async_tests/test_fork_safety.py (1)

61-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider anyio.run in the child worker.

The async client supports asyncio and trio. asyncio.run limits the child to asyncio, so the fork path is never exercised on trio. anyio.run(run) lets the worker take a backend parameter and cover both.

🤖 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 `@python/tests/async_tests/test_fork_safety.py` at line 61, Replace asyncio.run
in the child worker with anyio.run so run can execute using the selected async
backend, including both asyncio and trio. Pass the worker’s backend parameter
through the child execution path and preserve the existing run behavior.
python/glide-async/python/glide/glide_client.py (1)

590-590: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Initialize _create_pid in __init__.

create() assigns _create_pid, so the attribute is missing on any instance that skips create() or fails before Line 590. _execute_command covers this with getattr(self, "_create_pid", 0). Assign the attribute in __init__ next to _pipe_client_id instead, and read it directly.

♻️ Proposed diff
         self._pipe_client_id: int = 0
+        self._create_pid: int = 0
         self._is_asyncio: bool = True
🤖 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 `@python/glide-async/python/glide/glide_client.py` at line 590, Initialize
_create_pid in __init__ alongside _pipe_client_id, using the appropriate default
value, and remove its initialization from create(). Update _execute_command to
access self._create_pid directly instead of using getattr, while preserving the
existing PID behavior.
🤖 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 `@ffi/src/lib.rs`:
- Around line 590-602: Replace the ASYNC_PIPE Mutex<Option<&'static
SharedPipeWriter>> with an AtomicPtr<SharedPipeWriter>. Update get_async_pipe()
to perform a lock-free atomic load and convert null to None, have
init_async_pipe use compare_exchange for first-call-wins behavior, and have
reinit_async_pipe use an atomic store to replace the inherited writer without
locking or poisoning risk.

In `@python/glide-async/python/glide/client_pool.py`:
- Around line 131-164: Make fork recovery in _create_pool(), _setup_pipe(), and
reinit_async_pipe() perform the PID-change check before acquiring
_async_pipe_lock, using a child-safe reset path that closes and clears inherited
pipe state without blocking on ASYNC_PIPE or its mutex. Reinitialize the pipe
and writer in the child only after this reset, preserving normal locking for
non-fork paths. Add a regression test that forks while transport initialization
is active and verifies child commands complete.

In `@python/glide-async/python/glide/glide_client.py`:
- Around line 368-400: The fork-reset logic is duplicated in
AsyncClientPool.__init__, allowing stale Trio state to survive. Replace the
inline glide_client global reset in AsyncClientPool.__init__ with a call to
_detect_fork_and_reset(), while preserving the required lock context and
ensuring the pool uses the shared helper as the single reset implementation.
- Around line 797-801: Extract the existing fork-process validation from
_execute_command into a reusable _check_same_process method. Invoke it
immediately after each _is_closed check in _execute_batch, _execute_script,
_update_connection_password, and _refresh_iam_token, while preserving the
existing ClosingError behavior for clients reused after fork.

In `@python/tests/async_tests/test_fork_safety.py`:
- Around line 109-123: Replace the non-blocking result reads in
python/tests/async_tests/test_fork_safety.py lines 109-123 and 185-186: in the
first result-collection block, perform exactly num_workers bounded
result_queue.get(timeout=5.0) calls and handle queue.Empty while preserving the
existing assertions; at the second site, replace result_queue.get_nowait() with
result_queue.get(timeout=5.0). Ensure queue.Empty is available for handling.
- Around line 64-68: Add a forked-child worker test in TestForkSafety that
reuses the inherited parent client and asserts client operations raise
ClosingError, covering the documented pre-fork client rejection. Integrate the
worker into the existing parametrized anyio test flow and preserve current
fork-safety coverage.
- Around line 58-59: Suppress Ruff BLE001 specifically on the broad exception
handler in the child failure-reporting path of test_fork_safety.py, while
preserving the existing catch-all behavior and result_queue reporting.

---

Nitpick comments:
In `@ffi/src/lib.rs`:
- Around line 1481-1487: Update the ASYNC_PIPE initialization wait loop around
get_async_pipe() to yield between failed checks, using std::thread::yield_now()
or an equivalent short async delay/notification. Preserve the existing behavior
of returning the pipe immediately once get_async_pipe() succeeds.
- Around line 1142-1145: Update the async error handling around get_async_pipe
so it retrieves the pipe writer once, then uses that single bound writer for the
presence check and push_error call. Preserve the existing cid != 0 guard and
error arguments.
- Around line 831-838: In the write-failure branch of the flush thread, before
the `return` reached after filtering `EINTR` and `EAGAIN`, log the non-retryable
OS error using the existing logging mechanism. Include the captured error code
and enough context to identify the failed write, while preserving the current
retry behavior and thread exit.

In `@python/glide-async/python/glide/glide_client.py`:
- Line 590: Initialize _create_pid in __init__ alongside _pipe_client_id, using
the appropriate default value, and remove its initialization from create().
Update _execute_command to access self._create_pid directly instead of using
getattr, while preserving the existing PID behavior.

In `@python/tests/async_tests/test_fork_safety.py`:
- Line 61: Replace asyncio.run in the child worker with anyio.run so run can
execute using the selected async backend, including both asyncio and trio. Pass
the worker’s backend parameter through the child execution path and preserve the
existing run behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a873baef-9740-4447-b993-bbd18054d16e

📥 Commits

Reviewing files that changed from the base of the PR and between 4bf2677 and 84347cc.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • ffi/src/lib.rs
  • python/glide-async/python/glide/client_pool.py
  • python/glide-async/python/glide/glide_client.py
  • python/glide-shared/glide_shared/_glide_ffi.py
  • python/tests/async_tests/test_fork_safety.py

Comment thread ffi/src/lib.rs Outdated
Comment thread python/glide-async/python/glide/client_pool.py Outdated
Comment thread python/glide-async/python/glide/glide_client.py
Comment thread python/glide-async/python/glide/glide_client.py Outdated
Comment thread python/tests/async_tests/test_fork_safety.py Outdated
Comment thread python/tests/async_tests/test_fork_safety.py
Comment thread python/tests/async_tests/test_fork_safety.py

@valkey-review-bot valkey-review-bot Bot 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 PID-comparison + reinit_async_pipe mechanism is the right shape for this bug, but two things stop it short of the goal: the new Mutex that replaced OnceLock is itself the kind of state that does not survive fork(), so the recovery path can inherit it locked and hang exactly where the issue reports a hang; and the pre-fork-client guard is wired into _execute_command only, so exec()/script/password paths (and pool-created clients) still hang instead of raising, which is narrower than what the CHANGELOG entry promises.

Comment thread ffi/src/lib.rs Outdated
Comment thread python/glide-async/python/glide/glide_client.py Outdated
Comment thread python/glide-async/python/glide/client_pool.py Outdated
… paths, reuse helper in pool

Signed-off-by: Jeremy Parr-Pearson <jeremy.parr-pearson@improving.com>

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

🧹 Nitpick comments (1)
ffi/src/lib.rs (1)

903-948: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Redundant double load of get_async_pipe() at each call site.

At Line 906, get_async_pipe().is_some() is checked, then get_async_pipe() is called again inside the spawned future (lines 914, 924, 936). The same pattern appears at lines 1153-1154. Because ASYNC_PIPE only ever transitions from null to non-null or from one non-null writer to another (never back to null), the second call cannot observe a different Option variant than a state already confirmed non-null by the first. Fetch the writer once and reuse the Option to avoid the extra atomic load per call site.

♻️ Example simplification for handle_custom_error
-                if cid != 0 && get_async_pipe().is_some() {
-                    if let Some(w) = get_async_pipe() {
-                        w.push_error(cid, request_id, error_type, error_string);
-                    }
-                } else {
+                if cid != 0 {
+                    if let Some(w) = get_async_pipe() {
+                        w.push_error(cid, request_id, error_type, error_string);
+                    } else {
+                        unsafe {
+                            Self::send_async_custom_error(
+                                failure_callback,
+                                error_string,
+                                error_type,
+                                request_id,
+                            )
+                        };
+                    }
+                } else {
                     unsafe {
                         Self::send_async_custom_error(
                             failure_callback,
                             error_string,
                             error_type,
                             request_id,
                         )
                     };
                 }

Also applies to: 1150-1156

🤖 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 `@ffi/src/lib.rs` around lines 903 - 948, Update the async pipe handling around
the request completion task and the analogous handle_custom_error path to load
get_async_pipe() once, retain the confirmed writer Option, and move or reuse it
inside the spawned future instead of calling get_async_pipe() again at each
push_success or push_error call. Preserve the existing null-pipe fallback and
response behavior.
🤖 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 `@ffi/src/lib.rs`:
- Around line 743-758: Update init_async_pipe and reinit_async_pipe so pipe
flush threads are started only after the writer becomes the active ASYNC_PIPE
owner, or explicitly shut down any thread before discarding a rejected or
replaced SharedPipeWriter. Ensure failed first-call-wins CAS attempts and
non-fork reinitialization do not leave background threads running for the
process lifetime.

---

Nitpick comments:
In `@ffi/src/lib.rs`:
- Around line 903-948: Update the async pipe handling around the request
completion task and the analogous handle_custom_error path to load
get_async_pipe() once, retain the confirmed writer Option, and move or reuse it
inside the spawned future instead of calling get_async_pipe() again at each
push_success or push_error call. Preserve the existing null-pipe fallback and
response behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b4a297f1-528e-4511-8d71-6e743206dddd

📥 Commits

Reviewing files that changed from the base of the PR and between 84347cc and 37c6c41.

📒 Files selected for processing (4)
  • ffi/src/lib.rs
  • python/glide-async/python/glide/client_pool.py
  • python/glide-async/python/glide/glide_client.py
  • python/tests/async_tests/test_fork_safety.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • python/glide-async/python/glide/glide_client.py
  • python/glide-async/python/glide/client_pool.py

Comment thread ffi/src/lib.rs
Signed-off-by: Jeremy Parr-Pearson <jeremy.parr-pearson@improving.com>
@jeremyprime

Copy link
Copy Markdown
Collaborator Author

@coderabbitai help

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai rate limit to show your current review rate limit status.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
  • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
  • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai emit path instructions to emit generated path-specific review instructions for this repository.
  • @coderabbitai resolve merge conflict to automatically resolve merge conflicts.
  • @coderabbitai autofix to automatically fix issues identified in unresolved review comments.
  • @coderabbitai fix-ci to automatically fix failing CI checks by committing fixes to the current branch.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai generate configuration to create a PR that adds the current resolved configuration as .coderabbit.yaml (or show it if already present).
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

@jeremyprime

Copy link
Copy Markdown
Collaborator Author

@coderabbitai rate limit

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Your plan includes PR reviews subject to rate limits. Reviews are available now.

@jeremyprime

Copy link
Copy Markdown
Collaborator Author

@coderabbitai configuration

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Configuration used: Organization UI

CodeRabbit configuration
# Source: Organization UI (base)
language: en-US
# Source: Organization UI (base)
tone_instructions: ''
# Source: Organization UI (base)
early_access: false
# Source: Organization UI (base)
enable_free_tier: true
# Source: Organization UI (base)
inheritance: false
reviews:
  # Source: Organization UI (base)
  profile: chill
  # Source: Organization UI (base)
  request_changes_workflow: false
  # Source: Organization UI (base)
  high_level_summary: false
  # Source: Organization UI (base)
  high_level_summary_instructions: ''
  # Source: Organization UI (base)
  high_level_summary_placeholder: '`@coderabbitai` summary'
  # Source: Organization UI (base)
  high_level_summary_in_walkthrough: false
  # Source: Organization UI (base)
  auto_title_placeholder: '`@coderabbitai`'
  # Source: Organization UI (base)
  auto_title_instructions: ''
  # Source: Organization UI (base)
  review_status: false
  # Source: Organization UI (base)
  review_details: false
  # Source: defaults
  review_progress: true
  # Source: Organization UI (base)
  commit_status: true
  # Source: Organization UI (base)
  fail_commit_status: false
  # Source: Organization UI (base)
  collapse_walkthrough: true
  # Source: Organization UI (base)
  changed_files_summary: true
  # Source: Organization UI (base)
  sequence_diagrams: true
  # Source: Organization UI (base)
  estimate_code_review_effort: false
  # Source: Organization UI (base)
  assess_linked_issues: true
  # Source: Organization UI (base)
  related_issues: true
  # Source: Organization UI (base)
  related_prs: true
  # Source: Organization UI (base)
  suggested_labels: true
  # Source: defaults
  labeling_instructions: []
  # Source: defaults
  mutually_exclusive_groups: {}
  # Source: Organization UI (base)
  auto_apply_labels: false
  # Source: Organization UI (base)
  suggested_reviewers: true
  # Source: Organization UI (base)
  auto_assign_reviewers: false
  # Source: defaults
  suggested_reviewers_instructions: []
  # Source: Organization UI (base)
  in_progress_fortune: true
  # Source: Organization UI (base)
  poem: false
  # Source: Organization UI (base)
  enable_prompt_for_ai_agents: true
  # Source: defaults
  path_filters: []
  # Source: defaults
  path_instructions: []
  # Source: Organization UI (base)
  abort_on_close: true
  # Source: Organization UI (base)
  disable_cache: false
  slop_detection:
    # Source: Organization UI (base)
    enabled: true
  auto_review:
    # Source: Organization UI (base)
    enabled: true
    # Source: Organization UI (base)
    description_keyword: ''
    # Source: Organization UI (base)
    auto_incremental_review: true
    # Source: Organization UI (base)
    auto_pause_after_reviewed_commits: 5
    # Source: defaults
    ignore_title_keywords: []
    # Source: defaults
    labels: []
    # Source: Organization UI (base)
    drafts: false
    # Source: defaults
    base_branches: []
    # Source: defaults
    ignore_usernames: []
  finishing_touches:
    docstrings:
      # Source: Organization UI (base)
      enabled: false
    unit_tests:
      # Source: Organization UI (base)
      enabled: false
    simplify:
      # Source: Organization UI (base)
      enabled: false
    autofix:
      # Source: defaults
      enabled: true
    fix_ci:
      # Source: defaults
      enabled: true
    resolve_merge_conflict:
      # Source: defaults
      enabled: true
    # Source: defaults
    custom: []
  pre_merge_checks:
    # Source: Organization UI (base)
    override_requested_reviewers_only: false
    docstrings:
      # Source: Organization UI (base)
      mode: warning
      # Source: Organization UI (base)
      threshold: 80
    title:
      # Source: Organization UI (base)
      mode: warning
      # Source: Organization UI (base)
      requirements: ''
    description:
      # Source: Organization UI (base)
      mode: warning
    issue_assessment:
      # Source: Organization UI (base)
      mode: warning
    # Source: defaults
    custom_checks: []
  # Source: defaults
  post_merge_actions: []
  tools:
    ast-grep:
      # Source: defaults
      rule_dirs: []
      # Source: defaults
      util_dirs: []
      # Source: Organization UI (base)
      essential_rules: true
      # Source: defaults
      packages: []
    shellcheck:
      # Source: Organization UI (base)
      enabled: true
    ruff:
      # Source: Organization UI (base)
      enabled: true
    markdownlint:
      # Source: Organization UI (base)
      enabled: true
    github-checks:
      # Source: Organization UI (base)
      enabled: true
      # Source: Organization UI (base)
      timeout_ms: 90000
    languagetool:
      # Source: Organization UI (base)
      enabled: true
      # Source: defaults
      enabled_rules: []
      # Source: defaults
      disabled_rules: []
      # Source: defaults
      enabled_categories: []
      # Source: defaults
      disabled_categories: []
      # Source: Organization UI (base)
      enabled_only: false
      # Source: Organization UI (base)
      level: default
    biome:
      # Source: Organization UI (base)
      enabled: true
    hadolint:
      # Source: Organization UI (base)
      enabled: true
    swiftlint:
      # Source: Organization UI (base)
      enabled: true
    phpstan:
      # Source: Organization UI (base)
      enabled: true
      # Source: Organization UI (base)
      level: default
    phpmd:
      # Source: Organization UI (base)
      enabled: true
    phpcs:
      # Source: Organization UI (base)
      enabled: true
    golangci-lint:
      # Source: Organization UI (base)
      enabled: true
    yamllint:
      # Source: Organization UI (base)
      enabled: true
    gitleaks:
      # Source: Organization UI (base)
      enabled: true
    trufflehog:
      # Source: Organization UI (base)
      enabled: true
    checkov:
      # Source: Organization UI (base)
      enabled: true
    tflint:
      # Source: Organization UI (base)
      enabled: true
    detekt:
      # Source: Organization UI (base)
      enabled: true
    eslint:
      # Source: Organization UI (base)
      enabled: true
      e18e:
        # Source: defaults
        enabled: true
    flake8:
      # Source: Organization UI (base)
      enabled: true
    fbinfer:
      # Source: defaults
      enabled: true
      # Source: defaults
      enable_java: false
    fortitudeLint:
      # Source: Organization UI (base)
      enabled: true
    rubocop:
      # Source: Organization UI (base)
      enabled: true
    buf:
      # Source: Organization UI (base)
      enabled: true
    regal:
      # Source: Organization UI (base)
      enabled: true
    actionlint:
      # Source: Organization UI (base)
      enabled: true
    zizmor:
      # Source: defaults
      enabled: true
    pmd:
      # Source: Organization UI (base)
      enabled: true
    clang:
      # Source: Organization UI (base)
      enabled: true
    cppcheck:
      # Source: Organization UI (base)
      enabled: true
    opengrep:
      # Source: Organization UI (base)
      enabled: true
    semgrep:
      # Source: Organization UI (base)
      enabled: true
    circleci:
      # Source: Organization UI (base)
      enabled: true
    clippy:
      # Source: Organization UI (base)
      enabled: true
    sqlfluff:
      # Source: Organization UI (base)
      enabled: true
    squawk:
      # Source: defaults
      enabled: true
    trivy:
      # Source: Organization UI (base)
      enabled: true
    prismaLint:
      # Source: Organization UI (base)
      enabled: true
    pylint:
      # Source: Organization UI (base)
      enabled: true
    oxc:
      # Source: Organization UI (base)
      enabled: true
    shopifyThemeCheck:
      # Source: Organization UI (base)
      enabled: true
    luacheck:
      # Source: Organization UI (base)
      enabled: true
    brakeman:
      # Source: Organization UI (base)
      enabled: true
    dotenvLint:
      # Source: Organization UI (base)
      enabled: true
    htmlhint:
      # Source: Organization UI (base)
      enabled: true
    stylelint:
      # Source: Organization UI (base)
      enabled: true
    checkmake:
      # Source: Organization UI (base)
      enabled: true
    osvScanner:
      # Source: Organization UI (base)
      enabled: true
    oasdiff:
      # Source: defaults
      enabled: true
    reactDoctor:
      # Source: defaults
      enabled: true
    presidio:
      # Source: Organization UI (base)
      enabled: false
    blinter:
      # Source: Organization UI (base)
      enabled: true
    smartyLint:
      # Source: Organization UI (base)
      enabled: true
    emberTemplateLint:
      # Source: Organization UI (base)
      enabled: true
    skillspector:
      # Source: defaults
      enabled: true
    psscriptanalyzer:
      # Source: Organization UI (base)
      enabled: true
chat:
  # Source: Organization UI (base)
  art: true
  # Source: Organization UI (base)
  allow_non_org_members: true
  # Source: Organization UI (base)
  auto_reply: true
  integrations:
    jira:
      # Source: Organization UI (base)
      usage: auto
    linear:
      # Source: Organization UI (base)
      usage: auto
knowledge_base:
  # Source: Organization UI (base)
  opt_out: false
  web_search:
    # Source: Organization UI (base)
    enabled: true
  code_guidelines:
    # Source: Organization UI (base)
    enabled: true
    # Source: defaults
    filePatterns: []
  learnings:
    # Source: Organization UI (base)
    scope: auto
    # Source: defaults
    approval_delay: 0
  issues:
    # Source: Organization UI (base)
    scope: auto
  jira:
    # Source: Organization UI (base)
    usage: auto
    # Source: defaults
    project_keys: []
    # Source: defaults
    excluded_project_keys: []
  linear:
    # Source: Organization UI (base)
    usage: auto
    # Source: defaults
    team_keys: []
  pull_requests:
    # Source: Organization UI (base)
    scope: auto
  mcp:
    # Source: Organization UI (base)
    usage: auto
    # Source: defaults
    disabled_servers: []
  # Source: defaults
  automatic_repository_linking: false
  # Source: defaults
  linked_repositories: []
code_generation:
  docstrings:
    # Source: Organization UI (base)
    language: en-US
    # Source: defaults
    path_instructions: []
  unit_tests:
    # Source: defaults
    path_instructions: []
issue_enrichment:
  auto_enrich:
    # Source: Organization UI (base)
    enabled: false
  planning:
    # Source: Organization UI (base)
    enabled: true
    auto_planning:
      # Source: Organization UI (base)
      enabled: true
      # Source: defaults
      labels: []
  labeling:
    # Source: defaults
    labeling_instructions: []
    # Source: Organization UI (base)
    auto_apply_labels: false

@xShinnRyuu xShinnRyuu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

.

@xShinnRyuu xShinnRyuu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

.

Signed-off-by: Jeremy Parr-Pearson <jeremy.parr-pearson@improving.com>
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.

Python client: set() futures hang indefinitely under concurrent cluster-mode load, request_timeout never fires (2.5.0 regression from 2.4.2)

2 participants