apache / apache/rocketmq

[RIP] Proxy Admin API — online client query as the first module

Open
#10,601 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
22.6k
Forks
12k
Avg merge
3d 1h
Merged PRs (30d)
27

Description

## Motivation

Since 5.0, gRPC clients report Settings / Subscription / Heartbeat to the Proxy via the telemetry stream, but none of this runtime state is exposed through any admin interface. Management tools are therefore blind to gRPC clients, which is the common root cause behind several long-standing issues:

- rocketmq-dashboard#381: gRPC consumer lag displayed as -1
- rocketmq-dashboard#380: Pop consumers falsely reported as NOT_CONSUME_YET
- rocketmq-dashboard#402: request code 106/206 not supported

Tactical fixes on the dashboard side (e.g. rocketmq-dashboard#424) demonstrate the need, but the systematic solution requires the Proxy to expose its client runtime view through a standard admin API.

This RIP proposes a new admin interface surface on the Proxy, starting with **online client query** as the first module. It directly serves the Control Plane 5.0 initiative.

---

## Proposal (first module)

Two read-only RPCs, defined in rocketmq-apis:

### ListClients

```
ListClients(filter, page_size, next_token) → ClientInstance[]
```

**Filter fields:** group / topic / clientId prefix / language / role / connected_after

Returns a paginated list of client connections on the local Proxy node.

### DescribeClient

```
DescribeClient(client_id) → ClientDetail
```

Returns: negotiated Settings, subscription entries, recent heartbeat records, auth status.

---

## Key Design Decisions

### D1 — Service placement (open for community discussion)

**Option A (default proposal):** Extend the existing `apache.rocketmq.v2.Admin` service with these RPCs, on the same gRPC port as messaging.

- Pro: Zero new concepts; minimal review friction; ACL isolation is independent of service placement (`proxy.admin.client` resource type works regardless).
- Con: `ChangeLogLevel` is broker-ops, client query is proxy-runtime — conceptually mixed.

**Option B (alternative):** New dedicated `ProxyAdminService`.

- Pro: Clean conceptual separation.
- Con: Adds a new service to the API surface; needs extra justification.

Feedback requested: which approach does the community prefer?

### D2 — Authorization

New ACL 2.0 resource type `proxy.admin.client` (read-only actions `LIST` / `GET`), so the admin surface has its own permission scope, independent from the data plane.

Clusters without ACL enabled can use the admin API by default, with a global `proxyAdminEnable=false` kill switch.

### D3 — Multi-proxy semantics: local view

Each Proxy returns its local view with `proxy_endpoint` + `epoch` fields; aggregation/dedup is done by the consumer (dashboard / CLI). A cluster-level aggregated view can be a future evolution.

Rationale: no cross-node consensus overhead, clean failure domains, fastest path to a usable API.

### D4 — Pagination: cursor-based

`ListClients` uses an opaque `next_token` cursor, filtering pushed down into the client managers — no full dump on proxies with a large number of connections.

Rationale: client connections are highly dynamic; offset pagination is unstable under concurrent connect/disconnect events.

### D5 — Dual-protocol coverage

Both gRPC and Remoting clients connected to the Proxy are included, distinguished by a `protocol` field, so management tools get a single protocol-agnostic client list.

---

## Proto Sketch

```protobuf
message ClientFilter {
optional string consumer_group = 1;
optional string topic = 2;
optional string client_id_prefix = 3;
optional apache.rocketmq.v2.Language language = 4;
optional ClientRole role = 5; // PRODUCER / PUSH_CONSUMER / SIMPLE_CONSUMER
optional google.protobuf.Timestamp connected_after = 6;
}

message ListClientsRequest {
ClientFilter filter = 1;
int32 page_size = 2; // Server-enforced max, e.g. 1000
string next_token = 3;
}

message ClientInstance {
string client_id = 1;
Language language = 2;
string client_version = 3;
string access_point = 4;
google.protobuf.Timestamp connect_time = 5;
google.protobuf.Timestamp last_active_time = 6;
ClientRole role = 7;
repeated string groups = 8;
string auth_subject = 9;
}

message ListClientsResponse {
Status status = 1;
repeated ClientInstance clients = 2;
string next_token = 3;
string proxy_endpoint = 4;
int64 epoch = 5; // Local-view: node epoch for consumer-side dedup
}

message DescribeClientRequest { string client_id = 1; }

message ClientDetail {
ClientInstance instance = 1;
Settings settings = 2; // Reuse existing Settings message
repeated SubscriptionEntry subscriptions = 3;
repeated HeartbeatRecord recent_heartbeats = 4;
AuthStatus auth_status = 5;
}
```

Reuses existing `Language`, `Settings`, `Status` from `definition.proto` — no duplication.

---

## Existing Code Foundation (verified)

The Proxy module already has the necessary infrastructure:

| Component | Class | Role |
|-----------|-------|------|
| gRPC server assembly | `GrpcServerBuilder.addService()` | Supports registering additional services |
| Client channel mgmt | `GrpcChannelManager` | clientId → channel mappings |
| Client activity | `ClientActivity` | Registration, heartbeat, telemetry |
| Client settings | `GrpcClientSettingsManager` | Settings negotiation data source |
| Client registries | `ProducerManager` / `ConsumerManager` (broker module) | The actual client data |
| Metrics | `ProxyMetricsManager` | OpenTelemetry infrastructure |
| Config | `ProxyConfig` | Centralized configuration |

---

## Implementation Plan

| Step | Repo | Description |
|------|------|-------------|
| PR-08 | rocketmq-apis | Proto definitions + buf lint |
| PR-09 | rocketmq | ClientManager `scanClients()` with inline filtering |
| PR-10 | rocketmq | `ListClients` gRPC implementation + GrpcServer registration |
| PR-11 | rocketmq | `DescribeClient` gRPC implementation |
| PR-12 | rocketmq | Filter semantics (by group/topic) + Remoting protocol coverage |
| PR-13 | rocketmq | ACL 2.0 resource registration + gRPC interceptor |
| PR-14 | rocketmq | Local-view epoch/endpoint semantics + docs |
| PR-15 | rocketmq | Admin API OpenTelemetry metrics |
| PR-16 | rocketmq | Benchmark: 1M clients, ListClients P99 < 1s |
| PR-17 | rocketmq/docs | User guide + ACL policy template |

---

## Planned Follow-up Modules (not part of first module)

- Proxy config query / hot-update
- Rate-limit quota visibility
- Client kick-out
- Pop/Batch diagnostics

---

## DISCUSS

A [DISCUSS] thread will be posted on dev@rocketmq.apache.org. Feedback is especially welcome on:

1. **Service placement** (D1): Extend existing `Admin` vs. new `ProxyAdminService`?
2. **Local view** (D3): Is local view acceptable for the first version?
3. **ACL granularity** (D2): Is `proxy.admin.client` as a single resource sufficient?

---

**References:**
- dashboard#380: Pop consumers falsely reported as NOT_CONSUME_YET
- dashboard#381: gRPC consumer lag displayed as -1
- dashboard#402: request code 106/206 not supported
- dashboard#424: Cross-broker offset matching hardening (transitional fix)

Contributor guide

Open the contributing guide

Research direction

This is a design proposal rather than an assigned implementation, with unresolved choices about service placement, local-view semantics, and ACL scope. Start by reading GrpcServerBuilder.addService(), ClientActivity, GrpcChannelManager, GrpcClientSettingsManager, ProxyConfig, and the existing definitions in rocketmq-apis. Done would require an agreed design before the listed proto, implementation, authorization, documentation, and benchmark steps can be started.

Written by the indexing model from the issue text.

Assessment

Tech stack
grpc, java
Domain
backend-api-design, distributed-systems, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.