Upgrade a deployment¶
A deployment is a Helm release of the meridian-runtime chart. Upgrading it moves the release to a
newer version of that chart, in place: the database migrates, every component restarts on the new
image, and the data, the key and the configuration stay.
What people notice:
- The CLI stays connected. A delegation from
meridian connectis kept in the deployment's database, so it survives the dashboard restarting, and nobody reconnects a terminal. - Browsers sign in again. A browser's session is held in the dashboard's memory, so the dashboard's restart ends it.
- The database stays up. A database the deployment brought, and its plugin registry, run images of their own, and an upgrade that only moves the version does not restart them. The first upgrade from a chart that still labelled their pods with its version restarts them once more. The migration waits for the database to answer, up to five minutes, rather than failing at once.
- Launched plugins keep their sidecar. A plugin keeps the sidecar image it was launched with until it is launched again. See after an upgrade.
You apply an upgrade with your own cluster rights. Nothing in a deployment holds a right to change the cluster, and the dashboard does not upgrade itself.
| To | Run or do |
|---|---|
| Upgrade to the latest published chart | meridian upgrade-deployment |
| Upgrade to a particular version | meridian upgrade-deployment --chart-version <v> |
| Change a chart value | meridian upgrade-deployment --set <key>=<value>, or -f <file> |
| Move to the HA profile, or back | meridian upgrade-deployment --set resilience.profile=ha (or =tolerant) |
| Turn on TLS inside the cluster | meridian upgrade-deployment --set tls.internal.enabled=true |
| Turn on page checksums in the database it brought | meridian upgrade-deployment --enable-checksums |
| Return to an earlier revision | meridian rollback-deployment |
| Upgrade from your own pipeline | Plain Helm, Argo CD or Flux, below |
| Upgrade the command line itself | meridian upgrade, which is a different thing |
Take a backup before you upgrade, and copy it off the cluster: meridian backup --to <dir>, with
backups set up. See Back up and restore a deployment. What an upgrade
changes on the way, release by release, is in the
release notes.
Upgrading to chart 0.1.538
Chart releases are numbered by the CI run that tested them, so the
numbers jump; 0.1.538 is the current one. Upgrading to it keeps the
counts a deployment runs: resilience.profile stays as it was, and TLS
inside the cluster and the WAL archive stay off until you turn them on
(below, and
Back up and restore). Each
store's writer reads its own records again once a week, at low priority
(<store>.scrub.intervalDays, 7; 0 for never), and meridian status
shows when it last did and anything it found. A database the deployment
brought keeps what it was made with: meridian status says whether its
page checksums are on, and
--enable-checksums turns them on. With
Helm 4, the upgrade passes --force-conflicts (see
Plain Helm).
Each component connects as its own database login
(meridian_lake, meridian_bor, meridian_instrument,
meridian_street, meridian_conductor and meridian_dashboard), each
query held to 5 seconds, so one component's slow query is refused on
its own and never holds the database for the rest. The upgrade makes
them in a database the deployment brought. For a database you run, its
administrator makes them (see
Install a deployment); until
then the upgrade's check warns, naming each login missing with the
statement that makes it, and each such component connects as the
serving role, as before.
Load is refused, never crashed on. Each component works within a
budget of its memory limit (memory.budgetPercent, 80) and, with it
full, refuses a request for load with a time to ask again; past
memory.shedPercent (90) it cancels its largest request. Each plugin
has its share at its sidecar (sidecar.limits.inFlight,
requestsPerSecond and answerMiB: 16, 400 and 32 MiB), past which
only that plugin is refused; a launch can give one its own (see
Its limits, shards and standby pod).
It also brings the Resilience page and meridian status --profile,
a restore that chooses its timeline and --to-archive-end,
and gaps and switches to a backup source
on Data sources. A plugin on SDK 0.36.0 needs it.
Upgrading from a chart before 0.1.408
The upgrade to chart 0.1.408 adds each store's change log, the table a
standby follows, and the stores' hot layers, each bounded within the
component's memory limit (see
Service levels). Every
standby value defaults to 0, so a deployment keeps one member of each
component until you ask for more. It also brings
bonds and fixed income: the
debt templates, pools' factors in the lake, and the book's fixed-income
records. Plugins keep running as they are: one built on an older SDK
registers and sends what it did; move it with
meridian plugin migrate.
Upgrading from a chart before 0.1.313
The upgrade to chart 0.1.313 migrates the
instrument store (a listed series' terms, and the company records), the
conductor (the deployment's settings kept one record each, and starter
packs) and the dashboard's notices, each adding to what is held. The
book's snapshots change format, so each account's projection is
rebuilt from its journal once; the journal is not rewritten. The
settings home page gains Series request rule (series for an order
asked for, standing wants not, 500 a day) and Stale reference actions
permitted (refuse only); the Data sources page its Age tab; Browse its
Starter packs view. Plugins keep running as they are: one built on
SDK 0.32 registers and sends what it did, and reports no latency until
it moves to SDK 0.33.0 (see
Moving a plugin to a new release).
An ems moved to SDK 0.33.0 reads Stale reference actions permitted, so
a desk whose admin chose Warn refuses until a deployment admin
permits Refuse or warn. The options measures price at the US
Treasury's par yield curve, so a deployment running them launches a
rates plugin recording it (see Rates).
Upgrading from a chart before chart 0.1.309 also rebuilds each
account's projection from its journal once, as the book's snapshots
change format, so every lot relieved before it gains its
proceeds;
the journal is not rewritten. The dashboard's settings move from a row
of tabs to a grouped sidebar, every address unchanged, with FX pivot
currencies under Deployment, reading USD, then EUR, until a
deployment admin sets them. Placements made before it have no type and
read as DMA. Plugins keep running as they are: one built on an older
SDK registers and sends what it did, and one moved to SDK 0.32.0 or
later records and reads rates, liveness and
execution costs, and places by
type (see
Moving a plugin to a new release).
From chart 0.1.311 a plugin whose catalogue states the licence's
attribution terms registers, and rates resolve by their publisher's own
codes (see
The licence list).
Upgrading from a chart before chart 0.1.308 also adds
the analytics component: two
Deployments, a coordinator and a worker, on an image of its own,
ghcr.io/open-meridian/meridian-analytics, sized by the chart's
analytics.coordinator and analytics.worker values, stateless, with
no database and no volume, every pricing model built-in until a
deployment admin routes one; and the book records
the agents' guard on, once, as its
own entry, a connectivity plugin reporting no environment for its
session being live for agents. From one before chart 0.1.306, it starts
the book's snapshots, one every bor.snapshotInterval entries (default
1,000), and gives the Plugins page Browse and Registries
(see Registries); from one before chart 0.1.305, it runs that release's instrument migration (see
Review provisional records and the migration).
meridian status shows the release, each component and plugin, and the
book's health after the upgrade.
To upgrade with the command line¶
It checks first, and changes nothing if a check fails:
- the cluster is reachable, and you may patch Deployments and create and delete Jobs in the namespace;
- your
helmis 3.14 or newer, which an upgrade needs (see why); - no node is under disk pressure. An upgrade pulls new images onto the node, which would take more
of the disk it is short of, and its new pods would be scheduled nowhere. Free disk and wait for the
pressure to lift; on a laptop VM,
docker builder prune -aoften does it. Low free disk is only reported, asworth, and does not stop it; - the release exists and Helm holds it as
deployed, notfailedorpending-…; - the version you are moving to is published, and is not older than the one installed.
If the release is at that version already, it says so and exits 0.
Then it shows what it will do, and asks:
Upgrading meridian in meridian:
from meridian-runtime 0.1.180 (revision 7), running ghcr.io/open-meridian/meridian-runtime:845bd06
to meridian-runtime 0.1.182, running ghcr.io/open-meridian/meridian-runtime:9c5d480
helm upgrade meridian oci://ghcr.io/open-meridian/charts/meridian-runtime --version 0.1.182 --namespace meridian --reset-then-reuse-values --timeout 10m
Upgrade it? [y/N]
Where the deployment was installed with an archive for its edge plugins' older records, the plan says it is kept, as every value the deployment was given is:
Its archive, /mnt/nas/meridian-archive on the cluster's node, is kept: the deployment's own values carry it.
If a node restart has left pods behind, the plan names them too, and the same answer covers removing them (see pods left over from a restart):
Then it will remove 7 pods left over from a restart, which serve nothing:
pod/meridian-meridian-runtime-broker-6d9c7b5f4-x2x8q
...
Answer y. It runs that helm upgrade, then waits, up to --timeout, for:
- the new revision's migration Job to complete;
- every Deployment and StatefulSet of the release to roll out;
- every pod of theirs to run the image its template now names.
While it waits it shows where it is (see while it runs). When they have, it deletes the finished Jobs the release ran at earlier revisions and the pods left over from a restart, and reports:
Upgraded meridian in meridian: meridian-runtime 0.1.180 -> meridian-runtime 0.1.182, now revision 8.
Migrated: job/meridian-meridian-runtime-migrate-8 completed.
Every component on its new template, and ready:
conductor 1/1 ghcr.io/open-meridian/meridian-runtime:9c5d480
dashboard 1/1 ghcr.io/open-meridian/meridian-runtime:9c5d480
...
Cleaned up 2 finished Job(s) of earlier revisions: job/meridian-meridian-runtime-migrate-7, ...
Removed 7 pods left over from a restart: pod/meridian-meridian-runtime-broker-6d9c7b5f4-x2x8q, ...
Old ReplicaSets are left to the chart's revisionHistoryLimit.
A container that restarted while the upgrade ran is listed with the reason Kubernetes gives and the
meridian logs <component> --previous that shows why. That is worth reading, and it is not a
failure: a component that starts before its migration has finished exits and is restarted.
meridian status shows each component's restarts and the last one's reason at any time, and
meridian logs <component> what its running containers have printed.
While it runs¶
On a terminal, a block below what it has said is redrawn in place every half second: each step with its time, the total time, how many components are ready, and what it waits on now, and why.
Upgrading meridian in meridian 1m 04s
✓ checks 3s
✓ plan and confirmation 12s
✓ apply 8s
⠹ migration 41s
⠹ components 41s ready 4 of 7 ███████████░░░░░░░░░
· cleanup
waiting on conductor: 0 of 1 ready, waiting for its migration
A step is pending (·), in progress (a spinner), done (✓) or failed (✗). The migration and the
components are waited for together: a component that starts before its migration has finished exits
and is restarted, so while the migration runs, that is the reason given. Once the upgrade ends, the
block stays with every step's time, above the report.
Piped to a file or run in CI, it writes a line as each step starts and ends, and every 20 seconds a line saying what it still waits for, so a log never looks stalled:
[3s] apply: started
[11s] apply: done in 8s
[11s] migration: started
[11s] components: started
[31s] still waiting, 4 of 7 components ready: conductor: 0 of 1 ready, waiting for its migration
[45s] migration: done in 34s
[1m 02s] components: done in 51s
Set NO_COLOR to turn the colour off.
Pods left over from a restart¶
When a node restarts, as a laptop's Kubernetes does when the laptop does, each pod it ran can be left
behind stopped, as Failed or Succeeded, on the image it had then. Its Deployment has already
started a new pod in its place, and nothing restarts or removes the old one. It serves nothing.
The command does not wait for such a pod: one stopped for good and made by a ReplicaSet its Deployment has since replaced. Waiting for it to run the new image would last until the timeout, since it never changes. It says so as it waits, and removes it in the cleanup, by name, with the release's other leftovers. A plugin's pods are treated the same way: a launched plugin carries the release's labels. A pod still pending, running or terminating on the old image is waited for.
--yes answers the question for a script that has already read the plan. --release, -n,
--chart and --timeout are as for meridian up; see the
command line reference.
After an upgrade: relaunch plugins¶
A launched plugin keeps the sidecar it was launched with until it is launched again, so after an upgrade it may still run beside the old version's sidecar. The report ends by naming each such plugin, and each plugin whose pod started while the upgrade was under way, when the old launcher may have launched it. For each it prints the commands that move it:
For a plugin running live it prints meridian plugin dev --instance <instance> instead. The version
is left as <version>: meridian plugin list gives it. It relaunches nothing itself.
meridian doctor warns about the same plugins, as worth, at any time.
If it stops¶
Where it stops, it names what to run next, in meridian commands alone.
| It says | What to do |
|---|---|
a check stops |
Nothing was changed. Each one says what to do. |
the release is failed |
meridian status says why, in Helm's words, on its Release line. Fix the cause, then meridian rollback-deployment returns it to its last deployed revision, and you can upgrade again. |
the release is pending-upgrade or pending-install |
Another Helm operation is running, or one was interrupted. If none is running, meridian rollback-deployment clears it. |
| the migration failed | It names the Job. Read its log with meridian logs migrate. The new revision stays applied. Read the log before rolling back: a migration that got part of the way may have changed what the older version reads. |
| still waiting after the timeout | It lists what it was waiting for, each with why. Nothing is rolled back. meridian status shows where each component is. Run it again with a longer --timeout: it is at the new version already, so it only waits. |
To change a chart value¶
--set <key>=<value> changes one of the chart's values in the same approved upgrade, read as Helm
reads it: a.b=v, several split by commas, a.list[0]=v, a list as {x,y}, and true, false,
null and whole numbers typed. Repeat it for more; each applies over every -f file. A value is
an upgrade even at the chart version the deployment runs already: the version stays, and the value
changes. At that version, where the values come out as they were, it says there is nothing to do.
The plan names each path that changes, never its value. The --set is shown in the helm upgrade
line, except a value whose key names a secret, password, token, key, code or certificate, shown as
<withheld>:
Upgrading meridian in meridian:
from meridian-runtime 0.1.538 (revision 3), running ghcr.io/open-meridian/meridian-runtime:aae501d
to meridian-runtime 0.1.538, running ghcr.io/open-meridian/meridian-runtime:aae501d
Its values, with --set backup.walArchive.enabled merged over them (values not shown):
backup.walArchive: set
helm upgrade meridian oci://ghcr.io/open-meridian/charts/meridian-runtime --version 0.1.538 --namespace meridian --reset-then-reuse-values --timeout 10m --force-conflicts --set backup.walArchive.enabled=true
Upgrade it? [y/N]
Then it applies and waits as any upgrade does. --set goes with --chart-version too, to move
the version and change a value under one approval.
To move to the HA profile, and back¶
resilience.profile turns every count of the HA configuration on at once: a standby for each
writer, the database the deployment brought with two standbys under Patroni, the broker at three,
and the dashboard and the analytics component at two (see
Service levels). It needs the deployment's key in a
Secret named by key.existingSecret; the chart refuses ha without one.
A brought database moves under Patroni in the chart's two upgrades. The plan says so before it asks, and the one approval covers both:
Its values, with --set resilience.profile merged over them (values not shown):
resilience: set
Its database moves under Patroni, which takes two upgrades, and this one approval covers both. The first moves its member 0 under Patroni alone, keeping its data; once that has rolled out and member 0 is ready, the second, with the same chart and values, adds its 2 standbys, which clone from it. Until member 0 is ready the database answers nobody.
It runs the first, waits for it as for any upgrade, then runs the second:
The first of two upgrades: the database moves under Patroni, member 0 alone.
Applied (1 of 2) as revision 5. Waiting for the migration, then every component on its new template (up to 10m).
...
Member 0 is ready under Patroni (revision 5). The second upgrade, with the same chart and values, adds its 2 standbys, which clone from it.
Applied (2 of 2) as revision 6. Waiting for the migration, then every component on its new template (up to 10m).
...
Upgraded meridian in meridian: meridian-runtime 0.1.538 -> meridian-runtime 0.1.538, now revision 6.
If the first stops, the second is not run, and it says so. Once member 0 is ready, run the same command again: it finds the database under Patroni alone, says that this upgrade is the second, and adds the standbys. If the second stops, the first stays applied, with the database under Patroni, member 0 alone and serving; the same command adds the standbys.
meridian status then lists the database's members:
Database:
meridian-meridian-runtime-database 3 member(s), under Patroni, WAL archive on
meridian-meridian-runtime-database-0 primary ready
meridian-meridian-runtime-database-1 standby ready 0 B behind the primary
meridian-meridian-runtime-database-2 standby ready 0 B behind the primary
page checksums on, full_page_writes on
Back to tolerant is one upgrade:
Each count returns to one. A database that has moved under Patroni stays under it, with no standbys.
To turn on TLS inside the cluster¶
The chart makes a certificate authority of its own and each server's certificate from it, kept
across upgrades, and gives every client the authority's certificate. The deployment's own
connections inside the cluster then use TLS, and a plain one is refused: the stores', the
dashboard's and the Jobs' to the database the chart brings, every component's and sidecar's to the
broker, the brokers' to each other, the database's standbys to their primary, and the dashboard's
requests to each plugin. A database somebody else runs is then reached over TLS too, its server
verified against the public roots and this authority, or against
database.caSecret; one that offers no TLS is
refused.
It is one upgrade at the version the deployment runs. While it rolls out, each launched plugin's
pod may restart once. meridian status then says internal TLS on. It is off by default: a
desktop's one node has none of these connections on a network.
To turn on page checksums¶
With data checksums on, Postgres notices a page corrupted on its disk when it reads it. A database
the chart makes now has them from the start. One made by an earlier chart has them off:
meridian status says page checksums off, and meridian doctor says so as worth and names
this command. Turning them on stops the database while pg_checksums reads every page, so it is a
step of its own: it takes no --chart-version, -f, --set or --https, and no chart moves with
it. Where checksums are on already, it says so and changes nothing.
It checks first, and stops, changing nothing, unless the release is deployed, it brought its database, the chart holds the step (0.1.538 does), and backups are set up, since it takes one first (see Turn backups on). Then it says the downtime, estimated from the database's size, and asks:
Turning on page checksums in meridian-meridian-runtime-database (meridian in meridian):
the database 8.7 GiB on member 0's volume, 1 member
downtime about 7 minutes, an estimate: pg_checksums reads and writes every page, taken at 50 MiB/s, and stopping and starting everything takes a few minutes.
Nothing answers until it is done.
1. A backup, now, as `meridian backup` takes one. If it fails, nothing is changed.
2. The release's CronJobs suspended (the replica comparison, the backup, the scrub), every component stopped, then the database.
3. pg_checksums --enable on member 0's volume, in a Job from meridian-meridian-runtime-database-offline.
4. The database started, then every component, each to the count it ran at, and each CronJob as it was.
5. data_checksums read again, to say it is on.
Turn page checksums on? [y/N]
The estimate is a cautious one: the database's size at 50 MiB a second, plus four minutes to stop and start everything, plus the same again for each standby. What stops is everything: every component, the launched plugins among them, and the database. Nothing answers until it is done. The release's scheduled jobs, the backup and, with standbys, the replica comparison, are suspended while it is stopped, and each is given back as it was. Under Patroni the primary is moved to member 0 first; afterwards the standbys' volumes are deleted, and Patroni makes each again from member 0. At the end it reads the setting again:
Page checksums are on in meridian's database, meridian-meridian-runtime-database.
Backed up meridian to meridian-20261109T151322Z.meridian-backup (<bytes> bytes), on the deployment's backup volume.
...
database-offline: page checksums enabled: ...
Where it stops:
| It says | What it means |
|---|---|
| the backup failed | Nothing was changed. |
| stopping everything did not finish | The database was not changed, and everything was started again. |
| the Job failed | pg_checksums marks the database last, so it is as it was, without checksums, and everything was started again. The backup taken first is named. |
| the Job is still running | Everything stays stopped while the Job has the database's volume. Once it has ended, run meridian upgrade-deployment --enable-checksums again: it takes it from there, and starts everything. |
While it has the deployment stopped, meridian status says so, since when, and the command that
resumes it (see meridian status). It does not start while a
restore has the deployment stopped, or while a Job of an earlier one still runs.
To roll back¶
It returns the release to the last revision deployed before the newest; --to-revision <n> names
another, as meridian status numbers them. It checks first, and changes nothing if a check fails.
Then it shows the revision it rolls back from and the one it returns to, and asks:
This rolls meridian in meridian back:
from revision 8, failed, chart meridian-runtime-0.1.… (app …)
to revision 7, chart meridian-runtime-0.1.… (app …), as a new revision
Helm says of 8: …
The database is not rolled back with it. …
Roll it back? [y/N]
The database is not rolled back. What the newer revision's migration changed stays, and the
older runtime may refuse a schema it does not know. Read meridian logs migrate first. If the data
must go back too, restore the backup you took before the upgrade (see
Restore a backup).
Answer y. It rolls back, then waits, up to --timeout, for that revision's migration if it runs
one and for every component on its template, as an upgrade does. --yes answers for a script. See
meridian rollback-deployment.
To upgrade from your own pipeline¶
Firms usually apply an upgrade through their own change control: a reviewed version bump, applied by a pipeline. The chart is the same one the command line applies, and it works that way too. Whatever applies it, do what the command line does:
Before: the release is deployed; the new version is published and is newer than the one
installed.
The upgrade: the new chart's defaults, with the deployment's own values over them.
After:
- Wait for the migration Job of the new revision,
<release>-meridian-runtime-migrate-<revision>, to complete. If it fails, stop and read its log. - Wait for every Deployment and StatefulSet with the label
app.kubernetes.io/instance=<release>to roll out. - Delete the finished Jobs of earlier revisions. Their names end with the revision that ran them.
The Job ending in
-keyis kept: it runs once, at install.
kubectl rollout status does not wait for pods left over from a
restart, so a pipeline is not held up by them. Deleting them is
tidying, and optional.
Plain Helm¶
helm upgrade meridian oci://ghcr.io/open-meridian/charts/meridian-runtime \
--version 0.1.182 --namespace meridian --reset-then-reuse-values
kubectl wait -n meridian --for=condition=complete --timeout=10m \
job/meridian-meridian-runtime-migrate-8
kubectl rollout status -n meridian deployment -l app.kubernetes.io/instance=meridian --timeout=10m
kubectl rollout status -n meridian statefulset -l app.kubernetes.io/instance=meridian --timeout=10m
helm history meridian -n meridian gives the new revision, the number the migration Job ends with.
With Helm 4, add --force-conflicts, as meridian upgrade-deployment does: the chart's counts then
replace the ones a first run or a restore set, as Helm 3 replaced them. A database moving under
Patroni takes two upgrades with the same values, the second once
kubectl rollout status statefulset/meridian-meridian-runtime-database says the first has rolled out;
the chart's notes say so after the first.
Do not add --wait. Charts up to 0.1.182 mark an upgrade failed under --wait on one of their own
setup resources, although the upgrade worked. Wait with kubectl as above.
Flux¶
A HelmRelease runs Helm itself. The values it holds are the deployment's own, and on each upgrade
it applies them over the new chart's defaults, which is what --reset-then-reuse-values does. To
upgrade, change the version in Git:
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: meridian-runtime
namespace: meridian
spec:
interval: 1h
url: oci://ghcr.io/open-meridian/charts/meridian-runtime
ref:
tag: 0.1.182 # the version; change this to upgrade
layerSelector:
mediaType: application/vnd.cncf.helm.chart.content.v1.tar+gzip
operation: copy
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: meridian
namespace: meridian
spec:
interval: 10m
chartRef:
kind: OCIRepository
name: meridian-runtime
upgrade:
disableWait: true # see Plain Helm: wait for the migration yourself
values: {} # the deployment's own values
Flux waits for the resources by default, which fails as --wait does on charts up to 0.1.182;
disableWait turns that off. Check the migration Job and the rollouts afterwards, as above.
Argo CD¶
Argo CD renders the chart with helm template and applies what it renders. Its Application holds the
deployment's own values, so there is nothing to reuse: each sync renders the new chart's defaults
with those values. To upgrade, change targetRevision in Git to the new chart version.
Jobs named by revision
helm template renders every release as revision 1. The chart names its migration Job and its
other per-revision Jobs by Helm's revision, so under Argo CD each upgrade renders a Job with the
same name as the last one, and Kubernetes refuses to change a Job's template. Delete the
finished migration Job before syncing an upgrade. Nothing else depends on it.
Why not --reuse-values¶
helm upgrade --reuse-values reuses the previous release's values, the old chart's defaults among
them. The image tag is one of those defaults, so the upgrade applies the new chart's templates and
keeps the old image: every pod restarts, and nothing changes version.
--reset-then-reuse-values starts from the new chart's defaults, the new image among them, and
applies only the values the deployment was given over them. It needs Helm 3.14 or newer.
If the deployment's own values set image.tag, as meridian up --image does, that tag is one of
its own values and an upgrade keeps it. meridian upgrade-deployment says so before it asks. Remove
image.tag from the deployment's values if it should move with the chart.