If you’re a package maintainer, I’m especially interested in your input here. Open to all comments from anyone, but I’d like to avoid breaking systems and setups
Atuin does not run a daemon to sync. There are two kinds of sync that happen
- In the foreground, when someone runs
atuin sync
- In the background. This happens after a command runs, and is forked so as to not block the shell. It only happens once a certain duration has elapsed since the last sync
This was very simple to implement, and didn’t require that users setup or maintain any sort of service. At the time, I felt this best - Atuin’s install could be super simple, and not need any maintenance.
However… as time has gone on, this approach has had a whole bunch of downsides.
-
If the user wishes to sync after EVERY command, and there’s some sort of delay, we can end up with multiple syncs happening concurrently. This can even happen if the sync interval is low, there are issues with the filesystem, IO pressure, whatever. The workarounds for this kind of problem, realistically, are a hack.
-
Even if the user isn’t syncing, writing to SQLite before and after every command can cause problems on systems that are a bit unusual, or under high load.
-
Atuin runs some tasks on a schedule (check for updates, sync if needed, etc). It would also be good to be able to run periodic maintenance of the database, potentially pruning it, that kind of thing. Doing so at the moment would require adding more to the “post command” tasks, and writing it in a way that accounts for potentially multiple instances running at once. Or somehow modifying cron, which we cannot rely on systems having.
-
does this smell racey to anyone? it’s starting to.
I won’t carry on listing issues, but we do semi often have problems that come up that just would not exist if Atuin was running as a persistent background process, and most expensive compute was not tied to a shell-driven lifecycle.
The daemon
I’d like to move Atuin to be a daemon. We can eliminate a whole class of issues this way, and I think it’s the right direction for the future.
We could use crates like daemonize, which will handle forking/PID file management/etc. We probably wouldn’t need to do much in the way of system setup, and could just have something like atuin daemon
start the background process.
I’d rather not though, as afaik all systems we run on have some sort of system for managing background services. Unless this turns out to be too much of a nightmare.
- Homebrew services on mac
- systemd on most linux systems
- runit on void (cc @tranzystorekk, not sure what’s needed there)
- openrc on alpine
- idk about Windows, cc @YummyOreo for input there
The lifecycle would then be much simpler. The background process can handle actually writing to sqlite, syncing when required, all that. The foreground becomes basic - just writing over some socket to the background process. We can eliminate the forking after each command.
The TUI can be unaffected, as SQLite handles multiple readers without issue