Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sql: add copy-based migrations and VACUUM INTO (#6085)
------- ## Motivation Currently, when vacuuming is required after database migration (according to the configured `db-vacuum-state` value), simple SQL `VACUUM` command is used. According to the [description](https://www.sqlite.org/lang_vacuum.html#how_vacuum_works) in the SQLite docs, `VACUUM` requires as much as twice the size of the original database file in free disk space, as first it makes a copy of the database in the temporary directory, and then it copies the vacuumed database back to the original database utilizing WAL file which grows to the size of the database itself before changes are committed. According to the [remark in the SQLite source](https://github.com/sqlite/sqlite/blob/105c20648e1b05839fd0638686b95f2e3998abcb/src/vacuum.c#L97-L103) ``` ** Only 1x temporary space and only 1x writes would be required if ** the copy of step (3) were replaced by deleting the original database ** and renaming the transient database as the original. But that will ** not work if other processes are attached to the original database. ** And a power loss in between deleting the original and renaming the ** transient would cause the database file to appear to be deleted ** following reboot. ``` In case of go-spacemesh, we don't need concurrent access to the database from multiple processes, so we can optimize the vacuuming step to use only 1x space of the original database for vacuuming, given that `VACUUM INTO` makes a vacuumed copy of the database with no space requirements besides the size of the copy. Moreover, we can use the temporary copy of the database to run migrations faster as we can use `PRAGMA journal_mode=OFF` and `PRAGMA synchronous=OFF` for the temporary database safely, just dropping it if something goes wrong during migration. On top of requiring 2x space, normal `VACUUM` (without `INTO`) has another problem: very slow last step in which the original database is replaced. Let's compare `VACUUM` with `VACUUM INTO` on a Mac M3 Max laptop: ```console $ ls -lh /tmp/state.sql* total 164511960 -rw-r--r-- 1 ivan4th staff 78G Jun 28 00:06 state.sql -rw-r--r-- 1 ivan4th staff 32K Jun 28 01:32 state.sql-shm -rw-r--r-- 1 ivan4th staff 0B Jun 28 00:06 state.sql-wal $ time sqlite3 ~/rmme/sm-data/state.sql vacuum real 67m48.211s user 57m13.518s sys 4m41.778s $ time sqlite3 ~/rmme/sm-data/state.sql "vacuum into '/tmp/state.sql'" real 2m27.813s user 0m43.039s sys 0m56.265s ``` As it can be seen, `VACUUM INTO` is about 27 times faster on this particular machine. Another problem described in #6069 is that on Linux systems, `$TMP` directory is utilized during normal `VACUUM`, and in case if it's a RAM disk the vacuuming process may run out of space.
- Loading branch information