toga-ai 1.0.596 → 1.0.597
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
| [Compass VIP Support Importer (worker2)](features/compass-vip-support-importer.md) | A worker2 action that ingests Compass's quarterly VIP spreadsheet and assigns each VIP user's support technician by setting `Users.c_supportedByUserId` in `Clie | worker2/Worker/Client/Compass/VipSupport.php |
|
|
19
19
|
| [Creating Worker Actions](features/creating-worker-actions.md) | How to add a new callable Worker action — a PHP class whose `public static` methods are invoked as background jobs (via webhook, cron, or `_Worker::runTask()`). | worker2/Worker/, worker2/Controller/Index.php, _underscore/Worker.php |
|
|
20
20
|
| [Cross-account AWS access for worker2 crons (_Component_Aws_Workloads)](features/cross-account-aws-access.md) | `_Component_Aws_Workloads` is the **standard, and only sanctioned, way any new worker2 cron obtains AWS access** — for any account, any region, any AWS SDK clie | worker2/Component/Aws/Workloads/Workloads.php, worker2/Config/production.ini, worker2/Worker/Infrastructure/CloudWatch.php |
|
|
21
|
+
| [Elastic Beanstalk health monitor → OneUptime (ElasticBeanstalkHealth)](features/elastic-beanstalk-health-monitor.md) | `_Worker_Infrastructure_CloudWatch::ElasticBeanstalkHealth()` is a worker2 cron that reads each Elastic Beanstalk (EB) environment's **enhanced-health** status | worker2/Worker/Infrastructure/CloudWatch.php |
|
|
21
22
|
| [Elite Freshservice Sync (worker2)](features/elite-freshservice-sync.md) | `_Worker_Elite` processes Freshservice webhook events and syncs them into TOGA 2. | worker2/Worker/Elite.php, worker2/Config/dev-kmaramreddy-laptop.ini |
|
|
22
23
|
| [Error Escalation Cron (Errors::Escalate → ClickUp / email)](features/error-escalation-cron.md) | `_Worker_Infrastructure_Errors::Escalate` (renamed from `SyncWithClickup`) is the sole owner of **escalation, de-escalation, ClickUp ticketing, reminders, busin | worker2/Worker/Infrastructure/Errors.php, worker2/Worker/Notification/Email.php, worker2/Worker/Notification/EmailTemplate.php, worker2/Worker/Client/True.php, worker2/Worker/Clickup/ErrorTask.php, worker2/Worker/Clickup.php, worker2/Controller/Index.php, worker2/Config/production.ini, _underscore/Model/Core/Logs/Issue.php, dbchanges2/Core/2026-07-30a - Error escalation cron job.sql, dbchanges2/Logs/2026-08-03a - Issue clickupPriority.sql, dbchanges2/Core/2026-08-04a - Error neglect digest cron job.sql |
|
|
23
24
|
| [Error-Issue Auto-Resolution & Reopen (frequency-decay lifecycle)](features/error-issue-auto-resolution.md) | The error system could escalate and de-escalate an Issue's *urgency* but had no concept of an Issue being **resolved**. | worker2/Worker/Infrastructure/Errors.php, worker2/Worker/Clickup/ErrorTask.php, _underscore/Model/Core/Logs/Issue.php, tools/mvc/errors/get.php, tools/mvc/errors/issue/get.php, dbchanges2/Logs/2026-08-05a - Issue status baseline and auto-resolution.sql |
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Elastic Beanstalk health monitor → OneUptime (ElasticBeanstalkHealth)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: worker2
|
|
5
|
+
project: Worker
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-08-17
|
|
10
|
+
owners: [jcardinal]
|
|
11
|
+
files:
|
|
12
|
+
- worker2/Worker/Infrastructure/CloudWatch.php
|
|
13
|
+
related:
|
|
14
|
+
- ./cross-account-aws-access.md
|
|
15
|
+
- ./oneuptime-worker2-monitoring.md
|
|
16
|
+
- ./monitoring-framework.md
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Summary
|
|
20
|
+
|
|
21
|
+
`_Worker_Infrastructure_CloudWatch::ElasticBeanstalkHealth()` is a worker2 cron that reads
|
|
22
|
+
each Elastic Beanstalk (EB) environment's **enhanced-health** status and reports it to a
|
|
23
|
+
OneUptime **Incoming Request** monitor, which pages the team. It is a **"dumb reporter,
|
|
24
|
+
smart monitor"** heartbeat (the same Pattern-B contract as
|
|
25
|
+
[OneUptime push-metric monitors](./oneuptime-worker2-monitoring.md)): the worker decides
|
|
26
|
+
pass/fail and POSTs decided **string tokens** (`alarm=HIGH/OK`, `probe=DEGRADED/OK`) that
|
|
27
|
+
OneUptime string-matches — OneUptime cannot compare numbers on a pushed body.
|
|
28
|
+
|
|
29
|
+
The load-bearing design decision this doc records is the **paging (alarm) criteria**: page
|
|
30
|
+
on EB `Degraded`/`Severe`, but **suppress the common "an occasional HTTP 500 tripped
|
|
31
|
+
Degraded" false alarm** unless 5xx errors actually dominate the request mix.
|
|
32
|
+
|
|
33
|
+
**Critical behavior:** the method is **non-fatal and never throws** — it always returns a
|
|
34
|
+
string. worker2 has **no DLQ and a 3600s SQS visibility timeout**, so any uncaught 500
|
|
35
|
+
becomes a poison-message storm; an outer `catch(\Throwable)` is the backstop and the push
|
|
36
|
+
itself runs non-fatal.
|
|
37
|
+
|
|
38
|
+
## Key files / entry points
|
|
39
|
+
|
|
40
|
+
- `worker2/Worker/Infrastructure/CloudWatch.php` — `_Worker_Infrastructure_CloudWatch`,
|
|
41
|
+
action `ElasticBeanstalkHealth`, dispatched via
|
|
42
|
+
`_Worker::runTask('Infrastructure/CloudWatch/ElasticBeanstalkHealth', {awsAccountId,
|
|
43
|
+
environments, oneuptimeUrl})`.
|
|
44
|
+
- AWS access is obtained through `_Component_Aws_Workloads::assumeCredentials()` (STS-assume
|
|
45
|
+
a read-only role per account, one assume reused across regions) — see
|
|
46
|
+
[Cross-account AWS access](./cross-account-aws-access.md), which also documents the
|
|
47
|
+
`environments` region→env-names map parameter shape.
|
|
48
|
+
- `oneuptimeUrl` arrives as a **cron parameter and is a push credential** — never log it and
|
|
49
|
+
never record its value in a doc (see gotchas).
|
|
50
|
+
|
|
51
|
+
## Alarm / paging criteria (the decision logic)
|
|
52
|
+
|
|
53
|
+
Per environment, `alarm=HIGH` (page) when the EB `HealthStatus` is `Degraded` **or**
|
|
54
|
+
`Severe`. `Suspended` never pages.
|
|
55
|
+
|
|
56
|
+
- **`Severe` always pages.**
|
|
57
|
+
- **`Degraded` for a non-5xx reason** (latency, instances down, …) **always pages.**
|
|
58
|
+
- **`Degraded` attributed to HTTP 5xx errors pages only when 5xx errors dominate** — i.e.
|
|
59
|
+
the 5xx share of requests is at/above `HTTP_5XX_ALARM_RATIO` (default 0.80). An occasional
|
|
60
|
+
500 that trips Degraded is noise and must **not** page; a flood is a real problem and
|
|
61
|
+
**must** page.
|
|
62
|
+
- **Tiny-sample guard:** below `HTTP_5XX_MIN_REQUEST_COUNT` (default 20) requests in the
|
|
63
|
+
window, do **not** trust the ratio — page rather than let a 2-of-3 blip read as "80%
|
|
64
|
+
failing."
|
|
65
|
+
|
|
66
|
+
### Fail-safe: a blind read pages, never reads healthy
|
|
67
|
+
|
|
68
|
+
If the health read (`describeEnvironmentHealth`) throws `AwsException` for an environment,
|
|
69
|
+
the monitor sets **`alarm=HIGH`** for that environment (a blind read must never masquerade
|
|
70
|
+
as healthy) **and** sets **`probe=DEGRADED`** (coverage gap, distinct from a measured-bad
|
|
71
|
+
signal — same alarm-vs-probe split as the multi-client monitors in
|
|
72
|
+
[OneUptime push-metric monitors](./oneuptime-worker2-monitoring.md)).
|
|
73
|
+
|
|
74
|
+
### Configurable constants (top of the class)
|
|
75
|
+
|
|
76
|
+
| Constant | Default | Meaning |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `HEALTH_ALARM_STATUSES` | `['Degraded','Severe']` | HealthStatus values that page |
|
|
79
|
+
| `HTTP_5XX_ALARM_RATIO` | `0.80` | 5xx share (0.0–1.0) at/above which a 5xx-driven `Degraded` pages |
|
|
80
|
+
| `HTTP_5XX_MIN_REQUEST_COUNT` | `20` | Below this many requests in the window, page rather than trust the ratio |
|
|
81
|
+
| `HTTP_5XX_CAUSE_MARKERS` | `['5xx','http 5']` | Case-insensitive substrings identifying a 5xx-attributed EB `Cause` |
|
|
82
|
+
|
|
83
|
+
### Implementation shape
|
|
84
|
+
|
|
85
|
+
- `shouldPageForHealth(healthStatus, causes, applicationMetrics): [bool, ?float]` — returns
|
|
86
|
+
the page decision and the observed 5xx ratio.
|
|
87
|
+
- `causesAttributeTo5xx(causes): bool` — substring-matches `HTTP_5XX_CAUSE_MARKERS` against
|
|
88
|
+
the EB `Causes` strings (case-insensitive).
|
|
89
|
+
- The ratio is computed as `StatusCodes.Status5xx / RequestCount` from `ApplicationMetrics`
|
|
90
|
+
(raw counts — see below), **not** by parsing the percentage out of the `Causes` text.
|
|
91
|
+
- `describeEnvironmentHealth` is called with
|
|
92
|
+
`AttributeNames = ['HealthStatus','Status','Color','Causes','ApplicationMetrics']`.
|
|
93
|
+
- Per-environment report + OneUptime payload now include the per-env `alarm` token and the
|
|
94
|
+
observed `fivexxRatio`.
|
|
95
|
+
|
|
96
|
+
## How EB enhanced-health is read (durable AWS reference)
|
|
97
|
+
|
|
98
|
+
Facts verified against current AWS docs; they inform why the logic above is shaped as it is.
|
|
99
|
+
|
|
100
|
+
- **HealthStatus severity ladder:** `Ok → Info → Warning → Degraded → Severe` (plus the grey
|
|
101
|
+
states `Pending`/`Unknown`/`Suspended`/`NoData`). `Degraded` is the "high failure" tier
|
|
102
|
+
and is **routinely tripped by benign transients** (Auto Scaling scale-up, a mid-deploy
|
|
103
|
+
dip), which is exactly why `Degraded` alone is noisy. `Severe` = "very high failure /
|
|
104
|
+
environment effectively not serving."
|
|
105
|
+
- **`ApplicationMetrics` returns RAW COUNTS, not percentages** — despite the API-reference
|
|
106
|
+
prose saying "percentage"/"per second". Fields: `Duration` (Integer seconds, usually 10),
|
|
107
|
+
`RequestCount` (Integer, **total** requests over the window),
|
|
108
|
+
`StatusCodes.{Status2xx,Status3xx,Status4xx,Status5xx}` (Integer counts). AWS's own
|
|
109
|
+
example: 2xx 3391 + 5xx 843 = RequestCount 4234. With no traffic, `RequestCount=0` and
|
|
110
|
+
`StatusCodes` may be **absent** → treat as "no data," not "zero failures." The 5xx-ratio
|
|
111
|
+
denominator is `RequestCount` (the authoritative total).
|
|
112
|
+
- **Why an env is Degraded comes from `Causes`:** EB `Causes` strings literally contain e.g.
|
|
113
|
+
`"19.9 % of the requests are failing with HTTP 5xx."` A substring check reliably
|
|
114
|
+
**classifies** 5xx-driven vs. not (fail-safe: a wording change → treated as a normal
|
|
115
|
+
`Degraded` → pages). The **ratio itself must come from `ApplicationMetrics`**, never by
|
|
116
|
+
parsing the number out of `Causes`.
|
|
117
|
+
- **A failed application DEPLOYMENT is NOT reliably reflected in HealthStatus** — it can read
|
|
118
|
+
`Warning`/`Degraded`, or fail before any red request-failure signal. The authoritative
|
|
119
|
+
structured deploy signal is per-instance `describeInstancesHealth` →
|
|
120
|
+
`Deployment.Status ∈ {'In Progress','Deployed','Failed'}`. (This detector was evaluated and
|
|
121
|
+
removed this iteration — see Design history.)
|
|
122
|
+
- **IAM:** `describeEnvironmentHealth` / `describeInstancesHealth` / `describeEvents` are all
|
|
123
|
+
covered by the managed `AWSElasticBeanstalkReadOnly` policy and require enhanced health
|
|
124
|
+
enabled. No per-call charge.
|
|
125
|
+
|
|
126
|
+
## OneUptime monitor config (external, not code)
|
|
127
|
+
|
|
128
|
+
Paging depends on the OneUptime Incoming-Request monitor's **string-match** criteria on the
|
|
129
|
+
POSTed body — `Contains "alarm":"HIGH"` (and, if desired, `Contains "probe":"DEGRADED"`).
|
|
130
|
+
This is external OneUptime configuration, not worker2 code.
|
|
131
|
+
|
|
132
|
+
## Design history — rejected direction (this session)
|
|
133
|
+
|
|
134
|
+
An earlier iteration set `HEALTH_ALARM_STATUSES = ['Severe']` (dropping `Degraded` entirely)
|
|
135
|
+
and **added a separate deployment-failure detector** (`describeInstancesHealth` →
|
|
136
|
+
`Deployment.Status = 'Failed'`) to still catch deploy failures. That was **reverted** after a
|
|
137
|
+
scope change: the team decided they **do** want to page on `Degraded` (to catch a flood of
|
|
138
|
+
500s and non-500 degradations), with only the occasional-500 case suppressed via the 80%
|
|
139
|
+
ratio. The deployment-failure detector was **removed** — deploy failures that surface as
|
|
140
|
+
`Degraded` are now covered by the `Degraded` alarm. **Residual gap accepted for this
|
|
141
|
+
iteration:** a deploy that fails fast *without ever degrading health* is not caught.
|
|
142
|
+
|
|
143
|
+
## Known gaps / follow-ups (not done)
|
|
144
|
+
|
|
145
|
+
- **Per-region client construction is not individually guarded.** Each
|
|
146
|
+
`new ElasticBeanstalkClient` is not in its own try/catch, so a bad region/creds aborts the
|
|
147
|
+
**whole run** via the outer backstop instead of paging just that region's envs as blind.
|
|
148
|
+
Candidate follow-up now that blind reads page.
|
|
149
|
+
- **No first-party test harness exists in worker2** (all tests are vendor/).
|
|
150
|
+
`shouldPageForHealth()` and `causesAttributeTo5xx()` are pure and ideal to unit-test
|
|
151
|
+
(ratio at exactly 0.80; count 19 vs 20; `Severe` over a 5xx cause; empty causes; null
|
|
152
|
+
metrics) — deferred pending a harness.
|
|
153
|
+
|
|
154
|
+
## Gotchas / known issues
|
|
155
|
+
|
|
156
|
+
- **Keep the method non-fatal — never let it throw.** worker2 has no DLQ and a 3600s SQS
|
|
157
|
+
visibility timeout, so any uncaught 500 becomes a poison-message storm. The push runs
|
|
158
|
+
non-fatal and an outer `catch(\Throwable)` is the backstop.
|
|
159
|
+
- **A blind health read must page, not read healthy** — an `AwsException` on
|
|
160
|
+
`describeEnvironmentHealth` sets `alarm=HIGH` + `probe=DEGRADED` for that env.
|
|
161
|
+
- **`ApplicationMetrics` is raw counts, not percentages** — divide `Status5xx` by
|
|
162
|
+
`RequestCount`; `RequestCount=0`/absent `StatusCodes` means "no data," not "zero failures."
|
|
163
|
+
- **Classify 5xx-attribution from `Causes` text, but take the ratio from `ApplicationMetrics`
|
|
164
|
+
— never parse the percentage out of `Causes`.**
|
|
165
|
+
- **`oneuptimeUrl` is a push credential** — it arrives as a cron parameter; never log it or
|
|
166
|
+
record its value in a doc.
|
|
167
|
+
|
|
168
|
+
## Change history
|
|
169
|
+
- 2026-08-17 — Created. Documented the `ElasticBeanstalkHealth` alarm/paging criteria (page
|
|
170
|
+
on `Degraded`/`Severe`; suppress a 5xx-driven `Degraded` unless the 5xx share ≥
|
|
171
|
+
`HTTP_5XX_ALARM_RATIO` 0.80 with an `HTTP_5XX_MIN_REQUEST_COUNT` 20 tiny-sample guard;
|
|
172
|
+
`Severe` and non-5xx `Degraded` always page), the blind-read fail-safe (`alarm=HIGH` +
|
|
173
|
+
`probe=DEGRADED`), the four configurable constants, and the `shouldPageForHealth` /
|
|
174
|
+
`causesAttributeTo5xx` helper shape. Captured durable EB enhanced-health facts (severity
|
|
175
|
+
ladder; `ApplicationMetrics` returns raw counts not percentages; `Causes` classifies but
|
|
176
|
+
`ApplicationMetrics` sets the ratio; failed deploys aren't reliably in HealthStatus —
|
|
177
|
+
`describeInstancesHealth.Deployment.Status` is authoritative; `AWSElasticBeanstalkReadOnly`
|
|
178
|
+
covers the reads). Recorded the reverted `['Severe']`-only + deployment-failure-detector
|
|
179
|
+
direction and the accepted residual gap (a deploy that fails without degrading health).
|
|
180
|
+
(jcardinal)
|
|
181
|
+
</content>
|
|
182
|
+
</invoke>
|
package/knowledge/INDEX.md
CHANGED
|
@@ -19,7 +19,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
19
19
|
## 2.0 framework
|
|
20
20
|
|
|
21
21
|
- **_underscore** (_Underscore) _(framework core)_ — 58 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
22
|
-
- **worker2** (Worker) —
|
|
22
|
+
- **worker2** (Worker) — 50 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
23
23
|
- **api2** (API) — 24 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
24
24
|
- **dbchanges2** (Database Changes) _(framework core)_ — 8 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
25
25
|
- **toga2-supply** (TOGa Supply) — 6 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
|
package/package.json
CHANGED