GoToSocial WASM-based SQLite driver and BSD

       1227 words, 6 minutes

I started using GoToSocial (the fast, fun and small ActivityPub server) in 2022 on OpenBSD. Because it was nearly the only OpenBSD-native ActivityPub options at that time, because it was light and because it could use the SQLite database engine .

I stopped using it when it was marked BROKEN because of incompatibilities between modernc.org/sqlite and OpenBSD kernel. This is when I switched to Mastodon and stop using it. Until recently, when I discovered there was a pkgsrc option available.

Introduction

I decided to use GoToSocial again this summer to communicate on the Fediverse about what happens to the noGoo.me service; a SearXNG instance I run on OpenBSD and that can be used by anybody. Thanks to pkgsrc, GoToSocial would either run on a NetBSD bhyve virtual machine or on am OmniOS/illumos zone; depending on what works and what doesn’t, and depending on my daily mood ;-)

With version 0.16.0 “Snappy Sloth”, the project introduced an alternative wasm sqlite3 implementation and it started having impacts on the program memory usage. With version 0.17.0 “Selective Sloth”, it became even worse and the devs made an unsupported “nowasm” option available which basically targets FreeBSD and Linux 32-bits. On my NetBSD VM or the OmniOS zone, I need to allocate 4GB of RAM for GtS to start even though this is a single user instance that posts like 5 messages a week. Given that my OmniOS hypervisor has 128GB of RAM, this is not a problem for me but GoToSocial is supposed to be light so there must be ways to have it light on non-Linux 64-bit platforms…

I could simply switch to some Linux implementation but…

“Strength lies in differences, not in similarities.” – Stephen Covey

Disclaimer: I never tried running GtS on FreeBSD or OpenBSD these days. What follows may apply to those OSes or be totally useless.

Using WASM-based SQLite driver on NetBSD or OmniOS

Whether you’re using pkgsrc binary packages or compile GoToSocial using the pkgsrc source tree, you’ll get a WASM (GO_BUILDTAGS=“wasmsqlite3”) enabled version of the software. Depending on the time you run the installation (or the compilation), your version of GtS may differ. There are patches for version 0.17.1 on their way that may not be committed yet.

No matter if I used an OmniOS pkgsrc branded zone or a NetBSD bhyve virtual machine, resource usage was about the same running GtS 0.17.0 in WASM mode: CPU usage was rather low but the gotosocial process used about 1GB of RSS memory and 6GB of virtual memory. There seem to be a 4GB of memory that is required for accessing the SQLite database when the program starts.

The working settings is used were:

(Fail) Using nowasm on OmniOS

Trying to compile GoToSocial 0.17.1 with the GO_BUILDTAGS="nowasm" option failed whatever I tried. The error looked like :

# modernc.org/libc
vendor/modernc.org/libc/libc_unix.go:1308:34:
  (*ctime.Tm)(unsafe.Pointer(tm)).Ftm_gmtoff undefined (type
  *struct{Ftm_sec int32; Ftm_min int32; Ftm_hour int32; Ftm_mday int32;
  Ftm_mon int32; Ftm_year int32; Ftm_wday int32; Ftm_yday int32; Ftm_isdst
  int32} has no field or method Ftm_gmtoff)
vendor/modernc.org/libc/libc_unix.go:1309:34:
  (*ctime.Tm)(unsafe.Pointer(tm)).Ftm_zone undefined (type *struct{Ftm_sec
  int32; Ftm_min int32; Ftm_hour int32; Ftm_mday int32; Ftm_mon int32;
  Ftm_year int32; Ftm_wday int32; Ftm_yday int32; Ftm_isdst int32} has no
  field or method Ftm_zone)
  *** Error code 1

There is a closed pull request that indicates this should work someday. I couldn’t compile it, even adding the sqlite3_flock flags. Maybe it will work on next GtS release…

Using nowasm on NetBSD

After patching my local pkgsrc tree, I could compile GoToSocial 0.17.1 “Very Selective Sloth” on NetBSD, without WASM, using env PKG_OPTIONS.gotosocial=nowasm make build. This modification runs the build script with GO_BUILDTAGS="nowasm" configured.

Unfortunately, replacing the wasm binary with the nowasm binary, GoToSocial would not start anymore. Even starting with an empty (or non existent) sqlite.db, errors would raise and make the program die:

func=cache.(*Caches).Start level=INFO msg="start: 0xc001548008"
unexpected fault address 0x7f7fb11c556b fatal error: fault
[signal SIGBUS: bus error code=0x2 addr=0x7f7fb11c556b pc=0x1f6be2f]

goroutine 1 gp=0xc0000061c0 m=0 mp=0x44f2ce0 [running]:
runtime.throw({0x28b01f9?, 0x100000000?})
        runtime/panic.go:1067 +0x48 fp=0xc0009e9a20 sp=0xc0009e99f0 pc=0x470088
runtime.sigpanic()
        runtime/signal_unix.go:897 +0x18a fp=0xc0009e9a80 sp=0xc0009e9a20 pc=0x4722aa
modernc.org/libc.Xmemcpy(...)
        modernc.org/libc@v1.55.3/libc.go:1745
(...)

I could finally have GtS start with an empty database when I changed the db-sqlite-journal-mode setting to “TRUNCATE”.

The working nowasm settings I used were:

Still, there were no settings that allowed me to recover my existing data by transferring the sqlite.* files; as I normally do. So I went for another SQLite backup / restore method. See next section.

With the empty database, resources consumption of the gotosocial process was finally quite low again: very low CPU, 84M of RSS memory and 1.2GB of Virtual Memory. I could then switch my NetBSD VM back to using 1 vCPU and 1GB of vRAM. The 4GB of RAM to access the SQLite database aren’t required anymore.

Migration from WASM to nowasm

The migration from one OS to the other is straightforward when the GtS version and the WASM feature are the same. I also could upgrade from 0.17.0 WASM on NetBSD to 0.17.1 WASM on OmniOS.

But when it comes to switching from WASM to nowasm, the gotosocial binary quickly dies exposing line of logs that I am not smart enough to understand. To my eyes, it was like “blah blah memory allocation failed blah blah trying to open sqlite.db blah blah modernc.org/sqlite blah blah dies”.

Hopefully, after a helpful discussion with the kind GtS devs who pointed me towards configuration options to try and what is expected (or not) regarding SQLite data migration, I finally got a stable way to migrate my existing data from a WASM instance to a nowasm instance. Here’s the receipt so that you don’t enjoy the same adventure I encountered: 🌬️🌊🚣⛈️

On the “old” WASM GoToSocial instance, stop GtS, make a copy of the configuration file and the storage directory. Then use the sqlite3 command to make a proper flat export of the SQLite database. On my initial OmniOS zone, it looked like this:

# svcadm disable svc:/pkgsrc/gotosocial:default

# tar cpf gts-backup.tar /opt/local/etc/gotosocial /var/db/gotosocial
# sqlite3 /var/db/gotosocial/sqlite.db .dump > gts-sqlite.dump.sql
# gzip -f gts-backup.tar gts-sqlite.dump.sql

Transfer the backup archives to the “new” nowasm GoToSocial instance. In my case, this is a NetBSD 10.0_STABLE byhve virtual machine. I adapted the configuration file with the correct db-sqlite-journal-mode parameter, extracted the storage directory content and recreated a sane sqlite.db file using the sqlite3 command.

# tar xzpf gts-backup.tar.gz -C / /var/db/gotosocial/storage

# rm /var/db/gotosocial/sqlite.db*
# sudo -u gotosocial sh -c \
  "zcat gts-sqlite.dump.sql.gz | sqlite3 /var/db/gotosocial/sqlite.db"

GoToSocial can now be started. It will check the database and apply migration commands if required.

Note that in this configuration, ffmpeg needs to be installed on the system. GtS expects the command to have this exact name and to be located in the PATH environment variable.

# pkgin in ffmpeg7
# ln -s /usr/pkg/bin/ffmpeg7 /usr/pkg/bin/ffmpeg
# ln -s /usr/pkg/bin/ffprobe7 /usr/pkg/bin/ffprobe

# grep -B2 PATH /etc/rc.conf
# Add local overrides below.
#
export PATH=$PATH:/usr/pkg/sbin:/usr/pkg/bin:/usr/local/sbin:/usr/local/bin

Conclusion

The following dashboard illustrate the memory gain of switching to nowasm; the switch occurred on the 31/10/2024.

The program is stable and has not crashed during the last week. There wasn’t any error while using WASM on OmniOS either. So you can choose whether you want to compile GtS with a special flag or give it a bit more memory than what a light daemon should get :)