Aias00 opened a new issue, #7830: URL: https://github.com/apache/shenyu/issues/7830
### Current behavior The sensitive-word plugin intends to move CPU-heavy dictionary construction away from event loops, but places `subscribeOn(boundedElastic())` after a reactive Redis publisher. That moves subscription, while asynchronous Redis result emissions can still execute the downstream construction on the Redis I/O thread. Confirmed in Apache ShenYu 2.7.2-SNAPSHOT and upstream `master@7cc1818ace246f60a608e44b5fe985dff46908e7`. The inspected upstream file is identical to the current working copy. ### Code evidence The dictionary cache-miss/expiry path in [`SensitiveWordPlugin.dictionary()`](https://github.com/apache/shenyu/blob/7cc1818ace246f60a608e44b5fe985dff46908e7/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-sensitive-word/src/main/java/org/apache/shenyu/plugin/ai/sensitive/word/SensitiveWordPlugin.java#L204-L211) is: ```java redisTemplate.opsForSet() .members(redisKey) .collectList() // building the automaton is cpu bound, keep it away from the event loop .map(loaded -> AhoCorasick.of(merge(loaded, words))) .subscribeOn(Schedulers.boundedElastic()) ``` The later [`publishOn(boundedElastic())`](https://github.com/apache/shenyu/blob/7cc1818ace246f60a608e44b5fe985dff46908e7/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-sensitive-word/src/main/java/org/apache/shenyu/plugin/ai/sensitive/word/SensitiveWordPlugin.java#L231-L233) occurs after construction. It moves subsequent request-body scanning, but cannot move work that has already executed in the preceding `map`. The rule-word-only fallback at [line 219](https://github.com/apache/shenyu/blob/7cc1818ace246f60a608e44b5fe985dff46908e7/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-sensitive-word/src/main/java/org/apache/shenyu/plugin/ai/sensitive/word/SensitiveWordPlugin.java#L212-L219) also constructs an automaton before that final handoff. Trigger: a missing/expired dictionary followed by asynchronous Redis results; the cost increases with dictionary size. CPU-heavy automaton construction can delay work sharing the Redis event-loop thread. ### Minimal operator reproduction A standalone Reactor 3.6.7 probe used the same operator ordering and a named asynchronous source to simulate the Redis emission boundary: ```java Flux.just("word") .delayElements(Duration.ofMillis(10), ioScheduler) .collectList() .map(words -> { System.out.println("BUILD_THREAD=" + Thread.currentThread().getName()); return words; }) .subscribeOn(Schedulers.boundedElastic()) .publishOn(Schedulers.boundedElastic()) .map(words -> { System.out.println("SCAN_THREAD=" + Thread.currentThread().getName()); return words; }) .block(Duration.ofSeconds(5)); ``` Observed output: ```text BUILD_THREAD=simulated-redis-io-1 SCAN_THREAD=boundedElastic-2 ``` This verifies the operator-placement behavior; it is not a live Redis integration test or a throughput benchmark. ### Expected behavior / suggested direction Explicitly schedule automaton construction after asynchronous Redis emissions, for example by placing an appropriate `publishOn` before the CPU-heavy map, or by deferring construction inside an explicitly scheduled publisher. Cover fallback construction too. Preserve the cached-path handoff needed to keep `automaton.search(body)` off the request event loop; simply deleting all scheduling would not address the problem. ### Suggested validation - Verify on a named asynchronous source that dictionary construction runs on the intended worker, rather than the source emission thread. - Exercise miss, refresh, Redis-error fallback, and valid cached-dictionary paths. - Confirm request-body scanning remains off the event loop. No production latency/throughput impact has been measured. -- 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]
