Skip to content

feat(history): record load, memory, disk and usage history and chart it - #1236

Open
karlitschek wants to merge 4 commits into
masterfrom
feat/history
Open

karlitschek wants to merge 4 commits into
masterfrom
feat/history

Conversation

@karlitschek

Copy link
Copy Markdown
Member

Based on feat/os-status-collector (#1214).

AI disclosure: this change was written with Claude Code (claude-opus-5-5) and is to be reviewed by the author before it leaves draft; every commit carries an Assisted-by trailer.

🤖 Generated with Claude Code

@karlitschek
karlitschek force-pushed the feat/os-status-collector branch from 6d327c2 to 34014ec Compare October 25, 2026 19:19
Base automatically changed from feat/os-status-collector to master October 25, 2026 19:24
@nextcloud-command nextcloud-command added the AI assisted This PR contains AI-assisted commits label Oct 25, 2026
@karlitschek
karlitschek force-pushed the feat/history branch 2 times, most recently from bcff3dc to cbaeb33 Compare October 25, 2026 19:45
@karlitschek
karlitschek marked this pull request as ready for review October 25, 2026 20:25
karlitschek and others added 4 commits October 26, 2026 11:59
Everything on the Status page is a snapshot: the load and memory charts
keep two minutes, and only in the browser. Whether the server was slow
at ten o'clock or how fast the disk fills cannot be answered.

A background job now samples every five minutes what a cron process
sees the same way the web server does: load average, memory and swap,
free and total space of the data directory's file system, active users
(5 min, 24 h), the file count the storage job already caches and the
database size, measured at most once an hour. PHP-FPM and OPcache are
left out: the CLI process has its own OPcache and is no FPM worker.

The same job turns the samples into hourly and daily rows (averages,
plus peaks for load, memory and active users) and expires rows after
48 hours, 30 days and a year, all configurable. Both are computed from
the five-minute samples, recomputing a bucket only while all of its
samples still exist, so reruns change nothing and a paused cron cannot
shrink a finished bucket. The job is due every four minutes and keeps
one sample per five-minute slot, as TimedJob runs only once strictly
more than the interval has passed and a five-minute interval would
skip every other cron run.

The table is wide, one column per metric: a chart reads at most a few
hundred rows of one resolution with a single index range scan, and a
rollup is one pass over the rows. A narrow (metric, value) table would
need about 15 times the rows and index entries and a pivot for every
chart; its only gain, adding a metric without a migration, is rare.
The unique index on resolution, bucket start and node serves chart
reads, expiry and duplicate protection. The bucket start is stored
because rollup rows are written long after their bucket; the node name
keeps several web servers' samples apart.

An admin-only endpoint returns one range (24 h, 7 d, 30 d, 1 y) at the
finest resolution kept for all of it, at most 1000 points.

The table is the app's first migration, so the version goes to
8.0.0-dev.2.

Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
The load and memory charts get a range switch: Live keeps today's
two-minute chart and its polling untouched, while 24 h, 7 days, 30 days
and 1 year load the stored history. A new "Disk usage over time" chart
shows the data directory's file system, used against its size.

Points sit on a regular time axis, so time without samples (a paused
cron) shows as a gap rather than a line drawn across it, and a lone
sample between two gaps is drawn as a dot. The average line uses the
five-minute load average, which covers a whole sample; hourly and daily
points add the peak as a dashed line. Daily points are UTC days and are
labelled in UTC.

Each chart has an accessible name and a sentence below it with the
average and peak (or, for disk, the change over the range), which also
serves as its text alternative. Colours come from the theme, so the
charts follow dark mode. Before the first sample the chart says that
history collection has started. Charts showing the same range share one
request, reused for a minute.

Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
The Load and Memory boxes use the shared infobox, which lays out its
children in a row, so the range switch sat beside the chart and squeezed
it. A modifier stacks the switch above the chart, and the switch keeps
its own width instead of stretching.

Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
Assisted-by: ClaudeCode:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Frank Karlitschek <karlitschek@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI assisted This PR contains AI-assisted commits

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants