feat(plugins/playground): return all matching alerts + drop ephemeral workdir volume#2347
Merged
JocLRojas merged 2 commits intoJul 15, 2026
Merged
Conversation
…ow collection The run endpoint previously returned a single alert per event even when multiple rules matched. The correlation writer (saw plugin) already emits one NDJSON line per alert, but the runner's awaitAlert stopped at the first hit and RunResult carried a singular Alert field. Changes: - RunResult.Alert (json.RawMessage) -> Alerts ([]json.RawMessage). - Add RunResult.StopReason: quiet | hard_cap | no_alerts so the client can distinguish a drained pipeline from a truncated wait. - Replace findAlertByEventID (early return) with findAlertsByEventID, which collects every matching line and dedupes by alert id across polls via a caller-owned seen set. - Rework awaitAlerts to use a moving quiet window (AlertGrace) reset on every new alert, bounded by an absolute hard cap (EventTimeout) so a noisy rule cannot hold the HTTP request open. - Iterate NDJSON with strings.SplitSeq to avoid allocating a full []string on each of the ~150 polls per run.
The playground plugin only needs the workdir alive while the container runs: input logs, resulting_log.json and resulting_alert.json are all truncated on every run and no state is expected to survive a container restart. Mounting a host bind for it forces old config.yaml files to persist across upgrades and hides fresh defaults written by the playground -init step. Remove the /playground-workdir bind so the directory lives inside the event-processor container's tmpfs / overlay. If the event-processor restarts we lose the workdir, which is the intended behavior.
🛑 AI review — Sensitive area, extra care recommendedThis PR touches critical paths or introduces changes the model cannot judge with sufficient confidence. Review carefully before merging.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related fixes to the playground plugin so the
POST /playground/runflow can actually surface everything the event-processor produces, and so the
ephemeral workdir stops leaking state between container restarts.
Reference: internal discussion, no tracked issue.
Changes
1.
feat(plugins/playground): return all matching alerts with moving-window collectionWhat was broken. The run endpoint returned a single alert per event
even when multiple rules matched the same event. The correlation writer
(
sawplugin in EventProcessor) already appends one NDJSON line peralert to
resulting_alert.json, but the consumer side of the pipelinethrew away everything past the first match:
RunResult.Alertwas a singularjson.RawMessage, so the structphysically could not carry more than one alert.
findAlertByEventIDinscan.goshort-circuited on the first linematching the event id.
awaitAlertwaited a fixedAlertGrace(2s) after the event wasseen and then returned, so alerts that arrived after that window
were dropped even if the file contained them.
What changed.
RunResult.Alert (json.RawMessage) → Alerts ([]json.RawMessage).Breaking change in the JSON response: the
alertfield is goneand replaced by
alerts.RunResult.StopReasonfield:quiet|hard_cap|no_alerts.Lets the client tell "these are all the alerts" (
quiet) apart from"we cut the wait short, more may be pending" (
hard_cap).findAlertByEventID→findAlertsByEventID— collects everymatching line and dedupes by
alert.idacross polls via acaller-owned seen set.
awaitAlert→awaitAlerts: moving quiet window bounded by anabsolute hard cap:
AlertGrace, 2s) resets on every new alertobserved, so bursts of alerts are not truncated mid-burst.
EventTimeout, 15s) is the absolute per-run deadlineso a noisy rule cannot hold the HTTP request open indefinitely.
AlertGraceelapses,we return
no_alerts— same behavior as before for the emptycase.
Why moving window instead of a fixed grace. A fixed grace fails
when the correlation pipeline is slow: multiple rules matching the same
event can produce alerts spread out over more than 2s. The moving
window adapts to the actual arrival rate; the hard cap prevents
pathological cases from hanging the request.
2.
feat(installer): drop ephemeral playground-workdir volumeThe playground workdir only needs to survive the lifetime of the
event-processor container:
input/,resulting_log.jsonandresulting_alert.jsonare all truncated at the start of every run,and nothing is expected to persist across restarts.