5
5
6
6
package org.opensearch.alerting.transport
7
7
8
- import kotlinx.coroutines.CoroutineName
8
+ import kotlinx.coroutines.CoroutineScope
9
9
import kotlinx.coroutines.Dispatchers
10
- import kotlinx.coroutines.GlobalScope
11
10
import kotlinx.coroutines.launch
12
11
import org.apache.logging.log4j.LogManager
13
12
import org.opensearch.OpenSearchStatusException
@@ -25,6 +24,7 @@ import org.opensearch.action.search.SearchResponse
25
24
import org.opensearch.action.support.ActionFilters
26
25
import org.opensearch.action.support.HandledTransportAction
27
26
import org.opensearch.action.support.IndicesOptions
27
+ import org.opensearch.action.support.WriteRequest
28
28
import org.opensearch.action.support.master.AcknowledgedResponse
29
29
import org.opensearch.alerting.MonitorMetadataService
30
30
import org.opensearch.alerting.opensearchapi.suspendUntil
@@ -57,6 +57,7 @@ import kotlin.coroutines.resume
57
57
import kotlin.coroutines.resumeWithException
58
58
import kotlin.coroutines.suspendCoroutine
59
59
60
+ private val scope: CoroutineScope = CoroutineScope (Dispatchers .IO )
60
61
private val log = LogManager .getLogger(TransportDeleteMonitorAction ::class .java)
61
62
62
63
class TransportDeleteMonitorAction @Inject constructor(
@@ -90,8 +91,7 @@ class TransportDeleteMonitorAction @Inject constructor(
90
91
if (! validateUserBackendRoles(user, actionListener)) {
91
92
return
92
93
}
93
-
94
- GlobalScope .launch(Dispatchers .IO + CoroutineName (" DeleteMonitorAction" )) {
94
+ scope.launch {
95
95
DeleteMonitorHandler (client, actionListener, deleteRequest, user, transformedRequest.monitorId).resolveUserAndStart()
96
96
}
97
97
}
@@ -112,16 +112,15 @@ class TransportDeleteMonitorAction @Inject constructor(
112
112
checkUserPermissionsWithResource(user, monitor.user, actionListener, " monitor" , monitorId)
113
113
114
114
if (canDelete) {
115
- val deleteResponse = deleteMonitor(monitor)
116
- deleteDocLevelMonitorQueriesAndIndices(monitor)
117
- deleteMetadata(monitor)
115
+ val deleteResponse = deleteAllResourcesForMonitor(client, monitor, deleteRequest, monitorId)
118
116
actionListener.onResponse(DeleteMonitorResponse (deleteResponse.id, deleteResponse.version))
119
117
} else {
120
118
actionListener.onFailure(
121
119
AlertingException (" Not allowed to delete this monitor!" , RestStatus .FORBIDDEN , IllegalStateException ())
122
120
)
123
121
}
124
122
} catch (t: Exception ) {
123
+ log.error(" Failed to delete monitor ${deleteRequest.id()} " , t)
125
124
actionListener.onFailure(AlertingException .wrap(t))
126
125
}
127
126
}
@@ -145,69 +144,102 @@ class TransportDeleteMonitorAction @Inject constructor(
145
144
)
146
145
return ScheduledJob .parse(xcp, getResponse.id, getResponse.version) as Monitor
147
146
}
147
+ }
148
+
149
+ companion object {
150
+ @JvmStatic
151
+ suspend fun deleteAllResourcesForMonitor (
152
+ client : Client ,
153
+ monitor : Monitor ,
154
+ deleteRequest : DeleteRequest ,
155
+ monitorId : String ,
156
+ ): DeleteResponse {
157
+ val deleteResponse = deleteMonitorDocument(client, deleteRequest)
158
+ deleteMetadata(client, monitor)
159
+ deleteDocLevelMonitorQueriesAndIndices(client, monitor, monitorId)
160
+ return deleteResponse
161
+ }
148
162
149
- private suspend fun deleteMonitor ( monitor : Monitor ): DeleteResponse {
163
+ private suspend fun deleteMonitorDocument ( client : Client , deleteRequest : DeleteRequest ): DeleteResponse {
150
164
return client.suspendUntil { delete(deleteRequest, it) }
151
165
}
152
166
153
- private suspend fun deleteMetadata (monitor : Monitor ) {
167
+ suspend fun deleteMetadata (client : Client , monitor : Monitor ) {
154
168
val deleteRequest = DeleteRequest (ScheduledJob .SCHEDULED_JOBS_INDEX , " ${monitor.id} -metadata" )
155
- val deleteResponse: DeleteResponse = client.suspendUntil { delete(deleteRequest, it) }
169
+ .setRefreshPolicy(WriteRequest .RefreshPolicy .IMMEDIATE )
170
+ try {
171
+ val deleteResponse: DeleteResponse = client.suspendUntil { delete(deleteRequest, it) }
172
+ log.debug(" Monitor metadata: ${deleteResponse.id} deletion result: ${deleteResponse.result} " )
173
+ } catch (e: Exception ) {
174
+ // we only log the error and don't fail the request because if monitor document has been deleted,
175
+ // we cannot retry based on this failure
176
+ log.error(" Failed to delete monitor metadata ${deleteRequest.id()} ." , e)
177
+ }
156
178
}
157
179
158
- private suspend fun deleteDocLevelMonitorQueriesAndIndices (monitor : Monitor ) {
159
- val clusterState = clusterService.state()
160
- val metadata = MonitorMetadataService .getMetadata(monitor)
161
- metadata?.sourceToQueryIndexMapping?.forEach { (_, queryIndex) ->
162
-
163
- val indicesExistsResponse: IndicesExistsResponse =
164
- client.suspendUntil {
165
- client.admin().indices().exists(IndicesExistsRequest (queryIndex), it)
180
+ suspend fun deleteDocLevelMonitorQueriesAndIndices (
181
+ client : Client ,
182
+ monitor : Monitor ,
183
+ monitorId : String ,
184
+ ) {
185
+ try {
186
+ val metadata = MonitorMetadataService .getMetadata(monitor)
187
+ metadata?.sourceToQueryIndexMapping?.forEach { (_, queryIndex) ->
188
+
189
+ val indicesExistsResponse: IndicesExistsResponse =
190
+ client.suspendUntil {
191
+ client.admin().indices().exists(IndicesExistsRequest (queryIndex), it)
192
+ }
193
+ if (indicesExistsResponse.isExists == false ) {
194
+ return
166
195
}
167
- if (indicesExistsResponse.isExists == false ) {
168
- return
169
- }
170
- // Check if there's any queries from other monitors in this queryIndex,
171
- // to avoid unnecessary doc deletion, if we could just delete index completely
172
- val searchResponse: SearchResponse = client.suspendUntil {
173
- search(
174
- SearchRequest (queryIndex).source(
175
- SearchSourceBuilder ()
176
- .size(0 )
177
- .query(
178
- QueryBuilders .boolQuery().mustNot(
179
- QueryBuilders .matchQuery(" monitor_id" , monitorId)
196
+ // Check if there's any queries from other monitors in this queryIndex,
197
+ // to avoid unnecessary doc deletion, if we could just delete index completely
198
+ val searchResponse: SearchResponse = client.suspendUntil {
199
+ search(
200
+ SearchRequest (queryIndex).source(
201
+ SearchSourceBuilder ()
202
+ .size(0 )
203
+ .query(
204
+ QueryBuilders .boolQuery().mustNot(
205
+ QueryBuilders .matchQuery(" monitor_id" , monitorId)
206
+ )
180
207
)
181
- )
182
- ).indicesOptions(IndicesOptions .LENIENT_EXPAND_OPEN_HIDDEN ),
183
- it
184
- )
185
- }
186
- if (searchResponse.hits.totalHits.value == 0L ) {
187
- val ack: AcknowledgedResponse = client.suspendUntil {
188
- client.admin().indices().delete(
189
- DeleteIndexRequest (queryIndex).indicesOptions(IndicesOptions .LENIENT_EXPAND_OPEN_HIDDEN ),
208
+ ).indicesOptions(IndicesOptions .LENIENT_EXPAND_OPEN_HIDDEN ),
190
209
it
191
210
)
192
211
}
193
- if (ack.isAcknowledged == false ) {
194
- log.error(" Deletion of concrete queryIndex:$queryIndex is not ack'd!" )
195
- }
196
- } else {
197
- // Delete all queries added by this monitor
198
- val response: BulkByScrollResponse = suspendCoroutine { cont ->
199
- DeleteByQueryRequestBuilder (client, DeleteByQueryAction .INSTANCE )
200
- .source(queryIndex)
201
- .filter(QueryBuilders .matchQuery(" monitor_id" , monitorId))
202
- .refresh(true )
203
- .execute(
204
- object : ActionListener <BulkByScrollResponse > {
205
- override fun onResponse (response : BulkByScrollResponse ) = cont.resume(response)
206
- override fun onFailure (t : Exception ) = cont.resumeWithException(t)
207
- }
212
+ if (searchResponse.hits.totalHits.value == 0L ) {
213
+ val ack: AcknowledgedResponse = client.suspendUntil {
214
+ client.admin().indices().delete(
215
+ DeleteIndexRequest (queryIndex).indicesOptions(IndicesOptions .LENIENT_EXPAND_OPEN_HIDDEN ), it
208
216
)
217
+ }
218
+ if (ack.isAcknowledged == false ) {
219
+ log.error(" Deletion of concrete queryIndex:$queryIndex is not ack'd!" )
220
+ }
221
+ } else {
222
+ // Delete all queries added by this monitor
223
+ val response: BulkByScrollResponse = suspendCoroutine { cont ->
224
+ DeleteByQueryRequestBuilder (client, DeleteByQueryAction .INSTANCE )
225
+ .source(queryIndex)
226
+ .filter(QueryBuilders .matchQuery(" monitor_id" , monitorId))
227
+ .refresh(true )
228
+ .execute(
229
+ object : ActionListener <BulkByScrollResponse > {
230
+ override fun onResponse (response : BulkByScrollResponse ) = cont.resume(response)
231
+ override fun onFailure (t : Exception ) {
232
+ cont.resumeWithException(t)
233
+ }
234
+ }
235
+ )
236
+ }
209
237
}
210
238
}
239
+ } catch (e: Exception ) {
240
+ // we only log the error and don't fail the request because if monitor document has been deleted successfully,
241
+ // we cannot retry based on this failure
242
+ log.error(" Failed to delete doc level queries from query index." , e)
211
243
}
212
244
}
213
245
}
0 commit comments