Skip to content

Commit fb9ef27

Browse files
committed
initial lookback window implementation
1 parent ff223d2 commit fb9ef27

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
lines changed

src/main/kotlin/org/opensearch/commons/alerting/model/MonitorV2.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.opensearch.commons.alerting.model
22

33
import org.opensearch.common.CheckedFunction
4+
import org.opensearch.common.unit.TimeValue
45
import org.opensearch.commons.alerting.model.PPLMonitor.Companion.PPL_MONITOR_TYPE
56
import org.opensearch.core.ParseField
67
import org.opensearch.core.common.io.stream.StreamInput
@@ -19,8 +20,9 @@ interface MonitorV2 : ScheduledJob {
1920
override val schedule: Schedule
2021
override val lastUpdateTime: Instant // required for scheduled job maintenance
2122
override val enabledTime: Instant? // required for scheduled job maintenance
22-
val schemaVersion: Int // for updating monitors
2323
val triggers: List<TriggerV2>
24+
val schemaVersion: Int // for updating monitors
25+
val lookBackWindow: TimeValue? // how far back to look when querying data during monitor execution
2426

2527
fun asTemplateArg(): Map<String, Any?>
2628

@@ -50,6 +52,7 @@ interface MonitorV2 : ScheduledJob {
5052
const val LAST_UPDATE_TIME_FIELD = "last_update_time"
5153
const val ENABLED_TIME_FIELD = "enabled_time"
5254
const val TRIGGERS_FIELD = "triggers"
55+
const val LOOK_BACK_WINDOW_FIELD = "look_back_window"
5356

5457
// default values
5558
const val NO_ID = ""

src/main/kotlin/org/opensearch/commons/alerting/model/PPLMonitor.kt

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package org.opensearch.commons.alerting.model
22

33
import org.apache.logging.log4j.LogManager
4+
import org.opensearch.common.unit.TimeValue
45
import org.opensearch.commons.alerting.model.Monitor.Companion.SCHEMA_VERSION_FIELD
56
import org.opensearch.commons.alerting.model.MonitorV2.Companion.ENABLED_FIELD
67
import org.opensearch.commons.alerting.model.MonitorV2.Companion.ENABLED_TIME_FIELD
78
import org.opensearch.commons.alerting.model.MonitorV2.Companion.LAST_UPDATE_TIME_FIELD
9+
import org.opensearch.commons.alerting.model.MonitorV2.Companion.LOOK_BACK_WINDOW_FIELD
810
import org.opensearch.commons.alerting.model.MonitorV2.Companion.MONITOR_TYPE_FIELD
911
import org.opensearch.commons.alerting.model.MonitorV2.Companion.MONITOR_V2_TYPE
1012
import org.opensearch.commons.alerting.model.MonitorV2.Companion.NAME_FIELD
@@ -42,8 +44,9 @@ data class PPLMonitor(
4244
override val schedule: Schedule,
4345
override val lastUpdateTime: Instant,
4446
override val enabledTime: Instant?,
45-
override val schemaVersion: Int = NO_SCHEMA_VERSION,
4647
override val triggers: List<TriggerV2>,
48+
override val schemaVersion: Int = NO_SCHEMA_VERSION,
49+
override val lookBackWindow: TimeValue? = null,
4750
val queryLanguage: QueryLanguage = QueryLanguage.PPL, // default to PPL, SQL not currently supported
4851
val query: String
4952
) : MonitorV2 {
@@ -87,8 +90,9 @@ data class PPLMonitor(
8790
schedule = Schedule.readFrom(sin),
8891
lastUpdateTime = sin.readInstant(),
8992
enabledTime = sin.readOptionalInstant(),
90-
schemaVersion = sin.readInt(),
9193
triggers = sin.readList(TriggerV2::readFrom),
94+
schemaVersion = sin.readInt(),
95+
lookBackWindow = TimeValue.parseTimeValue(sin.readString(), PLACEHOLDER_LOOK_BACK_WINDOW_SETTING_NAME),
9296
queryLanguage = sin.readEnum(QueryLanguage::class.java),
9397
query = sin.readString()
9498
)
@@ -112,8 +116,9 @@ data class PPLMonitor(
112116
builder.field(ENABLED_FIELD, enabled)
113117
builder.nonOptionalTimeField(LAST_UPDATE_TIME_FIELD, lastUpdateTime)
114118
builder.optionalTimeField(ENABLED_TIME_FIELD, enabledTime)
115-
builder.field(SCHEMA_VERSION_FIELD, schemaVersion)
116119
builder.field(TRIGGERS_FIELD, triggers.toTypedArray())
120+
builder.field(SCHEMA_VERSION_FIELD, schemaVersion)
121+
builder.field(LOOK_BACK_WINDOW_FIELD, lookBackWindow?.toHumanReadableString(0))
117122
builder.field(QUERY_LANGUAGE_FIELD, queryLanguage.value)
118123
builder.field(QUERY_FIELD, query)
119124

@@ -142,12 +147,16 @@ data class PPLMonitor(
142147
}
143148
out.writeInstant(lastUpdateTime)
144149
out.writeOptionalInstant(enabledTime)
145-
out.writeInt(schemaVersion)
146150
out.writeVInt(triggers.size)
147151
triggers.forEach {
148152
out.writeEnum(TriggerV2.TriggerV2Type.PPL_TRIGGER)
149153
it.writeTo(out)
150154
}
155+
out.writeInt(schemaVersion)
156+
157+
out.writeBoolean(lookBackWindow != null)
158+
lookBackWindow?.let { out.writeString(lookBackWindow.toHumanReadableString(0)) }
159+
151160
out.writeEnum(queryLanguage)
152161
out.writeString(query)
153162
}
@@ -162,6 +171,7 @@ data class PPLMonitor(
162171
LAST_UPDATE_TIME_FIELD to lastUpdateTime.toEpochMilli(),
163172
ENABLED_TIME_FIELD to enabledTime?.toEpochMilli(),
164173
TRIGGERS_FIELD to triggers,
174+
LOOK_BACK_WINDOW_FIELD to lookBackWindow?.toHumanReadableString(0),
165175
QUERY_LANGUAGE_FIELD to queryLanguage.value,
166176
QUERY_FIELD to query
167177
)
@@ -188,6 +198,11 @@ data class PPLMonitor(
188198
const val QUERY_LANGUAGE_FIELD = "query_language"
189199
const val QUERY_FIELD = "query"
190200

201+
// mock setting name used when parsing TimeValue
202+
// TimeValue class is usually reserved for declaring settings, but we're using it
203+
// outside that use case here, which is why we need these placeholders
204+
private const val PLACEHOLDER_LOOK_BACK_WINDOW_SETTING_NAME = "ppl_monitor_look_back_window"
205+
191206
@JvmStatic
192207
@JvmOverloads
193208
@Throws(IOException::class)
@@ -198,8 +213,9 @@ data class PPLMonitor(
198213
var schedule: Schedule? = null
199214
var lastUpdateTime: Instant? = null
200215
var enabledTime: Instant? = null
201-
var schemaVersion = NO_SCHEMA_VERSION
202216
val triggers: MutableList<TriggerV2> = mutableListOf()
217+
var schemaVersion = NO_SCHEMA_VERSION
218+
var lookBackWindow: TimeValue? = null
203219
var queryLanguage: QueryLanguage = QueryLanguage.PPL // default to PPL
204220
var query: String? = null
205221

@@ -216,7 +232,6 @@ data class PPLMonitor(
216232
SCHEDULE_FIELD -> schedule = Schedule.parse(xcp)
217233
LAST_UPDATE_TIME_FIELD -> lastUpdateTime = xcp.instant()
218234
ENABLED_TIME_FIELD -> enabledTime = xcp.instant()
219-
SCHEMA_VERSION_FIELD -> schemaVersion = xcp.intValue()
220235
TRIGGERS_FIELD -> {
221236
XContentParserUtils.ensureExpectedToken(
222237
XContentParser.Token.START_ARRAY,
@@ -227,6 +242,15 @@ data class PPLMonitor(
227242
triggers.add(PPLTrigger.parseInner(xcp))
228243
}
229244
}
245+
SCHEMA_VERSION_FIELD -> schemaVersion = xcp.intValue()
246+
LOOK_BACK_WINDOW_FIELD -> {
247+
lookBackWindow = if (xcp.currentToken() == XContentParser.Token.VALUE_NULL) {
248+
null
249+
} else {
250+
val input = xcp.text()
251+
TimeValue.parseTimeValue(input, PLACEHOLDER_LOOK_BACK_WINDOW_SETTING_NAME) // throws IllegalArgumentException if there's parsing error
252+
}
253+
}
230254
QUERY_LANGUAGE_FIELD -> {
231255
val input = xcp.text()
232256
val enumMatchResult = QueryLanguage.enumFromString(input)
@@ -269,6 +293,10 @@ data class PPLMonitor(
269293
requireNotNull(query) { "Query is null" }
270294
requireNotNull(lastUpdateTime) { "Last update time is null" }
271295

296+
if (schedule is IntervalSchedule && lookBackWindow != null) {
297+
throw IllegalArgumentException("Look back windows only supported for CRON schedules")
298+
}
299+
272300
if (queryLanguage == QueryLanguage.SQL) {
273301
throw IllegalArgumentException("SQL queries are not supported. Please use a PPL query.")
274302
}
@@ -282,8 +310,9 @@ data class PPLMonitor(
282310
schedule,
283311
lastUpdateTime,
284312
enabledTime,
285-
schemaVersion,
286313
triggers,
314+
schemaVersion,
315+
lookBackWindow,
287316
queryLanguage,
288317
query
289318
)

src/main/kotlin/org/opensearch/commons/alerting/model/PPLTrigger.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ data class PPLTrigger(
164164
const val CUSTOM_CONDITION_FIELD = "custom_condition"
165165

166166
// mock setting name used when parsing TimeValue
167+
// TimeValue class is usually reserved for declaring settings, but we're using it
168+
// outside that use case here, which is why we need these placeholders
167169
private const val PLACEHOLDER_SUPPRESS_SETTING_NAME = "ppl_trigger_suppress_duration"
168170
private const val PLACEHOLDER_EXPIRE_SETTING_NAME = "ppl_trigger_expire_duration"
169171

0 commit comments

Comments
 (0)