-
Notifications
You must be signed in to change notification settings - Fork 43
Add SQLite backend #12
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
base: master
Are you sure you want to change the base?
Conversation
afa34dc
to
287aed7
Compare
Another advantage of the SQLite backend is we can write smaller unit tests for the models that start with a fresh in-memory database. We'll still want the same tests to run on the Postgres backend as part of CI, but this will help speed up development, hopefully... |
Cargo.toml
Outdated
[features] | ||
default = ["postgres"] | ||
postgres = [] | ||
sqlite = [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be
postgres = ["diesel/postgres", "diesel_infer_schema/postgres"]
sqlite = ["diesel/sqlite", "diesel_infer_schema/sqlite"]
Then remove those features from diesel and diesel_infer_schema below, and diesel will be compiled with or without the database engines depending on how you want to compile rustodon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the tip! I've applied that in 4ac4311.
You probably don't want me accidentally committing IDE-specific stuff or databases in here.
Compatibility notes: 1. TIMESTAMP WITH TIME ZONE is not supported by SQLite. I opted to use DATETIME instead. This is probably not a big deal if the application reads and writes times in UTC (and only converts to/from a local time zone in the UI). 2. In SQLite, BIGINT and INTEGER have the same storage class. However: a. Diesel won't allow i64 fields to be used with a column having type "INTEGER" b. SQLite will interpret a column of type INTEGER PRIMARY KEY NOT NULL to be a rowid alias, but will not do so for BIGINT PRIMARY KEY NOT NULL The short version is that we're currently using BIGINT in tables, but we lose autoincrement. This is not a problem if IDs are assigned by the application (e.g. with the Snowflake algorithm), but if we're expecting autoincrement (and it looks like the Postgres schema is) then we'll have to find some way around (a).
This should be made generic at some point. Soon.
rustfmt was applied to master; doing it here (with the same rustfmt.toml) will hopefully avoid the nastier merge conflicts.
We can use Cargo's feature dependencies feature (ahem) to reduce duplication in the diesel and diesel_infer_schema dependency specs.
4ac4311
to
ac0e5c3
Compare
This switch is permanent until the journal mode is changed again; see https://sqlite.org/wal.html#persistence_of_wal_mode https://web.archive.org/web/20180114031518/https://sqlite.org/wal.html#persistence_of_wal_mode Another application reading the database *could* change the journal mode back to DELETE, which would have performance implications (at best); however, I think we can probably get away with saying "well don't do that". A more sophisticated solution might switch to WAL mode when a connection is first acquired.
re: diesel-rs/diesel#1116 It looks like we will have to do manual override. |
@yipdw i really like the idea you posted in discord:
|
It could be; I'm not yet sure if it'll work or what the code would end up looking like. One thing that will happen is that we'd assume the burden of remembering that ID fields (both autoincremented PK and FK) must be |
@yipdw i had another thought: what if we forego 🤷♀️ I initially pushed for using |
@barzamin I think sticking with 64-bit IDs is fine in principle. Also, if e.g. you eventually want to switch away from autoincrementation to (say) a Snowflake-like ID generation algorithm, then I'll give the |
Just to be clear, the issue with SQLite is an unfortunate clash of circumstances:
|
I think we should probably do snowflake IDs from the get-go. |
Yeah, if we're going to do Snowflake-ish IDs, it is much easier to do that starting out. I guess that'd be a separate issue if @barzamin is down with the idea. |
I am very down with snowflakes. Opened #17 to track this. |
@yipdw yes, that would be much appreciated! |
SQLite hacks
database/src/models/account.rs
Outdated
diesel::insert_into(accounts) | ||
.values(&self) | ||
.get_result(&**conn) | ||
let inserted = diesel::insert_into(accounts).values(&self).execute(&**conn); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I could do this all over again, I would have done it with a lot more ?
|
||
set -euo pipefail | ||
|
||
exec scripts/run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shim script is here mainly to allow cargo watch
to continue to do the right thing while allowing us to infer the right build features and set them at runtime.
fi | ||
|
||
if [ ! -f "database/src/schema.rs" ]; then | ||
echo "Database schema does not exist, run scripts/setup before attempting to run the tests." >&2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This warning is correct and scripts/setup
should likely continue to do the schema generation, but in retrospect build.rs
could also just do the schema generation as well, and avoid having the user forced to take action.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Investigated this further, and because db stuff is broken out into its own crate, its own build.rs
would need to run first in order to get the crate to build. Unfortunately, SQLite paths would likely be qualified in the root crate, not the dependent crate, so any DATABASE_URL
that exists that would allow the schema generation to proceed would need to be requalified within the context of the database
subdirectory, at which point this would all start to feel like it's doing too much work / assuming too much on what's inside that env var.
Just leaving things as is for now.
database/src/schema.rs
Outdated
@@ -1 +0,0 @@ | |||
infer_schema!("dotenv:DATABASE_URL"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This removal ( and replacement via manual schema generation ) was due to SQLite throwing several errors using infer_schema
in the tests; since Diesel seems to be deprecating infer_schema
anyways, it seemed a positive direction.
Tests passing again, there were some issues w/ things on master and the CI setup that were causing tests to fail. |
This is no longer relevant -- we're using i64s generated via flaken. |
There was an out of band discussion on this branch regarding handling Primary folks in that discussion were @yipdw and @nightpool . Comment left here just to note that if that behavior changes in |
Whew, I need to look at this again -- sorry for falling off the world. It looks like all we need now is an |
@@ -1,4 +1,5 @@ | |||
use db; | |||
use db::datetime::Rfc339able; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we should add another 3 to this module name. db::datetime::Rfc3339able
strikes me as something slightly more recognisable and usable in a Datetime context than "MLTNET - A "MULTI-TELNET" SUBSYSTEM FOR TENEX"
@@ -1,3 +1,4 @@ | |||
[print_schema] | |||
file = "database/src/schema.rs" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if this is the schema file, then how come we're deleting it, i'm a bit confused…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
database/src/schema.rs
is generated by diesel print-schema
inside scripts/setup
. (This branch adds some scripts to setup and run the server; if/when this is merged, those scripts will probably become part of the standard setup/run procedure.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iirc it's diesel best practice to check schema.rs into source control (so you don't have to print-schema when deploying the app For Realz). don't quote me on that though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
although that gets done as part of migrations so extremely 🤷♀️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that makes sense in situations where you're pushing source code to a bunch of app servers (which is the classic Web application deployment model), but I don't think it matters much when you're building a binary and deploying that to a bunch of app servers or (more common in the SQLite case) building a binary and installing it on many machines, since you won't be recompiling on the destination machine in either case.
That said, if there arises some benefit to checking in schema.rs
, it doesn't seem like it would be a difficult change to implement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, we have it checked in right now and i think that's the only reason to keep it around atm. Pulling it out is totally fine 💜
pub mod validators; | ||
|
||
#[cfg(all(feature = "sqlite", feature = "postgres"))] | ||
compile_error!("sqlite and postgres features cannot be simultaneously selected"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i wish there was an easier (and prettier) way to do this… after all diesel doesn't use feature flags and can have all of its databases enabled at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diesel (or at least the version of Diesel we're using) has both postgres
and sqlite
feature flags. The postgres
and sqlite
feature flags on the database crate toggle feature flags on diesel
and diesel_infer_schema
; we use them here as well.
I think there is a case to build in support for multiple servers for e.g. pre-built Rustodon binary distributions, but that seems to be some way off. In the meantime, switching using types seemed like the mechanism that required the fewest code changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i opened an issue for this in diesel diesel-rs/diesel#1853
I'm interested in using SQLite as a database backend for rustodon. Truly database-agnostic code is difficult to do, and it might end up being impractical, but:
Tasks:
make sure concurrent writers workconcurrent write isn't allowed, but WAL should at least result in fewer exclusive locksmigrations_sqlite
in sync withmigrations
i64
IDs working withINTEGER PRIMARY KEY NOT NULL
(should be possible, becauseINTEGER
columns in SQLite can hold signed 64-bit integers. this might end up as a Diesel patch if I can prove it's a Diesel concern)updated_at
trigger working on tables that use it