apache / apache/shenyu

[BUG] Etcd selectInstances leaks watchers that cannot be unwatched

Open Beginner friendly
#6,527 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
8.8k
Forks
3.1k
Avg merge
7d 1h
Merged PRs (30d)
85

Description

### Search before asking

- [x] I had searched in the [issues](https://github.com/apache/shenyu/issues) and found no similar issues.

### Apache ShenYu Component

shenyu-registry

### What happened

`EtcdInstanceRegisterRepository.selectInstances()` creates an etcd watcher the first time a service key is selected, but it does not keep the returned `Watch.Watcher` handle:

```java
this.client.watchKeyChanges(watchKey, Watch.listener(response -> {
...
watcherInstanceRegisterMap.put(selectKey, getInstanceRegisterFun.apply(serverNodes));
}));
```

In the same class, explicit `watchInstances()` stores watcher handles in `watchCache`:

```java
final Watch.Watcher watcher = this.client.watchKeyChanges(watchKey, Watch.listener(...));
watchCache.put(watchKey, watcher);
```

`unWatchInstances()` and `close()` only close watchers present in `watchCache`:

```java
if (watchCache.containsKey(key)) {
watchCache.get(key).forEach(Watch.Watcher::close);
watchCache.removeAll(key);
}

watchCache.values().forEach(Watch.Watcher::close);
watchCache.clear();
```

Because the watcher created by `selectInstances()` is not stored, it cannot be closed by `unWatchInstances()` or `close()`. Repeated service selections can leave background etcd watchers active for the lifetime of the client.

### What you expected to happen

Every watcher created by `EtcdInstanceRegisterRepository` should be tracked and closed. The watcher from `selectInstances()` should either be stored in `watchCache` under a consistent key, or `selectInstances()` should avoid creating a persistent watcher that the repository cannot later release.

### How to reproduce

1. Configure Etcd discovery.
2. Call `EtcdInstanceRegisterRepository.selectInstances(serviceName)` for a service key that is not already in `watcherInstanceRegisterMap`.
3. The method creates a watcher for `InstancePathConstants.buildInstanceParentPath(serviceName)`.
4. Call `unWatchInstances(...)` or `close()`.
5. Only watchers in `watchCache` are closed, so the watcher created from `selectInstances()` remains untracked.

### Debug logs

_No response_

### Environment

Current master branch.

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in EtcdInstanceRegisterRepository at selectInstances(), then compare its watcher creation with watchInstances(), unWatchInstances(), and close(). Ensure the watcher created during selection is accounted for by the repository lifecycle, and verify that selecting a service followed by unWatchInstances() or close() leaves no active watcher.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
distributed-systems
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.