Skip to content

Commit

Permalink
Persist remote tabs to a sql database.
Browse files Browse the repository at this point in the history
  • Loading branch information
mhammond committed Feb 28, 2022
1 parent 3089294 commit 14370bb
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 48 deletions.
5 changes: 5 additions & 0 deletions CHANGES_UNRELEASED.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,8 @@ Use the template below to make assigning a version number during the release cut
### What's Changed
- Updated interruption handling and added support for shutdown-mode which interrupts all operations.

## Tabs
### ⚠️ Breaking Changes ⚠️

- The tabs component's constructor now requires the path to the database file where remote tabs will be persisted to.
- Requesting remote tabs before the first sync will now return the tabs in this database, so may be "stale".
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 13 additions & 7 deletions components/tabs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,26 @@ license = "MPL-2.0"
exclude = ["/android", "/ios"]

[dependencies]
sync15 = { path = "../sync15" }
anyhow = "1.0"
error-support = { path = "../support/error" }
interrupt-support = { path = "../support/interrupt" }
lazy_static = "1.4"
log = "0.4"
rusqlite = { version = "0.24.2", features = ["bundled", "unlock_notify"] }
serde = "1"
serde_derive = "1"
serde_json = "1"
lazy_static = "1.4"
log = "0.4"
url = "2.2"
error-support = { path = "../support/error" }
interrupt-support = { path = "../support/interrupt" }
sql-support = { path = "../support/sql" }
sync-guid = { path = "../support/guid", features = ["random"] }
sync15 = { path = "../sync15" }
thiserror = "1.0"
anyhow = "1.0"
uniffi = "^0.17"
uniffi_macros = "^0.17"
url = "2.2"

[dev-dependencies]
tempfile = "3.1"
env_logger = { version = "0.8.0", default-features = false, features = ["termcolor", "atty", "humantime"] }

[build-dependencies]
uniffi_build = { version = "^0.17", features = [ "builtin-bindgen" ]}
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE)
class RemoteTabsTest {
@Rule
@JvmField
val dbFolder = TemporaryFolder()

@Before
fun init() {
Megazord.init()
}

protected fun getTestStore(): TabsStore {
return TabsStore()
return TabsStore(path = dbFolder.newFile().absolutePath)
}

@Test
Expand Down
8 changes: 8 additions & 0 deletions components/tabs/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ pub enum ErrorKind {

#[error("Error parsing URL: {0}")]
UrlParseError(#[from] url::ParseError),

#[error("Error executing SQL: {0}")]
SqlError(#[from] rusqlite::Error),

#[error("Error opening database: {0}")]
OpenDatabaseError(#[from] sql_support::open_database::Error),
}

error_support::define_error! {
ErrorKind {
(SyncAdapterError, sync15::Error),
(JsonError, serde_json::Error),
(UrlParseError, url::ParseError),
(SqlError, rusqlite::Error),
(OpenDatabaseError, sql_support::open_database::Error),
}
}
1 change: 1 addition & 0 deletions components/tabs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#[macro_use]
pub mod error;
mod schema;
mod storage;
mod sync;

Expand Down
70 changes: 70 additions & 0 deletions components/tabs/src/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Tabs is a bit special - it's a trivial SQL schema and is only used as a persistent
// cache, and the semantics of the "tabs" collection means there's no need for
// syncChangeCounter/syncStatus nor a mirror etc.

use rusqlite::{Connection, Transaction};
use sql_support::open_database::{
ConnectionInitializer as MigrationLogic, Error as MigrationError, Result as MigrationResult,
};

// The payload is json and this module doesn't need to deserialize, so we just
// store each "payload" as a row.
// On each Sync we delete all local rows re-populate them with every record on
// the server. When we read the DB, we also read every single record.
// So we have no primary keys, no foreign keys, and really completely waste the
// fact we are using sql.
const CREATE_SCHEMA_SQL: &str = "
CREATE TABLE IF NOT EXISTS tabs (
payload TEXT NOT NULL
);
";

pub struct TabsMigrationLogin;

impl MigrationLogic for TabsMigrationLogin {
const NAME: &'static str = "tabs storage db";
const END_VERSION: u32 = 1;

fn prepare(&self, conn: &Connection) -> MigrationResult<()> {
let initial_pragmas = "
-- We don't care about temp tables being persisted to disk.
PRAGMA temp_store = 2;
-- we unconditionally want write-ahead-logging mode.
PRAGMA journal_mode=WAL;
-- foreign keys seem worth enforcing (and again, we don't care in practice)
PRAGMA foreign_keys = ON;
";
conn.execute_batch(initial_pragmas)?;
// This is where we'd define our sql functions if we had any!
conn.set_prepared_statement_cache_capacity(128);
Ok(())
}

fn init(&self, db: &Transaction<'_>) -> MigrationResult<()> {
log::debug!("Creating schema");
db.execute_batch(CREATE_SCHEMA_SQL)?;
Ok(())
}

fn upgrade_from(&self, _db: &Transaction<'_>, version: u32) -> MigrationResult<()> {
Err(MigrationError::IncompatibleVersion(version))
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::storage::TabsStorage;

#[test]
fn test_create_schema_twice() {
let mut db = TabsStorage::new_with_mem_path("test");
let conn = db.open_or_create().unwrap();
conn.execute_batch(CREATE_SCHEMA_SQL)
.expect("should allow running twice");
}
}
Loading

0 comments on commit 14370bb

Please sign in to comment.