mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
Merge branch 'fix/v1437-review' into release/v1.43.7
# Conflicts: # src/app/api/analytics/route.ts
This commit is contained in:
commit
45c027aee2
217 files changed
+3636
-2127
No files matched your search
+40
-12
@@ -4203,10 +4203,13 @@ paths:
|
||||
|
||||
|
||||
With `groupBy=day` on a cumulative type the rows are one sum per local day, and `limit` / `offset` page over
|
||||
those days; every returned day is complete. `type=SLEEP_DURATION` returns one row per night, paged the same way
|
||||
over nights. On both, `meta.total` counts the days or nights in the filtered window. A day-sum page takes
|
||||
`limit` as asked (up to the list's 5000); a sleep page holds at most 1000 nights, and `meta.limit` echoes the
|
||||
value applied.
|
||||
those days; every returned day is complete, which is why `valueMin` / `valueMax` (a filter on single readings)
|
||||
are refused with 422 beside `groupBy=day`. `type=SLEEP_DURATION` returns one row per night, paged the same way
|
||||
over nights; every night of the page has a row, and a day whose only sleep is daytime naps carries `value` 0
|
||||
with its naps in `napCount` / `napAsleepMinutes`. Its `dayKey` is the same day `/api/sleep/night?date=` answers
|
||||
for. On both, `meta.total` counts the days or nights in the filtered window. A day-sum page takes `limit` as
|
||||
asked (up to the list's 5000); a sleep page holds at most 1000 nights, and `meta.limit` echoes the value
|
||||
applied.
|
||||
parameters:
|
||||
- in: query
|
||||
name: type
|
||||
@@ -4727,8 +4730,8 @@ paths:
|
||||
source's stage segments (each with an absolute start/end), the per-stage breakdown, asleep/in-bed/awake totals,
|
||||
the mid-sleep awakenings count, and same-wake-day naps surfaced separately. Session-clustered, keyed by the
|
||||
local wake day, collapsed to one source via the sleep priority ladder so two sources never overlay. `date`
|
||||
omitted returns the most recent night. A read-only view over existing per-stage SLEEP_DURATION rows — no schema,
|
||||
no new measurement type."
|
||||
omitted returns the most recent night holding sleep, however old. Any past date is answered, with no window cut.
|
||||
A read-only view over existing per-stage SLEEP_DURATION rows — no schema, no new measurement type."
|
||||
parameters:
|
||||
- in: query
|
||||
name: date
|
||||
@@ -47896,7 +47899,9 @@ components:
|
||||
- naps
|
||||
additionalProperties: false
|
||||
description: "One reconstructed sleep night: the main session's hypnogram segments + breakdown, plus same-wake-day naps
|
||||
surfaced separately."
|
||||
surfaced separately. A session is a nap when it holds under three hours asleep centred between 10:00 and 20:00
|
||||
local time; the main session is the longest session that is not a nap. A day whose only sleep is naps has `main:
|
||||
null` and its naps."
|
||||
SleepRhythmResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -62987,14 +62992,30 @@ components:
|
||||
- type: "null"
|
||||
description: Non-null when two writer buckets reported clearly different asleep totals for the latest night's main
|
||||
session. Observational only — the served summary stays the winning writer's totals.
|
||||
recoveryEmpty:
|
||||
anyOf:
|
||||
- type: string
|
||||
enum:
|
||||
- general
|
||||
- oura_permission
|
||||
- oura_no_resilience
|
||||
- type: "null"
|
||||
description: "Why the recovery page has nothing to chart, resolved on the server; null when any recovery type
|
||||
(ANS_CHARGE, DAY_STRAIN, WORKOUT_STRAIN, CARDIO_LOAD, AVERAGE_HEART_RATE, MAX_HEART_RATE,
|
||||
ENERGY_EXPENDITURE_KJ, RESILIENCE) has a reading. `oura_permission`: Oura is connected and its grant leaves
|
||||
out the scope the resilience level needs, one reconnect fixes it. `oura_no_resilience`: Oura is connected
|
||||
and no level has arrived yet. `general`: anyone else. Clients render the empty note from this value and
|
||||
never work it out from the connection status."
|
||||
required:
|
||||
- summaries
|
||||
- bmi
|
||||
- lastSeenByType
|
||||
- sleepSourceDiscrepancy
|
||||
- recoveryEmpty
|
||||
additionalProperties: false
|
||||
description: "What `?slice=summaries` answers: the per-type summaries and their freshness, and nothing thick. Four keys,
|
||||
and the caller gets them from two SQL passes rather than the default slice's chain."
|
||||
description: "What `?slice=summaries` answers: the per-type summaries and their freshness, the recovery page's empty
|
||||
reason, and nothing thick. Five keys, and the caller gets them from two SQL passes rather than the default
|
||||
slice's chain."
|
||||
InsightsGenerateReadEnvelope:
|
||||
type: object
|
||||
properties:
|
||||
@@ -88257,9 +88278,9 @@ components:
|
||||
type: number
|
||||
description: Effective weight after redistributing missing sub-scores.
|
||||
gap:
|
||||
description: "Only on a null `consistency` / `timing`: why it is withheld. `few_nights` — fewer than three nights in the
|
||||
window have a main sleep on the clock (count in `rhythmNights`); `no_main_sleep` — the latest day had
|
||||
only a daytime nap. Absent otherwise."
|
||||
description: "Only on a null `consistency` / `timing`: why it is withheld. `few_nights` — fewer than `rhythmMinNights`
|
||||
nights in the window have a main sleep on the clock (count in `rhythmNights`); `no_main_sleep` — the
|
||||
latest day had only a daytime nap. Absent otherwise."
|
||||
type: string
|
||||
enum:
|
||||
- few_nights
|
||||
@@ -88281,6 +88302,12 @@ components:
|
||||
maximum: 9007199254740991
|
||||
description: Nights in the window with a main sleep placed on the clock — what consistency and timing are computed from.
|
||||
A daytime nap is not one; a short night is.
|
||||
rhythmMinNights:
|
||||
type: integer
|
||||
minimum: -9007199254740991
|
||||
maximum: 9007199254740991
|
||||
description: 'How many `rhythmNights` consistency and timing need before they are scored; below it they carry `gap:
|
||||
"few_nights"`.'
|
||||
series:
|
||||
type: array
|
||||
items:
|
||||
@@ -88295,6 +88322,7 @@ components:
|
||||
- subScores
|
||||
- windowNights
|
||||
- rhythmNights
|
||||
- rhythmMinNights
|
||||
- series
|
||||
additionalProperties: false
|
||||
parameters:
|
||||
|
||||
@@ -265,7 +265,10 @@ reading:
|
||||
nothing left but a data type waiting for its second attempt and queued the
|
||||
next slice for when it is due (`delaySeconds`).
|
||||
- `meta["googleHealth.backfill.ended"]` appears on the slice where a data
|
||||
type ended short, with the same entries. A walk stopped for lack of
|
||||
type ended short: the `collection`, its measurement `type`, the `reason`,
|
||||
whether the end is `final`, how many data types have ended short so far
|
||||
(`endedCount`) and of those for good (`finalCount`), and their `types`.
|
||||
The full entries are in the slice report's `ended`. A walk stopped for lack of
|
||||
progress also leaves a warning:
|
||||
`fetchHeartRate: 20 pages in a row reached no further back than <instant>`.
|
||||
- `meta["googleHealth.backfill.edgeFilterUnsupported"]` appears on the slice
|
||||
|
||||
@@ -7,7 +7,7 @@ A Node process that reaches its heap limit stops with `JavaScript heap out of me
|
||||
Two background wide events, no configuration needed:
|
||||
|
||||
- `runtime.heap.sampled` (info), every five minutes, and every minute while the heap is at or above 70 % of its limit. It goes to stdout and Loki but not to Admin > App logs, so it cannot push other events out of that page's 500-entry buffer. It carries `heap_used_mb`, `heap_total_mb`, `heap_limit_mb`, `heap_used_ratio`, `old_space_mb`, `rss_mb`, `external_mb`, `array_buffers_mb`, `active_requests`, `active_jobs`, `in_flight_dropped` and `uptime_s`.
|
||||
- `runtime.heap.pressure` (warn), the first time the heap reaches 85 % of its limit. Besides the figures above it lists the 50 oldest requests and jobs in flight (`in_flight_oldest`: kind, method, route, age in seconds, whether it was a server action) and the routes most of them share (`in_flight_by_route`). Routes are reduced to their shape: no query string, secret path segments replaced by `[REDACTED]`, ids by `:id`, anything unusual by `:x`. It writes again only after the heap has dropped below 70 % and thirty minutes have passed.
|
||||
- `runtime.heap.pressure` (warn), the first time the heap reaches 85 % of its limit. Besides the figures above it lists the 50 oldest requests and jobs in flight (`in_flight_oldest`: kind, method, route, age in seconds, whether it was a server action) and the routes most of them share (`in_flight_by_route`). Routes are reduced to their shape: no query string, secret path segments replaced by `[REDACTED]`, ids by `:id`, anything unusual by `:x`. It writes again only after the heap has dropped below 70 % and thirty minutes have passed. One exception: when the heap reaches 95 %, the same report is written again at once, with `pressure_ratio: 0.95`, whatever the cooldown; that tier re-arms as soon as the heap is back under 85 %. A heap that climbs to its limit shortly after an 85 % report therefore still leaves a report naming the work that pushed it over.
|
||||
|
||||
The heap is read every five seconds, a single call that takes microseconds. Tracking requests costs one map insert and one delete per request.
|
||||
|
||||
@@ -17,6 +17,16 @@ To find the last lines before a crash:
|
||||
docker compose logs app --since 2h | grep '"runtime.heap.'
|
||||
```
|
||||
|
||||
## The report Node writes when the heap runs out
|
||||
|
||||
The image sets `NODE_OPTIONS="--report-on-fatalerror --report-filename=stderr --report-exclude-env --report-exclude-network"`. When the process runs out of heap, Node writes a diagnostic report, about 10 KB of JSON with `"trigger": "OOMError"`, to stderr just before it exits: the JavaScript stack at the failed allocation, the native stack, the heap spaces and the open handles. It lands in the container log beside the `runtime.heap.*` lines:
|
||||
|
||||
```sh
|
||||
docker compose logs app --since 2h | grep -A400 '"trigger": "OOMError"'
|
||||
```
|
||||
|
||||
The two exclude flags keep environment variables (keys, passwords, the database URL) and network interfaces out of the report. If you set `NODE_OPTIONS` yourself, for example to raise `--max-old-space-size`, your value replaces the image's; add the four flags to it to keep the report.
|
||||
|
||||
## Optional heap snapshot
|
||||
|
||||
A snapshot shows what the heap is made of, which the events above cannot. Turn it on only while you are chasing a specific crash:
|
||||
|
||||
@@ -79,9 +79,13 @@ Raise the limit on the container that runs the worker and re-upload:
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
NODE_OPTIONS: --max-old-space-size=1024
|
||||
NODE_OPTIONS: --max-old-space-size=1024 --report-on-fatalerror --report-filename=stderr --report-exclude-env --report-exclude-network
|
||||
```
|
||||
|
||||
Your `NODE_OPTIONS` replaces the image's, so carry the four `--report-*`
|
||||
flags over: they make an out-of-memory exit leave a diagnostic report in
|
||||
the container log (see `docs/ops/heap-diagnostics.md`).
|
||||
|
||||
An import interrupted by a container restart or an out-of-memory kill
|
||||
does not stay stuck: the worker reconciles non-terminal import jobs at
|
||||
boot and every 15 minutes, flipping orphaned rows to `failed`
|
||||
|
||||
Reference in new issue
Block a user