AsperforMias opened a new issue, #3617: URL: https://github.com/apache/dubbo-go/issues/3617
## β Verification Checklist - [x] π I have searched the [existing issues](https://github.com/apache/dubbo-go/issues) and confirmed this is not a duplicate --- ## π§ Environment Information ### π Go Version go1.26.1 ### π¦ Dubbo-go Version v3.3.2 (confirmed by reproduction). The same code is present on `develop` (@ `115af01d`), so the bug is still unfixed. ### π₯οΈ Server Configuration dubbo-go v3.3.2 provider, application-level registry (`registry.type=service`) ### π» Client Configuration dubbo-go v3.3.2 consumer, application-level service discovery ### π Protocol Configuration Triple ### π Registry Configuration Nacos v2.5.1, application-level service discovery (`service-discovery-registry://...`) ### πΎ Operating System π§ Linux (Docker), reproduced on macOS Docker Desktop --- ## π Issue Details ### π Bug Description With **application-level service discovery**, a consumer reference that sets `provided_by` (e.g. `client.WithProvidedBy("demo-provider")`, or `provided_by` in yaml) **never subscribes to any instance**. The directory stays empty forever and every call fails with `No provider available`, even though the provider is healthy and registered in Nacos. Removing `provided_by` (and letting the interfaceβapp mapping come from the metadata-report / `mapping` config group) makes the exact same setup work immediately. Root cause (confirmed by source reading + reproduction): 1. `serviceDiscoveryRegistry.Subscribe` seeds the mapping listener with the `provided_by` value as its *baseline* (`oldServiceNames`): https://github.com/apache/dubbo-go/blob/v3.3.2/registry/servicediscovery/service_discovery_registry.go#L510 ```go mappingListener := NewMappingListener(s.url, url, parseServices(url.GetParam(constant.ProvidedBy, "")), notify) services := s.getServices(url, mappingListener) ... err := mappingListener.OnEvent(registry.NewServiceMappingChangedEvent(url.ServiceKey(), services)) ``` 2. `getServices` short-circuits on `provided_by` and returns the *same* set (`demo-provider`), without calling `findMappedServices` β so no mapping listener is registered for future updates either: https://github.com/apache/dubbo-go/blob/v3.3.2/registry/servicediscovery/service_discovery_registry.go#L621-L631 3. The first (and only) `OnEvent` therefore has `newServiceNames == oldServiceNames` and hits the early return in `ServiceMappingChangedListenerImpl.OnEvent`, **without ever calling `SubscribeURL`**: https://github.com/apache/dubbo-go/blob/v3.3.2/registry/servicediscovery/service_mapping_change_listener_impl.go#L83-L85 ```go if newServiceNames.Empty() || oldServiceNames.String() == newServiceNames.String() { return nil } ``` As a result, with `provided_by` set, `SubscribeURL` is never invoked: no initial `GetInstances`, no `ServiceInstancesChangedListener`, no `AddListener`. Since `getServices` never registered a mapping listener, no later mapping event can arrive to recover either. The consumer is permanently empty from the first call. Impact: any consumer using `provided_by` + application-level discovery is 100% broken (not a race β it fails deterministically on every call from startup). ### π Steps to Reproduce 1. Start a Nacos server and a dubbo-go v3.3.2 provider using application-level registry: ```go srv, _ := server.NewServer( server.WithServerProtocol(protocol.WithTriple(), protocol.WithPort(20001)), server.WithServerRegistry( registry.WithNacos(), registry.WithAddress("nacos:8848"), registry.WithRegisterService(), // application-level ), server.SetServerApplication(&global.ApplicationConfig{Name: "demo-provider"}), ) // register any triple service, e.g. com.example.GreetService, then srv.Serve() ``` 2. Start a consumer whose reference sets `provided_by`: ```go cli, _ := client.NewClient( client.WithClientRegistry( registry.WithNacos(), registry.WithAddress("nacos:8848"), registry.WithRegisterService(), ), client.SetClientApplication(&global.ApplicationConfig{Name: "demo-consumer"}), ) conn, _ := cli.DialWithInfo("com.example.GreetService", &client.ClientInfo{InterfaceName: "com.example.GreetService", MethodNames: []string{"SayHello"}}, client.WithProvidedBy("demo-provider"), // <-- the trigger ) conn.CallUnary(ctx, []any{&emptypb.Empty{}}, resp, "SayHello") ``` 3. Every call fails with `No provider available ... from registry service-discovery-registry://nacos:...`. 4. Control: delete `client.WithProvidedBy("demo-provider")` and publish the standard mapping config (dataId = interface name, group = `mapping`, content = `demo-provider`; the provider publishes it automatically) β the same consumer works on the first call. Consumer log with `provided_by` set β the mapping is found, then **nothing** (no `synchronized instance notification`, no `received instance notification event`, no `AddListener`): ``` INFO servicediscovery/service_discovery_registry.go:516 [Registry][ServiceDiscovery] find initial mapping applications "HashSet\ndemo-provider" for service com.example.GreetService # ... silence afterwards; every invocation: # Failed to invoke the method SayHello. No provider available for the service # tri://...?interface=com.example.GreetService... from registry service-discovery-registry://nacos:8848... ``` A full docker-compose reproduction (Nacos + provider + consumer, one script) is available and can be attached/shared as a repo if useful. ### β Expected Behavior `provided_by` should make the consumer subscribe to the named application(s) and fetch their instances β i.e. `SubscribeURL` must be invoked for the initial `provided_by` set, invokers built, and calls succeed. This is also how the equivalent Java Dubbo reference config behaves. ### β Actual Behavior The consumer deterministically fails every call from startup: ``` Failed to invoke the method SayHello. No provider available for the service tri://:@172.30.0.12:/?interface=com.example.GreetService&group=&version= from registry service-discovery-registry://nacos:8848?...®istry.type=service&... on the consumer 172.30.0.12 using the dubbo version 3.3.2. Please check if the providers have been started and registered. ``` The provider is registered and healthy in Nacos (verified via `/nacos/v1/ns/instance/list?serviceName=demo-provider`). ### π‘ Possible Solution The seeding of `oldServiceNames` from `provided_by` makes the *initial* subscribe look like a no-op mapping diff. Minimal-invasion options (either one fixes it; the first is smaller): 1. In `serviceDiscoveryRegistry.Subscribe`, perform the initial subscribe directly instead of routing it through the diff-based listener: ```go if !services.Empty() { s.SubscribeURL(url, notify, services) // initial subscribe is not a "mapping change" } ``` and keep `mappingListener` (with its `provided_by` seed) only for subsequent real mapping updates. `SubscribeURL` already handles listener reuse/dedup via `serviceListeners`, so this does not change behavior for the metadata-report mapping path. 2. Alternatively, seed `oldServiceNames` with an empty set so the first `OnEvent` takes the existing `oldServiceNames.Empty()` branch and calls `SubscribeURL`. The `provided_by`-seeded dedup for later identical mapping events is then lost, but `SubscribeURL`'s listener cache already makes a repeated subscribe cheap. Regression test suggestion: `Subscribe` a reference with `provided-by` set against a mocked `ServiceDiscovery`, and assert `GetInstances` is called / a `ServiceInstancesChangedListener` is installed β currently neither happens. -- 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]
