My workaround for a daily ZRAM Sync of Openhab rrjd persistance

Here my rather lengthy post about what I did to use ZRAMs daily sync:

Motivation:

  1. After a power failure OpenHABs persistence is lost up to point in time when ZRAM did last syncronize the persistence folder to SD card.
  2. After a power failure ZRAM does not start - see: https://github.com/ecdye/zram-config/issues/142 but mentioned fix did not work for me.
  3. Current default ZRAM config for OpenHAB does not allow online ZRAM sync - Openhab service is stopped and after Zsync started again. I dont like that.
  4. When doing a “online” ZRAM sync OpenHAB RRD4j and MapDB persistance may fail - no system likes messing with files it wants to acces, results in loss of data.
  5. When doing a “online” ZRAM sync OpenHAB logging to openhab.log file will stop . (But it may be waken up by Karaf console e.g. by command log:tail).

My Setup:

Openhab 5.1.4 on a Raspi 4 with 2 GB of RAM.
This was set up by using OpenHABian v1.11.0. 32GB SD Card.
RaspiOS: Release = Debian GNU/Linux 12 (bookworm) , Kernel = Linux 6.6.62+rpt-rpi-v8
app. 800 Items, app. 500 items are persisted by RRD4j by every6sec and restoreOnStartup strategy. No other persistence service is running.

Workaround overview:

  1. Modify zram-config so OpenHAB service is not restated.
  2. ZRAM sync just the folder holding persistance database files.
  3. Pause RRD4j persistence logging to allow safe online Zram sync.
  4. Create a systemd timer and service to ZRAM sync once each day.
  5. Detect if Raspi was restarted right after a power fail and do a 2nd (clean) restart if so.

If you plan to implement the workaround it is time now to do some kind of backup - e.g. OpenHAB config backup using “sudo openhab-config” and menue pt. 50

Workaround steps in detail:

1) Modify zram-config so OpenHAB service is not restated running “zram-config sync”.
Default Openhabian setting for ZRAM sync will stop all related services to do a clean syncronisation to SD card.
For sure this is the safest/cleanest way to do a sync, but I really dont like that so many services are pulled down and started up again. Things can go wrong during service startups, clients may not be logging in,…
1.1) Change zram-config file to not shut down OpenHAB and related services:
I am using default zram-config from ZRAM-config github from here: zram-config/zram-config at main · ecdye/zram-config · GitHub
This can be done like this:
1.1.1) Log into host (openhabian) running OpenHAB.
1.1.2) Stop OpenHAB service: sudo systemctl stop openhab.service
1.1.3) Stop ZRAM service: sudo systemctl stop zram-config.service
Check if ZRAM stopped clean: zramctl … should not give any response, if yes delete listed “ghost” zram device e.g. sudo rm /dev/zram2
1.1.4) Remove zram-config file: sudo rm /usr/local/sbin/zram-config
1.1.5) Create a new one, copy the github zram-config to /usr/local/sbin/zram-config or use nano editor: sudo nano /usr/local/sbin/zram-config
1.1.6) paste all the content of zram-config/zram-config at main · ecdye/zram-config · GitHub and finish nano editor with “strg + X” and y to save
1.1.7) Make zram-config executable: sudo chmod 777 /usr/local/sbin/zram-config

2) Modify zram-sync to syncronize just the folder holding persistance database files.
ZRAM sync allows to add a target folder option for syncronisation - I just want to syncronize /var/lib/openhab/persistence
Also stop rrd4j persistence from writing to persistance rrd files during syncronisation (see also next pt. 3)
2.1) Create special zram-sync bash script: zram-oh-persistence-sync
This can be done like this using nano editor:
2.1.1) sudo nano /usr/local/sbin/zram-oh-persistence-sync
2.1.2) paste following text:

#!/usr/bin/env bash
cp /etc/openhab/persistence/rrd4j-empty.txt /etc/openhab/persistence/rrd4j.persist
echo "Persistence strategies emptied...now waiting 30 sec"
sleep 30
/usr/local/sbin/zram-config sync /var/lib/openhab/persistence
echo "Finished running: /usr/local/sbin/zram-config sync /var/lib/openhab/persistence ...now waiting 60 sec"
sleep 60
cp /etc/openhab/persistence/rrd4j-ok.txt /etc/openhab/persistence/rrd4j.persist
echo "Persistence strategies restored"
exit 0

2.1.3) A strg + X followed by y …saves the file
2.1.4) sudo chmod 777 /usr/local/sbin/zram-oh-persistence-sync … sets it executable

3) Pause RRD4j persistence logging to allow safe online Zram sync.
The idea is to temporerally persist no items during zram-sync and persist normally when zram-sync is done.
Persistence in OpenHAB can be defined via UI or via a setup file for rrd4j called: /etc/openhab/persistence/rrd4j.persist.
If the setup file exists it is used. Changes in persistence via UI is disabled. Changes in setup file are taken into account on the fly.
So 2 files must be created one for no persistance and one for your normal persistance strategies setup.
3.1) Create a file which will persists zero items - used during syncronisation
3.1.1) sudo nano /etc/openhab/persistence/rrd4j-empty.txt
3.1.2) paste following text:

Strategies {
    every6sec : "0/6 * * * * ? *"
}
Items {
    // File: /etc/openhab/persistence/rrd4j.persist
    // Temporarily left empty to pause persistence
}

3.1.3) A strg + X followed by y …saves the file

3.2) Create a file which will persists all items/groups you want persisted. (Here my file, change to your needs !)
3.2.1) sudo nano /etc/openhab/persistence/rrd4j-ok.txt
3.2.2) paste following text (Thats the file I use, change to your needs):

Strategies {
    every6sec : "0/6 * * * * ? *"
}
Items {
    // File: /etc/openhab/persistence/rrd4j.persist
    // Persist all Items from these Groups
        Elektroheizung*, Energiemessung1*, GoeCharger*, Heizung*, PV_Wechselrichter*, RaspberryPi*, Raum1_Thermometer*, Raum2_Thermometer*, Raum3_Thermometer*, Shelly_Heizung*, Stadel_PV*, Stromzaehler*, Thermische_Solaranlage*, Warmwasser_Boiler*, Wasserpumpe* : strategy = every6sec, restoreOnStartup
}

3.2.3) A strg + X followed by y …saves the file

I had to switch from UI persistence setup to file based persistance setup.
First I created a correct /etc/openhab/persistence/rrd4j.persist file which reflected UI setup. Then restarted OpenHAB so persistence switched over to file based setup.
Now call up UI page for persistence and go to RRD4j configuration. If it looks like you still are able to do modifications via UI you should correct this:
Press red Remove persistence configuration link at the bottom of the config page and OK the popup and finally Save (upper right corner).
Calling up the RRD4j Config page again should show now: Note: This persistence configuration is not editable because it has been provisioned from a file.
Check OH documentation for persistence here: Persistence | openHAB

4) Create a systemd timer and service to ZRAM sync once each day.
The above created /usr/local/sbin/zram-oh-persistence-sync file has to be started once a day to do syncronisation.
So 2 systemd files are created one timer and one service file called by the timer
4.1) Create new zsync timer systemd file
4.1.1) sudo nano /etc/systemd/system/zsync-oh-persistence.timer
4.1.2) paste following text

[Unit]
Description=Perform nightly zram sync to persistent storage (only OpenHab Persistence)

[Timer]
Unit=zsync-oh-persistence.service
OnCalendar=*-*-* 23:15
RandomizedDelaySec=10m
Persistent=true

[Install]
WantedBy=timers.target

4.1.3) A strg + X followed by y …saves the file
So with this timer the sync takes place at around 23:15 , you may change this to your needs.

4.2) Create new zsync service systemd file
4.2.1) sudo nano /etc/systemd/system/zsync-oh-persistence.service
4.2.2) paste following text

[Unit]
Description=Perform nightly zram sync to persistent storage (only OpenHab Persistance)
After=zram-config.service
Wants=zsync-oh-persistence.timer

[Service]
Type=oneshot
Environment=SERVICE=1
ExecStart=/usr/local/sbin/zram-oh-persistence-sync

4.2.3) A strg + X followed by y …saves the file

4.3) Enable and start the new timer and service file
4.3.1) sudo systemctl start zsync-oh-persistence.timer
4.3.2) sudo systemctl enable zsync-oh-persistence.timer
4.3.3) sudo systemctl daemon-reload

5) Detect if Raspi was restarted right after a power fail and do a 2nd (clean) restart if so.
When a power fail occurs there is a high chance that ZRAM will not start up correctly.
This happenes on my system and is also described here: Zram is not loaded after a power outage · Issue #142 · ecdye/zram-config · GitHub
The mentioned fix did not work for me, so following my brutal fix…to detect power fail and restart a 2nd time if booting was after a power failure
At Raspi startup a file is checked for existance and gets created. At an orderly shutdown this file gets deleted.
So if this file exist at startup boot up was right after a power fail and a reboot command is fired up.
5.1) Create a bash script to do the “magic” decribed above.
5.1.1) sudo nano /bin/powercheck
5.1.2) paste following text

#!/bin/sh

FLAG1=/etc/poweroff-flag-file
FLAG2=/etc/bad-reboot-flag-file

if [ "$1" = "start" ]; then
  if [ -f "$FLAG1" ]; then
    echo "Detected impropert shutdown" >&2
    (
    echo 'Detected improper reboot'
    rm -f $FLAG1
    echo "Power fail detected" > $FLAG1
    rm -f $FLAG2
    echo "Power fail detected" > $FLAG2
    echo "So reboot now again..." >> $FLAG2
    date >>  "$FLAG2"
    reboot
    )
  fi

  date >>  "$FLAG1"
elif [ "$1" = "stop" ]; then
  rm -f $FLAG1
fi

5.1.3) A strg + X followed by y …saves the file
5.1.4) sudo chmod 777 /bin/powercheck … sets it to executable

5.2) Create a systemd service to have the bash script run a boot and shutdown.
5.2.1) sudo nano /etc/systemd/system/powercheck.service
5.2.2) paste following text

[Unit]
Description=Detect if system was shut down cleanly
After=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/powercheck start
ExecStop=/bin/powercheck stop

[Install]
WantedBy=multi-user.target

5.2.3) A strg + X followed by y …saves the file
5.2.4) sudo systemctl start powercheck.service
5.2.5) sudo systemctl enable powercheck.service
5.2.6) sudo systemctl daemon-reload

Its also a good idea to finally reboot your OpenHAB host using : sudo reboot

Notes:

  1. I did test 15 times a zram-sync followed by a power fail - all ok, no loss of data. And about 60 times a zram-sync which is done then automatically on a daily basis - all ok.

  2. Actual zram-config included a “bind_dir” folder in /etc/ztab config file. This is now removed, automatically if old version of ztab is detected.

    See: GitHub - ecdye/zram-config: A complete zram-config utility for swap, directories, and logs to reduce SD, NAND and eMMC block wear. · GitHub

  3. I deleted MapDB persistance and only use now RRD4j - also to restore items on startup (Strategy: restoreOnStartup). My tests where showing that MapDB stops working once it is “online” synced. Keep in mind that RRD4j just saves numbers and status of switches.

  4. I changed also my ztab file (very optional - zram must be stopped during editing!), my /etc/ztab file looks like this:

# swap	alg		mem_limit	disk_size	swap_priority	page-cluster	swappiness
swap	lzo-rle		200M		1G		75		0		150

# dir	alg		mem_limit	disk_size	target_dir		
dir	lzo-rle		800M		2G		/var/lib/openhab/persistence

# log	alg		mem_limit	disk_size	target_dir			oldlog_dir
log	lzo-rle		400M		1G		/var/log

Did now some corrections:

  • It’s called persistent and not persistant (pardon my denglish)
  • systemctl commands for zsync-oh-persistence.service removed (not needed)
  • Migration from UI based to file based persistence refined.

Can you please submit this as a PR to openHABian so everybody can benefit?

I doubt #3 is required, plus for a general setup there’s more OH parts / 3rd party systems such as InfluxDB that access zram mounts.
So make that optional and discuss on Github upfront cc:@ecdye.

Regarding Pt. 3 - pausing RRD4j from writing:
During testing I did see problems when not doing it.
A zsync without was resulting in empty RRD4j .rrd database files for some of my 500 persisted items. Not on every zsync, I would say there was a 25% chance to loose RRD4j data.

To make this more suitable for other setups I plan to test also how MapDB is doing. Since I don’t have others persistence running I was hoping on community involvement …
For sure InfluxDB would be an important persistense to test.

Be aware that rrd4j can only restore numeric states. All items that are not Number or OnOff types cannot be restored (e.g. Color lights).

Added now MapDB as an option. But had not time to do Zsync tests … will follow.

Did now some quick tests with MapDB active during “online” (OpenHAB running) zram syncronisation. Bad news…once zram syncs MapDB does not come to life any more. The timestamps of the storage.mapdb files do not change any more and no data is saved.
Trying to halt MapDB from writing by changing to a “empty” mapdb.persist file is no problem. While mapdb.persist is not containing any items also the timestamps do not change, but return to life once items are implemented again.

Seems that MapDB is not so “forgiving” messing around with its files as RRD4j is.

So I remove the MapDB option …

While testing cloning and syncing of SD cards in openhabian, I inspected the zram-config script and made a couple of observations that may be relevant for your application.

  1. when zram-config “sync” is issued the script stops an harcoded list of services, including openhab, but with the --no-block option. The intent is to avoid that the services write on persistence, but it does not wait for the service to properly stop . Especially under heavy load it may take a while for the service to stop but zram-config does not wait and proceed immediately.
  2. The power loss condition is very similar to live raw copy or sync. I think that the lack of ZRAM devices at first boot is due to the remnants of ZRAM temporary files, such as /usr/local/lib/zram-config/zram-device-list. I reported some tests in my post

Thanks for the inputs,
ad. 1) The list of services to be stoped by zram sync is the reason why I propose in step 1.1 to use the original zram-config file (from main branch). The zram-config file used by OpenHABian setup uses zram-config the openhab branch and stops a bunch of services like openhab, mosquitto,… which are used by OpenHab.
I will do some tests to check if --no-block option would give some advantages - specially in correctly syncing MapDB persistance which I had to remove from my workaround.
ad.2) Yes I had same ( … already exists as a zram device, skipping recreation of device. ) warnings in zram-config.log . Anyway a reboot due to power loss must be detected (step 5) . I have to try if just deleting file /usr/local/lib/zram-config/zram-device-list at a reboot after power fail could make 2nd reboot obsolete. Depends if deleting can be done before zram-config.service is started.

It seems to me that the final result of your procedure is to replicate the openhabian “SD Sync” behavior but using the main SD as target instead of an SD in an external USB reader.

I would say that instead of modifying zram-config and trying to give a zsync command, which is known to have several issues, it would be much easier to directly use a modified version of the command that transfers persistence files to the SD card: it is simply matter of changing the target path.

As openHAB persistence is very challenging in your system (500 items every 6 seconds is a lot and writes very frequently) your idea of temporarily pausing persistence in openHAB by swapping its textual configuration is very interesting and in principle it should work also for mapdb persistence as well. The sync script would simply be something like

bash
mv /srv/openhab-conf/persistence/rrd4j-empty.cfg /srv/openhab-conf/persistence/rrd4j.persist
mv /srv/openhab-conf/persistence/mapdb-empty.cfg /srv/openhab-conf/persistence/mapdb.persist

sleep 5 # time required for openhab to really pause persistences

rsync --one-file-system --delete -aKh "/var/lib/openhab/persistence/" /opt/zram/persistence.bind  # transfer from upper (in ZRAM) to lower (in SD)

mv /srv/openhab-conf/persistence/rrd4j-ok.persist /srv/openhab-conf/persistence/rrd4j.cfg
mv /srv/openhab-conf/persistence/mapdb-ok.persist /srv/openhab-conf/persistence/mapdb.cfg

In my case, after the correction of “SD sync”, openhabian mirror from ZRAM to the external SD that occurs every 2 hours (I have performed to pull request to update the documentation where it is stated the SD mirror is performed daily) always work, but my load is lighter (200 items every minute) and the likelyhood that rsync runs when openhab writes is very low (but not zero!).

You’re right, I would even suggest to replace the current zsync implementation with that command.

Not sure what the file renaming is needed for, though.
And I believe persistence load should not make a difference here as rsync does the job file-by-file.

PS your sync analysis was a great job, thanks a lot

As mentioned in the first post by the OP, synced mapdb persistence file happened to get corrupted during his test. Corruption of synced files never occured during all my tests but I did not push the workload very much because I was focused on replicating the issue of RawCopy always needing to perform e2fsck before set-partuuid.
In principle, by increasing the workload using my test scripts the rsync can be tested in a more demanding context. Possibly mapdb is more prone to corruption because many items are related to a single file.