Repository navigation
fix: guard against EmptyMap cast in doc-level monitor recreateRunContext and mapping traversal - #2221
Conversation
PR Reviewer Guide 🔍(Review updated until commit f7425d9)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to f7425d9 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit f7425d9
Suggestions up to commit f3a8aac
Suggestions up to commit 276f481
Suggestions up to commit ef25277
Suggestions up to commit dec1828
|
31032ab to
dec1828
Compare
|
Persistent review updated to latest commit dec1828 |
|
Persistent review updated to latest commit ef25277 |
ef25277 to
276f481
Compare
|
Persistent review updated to latest commit 276f481 |
|
Persistent review updated to latest commit f3a8aac |
…mapping traversal
Fix A (MonitorMetadataService.kt): guard lastRunContext cast in recreateRunContext() —
if lastRunContext.isEmpty() pass null to createFullRunContext() (which accepts null and
builds a fresh context) instead of casting EmptyMap to MutableMap, which throws
ClassCastException on monitors that have no last_run_context in stored metadata.
Fix B (DocLevelMonitorQueries.kt): replace bare MutableMap casts in
traverseMappingsAndUpdate() with .toMutableMap() defensive copies so that
Collections.emptyMap() values from index mapping nested objects {} do not cause
UnsupportedOperationException when the traversal tries to mutate them.
Fix C (MonitorFanOutUtils.kt / DocumentLevelMonitorRunner.kt /
RemoteDocumentLevelMonitorRunner.kt): tighten initializeNewLastRunContext() return
type from Map to MutableMap (matching the actual runtime type) and remove the now-
unnecessary as MutableMap casts at both call sites.
Related: opensearch-project/common-utils#967
Signed-off-by: thecodingshrimp <leonard.stutzer@sap.com>
f3a8aac to
67be38d
Compare
…tern - MonitorMetadataService: guard recreateRunContext against non-empty but wrongly-typed lastRunContext by filtering non-Map entries before casting - DocLevelMonitorQueries: fix traverseMappingsAndUpdate to write mutated child subtree back into parent node (previously lost on recursive calls); remove redundant second toMutableMap() copy passed to processLeafFn - DocumentLevelMonitorRunner: replace outer lastRunContext unchecked cast with safe filter-reconstruct; use ?: continue instead of cast on value access - RemoteDocumentLevelMonitorRunner: same fixes as DocumentLevelMonitorRunner - MonitorFanOutUtils: add @return KDoc ownership note to initializeNewLastRunContext - Add unit tests: MonitorFanOutUtilsTests (3 tests for initializeNewLastRunContext) and DocLevelMonitorQueriesTraversalTests (5 tests for traverseMappingsAndUpdate covering unmodifiable/empty-map inputs, rename, and nested recursion) Signed-off-by: thecodingshrimp <leonard.stutzer@sap.com>
…uard - MonitorFanOutUtils: remove stale @PARAM monitorCtx (no such parameter); add missing @PARAM shardCount - DocLevelMonitorQueries: correct internal-node comment to describe the actual copy-mutate-writeback mechanism, not the old direct-mutation contract - DocumentLevelMonitorRunner, RemoteDocumentLevelMonitorRunner: replace '?: continue' dead code (key always present when iterating map's own keys) with '!!' plus clarifying comment Signed-off-by: thecodingshrimp <leonard.stutzer@sap.com>
67be38d to
f7425d9
Compare
|
Persistent review updated to latest commit f7425d9 |
1 similar comment
|
Persistent review updated to latest commit f7425d9 |
Summary
This PR adds defence-in-depth guards against
ClassCastException/UnsupportedOperationExceptioncaused by immutable empty maps being cast to
MutableMapin the doc-level monitor subsystem.Closes: #2220
Related: opensearch-project/common-utils#967 (root-cause fix belongs there)
Root cause
MonitorMetadata.lastRunContextdefaults tokotlin.collections.EmptyMap(the immutablesingleton returned by
mapOf()) when deserialized from.opensearch-alerting-configandthe stored document has no
last_run_contextfield (monitor never ran, or created underan older plugin version).
MonitorMetadataService.recreateRunContext()line 210 casts this directly toMutableMap<String, MutableMap<String, Any>>.EmptyMapis not aMutableMap, so theJVM throws
ClassCastExceptionon anyPUTupdate to such a monitor.The correct fix — changing
mapOf()tomutableMapOf()inMonitorMetadata.kt— istracked in opensearch-project/common-utils#967. This PR adds a guard at the cast site so
that the crash cannot occur regardless of which
common-utilsversion is deployed.Changes
Fix A —
MonitorMetadataService.kt(critical)Guard the cast at line 210: if
lastRunContext.isEmpty(), passnulltocreateFullRunContext()(which acceptsnulland builds a fresh context).Fix B —
DocLevelMonitorQueries.kt(plausible crash path)traverseMappingsAndUpdate()casts values fromindexMetadata.mapping()?.sourceAsMapdirectly to
MutableMap. Java'sCollections.emptyMap()(returned for empty nestedobjects
{}) is not mutable;.put()/.remove()would throwUnsupportedOperationException. Replace bare casts with.toMutableMap()defensivecopies at the three cast sites inside
traverseMappingsAndUpdate().Fix C —
MonitorFanOutUtils.kt/DocumentLevelMonitorRunner.kt(contract)initializeNewLastRunContext()always returns aMutableMapbut its return type isdeclared as
Map. Update the declaration and remove the now-unnecessaryas MutableMapcast at the call site in
DocumentLevelMonitorRunner.ktandRemoteDocumentLevelMonitorRunner.kt.Files changed
alerting/src/main/kotlin/org/opensearch/alerting/MonitorMetadataService.ktalerting/src/main/kotlin/org/opensearch/alerting/util/DocLevelMonitorQueries.ktalerting/src/main/kotlin/org/opensearch/alerting/MonitorFanOutUtils.ktalerting/src/main/kotlin/org/opensearch/alerting/DocumentLevelMonitorRunner.ktalerting/src/main/kotlin/org/opensearch/alerting/remote/monitors/RemoteDocumentLevelMonitorRunner.ktTesting
./gradlew test).opensearch-alerting-config, thenPUTan update to the monitor — noClassCastExceptionthrown(
{}) does not throwUnsupportedOperationException