Skip to content

Release 1.11.0

Choose a tag to compare

@github-actions github-actions released this 18 Aug 21:39

OWA 1.11.0 adds two subsystems that work together: date partitioning for the fact tables, and a job scheduler that keeps them maintained from a single cron entry.

Upgrading: three things to do

1. Apply the database updates. The required schema version moves from 12 to 15. OWA prompts you in the browser, or:

/path/to/php cli.php cmd=update

Until this runs, the scheduler refuses every job and says so.

2. Add the cron entry. This is new, and nothing periodic runs without it:

* * * * * cd /path/to/owa && php cli.php cmd=schedule-run

One line, whatever you schedule later. Because a scheduler that is not running produces no output, OWA reminds you at the top of every admin page until the entry exists. Check it took with cli.php cmd=schedule-status.

3. Run partition-init when you are ready. New installations are partitioned automatically. An existing installation is deliberately not converted during the update, so partitioning does nothing for you until you convert it yourself:

/path/to/php cli.php cmd=partition-init --dry-run   # see the plan, change nothing
/path/to/php cli.php cmd=partition-init             # convert

Run it in a maintenance window. It rewrites each fact table twice, once to widen the primary key and once to apply the partitioning, and MySQL blocks writes on a table for the duration. Budget roughly five seconds per million rows, per table. That is why it is not part of the update: an upgrade should not decide for you when your tracking stops accepting writes.

There is no hurry and nothing degrades while you wait. Until you convert, the rotate-partitions job refuses with "no fact table is partitioned", which is the expected state rather than a fault. What you are waiting for is the payoff: retention becomes a metadata operation instead of a DELETE over millions of rows, and reports read only the periods covering the reporting period.

See Partitioning Fact Tables for what the layout looks like, how granularity is chosen, and what the conversion does.

Partitioning the fact tables (#989, #994)

All seven fact tables are split into periods on their yyyymmdd column. Dropping old data becomes a metadata operation instead of a DELETE over millions of rows, and reports read only the periods covering the reporting period.

  • Tiered layouts. Recent months keep full granularity; older history is merged into whole years, and into runs of years if needed, so a long-running installation stays inside the server's open-file budget. Controlled by OWA_PARTITION_DETAIL_MONTHS (36).
  • Nothing is deleted by default. partition-rotate ships with no keep, so it maintains the twelve-month lead and merges the tail without discarding data. Retention is opt-in, via OWA_SCHEDULED_JOBS.
  • Four commands: partition-status (read-only), partition-init (one-time conversion), partition-rotate (maintenance), partition-reorganize (change granularity), partition-drop (remove periods past a cutoff).

A job scheduler (#997, #1000)

Periodic work is registered in code and dispatched from one cron entry, rather than an entry per task.

  • One registered job: rotate-partitions, monthly, running partition-rotate with no arguments.
  • Configurable per installation through OWA_SCHEDULED_JOBS in owa-config.php: retune what ships, add jobs, or turn one off. Overrides are per key, so changing arguments keeps the shipped schedule.
  • A missed job runs once, not once per miss. The scheduler records the occurrence a run satisfied, so a machine that was off for three days runs a daily job once when it comes back.
  • Jobs never overlap themselves, via a per-job lock.
  • schedule-status explains why a job is not running, working through the possible causes and naming the first that applies rather than reporting only that something is behind.

Fixes

  • Password reset returned a 500 on PHP 8, and never sent mail when a From address was configured (#990, @digikwal)
  • Visitors roster showed the wrong day (#991)
  • JS bundles leaked globals; they are now wrapped in an IIFE (#993, @digikwal)
  • Asset URLs were built with a doubled slash (#998)
  • A missing site id, and an unresolvable action, are now answered rather than raising (#983, #996)
  • addIndex() no longer adds duplicate copies of an index, and exact duplicate indexes are dropped (#985, #986)
  • visitor_id is now indexed on the fact tables (#988)
  • The error log is created group-writable (#987)
  • CLI boolean arguments accept --switch syntax (#995)
  • Pinned patched js-yaml and fast-uri through overrides (#984)

Documentation

New wiki pages: Scheduled Jobs and Partitioning Fact Tables. Installation, Updating and Technical Requirements now cover the cron entry.

Contributors

Thank you to @digikwal for the PHP 8 password-reset fix and mailer From handling (#990), and for wrapping the JS bundles so they stop leaking globals (#993).

Internal

Test and CI work, not user facing: an isolation sweep that runs every test file in its own process, and the defects it found — settings flushed from a destructor could be lost and could exit the process 255, and Mysql::close() was not idempotent (#1001, #1002, #1003).