Backed out changesets b05343a5b533 and 57b2cda7e0db (bug 1818349) for causing bug 1819723.

This commit is contained in:
Ryan VanderMeulen 2023-03-01 17:52:01 -05:00
parent f7edb0b474
commit 76b958d3b3
24 changed files with 304 additions and 408 deletions

View File

@ -15,6 +15,11 @@ git = "https://github.com/FirefoxGraphics/wpf-gpu-raster"
rev = "a6514854d4518b02f2805719ff6cd74dae7dfde6"
replace-with = "vendored-sources"
[source."https://github.com/bendk/application-services"]
git = "https://github.com/bendk/application-services"
rev = "ecb35df5fc40357c49922f90e86bf4147fa52953"
replace-with = "vendored-sources"
[source."https://github.com/chris-zen/coremidi.git"]
git = "https://github.com/chris-zen/coremidi.git"
rev = "fc68464b5445caf111e41f643a2e69ccce0b4f83"
@ -75,11 +80,6 @@ git = "https://github.com/mozilla-spidermonkey/jsparagus"
rev = "688a6574cf830b4a3adaf9c5d41efc1f7dd4017f"
replace-with = "vendored-sources"
[source."https://github.com/mozilla/application-services"]
git = "https://github.com/mozilla/application-services"
rev = "5433b3dc9ac152a0da29ed6642df839d79515a59"
replace-with = "vendored-sources"
[source."https://github.com/mozilla/audioipc"]
git = "https://github.com/mozilla/audioipc"
rev = "fb7a2b12ced3b43e6a268621989c6191d1ed7e39"

20
Cargo.lock generated
View File

@ -1595,7 +1595,7 @@ dependencies = [
[[package]]
name = "error-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"error-support-macros",
"lazy_static",
@ -1607,7 +1607,7 @@ dependencies = [
[[package]]
name = "error-support-macros"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"proc-macro2",
"quote",
@ -2715,7 +2715,7 @@ dependencies = [
[[package]]
name = "interrupt-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"lazy_static",
"parking_lot 0.12.999",
@ -3837,7 +3837,7 @@ dependencies = [
[[package]]
name = "nss_build_common"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
[[package]]
name = "nsstring"
@ -5045,7 +5045,7 @@ dependencies = [
[[package]]
name = "sql-support"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"ffi-support",
"interrupt-support",
@ -5227,7 +5227,7 @@ dependencies = [
[[package]]
name = "sync-guid"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"base64",
"rand 0.8.5",
@ -5238,7 +5238,7 @@ dependencies = [
[[package]]
name = "sync15"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"anyhow",
"error-support",
@ -5268,7 +5268,7 @@ dependencies = [
[[package]]
name = "tabs"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"anyhow",
"error-support",
@ -6042,7 +6042,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "viaduct"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"ffi-support",
"log",
@ -6199,7 +6199,7 @@ dependencies = [
[[package]]
name = "webext-storage"
version = "0.1.0"
source = "git+https://github.com/mozilla/application-services?rev=5433b3dc9ac152a0da29ed6642df839d79515a59#5433b3dc9ac152a0da29ed6642df839d79515a59"
source = "git+https://github.com/bendk/application-services?rev=ecb35df5fc40357c49922f90e86bf4147fa52953#ecb35df5fc40357c49922f90e86bf4147fa52953"
dependencies = [
"error-support",
"ffi-support",

View File

@ -170,12 +170,12 @@ warp = { git = "https://github.com/glandium/warp", rev = "4af45fae95bc98b0eba1ef
cssparser = { git = "https://github.com/servo/rust-cssparser", rev = "b196a164dcbb317016d4aa6c58c13147e6045ebb" }
# application-services overrides to make updating them all simpler.
interrupt-support = { git = "https://github.com/mozilla/application-services", rev = "5433b3dc9ac152a0da29ed6642df839d79515a59" }
sql-support = { git = "https://github.com/mozilla/application-services", rev = "5433b3dc9ac152a0da29ed6642df839d79515a59" }
sync15 = { git = "https://github.com/mozilla/application-services", rev = "5433b3dc9ac152a0da29ed6642df839d79515a59" }
tabs = { git = "https://github.com/mozilla/application-services", rev = "5433b3dc9ac152a0da29ed6642df839d79515a59" }
viaduct = { git = "https://github.com/mozilla/application-services", rev = "5433b3dc9ac152a0da29ed6642df839d79515a59" }
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "5433b3dc9ac152a0da29ed6642df839d79515a59" }
interrupt-support = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
sql-support = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
sync15 = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
tabs = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
viaduct = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
webext-storage = { git = "https://github.com/bendk/application-services", rev = "ecb35df5fc40357c49922f90e86bf4147fa52953" }
# Patch mio 0.6 to use winapi 0.3 and miow 0.3, getting rid of winapi 0.2.
# There is not going to be new version of mio 0.6, mio now being >= 0.7.11.

View File

@ -79,8 +79,6 @@ class TPSTestRunner(object):
# hrm - not sure what the release/beta channels will do?
"xpinstall.signatures.required": False,
"services.sync.testing.tps": True,
# removed data: schema restriction for easier testing of tabs
"services.sync.engine.tabs.filteredSchemes": "about|resource|chrome|file|blob|moz-extension",
"engine.bookmarks.repair.enabled": False,
"extensions.experiments.enabled": True,
"webextensions.storage.sync.kinto": False,

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"2a41a998c288fd049dd4dd62bc20e3ad13ded43642c27880dd5af0260bb72865","src/argument.rs":"bb97e801ce2c80b878328b15783678b913c2f34cf6f26a60d894c8da6b4e47aa","src/lib.rs":"8dd5b6225791730881a3500c3013c48678879430d859d0b92ac9dad4c42b04e0"},"package":null}
{"files":{"Cargo.toml":"2a41a998c288fd049dd4dd62bc20e3ad13ded43642c27880dd5af0260bb72865","src/argument.rs":"1e4ec4140c536d2feea30eb1e4eef3a6e24533192108e91610ee5c2e75eb5e75","src/lib.rs":"fbd9c048ac86c16ef388fbb9a3c40f60d3aecab41a902740c81b3df6ee6631fe"},"package":null}

View File

@ -2,20 +2,16 @@
* 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/. */
use syn::spanned::Spanned;
const ERR_MSG: &str = "Expected #[handle_error(path::to::Error)]";
/// Returns the path to the type of the "internal" error.
pub(crate) fn validate(arguments: &syn::AttributeArgs) -> syn::Result<&syn::Path> {
if arguments.len() != 1 {
return Err(syn::Error::new(proc_macro2::Span::call_site(), ERR_MSG));
}
let nested_meta = arguments.iter().next().unwrap();
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = nested_meta {
Ok(path)
} else {
Err(syn::Error::new(nested_meta.span(), ERR_MSG))
pub(crate) fn validate(arguments: &syn::AttributeArgs) -> syn::Result<()> {
// For now we validate that there are no arguments.
// if we want to improve the macro to support attributes, for example,
// if we'd like to pass in the return type of the body we can modify this
// logic to parse and return the type.
if !arguments.is_empty() {
return Err(syn::Error::new(
proc_macro2::Span::call_site(),
"Expected #[handle_error] with no arguments",
));
}
Ok(())
}

View File

@ -48,11 +48,9 @@ mod argument;
/// }
/// }
///
/// // The `handle_error` macro maps from the error supplied in the mandatory argument
/// // (ie, `Error` in this example) to the error returned by the function (`ExternalError`
/// // in this example)
/// #[handle_error(Error)]
/// #[handle_error]
/// fn do_something() -> std::result::Result<String, ExternalError> {
/// // The `handle_error` macro maps from `Error` to `ExternalError`
/// Err(Error{})
/// }
///
@ -74,13 +72,18 @@ fn impl_handle_error(
arguments: &syn::AttributeArgs,
) -> syn::Result<proc_macro2::TokenStream> {
if let syn::Item::Fn(item_fn) = input {
let err_path = argument::validate(arguments)?;
argument::validate(arguments)?;
let original_body = &item_fn.block;
let mut new_fn = item_fn.clone();
new_fn.block = parse_quote! {
{
(|| -> ::std::result::Result<_, #err_path> {
// Note: the `Result` here is a smell
// because the macro is **assuming** a `Result` exists
// that reflects the return value of the block
// An improvement would include the error of the `original_block`
// as an attribute to the macro itself.
(|| -> Result<_> {
#original_body
})().map_err(::error_support::convert_log_report_error)
}
@ -92,7 +95,7 @@ fn impl_handle_error(
} else {
Err(syn::Error::new(
input.span(),
"#[handle_error(..)] can only be used on functions",
"#[handle_error] can only be used on functions",
))
}
}

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"1f11acaa90a112979205b4c7af9ba0c015afab5f3141dd082d58c862c84490e3","README.md":"6d4ff5b079ac5340d18fa127f583e7ad793c5a2328b8ecd12c3fc723939804f2","src/bso/content.rs":"d2d650f4932e9a7068a25dd7df0085b92cd8976a0635320e6ae306d5a425075c","src/bso/crypto.rs":"27602dcccb37d3a55620ee4e16b705da455d49af575de115c7c79c0178eb1d6d","src/bso/mod.rs":"09e723dc7e99295ecafdcadffaf604d66ea27cf2b7f1fd9ab3cac4f4698ff6a7","src/bso/test_utils.rs":"4ec5a2df5e1c0ec14dc770681e959bdcef6ef04f6fde435999197f46a8ae4831","src/client/coll_state.rs":"4301526b987532cd7ebd5d089980c9524aa29360e08fa0c1f5437696ed354822","src/client/coll_update.rs":"627e2266c5c8f1c5e0bc83061fa14c1287c7d17c78b47882543521f75543b499","src/client/collection_keys.rs":"c27b2277a3a52033b58ab01490fc2ea7007494195dd5e6dc2c6931a4ca96795a","src/client/mod.rs":"8f588d4a035cf79d96f2500f06d5651c1a7c566127c456ffa5429811ddce3fd6","src/client/request.rs":"8841524e37d8195867bdf6ba98c75f610cf47a4644adeebd6372cc6713f2260a","src/client/state.rs":"4e31193ef2471c1dfabf1c6a391bcb95e14ddb45855786a4194ff187d5c9347c","src/client/status.rs":"f445a8765dac9789444e23b5145148413407bb1d18a15ef56682243997f591bf","src/client/storage_client.rs":"8de72d4ba3ca4f68c8e1898466de83a2b543545a18679800cb4f7fbda2dc3183","src/client/sync.rs":"a04478c4e31bbf39f593decfc4844bfb7e678907031f0a93d1e005cf0047ebb4","src/client/sync_multiple.rs":"3729d4afd90ab1bd9982a3506252c99d8f37619cc1792ef4feba352ad01a7192","src/client/token.rs":"b268759d31e0fe17e0e2a428694cd9a317fcfbdd52f023d5d8c7cc6f00f1a102","src/client/util.rs":"71cc70ee41f821f53078675e636e9fad9c6046fa1a989e37f5487e340a2277d6","src/client_types.rs":"3c3cac1540b92482f43660d9e43bdde8481c4cc1a98253a68c80e791231f5976","src/clients_engine/engine.rs":"0eaa078978c95fc96284b48a7458ebff49f0aa70b1148ef019b9350b5ba4d0d4","src/clients_engine/mod.rs":"461729e6f89b66b2cbd89b041a03d4d6a8ba582284ed4f3015cb13e1a0c6da97","src/clients_engine/record.rs":"69357413571d688eea3a5207f9b88088cde285b9373c7bd4ea1e018dbc823dd2","src/clients_engine/ser.rs":"9796e44ed7daf04f22afbb51238ac25fd0de1438b72181351b4ca29fd70fd429","src/device_type.rs":"fe217453f19b374abcc10e9f503b25f4f712b555497bebe5aefcf2e9b258d28e","src/enc_payload.rs":"aa3eea7df49b24cd59831680a47c417b73a3e36e6b0f3f4baf14ca66bd68be6b","src/engine/bridged_engine.rs":"9c0d602b3553932e77a87caba9262d3a0fc146500c6d46f1770273be6636d064","src/engine/changeset.rs":"949c520e508da0158b2df9fa20235f942ad8096cebbfc942b0fae41c35830c6b","src/engine/mod.rs":"cb638c2170c3785785877dfd059f26fbbfeae5edc599745dd861e4c299d310ad","src/engine/request.rs":"5923025fb9550178339f880a1bf8526d8e853e7a0b2bce6d9d687cc808ac0085","src/engine/sync_engine.rs":"9a5c6993dcceec8f82ead97a6d1840c7ed7dc6b326f7234c77f18848b6baf836","src/error.rs":"a45cfe02e6301f473c34678b694943c1a04308b8c292c6e0448bf495194c3b5e","src/key_bundle.rs":"ff8b10b95add934ecbc434b37ed089805886828ed159fd38bd692d1f01d06f7f","src/lib.rs":"eca1fa801820238141c4badeeec45d430aaec8b2ce088446ef94456d51014889","src/record_types.rs":"02bb3d352fb808131d298f9b90d9c95b7e9e0138b97c5401f3b9fdacc5562f44","src/server_timestamp.rs":"0020f31971ccbfc485894cabc3087459d42252b86d7de07f2136997864b0373b","src/telemetry.rs":"35e0313a052f16326e451e3d6e371337c1d71a471f32234ad9649fc1fa9f2237"},"package":null}
{"files":{"Cargo.toml":"1f11acaa90a112979205b4c7af9ba0c015afab5f3141dd082d58c862c84490e3","README.md":"6d4ff5b079ac5340d18fa127f583e7ad793c5a2328b8ecd12c3fc723939804f2","src/bso/content.rs":"d2d650f4932e9a7068a25dd7df0085b92cd8976a0635320e6ae306d5a425075c","src/bso/crypto.rs":"27602dcccb37d3a55620ee4e16b705da455d49af575de115c7c79c0178eb1d6d","src/bso/mod.rs":"09e723dc7e99295ecafdcadffaf604d66ea27cf2b7f1fd9ab3cac4f4698ff6a7","src/bso/test_utils.rs":"4ec5a2df5e1c0ec14dc770681e959bdcef6ef04f6fde435999197f46a8ae4831","src/client/coll_state.rs":"b0c47e44168ea2c7017cd8531f76bb230f9be66b119bb7416537b8693a1d0a0a","src/client/coll_update.rs":"cc12dfde0817eae68aa8e176497ed16e9e3307f72a33faa3fe329d7a3bfd1598","src/client/collection_keys.rs":"c27b2277a3a52033b58ab01490fc2ea7007494195dd5e6dc2c6931a4ca96795a","src/client/mod.rs":"9500b1d22a5064bbbd6a3d6bcc63fc4191e8ea4605ded359bc6c2dc2887626a3","src/client/request.rs":"8841524e37d8195867bdf6ba98c75f610cf47a4644adeebd6372cc6713f2260a","src/client/state.rs":"4e31193ef2471c1dfabf1c6a391bcb95e14ddb45855786a4194ff187d5c9347c","src/client/status.rs":"f445a8765dac9789444e23b5145148413407bb1d18a15ef56682243997f591bf","src/client/storage_client.rs":"3637b4522048353b06ad24031c150c66c13d9c27cef293e400db88807421633c","src/client/sync.rs":"ed7225c314df27793ed5de6da93cc4b75a98da1c14ac82e37a723a99821d4dc7","src/client/sync_multiple.rs":"3729d4afd90ab1bd9982a3506252c99d8f37619cc1792ef4feba352ad01a7192","src/client/token.rs":"b268759d31e0fe17e0e2a428694cd9a317fcfbdd52f023d5d8c7cc6f00f1a102","src/client/util.rs":"71cc70ee41f821f53078675e636e9fad9c6046fa1a989e37f5487e340a2277d6","src/client_types.rs":"3c3cac1540b92482f43660d9e43bdde8481c4cc1a98253a68c80e791231f5976","src/clients_engine/engine.rs":"b4a9c92f251b23e5a5452930e4472e7fea9ed12784a78836ce7337a94457ab86","src/clients_engine/mod.rs":"461729e6f89b66b2cbd89b041a03d4d6a8ba582284ed4f3015cb13e1a0c6da97","src/clients_engine/record.rs":"69357413571d688eea3a5207f9b88088cde285b9373c7bd4ea1e018dbc823dd2","src/clients_engine/ser.rs":"9796e44ed7daf04f22afbb51238ac25fd0de1438b72181351b4ca29fd70fd429","src/device_type.rs":"fe217453f19b374abcc10e9f503b25f4f712b555497bebe5aefcf2e9b258d28e","src/enc_payload.rs":"aa3eea7df49b24cd59831680a47c417b73a3e36e6b0f3f4baf14ca66bd68be6b","src/engine/bridged_engine.rs":"9c0d602b3553932e77a87caba9262d3a0fc146500c6d46f1770273be6636d064","src/engine/changeset.rs":"5e323aa07f0b18d22495a695b829326d18287ff75155b4818adf66b86e16ba00","src/engine/mod.rs":"f84a254642c1876fe56506703fb010a7866eb5d40af3fc238bf92b62a61cb6cc","src/engine/request.rs":"f40bac0b3f5286446a4056de885fd81e4fa77e4dc7d5bbb6aa644b93201046de","src/engine/sync_engine.rs":"5314d0163ccc93d78f5879d52cf2b60b9622e80722d84d3482cfa7c26df6bfdd","src/error.rs":"a45cfe02e6301f473c34678b694943c1a04308b8c292c6e0448bf495194c3b5e","src/key_bundle.rs":"ff8b10b95add934ecbc434b37ed089805886828ed159fd38bd692d1f01d06f7f","src/lib.rs":"25bec0ac0f8ed0e6a341f645f7f265c7e9a19332c13d05876f7332eccbd2a2de","src/record_types.rs":"02bb3d352fb808131d298f9b90d9c95b7e9e0138b97c5401f3b9fdacc5562f44","src/server_timestamp.rs":"0020f31971ccbfc485894cabc3087459d42252b86d7de07f2136997864b0373b","src/telemetry.rs":"35e0313a052f16326e451e3d6e371337c1d71a471f32234ad9649fc1fa9f2237"},"package":null}

View File

@ -9,18 +9,17 @@ use crate::error;
use crate::KeyBundle;
use crate::ServerTimestamp;
/// Holds state for a collection necessary to perform a sync of it. Lives for the lifetime
/// of a single sync.
/// Holds state for a collection. In general, only the CollState is
/// needed to sync a collection (but a valid GlobalState is needed to obtain
/// a CollState)
#[derive(Debug, Clone)]
pub struct CollState {
// Info about the server configuration/capabilities
pub config: InfoConfiguration,
// from meta/global, used for XIUS when we POST outgoing record based on this state.
// initially from meta/global, updated after an xius POST/PUT.
pub last_modified: ServerTimestamp,
pub key: KeyBundle,
}
/// This mini state-machine helps build a CollState
#[derive(Debug)]
pub enum LocalCollState {
/// The state is unknown, with the EngineSyncAssociation the collection
@ -39,7 +38,7 @@ pub enum LocalCollState {
SyncIdChanged { ids: CollSyncIds },
/// The collection is ready to sync.
Ready { coll_state: CollState },
Ready { key: KeyBundle },
}
pub struct LocalCollStateMachine<'state> {
@ -72,27 +71,14 @@ impl<'state> LocalCollStateMachine<'state> {
if ids.global == meta_global.sync_id
&& ids.coll == engine_meta.sync_id =>
{
// We are done - build the CollState
let coll_keys = CollectionKeys::from_encrypted_payload(
self.global_state.keys.clone(),
self.global_state.keys_timestamp,
self.root_key,
)?;
let key = coll_keys.key_for_collection(name).clone();
let name = engine.collection_name();
let config = self.global_state.config.clone();
let last_modified = self
.global_state
.collections
.get(name.as_ref())
.cloned()
.unwrap_or_default();
let coll_state = CollState {
config,
last_modified,
key,
};
Ok(LocalCollState::Ready { coll_state })
Ok(LocalCollState::Ready {
key: coll_keys.key_for_collection(name).clone(),
})
}
_ => Ok(LocalCollState::SyncIdChanged {
ids: CollSyncIds {
@ -134,8 +120,23 @@ impl<'state> LocalCollStateMachine<'state> {
loop {
log::trace!("LocalCollState in {:?}", s);
match s {
LocalCollState::Ready { coll_state } => return Ok(Some(coll_state)),
LocalCollState::Ready { key } => {
let name = engine.collection_name();
let config = self.global_state.config.clone();
let last_modified = self
.global_state
.collections
.get(name.as_ref())
.cloned()
.unwrap_or_default();
return Ok(Some(CollState {
config,
last_modified,
key,
}));
}
LocalCollState::Declined | LocalCollState::NoSuchCollection => return Ok(None),
_ => {
count += 1;
if count > 10 {
@ -171,7 +172,7 @@ mod tests {
use crate::engine::CollectionRequest;
use crate::engine::{IncomingChangeset, OutgoingChangeset};
use crate::record_types::{MetaGlobalEngine, MetaGlobalRecord};
use crate::{telemetry, CollectionName};
use crate::telemetry;
use anyhow::Result;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
@ -226,7 +227,7 @@ mod tests {
}
impl SyncEngine for TestSyncEngine {
fn collection_name(&self) -> CollectionName {
fn collection_name(&self) -> std::borrow::Cow<'static, str> {
self.collection_name.into()
}

View File

@ -8,8 +8,9 @@ use super::{
};
use crate::bso::OutgoingEncryptedBso;
use crate::engine::{CollectionRequest, IncomingChangeset, OutgoingChangeset};
use crate::error::{self, Error, Result};
use crate::{CollectionName, KeyBundle, ServerTimestamp};
use crate::error::{self, Error, ErrorResponse, Result};
use crate::{KeyBundle, ServerTimestamp};
use std::borrow::Cow;
pub fn encrypt_outgoing(
o: OutgoingChangeset,
@ -23,8 +24,8 @@ pub fn encrypt_outgoing(
pub fn fetch_incoming(
client: &Sync15StorageClient,
state: &CollState,
collection_request: CollectionRequest,
state: &mut CollState,
collection_request: &CollectionRequest,
) -> Result<IncomingChangeset> {
let collection = collection_request.collection.clone();
let (records, timestamp) = match client.get_encrypted_records(collection_request)? {
@ -35,6 +36,8 @@ pub fn fetch_incoming(
} => (record, last_modified),
other => return Err(other.create_storage_error()),
};
// xxx - duplication below of `timestamp` smells wrong
state.last_modified = timestamp;
let mut result = IncomingChangeset::new(collection, timestamp);
result.changes.reserve(records.len());
for record in records {
@ -51,7 +54,7 @@ pub fn fetch_incoming(
pub struct CollectionUpdate<'a> {
client: &'a Sync15StorageClient,
state: &'a CollState,
collection: CollectionName,
collection: Cow<'static, str>,
xius: ServerTimestamp,
to_update: Vec<OutgoingEncryptedBso>,
fully_atomic: bool,
@ -61,7 +64,7 @@ impl<'a> CollectionUpdate<'a> {
pub fn new(
client: &'a Sync15StorageClient,
state: &'a CollState,
collection: CollectionName,
collection: Cow<'static, str>,
xius: ServerTimestamp,
records: Vec<OutgoingEncryptedBso>,
fully_atomic: bool,
@ -83,12 +86,19 @@ impl<'a> CollectionUpdate<'a> {
fully_atomic: bool,
) -> Result<CollectionUpdate<'a>> {
let collection = changeset.collection.clone();
let xius = changeset.timestamp;
if xius < state.last_modified {
// We know we are going to fail the XIUS check...
return Err(Error::StorageHttpError(ErrorResponse::PreconditionFailed {
route: collection.into_owned(),
}));
}
let to_update = encrypt_outgoing(changeset, &state.key)?;
Ok(CollectionUpdate::new(
client,
state,
collection,
state.last_modified,
xius,
to_update,
fully_atomic,
))

View File

@ -25,7 +25,7 @@ mod sync_multiple;
mod token;
mod util;
pub(crate) use coll_state::{CollState, LocalCollStateMachine};
pub(crate) use coll_state::CollState;
pub(crate) use coll_update::{fetch_incoming, CollectionUpdate};
pub(crate) use collection_keys::CollectionKeys;
pub(crate) use request::InfoConfiguration;

View File

@ -7,10 +7,10 @@ use super::request::{
};
use super::token;
use crate::bso::{IncomingBso, IncomingEncryptedBso, OutgoingBso, OutgoingEncryptedBso};
use crate::engine::{CollectionPost, CollectionRequest};
use crate::engine::CollectionRequest;
use crate::error::{self, Error, ErrorResponse};
use crate::record_types::MetaGlobalRecord;
use crate::{CollectionName, Guid, ServerTimestamp};
use crate::{Guid, ServerTimestamp};
use serde_json::Value;
use std::str::FromStr;
use std::sync::atomic::{AtomicU32, Ordering};
@ -293,7 +293,7 @@ impl Sync15StorageClient {
pub fn get_encrypted_records(
&self,
collection_request: CollectionRequest,
collection_request: &CollectionRequest,
) -> error::Result<Sync15ClientResponse<Vec<IncomingEncryptedBso>>> {
self.collection_request(Method::Get, collection_request)
}
@ -356,7 +356,7 @@ impl Sync15StorageClient {
fn collection_request<T>(
&self,
method: Method,
r: CollectionRequest,
r: &CollectionRequest,
) -> error::Result<Sync15ClientResponse<T>>
where
for<'a> T: serde::de::Deserialize<'a>,
@ -367,12 +367,15 @@ impl Sync15StorageClient {
pub fn new_post_queue<'a, F: PostResponseHandler>(
&'a self,
coll: &'a CollectionName,
coll: &str,
config: &InfoConfiguration,
ts: ServerTimestamp,
on_response: F,
) -> error::Result<PostQueue<PostWrapper<'a>, F>> {
let pw = PostWrapper { client: self, coll };
let pw = PostWrapper {
client: self,
coll: coll.into(),
};
Ok(PostQueue::new(config, ts, pw, on_response))
}
@ -423,7 +426,7 @@ impl Sync15StorageClient {
pub struct PostWrapper<'a> {
client: &'a Sync15StorageClient,
coll: &'a CollectionName,
coll: String,
}
impl<'a> BatchPoster for PostWrapper<'a> {
@ -435,10 +438,10 @@ impl<'a> BatchPoster for PostWrapper<'a> {
commit: bool,
_: &PostQueue<T, O>,
) -> error::Result<PostResponse> {
let r = CollectionPost::new(self.coll.clone())
let r = CollectionRequest::new(self.coll.clone())
.batch(batch)
.commit(commit);
let url = build_collection_post_url(Url::parse(&self.client.tsc.api_endpoint()?)?, r)?;
let url = build_collection_request_url(Url::parse(&self.client.tsc.api_endpoint()?)?, &r)?;
let req = self
.client
@ -450,27 +453,19 @@ impl<'a> BatchPoster for PostWrapper<'a> {
}
}
fn build_collection_url(mut base_url: Url, collection: CollectionName) -> error::Result<Url> {
fn build_collection_request_url(mut base_url: Url, r: &CollectionRequest) -> error::Result<Url> {
base_url
.path_segments_mut()
.map_err(|_| Error::UnacceptableUrl("Storage server URL is not a base".to_string()))?
.extend(&["storage", &collection]);
.extend(&["storage", &r.collection]);
// This is strange but just accessing query_pairs_mut makes you have
// a trailing question mark on your url. I don't think anything bad
// would happen here, but I don't know, and also, it looks dumb so
// I'd rather not have it.
if base_url.query() == Some("") {
base_url.set_query(None);
}
Ok(base_url)
}
fn build_collection_request_url(mut base_url: Url, r: CollectionRequest) -> error::Result<Url> {
let mut pairs = base_url.query_pairs_mut();
if r.full {
pairs.append_pair("full", "1");
}
if r.limit > 0 {
pairs.append_pair("limit", &r.limit.to_string());
}
if let Some(ids) = &r.ids {
// Most ids are 12 characters, and we comma separate them, so 13.
let mut buf = String::with_capacity(ids.len() * 13);
@ -482,33 +477,32 @@ fn build_collection_request_url(mut base_url: Url, r: CollectionRequest) -> erro
}
pairs.append_pair("ids", &buf);
}
if let Some(ts) = r.older {
pairs.append_pair("older", &ts.to_string());
}
if let Some(ts) = r.newer {
pairs.append_pair("newer", &ts.to_string());
}
if let Some(l) = r.limit {
pairs.append_pair("sort", l.order.as_str());
pairs.append_pair("limit", &l.num.to_string());
}
pairs.finish();
drop(pairs);
build_collection_url(base_url, r.collection)
}
#[cfg(feature = "sync-client")]
fn build_collection_post_url(mut base_url: Url, r: CollectionPost) -> error::Result<Url> {
let mut pairs = base_url.query_pairs_mut();
if let Some(batch) = &r.batch {
pairs.append_pair("batch", batch);
}
if r.commit {
pairs.append_pair("commit", "true");
}
if let Some(ts) = r.older {
pairs.append_pair("older", &ts.to_string());
}
if let Some(ts) = r.newer {
pairs.append_pair("newer", &ts.to_string());
}
if let Some(o) = r.order {
pairs.append_pair("sort", o.as_str());
}
pairs.finish();
drop(pairs);
build_collection_url(base_url, r.collection)
// This is strange but just accessing query_pairs_mut makes you have
// a trailing question mark on your url. I don't think anything bad
// would happen here, but I don't know, and also, it looks dumb so
// I'd rather not have it.
if base_url.query() == Some("") {
base_url.set_query(None);
}
Ok(base_url)
}
#[cfg(test)]
@ -542,15 +536,34 @@ mod test {
let base = Url::parse("https://example.com/sync").unwrap();
let empty =
build_collection_request_url(base.clone(), CollectionRequest::new("foo".into()))
.unwrap();
build_collection_request_url(base.clone(), &CollectionRequest::new("foo")).unwrap();
assert_eq!(empty.as_str(), "https://example.com/sync/storage/foo");
let batch_start = build_collection_request_url(
base.clone(),
&CollectionRequest::new("bar")
.batch(Some("true".into()))
.commit(false),
)
.unwrap();
assert_eq!(
batch_start.as_str(),
"https://example.com/sync/storage/bar?batch=true"
);
let batch_commit = build_collection_request_url(
base.clone(),
&CollectionRequest::new("asdf")
.batch(Some("1234abc".into()))
.commit(true),
)
.unwrap();
assert_eq!(
batch_commit.as_str(),
"https://example.com/sync/storage/asdf?batch=1234abc&commit=true"
);
let idreq = build_collection_request_url(
base.clone(),
CollectionRequest::new("wutang".into())
.full()
.ids(&["rza", "gza"]),
&CollectionRequest::new("wutang").full().ids(&["rza", "gza"]),
)
.unwrap();
assert_eq!(
@ -560,46 +573,15 @@ mod test {
let complex = build_collection_request_url(
base,
CollectionRequest::new("specific".into())
&CollectionRequest::new("specific")
.full()
.limit(10, RequestOrder::Oldest)
.limit(10)
.sort_by(RequestOrder::Oldest)
.older_than(ServerTimestamp(9_876_540))
.newer_than(ServerTimestamp(1_234_560)),
)
.unwrap();
assert_eq!(complex.as_str(),
"https://example.com/sync/storage/specific?full=1&older=9876.54&newer=1234.56&sort=oldest&limit=10");
}
#[cfg(feature = "sync-client")]
#[test]
fn test_post_query_building() {
let base = Url::parse("https://example.com/sync").unwrap();
let empty =
build_collection_post_url(base.clone(), CollectionPost::new("foo".into())).unwrap();
assert_eq!(empty.as_str(), "https://example.com/sync/storage/foo");
let batch_start = build_collection_post_url(
base.clone(),
CollectionPost::new("bar".into())
.batch(Some("true".into()))
.commit(false),
)
.unwrap();
assert_eq!(
batch_start.as_str(),
"https://example.com/sync/storage/bar?batch=true"
);
let batch_commit = build_collection_post_url(
base,
CollectionPost::new("asdf".into())
.batch(Some("1234abc".into()))
.commit(true),
)
.unwrap();
assert_eq!(
batch_commit.as_str(),
"https://example.com/sync/storage/asdf?batch=1234abc&commit=true"
);
"https://example.com/sync/storage/specific?full=1&limit=10&older=9876.54&newer=1234.56&sort=oldest");
}
}

View File

@ -2,7 +2,10 @@
* 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/. */
use super::{CollectionUpdate, GlobalState, LocalCollStateMachine, Sync15StorageClient};
use super::coll_state::LocalCollStateMachine;
use super::coll_update::CollectionUpdate;
use super::state::GlobalState;
use super::storage_client::Sync15StorageClient;
use crate::clients_engine;
use crate::engine::{IncomingChangeset, SyncEngine};
use crate::error::Error;
@ -25,17 +28,18 @@ pub fn synchronize_with_clients_engine(
log::info!("Syncing collection {}", collection);
// our global state machine is ready - get the collection machine going.
let coll_state = match LocalCollStateMachine::get_state(engine, global_state, root_sync_key)? {
Some(coll_state) => coll_state,
None => {
// XXX - this is either "error" or "declined".
log::warn!(
"can't setup for the {} collection - hopefully it works later",
collection
);
return Ok(());
}
};
let mut coll_state =
match LocalCollStateMachine::get_state(engine, global_state, root_sync_key)? {
Some(coll_state) => coll_state,
None => {
// XXX - this is either "error" or "declined".
log::warn!(
"can't setup for the {} collection - hopefully it works later",
collection
);
return Ok(());
}
};
if let Some(clients) = clients {
engine.prepare_for_sync(&|| clients.get_client_data())?;
@ -55,7 +59,7 @@ pub fn synchronize_with_clients_engine(
.map(|(idx, collection_request)| {
interruptee.err_if_interrupted()?;
let incoming_changes =
super::fetch_incoming(client, &coll_state, collection_request)?;
super::fetch_incoming(client, &mut coll_state, &collection_request)?;
log::info!(
"Downloaded {} remote changes (request {} of {})",
@ -68,18 +72,25 @@ pub fn synchronize_with_clients_engine(
.collect::<Result<Vec<_>, Error>>()?
};
let outgoing = engine.apply_incoming(incoming, telem_engine)?;
let new_timestamp = incoming.last().expect("must have >= 1").timestamp;
let mut outgoing = engine.apply_incoming(incoming, telem_engine)?;
interruptee.err_if_interrupted()?;
// Bump the timestamps now just incase the upload fails.
// xxx - duplication below smells wrong
outgoing.timestamp = new_timestamp;
coll_state.last_modified = new_timestamp;
log::info!("Uploading {} outgoing changes", outgoing.changes.len());
let upload_info =
CollectionUpdate::new_from_changeset(client, &coll_state, outgoing, fully_atomic)?
.upload()?;
log::info!(
"Upload success ({} records success, {} records failed)",
upload_info.successful_ids.len(),
upload_info.failed_ids.len()
);
// ideally we'd report this per-batch, but for now, let's just report it
// as a total.
let mut telem_outgoing = telemetry::EngineOutgoing::new();

View File

@ -54,11 +54,13 @@ impl<'a> Driver<'a> {
inbound: IncomingChangeset,
should_refresh_client: bool,
) -> Result<OutgoingChangeset> {
let mut outgoing = OutgoingChangeset::new(COLLECTION_NAME, inbound.timestamp);
outgoing.timestamp = inbound.timestamp;
self.interruptee.err_if_interrupted()?;
let outgoing_commands = self.command_processor.fetch_outgoing_commands()?;
let mut has_own_client_record = false;
let mut changes = Vec::new();
for bso in inbound.changes {
self.interruptee.err_if_interrupted()?;
@ -126,7 +128,9 @@ impl<'a> Driver<'a> {
ttl: Some(CLIENTS_TTL),
..Default::default()
};
changes.push(OutgoingBso::from_content(envelope, current_client_record)?);
outgoing
.changes
.push(OutgoingBso::from_content(envelope, current_client_record)?);
}
} else {
// Add the other client to our map of recently synced clients.
@ -172,7 +176,9 @@ impl<'a> Driver<'a> {
ttl: Some(CLIENTS_TTL),
..Default::default()
};
changes.push(OutgoingBso::from_content(envelope, new_client)?);
outgoing
.changes
.push(OutgoingBso::from_content(envelope, new_client)?);
}
}
@ -185,10 +191,12 @@ impl<'a> Driver<'a> {
ttl: Some(CLIENTS_TTL),
..Default::default()
};
changes.push(OutgoingBso::from_content(envelope, current_client_record)?);
outgoing
.changes
.push(OutgoingBso::from_content(envelope, current_client_record)?);
}
Ok(OutgoingChangeset::new(COLLECTION_NAME.into(), changes))
Ok(outgoing)
}
/// Builds a fresh client record for this device.
@ -283,7 +291,7 @@ impl<'a> Engine<'a> {
global_state.keys_timestamp,
root_sync_key,
)?;
let coll_state = CollState {
let mut coll_state = CollState {
config: global_state.config.clone(),
last_modified: global_state
.collections
@ -293,7 +301,7 @@ impl<'a> Engine<'a> {
key: coll_keys.key_for_collection(COLLECTION_NAME).clone(),
};
let inbound = self.fetch_incoming(storage_client, &coll_state)?;
let inbound = self.fetch_incoming(storage_client, &mut coll_state)?;
let mut driver = Driver::new(
self.command_processor,
@ -304,6 +312,8 @@ impl<'a> Engine<'a> {
let outgoing = driver.sync(inbound, should_refresh_client)?;
self.recent_clients = driver.recent_clients;
coll_state.last_modified = outgoing.timestamp;
self.interruptee.err_if_interrupted()?;
let upload_info =
CollectionUpdate::new_from_changeset(storage_client, &coll_state, outgoing, true)?
@ -322,15 +332,15 @@ impl<'a> Engine<'a> {
fn fetch_incoming(
&self,
storage_client: &Sync15StorageClient,
coll_state: &CollState,
coll_state: &mut CollState,
) -> Result<IncomingChangeset> {
// Note that, unlike other stores, we always fetch the full collection
// on every sync, so `inbound` will return all clients, not just the
// ones that changed since the last sync.
let coll_request = CollectionRequest::new(COLLECTION_NAME.into()).full();
let coll_request = CollectionRequest::new(COLLECTION_NAME).full();
self.interruptee.err_if_interrupted()?;
let inbound = crate::client::fetch_incoming(storage_client, coll_state, coll_request)?;
let inbound = crate::client::fetch_incoming(storage_client, coll_state, &coll_request)?;
Ok(inbound)
}
@ -550,7 +560,7 @@ mod tests {
.into_iter()
.map(|c| OutgoingBso::to_test_incoming(&c))
.collect(),
timestamp: ServerTimestamp::default(),
timestamp: outgoing.timestamp,
collection: outgoing.collection,
};
if let Value::Array(expected) = expected {
@ -789,7 +799,7 @@ mod tests {
.into_iter()
.map(|c| OutgoingBso::to_test_incoming(&c))
.collect(),
timestamp: ServerTimestamp::default(),
timestamp: outgoing.timestamp,
collection: outgoing.collection,
};
for (incoming_cleartext, record) in zip(incoming.changes, expected) {

View File

@ -3,50 +3,40 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use crate::bso::{IncomingBso, OutgoingBso};
use crate::{CollectionName, ServerTimestamp};
use crate::ServerTimestamp;
// Incoming and Outgoing changesets are almost identical except for the timestamp.
// Separate types still helps avoid confusion with that timestamp, so they're split.
#[derive(Debug)]
pub struct IncomingChangeset {
pub changes: Vec<IncomingBso>,
/// The server timestamp of the collection.
#[derive(Debug, Clone)]
pub struct RecordChangeset<T> {
pub changes: Vec<T>,
/// For GETs, the last sync timestamp that should be persisted after
/// applying the records.
/// For POSTs, this is the XIUS timestamp.
pub timestamp: ServerTimestamp,
pub collection: CollectionName,
pub collection: std::borrow::Cow<'static, str>,
}
impl IncomingChangeset {
pub type IncomingChangeset = RecordChangeset<IncomingBso>;
pub type OutgoingChangeset = RecordChangeset<OutgoingBso>;
impl<T> RecordChangeset<T> {
#[inline]
pub fn new(collection: CollectionName, timestamp: ServerTimestamp) -> Self {
pub fn new(
collection: impl Into<std::borrow::Cow<'static, str>>,
timestamp: ServerTimestamp,
) -> RecordChangeset<T> {
Self::new_with_changes(collection, timestamp, Vec::new())
}
#[inline]
pub fn new_with_changes(
collection: CollectionName,
collection: impl Into<std::borrow::Cow<'static, str>>,
timestamp: ServerTimestamp,
changes: Vec<IncomingBso>,
) -> Self {
Self {
changes: Vec<T>,
) -> RecordChangeset<T> {
RecordChangeset {
changes,
timestamp,
collection,
}
}
}
#[derive(Debug)]
pub struct OutgoingChangeset {
pub changes: Vec<OutgoingBso>,
pub collection: CollectionName,
}
impl OutgoingChangeset {
#[inline]
pub fn new(collection: CollectionName, changes: Vec<OutgoingBso>) -> Self {
Self {
collection,
changes,
collection: collection.into(),
}
}
}

View File

@ -32,7 +32,5 @@ mod sync_engine;
pub use bridged_engine::{ApplyResults, BridgedEngine};
pub use changeset::{IncomingChangeset, OutgoingChangeset};
#[cfg(feature = "sync-client")]
pub(crate) use request::CollectionPost;
pub use request::{CollectionRequest, RequestOrder};
pub use sync_engine::{CollSyncIds, EngineSyncAssociation, SyncEngine, SyncEngineId};

View File

@ -1,24 +1,37 @@
/* 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/. */
use crate::{CollectionName, Guid, ServerTimestamp};
#[derive(Debug, Default, Clone, PartialEq, Eq)]
use crate::{Guid, ServerTimestamp};
use std::borrow::Cow;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CollectionRequest {
pub collection: CollectionName,
pub collection: Cow<'static, str>,
pub full: bool,
pub ids: Option<Vec<Guid>>,
pub limit: Option<RequestLimit>,
pub limit: usize,
pub older: Option<ServerTimestamp>,
pub newer: Option<ServerTimestamp>,
pub order: Option<RequestOrder>,
pub commit: bool,
pub batch: Option<String>,
}
impl CollectionRequest {
#[inline]
pub fn new(collection: CollectionName) -> CollectionRequest {
pub fn new<S>(collection: S) -> CollectionRequest
where
S: Into<Cow<'static, str>>,
{
CollectionRequest {
collection,
..Default::default()
collection: collection.into(),
full: false,
ids: None,
limit: 0,
older: None,
newer: None,
order: None,
commit: false,
batch: None,
}
}
@ -51,47 +64,30 @@ impl CollectionRequest {
}
#[inline]
pub fn limit(mut self, num: usize, order: RequestOrder) -> CollectionRequest {
self.limit = Some(RequestLimit { num, order });
pub fn sort_by(mut self, order: RequestOrder) -> CollectionRequest {
self.order = Some(order);
self
}
}
// This is just used interally - consumers just provide the content, not request params.
#[cfg(feature = "sync-client")]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub(crate) struct CollectionPost {
pub collection: CollectionName,
pub commit: bool,
pub batch: Option<String>,
}
#[cfg(feature = "sync-client")]
impl CollectionPost {
#[inline]
pub fn new(collection: CollectionName) -> Self {
Self {
collection,
..Default::default()
}
pub fn limit(mut self, num: usize) -> CollectionRequest {
self.limit = num;
self
}
#[inline]
pub fn batch(mut self, batch: Option<String>) -> Self {
pub fn batch(mut self, batch: Option<String>) -> CollectionRequest {
self.batch = batch;
self
}
#[inline]
pub fn commit(mut self, v: bool) -> Self {
pub fn commit(mut self, v: bool) -> CollectionRequest {
self.commit = v;
self
}
}
// Asking for the order of records only makes sense if you are limiting them
// in some way - consumers don't care about the order otherwise as everything
// is processed as a set.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum RequestOrder {
Oldest,
@ -115,11 +111,3 @@ impl std::fmt::Display for RequestOrder {
f.write_str(self.as_str())
}
}
// If you specify a numerical limit you must provide the order so backfilling
// is possible (ie, so you know which ones you got!)
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct RequestLimit {
pub(crate) num: usize,
pub(crate) order: RequestOrder,
}

View File

@ -4,7 +4,7 @@
use super::{CollectionRequest, IncomingChangeset, OutgoingChangeset};
use crate::client_types::ClientData;
use crate::{telemetry, CollectionName, Guid, ServerTimestamp};
use crate::{telemetry, Guid, ServerTimestamp};
use anyhow::Result;
use std::fmt;
@ -104,7 +104,7 @@ impl TryFrom<&str> for SyncEngineId {
/// Different engines will produce errors of different types. To accommodate
/// this, we force them all to return anyhow::Error.
pub trait SyncEngine {
fn collection_name(&self) -> CollectionName;
fn collection_name(&self) -> std::borrow::Cow<'static, str>;
/// Prepares the engine for syncing. The tabs engine currently uses this to
/// store the current list of clients, which it uses to look up device names
@ -167,7 +167,7 @@ pub trait SyncEngine {
records_synced: Vec<Guid>,
) -> Result<()>;
/// The engine is responsible for building collection requests. Engines
/// The engine is responsible for building the collection request. Engines
/// typically will store a lastModified timestamp and use that to build a
/// request saying "give me full records since that date" - however, other
/// engines might do something fancier. This could even later be extended to

View File

@ -37,11 +37,6 @@ pub use key_bundle::KeyBundle;
pub use server_timestamp::ServerTimestamp;
pub use sync_guid::Guid;
// Collection names are almost always `static, so we use a `Cow`:
// * Either a `String` or a `'static &str` can be `.into()`'d into one of these.
// * Cloning one with a `'static &str` is extremely cheap and doesn't allocate memory.
pub type CollectionName = std::borrow::Cow<'static, str>;
// For skip_serializing_if
fn skip_if_default<T: PartialEq + Default>(v: &T) -> bool {
*v == T::default()

View File

@ -1 +1 @@
{"files":{"Cargo.toml":"8ae43dd6969feea0eabc5c53d830960212e6cb1a18b43a00f47e8784c2fd0063","README.md":"c48b8f391ef822c4f3971b5f453a1e7b43bea232752d520460d2f04803aead1a","build.rs":"33e61b811b19ed2b58e319cc65d5988bed258d2c4fea2d706301184c59847a0f","src/error.rs":"ac3d450f0ba6a855c37fa2dd829004b22dce5ad4416ebec66a3d7d6212bdbcd7","src/lib.rs":"7208f78955e015ef8bab7916307e551cd3c1bd56d7fe14f8b53cd53bc4b38555","src/schema.rs":"2b7b51f3c2edc0ca603495c10b917603fd9ac791c4a366080e40d090b13b91f2","src/storage.rs":"eb9505477a926919769b09988ebbeeba96ceada634aec79b73fa75f79c038b03","src/store.rs":"ab0b6214b30b0f0fa7c6a89098ff3db1a8f76264f6711c4481c0be460afe522b","src/sync/bridge.rs":"705a24934c90494c2074288d03266504a866c4f04cb89c4e17b762cb498d621e","src/sync/engine.rs":"f2c5e4a2cb400ca33b649a078fc56e59a01a81a6e16e42b126e8b00d22b5eeea","src/sync/full_sync.rs":"47660644b3a66f3adff1130bb64e2ef40cb29749a40bc28d71246641d28ad080","src/sync/mod.rs":"81f98303b2a60aa6c6ed0d9777a95d6bbff46e57a494a0172d81c5819296802e","src/sync/record.rs":"896ebc6aa213bac9e6170c7e0b7dbee322f62e5f6c28462cb6da0bfe8ce938ba","src/tabs.udl":"a555fe11b5fa7ea9aefa7d7be31906a63b31cbc16b9b7f5ad952fd0e08ba5c61","uniffi.toml":"5156701368f0b5856e658143714a43058385c8ac53bee72d7a5a332b576dfb82"},"package":null}
{"files":{"Cargo.toml":"8ae43dd6969feea0eabc5c53d830960212e6cb1a18b43a00f47e8784c2fd0063","README.md":"c48b8f391ef822c4f3971b5f453a1e7b43bea232752d520460d2f04803aead1a","build.rs":"33e61b811b19ed2b58e319cc65d5988bed258d2c4fea2d706301184c59847a0f","src/error.rs":"ac3d450f0ba6a855c37fa2dd829004b22dce5ad4416ebec66a3d7d6212bdbcd7","src/lib.rs":"7208f78955e015ef8bab7916307e551cd3c1bd56d7fe14f8b53cd53bc4b38555","src/schema.rs":"2b7b51f3c2edc0ca603495c10b917603fd9ac791c4a366080e40d090b13b91f2","src/storage.rs":"0a74e341d33b6734f6ef0130e2ba506ec313b5a5f8d6660c535ffc53520edabc","src/store.rs":"ab0b6214b30b0f0fa7c6a89098ff3db1a8f76264f6711c4481c0be460afe522b","src/sync/bridge.rs":"a444c97991cfa8e0dd92ba3b35c6864ddd0cfec1d17e5cc514b9c36ada8d46c3","src/sync/engine.rs":"bbee416c9d45b90b945016d26c2133639c1ad057c6f52fdbdfa5a4f4f631a378","src/sync/full_sync.rs":"514668bb76d1fe6de3a36e64e6984b8dbd391155685ea21cbf91f67a957babdd","src/sync/mod.rs":"81f98303b2a60aa6c6ed0d9777a95d6bbff46e57a494a0172d81c5819296802e","src/sync/record.rs":"896ebc6aa213bac9e6170c7e0b7dbee322f62e5f6c28462cb6da0bfe8ce938ba","src/tabs.udl":"a555fe11b5fa7ea9aefa7d7be31906a63b31cbc16b9b7f5ad952fd0e08ba5c61","uniffi.toml":"5156701368f0b5856e658143714a43058385c8ac53bee72d7a5a332b576dfb82"},"package":null}

View File

@ -27,8 +27,6 @@ pub type RemoteTabRecord = RemoteTab;
pub(crate) const TABS_CLIENT_TTL: u32 = 15_552_000; // 180 days, same as CLIENTS_TTL
const FAR_FUTURE: i64 = 4_102_405_200_000; // 2100/01/01
const MAX_PAYLOAD_SIZE: usize = 512 * 1024; // Twice as big as desktop, still smaller than server max (2MB)
const MAX_TITLE_CHAR_LENGTH: usize = 512; // We put an upper limit on title sizes for tabs to reduce memory
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RemoteTab {
@ -148,44 +146,30 @@ impl TabsStorage {
self.local_tabs.borrow_mut().replace(local_state);
}
// We try our best to fit as many tabs in a payload as possible, this includes
// limiting the url history entries, title character count and finally drop enough tabs
// until we have small enough payload that the server will accept
pub fn prepare_local_tabs_for_upload(&self) -> Option<Vec<RemoteTab>> {
if let Some(local_tabs) = self.local_tabs.borrow().as_ref() {
let mut sanitized_tabs: Vec<RemoteTab> = local_tabs
.iter()
.cloned()
.filter_map(|mut tab| {
if tab.url_history.is_empty() || !is_url_syncable(&tab.url_history[0]) {
return None;
}
let mut sanitized_history = Vec::with_capacity(TAB_ENTRIES_LIMIT);
for url in tab.url_history {
if sanitized_history.len() == TAB_ENTRIES_LIMIT {
break;
return Some(
local_tabs
.iter()
.cloned()
.filter_map(|mut tab| {
if tab.url_history.is_empty() || !is_url_syncable(&tab.url_history[0]) {
return None;
}
if is_url_syncable(&url) {
sanitized_history.push(url);
let mut sanitized_history = Vec::with_capacity(TAB_ENTRIES_LIMIT);
for url in tab.url_history {
if sanitized_history.len() == TAB_ENTRIES_LIMIT {
break;
}
if is_url_syncable(&url) {
sanitized_history.push(url);
}
}
}
tab.url_history = sanitized_history;
// Truncate the title to some limit and append ellipsis
// to incate that we've truncated
if tab.title.len() > MAX_TITLE_CHAR_LENGTH {
tab.title.truncate(MAX_TITLE_CHAR_LENGTH - 3);
// Append ellipsis char for any client displaying the full title
tab.title.push('\u{2026}');
}
Some(tab)
})
.collect();
// Sort the tabs so when we trim tabs it's the oldest tabs
sanitized_tabs.sort_by(|a, b| b.last_used.cmp(&a.last_used));
// If trimming the tab length failed for some reason, just return the untrimmed tabs
trim_tabs_length(&mut sanitized_tabs, MAX_PAYLOAD_SIZE);
return Some(sanitized_tabs);
tab.url_history = sanitized_history;
Some(tab)
})
.collect(),
);
}
None
}
@ -346,11 +330,12 @@ impl TabsStorage {
}
pub(crate) fn put_meta(&mut self, key: &str, value: &dyn ToSql) -> Result<()> {
let db = self.open_or_create()?;
db.execute_cached(
"REPLACE INTO moz_meta (key, value) VALUES (:key, :value)",
&[(":key", &key as &dyn ToSql), (":value", value)],
)?;
if let Some(db) = self.open_if_exists()? {
db.execute_cached(
"REPLACE INTO moz_meta (key, value) VALUES (:key, :value)",
&[(":key", &key as &dyn ToSql), (":value", value)],
)?;
}
Ok(())
}
@ -377,28 +362,6 @@ impl TabsStorage {
}
}
// Trim the amount of tabs in a list to fit the specified memory size
fn trim_tabs_length(tabs: &mut Vec<RemoteTab>, payload_size_max_bytes: usize) {
// Ported from https://searchfox.org/mozilla-central/rev/84fb1c4511312a0b9187f647d90059e3a6dd27f8/services/sync/modules/util.sys.mjs#422
// See bug 535326 comment 8 for an explanation of the estimation
let max_serialized_size = (payload_size_max_bytes / 4) * 3 - 1500;
let size = compute_serialized_size(tabs);
if size > max_serialized_size {
// Estimate a little more than the direct fraction to maximize packing
let cutoff = (tabs.len() * max_serialized_size) / size;
tabs.truncate(cutoff);
// Keep dropping off the last entry until the data fits.
while compute_serialized_size(tabs) > max_serialized_size {
tabs.pop();
}
}
}
fn compute_serialized_size(v: &Vec<RemoteTab>) -> usize {
serde_json::to_string(v).unwrap_or_default().len()
}
// Try to keep in sync with https://searchfox.org/mozilla-central/rev/2ad13433da20a0749e1e9a10ec0ab49b987c2c8e/modules/libpref/init/all.js#3927
fn is_url_syncable(url: &str) -> bool {
url.len() <= URI_LENGTH_MAX
@ -442,15 +405,12 @@ mod tests {
#[test]
fn test_tabs_meta() {
let dir = tempfile::tempdir().unwrap();
let db_name = dir.path().join("test_tabs_meta.db");
let mut db = TabsStorage::new(db_name);
let mut db = TabsStorage::new_with_mem_path("test");
let test_key = "TEST KEY A";
let test_value = "TEST VALUE A";
let test_key2 = "TEST KEY B";
let test_value2 = "TEST VALUE B";
// should automatically make the DB if one doesn't exist
db.put_meta(test_key, &test_value).unwrap();
db.put_meta(test_key2, &test_value2).unwrap();
@ -545,54 +505,6 @@ mod tests {
])
);
}
#[test]
fn test_trimming_tab_title() {
let mut storage = TabsStorage::new_with_mem_path("test_prepare_local_tabs_for_upload");
assert_eq!(storage.prepare_local_tabs_for_upload(), None);
storage.update_local_state(vec![RemoteTab {
title: "a".repeat(MAX_TITLE_CHAR_LENGTH + 10), // Fill a string more than max
url_history: vec!["https://foo.bar".to_owned()],
icon: None,
last_used: 0,
}]);
let mut truncated_title = "a".repeat(MAX_TITLE_CHAR_LENGTH - 3);
truncated_title.push('\u{2026}');
assert_eq!(
storage.prepare_local_tabs_for_upload(),
Some(vec![
// title trimmed to 50 characters
RemoteTab {
title: truncated_title, // title was trimmed to only max char length
url_history: vec!["https://foo.bar".to_owned()],
icon: None,
last_used: 0,
},
])
);
}
#[test]
fn test_trim_tabs_length() {
let mut storage = TabsStorage::new_with_mem_path("test_prepare_local_tabs_for_upload");
assert_eq!(storage.prepare_local_tabs_for_upload(), None);
let mut too_many_tabs: Vec<RemoteTab> = Vec::new();
for n in 1..5000 {
too_many_tabs.push(RemoteTab {
title: "aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa" //50 characters
.to_owned(),
url_history: vec![format!("https://foo{}.bar", n)],
icon: None,
last_used: 0,
});
}
let tabs_mem_size = compute_serialized_size(&too_many_tabs);
// ensure we are definitely over the payload limit
assert!(tabs_mem_size > MAX_PAYLOAD_SIZE);
// Add our over-the-limit tabs to the local state
storage.update_local_state(too_many_tabs.clone());
// prepare_local_tabs_for_upload did the trimming we needed to get under payload size
let tabs_to_upload = &storage.prepare_local_tabs_for_upload().unwrap();
assert!(compute_serialized_size(tabs_to_upload) <= MAX_PAYLOAD_SIZE);
}
// Helper struct to model what's stored in the DB
struct TabsSQLRecord {
guid: String,

View File

@ -4,7 +4,7 @@
use std::sync::{Arc, Mutex};
use crate::error::{ApiResult, TabsApiError};
use crate::error::{ApiResult, Result, TabsApiError};
use crate::sync::engine::TabsSyncImpl;
use crate::TabsStore;
use error_support::handle_error;
@ -47,7 +47,7 @@ impl BridgedEngineImpl {
impl BridgedEngine for BridgedEngineImpl {
type Error = TabsApiError;
#[handle_error(crate::Error)]
#[handle_error]
fn last_sync(&self) -> ApiResult<i64> {
Ok(self
.sync_impl
@ -58,7 +58,7 @@ impl BridgedEngine for BridgedEngineImpl {
.as_millis())
}
#[handle_error(crate::Error)]
#[handle_error]
fn set_last_sync(&self, last_sync_millis: i64) -> ApiResult<()> {
self.sync_impl
.lock()
@ -67,7 +67,7 @@ impl BridgedEngine for BridgedEngineImpl {
Ok(())
}
#[handle_error(crate::Error)]
#[handle_error]
fn sync_id(&self) -> ApiResult<Option<String>> {
Ok(match self.sync_impl.lock().unwrap().get_sync_assoc()? {
EngineSyncAssociation::Connected(id) => Some(id.coll.to_string()),
@ -75,7 +75,7 @@ impl BridgedEngine for BridgedEngineImpl {
})
}
#[handle_error(crate::Error)]
#[handle_error]
fn reset_sync_id(&self) -> ApiResult<String> {
let new_id = SyncGuid::random().to_string();
let new_coll_ids = CollSyncIds {
@ -89,7 +89,7 @@ impl BridgedEngine for BridgedEngineImpl {
Ok(new_id)
}
#[handle_error(crate::Error)]
#[handle_error]
fn ensure_current_sync_id(&self, sync_id: &str) -> ApiResult<String> {
let mut sync_impl = self.sync_impl.lock().unwrap();
let assoc = sync_impl.get_sync_assoc()?;
@ -105,7 +105,7 @@ impl BridgedEngine for BridgedEngineImpl {
Ok(sync_id.to_string()) // this is a bit odd, why the result?
}
#[handle_error(crate::Error)]
#[handle_error]
fn prepare_for_sync(&self, client_data: &str) -> ApiResult<()> {
let data: ClientData = serde_json::from_str(client_data)?;
self.sync_impl.lock().unwrap().prepare_for_sync(data)
@ -116,14 +116,14 @@ impl BridgedEngine for BridgedEngineImpl {
Ok(())
}
#[handle_error(crate::Error)]
#[handle_error]
fn store_incoming(&self, incoming: Vec<IncomingBso>) -> ApiResult<()> {
// Store the incoming payload in memory so we can use it in apply
*(self.incoming.lock().unwrap()) = incoming;
Ok(())
}
#[handle_error(crate::Error)]
#[handle_error]
fn apply(&self) -> ApiResult<ApplyResults> {
let mut incoming = self.incoming.lock().unwrap();
// We've a reference to a Vec<> but it's owned by the mutex - swap the mutex owned
@ -141,7 +141,7 @@ impl BridgedEngine for BridgedEngineImpl {
})
}
#[handle_error(crate::Error)]
#[handle_error]
fn set_uploaded(&self, server_modified_millis: i64, ids: &[SyncGuid]) -> ApiResult<()> {
self.sync_impl
.lock()
@ -149,13 +149,13 @@ impl BridgedEngine for BridgedEngineImpl {
.sync_finished(ServerTimestamp::from_millis(server_modified_millis), ids)
}
#[handle_error(crate::Error)]
#[handle_error]
fn sync_finished(&self) -> ApiResult<()> {
*(self.incoming.lock().unwrap()) = Vec::default();
Ok(())
}
#[handle_error(crate::Error)]
#[handle_error]
fn reset(&self) -> ApiResult<()> {
self.sync_impl
.lock()
@ -164,7 +164,7 @@ impl BridgedEngine for BridgedEngineImpl {
Ok(())
}
#[handle_error(crate::Error)]
#[handle_error]
fn wipe(&self) -> ApiResult<()> {
self.sync_impl.lock().unwrap().wipe()?;
Ok(())
@ -210,7 +210,7 @@ impl TabsBridgedEngine {
}
// Decode the JSON-encoded IncomingBso's that UniFFI passes to us
#[handle_error(crate::Error)]
#[handle_error]
fn convert_incoming_bsos(&self, incoming: Vec<String>) -> ApiResult<Vec<IncomingBso>> {
let mut bsos = Vec::with_capacity(incoming.len());
for inc in incoming {
@ -220,7 +220,7 @@ impl TabsBridgedEngine {
}
// Encode OutgoingBso's into JSON for UniFFI
#[handle_error(crate::Error)]
#[handle_error]
fn convert_outgoing_bsos(&self, outgoing: Vec<OutgoingBso>) -> ApiResult<Vec<String>> {
let mut bsos = Vec::with_capacity(outgoing.len());
for e in outgoing {
@ -279,7 +279,7 @@ mod tests {
title: "my first tab".to_string(),
url_history: vec!["http://1.com".to_string()],
icon: None,
last_used: 2,
last_used: 0,
},
RemoteTab {
title: "my second tab".to_string(),

View File

@ -311,7 +311,11 @@ impl SyncEngine for TabsEngine {
.unwrap()
.apply_incoming(inbound.changes, telem)?;
Ok(OutgoingChangeset::new("tabs".into(), outgoing_records))
Ok(OutgoingChangeset::new_with_changes(
"tabs",
inbound.timestamp,
outgoing_records,
))
}
fn sync_finished(
@ -339,9 +343,7 @@ impl SyncEngine for TabsEngine {
Ok(if since == server_timestamp {
vec![]
} else {
vec![CollectionRequest::new("tabs".into())
.full()
.newer_than(since)]
vec![CollectionRequest::new("tabs").full().newer_than(since)]
})
}

View File

@ -2,7 +2,7 @@
* 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/. */
use crate::{sync::engine::TabsSyncImpl, ApiResult, TabsEngine, TabsStore};
use crate::{sync::engine::TabsSyncImpl, ApiResult, Result, TabsEngine, TabsStore};
use error_support::handle_error;
use interrupt_support::NeverInterrupts;
use std::sync::Arc;
@ -11,7 +11,7 @@ use sync15::engine::EngineSyncAssociation;
use sync15::KeyBundle;
impl TabsStore {
#[handle_error(crate::Error)]
#[handle_error]
pub fn reset(self: Arc<Self>) -> ApiResult<()> {
let mut sync_impl = TabsSyncImpl::new(Arc::clone(&self));
sync_impl.reset(&EngineSyncAssociation::Disconnected)?;
@ -19,7 +19,7 @@ impl TabsStore {
}
/// A convenience wrapper around sync_multiple.
#[handle_error(crate::Error)]
#[handle_error]
pub fn sync(
self: Arc<Self>,
key_id: String,