mirror of
https://github.com/Sea-Haven-Industries/apm-wo-analysis.git
synced 2026-09-30 04:13:13 +00:00
fix(pipeline): page when classifier has no daily invocation
Some checks failed
Deploy / deploy (push) Has been cancelled
Some checks failed
Deploy / deploy (push) Has been cancelled
This commit is contained in:
parent
1b3259e875
commit
e4fc32599f
4 changed files with 54 additions and 6 deletions
|
|
@ -311,6 +311,27 @@ class PipelineStack(Stack):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Daily-export freshness. The DLQ-depth alarm stays OK when the
|
||||||
|
# classifier is never invoked. Sum Invocations over 86400s < 1 pages
|
||||||
|
# a silent day. Missing data is the outage (no datapoint = no run),
|
||||||
|
# so BREACHING, not the house-default NOT_BREACHING.
|
||||||
|
self.classifier_fn.metric_invocations(
|
||||||
|
period=Duration.days(1),
|
||||||
|
statistic="Sum",
|
||||||
|
).create_alarm(
|
||||||
|
self,
|
||||||
|
"ClassifierInvocationsAlarm",
|
||||||
|
alarm_name="apm-wo-analysis-classifier-invocations",
|
||||||
|
alarm_description=(
|
||||||
|
"apm-wo-analysis-classifier had fewer than 1 invocation in 24h "
|
||||||
|
"(daily export missing or S3 trigger broken)"
|
||||||
|
),
|
||||||
|
threshold=1,
|
||||||
|
evaluation_periods=1,
|
||||||
|
comparison_operator=cloudwatch.ComparisonOperator.LESS_THAN_THRESHOLD,
|
||||||
|
treat_missing_data=cloudwatch.TreatMissingData.BREACHING,
|
||||||
|
).add_alarm_action(cw_actions.SnsAction(alarm_topic))
|
||||||
|
|
||||||
# S3 trigger: any .xlsx/.csv landing under raw/ invokes the classifier.
|
# S3 trigger: any .xlsx/.csv landing under raw/ invokes the classifier.
|
||||||
for suffix in (".xlsx", ".csv"):
|
for suffix in (".xlsx", ".csv"):
|
||||||
self.exports_bucket.add_event_notification(
|
self.exports_bucket.add_event_notification(
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,10 @@ alerts) aren't surfaced and the dashboard has no new snapshot. Recoverable by
|
||||||
re-processing the export; not permanent data loss.
|
re-processing the export; not permanent data loss.
|
||||||
|
|
||||||
### Detection
|
### Detection
|
||||||
|
- Alarm **`apm-wo-analysis-classifier-invocations`** (Sum Invocations over 86400 s `< 1`, `treat_missing_data=breaching`) — a silent day, including a missing export.
|
||||||
- No **daily summary** in the WO Slack channel by the usual time (or no 3rd-escalation alert on a day one's expected).
|
- No **daily summary** in the WO Slack channel by the usual time (or no 3rd-escalation alert on a day one's expected).
|
||||||
- **Grafana** shows no `dt = today` in the Snapshot Date dropdown / panels empty for today.
|
- **Grafana** shows no `dt = today` in the Snapshot Date dropdown / panels empty for today.
|
||||||
- **Messages in `apm-wo-analysis-classifier-dlq`** (SQS) — strongest signal the classifier failed.
|
- **Messages in `apm-wo-analysis-classifier-dlq`** (SQS) — strongest signal the classifier failed after it *did* run.
|
||||||
- CloudWatch errors in `/aws/lambda/apm-wo-analysis-classifier` or `-slack-post`.
|
- CloudWatch errors in `/aws/lambda/apm-wo-analysis-classifier` or `-slack-post`.
|
||||||
|
|
||||||
### Context
|
### Context
|
||||||
|
|
@ -83,7 +84,7 @@ The curated daily APM filter-view export (~350 WOs, `.xlsx`/`.csv`) reaches S3 b
|
||||||
**Verify the agent:**
|
**Verify the agent:**
|
||||||
```bash
|
```bash
|
||||||
launchctl list | grep apm-wo-uploader # present + last exit 0
|
launchctl list | grep apm-wo-uploader # present + last exit 0
|
||||||
tail -f ~/apm-wo-drop/uploader.log # per-run logging (TBD: confirm log path)
|
tail -f ~/.local/log/apm-wo-uploader.log # per-run logging (outside WatchPaths)
|
||||||
```
|
```
|
||||||
**Common upstream issues:** agent unloaded (`launchctl load -w …plist`); script
|
**Common upstream issues:** agent unloaded (`launchctl load -w …plist`); script
|
||||||
moved back into `~/Documents` (TCC blocks it — `LastExitStatus=32256`); `apm-wo-drop`
|
moved back into `~/Documents` (TCC blocks it — `LastExitStatus=32256`); `apm-wo-drop`
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,19 @@ set -euo pipefail
|
||||||
|
|
||||||
DROP_DIR="$HOME/apm-wo-drop"
|
DROP_DIR="$HOME/apm-wo-drop"
|
||||||
UPLOADED_DIR="$DROP_DIR/uploaded"
|
UPLOADED_DIR="$DROP_DIR/uploaded"
|
||||||
LOG_FILE="$DROP_DIR/.upload.log"
|
# Log and lock MUST sit outside WatchPaths (~/apm-wo-drop). Writing them
|
||||||
|
# inside the drop folder retriggers launchd on every log line and lock
|
||||||
|
# mkdir/rmdir, which is what ballooned .upload.log to tens of MB.
|
||||||
|
STATE_DIR="$HOME/.local/log"
|
||||||
|
LOG_FILE="$STATE_DIR/apm-wo-uploader.log"
|
||||||
|
LOCK_DIR="$STATE_DIR/apm-wo-uploader.lock"
|
||||||
BUCKET="apm-wo-analysis-exports-328440206208"
|
BUCKET="apm-wo-analysis-exports-328440206208"
|
||||||
PROFILE="${APM_WO_AWS_PROFILE:-apm-wo-drop}"
|
PROFILE="${APM_WO_AWS_PROFILE:-apm-wo-drop}"
|
||||||
|
|
||||||
mkdir -p "$UPLOADED_DIR"
|
mkdir -p "$UPLOADED_DIR" "$STATE_DIR"
|
||||||
exec >> "$LOG_FILE" 2>&1
|
exec >> "$LOG_FILE" 2>&1
|
||||||
|
|
||||||
# Single-flight: WatchPaths can fire several times for one save.
|
# Single-flight: WatchPaths can fire several times for one save.
|
||||||
LOCK_DIR="$DROP_DIR/.upload.lock"
|
|
||||||
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
|
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
|
||||||
echo "$(date '+%Y-%m-%dT%H:%M:%S') skipping — another run holds the lock"
|
echo "$(date '+%Y-%m-%dT%H:%M:%S') skipping — another run holds the lock"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
@ -50,7 +54,7 @@ for f in "$DROP_DIR"/*.xlsx "$DROP_DIR"/*.csv; do
|
||||||
else
|
else
|
||||||
echo "$(date '+%Y-%m-%dT%H:%M:%S') FAIL $name"
|
echo "$(date '+%Y-%m-%dT%H:%M:%S') FAIL $name"
|
||||||
failed_count=$((failed_count + 1))
|
failed_count=$((failed_count + 1))
|
||||||
osascript -e "display notification \"Failed to upload $name — see .upload.log\" with title \"APM WO Uploader\" sound name \"Basso\"" 2>/dev/null || true
|
osascript -e "display notification \"Failed to upload $name — see $LOG_FILE\" with title \"APM WO Uploader\" sound name \"Basso\"" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,28 @@ def test_classifier_has_dlq():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_classifier_daily_invocation_alarm_treats_missing_as_breaching():
|
||||||
|
# A silent day publishes no Invocations datapoint. NOT_BREACHING would
|
||||||
|
# hide the outage; BREACHING is the page. Dimension Value is a Ref to the
|
||||||
|
# function (function_name is set, but CDK still Refs the resource).
|
||||||
|
_template().has_resource_properties(
|
||||||
|
"AWS::CloudWatch::Alarm",
|
||||||
|
Match.object_like(
|
||||||
|
{
|
||||||
|
"AlarmName": "apm-wo-analysis-classifier-invocations",
|
||||||
|
"Namespace": "AWS/Lambda",
|
||||||
|
"MetricName": "Invocations",
|
||||||
|
"Statistic": "Sum",
|
||||||
|
"Period": 86400,
|
||||||
|
"Threshold": 1,
|
||||||
|
"ComparisonOperator": "LessThanThreshold",
|
||||||
|
"EvaluationPeriods": 1,
|
||||||
|
"TreatMissingData": "breaching",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_interactions_stage_is_throttled():
|
def test_interactions_stage_is_throttled():
|
||||||
# The public Slack interactions endpoint caps rate/burst.
|
# The public Slack interactions endpoint caps rate/burst.
|
||||||
_template().has_resource_properties(
|
_template().has_resource_properties(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue