ban-xiu opened a new issue, #16247: URL: https://github.com/apache/dubbo/issues/16247
### Pre-check - [x] I am sure that all the content I provide is in English. ### Search before asking - [x] I had searched in the [issues](https://github.com/apache/dubbo/issues?q=is%3Aissue) and found no similar issues. ### Apache Dubbo Component Java SDK (apache/dubbo) ### Dubbo Version Dubbo Java 3.3.7-SNAPSHOT ### Steps to reproduce this issue Pre-requisite: a provider configured with `register-mode=all` so Dubbo fans out the same provider URL to two registries — an interface-level one (`registry://`, e.g. ZooKeeper) and an instance-level one (`service-discovery-registry://`). 1. Configure the provider so both registries are in use, for example: ```properties dubbo.application.register-mode=all dubbo.registries.zk.address=zookeeper://127.0.0.1:2181 dubbo.registries.sd.address=zookeeper://127.0.0.1:2181?registry-type=service ``` 2. Make the **first** registry fail at registration time (easiest repro: start the provider with ZooKeeper unreachable, or let the interface-level registry throw `NoNode` during the write). The failing registry can be either arm — whichever is processed first. 3. Export a service: ```java ServiceConfig<DemoService> sc = new ServiceConfig<>(); sc.setInterface(DemoService.class); sc.setRef(new DemoServiceImpl()); sc.export(); ``` 4. Observe that `export()` throws the underlying `RuntimeException` from the failing arm and the **second** registry is never registered to, even though the two registrations are semantically independent and the user explicitly opted into dual mode with `register-mode=all`. Root cause is in `RegistryProtocol#export`: ```java // dubbo-registry-api/.../RegistryProtocol.java boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true); if (register) { register(registry, registeredProviderUrl); // <-- not guarded per-arm } ``` The single call fans out to both arms and any `RuntimeException` from one arm aborts the whole export. A minimal unit-test reproducer (no external registry needed) using a mocked `Registry`: ```java URL registryUrl = new ServiceConfigURL("zookeeper", "127.0.0.1", 2181, Map.of("check", "false")); URL providerUrl = new ServiceConfigURL("dubbo", "127.0.0.1", 20880, "com.example.DemoService", new HashMap<>()); Registry registry = Mockito.mock(Registry.class); Mockito.doThrow(new IllegalStateException("NoNode for /dubbo/...")) .when(registry).register(providerUrl); // Through the current export() -> register() path, this RuntimeException // propagates and any sibling registration never runs; there is also no // per-arm outcome emitted to FrameworkStatusReportService. ``` ### What you expected to happen Under `register-mode=all`, each arm of the dual registration should be handled independently, and outcomes should be observable per arm: 1. **Failure isolation.** A failure on one arm (interface-level OR instance-level) must not abort the other arm. Propagation should only happen when the registry URL explicitly asks for `check=true` — which is the convention Dubbo already uses elsewhere for `CHECK_KEY`. 2. **Per-arm outcome reporting.** `FrameworkStatusReportService` should emit an outcome that identifies which arm was attempted, against which registry address, for which service, and whether it succeeded or failed (and why). Today only a coarse `{application, status}` payload is emitted, so operators cannot tell which side of the dual registration failed. 3. **Accurate log tags.** The success log currently hardcodes `[INSTANCE_REGISTER]` in `ExporterChangeableWrapper#register` regardless of whether the registry is actually an instance-level one, which makes grep-based troubleshooting misleading for interface-level arms. What currently happens instead: - **(1)** The `RuntimeException` from the first arm aborts `export()` and the second arm is never tried. Users who opted into dual mode lose the healthy arm because the other arm was transiently unhealthy. - **(2)** Observers subscribed to the `registration` report key only see `{application, status=interface|instance}`. They cannot attribute a partial failure to a specific arm/registry/service, and they never see an error message. - **(3)** Relevant log line: ``` [INSTANCE_REGISTER] Registered dubbo service ... to registry zookeeper://... ``` …is printed even when the registry is `zookeeper://` (interface-level). Concretely, the expected payload shape for the outcome report is: ```json { "application": "APP", "mode": "INTERFACE_REGISTER", // or INSTANCE_REGISTER "registry": "127.0.0.1:2181", "service": "GroupA/DemoService:1.0.0", "status": "SUCCESS", // or FAILED "error": "NoNode for /dubbo/..." // present only on FAILED } ``` emitted under the same `REGISTRATION_STATUS` key so existing `FrameworkStatusReporter` extensions keep working (new fields are additive). ### Anything else - Occurs deterministically: every time one arm of a dual registration throws a `RuntimeException` synchronously. Not a flake. - The existing logger code `CONFIG_REGISTER_INSTANCE_ERROR` (5-11) is a natural fit for the failure path — no new code is needed. - Backward compatibility: - No public API removed. - `reportRegistrationStatus(...)` / `createRegistrationReport(...)` untouched; reporters that only read `application` / `status` keep working. - Users who relied on "one failure aborts everything" can keep that by setting `check=true` on the registry URL, which is the documented knob for "registration must succeed or fail fast". - Consumer-side `registry.register(...)` (for consumer URLs) is **not** part of this report; the scope here is provider-side dual registration only. ### Do you have a (mini) reproduction demo? - [x] Yes, I have a minimal reproduction demo to help resolve this issue more effectively! ### Are you willing to submit a pull request to fix on your own? - [x] Yes I am willing to submit a pull request on my own! ### Code of Conduct - [x] I agree to follow this project's [Code of Conduct](https://www.apache.org/foundation/policies/conduct) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
