Aias00 opened a new issue, #7590:
URL: https://github.com/apache/shenyu/issues/7590
## Summary
`LoadServiceDocEntryImpl.supportSwaggerPluginSet` is a `private static`
**non-volatile, non-final** field. It is written under `synchronized` on one
thread and read without any synchronization on a different thread, so the
reader is not guaranteed to ever observe the write.
## Evidence
`shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/LoadServiceDocEntryImpl.java`
```java
66: @SuppressWarnings("unchecked")
67: private static Set<String> supportSwaggerPluginSet =
Collections.emptySet();
```
**Writer** — startup path, `loadApiDocument()`:
```java
91: public synchronized void loadApiDocument() {
...
185: supportSwaggerPluginSet = new HashSet<>(pluginNames);
```
**Reader** — discovery / data-change path:
```java
208: if
(!supportSwaggerPluginSet.contains(discoverySyncData.getPluginName())) {
```
The read is reached from:
```
DiscoveryDataChangedEventSyncListener:138-139
→ eventPublisher.publishEvent(new
DataChangedEvent(ConfigGroupEnum.DISCOVER_UPSTREAM, ...))
→ DataChangedEventDispatcher:138
→ LoadServiceDocEntry.loadDocOnUpstreamChanged(...)
→
LoadServiceDocEntryImpl.getClusterLastUpdateInstance(DiscoverySyncData):208
```
i.e. a registry/sync callback thread, whereas the write happens on the admin
startup thread inside `ShenyuStandaloneService:49`. `synchronized` on the
writer does **not** create a happens-before edge for an unsynchronized reader,
so the JMM permits the reader to keep seeing the initial
`Collections.emptySet()` indefinitely.
## Impact
When the stale empty set is observed, `contains(...)` is always `false`, so
`getClusterLastUpdateInstance` returns `null` for every discovery upstream and
logs *"pluginName={} does not support pulling API documents."*. API document
pulling is then permanently disabled until the process restarts — and it may
stay broken even after a restart, since there is no ordering guarantee.
## Suggested fix
Make the field safely publishable. The minimal change:
```java
private static volatile Set<String> supportSwaggerPluginSet =
Collections.emptySet();
```
or better, make it an immutable instance field owned by the bean and
injected/refreshed explicitly, e.g.
```java
private final AtomicReference<Set<String>> supportSwaggerPluginSet =
new AtomicReference<>(Collections.emptySet());
```
with `supportSwaggerPluginSet.set(Set.copyOf(pluginNames))` on refresh.
`Set.copyOf` also removes the need for the `@SuppressWarnings("unchecked")` on
the field.
Please also consider whether the field needs to be `static` at all —
`LoadServiceDocEntryImpl` is a `@Service` singleton, so an instance field is
sufficient and avoids cross-instance sharing.
## Environment
- Branch: `master` @ `a7d80b7`
--
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]