Statistics tab and cleanup for datapoints that are no longer logged (#247) - #569
Conversation
…logged (#247) Years of enabling and deleting states leave a SQL database full of values nobody can reach any more - the reporter had over a million. Until now the only way to find and remove them was a forum script. Adapter side, two new messages: - `getDpStatistics` returns one row per ID in the `datapoints` table with its storage type, exact number of values, oldest and newest timestamp, an estimated size and a status, plus totals. It costs one `GROUP BY id` and one size query per time series table rather than one query per datapoint, because a long-running database holds thousands of IDs. - `cleanupOrphaned` removes the values of orphaned datapoints. Without `confirm: true` it deletes nothing and only reports what a confirmed run would remove, so the dialog can show the list and the counts first. Both messages share one collection routine, so the preview and the deletion cannot disagree about what is orphaned. The status deliberately separates two cases that the request lumped together. A state that no longer exists in ioBroker cannot be charted by anyone, so dropping its history is safe and that is the default. A state that still exists but has logging switched off is different: its history is reachable and may be wanted on purpose, so it is only included when the caller asks for it explicitly, and the dialog warns about it. Cleanup deletes from every `ts_*` table, not only the one the stored type suggests: a datapoint whose storage type was changed over the years has rows in more than one. It also removes the `datapoints` row, otherwise the ID would keep appearing in the statistics with zero values. Three new SQL builders per dialect: `getIdCounts`, `getTableSize` and `deleteDatapoint`. About the size column: no SQL dialect can report the bytes of a single datapoint, so it is the row count multiplied by the average row width the database reports - MySQL `information_schema`, PostgreSQL `pg_total_relation_size`/`reltuples`, MS SQL `dm_db_partition_stats`, SQLite `dbstat`. It is labelled as an estimate throughout, and when a width cannot be determined - `dbstat` is not in every sqlite3 build, a restricted role may not reach the catalog - the answer carries null rather than a fabricated number, and a total that would be partial is null too. Admin side: a `Statistics` panel holding a sortable, filterable table with the summary, and a Clean up button that runs the dry run first and opens a confirmation dialog listing every affected ID with its counts. Tests: test/testStatistics.js, 17 cases, in the CI unit list. The pure helpers live in src/lib/statistics.ts rather than main.ts so they are reachable without importing @iobroker/adapter-core. The SQL is additionally exercised against a real SQLite database, including a datapoint with rows in two tables, to show that the cleanup empties both and leaves the surviving datapoint untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`npm run lint` was not run before pushing - only prettier and the type checks - so check-and-lint caught both, and every adapter-tests job was skipped because it needs that one. - `sortField: 'count' as SortField` tripped no-unnecessary-type-assertion. The literal is already assignable; the assertion only hid that. - `super.componentDidMount()` was left floating. DataBrowser awaits it, which is what this should have done from the start. Bundle rebuilt so the committed output matches the source. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Pushed a fix for the
Worth noting for the record: the error alone failed the job, and because every
|
…atistics # Conflicts: # README.md # build/main.js.map
Implements #247, open since 2022. The reporter had over a million values left behind by states he had deleted or stopped logging, and the only way to find them was a forum script.
Adapter: two new messages
getDpStatistics— one row per ID indatapoints: storage type, exact number of values, oldest and newest timestamp, estimated size, status, plus totals. OneGROUP BY idand one size query per table, not per datapoint; a long-running database holds thousands of IDs.cleanupOrphaned— removes the values of orphaned datapoints. Withoutconfirm: trueit deletes nothing and reports only what a confirmed run would remove. Both messages share one collection routine, so the preview and the deletion cannot disagree about what counts as orphaned.One decision I made beyond the request
The request said "all IDs that no longer exist or are not logged". Those are two very different things, so the status keeps them apart:
objectMissingloggingDisabledactiveA state whose logging is merely switched off still has reachable history that may be kept on purpose. Deleting it by default would be a data-loss trap, so
selectForCleanupdefaults to the safe half even when the caller passes no scope at all.About the size column
No SQL dialect can report the bytes of a single datapoint. The column is the row count multiplied by the average row width the database reports — MySQL
information_schema, PostgreSQLpg_total_relation_size/reltuples, MS SQLdm_db_partition_stats, SQLitedbstat. It is labelled an estimate in the UI, and where a width cannot be determined (dbstatis not compiled into every sqlite3 build, a restricted role may not reach the catalog) the answer carriesnullrather than a fabricated number. A total that would be partial isnulltoo, because a partial sum presented as a total understates the footprint.Two details that are easy to get wrong
ts_*table, not only the one the stored type suggests. A datapoint whose storage type was changed over the years has rows in more than one, and the test covers exactly that case.datapointsrow. Otherwise the ID would keep showing up in the statistics with zero values after a cleanup.Admin UI
A new
Statisticspanel: sortable, filterable table with a summary line and status chips, and a Clean up button that runs the dry run first and opens a confirmation dialog listing every affected ID with its counts and estimated size.Three new SQL builders per dialect:
getIdCounts,getTableSize,deleteDatapoint.Verification
test/testStatistics.js, 17 cases, in the CI unit list. 163 unit tests pass in total;check:ts, thesrc-admintype check andprettier --checkare clean. The pure helpers live insrc/lib/statistics.tsrather thanmain.tsso they are reachable without importing@iobroker/adapter-core— the constraint #567 established.The SQL is exercised against a real SQLite database, not just asserted as strings: counts and time ranges per ID, and a cleanup of a datapoint with rows in two tables showing both are emptied, its
datapointsrow removed, and the surviving datapoint's values untouched.The admin bundle is rebuilt and committed; the
Statisticscomponent is verified present inadmin/custom/assets/.Not covered by automated tests: the React component itself. There is no component test setup in this repository, so the table, sorting and the confirmation dialog were verified by type checking and a successful federation build, not by rendering. Worth a click-through before release.
🤖 Generated with Claude Code