7
7
package org .gridsuite .securityanalysis .server .service ;
8
8
9
9
import com .fasterxml .jackson .databind .ObjectMapper ;
10
+ import com .google .common .collect .Sets ;
10
11
import com .powsybl .commons .PowsyblException ;
11
12
import com .powsybl .computation .local .LocalComputationManager ;
12
13
import com .powsybl .contingency .Contingency ;
32
33
33
34
import java .util .ArrayList ;
34
35
import java .util .List ;
36
+ import java .util .Map ;
35
37
import java .util .Objects ;
38
+ import java .util .Set ;
36
39
import java .util .UUID ;
40
+ import java .util .concurrent .CancellationException ;
37
41
import java .util .concurrent .CompletableFuture ;
42
+ import java .util .concurrent .ConcurrentHashMap ;
38
43
import java .util .function .Consumer ;
39
- import java .util .logging .Level ;
40
44
import java .util .stream .Collectors ;
41
45
42
46
/**
43
47
* @author Geoffroy Jamgotchian <geoffroy.jamgotchian at rte-france.com>
48
+ * @author Franck Lecuyer <franck.lecuyer at rte-france.com>
44
49
*/
45
50
@ Service
46
51
public class SecurityAnalysisWorkerService {
@@ -62,15 +67,26 @@ public class SecurityAnalysisWorkerService {
62
67
63
68
private SecurityAnalysisResultPublisherService resultPublisherService ;
64
69
70
+ private SecurityAnalysisStoppedPublisherService stoppedPublisherService ;
71
+
72
+ private Map <UUID , CompletableFuture <SecurityAnalysisResult >> futures = new ConcurrentHashMap <>();
73
+
74
+ private Map <UUID , SecurityAnalysisCancelContext > cancelComputationRequests = new ConcurrentHashMap <>();
75
+
76
+ private Set <UUID > runRequests = Sets .newConcurrentHashSet ();
77
+
65
78
public SecurityAnalysisWorkerService (NetworkStoreService networkStoreService , ActionsService actionsService ,
66
79
SecurityAnalysisResultRepository resultRepository , ObjectMapper objectMapper ,
67
- SecurityAnalysisConfigService configService , SecurityAnalysisResultPublisherService resultPublisherService ) {
80
+ SecurityAnalysisConfigService configService ,
81
+ SecurityAnalysisResultPublisherService resultPublisherService ,
82
+ SecurityAnalysisStoppedPublisherService stoppedPublisherService ) {
68
83
this .networkStoreService = Objects .requireNonNull (networkStoreService );
69
84
this .actionsService = Objects .requireNonNull (actionsService );
70
85
this .resultRepository = Objects .requireNonNull (resultRepository );
71
86
this .objectMapper = Objects .requireNonNull (objectMapper );
72
87
this .configService = Objects .requireNonNull (configService );
73
88
this .resultPublisherService = Objects .requireNonNull (resultPublisherService );
89
+ this .stoppedPublisherService = Objects .requireNonNull (stoppedPublisherService );
74
90
}
75
91
76
92
private static String sanitizeParam (String param ) {
@@ -111,6 +127,10 @@ private Mono<Network> getNetwork(UUID networkUuid, List<UUID> otherNetworkUuids)
111
127
}
112
128
113
129
public Mono <SecurityAnalysisResult > run (SecurityAnalysisRunContext context ) {
130
+ return run (context , null );
131
+ }
132
+
133
+ private Mono <SecurityAnalysisResult > run (SecurityAnalysisRunContext context , UUID resultUuid ) {
114
134
Objects .requireNonNull (context );
115
135
116
136
LOGGER .info ("Run security analysis on contingency lists: {}" , context .getContingencyListNames ().stream ().map (SecurityAnalysisWorkerService ::sanitizeParam ).collect (Collectors .toList ()));
@@ -125,26 +145,75 @@ public Mono<SecurityAnalysisResult> run(SecurityAnalysisRunContext context) {
125
145
return Mono .zip (network , contingencies )
126
146
.flatMap (tuple -> {
127
147
SecurityAnalysis securityAnalysis = configService .getSecurityAnalysisFactory ().create (tuple .getT1 (), LocalComputationManager .getDefault (), 0 );
128
- CompletableFuture <SecurityAnalysisResult > result = securityAnalysis .run (VariantManagerConstants .INITIAL_VARIANT_ID , context .getParameters (), n -> tuple .getT2 ());
129
- return Mono .fromCompletionStage (result );
148
+ CompletableFuture <SecurityAnalysisResult > future = securityAnalysis .run (VariantManagerConstants .INITIAL_VARIANT_ID , context .getParameters (), n -> tuple .getT2 ());
149
+ if (resultUuid != null ) {
150
+ futures .put (resultUuid , future );
151
+ }
152
+ if (resultUuid != null && cancelComputationRequests .get (resultUuid ) != null ) {
153
+ return Mono .empty ();
154
+ } else {
155
+ return Mono .fromCompletionStage (future );
156
+ }
130
157
});
131
158
}
132
159
133
160
@ Bean
134
- public Consumer <Flux <Message <String >>> consumeRun () {
135
- return f -> f .log (CATEGORY_BROKER_INPUT , Level .FINE )
136
- .flatMap (message -> {
137
- SecurityAnalysisResultContext resultContext = SecurityAnalysisResultContext .fromMessage (message , objectMapper );
138
-
139
- return run (resultContext .getRunContext ())
140
- .flatMap (result -> resultRepository .insert (resultContext .getResultUuid (), result )
141
- .then (resultRepository .insertStatus (resultContext .getResultUuid (), SecurityAnalysisStatus .COMPLETED .name ())))
142
- .doOnSuccess (unused -> {
143
- resultPublisherService .publish (resultContext .getResultUuid (), resultContext .getRunContext ().getReceiver ());
144
- LOGGER .info ("Security analysis complete (resultUuid='{}')" , resultContext .getResultUuid ());
145
- });
146
- })
147
- .doOnError (throwable -> LOGGER .error (throwable .toString (), throwable ))
148
- .subscribe ();
161
+ public Consumer <Message <String >> consumeRun () {
162
+ return message -> {
163
+
164
+ SecurityAnalysisResultContext resultContext = SecurityAnalysisResultContext .fromMessage (message , objectMapper );
165
+ runRequests .add (resultContext .getResultUuid ());
166
+
167
+ run (resultContext .getRunContext (), resultContext .getResultUuid ())
168
+ .flatMap (result -> resultRepository .insert (resultContext .getResultUuid (), result )
169
+ .then (resultRepository .insertStatus (resultContext .getResultUuid (), SecurityAnalysisStatus .COMPLETED .name ()))
170
+ .then (Mono .just (result )))
171
+ .doOnSuccess (result -> {
172
+ if (result != null ) { // result available
173
+ resultPublisherService .publish (resultContext .getResultUuid (), resultContext .getRunContext ().getReceiver ());
174
+ LOGGER .info ("Security analysis complete (resultUuid='{}')" , resultContext .getResultUuid ());
175
+ } else { // result not available : stop computation request
176
+ if (cancelComputationRequests .get (resultContext .getResultUuid ()) != null ) {
177
+ stoppedPublisherService .publish (resultContext .getResultUuid (), cancelComputationRequests .get (resultContext .getResultUuid ()).getReceiver ());
178
+ }
179
+ }
180
+ })
181
+ .doOnError (throwable -> {
182
+ if (!(throwable instanceof CancellationException )) {
183
+ LOGGER .error (throwable .toString (), throwable );
184
+ }
185
+ })
186
+ .doFinally (s -> {
187
+ futures .remove (resultContext .getResultUuid ());
188
+ cancelComputationRequests .remove (resultContext .getResultUuid ());
189
+ runRequests .remove (resultContext .getResultUuid ());
190
+ })
191
+ .subscribe ();
192
+ };
193
+ }
194
+
195
+ @ Bean
196
+ public Consumer <Message <String >> consumeCancel () {
197
+ return message -> {
198
+ SecurityAnalysisCancelContext cancelContext = SecurityAnalysisCancelContext .fromMessage (message );
199
+
200
+ if (runRequests .contains (cancelContext .getResultUuid ())) {
201
+ cancelComputationRequests .put (cancelContext .getResultUuid (), cancelContext );
202
+ }
203
+
204
+ // find the completableFuture associated with result uuid
205
+ CompletableFuture <SecurityAnalysisResult > future = futures .get (cancelContext .getResultUuid ());
206
+ if (future != null ) {
207
+ future .cancel (true ); // cancel computation in progress
208
+
209
+ resultRepository .delete (cancelContext .getResultUuid ())
210
+ .doOnSuccess (unused -> {
211
+ stoppedPublisherService .publish (cancelContext .getResultUuid (), cancelContext .getReceiver ());
212
+ LOGGER .info ("Security analysis stopped (resultUuid='{}')" , cancelContext .getResultUuid ());
213
+ })
214
+ .doOnError (throwable -> LOGGER .error (throwable .toString (), throwable ))
215
+ .subscribe ();
216
+ }
217
+ };
149
218
}
150
219
}
0 commit comments