# RxJava 4 Admin & Architecture Guide

This guide is for engineers, architects, and contributors who need to understand RxJava 4 from the inside: how the library is structured, how operators are implemented, how schedulers and backpressure work under the hood, how to build and customize it, and where its current edges are. For day-to-day usage (installing, creating flows, operators), see the companion **User Guide**.

> [!NOTE]
> RxJava 4 (`io.reactivex.rxjava4`, build version `4.0.0-SNAPSHOT`) is a forward-looking rewrite of RxJava targeting **Java 26** and `java.util.concurrent.Flow`, adding virtual-thread-native APIs. Where this guide describes APIs marked "in development" in the source, it says so explicitly.

---

## 1. Executive Summary

RxJava is the JVM implementation of [ReactiveX](http://reactivex.io) — a library for composing asynchronous, event-based programs with **observable sequences**. It implements the observer pattern, extends it with hundreds of composition operators, and abstracts away threading, synchronization, and concurrent data structures.

| Attribute | Value |
|-----------|-------|
| Project | ReactiveX/RxJava |
| Version line | 4.x (`4.0.0-SNAPSHOT` at this commit) |
| Package root | `io.reactivex.rxjava4` |
| Maven coordinates | `io.reactivex.rxjava4:rxjava` |
| Language / target | Java 26 (build toolchain JDK 26) |
| Runtime dependencies | **None** (single self-contained JAR) |
| Build system | Gradle (`java-library`) |
| License | Apache License 2.0 |
| Distribution | Maven Central (releases) + Sonatype snapshots |
| Packaging | Plain JAR + JPMS `module-info` + OSGi (bnd) metadata, multi-release |
| Lead maintainer | David Karnok (`akarnokd`) |

**What's new in 4.x at a glance:**

- Native **Java 26** implementation built on `java.util.concurrent.Flow`.
- **Virtual thread** support: `Schedulers.virtual()`, `Flowable.virtualCreate(...)` / `virtualTransform(...)`, and the new `Streamable<T>` / `Streamer<T>` async-enumerable types.
- `record`-based configuration types (`FlatMapConfig`, `GenericConfig`) to reduce operator overload bloat.
- Java `Cleaner` API used for resource-leak detection and adaptive cleanup.
- `Schedulers.io()` **deprecated** in favor of `cached()` / `virtual()`.
- System properties renamed to the `rx4.*` prefix.
- No third-party runtime dependency (Reactive Streams is now satisfied via `java.util.concurrent.Flow`).

---

## 2. Architecture

### 2.1 The reactive type system

RxJava's design follows the [Reactive Streams](https://github.com/reactive-streams/reactive-streams-jvm) specification: every reactive type has a **class** (with factory + operator methods), a **source interface**, and a **consumer interface**.

| Cardinality | Class | Source interface | Consumer |
|-------------|-------|------------------|----------|
| 0..N, backpressured | `Flowable` | `Publisher` (Reactive Streams / `Flow.Publisher`) | `Subscriber` |
| 0..N, unbounded | `Observable` | `ObservableSource` | `Observer` |
| 1 item or error | `Single` | `SingleSource` | `SingleObserver` |
| 0..1 item or error | `Maybe` | `MaybeSource` | `MaybeObserver` |
| 0 items (completion or error) | `Completable` | `CompletableSource` | `CompletableObserver` |

The base classes are declared as abstract; for example:

```java
public abstract non-sealed class Flowable<@NonNull T>
        implements Publisher<T>, FlowableDocBasic<T> { ... }
```

`Flowable` is a Reactive Streams `Publisher`, making it interoperable with any spec-compliant library and with `java.util.concurrent.Flow`. `Observable` implements the RxJava-specific `ObservableSource<T>`.

> [!NOTE]
> The `...Source` interfaces (`ObservableSource`, `SingleSource`, etc.) are **RxJava-specific** and not part of the Reactive Streams spec — there is no `FlowableSource` because Reactive Streams already provides `Publisher`. `Single`, `Maybe`, and `Completable` have no equivalent in Rx.NET.

### 2.2 Signal contracts

Every flow obeys a strict signal protocol. Violating it produces a `ProtocolViolationException`.

| Type | Contract |
|------|----------|
| `Flowable` / `Observable` | `onSubscribe onNext* (onError \| onComplete)?` |
| `Single` | `onSubscribe (onError \| onSuccess)?` |
| `Maybe` | `onSubscribe (onSuccess \| onError \| onComplete)?` |
| `Completable` | `onSubscribe (onError \| onComplete)?` |

Two lifecycle/cancellation abstractions exist:

- **`Subscription`** — the Reactive Streams handle on a `Flowable`. Carries `request(long n)` (interactive flow control) and `cancel()`.
- **`Disposable`** — the lifecycle handle for non-backpressured types (`Observable`, `Single`, `Maybe`, `Completable`). Carries `dispose()` / `isDisposed()`.

### 2.3 How operators are implemented

Unlike RxJava 1.x (which wrapped an `OnSubscribe` callback and used `lift`), the base classes are **abstract, stateless, and do not wrap a callback** — saving an allocation per operator at assembly time. Operators are concrete subclasses of the base type.

The `subscribe(...)` method is `final` and delegates to a protected `subscribeActual(...)` that each operator implements:

```java
@Override
public final void subscribe(Subscriber<? super T> s) {
    subscribeActual(hook.onSubscribe(s));   // assembly/subscribe hook applied here
}

protected abstract void subscribeActual(Subscriber<? super T> s);
```

A representative operator — `map`:

```java
public final class FlowableMap<T, R> extends Flowable<R> {
    final Flowable<? extends T> source;
    final Function<? super T, ? extends R> mapper;

    public FlowableMap(Flowable<? extends T> source, Function<? super T, ? extends R> mapper) {
        this.source = source;
        this.mapper = mapper;
    }

    @Override
    protected void subscribeActual(Subscriber<? super R> subscriber) {
        source.subscribe(new FlowableMapSubscriber<>(subscriber, mapper));
    }

    static final class FlowableMapSubscriber<T, R> implements Subscriber<T>, Subscription { /* ... */ }
}
```

Each public operator method wraps construction in the **assembly hook** so plugins can observe/replace the assembled flow:

```java
public final Flowable<R> map(Function<? super T, ? extends R> mapper) {
    return RxJavaPlugins.onAssembly(new FlowableMap<>(this, mapper));
}
```

Because Java lacks extension methods, you "add" operators externally via `lift(operator)` (operate at the consumer level) or `compose(transformer)` (operate at the flow level). Other Reactive Streams libraries are pulled in via `Flowable.fromPublisher(Publisher)`.

### 2.4 Operator lifecycle rules

An operator must:

1. Propagate `subscribe` upstream toward the producer.
2. Obey the RxJava contract — serialize all events; treat `onError`/`onComplete` as terminal.
3. Clean up resources by watching `onError`, `onComplete`, **and** `cancel`/`dispose`.
4. Propagate `cancel`/`dispose` upstream.
5. For `Flowable`, negotiate and propagate `request(n)`.

The terminal `subscribe` does **not** call `cancel`/`dispose` after receiving `onComplete`/`onError` — terminal events already release the chain.

### 2.5 Operator fusion (performance internals)

RxJava optimizes the declarative chain through two levels of **fusion**. This is internal machinery, but understanding it helps when profiling or implementing custom operators.

**Macro-fusion** — assembly-time rewrites of operator pairs `a().b()`:

- `b` recognizes `a` and elides itself (`empty().flatMap()` → no-op).
- `b` substitutes a more efficient operator (`just().subscribeOn()` → `just().observeOn()` / `ScalarScheduledPublisher`).
- Adjacent same operators merge parameters (`filter(p1).filter(p2)` → `filter(p1 && p2)`).

Sources can opt in by implementing `java.util.Callable` (value extracted at/after subscription) or RxJava's `ScalarCallable` (value safely extractable at assembly time). A `null` from a `Callable`/`ScalarCallable` is treated as an empty source (Reactive Streams forbids `null` values).

**Micro-fusion** — reuses internal structures across operator pairs:

- **`ConditionalSubscriber`** adds `boolean tryOnNext(T value)`, letting operators like `filter` avoid wasteful `request(1)` calls when an item is dropped.
- **Queue-fusion** (`QueueSubscription` / `QueueFuseable`) lets adjacent operators share one queue, attaching logic at `poll()`. It negotiates a mode via `requestFusion(int)` with bit flags `NONE=0`, `SYNC=1`, `ASYNC=2`, `ANY=SYNC|ASYNC`, `BOUNDARY=4`. In SYNC mode the request/onError rail is bypassed; in ASYNC mode availability is signalled via `onNext(null)`.

> [!IMPORTANT]
> A consequence of queue-fusion: an intermediate operator must **not** forward an upstream `Subscription` directly to `downstream.onSubscribe(...)`, or the fused chain may skip it. Operators implement both `Subscriber` and `Subscription` on themselves and delegate `request`/`cancel` upstream while calling `child.onSubscribe(this)`.

### 2.6 The plugin / hook system (`RxJavaPlugins`)

`io.reactivex.rxjava4.plugins.RxJavaPlugins` is the global extension point. It exposes:

- **Assembly hooks** — `setOnFlowableAssembly`, `setOnObservableAssembly`, `setOnSingleAssembly`, `setOnMaybeAssembly`, `setOnCompletableAssembly`, `setOnConnectableFlowableAssembly`. Used for instrumentation, debugging (e.g. assembly-stack tracing), and testing.
- **Scheduler hooks** — `setInit{Computation,Cached,Single,NewThread,Virtual}SchedulerHandler` (one-time, at init) and `set{...}SchedulerHandler` (runtime override), plus `createComputationScheduler(ThreadFactory)` etc. for fresh instances.
- **Error handling** — `setErrorHandler(Consumer<Throwable>)` for undeliverable errors; `onError(Throwable)` is where `UndeliverableException`/`OnErrorNotImplementedException` are routed.
- **Schedule hook** — `setScheduleHandler(Function<Runnable, Runnable>)` wraps every task submitted to a scheduler.
- **Safety toggles** — `setFailOnNonBlockingScheduler(boolean)` makes blocking on `computation()`/`single()` throw; `lockdown()` freezes all plugin configuration (call after app init to prevent later tampering); `reset()` restores defaults.

> [!TIP]
> In production, configure `RxJavaPlugins` once during application startup, set a global `setErrorHandler(...)` as a safety net for undeliverable errors, then call `RxJavaPlugins.lockdown()` to prevent runtime reconfiguration.

### 2.7 Configuration records (4.x)

The `io.reactivex.rxjava4.core.config` package introduces `record`-based configuration to collapse the combinatorial explosion of operator overloads:

| Record | Fields | Used by |
|--------|--------|---------|
| `FlatMapConfig` | `boolean delayErrors`, `int maxConcurrency`, `int bufferSize` | `flatMap`-family operators |
| `GenericConfig` | `boolean delayErrors`, `int bufferSize`, `int prefetch` | prefetch/buffer-tunable operators |

Both validate positivity of numeric fields (via `ObjectHelper.verifyPositive`) and default sizes to `Flowable.bufferSize()`. They carry convenience constructors so callers can specify only the fields they care about.

```java
// equivalent ways to construct a flatMap config
new FlatMapConfig();                       // no delay, default sizes
new FlatMapConfig(true);                   // delay errors
new FlatMapConfig(8);                      // maxConcurrency = 8
new FlatMapConfig(true, 8, 256);           // fully customized
```

> [!NOTE]
> Source comments note these are records *"once value classes are available"* — they're a stepping stone toward Project Valhalla value classes.

---

## 3. Scheduler Internals

The `io.reactivex.rxjava4.schedulers.Schedulers` class is a `final` utility holding lazily-initialized, shared `Scheduler` singletons (`COMPUTATION`, `CACHED`, `VIRTUAL`, `SINGLE`, `NEW_THREAD`, `TRAMPOLINE`), each created through an `init...SchedulerHandler` hook and accessed through an `on...Scheduler` hook.

### 3.1 Standard scheduler implementations

| Factory | Backing implementation | Notes |
|---------|------------------------|-------|
| `computation()` | `ComputationScheduler` — pool of single-threaded `ScheduledExecutorService`s, sized to `Runtime.availableProcessors()` | Default for async operators; do not block on it |
| `cached()` | `CachedScheduler` — dynamically growing pool that reuses idle workers | **New in 4.x**; classic blocking-I/O scheduler; can grow unbounded |
| `virtual()` | `DeferredExecutorScheduler` over `Executors.newVirtualThreadPerTaskExecutor()` per worker | **New in 4.x**; for virtual-blocking / many-IO work |
| `single()` | `SingleScheduler` — one shared background thread, FIFO | Sequential work; do not block on it |
| `newThread()` | `NewThreadScheduler` — a new single-threaded executor per unit of work | Unbounded thread creation; rarely the right choice |
| `trampoline()` | `TrampolineScheduler` (singleton) | Queues on the current thread; not overridable via plugins |
| `io()` | Delegates to `cached()` | **Deprecated since 4.0.0** |

### 3.2 Wrapping executors

- `from(Executor)` — basic wrapper; non-interruptible tasks; timed tasks borrow `single()` for the delay.
- `from(Executor, interruptibleWorker)` — disposing a task interrupts it.
- `from(Executor, interruptibleWorker, fair)` — `fair=true` reschedules between tasks so the underlying executor can interleave other work.
- `fromSupplier(Supplier<Executor>, interruptible, fair)` — each worker obtains its own `Executor`.

If the underlying executor supports `ScheduledExecutorService`, timed/periodic tasks use it directly; otherwise they fall back to `single()` for the timing. A `RejectedExecutionException` from the executor is routed to `RxJavaPlugins.onError(...)`.

### 3.3 Lifecycle

`Schedulers.shutdown()` and `Schedulers.start()` stop/start the standard schedulers (idempotent, thread-safe). Custom schedulers created via `RxJavaPlugins.create...Scheduler(ThreadFactory)` require a manual `Scheduler.shutdown()` so the JVM (or a J2EE container) can unload cleanly.

### 3.4 Scheduler tuning via system properties

Set these **before** the `Schedulers` class is first referenced:

| Property | Type | Effect |
|----------|------|--------|
| `rx4.computation-threads` | int | Thread count for `computation()` (default: available CPUs) |
| `rx4.computation-priority` | int | Thread priority for `computation()` |
| `rx4.cached-keep-alive-time` | long | Keep-alive for `cached()`/`io()` workers |
| `rx4.cached-priority` | int | Thread priority for `cached()`/`io()` |
| `rx4.cached-scheduled-release` | boolean | `true` = release workers only after the task finishes (*scheduled* mode); default `false` = *eager* |
| `rx4.newthread-priority` | int | Thread priority for `newThread()` |
| `rx4.single-priority` | int | Thread priority for `single()` |
| `rx4.purge-enabled` | boolean | Enable purging of backing thread pools (default `true`) |
| `rx4.scheduler.use-nanotime` | boolean | Use `System.nanoTime()` for `Scheduler.now(...)` instead of `currentTimeMillis()` |
| `rx4.buffer-size` | int | Default internal buffer size (`Flowable.bufferSize()`; default `128`) |

> [!WARNING]
> The `rx4.*` prefix is current as of this commit, but a rename to `rxjava4.*` is tracked in issue [#8081](https://github.com/ReactiveX/RxJava/issues/8081). Pin to a specific release and re-check property names when upgrading within 4.x.

### 3.5 The `TestScheduler`

`TestScheduler` (in the `schedulers` package) is a non-thread-safe scheduler with a manually advanced **virtual clock** — essential for deterministic testing of time-based operators (`debounce`, `interval`, `timeout`). It queues tasks in a `PriorityBlockingQueue` ordered by virtual time, and you advance time explicitly. Companion testing tools are `TestSubscriber` and `TestObserver`.

---

## 4. Backpressure Internals

Backpressure is interactive flow control: a downstream `Subscriber` calls `Subscription.request(n)` to grant the upstream permission to emit up to `n` items. Only `Flowable` participates.

- **`BackpressureStrategy`** (`{MISSING, ERROR, BUFFER, DROP, LATEST}`) — chosen when converting an `Observable` to a `Flowable` via `toFlowable(strategy)`.
- **`onBackpressureBuffer / onBackpressureDrop / onBackpressureLatest`** — applied to an existing `Flowable` for finer control. `onBackpressureBuffer` has bounded-capacity overloads that take a `BackpressureOverflowStrategy` (`{ERROR, DROP_OLDEST, DROP_LATEST}`).
- **Default buffer/prefetch size** is `128`, overridable via `rx4.buffer-size` and readable via `Flowable.bufferSize()`.

When backpressure is violated, RxJava raises `MissingBackpressureException` (downstream not requesting) or `QueueOverflowException` (concurrent unsynchronized `onNext`, or a producer ignoring backpressure entirely).

---

## 5. Repository Structure

```
RxJava/
├── build.gradle              Gradle build (java-library, checkstyle, jmh, license, bnd/OSGi, maven-publish)
├── settings.gradle           rootProject.name = 'rxjava'
├── gradle.properties         group/version/POM metadata (group=io.reactivex.rxjava4)
├── gradlew / gradlew.bat     Gradle wrapper
├── DESIGN.md                 Library design: terminology, contracts, fusion, creation model
├── CONTRIBUTING.md           Contribution + AI-contribution policy, license header
├── SECURITY.md               Private vulnerability disclosure policy
├── README.md                 Project overview + usage guide
├── codecov.yml               Coverage gate (project & patch target 95%)
├── push_javadoc.sh           CI script: publishes Javadoc to the gh-pages branch
├── pmd.xml                   PMD ruleset
├── config/
│   ├── checkstyle/           checkstyle.xml + suppressions.xml
│   └── license/              License HEADER templates
├── gradle/                   wrapper + javadoc_cleanup.gradle
└── src/
    ├── main/
    │   ├── java/io/reactivex/rxjava4/
    │   │   ├── core/                Base types: Flowable, Observable, Single, Maybe, Completable,
    │   │   │   │                    Scheduler, Notification, BackpressureStrategy, Streamable,
    │   │   │   │                    Streamer, Virtual{Emitter,Generator,Transformer}, *Emitter, *Source
    │   │   │   ├── core/config/     FlatMapConfig, GenericConfig (record-based config)
    │   │   │   └── core/docs/       Javadoc helper interfaces (e.g. FlowableDocBasic)
    │   │   ├── annotations/         @Beta, @Experimental, @NonNull, @SchedulerSupport, etc.
    │   │   ├── disposables/         Disposable, CompositeDisposable, DisposableContainer
    │   │   ├── exceptions/          CompositeException, MissingBackpressureException, ...
    │   │   ├── functions/           Function, BiFunction, Consumer, Predicate, Supplier, ...
    │   │   ├── flowables/           ConnectableFlowable, GroupedFlowable
    │   │   ├── observables/         ConnectableObservable, GroupedObservable
    │   │   ├── observers/           Public observer base classes
    │   │   ├── subscribers/         Public subscriber base classes (incl. TestSubscriber)
    │   │   ├── parallel/            ParallelFlowable
    │   │   ├── processors/          PublishProcessor, BehaviorProcessor, ...
    │   │   ├── subjects/            PublishSubject, BehaviorSubject, ReplaySubject, ...
    │   │   ├── operators/           Public operator SPI types
    │   │   ├── plugins/             RxJavaPlugins (global hooks)
    │   │   ├── schedulers/          Schedulers, TestScheduler, Timed, SchedulerRunnableIntrospection
    │   │   └── internal/            PRIVATE API — operators/, schedulers/, observers/, fuseable/,
    │   │       │                    queue/, util/, functions/, jdk8/, subscriptions/, ...
    │   │       └── operators/       The actual operator implementations, by base type
    │   ├── module/                  module-info.java (JPMS)
    │   └── resources/META-INF/
    │       └── proguard/rxjava4.pro  R8/ProGuard consumer rules
    ├── test/                        Unit + Reactive Streams TCK tests (JUnit/TestNG)
    └── jmh/                         JMH microbenchmarks
```

The single largest files are the base-type classes — `Flowable.java` (~1.2 MB), `Observable.java` (~980 KB), `Maybe.java` (~336 KB), `Single.java` (~305 KB), `Completable.java` (~180 KB) — because each declares the full operator surface (hundreds of methods and overloads). `Flowable` alone exposes ~169 instance operators plus dozens of static factories.

> [!CAUTION]
> Everything under `io.reactivex.rxjava4.internal.*` is **private API**. It is the bulk of the codebase (the actual operator implementations live in `internal/operators/{flowable,observable,single,maybe,completable,mixed,parallel}`), but it can change at any time and must not be referenced by application code.

---

## 6. Build, Test & Release

### 6.1 Building

```bash
git clone git@github.com:ReactiveX/RxJava.git
cd RxJava/
./gradlew build
```

```text
BUILD SUCCESSFUL
```

The build runs compilation, checkstyle, the JUnit and TestNG test suites, and JaCoCo coverage.

### 6.2 Toolchain and Java version

```groovy
java {
    toolchain { languageVersion = JavaLanguageVersion.of("26") }
    sourceCompatibility = JavaVersion.VERSION_26
    targetCompatibility = JavaVersion.VERSION_26
}
```

An "Outreach Program" hook lets CI compile against an experimental JDK:

```bash
JDK_EXPERIMENTAL=27 ./gradlew build   # use a newer toolchain than the default 26
```

When `JDK_EXPERIMENTAL` is set, JaCoCo coverage is skipped (the `jacoco` plugin is only applied for the baseline build).

### 6.3 Test stack

| Concern | Tooling |
|---------|---------|
| Unit tests | JUnit 4.13.2 + JUnit Jupiter 6.0.3 (`useJUnitPlatform()`) |
| Secondary runner | TestNG 7.12.0 (`./gradlew testNG`, wired into `check`) |
| Mocking | Mockito 4.11.0 |
| Spec compliance | Reactive Streams TCK 1.0.4 (`reactive-streams-tck`, `reactive-streams-tck-flow`) |
| Test utilities | Guava 33.6.0-jre |
| Coverage | JaCoCo, gate at **95%** project & patch (`codecov.yml`) |
| Benchmarks | JMH 1.37 (`me.champeau.jmh` plugin; `./gradlew jmh -Pjmh=<regex>`) |

Tests run with `maxHeapSize = "1200m"` and parallel forks (all CPUs on CI, half locally).

> [!NOTE]
> All runtime dependencies above are **test-scope only**. The shipped artifact has no third-party runtime dependency.

### 6.4 Packaging

The build produces a single JAR enriched with:

- **JPMS**: `module-info.java` (from `src/main/module/`), assembled as a multi-release class for Java 9+.
- **OSGi**: bnd (`biz.aQute.bnd.builder`) generates the bundle manifest — `Bundle-SymbolicName: io.reactivex.rxjava4.rxjava`, exporting `io.reactivex.rxjava4.*` while hiding `io.reactivex.rxjava4.internal.*`.
- **R8/ProGuard**: `META-INF/proguard/rxjava4.pro` consumer rules.

### 6.5 Publishing & CI

- Releases publish to **Maven Central** via `com.vanniktech.maven.publish`; signing (`SIGNING_PRIVATE_KEY` / `SIGNING_PASSWORD`) is applied in `releaseMode=full`. The version is driven by the `BUILD_TAG` env var (a leading `v` is stripped).
- **Snapshots** publish to the Sonatype snapshots repository.
- **Javadoc** is published to the `gh-pages` branch by `push_javadoc.sh` (CI uses the `JAVADOCS_TOKEN` secret; gracefully skips when absent). Javadoc excludes `internal`, `test`, `perf`, and `jmh` sources and links against the JDK 26 and Reactive Streams APIs.
- A GitHub Actions **Snapshot** workflow and **OpenSSF Scorecard** run on the `4.x` branch.

> [!IMPORTANT]
> The current development branch is **`4.x`** (commit `6beb0e3`). `CONTRIBUTING.md` still instructs contributors to target `3.x` — a stale reference; submit 4.x work against the `4.x` branch.

---

## 7. Security

| Topic | Detail |
|-------|--------|
| License | Apache License 2.0 — `Copyright (c) 2016-present, RxJava Contributors` |
| Vulnerability reporting | **Privately**, via GitHub Security Advisories (`/security/advisories/new`). Do **not** open public issues. Allow ≥90 days before public disclosure. |
| Supply chain | No third-party **runtime** dependencies → minimal transitive attack surface |
| Posture signals | OpenSSF Scorecard + OpenSSF Best Practices badges on the README |
| Maintenance | Volunteer-maintained on a reasonable-effort basis |

### 7.1 Operational security considerations for integrators

- **Error leakage.** Unhandled errors flow to `RxJavaPlugins.onError(...)` and print to the thread's `UncaughtExceptionHandler`. Set a global `setErrorHandler(...)` to control where stack traces (which may contain sensitive data) are logged.
- **Resource exhaustion.** `cached()` and `newThread()` can spawn an unbounded number of threads, and `BackpressureStrategy.BUFFER` / `onBackpressureBuffer()` can buffer unboundedly. Under adversarial or bursty load this is a denial-of-service vector. Bound concurrency (`maxConcurrency`), use bounded buffers with a `BackpressureOverflowStrategy`, and apply `timeout(...)`.
- **Plugin tampering.** `RxJavaPlugins` is process-global. Call `RxJavaPlugins.lockdown()` after startup so no library or code path can later swap schedulers or error handlers.
- **Leak detection.** RxJava 4 uses the Java `Cleaner` API for resource-leak detection and adaptive cleanup — useful in long-running services to surface undisposed workers.

---

## 8. Operations & Tuning

### 8.1 Production checklist

- [ ] Configure `RxJavaPlugins` (error handler, scheduler overrides) at startup, then `lockdown()`.
- [ ] Set `rx4.computation-threads` if the default (CPU count) doesn't match your workload.
- [ ] Route blocking I/O to `cached()` or `virtual()`; keep `computation()`/`single()` non-blocking.
- [ ] Consider `RxJavaPlugins.setFailOnNonBlockingScheduler(true)` in staging to catch accidental blocking.
- [ ] Bound every fan-out (`flatMap` `maxConcurrency`) and every buffer.
- [ ] Ensure lifecycle-scoped subscriptions are disposed (use `CompositeDisposable`).
- [ ] Add `-dontwarn java.util.concurrent.Flow*` for plain ProGuard builds.

### 8.2 Diagnostics

| Need | Tool |
|------|------|
| Deterministic time-based tests | `TestScheduler` + `TestSubscriber`/`TestObserver` |
| Trace where a flow was assembled | Assembly hooks via `RxJavaPlugins.setOn*Assembly(...)` |
| Wrap/observe every scheduled task | `RxJavaPlugins.setScheduleHandler(...)` |
| Unwrap a wrapped scheduled `Runnable` | `SchedulerRunnableIntrospection.getWrappedRunnable()` |
| Centralize undeliverable errors | `RxJavaPlugins.setErrorHandler(...)` |

### 8.3 Performance notes

- Operator fusion (macro + micro) removes intermediate allocations and queue copies automatically; you benefit without doing anything, but be aware that custom operators must implement the fusion interfaces correctly to participate.
- The default buffer size (`128`) governs prefetch granularity; tune via `rx4.buffer-size` or per-operator `bufferSize`/`prefetch` arguments (or a `GenericConfig`).
- JMH benchmarks live under `src/jmh` — use them to validate the performance impact of changes (`includeTests = false`, runs with `-Djmh.ignoreLock=true`).

---

## 9. Versioning & Stability

RxJava follows semantic-ish versioning:

- **Minor** (`4.x`) — non-trivial new functionality or behavior-affecting enhancements (e.g. adding backpressure to an operator). Backwards-compatible but may differ in edge cases.
- **Patch** (`4.x.y`) — bug fixes, trivial additions (method overloads), and new `@Beta`/`@Experimental` APIs.

Stability annotations (`io.reactivex.rxjava4.annotations`):

| Annotation | Guarantee |
|------------|-----------|
| `@Beta` | May change or be removed at any time. Don't expose through a library API without shading. |
| `@Experimental` | Will almost certainly change; do not use in production. |
| `@Deprecated` | Supported until the next major release; migrate away. |
| (`internal.*` package) | Private; no guarantees whatsoever. |

Cross-line support: bug fixes are applied to both `3.x` and `4.x` where possible; new features go only to `4.x`. RxJava 3.x will be supported for **at least one year** after the 4.x official release. RxJava 1.x (EOL 2018-03-31) and 2.x (EOL 2021-02-28) are end-of-life.

---

## 10. Improvements & Known Edges

Observed from the source and the project's own issue tracker at this commit.

### 10.1 In-development 4.x APIs

Several new types carry `TODO proper docs` / `FIXME native implementation` markers in their Javadoc and bodies:

- `Streamable` / `Streamer` — documented as "TODO proper docs"; factory methods like `Streamable.create(...)` are currently implemented by delegating to `Flowable.virtualCreate(...).toStreamable()` rather than a native implementation (`// FIXME native implementation`). The default `stream()` notes it should use "a practically no-op disposable container instead".
- `Schedulers.virtual()` Javadoc is a stub ("TODO explain all the rest").

These APIs work but are not finalized — pin to a release and re-verify behavior across patch versions if you depend on them.

### 10.2 Documentation / process debt

- `CONTRIBUTING.md` still says to target the `3.x` branch; current development is on `4.x`.
- README links to wiki pages still tracked for 4.x updates (issue [#6132](https://github.com/ReactiveX/RxJava/issues/6132): "Update wiki to reflect 4.x").
- Marble-diagram coverage for `Flowable` is tracked as incomplete (issue [#5813](https://github.com/ReactiveX/RxJava/issues/5813)).
- System-property rename `rx4.*` → `rxjava4.*` is pending (issue [#8081](https://github.com/ReactiveX/RxJava/issues/8081)).

### 10.3 Community-reported issues (sample)

| Issue | Summary |
|-------|---------|
| [#6958](https://github.com/ReactiveX/RxJava/issues/6958) | Recursive `concat` can cause `StackOverflowError` |
| [#7100](https://github.com/ReactiveX/RxJava/issues/7100) | `Flowable#groupBy` race leading to a backpressure issue |
| [#6931](https://github.com/ReactiveX/RxJava/issues/6931) | `parallel` performs poorly at parallelism 10+ |
| [#7912](https://github.com/ReactiveX/RxJava/issues/7912) | Make `cache`/`replay` operators more GC-friendly |
| [#7548](https://github.com/ReactiveX/RxJava/issues/7548) | Feature request: `Observable.usingWhen()` for `Publisher`-generated resources |
| [#7422](https://github.com/ReactiveX/RxJava/issues/7422) | Feature request: `unfold` construct for `Flowable`/`Observable` |
| [#8054](https://github.com/ReactiveX/RxJava/issues/8054) | Javadoc self-check to expand `{@inheritDoc}` usage |
| [#8079](https://github.com/ReactiveX/RxJava/issues/8079) | Convert JUnit 4 tests to JUnit 6 |

> [!NOTE]
> Several open issues reference bug classes the 4.x rewrite explicitly targets — the README lists resolving anomalies in `groupBy`, `window`, and `concat` among its goals.

---

## 11. Quick Reference

### 11.1 Base types

| Type | Cardinality | Backpressure | Source iface | Consumer |
|------|-------------|--------------|--------------|----------|
| `Flowable<T>` | 0..N | yes | `Publisher` | `Subscriber` |
| `Observable<T>` | 0..N | no | `ObservableSource` | `Observer` |
| `Single<T>` | 1 / error | n/a | `SingleSource` | `SingleObserver` |
| `Maybe<T>` | 0..1 / error | n/a | `MaybeSource` | `MaybeObserver` |
| `Completable` | 0 / error | n/a | `CompletableSource` | `CompletableObserver` |

### 11.2 Schedulers

| Factory | Use | 4.x status |
|---------|-----|-----------|
| `computation()` | CPU-bound | stable |
| `cached()` | blocking I/O | new |
| `virtual()` | virtual-thread blocking | new |
| `single()` | sequential | stable |
| `newThread()` | thread-per-task | stable |
| `trampoline()` | current-thread FIFO | stable |
| `io()` | → `cached()` | deprecated |
| `from(Executor, …)` / `fromSupplier(…)` | wrap your own executor | stable |

### 11.3 Key system properties

`rx4.computation-threads`, `rx4.cached-keep-alive-time`, `rx4.cached-scheduled-release`, `rx4.scheduler.use-nanotime`, `rx4.purge-enabled`, `rx4.buffer-size` (default `128`).

### 11.4 Exception types

`MissingBackpressureException`, `QueueOverflowException`, `OnErrorNotImplementedException`, `UndeliverableException`, `CompositeException`, `ProtocolViolationException`; utilities `Exceptions.propagate(...)` / `Exceptions.throwIfFatal(...)`.

### 11.5 New 4.x APIs

`Schedulers.virtual()` · `Flowable.virtualCreate(VirtualGenerator[, Scheduler|ExecutorService])` · `Flowable.virtualTransform(VirtualTransformer[, …])` · `Flowable.toStreamable([ExecutorService])` · `Streamable<T>` / `Streamer<T>` · `VirtualEmitter` / `VirtualGenerator` / `VirtualTransformer` · `FlatMapConfig` / `GenericConfig`.

### 11.6 Resources

- Javadoc: `http://reactivex.io/RxJava/4.x/javadoc/` (snapshot: `.../snapshot/`)
- Wiki: `https://github.com/ReactiveX/RxJava/wiki`
- Issues: `https://github.com/ReactiveX/RxJava/issues`
- Design doc: `DESIGN.md` in the repository root

---

_Generated June 2026 from ReactiveX/RxJava at 6beb0e3. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
