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]

Reply via email to