-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make database operations 30x faster by using SQLite #28
base: master
Are you sure you want to change the base?
Conversation
Hey @kiler129 ! 🤓 I have been thinking about this for a bit and it really looks like a great improvement to the script. I have two thoughts we may want to discuss: 1. Should we keep both the old/current as well as the new script?Since this script is a pretty big change to "how things work", as well as what a potential user has to know about that to be able to work with it (reading txt file vs sqlite db, etc.), I am wondering if we should keep the old script available for people with a small usecase, and add this version for bigger usecases. I think maintaining both versions shouldn't be that big of a deal (at least if you can keep an eye on it as well 😅 ). 2. Should we consider "moving" to a "real" programming language to implement this script?Looking at how this version works, I am wondering if a shift to a "real" programming language that works on most systems, like f.ex. golang (for performance) or python (for convenience and accessibility) would be the "better" path for future optimization? The single reason I initially used Bash to implement this script is because it was a one-time operation for me, which I will probably never do again. Bash is hard though, especially optimizing, debugging and testing it. Utilizing a programming language, accessing a database, as well as providing the CLI tools to work with it would be much easier. Note: Following this route does not necessitate "trashing" this PR, it might simply be a third version, or an evolution of this PR. |
Don't we all start with "it will be just a small fix"? ...and that's just the beginning
I was battling with this as well: should we make this a parameter... or maybe support both based on existence of the current "resume" file? What about the default? (should we feature-check or....) This is exactly why I put this PR as a draft to see what's the best compromise for that. I think if the intention would be to keep the current database a smart way would be to:
The good: easy of maintenance The bad: portability However, I started digging deeper in a hope we can MAYBE at least justify this by not using The ugly: real world has no alternatives Off-topic: |
I really don't want to over engineer this. The chance of somebody using the initial script and then wanting to migrate to the SQLite script is minuscule. The fact that a migration is already possible with your PR is more than good enough. I would simply propose to have two separate script files in this repo, one with a text file as DB, and one with SQLite. That way a user can decide for themselves which version is right for them, and both versions are completely independent. Also, if you intend to further refine this script it might actually be better to create your own repository, f.ex. as a fork. I really don't want to make this script any more complex than it already is. If it needs to support more usecases, I would rather switch to a language like golang and provide cross platform executables. For the change of using sqlite instead of a text file, I would be fine with having a separate script in this repo, but if you intend to support running this script on changing data (which is currently explicitly stated in the README as not supported, not even data that is actively read ) I would not want to support such things in a simple bash script, because I would want to actually feel comfortable maintaining it and ensuring it works as intended 😅 I am mainly an android programmer, not a sysadmin, so my linux, zfs, and bash expertise is quite limited.
Even using a golang program for most of it and executing a bash command from within for things that are not supported natively would be preferred by me. I also have to say that, as I mentioned initially, I don't really have a usecase for this right now and probably not for the foreseeable future, meaning its not likely I will invest a lot of time into this. |
This breaks with filenames that have apostrophes, but I much prefer this approach to the text file |
So one way to fix it is by doing Also, another bug I noticed is that since the rebalance function is called in its own scope rebalance_cache_db string will be empty if you finish before the interval and its called outside the function loop. One way to avoid it is to use lastpipe according to FAQ https://mywiki.wooledge.org/BashFAQ/024 |
I also highly recommend using the go language since I already had to use it since the problem I was having was the long wait times for it to find all the file counts on my system. I implemented using locar to get my file count faster since I have the ZFS system on a mirror array. |
Is it really a good idea to use sqlite for this? Isn't sqlite well known to behave in a weird way on zfs currently? |
Preface
Rebalancing is a lengthy process and thus ensuring it can be resumed is rather important. Currently, the code implements
a simple database-like textual file. When script is interrupted and then subsequently restarted, the rebalancing process
can be restarted more-or-less from where it was interrupted.
The problem
Current solution is great conceptually. However, the implementation suffers from very poor performance due to multiple
factors:
The result of this is a very poor performance during both rebalance and when script needs to be resumed. In the case of
my dataset with ~1M files resume from 500k point took close to 12 hours(!). The performance problems are magnified with
small files being present
Solutions evaluation
The first optimization I attempted was limiting re-reads for counting. However, this still proven very limiting. The
next idea I tested was using
bash
associative arrays, implementing an in-memory cache persisted occasionally to thedisk. Unfortunately, with 500k+ files the memory usage started to creep into 500-600-800MB range, with more and more
being ostensibly leaked. This shows clearly that bash array were never designed for such a dataset.
I scraped the idea and came back to the drawing board. After ad-hoc testing, I implemented a prototype based on SQLite
database, that is stored in a dedicated file as well. The time for read-save operations were cut by orders of magnitude,
despite still launching at least 2 processes for every file. As a next step I implemented a delayed database flush, with
60s timeout, further cutting the db operations time.
Potential impact for users
The change carries some impact to systems utilizing this project.
sqlite3
tool offers an easy interfaceREADME.md
contains a simulated transaction, so that unfamiliar users can easily interact with the dbsqlite3
sqlite3
is already present on systems that are most likely the target of this scriptsqlite3
binary on TrueNAS SCALE, TrueNAS CORE, macOS 13, as well as Ubuntusqlite3
by default, but package is in the default repoconvert-legacy-db.sh
that handles the migrationunintentionally
Testing methodology
cp rebalance_db.txt_copy rebalance_db.txt ; ./convert-legacy-db.sh
(this takes ~35 seconds)cp rebalance_db.txt_copy rebalance_db.txt
time ./zfs-inplace-rebalancing.sh --checksum false --passes 2 /mnt/testdata > rebalance.log
Test results
.txt
-based database: 29h 14minIn other words, it's almost a 30x speed-up :)
There are a few smaller low-hanging fruits for optimization, but I'm tackling one thing at a time.
WDYT?
...about this PR and the idea itself? Marking as
draft
as of right now as I didn't have a chance to test on FreeBSDyet (and there are minor bugs with special characters ;)).