Skip to content

Commit

Permalink
wip: First throw at adding sqlite as a storage backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Eragonfr committed Mar 5, 2024
1 parent 8faf728 commit 07ba38f
Show file tree
Hide file tree
Showing 24 changed files with 2,467 additions and 204 deletions.
373 changes: 203 additions & 170 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"syncstorage-mysql",
"syncstorage-settings",
"syncstorage-spanner",
"syncstorage-sqlite",
"tokenserver-auth",
"tokenserver-common",
"tokenserver-db",
Expand All @@ -23,6 +24,7 @@ authors = [
"Ben Bangert <[email protected]>",
"Phil Jenvey <[email protected]>",
"Mozilla Services Engineering <[email protected]>",
"Eragon <[email protected]>",
]
edition = "2021"
license = "MPL-2.0"
Expand Down
23 changes: 21 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ PATH_TO_GRPC_CERT = ../server-syncstorage/local/lib/python2.7/site-packages/grpc
SRC_ROOT = $(shell pwd)
PYTHON_SITE_PACKGES = $(shell $(SRC_ROOT)/venv/bin/python -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")

clippy_sqlite:
# Matches what's run in circleci
cargo clippy --workspace --all-targets --no-default-features --features=syncstorage-db/sqlite -- -D warnings

clippy_mysql:
# Matches what's run in circleci
cargo clippy --workspace --all-targets --no-default-features --features=syncstorage-db/mysql --features=py_verifier -- -D warnings
Expand Down Expand Up @@ -57,19 +61,34 @@ run_mysql: python
RUST_BACKTRACE=full \
cargo run --no-default-features --features=syncstorage-db/mysql --features=py_verifier -- --config config/local.toml

run_sqlite: python
PATH="./venv/bin:$(PATH)" \
# See https://github.com/PyO3/pyo3/issues/1741 for discussion re: why we need to set the
# below env var
PYTHONPATH=$(PYTHON_SITE_PACKGES) \
RUST_LOG=debug \
RUST_BACKTRACE=full \
cargo run --no-default-features --features=syncstorage-db/sqlite -- --config config/local.toml

run_spanner: python
GOOGLE_APPLICATION_CREDENTIALS=$(PATH_TO_SYNC_SPANNER_KEYS) \
GRPC_DEFAULT_SSL_ROOTS_FILE_PATH=$(PATH_TO_GRPC_CERT) \
# See https://github.com/PyO3/pyo3/issues/1741 for discussion re: why we need to set the
# below env var
PYTHONPATH=$(PYTHON_SITE_PACKGES) \
PATH="./venv/bin:$(PATH)" \
PATH="./venv/bin:$(PATH)" \
RUST_LOG=debug \
RUST_BACKTRACE=full \
cargo run --no-default-features --features=syncstorage-db/spanner --features=py_verifier -- --config config/local.toml

test:
test_mysql:
SYNC_SYNCSTORAGE__DATABASE_URL=mysql://sample_user:sample_password@localhost/syncstorage_rs \
SYNC_TOKENSERVER__DATABASE_URL=mysql://sample_user:sample_password@localhost/tokenserver_rs \
RUST_TEST_THREADS=1 \
cargo test --workspace

test_sqlite:
SYNC_SYNCSTORAGE__DATABASE_URL=:memory: \
SYNC_TOKENSERVER__DATABASE_URL=:memory: \
RUST_TEST_THREADS=1 \
cargo test --workspace
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Mozilla Sync Storage built with [Rust](https://rust-lang.org).
- [Local Setup](#local-setup)
- [MySQL](#mysql)
- [Spanner](#spanner)
- [Sqlite](#sqlite)
- [Running via Docker](#running-via-docker)
- [Connecting to Firefox](#connecting-to-firefox)
- [Logging](#logging)
Expand Down Expand Up @@ -179,6 +180,15 @@ To run an application server that points to the local Spanner emulator:
SYNC_SYNCSTORAGE__SPANNER_EMULATOR_HOST=localhost:9010 make run_spanner
```

### Sqlite

Setting up the server with sqlite only requires a path to the database file,
which will be created automatically:

`sqlite:path/syncdb.sqlite`

This requires at least sqlite v3.24.0 to be installed on the host system.

### Running via Docker

This requires access to [Google Cloud Rust (raw)](https://crates.io/crates/google-cloud-rust-raw/) crate. Please note that due to interdependencies, you will need to ensure that `grpcio` and `protobuf` match the version used by `google-cloud-rust-raw`.
Expand Down
11 changes: 8 additions & 3 deletions config/local.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ human_logs = 1

# Example Syncstorage settings:
# Example MySQL DSN:
syncstorage.database_url = "mysql://sample_user:sample_password@localhost/syncstorage_rs"
#syncstorage.database_url = "mysql://sample_user:sample_password@localhost/syncstorage_rs"
# Example Spanner DSN:
# database_url="spanner://projects/SAMPLE_GCP_PROJECT/instances/SAMPLE_SPANNER_INSTANCE/databases/SAMPLE_SPANNER_DB"
# Example SQLite DSN:
# database_url="sqlite://PATH_TO_FILE/FILE.sqlite"
syncstorage.database_url = ":memory:"

# enable quota limits
syncstorage.enable_quota = 0
# set the quota limit to 2GB.
Expand All @@ -16,7 +20,8 @@ syncstorage.enabled = true
syncstorage.limits.max_total_records = 1666 # See issues #298/#333

# Example Tokenserver settings:
tokenserver.database_url = "mysql://sample_user:sample_password@localhost/tokenserver_rs"
#tokenserver.database_url = "mysql://sample_user:sample_password@localhost/tokenserver_rs"
tokenserver.database_url = ":memory:"
tokenserver.enabled = true
tokenserver.fxa_email_domain = "api-accounts.stage.mozaws.net"
tokenserver.fxa_metrics_hash_secret = "INSERT_SECRET_KEY_HERE"
Expand All @@ -26,5 +31,5 @@ tokenserver.fxa_browserid_issuer = "https://api-accounts.stage.mozaws.net"
tokenserver.fxa_browserid_server_url = "https://verifier.stage.mozaws.net/v2"

# cors settings
# cors_allowed_origin = "localhost"
cors_allowed_origin = "localhost"
# cors_max_age = 86400
2 changes: 1 addition & 1 deletion syncserver-db-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ http.workspace=true
thiserror.workspace=true

deadpool = { git = "https://github.com/mozilla-services/deadpool", tag = "deadpool-v0.7.0" }
diesel = { version = "1.4", features = ["mysql", "r2d2"] }
diesel = { version = "1.4", features = ["mysql", "sqlite","r2d2"] }
diesel_migrations = { version = "1.4.0", features = ["mysql"] }
syncserver-common = { path = "../syncserver-common" }
26 changes: 13 additions & 13 deletions syncserver-db-common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use thiserror::Error;
/// Error specific to any MySQL database backend. These errors are not related to the syncstorage
/// or tokenserver application logic; rather, they are lower-level errors arising from diesel.
#[derive(Debug)]
pub struct MysqlError {
kind: MysqlErrorKind,
pub struct SqlError {
kind: SqlErrorKind,
pub status: StatusCode,
pub backtrace: Backtrace,
}

#[derive(Debug, Error)]
enum MysqlErrorKind {
enum SqlErrorKind {
#[error("A database error occurred: {}", _0)]
DieselQuery(#[from] diesel::result::Error),

Expand All @@ -29,8 +29,8 @@ enum MysqlErrorKind {
Migration(diesel_migrations::RunMigrationsError),
}

impl From<MysqlErrorKind> for MysqlError {
fn from(kind: MysqlErrorKind) -> Self {
impl From<SqlErrorKind> for SqlError {
fn from(kind: SqlErrorKind) -> Self {
Self {
kind,
status: StatusCode::INTERNAL_SERVER_ERROR,
Expand All @@ -39,21 +39,21 @@ impl From<MysqlErrorKind> for MysqlError {
}
}

impl_fmt_display!(MysqlError, MysqlErrorKind);
impl_fmt_display!(SqlError, SqlErrorKind);

from_error!(
diesel::result::Error,
MysqlError,
MysqlErrorKind::DieselQuery
SqlError,
SqlErrorKind::DieselQuery
);
from_error!(
diesel::result::ConnectionError,
MysqlError,
MysqlErrorKind::DieselConnection
SqlError,
SqlErrorKind::DieselConnection
);
from_error!(diesel::r2d2::PoolError, MysqlError, MysqlErrorKind::Pool);
from_error!(diesel::r2d2::PoolError, SqlError, SqlErrorKind::Pool);
from_error!(
diesel_migrations::RunMigrationsError,
MysqlError,
MysqlErrorKind::Migration
SqlError,
SqlErrorKind::Migration
);
7 changes: 7 additions & 0 deletions syncserver-db-common/src/test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use diesel::{
mysql::MysqlConnection,
sqlite::SqliteConnection,
r2d2::{CustomizeConnection, Error as PoolError},
Connection,
};
Expand All @@ -12,3 +13,9 @@ impl CustomizeConnection<MysqlConnection, PoolError> for TestTransactionCustomiz
conn.begin_test_transaction().map_err(PoolError::QueryError)
}
}

impl CustomizeConnection<SqliteConnection, PoolError> for TestTransactionCustomizer {
fn on_acquire(&self, conn: &mut SqliteConnection) -> Result<(), PoolError> {
conn.begin_test_transaction().map_err(PoolError::QueryError)
}
}
4 changes: 3 additions & 1 deletion syncstorage-db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ syncserver-db-common = { path = "../syncserver-db-common" }
syncserver-settings = { path = "../syncserver-settings" }
syncstorage-db-common = { path = "../syncstorage-db-common" }
syncstorage-mysql = { path = "../syncstorage-mysql", optional = true }
syncstorage-settings = { path = "../syncstorage-settings" }
syncstorage-sqlite = { path = "../syncstorage-sqlite/", optional = true}
syncstorage-spanner = { path = "../syncstorage-spanner", optional = true }
syncstorage-settings = { path = "../syncstorage-settings" }
tokio = { workspace = true, features = ["macros", "sync"] }

[features]
mysql = ['syncstorage-mysql']
spanner = ['syncstorage-spanner']
sqlite = ['syncstorage-sqlite']
11 changes: 9 additions & 2 deletions syncstorage-db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ pub use syncstorage_mysql::DbError;
#[cfg(feature = "mysql")]
pub type DbImpl = syncstorage_mysql::MysqlDb;

#[cfg(feature = "sqlite")]
pub type DbPoolImpl = syncstorage_sqlite::SqliteDbPool;
#[cfg(feature = "sqlite")]
pub use syncstorage_sqlite::DbError;
#[cfg(feature = "sqlite")]
pub type DbImpl = syncstorage_sqlite::SqliteDb;

#[cfg(feature = "spanner")]
pub type DbPoolImpl = syncstorage_spanner::SpannerDbPool;
#[cfg(feature = "spanner")]
Expand All @@ -31,8 +38,8 @@ pub use syncstorage_db_common::{
Db, DbPool, Sorting, UserIdentifier,
};

#[cfg(all(feature = "mysql", feature = "spanner"))]
#[cfg(all(feature = "mysql", feature = "spanner", feature = "sqlite"))]
compile_error!("only one of the \"mysql\" and \"spanner\" features can be enabled at a time");

#[cfg(not(any(feature = "mysql", feature = "spanner")))]
#[cfg(not(any(feature = "mysql", feature = "spanner", feature = "sqlite")))]
compile_error!("exactly one of the \"mysql\" and \"spanner\" features must be enabled");
12 changes: 6 additions & 6 deletions syncstorage-mysql/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt;
use backtrace::Backtrace;
use http::StatusCode;
use syncserver_common::{from_error, impl_fmt_display, InternalError, ReportableError};
use syncserver_db_common::error::MysqlError;
use syncserver_db_common::error::SqlError;
use syncstorage_db_common::error::{DbErrorIntrospect, SyncstorageDbError};
use thiserror::Error;

Expand Down Expand Up @@ -49,7 +49,7 @@ enum DbErrorKind {
Common(SyncstorageDbError),

#[error("{}", _0)]
Mysql(MysqlError),
Mysql(SqlError),
}

impl From<DbErrorKind> for DbError {
Expand Down Expand Up @@ -124,24 +124,24 @@ from_error!(SyncstorageDbError, DbError, DbErrorKind::Common);
from_error!(
diesel::result::Error,
DbError,
|error: diesel::result::Error| DbError::from(DbErrorKind::Mysql(MysqlError::from(error)))
|error: diesel::result::Error| DbError::from(DbErrorKind::Mysql(SqlError::from(error)))
);
from_error!(
diesel::result::ConnectionError,
DbError,
|error: diesel::result::ConnectionError| DbError::from(DbErrorKind::Mysql(MysqlError::from(
|error: diesel::result::ConnectionError| DbError::from(DbErrorKind::Mysql(SqlError::from(
error
)))
);
from_error!(
diesel::r2d2::PoolError,
DbError,
|error: diesel::r2d2::PoolError| DbError::from(DbErrorKind::Mysql(MysqlError::from(error)))
|error: diesel::r2d2::PoolError| DbError::from(DbErrorKind::Mysql(SqlError::from(error)))
);
from_error!(
diesel_migrations::RunMigrationsError,
DbError,
|error: diesel_migrations::RunMigrationsError| DbError::from(DbErrorKind::Mysql(
MysqlError::from(error)
SqlError::from(error)
))
);
28 changes: 28 additions & 0 deletions syncstorage-sqlite/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "syncstorage-sqlite"
version.workspace=true
license.workspace=true
authors.workspace=true
edition.workspace=true

[dependencies]
backtrace.workspace=true
base64.workspace=true
futures.workspace=true
http.workspace=true
slog-scope.workspace=true

async-trait = "0.1.40"
diesel = { version = "1.4", features = ["sqlite", "r2d2"] }
diesel_logger = "0.1.1"
diesel_migrations = { version = "1.4.0", features = ["sqlite"] }
syncserver-common = { path = "../syncserver-common" }
syncserver-db-common = { path = "../syncserver-db-common" }
syncstorage-db-common = { path = "../syncstorage-db-common" }
syncstorage-settings = { path = "../syncstorage-settings" }
thiserror = "1.0.26"
url = "2.1"

[dev-dependencies]
env_logger.workspace=true
syncserver-settings = { path = "../syncserver-settings" }
8 changes: 8 additions & 0 deletions syncstorage-sqlite/migrations/2024-01-19-131212_Init/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- DROP INDEX IF EXISTS `bso_expiry_idx`;
-- DROP INDEX IF EXISTS `bso_usr_col_mod_idx`;

-- DROP TABLE IF EXISTS `bso`;
-- DROP TABLE IF EXISTS `collections`;
-- DROP TABLE IF EXISTS `user_collections`;
-- DROP TABLE IF EXISTS `batch_uploads`;
-- DROP TABLE IF EXISTS `batch_upload_items`;
Loading

0 comments on commit 07ba38f

Please sign in to comment.