Feature/composite workflow execution v1#1
Feature/composite workflow execution v1#1stevanbz wants to merge 18 commits intofeature/composite-workflow-transport-crud-executionfrom
Conversation
…enario Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
|
can you look into adding this painless script module at plugin load test Let's look into how we can verify composite monitors containing bucket level monitors |
…nitor index is not initialized yet. Added workflow crud test cases Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
eirsep
left a comment
There was a problem hiding this comment.
Thanks for the changes, Stevan
have reviewed 50% of the PR
will review more while you can address the comments
| import org.opensearch.action.ActionRequest | ||
| import org.opensearch.action.ActionResponse | ||
| import org.opensearch.alerting.action.ExecuteMonitorAction | ||
| import org.opensearch.alerting.action.ExecuteWorkflowAction |
There was a problem hiding this comment.
let's discuss offline about cluster/node level settings for composite workflows
| periodEnd: Instant, | ||
| dryrun: Boolean | ||
| dryrun: Boolean, | ||
| workflowExecutionContext: WorkflowRunContext? |
There was a problem hiding this comment.
NIT: name the variable same as the type
| else QueryBuilders.boolQuery().must(source.query()) | ||
| queryBuilder.filter(QueryBuilders.termsQuery(fieldName, bucketValues)) | ||
|
|
||
| if (workflowRunContext != null && !workflowRunContext.indexToDocIds.isNullOrEmpty()) { |
There was a problem hiding this comment.
why are we applying this logic here and not in InputService where the actual search query is being executed ?
There was a problem hiding this comment.
Yeah you are right. This logic can be removed from here - I forgot to remove it once I added in input service. Tnx and good catch!
| } | ||
|
|
||
| // If monitor execution is triggered from a workflow | ||
| val indexToRelatedDocIdsMap = workflowRunContext?.indexToDocIds |
There was a problem hiding this comment.
can we intialize this just before its usage instead of here?
| } | ||
| } | ||
|
|
||
| private fun updateInputQueryWithFindingDocIds( |
There was a problem hiding this comment.
add comments/javadocs to explain what we intend to do wherever we are using chained findings filtering
|
|
||
| // Rewrite query to consider the doc ids per given index | ||
| if (chainedFindingExist(indexToDocIds)) { | ||
| val updatedSourceQuery = updateInputQueryWithFindingDocIds(input.query.query(), indexToDocIds!!) |
There was a problem hiding this comment.
why are we changing at input query
add a filter after search query is constructed
There was a problem hiding this comment.
null check for query required?
You are right. Adding the null check.
There was a problem hiding this comment.
why are we changing at input query add a filter after search query is constructed
Since rewrittenQuery.query() returns QueryBuilder()! (which can be null) we must do a cast to a BoolQueryBuilder (I guess) which then later we would need to set again to a rewrittenQuery.query.
You can see here that later on query is transformed in a String so it wouldn't be so straight forward to add a filter.
I don't have any more idea how to do this in elegant way (maybe lacking domain knowledge around the OpenSearch classes I can use for this purpose)- if you can give me a hint or a code snippet how I can do, it would be good. Tnx!
| } | ||
| } | ||
|
|
||
| private fun updateInputQueryWithFindingDocIds( |
There was a problem hiding this comment.
this should be a common methods used in all the monitor types
There was a problem hiding this comment.
I thought the same - but then I saw that the search query is executed on a different way depending of the monitor type.
Ie. here you can see how the doc level monitor is getting the matching docs. So, for example, doc level monitor iterates through the list of indices and getting the documents index by index. That's why I adjusted getting the matched docIds on doc level monitor to be aligned with existing logic. Check it out here
| val xContentRegistry: NamedXContentRegistry, | ||
| ) { | ||
|
|
||
| suspend fun getFindingDocIdsPerMonitorExecution(chainedMonitor: Monitor, workflowExecutionId: String): Map<String, List<String>> { |
| .seqNoAndPrimaryTerm(true) | ||
| ) | ||
| .indices(chainedMonitor.dataSources.findingsIndex) | ||
| val searchResponse: SearchResponse = client.suspendUntil { client.search(searchRequest, it) } |
| return buildMonitors(searchResponse) | ||
| } | ||
|
|
||
| private fun buildMonitors(response: SearchResponse): List<Monitor> { |
There was a problem hiding this comment.
should this function be called parseMonitors
| return monitors | ||
| } | ||
|
|
||
| suspend fun getDocIdsPerFindingIndex(monitorId: String, workflowExecutionId: String): Map<String, List<String>> { |
There was a problem hiding this comment.
This function will be removed since it's not used at all.
…e workflow Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
… consider the workflow execution id Added worfklow service used for retrieving monitors and their findings. Added business logic for considering the chained monitors Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
…when loading the cluster Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
|
|
||
| class ExecuteWorkflowRequest : ActionRequest { | ||
| val dryrun: Boolean | ||
| val requestEnd: TimeValue |
There was a problem hiding this comment.
Copy-paste of ExecuteMonitorRequest. Used in CompositeWorkflowRunner - and passed to concrete monitor runner - ie in bucketLevelMonitors used for defining the search params when creating findings. Check it out here
| import org.opensearch.commons.alerting.model.Workflow | ||
| import java.io.IOException | ||
|
|
||
| class ExecuteWorkflowRequest : ActionRequest { |
| ) | ||
|
|
||
| override fun validate(): ActionRequestValidationException? { | ||
| return null |
There was a problem hiding this comment.
Added check. Tnx and good point
|
|
||
| class ExecuteWorkflowResponse : ActionResponse, ToXContentObject { | ||
|
|
||
| val workflowRunResult: List<MonitorRunResult<*>> |
There was a problem hiding this comment.
we should store other fields like workflow execution start, and end time, status=failed, successful
There was a problem hiding this comment.
Makes sense. Will add those fields and appropriate logic around them
| return listOf() | ||
| } | ||
|
|
||
| override fun replacedRoutes(): MutableList<RestHandler.ReplacedRoute> { |
There was a problem hiding this comment.
why do we need replacedRoutes. we are not replacing routes. this would be a new API
There was a problem hiding this comment.
Removing class completely. Sorry
| TODO("Not yet implemented") | ||
| } | ||
| ): List<MonitorRunResult<*>> { | ||
| val workflowExecutionId = UUID.randomUUID().toString() |
There was a problem hiding this comment.
we should make this execution id more deterministic..
workflowId+timestamp
There was a problem hiding this comment.
Something like:
val workflowExecutionId = UUID.randomUUID().toString() + LocalDateTime.now()
What do you think?
There was a problem hiding this comment.
Changed to something like:
val executionId = workflow.id.plus(LocalDateTime.now()).plus(UUID.randomUUID().toString())
| ): MonitorRunResult<*> { | ||
| TODO("Not yet implemented") | ||
| } | ||
| ): List<MonitorRunResult<*>> { |
There was a problem hiding this comment.
This should return workflowRunResult which should contain list of monitorRunResult
| return indexToRelatedDocIdsMap | ||
| } | ||
|
|
||
| suspend fun searchMonitors(monitors: List<String>, size: Int, owner: String?): List<Monitor> { |
There was a problem hiding this comment.
No will remove it. Good catch
|
|
||
| val delegates = (workflow.inputs[0] as CompositeInput).sequence.delegates.sortedBy { it.order } | ||
| // Fetch monitors by ids | ||
| val monitors = monitorCtx.workflowService!!.searchMonitors(delegates.map { it.monitorId }, delegates.size, workflow.owner) |
| // Validate the monitors size | ||
| if (delegates.size != monitors.size) { | ||
| val diffMonitorIds = delegates.map { it.monitorId }.minus(monitors.map { it.id }.toSet()).joinToString() | ||
| throw IllegalStateException("Delegate monitors don't exist $diffMonitorIds") |
There was a problem hiding this comment.
Plz also log workflow id in the message
There was a problem hiding this comment.
Will do. Also will add a logs on the beginning and end of workflow execution
…esponse class Code adjusted according to comments Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
d94c257 to
a1e0408
Compare
Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
| var indexToDocIds = mapOf<String, List<String>>() | ||
| var delegateMonitor: Monitor | ||
| delegateMonitor = monitorsById[delegate.monitorId] | ||
| ?: throw IllegalStateException("Delegate monitor not found ${delegate.monitorId} for the workflow $workflow.id") |
| * @param chainedMonitor Monitor that is previously executed | ||
| * @param workflowExecutionId Execution id of the current workflow | ||
| */ | ||
| suspend fun getFindingDocIdsByExecutionId(chainedMonitor: Monitor, workflowExecutionId: String): Map<String, List<String>> { |
There was a problem hiding this comment.
I was thinking and let me elaborate a little bit my thinking and proposed solution:
Let's catch all the exceptions that can be raised, and wrap them up in AlertingException (check it out here). The caller function - the function in CompositeWorkflowRunner (here) will do a check and return empty workflow run result. What do you think?
| ?: throw IllegalStateException("Delegate monitor not found ${delegate.monitorId} for the workflow $workflow.id") | ||
| if (delegate.chainedFindings != null) { | ||
| val chainedMonitor = monitorsById[delegate.chainedFindings!!.monitorId] | ||
| ?: throw IllegalStateException("Chained finding monitor not found ${delegate.monitorId} for the workflow $workflow.id") |
| dryRun, | ||
| workflowRunContext | ||
| ) | ||
| } else { |
There was a problem hiding this comment.
NIT: use else if for query level and throw unsupported exception
There was a problem hiding this comment.
Should I also wrap into alerting exception or? Ie. something like this:
Something like this:
else if(delegateMonitor.isQueryLevelMonitor()){ QueryLevelMonitorRunner.runMonitor( delegateMonitor, monitorCtx, periodStart, periodEnd, dryRun, workflowRunContext ) } else { throw AlertingException.wrap( IllegalStateException("Unsupported monitor type") ) }
| data class WorkflowRunContext( | ||
| val chainedMonitorId: String?, | ||
| val workflowExecutionId: String, | ||
| val indexToDocIds: Map<String, List<String>> |
There was a problem hiding this comment.
indexToDocIds is not a good variable name. Someone reading the code would not understand that this is the input source.
There was a problem hiding this comment.
What about "matchingDocIdsPerIndex"?
Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
|
Let's have latestRunTime and latestExecutionId in workflow object or workflow metadata object. |
…dation if the query monitor is part of the workflow chain Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
| @JvmField val WORKFLOW_BASE_URI = "/_plugins/_alerting/workflows" | ||
| @JvmField val DESTINATION_BASE_URI = "/_plugins/_alerting/destinations" | ||
| @JvmField val LEGACY_OPENDISTRO_MONITOR_BASE_URI = "/_opendistro/_alerting/monitors" | ||
| @JvmField val LEGACY_OPENDISTRO_WORKFLOW_BASE_URI = "/_opendistro/_alerting/workflows" |
There was a problem hiding this comment.
This only for legacy APIs. This is a new API, so we should not have this
Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
… checking workflow metadata. Changed flow of workflow execution Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
20de168 to
8e0d28d
Compare
|
…hat verify that workflow metadata is not created Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
…he monitors once the workflow is updated Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
Signed-off-by: Stevan Buzejic <buzejic.stevan@gmail.com>
Issue #, if available:
Description of changes:
CheckList:
[ ] Commits are signed per the DCO using --signoff
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.