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]

Reply via email to